diff --git a/include/libnfs-private.h b/include/libnfs-private.h index 3d66e1c..0b451cb 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -486,6 +486,9 @@ int nfs4_open_async(struct nfs_context *nfs, const char *path, int flags, int nfs4_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, size_t count, nfs_cb cb, void *private_data, int update_pos); +int nfs4_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, + uint64_t offset, size_t count, const char *buf, + nfs_cb cb, void *private_data, int update_pos); int nfs4_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); int nfs4_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index 14bb5ad..ec88bb7 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -585,7 +585,8 @@ nfs_pwrite(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, if (nfs_pwrite_async(nfs, nfsfh, offset, count, buf, pwrite_cb, &cb_data) != 0) { - nfs_set_error(nfs, "nfs_pwrite_async failed"); + nfs_set_error(nfs, "nfs_pwrite_async failed. %s", + nfs_get_error(nfs)); return -1; } @@ -607,7 +608,8 @@ nfs_write(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, cb_data.call = "write"; if (nfs_write_async(nfs, nfsfh, count, buf, pwrite_cb, &cb_data) != 0) { - nfs_set_error(nfs, "nfs_write_async failed"); + nfs_set_error(nfs, "nfs_write_async failed. %s", + nfs_get_error(nfs)); return -1; } diff --git a/lib/libnfs.c b/lib/libnfs.c index 2a24782..5db2157 100755 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -1032,9 +1032,13 @@ nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, return nfs3_pwrite_async_internal(nfs, nfsfh, offset, (size_t)count, buf, cb, private_data, 0); + case NFS_V4: + return nfs4_pwrite_async_internal(nfs, nfsfh, offset, + (size_t)count, buf, + cb, private_data, 0); 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; } } @@ -1047,9 +1051,13 @@ nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, case NFS_V3: 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); 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_v4.c b/lib/nfs_v4.c index 4a7d6d2..54d08ce 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -93,6 +93,9 @@ #include "libnfs-raw.h" #include "libnfs-private.h" +#ifndef discard_const +#define discard_const(ptr) ((void *)((intptr_t)(ptr))) +#endif struct nfs4_cb_data; typedef int (*op_filler)(struct nfs4_cb_data *data, nfs_argop4 *op); @@ -1653,7 +1656,12 @@ nfs4_populate_open(struct nfs4_cb_data *data, nfs_argop4 *op) memset(oargs, 0, sizeof(*oargs)); oargs->seqid = nfs->seqid++; - oargs->share_access = OPEN4_SHARE_ACCESS_READ; + if (aargs->access & ACCESS4_READ) { + oargs->share_access |= OPEN4_SHARE_ACCESS_READ; + } + if (aargs->access & ACCESS4_MODIFY) { + oargs->share_access |= OPEN4_SHARE_ACCESS_WRITE; + } oargs->share_deny = OPEN4_SHARE_DENY_NONE; oargs->owner.clientid = nfs->clientid; oargs->owner.owner.owner_len = strlen(nfs->client_name); @@ -2121,3 +2129,95 @@ nfs4_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb, return 0; } + +static void +nfs4_pwrite_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; + WRITE4resok *wres = NULL; + struct nfsfh *nfsfh; + int i; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + nfsfh = data->filler.blob0.val; + data->filler.blob0.val = NULL; + + if (check_nfs4_error(nfs, status, data, res, "WRITE")) { + free_nfs4_cb_data(data); + return; + } + + if ((i = nfs4_find_op(nfs, data, res, OP_WRITE, "WRITE")) < 0) { + return; + } + wres = &res->resarray.resarray_val[i].nfs_resop4_u.opwrite.WRITE4res_u.resok4; + + if (data->rw_data.update_pos) { + nfsfh->offset = data->rw_data.offset + wres->count; + } + + data->cb(wres->count, nfs, NULL, data->private_data); + free_nfs4_cb_data(data); +} + +int +nfs4_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, + uint64_t offset, size_t count, const char *buf, + nfs_cb cb, void *private_data, int update_pos) +{ + COMPOUND4args args; + nfs_argop4 op[2]; + PUTFH4args *pfargs; + WRITE4args *wargs; + 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; + data->rw_data.offset = offset; + data->rw_data.update_pos = update_pos; + + 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_WRITE; + wargs = &op[1].nfs_argop4_u.opwrite; + wargs->stateid.seqid = nfsfh->stateid.seqid; + memcpy(wargs->stateid.other, nfsfh->stateid.other, 12); + wargs->offset = offset; + wargs->stable = UNSTABLE4; + wargs->data.data_len = count; + wargs->data.data_val = discard_const(buf); + + + memset(&args, 0, sizeof(args)); + args.argarray.argarray_len = sizeof(op) / sizeof(nfs_argop4); + args.argarray.argarray_val = op; + + if (rpc_nfs4_compound_async(nfs->rpc, nfs4_pwrite_cb, &args, + data) != 0) { + data->filler.blob0.val = NULL; + free_nfs4_cb_data(data); + return -1; + } + + return 0; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 6fcb546..a46e0aa 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -5,8 +5,8 @@ AM_CFLAGS = $(WARN_CFLAGS) LDADD = ../lib/libnfs.la noinst_PROGRAMS = prog_create prog_fstat prog_link prog_lstat prog_mkdir \ - prog_mknod prog_mount prog_open_read prog_rename prog_rmdir prog_stat \ - prog_symlink prog_timeout prog_unlink + prog_mknod prog_mount prog_open_read prog_open_write prog_rename \ + prog_rmdir prog_stat prog_symlink prog_timeout prog_unlink EXTRA_PROGRAMS = ld_timeout CLEANFILES = ld_timeout.o ld_timeout.so diff --git a/tests/prog_open_write.c b/tests/prog_open_write.c new file mode 100644 index 0000000..5d81e4f --- /dev/null +++ b/tests/prog_open_write.c @@ -0,0 +1,127 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) by Ronnie Sahlberg 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#define _FILE_OFFSET_BITS 64 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libnfs.h" + +void usage(void) +{ + fprintf(stderr, "Usage: prog_open_write " + "\t\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nfs_context *nfs = NULL; + struct nfs_url *url = NULL; + int ret = 0; + int flags = 0, count; + struct nfsfh *fh; + + if (argc != 6) { + usage(); + } + + if (strstr(argv[4], "O_RDONLY")) { + flags |= O_RDONLY; + } + if (strstr(argv[4], "O_RDWR")) { + flags |= O_RDWR; + } + if (strstr(argv[4], "O_WRONLY")) { + flags |= O_WRONLY; + } + if (strstr(argv[4], "O_NOFOLLOW")) { + flags |= O_NOFOLLOW; + } + if (strstr(argv[4], "O_APPEND")) { + flags |= O_APPEND; + } + if (strstr(argv[4], "O_TRUNC")) { + flags |= O_TRUNC; + } + + nfs = nfs_init_context(); + if (nfs == NULL) { + printf("failed to init context\n"); + exit(1); + } + + url = nfs_parse_url_full(nfs, argv[1]); + if (url == NULL) { + fprintf(stderr, "%s\n", nfs_get_error(nfs)); + exit(1); + } + + if (nfs_mount(nfs, url->server, url->path) != 0) { + fprintf(stderr, "Failed to mount nfs share : %s\n", + nfs_get_error(nfs)); + ret = 1; + goto finished; + } + + if (nfs_chdir(nfs, argv[2]) != 0) { + fprintf(stderr, "Failed to chdir to \"%s\" : %s\n", + argv[2], nfs_get_error(nfs)); + ret = 1; + goto finished; + } + + if (nfs_open(nfs, argv[3],flags, &fh)) { + fprintf(stderr, "Failed to open(): %s\n", + nfs_get_error(nfs)); + ret = 1; + goto finished; + } + + if (strlen(argv[5]) > 0) { + count = nfs_write(nfs, fh, strlen(argv[5]), argv[5]); + if (count < 0) { + fprintf(stderr, "Failed to write(): %s\n", + nfs_get_error(nfs)); + ret = 1; + goto finished; + } + } + + if (nfs_close(nfs, fh)) { + fprintf(stderr, "Failed to close(): %s\n", + nfs_get_error(nfs)); + ret = 1; + goto finished; + } + +finished: + nfs_destroy_url(url); + nfs_destroy_context(nfs); + + return ret; +} diff --git a/tests/test_0300_write.sh b/tests/test_0300_write.sh index 9e8b2c3..a94fa62 100755 --- a/tests/test_0300_write.sh +++ b/tests/test_0300_write.sh @@ -2,22 +2,18 @@ . ./functions.sh -echo "basic write test" +echo "NFSv${VERS} Basic nfs_write() test." start_share -echo -n "Create a 10M file ... " -dd if=/dev/urandom of="${TESTDIR}/orig" bs=1M count=10 2>/dev/null || failure +echo -n "test writing to a file (1) ... " +touch "${TESTDIR}/open1" +./prog_open_write "${TESTURL}/?version=${VERS}" "." /open1 O_WRONLY "kangabanga" >/dev/null || failure success -echo -n "Copy file to the NFS server ... " -../utils/nfs-cp "${TESTDIR}/orig" "${TESTURL}/copy" >/dev/null || failure -success - -echo -n "Verify the files are identical ... " -ORIGSUM=`md5sum "${TESTDIR}/orig" | cut -d " " -f 1` -COPYSUM=`md5sum "${TESTDIR}/copy" | cut -d " " -f 1` -[ "${ORIGSUM}" != "${COPYSUM}" ] && failure +echo -n "verify the data is correct ... " +echo -n "kangabanga" > "${TESTDIR}/verify1" +diff "${TESTDIR}/verify1" "${TESTDIR}/open1" || failure success stop_share