From 5ebd10f54c7fc51e3d0b6f399fac24d76a9f76a7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 3 Sep 2017 05:43:04 +1000 Subject: [PATCH] NFSv4: Add access()/access2() Signed-off-by: Ronnie Sahlberg --- include/libnfs-private.h | 4 ++ lib/libnfs-sync.c | 6 +- lib/libnfs.c | 8 ++- lib/nfs_v4.c | 121 +++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 12 ++-- tests/prog_access.c | 99 ++++++++++++++++++++++++++++++ tests/prog_access2.c | 101 +++++++++++++++++++++++++++++++ tests/test_0430_access.sh | 44 ++++++++++++++ tests/test_0440_access2.sh | 44 ++++++++++++++ 9 files changed, 429 insertions(+), 10 deletions(-) create mode 100644 tests/prog_access.c create mode 100644 tests/prog_access2.c create mode 100755 tests/test_0430_access.sh create mode 100755 tests/test_0440_access2.sh diff --git a/include/libnfs-private.h b/include/libnfs-private.h index abc4099..54a0c67 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -472,6 +472,10 @@ int nfs3_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, const void *buf, nfs_cb cb, void *private_data); +int nfs4_access_async(struct nfs_context *nfs, const char *path, int mode, + nfs_cb cb, void *private_data); +int nfs4_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, + void *private_data); int nfs4_chdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void *private_data); int nfs4_chmod_async_internal(struct nfs_context *nfs, const char *path, diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index 20017fa..aa1e872 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -1493,7 +1493,8 @@ nfs_access(struct nfs_context *nfs, const char *path, int mode) cb_data.is_finished = 0; if (nfs_access_async(nfs, path, mode, access_cb, &cb_data) != 0) { - nfs_set_error(nfs, "nfs_access_async failed"); + nfs_set_error(nfs, "nfs_access_async failed. %s", + nfs_get_error(nfs)); return -1; } @@ -1530,7 +1531,8 @@ nfs_access2(struct nfs_context *nfs, const char *path) cb_data.is_finished = 0; if (nfs_access2_async(nfs, path, access2_cb, &cb_data) != 0) { - nfs_set_error(nfs, "nfs_access2_async failed"); + nfs_set_error(nfs, "nfs_access2_async failed. %s", + nfs_get_error(nfs)); return -1; } diff --git a/lib/libnfs.c b/lib/libnfs.c index 5275658..3a65197 100755 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -1547,9 +1547,11 @@ nfs_access_async(struct nfs_context *nfs, const char *path, int mode, switch (nfs->version) { case NFS_V3: return nfs3_access_async(nfs, path, mode, cb, private_data); + case NFS_V4: + return nfs4_access_async(nfs, path, mode, 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; } } @@ -1561,6 +1563,8 @@ nfs_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, switch (nfs->version) { case NFS_V3: return nfs3_access2_async(nfs, path, cb, private_data); + case NFS_V4: + return nfs4_access2_async(nfs, path, cb, private_data); default: nfs_set_error(nfs, "%s does not support NFSv4", __FUNCTION__); diff --git a/lib/nfs_v4.c b/lib/nfs_v4.c index b919f47..3595167 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -1297,6 +1297,16 @@ nfs4_populate_getattr(struct nfs4_cb_data *data, nfs_argop4 *op) return nfs4_op_getfh(data->nfs, op); } +static int +nfs4_populate_access(struct nfs4_cb_data *data, nfs_argop4 *op) +{ + uint32_t mode; + + memcpy(&mode, data->filler.blob3.val, sizeof(uint32_t)); + + return nfs4_op_access(data->nfs, op, mode); +} + static void nfs4_mount_4_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) @@ -4200,3 +4210,114 @@ nfs4_fchown_async(struct nfs_context *nfs, struct nfsfh *fh, int uid, int gid, return 0; } + +static void +nfs4_access_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; + ACCESS4resok *aresok; + int i; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (check_nfs4_error(nfs, status, data, res, "ACCESS")) { + return; + } + + if ((i = nfs4_find_op(nfs, data, res, OP_ACCESS, "ACCESS")) < 0) { + return; + } + + aresok = &res->resarray.resarray_val[i].nfs_resop4_u.opaccess.ACCESS4res_u.resok4; + + /* access2 */ + if (data->filler.flags) { + int mode = 0; + + if (aresok->access & ACCESS4_READ) { + mode |= R_OK; + } + if (aresok->access & ACCESS4_MODIFY) { + mode |= W_OK; + } + if (aresok->access & ACCESS4_EXECUTE) { + mode |= X_OK; + } + data->cb(mode, nfs, NULL, data->private_data); + free_nfs4_cb_data(data); + return; + } + + if (aresok->supported != aresok->access) { + data->cb(-EACCES, nfs, NULL, data->private_data); + free_nfs4_cb_data(data); + return; + } + + data->cb(0, nfs, NULL, data->private_data); + free_nfs4_cb_data(data); +} + +static int +nfs4_access_internal(struct nfs_context *nfs, const char *path, int mode, + int is_access2, nfs_cb cb, void *private_data) +{ + struct nfs4_cb_data *data; + uint32_t m; + + data = init_cb_data_full_path(nfs, path); + if (data == NULL) { + return -1; + } + + data->cb = cb; + data->private_data = private_data; + data->filler.func = nfs4_populate_access; + data->filler.max_op = 1; + data->filler.flags = is_access2; + + data->filler.blob3.val = malloc(sizeof(uint32_t)); + if (data->filler.blob3.val == NULL) { + nfs_set_error(nfs, "Out of memory"); + return -1; + } + data->filler.blob3.free = free; + + m = 0; + if (mode & R_OK) { + m |= ACCESS4_READ; + } + if (mode & W_OK) { + m |= ACCESS4_MODIFY; + } + if (mode & X_OK) { + m |= ACCESS4_EXECUTE; + } + memcpy(data->filler.blob3.val, &m, sizeof(uint32_t)); + + if (nfs4_lookup_path_async(nfs, data, nfs4_access_cb) < 0) { + free_nfs4_cb_data(data); + return -1; + } + + return 0; +} + +int +nfs4_access_async(struct nfs_context *nfs, const char *path, int mode, + nfs_cb cb, void *private_data) +{ + return nfs4_access_internal(nfs, path, mode, 0, + cb, private_data); +} + +int +nfs4_access2_async(struct nfs_context *nfs, const char *path, nfs_cb cb, + void *private_data) +{ + return nfs4_access_internal(nfs, path, R_OK|W_OK|X_OK, 1, + cb, private_data); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 6bde99b..9e7c223 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,12 +4,12 @@ AM_CPPFLAGS = -I${srcdir}/../include -I${srcdir}/../include/nfsc \ AM_CFLAGS = $(WARN_CFLAGS) LDADD = ../lib/libnfs.la -noinst_PROGRAMS = prog_chmod prog_chown prog_create prog_fchmod prog_fchown \ - prog_fstat prog_ftruncate prog_lchmod prog_lchown prog_link \ - prog_lseek prog_lstat prog_mkdir prog_mknod prog_mount \ - prog_open_read prog_open_write prog_rename prog_rmdir \ - prog_stat prog_statvfs prog_symlink prog_timeout prog_truncate \ - prog_unlink +noinst_PROGRAMS = prog_access prog_access2 prog_chmod prog_chown prog_create \ + prog_fchmod prog_fchown prog_fstat prog_ftruncate prog_lchmod \ + prog_lchown prog_link prog_lseek prog_lstat prog_mkdir \ + prog_mknod prog_mount prog_open_read prog_open_write \ + prog_rename prog_rmdir prog_stat prog_statvfs prog_symlink \ + prog_timeout prog_truncate prog_unlink EXTRA_PROGRAMS = ld_timeout CLEANFILES = ld_timeout.o ld_timeout.so diff --git a/tests/prog_access.c b/tests/prog_access.c new file mode 100644 index 0000000..57d2653 --- /dev/null +++ b/tests/prog_access.c @@ -0,0 +1,99 @@ +/* -*- 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_access \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; + + if (argc != 5) { + usage(); + } + + if (strstr(argv[4], "R_OK")) { + flags |= R_OK; + } + if (strstr(argv[4], "W_OK")) { + flags |= W_OK; + } + if (strstr(argv[4], "X_OK")) { + flags |= X_OK; + } + + 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_access(nfs, argv[3], flags)) { + fprintf(stderr, "Failed to access(): %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/prog_access2.c b/tests/prog_access2.c new file mode 100644 index 0000000..45820a0 --- /dev/null +++ b/tests/prog_access2.c @@ -0,0 +1,101 @@ +/* -*- 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_access \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; + + if (argc != 5) { + usage(); + } + + if (strstr(argv[4], "R_OK")) { + flags |= R_OK; + } + if (strstr(argv[4], "W_OK")) { + flags |= W_OK; + } + if (strstr(argv[4], "X_OK")) { + flags |= X_OK; + } + + 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 ((ret = nfs_access2(nfs, argv[3])) != flags) { + fprintf(stderr, "access2() failed. Received mode %08x but " + "expected %08x: %s\n", ret, flags, + nfs_get_error(nfs)); + ret = 1; + goto finished; + } + ret = 0; + +finished: + nfs_destroy_url(url); + nfs_destroy_context(nfs); + + return ret; +} diff --git a/tests/test_0430_access.sh b/tests/test_0430_access.sh new file mode 100755 index 0000000..7a7c712 --- /dev/null +++ b/tests/test_0430_access.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +. ./functions.sh + +echo "NFSv${VERS} Basic access tests." + +start_share + +dd if=/dev/zero of=testdata/testfile count=1 bs=32768 2>/dev/null + +echo -n "test access(R_OK) on a readable file ... " +chmod 400 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile R_OK || failure +success + +echo -n "test access(W_OK) on a writeable file ... " +chmod 200 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile W_OK || failure +success + +echo -n "test access(X_OK) on an executable file ... " +chmod 100 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile X_OK || failure +success + +echo -n "test access(R_OK) on a non-readable file ... " +chmod 300 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile R_OK 2>/dev/null && failure +success + +echo -n "test access(W_OK) on a non-writeable file ... " +chmod 500 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile W_OK 2>/dev/null && failure +success + +echo -n "test access(X_OK) on a non-executable file ... " +chmod 600 "${TESTDIR}/testfile" +./prog_access "${TESTURL}/?version=${VERS}" "." /testfile X_OK 2>/dev/null && failure +success + + +stop_share + +exit 0 diff --git a/tests/test_0440_access2.sh b/tests/test_0440_access2.sh new file mode 100755 index 0000000..aa8f83f --- /dev/null +++ b/tests/test_0440_access2.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +. ./functions.sh + +echo "NFSv${VERS} Basic access2 tests." + +start_share + +dd if=/dev/zero of=testdata/testfile count=1 bs=32768 2>/dev/null + +echo -n "test access2(R_OK) on a readable file ... " +chmod 400 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile R_OK || failure +success + +echo -n "test access2(W_OK) on a writeable file ... " +chmod 200 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile W_OK || failure +success + +echo -n "test access2(X_OK) on an executable file ... " +chmod 100 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile X_OK || failure +success + +echo -n "test access2(R_OK) on a non-readable file ... " +chmod 300 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile R_OK 2>/dev/null && failure +success + +echo -n "test access2(W_OK) on a non-writeable file ... " +chmod 500 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile W_OK 2>/dev/null && failure +success + +echo -n "test access2(X_OK) on a non-executable file ... " +chmod 600 "${TESTDIR}/testfile" +./prog_access2 "${TESTURL}/?version=${VERS}" "." /testfile X_OK 2>/dev/null && failure +success + + +stop_share + +exit 0