From 1ec6b50aef06d7b3e27db1b406e080f7785677e9 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 27 Nov 2011 14:08:24 +1100 Subject: [PATCH] Add MKNOD command support Add support for raw nfs3 mknod and sync and async posixlike api --- include/libnfs-raw.h | 15 +++++++ include/libnfs.h | 23 ++++++++++ lib/libnfs-sync.c | 30 +++++++++++++ lib/libnfs-win32.def | 2 + lib/libnfs.c | 103 +++++++++++++++++++++++++++++++++++++++++++ nfs/nfs.c | 60 +++++++++++++++++++++++++ nfs/nfs.x | 3 +- 7 files changed, 235 insertions(+), 1 deletion(-) diff --git a/include/libnfs-raw.h b/include/libnfs-raw.h index 5e44519..0a212c8 100644 --- a/include/libnfs-raw.h +++ b/include/libnfs-raw.h @@ -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); /* diff --git a/include/libnfs.h b/include/libnfs.h index 9758a78..4f56c87 100644 --- a/include/libnfs.h +++ b/include/libnfs.h @@ -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); diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index bd6048e..701cd06 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -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; +} /* diff --git a/lib/libnfs-win32.def b/lib/libnfs-win32.def index f553e71..4b1e6f5 100644 --- a/lib/libnfs-win32.def +++ b/lib/libnfs-win32.def @@ -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 diff --git a/lib/libnfs.c b/lib/libnfs.c index a966b02..71c6e9c 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -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() diff --git a/nfs/nfs.c b/nfs/nfs.c index 9d8086f..5f375ec 100644 --- 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) { diff --git a/nfs/nfs.x b/nfs/nfs.x index 1f4976b..76b6d7e 100644 --- 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; -- 2.34.1