diff --git a/include/libnfs-private.h b/include/libnfs-private.h index 5e1f99f..288e370 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -484,6 +484,8 @@ int nfs4_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); int nfs4_mkdir2_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data); +int nfs4_mknod_async(struct nfs_context *nfs, const char *path, int mode, + int dev, nfs_cb cb, void *private_data); int nfs4_mount_async(struct nfs_context *nfs, const char *server, const char *export, nfs_cb cb, void *private_data); int nfs4_open_async(struct nfs_context *nfs, const char *path, int flags, diff --git a/lib/libnfs.c b/lib/libnfs.c index 8c738a9..e6aa220 100755 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -1247,9 +1247,11 @@ nfs_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev, switch (nfs->version) { case NFS_V3: return nfs3_mknod_async(nfs, path, mode, dev, cb, private_data); + case NFS_V4: + return nfs4_mknod_async(nfs, path, mode, dev, cb, private_data); default: - nfs_set_error(nfs, "%s does not support NFSv4", - __FUNCTION__); + nfs_set_error(nfs, "%s does not support NFSv%d", + __FUNCTION__, nfs->version); return -1; } } diff --git a/lib/nfs_v3.c b/lib/nfs_v3.c index 87ce61a..9a4e76c 100644 --- a/lib/nfs_v3.c +++ b/lib/nfs_v3.c @@ -3001,6 +3001,7 @@ nfs3_mknod_continue_internal(struct nfs_context *nfs, args.what.mknoddata3_u.blk_device.dev_attributes.mode.set_mode3_u.mode = cb_data->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 = cb_data->major; args.what.mknoddata3_u.blk_device.spec.specdata2 = cb_data->minor; + break; case S_IFSOCK: args.what.type = NF3SOCK; args.what.mknoddata3_u.sock_attributes.mode.set_it = 1; diff --git a/lib/nfs_v4.c b/lib/nfs_v4.c index f74e121..5072767 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -2752,3 +2752,128 @@ nfs4_rename_async(struct nfs_context *nfs, const char *oldpath, return 0; } + +static void +nfs4_mknod_cb(struct rpc_context *rpc, int status, void *command_data, + void *private_data) +{ + struct nfs4_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + COMPOUND4res *res = command_data; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (check_nfs4_error(nfs, status, data, res, "MKNOD")) { + free_nfs4_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs4_cb_data(data); +} + +static int +nfs4_populate_mknod(struct nfs4_cb_data *data, nfs_argop4 *op) +{ + CREATE4args *cargs; + uint32_t mode, *ptr; + int dev; + + /* Strip off the file type before we marshall it */ + ptr = (void *)data->filler.blob1.val; + mode = *ptr; + *ptr = htonl(mode & ~S_IFMT); + + dev = data->filler.blob2.len; + + op[0].argop = OP_CREATE; + cargs = &op[0].nfs_argop4_u.opcreate; + memset(cargs, 0, sizeof(*cargs)); + cargs->objname.utf8string_val = data->filler.data; + cargs->objname.utf8string_len = strlen(cargs->objname.utf8string_val); + cargs->createattrs.attrmask.bitmap4_len = data->filler.blob0.len; + cargs->createattrs.attrmask.bitmap4_val = data->filler.blob0.val; + cargs->createattrs.attr_vals.attrlist4_len = data->filler.blob1.len; + cargs->createattrs.attr_vals.attrlist4_val = data->filler.blob1.val; + + switch (mode & S_IFMT) { + case S_IFCHR: + cargs->objtype.type = NF4CHR; + cargs->objtype.createtype4_u.devdata.specdata1 = major(dev); + cargs->objtype.createtype4_u.devdata.specdata2 = minor(dev); + break; + case S_IFBLK: + cargs->objtype.type = NF4BLK; + cargs->objtype.createtype4_u.devdata.specdata1 = major(dev); + cargs->objtype.createtype4_u.devdata.specdata2 = minor(dev); + break; + } + + return 1; +} + +/* Takes object name as filler.data + * blob0 as attribute mask + * blob1 as attribute value + * blob2.len as dev + */ +int +nfs4_mknod_async(struct nfs_context *nfs, const char *path, int mode, int dev, + nfs_cb cb, void *private_data) +{ + struct nfs4_cb_data *data; + uint32_t *u32ptr; + + switch (mode & S_IFMT) { + case S_IFCHR: + case S_IFBLK: + break; + default: + nfs_set_error(nfs, "Invalid file type for " + "MKNOD call"); + return -1; + } + + data = init_cb_data_split_path(nfs, path); + if (data == NULL) { + return -1; + } + + data->cb = cb; + data->private_data = private_data; + data->filler.func = nfs4_populate_mknod; + data->filler.max_op = 1; + + /* attribute mask */ + u32ptr = malloc(2 * sizeof(uint32_t)); + if (u32ptr == NULL) { + nfs_set_error(nfs, "Out of memory allocating bitmap"); + return 0; + } + u32ptr[0] = 0; + u32ptr[1] = 1 << (FATTR4_MODE - 32); + data->filler.blob0.len = 2; + data->filler.blob0.val = u32ptr; + data->filler.blob0.free = free; + + /* attribute values */ + u32ptr = malloc(1 * sizeof(uint32_t)); + if (u32ptr == NULL) { + nfs_set_error(nfs, "Out of memory allocating attributes"); + free_nfs4_cb_data(data); + return -1; + } + u32ptr[0] = mode; + data->filler.blob1.len = 4; + data->filler.blob1.val = u32ptr; + data->filler.blob1.free = free; + + data->filler.blob2.len = dev; + + if (nfs4_lookup_path_async(nfs, data, nfs4_mknod_cb) < 0) { + free_nfs4_cb_data(data); + return -1; + } + + return 0; +} diff --git a/tests/test_0310_mknod.sh b/tests/test_0310_mknod.sh index d418534..6723edf 100755 --- a/tests/test_0310_mknod.sh +++ b/tests/test_0310_mknod.sh @@ -44,6 +44,18 @@ echo -n "Create a chrdev outside the share (abs) (6)... " ./prog_mknod "${TESTURL}/?uid=0&version=${VERS}" "." ../subdir2/mknod6 020775 0x1234 2>/dev/null && failure success +echo -n "Create a blkdev in the root (abs) (7)... " +./prog_mknod "${TESTURL}/?uid=0&version=${VERS}" "." /mknod7 060755 0x1234 || failure +success + +echo -n "Stat the node ... " +./prog_stat "${TESTURL}/?version=${VERS}" "." mknod7 > "${TESTDIR}/output" || failure +success + +echo -n "Testing nfs_mode and verify it is a BLKDEV ... " +grep "nfs_mode:60755" "${TESTDIR}/output" >/dev/null || failure +success + stop_share exit 0