Add MKNOD command support
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 27 Nov 2011 03:08:24 +0000 (14:08 +1100)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 27 Nov 2011 03:08:24 +0000 (14:08 +1100)
Add support for raw nfs3 mknod  and sync and async posixlike api

include/libnfs-raw.h
include/libnfs.h
lib/libnfs-sync.c
lib/libnfs-win32.def
lib/libnfs.c
nfs/nfs.c
nfs/nfs.x

index 5e445192fff3959132b392d71866128305bff1e4..0a212c899d76f12199849e36683e8245d3c91c94 100644 (file)
@@ -425,6 +425,21 @@ int rpc_nfs_rmdir_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh,
 int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *name, int mode, void *private_data);
 
 
+/*
+ * Call NFS/MKNOD
+ * Function returns
+ *  0 : The call was initiated. The callback will be invoked when the call completes.
+ * <0 : An error occured when trying to set up the call. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ * RPC_STATUS_SUCCESS : We got a successful response from the nfs daemon.
+ *                      data is MKNOD3res *
+ * RPC_STATUS_ERROR   : An error occured when trying to contact the nfs daemon.
+ *                      data is the error string.
+ * RPC_STATUS_CANCEL : The connection attempt was aborted before it could complete.
+ *                     data is NULL.
+ */
+int rpc_nfs_mknod_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, int mode, int major, int minor, void *private_data);
 
 
 /*
index 9758a7814619c35ab44612871c1f249e493aa628..4f56c876f326d1c2b67e3ebb1e222dd29cdc3c39 100644 (file)
@@ -545,6 +545,29 @@ EXTERN int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode,
 EXTERN int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh **nfsfh);
 
 
+/*
+ * MKNOD()
+ */
+/*
+ * Async mknod()
+ *
+ * Function returns
+ *  0 : The operation was initiated. Once the operation finishes, the callback will be invoked.
+ * <0 : An error occured when trying to set up the operation. The callback will not be invoked.
+ *
+ * When the callback is invoked, status indicates the result:
+ *      0 : Success.
+ * -errno : An error occured.
+ *          data is the error string.
+ */
+EXTERN int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev, nfs_cb cb, void *private_data);
+/*
+ * Sync mknod()
+ * Function returns
+ *      0 : Success
+ * -errno : An error occured.
+ */
+EXTERN int nfs_mknod(struct nfs_context *nfs, const char *path, int mode, int dev);
 
 
 
index bd6048eb2751233cf3f66e4f105ec39333a091cb..701cd0678a747716d514227a590f8d786ca2975a 100644 (file)
@@ -576,7 +576,37 @@ int nfs_creat(struct nfs_context *nfs, const char *path, int mode, struct nfsfh
        return cb_data.status;
 }
 
