Add support for O_TRUNC with nfs_open()
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 2 Feb 2014 20:16:07 +0000 (12:16 -0800)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 2 Feb 2014 20:16:07 +0000 (12:16 -0800)
O_TRUNC will attempt to truncate the file when opened with O_RDWR
or O_WRONLY.

Normal posix open(O_RDONLY|O_TRUNC) is undefined.

libnfs nfs_open() only uses the O_TRUNC flag when used in combination with either O_RDWR or O_WRONLY.
When O_TRUNC is used together with O_RDONLY libnfs will silently ignore the O_TRUNC flag.

Libnfs nfs_open(O_RDONLY|O_TRUNC) is thus the same as nfs_open(O_RDONLY)

include/nfsc/libnfs.h
lib/libnfs.c

index b9138c9066409ea3fab16053a4ebca4c9a2d793d..0504a6aac4e13b789deae2b93b536a0fa822ea4f 100644 (file)
@@ -276,6 +276,7 @@ EXTERN int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct stat *
  * O_RDONLY
  * O_WRONLY
  * O_RDWR
+ * O_TRUNC (Only valid with O_RDWR or O_WRONLY. Ignored otherwise.)
  *
  * When the callback is invoked, status indicates the result:
  *      0 : Success.
index bfb1b7ce70bba59dd312c0bd7bdd79935886b820..c2c20d4481b20945e8f30e3f8b2f28aef020a2bb 100644 (file)
@@ -143,7 +143,6 @@ struct nfs_mcb_data {
 
 static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh);
 
-
 void nfs_set_auth(struct nfs_context *nfs, struct AUTH *auth)
 {
        rpc_set_auth(nfs->rpc, auth);
@@ -1244,6 +1243,37 @@ int nfs_stat_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *p
 /*
  * Async open()
  */
+static void nfs_open_trunc_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
+{
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       SETATTR3res *res;
+
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       if (status == RPC_STATUS_ERROR) {
+               data->cb(-EFAULT, nfs, command_data, data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+       if (status == RPC_STATUS_CANCEL) {
+               data->cb(-EINTR, nfs, "Command was cancelled", data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       res = command_data;
+       if (res->status != NFS3_OK) {
+               rpc_set_error(nfs->rpc, "NFS: Setattr failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status));
+               data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return;
+       }
+
+       data->cb(0, nfs, NULL, data->private_data);
+       free_nfs_cb_data(data);
+}
+
 static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data)
 {
        ACCESS3res *res;
@@ -1314,6 +1344,28 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data,
        nfsfh->fh = data->fh;
        data->fh.data.data_val = NULL;
 
+       /* Try to truncate it if we were requested to */
+       if ((data->continue_int & O_TRUNC) &&
+           (data->continue_int & (O_RDWR|O_WRONLY))) {
+               SETATTR3args args;
+
+               memset(&args, 0, sizeof(SETATTR3args));
+               args.object = nfsfh->fh;
+               args.new_attributes.size.set_it = 1;
+               args.new_attributes.size.set_size3_u.size = 0;
+
+               if (rpc_nfs3_setattr_async(nfs->rpc, nfs_open_trunc_cb, &args,
+                               data) != 0) {
+                       rpc_set_error(nfs->rpc, "RPC error: Failed to send "
+                               "SETATTR call for %s", data->path);
+                       data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc),
+                               data->private_data);
+                       free_nfs_cb_data(data);
+                       return;
+               }
+               return;
+       }
+
        data->cb(0, nfs, nfsfh, data->private_data);
        free_nfs_cb_data(data);
 }
@@ -1338,8 +1390,10 @@ static int nfs_open_continue_internal(struct nfs_context *nfs, struct nfs_cb_dat
        args.access = nfsmode;
 
        if (rpc_nfs3_access_async(nfs->rpc, nfs_open_cb, &args, data) != 0) {
-               rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS call for %s", data->path);
-               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               rpc_set_error(nfs->rpc, "RPC error: Failed to send OPEN ACCESS "
+                               "call for %s", data->path);
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc),
+                               data->private_data);
                free_nfs_cb_data(data);
                return -1;
        }