From 42d14779b74e4a7e6206928899ee3a8c80507101 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 30 Jul 2017 08:36:18 +1000 Subject: [PATCH] NFSv4: Add support for open(O_TRUNC) Signed-off-by: Ronnie Sahlberg --- include/nfsc/libnfs.h | 2 +- lib/nfs_v4.c | 111 ++++++++++++++++++++++++++++---- tests/test_0223_open_O_TRUNC.sh | 31 +++++++++ 3 files changed, 132 insertions(+), 12 deletions(-) create mode 100755 tests/test_0223_open_O_TRUNC.sh diff --git a/include/nfsc/libnfs.h b/include/nfsc/libnfs.h index 9a36f64..8faebc0 100755 --- a/include/nfsc/libnfs.h +++ b/include/nfsc/libnfs.h @@ -513,7 +513,7 @@ EXTERN uint16_t nfs_umask(struct nfs_context *nfs, uint16_t mask); * mode is a combination of the flags : * O_RDONLY, O_WRONLY, O_RDWR , O_SYNC, O_APPEND, O_TRUNC, O_NOFOLLOW * - * ( O_SYNC, O_APPEND, O_TRUNC are not yet supported for NFSv4. ) + * ( O_SYNC, O_APPEND are not yet supported for NFSv4. ) * * Function returns * 0 : The command was queued successfully. The callback will be invoked once diff --git a/lib/nfs_v4.c b/lib/nfs_v4.c index 54d08ce..5e30fda 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -1366,6 +1366,78 @@ nfs4_rmdir_async(struct nfs_context *nfs, const char *orig_path, return 0; } +static void +nfs4_open_truncate_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; + struct nfsfh *fh; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (check_nfs4_error(nfs, status, data, res, "SETATTR")) { + free_nfs4_cb_data(data); + return; + } + + fh = data->filler.blob0.val; + + data->filler.blob0.val = NULL; + data->filler.blob1.val = NULL; + data->cb(0, nfs, fh, data->private_data); + free_nfs4_cb_data(data); +} + +static int +nfs4_open_truncate(struct rpc_context *rpc, struct nfs4_cb_data *data) +{ + struct nfs_context *nfs = data->nfs; + struct nfsfh *nfsfh; + nfs_argop4 op[2]; + COMPOUND4args args; + PUTFH4args *pfargs; + SETATTR4args *saargs; + static uint32_t mask[2] = {1 << (FATTR4_SIZE), + 1 << (FATTR4_TIME_MODIFY_SET - 32)}; + static char zero[12]; + + nfsfh = data->filler.blob0.val; + + op[0].argop = OP_PUTFH; + pfargs = &op[0].nfs_argop4_u.opputfh; + pfargs->object.nfs_fh4_len = nfsfh->fh.len; + pfargs->object.nfs_fh4_val = nfsfh->fh.val; + + op[1].argop = OP_SETATTR; + saargs = &op[1].nfs_argop4_u.opsetattr; + saargs->stateid.seqid = nfsfh->stateid.seqid; + memcpy(saargs->stateid.other, nfsfh->stateid.other, 12); + + saargs->obj_attributes.attrmask.bitmap4_len = 2; + saargs->obj_attributes.attrmask.bitmap4_val = mask; + + saargs->obj_attributes.attr_vals.attrlist4_len = 12; + saargs->obj_attributes.attr_vals.attrlist4_val = zero; + + + memset(&args, 0, sizeof(args)); + args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4); + args.argarray.argarray_val = op; + + if (rpc_nfs4_compound_async(rpc, nfs4_open_truncate_cb, &args, + data) != 0) { + nfs_set_error(nfs, "Failed to queue TRUNCATE. %s", + nfs_get_error(nfs)); + data->cb(-ENOMEM, nfs, nfs_get_error(nfs), data->private_data); + free_nfs4_cb_data(data); + return -1; + } + + return 0; +} + static void nfs4_open_confirm_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) @@ -1391,16 +1463,23 @@ nfs4_open_confirm_cb(struct rpc_context *rpc, int status, void *command_data, ocresok = &res->resarray.resarray_val[i].nfs_resop4_u.opopen_confirm.OPEN_CONFIRM4res_u.resok4; fh = data->filler.blob0.val; - data->filler.blob0.val = NULL; - data->filler.blob1.val = NULL; fh->stateid.seqid = ocresok->open_stateid.seqid; memcpy(fh->stateid.other, ocresok->open_stateid.other, 12); + if (data->filler.flags & O_TRUNC) { + nfs4_open_truncate(rpc, data); + return; + } + + data->filler.blob0.val = NULL; + data->filler.blob1.val = NULL; data->cb(0, nfs, fh, data->private_data); free_nfs4_cb_data(data); } +/* Stores nfsfh in data.blob0 and nfsfh->fh.val in data.blob1. + */ static void nfs4_open_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) @@ -1504,6 +1583,11 @@ nfs4_open_cb(struct rpc_context *rpc, int status, void *command_data, return; } + if (data->filler.flags & O_TRUNC) { + nfs4_open_truncate(rpc, data); + return; + } + data->filler.blob0.val = NULL; data->filler.blob1.val = NULL; data->cb(0, nfs, fh, data->private_data); @@ -1640,15 +1724,15 @@ nfs4_populate_open(struct nfs4_cb_data *data, nfs_argop4 *op) aargs = &op[0].nfs_argop4_u.opaccess; memset(aargs, 0, sizeof(*aargs)); - if (data->filler.flags & O_WRONLY) { - aargs->access |= ACCESS4_MODIFY; - } - if (data->filler.flags & O_RDWR) { - aargs->access |= ACCESS4_READ|ACCESS4_MODIFY; - } - if (!(data->filler.flags & (O_WRONLY|O_RDWR))) { - aargs->access |= ACCESS4_READ; - } + if (data->filler.flags & O_WRONLY) { + aargs->access |= ACCESS4_MODIFY; + } + if (data->filler.flags & O_RDWR) { + aargs->access |= ACCESS4_READ|ACCESS4_MODIFY; + } + if (!(data->filler.flags & (O_WRONLY|O_RDWR))) { + aargs->access |= ACCESS4_READ; + } /* Open */ op[1].argop = OP_OPEN; @@ -1686,6 +1770,11 @@ nfs4_open_async(struct nfs_context *nfs, const char *orig_path, int flags, struct nfs4_cb_data *data; char *path; + /* O_TRUNC is only valid for O_RDWR or O_WRONLY */ + if (flags & O_TRUNC && !(flags & (O_RDWR|O_WRONLY))) { + flags &= ~O_TRUNC; + } + data = malloc(sizeof(*data)); if (data == NULL) { nfs_set_error(nfs, "Out of memory. Failed to allocate " diff --git a/tests/test_0223_open_O_TRUNC.sh b/tests/test_0223_open_O_TRUNC.sh new file mode 100755 index 0000000..c310271 --- /dev/null +++ b/tests/test_0223_open_O_TRUNC.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +. ./functions.sh + +echo "NFSv${VERS} Open(O_TRUNC) test." + +start_share + +mkdir "${TESTDIR}/subdir" + +echo -n "test open(O_WRONLY|O_TRUNC) (1) ... " +echo -n "kangabanga" > "${TESTDIR}/open1" +./prog_open_write "${TESTURL}/?version=${VERS}" "." /open1 O_WRONLY,O_TRUNC "" >/dev/null || failure +success + +echo -n "verify the file got truncated ... " +expr `stat --printf="%s" "${TESTDIR}/open1"` "==" "0" >/dev/null || failure +success + +echo -n "test open(O_RDONLY|O_TRUNC) (2) ... " +echo -n "kangabanga" > "${TESTDIR}/open1" +./prog_open_write "${TESTURL}/?version=${VERS}" "." /open1 O_RDONLY,O_TRUNC "" >/dev/null || failure +success + +echo -n "verify the file did not get truncated ... " +expr `stat --printf="%s" "${TESTDIR}/open1"` "==" "10" >/dev/null || failure +success + +stop_share + +exit 0