+/*
+ * mknod()
+ */
+static void mknod_cb(int status, struct nfs_context *nfs, void *data, void *private_data)
+{
+       struct sync_cb_data *cb_data = private_data;
+
+       cb_data->is_finished = 1;
+       cb_data->status = status;
 
+       if (status < 0) {
+               nfs_set_error(nfs, "mknod call failed with \"%s\"", (char *)data);
+               return;
+       }
+}
+
+int nfs_mknod(struct nfs_context *nfs, const char *path, int mode, int dev)
+{
+       struct sync_cb_data cb_data;
+
+       cb_data.is_finished = 0;
+
+       if (nfs_mknod_async(nfs, path, mode, dev, mknod_cb, &cb_data) != 0) {
+               nfs_set_error(nfs, "nfs_creat_async failed");
+               return -1;
+       }
+
+       wait_for_nfs_reply(nfs, &cb_data);
+
+       return cb_data.status;
+}
 
 
 /*
index f553e715090e1c25316d66d7266c483c965c2c1c..4b1e6f515dcc463b979db7366f9e70d8fd874465 100644 (file)
@@ -38,6 +38,8 @@ nfs_lseek
 nfs_lseek_async
 nfs_mkdir
 nfs_mkdir_async
+nfs_mknod
+nfs_mknod_async
 nfs_mount
 nfs_mount_async
 nfs_open
index a966b02f272fd62fcec84b4318e7937b2b0fe34a..71c6e9cb1b2ad78b3a3783ff0a962810fafd349d 100644 (file)
@@ -1766,6 +1766,109 @@ int nfs_unlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void
 }
 
 
+/*
+ * Async mknod()
+ */
+struct mknod_cb_data {
+       char *path;
+       int mode;
+       int major;
+       int minor;
+};
+
+static void free_mknod_cb_data(void *ptr)
+{
+       struct mknod_cb_data *data = ptr;
+
+       free(data->path);
+       free(data);
+}
+
+static void nfs_mknod_cb(struct rpc_context *rpc _U_, int status, void *command_data, void *private_data)
+{
+       MKNOD3res *res;
+       struct nfs_cb_data *data = private_data;
+       struct nfs_context *nfs = data->nfs;
+       char *str = data->continue_data;
+       
+       str = &str[strlen(str) + 1];
+
+       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: MKNOD of %s/%s failed with %s(%d)", data->saved_path, str, 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 int nfs_mknod_continue_internal(struct nfs_context *nfs, struct nfs_cb_data *data)
+{
+       struct mknod_cb_data *cb_data = data->continue_data;
+       char *str = cb_data->path;
+       
+       str = &str[strlen(str) + 1];
+
+       if (rpc_nfs_mknod_async(nfs->rpc, nfs_mknod_cb, &data->fh, str, cb_data->mode, cb_data->major, cb_data->minor, data) != 0) {
+               data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data);
+               free_nfs_cb_data(data);
+               return -1;
+       }
+       return 0;
+}
+
+int nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev, nfs_cb cb, void *private_data)
+{
+       char *ptr;
+       struct mknod_cb_data *cb_data;
+
+       cb_data = malloc(sizeof(struct mknod_cb_data));
+       if (cb_data == NULL) {
+               rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for cb data");
+               return -1;
+       }
+
+       cb_data->path = strdup(path);
+       if (cb_data->path == NULL) {
+               rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path");
+               free(cb_data);          
+               return -1;
+       }
+
+       ptr = strrchr(cb_data->path, '/');
+       if (ptr == NULL) {
+               rpc_set_error(nfs->rpc, "Invalid path %s", path);
+               return -1;
+       }
+       *ptr = 0;
+
+       cb_data->mode = mode;
+       cb_data->major = major(dev);
+       cb_data->minor = minor(dev);
+
+       /* data->path now points to the parent directory,  and beyond the nul terminateor is the new directory to create */
+       if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_mknod_continue_internal, cb_data, free_mknod_cb_data, 0) != 0) {
+               rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components");
+               free_mknod_cb_data(cb_data);
+               return -1;
+       }
+
+       return 0;
+}
 
 /*
  * Async opendir()
index 9d8086f7ca1e5611ad907128526f2eb2938b5c3f..5f375ec0e6e5f05c7da97c0fb2d17dfe73cf1d9b 100644 (file)
--- a/nfs/nfs.c
+++ b/nfs/nfs.c
@@ -447,6 +447,66 @@ int rpc_nfs_create_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh,
 
 
 
+int rpc_nfs_mknod_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, int mode, int major, int minor, void *private_data)
+{
+       struct rpc_pdu *pdu;
+       MKNOD3args args;
+
+       pdu = rpc_allocate_pdu(rpc, NFS_PROGRAM, NFS_V3, NFS3_MKNOD, cb, private_data, (xdrproc_t)xdr_MKNOD3res, sizeof(MKNOD3res));
+       if (pdu == NULL) {
+               rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfs/mknod call");
+               return -1;
+       }
+
+       memset(&args, 0, sizeof(MKNOD3args));
+       args.where.dir.data.data_len = fh->data.data_len;
+       args.where.dir.data.data_val = fh->data.data_val;
+       args.where.name = file;
+       switch (mode & S_IFMT) {
+       case S_IFCHR:
+               args.what.type = NF3CHR;
+               args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_it = 1;
+               args.what.mknoddata3_u.chr_device.dev_attributes.mode.set_mode3_u.mode = mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+               args.what.mknoddata3_u.chr_device.spec.specdata1 = major;
+               args.what.mknoddata3_u.chr_device.spec.specdata2 = minor;
+               break;
+       case S_IFBLK:
+               args.what.type = NF3BLK;
+               args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_it = 1;
+               args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_mode3_u.mode = mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+               args.what.mknoddata3_u.blk_device.spec.specdata1 = major;
+               args.what.mknoddata3_u.blk_device.spec.specdata2 = minor;
+       case S_IFSOCK:
+               args.what.type = NF3SOCK;
+               args.what.mknoddata3_u.sock_attributes.mode.set_it = 1;
+               args.what.mknoddata3_u.sock_attributes.mode.set_mode3_u.mode = mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+               break;
+       case S_IFIFO:
+               args.what.type = NF3FIFO;
+               args.what.mknoddata3_u.pipe_attributes.mode.set_it = 1;
+               args.what.mknoddata3_u.pipe_attributes.mode.set_mode3_u.mode = mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
+               break;
+       default:
+               rpc_set_error(rpc, "Invalid file type for nfs/mknod call");
+               rpc_free_pdu(rpc, pdu);
+               return -1;
+       }
+
+       if (xdr_MKNOD3args(&pdu->xdr, &args) == 0) {
+               rpc_set_error(rpc, "XDR error: Failed to encode MKNOD3args");
+               rpc_free_pdu(rpc, pdu);
+               return -2;
+       }
+
+       if (rpc_queue_pdu(rpc, pdu) != 0) {
+               rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfs/mknod call");
+               rpc_free_pdu(rpc, pdu);
+               return -3;
+       }
+
+       return 0;
+}
+
 
 int rpc_nfs_remove_async(struct rpc_context *rpc, rpc_cb cb, struct nfs_fh3 *fh, char *file, void *private_data)
 {
index 1f4976b14e3a3b5188a2306dc616c7fe9d2db654..76b6d7e508f799c98e0c5a99ca43a8a0d73d4d67 100644 (file)
--- a/nfs/nfs.x
+++ b/nfs/nfs.x
@@ -816,7 +816,8 @@ program NFS_PROGRAM {
                SYMLINK3res
                NFS3_SYMLINK(SYMLINK3args)         = 10;
 
-/*             MKNOD3res NFSPROC3_MKNOD(MKNOD3args)             = 11;*/
+               MKNOD3res
+               NFS3_MKNOD(MKNOD3args)             = 11;
 
                REMOVE3res
                NFS3_REMOVE(REMOVE3args)           = 12;