From 65de83140aa6264b75a8cb5eda9e0ba1b1f56b9b Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 3 Aug 2017 20:23:08 +1000 Subject: [PATCH] NFSv4: Add support (and tests) for open(O_APPEND) Signed-off-by: Ronnie Sahlberg --- include/libnfs-private.h | 3 + include/nfsc/libnfs.h | 2 - lib/libnfs.c | 5 +- lib/nfs_v4.c | 125 +++++++++++++++++++++++++++++++ tests/test_0224_open_O_APPEND.sh | 23 ++++++ 5 files changed, 153 insertions(+), 5 deletions(-) create mode 100755 tests/test_0224_open_O_APPEND.sh diff --git a/include/libnfs-private.h b/include/libnfs-private.h index 039c2b6..e470987 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -498,6 +498,9 @@ int nfs4_stat64_async(struct nfs_context *nfs, const char *path, int no_follow, nfs_cb cb, void *private_data); int nfs4_symlink_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); +int nfs4_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, + uint64_t count, const void *buf, nfs_cb cb, + void *private_data); #ifdef __cplusplus } diff --git a/include/nfsc/libnfs.h b/include/nfsc/libnfs.h index e5e4c63..b2061a2 100755 --- a/include/nfsc/libnfs.h +++ b/include/nfsc/libnfs.h @@ -513,8 +513,6 @@ 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_APPEND is not yet supported for NFSv4. ) - * * Function returns * 0 : The command was queued successfully. The callback will be invoked once * the command completes. diff --git a/lib/libnfs.c b/lib/libnfs.c index 5db2157..bafe975 100755 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -1052,9 +1052,8 @@ nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, return nfs3_write_async(nfs, nfsfh, count, buf, cb, private_data); case NFS_V4: - return nfs4_pwrite_async_internal(nfs, nfsfh, nfsfh->offset, - (size_t)count, buf, - cb, private_data, 1); + return nfs4_write_async(nfs, nfsfh, count, buf, + cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv%d", __FUNCTION__, nfs->version); diff --git a/lib/nfs_v4.c b/lib/nfs_v4.c index 09af26c..2a2baf4 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -1535,6 +1535,10 @@ nfs4_open_cb(struct rpc_context *rpc, int status, void *command_data, fh->is_sync = 1; } + if (data->filler.flags & O_APPEND) { + fh->is_append = 1; + } + fh->fh.len = gresok->object.nfs_fh4_len; fh->fh.val = malloc(fh->fh.len); if (fh->fh.val == NULL) { @@ -1779,6 +1783,10 @@ nfs4_open_async(struct nfs_context *nfs, const char *orig_path, int flags, flags &= ~O_TRUNC; } + if (flags & O_APPEND && !(flags & (O_RDWR|O_WRONLY))) { + flags &= ~O_APPEND; + } + data = malloc(sizeof(*data)); if (data == NULL) { nfs_set_error(nfs, "Out of memory. Failed to allocate " @@ -2322,3 +2330,120 @@ nfs4_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, return 0; } + +static void +nfs4_write_append_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; + GETATTR4resok *garesok = NULL; + struct nfsfh *nfsfh; + int i; + uint64_t offset; + char *buf; + uint32_t count; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + nfsfh = data->filler.blob0.val; + data->filler.blob0.val = NULL; + + buf = data->filler.blob1.val; + count = data->filler.blob1.len; + data->filler.blob1.val = NULL; + + if (check_nfs4_error(nfs, status, data, res, "GETATTR")) { + free_nfs4_cb_data(data); + return; + } + + if ((i = nfs4_find_op(nfs, data, res, OP_GETATTR, "GETATTR")) < 0) { + return; + } + + + garesok = &res->resarray.resarray_val[i].nfs_resop4_u.opgetattr.GETATTR4res_u.resok4; + if (garesok->obj_attributes.attr_vals.attrlist4_len < 8) { + data->cb(-EINVAL, nfs, nfs_get_error(nfs), data->private_data); + free_nfs4_cb_data(data); + return; + } + + offset = nfs_pntoh64((uint32_t *)(void *)garesok->obj_attributes.attr_vals.attrlist4_val); + + if (nfs4_pwrite_async_internal(nfs, nfsfh, offset, + (size_t)count, buf, + data->cb, data->private_data, 1) < 0) { + free_nfs4_cb_data(data); + data->cb(-ENOMEM, nfs, nfs_get_error(nfs), data->private_data); + return; + } + + free_nfs4_cb_data(data); +} + +int +nfs4_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, + const void *buf, nfs_cb cb, void *private_data) +{ + if (nfsfh->is_append) { + COMPOUND4args args; + nfs_argop4 op[2]; + PUTFH4args *pfargs; + GETATTR4args *gaargs; + static uint32_t attributes = 1 << FATTR4_SIZE; + struct nfs4_cb_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) { + nfs_set_error(nfs, "Out of memory. Failed to allocate " + "cb data"); + return -1; + } + memset(data, 0, sizeof(*data)); + + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + data->filler.blob0.val = nfsfh; + + memset(op, 0, sizeof(op)); + + 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_GETATTR; + gaargs = &op[1].nfs_argop4_u.opgetattr; + memset(gaargs, 0, sizeof(*gaargs)); + + gaargs->attr_request.bitmap4_len = 1; + gaargs->attr_request.bitmap4_val = &attributes; + + memset(&args, 0, sizeof(args)); + args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4); + args.argarray.argarray_val = op; + + data->filler.blob0.val = nfsfh; + data->filler.blob1.val = discard_const(buf); + data->filler.blob1.len = count; + + if (rpc_nfs4_compound_async(nfs->rpc, nfs4_write_append_cb, + &args, data) != 0) { + data->filler.blob0.val = NULL; + data->filler.blob1.val = NULL; + free_nfs4_cb_data(data); + return -1; + } + + return 0; + } + + return nfs4_pwrite_async_internal(nfs, nfsfh, nfsfh->offset, + (size_t)count, buf, + cb, private_data, 1); +} diff --git a/tests/test_0224_open_O_APPEND.sh b/tests/test_0224_open_O_APPEND.sh new file mode 100755 index 0000000..9e7a242 --- /dev/null +++ b/tests/test_0224_open_O_APPEND.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +. ./functions.sh + +echo "NFSv${VERS} Open(O_APPEND) test." + +start_share + +mkdir "${TESTDIR}/subdir" + +echo -n "test open(O_APPEND) (1) ... " +echo -n "GOAT:" > "${TESTDIR}/open1" +./prog_open_write "${TESTURL}/?version=${VERS}" "." /open1 O_WRONLY,O_APPEND "NieR" >/dev/null || failure +./prog_open_write "${TESTURL}/?version=${VERS}" "." /open1 O_WRONLY,O_APPEND "Automata" >/dev/null || failure +success + +echo -n "verify it got appended ... " +grep "GOAT:NieRAutomata" "${TESTDIR}/open1" >/dev/null || failure +success + +stop_share + +exit 0