diff --git a/include/libnfs-private.h b/include/libnfs-private.h index a7e2978..222fab9 100644 --- a/include/libnfs-private.h +++ b/include/libnfs-private.h @@ -486,6 +486,8 @@ int nfs4_ftruncate_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t length, nfs_cb cb, void *private_data); int nfs4_link_async(struct nfs_context *nfs, const char *oldpath, const char *newpath, nfs_cb cb, void *private_data); +int nfs4_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, + int64_t offset, int whence, 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, diff --git a/lib/libnfs-sync.c b/lib/libnfs-sync.c index c659824..07ecf5a 100644 --- a/lib/libnfs-sync.c +++ b/lib/libnfs-sync.c @@ -1021,7 +1021,7 @@ lseek_cb(int status, struct nfs_context *nfs, void *data, void *private_data) if (status < 0) { nfs_set_error(nfs, "lseek call failed with \"%s\"", - (char *)data); + nfs_get_error(nfs)); return; } @@ -1040,7 +1040,8 @@ nfs_lseek(struct nfs_context *nfs, struct nfsfh *nfsfh, int64_t offset, int when if (nfs_lseek_async(nfs, nfsfh, offset, whence, lseek_cb, &cb_data) != 0) { - nfs_set_error(nfs, "nfs_lseek_async failed"); + nfs_set_error(nfs, "nfs_lseek_async failed. %s", + nfs_get_error(nfs)); return -1; } diff --git a/lib/libnfs.c b/lib/libnfs.c index 24c49f7..0a8bf2d 100755 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -1348,9 +1348,12 @@ nfs_lseek_async(struct nfs_context *nfs, struct nfsfh *nfsfh, int64_t offset, case NFS_V3: return nfs3_lseek_async(nfs, nfsfh, offset, whence, cb, private_data); + case NFS_V4: + return nfs4_lseek_async(nfs, nfsfh, offset, whence, + 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_v4.c b/lib/nfs_v4.c index 0a93ebb..a98a7bd 100644 --- a/lib/nfs_v4.c +++ b/lib/nfs_v4.c @@ -3495,10 +3495,6 @@ nfs4_fsync_cb(struct rpc_context *rpc, int status, void *command_data, assert(rpc->magic == RPC_CONTEXT_MAGIC); - if (res) { - nfs_increment_seqid(nfs, res->status); - } - if (check_nfs4_error(nfs, status, data, res, "FSYNC")) { free_nfs4_cb_data(data); return; @@ -3597,3 +3593,123 @@ nfs4_ftruncate_async(struct nfs_context *nfs, struct nfsfh *fh, return 0; } + +static void +nfs4_lseek_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 *fh = data->filler.blob0.val; + struct nfs_stat_64 st; + uint64_t offset; + int i; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + memcpy(&offset, data->filler.blob1.val, sizeof(uint64_t)); + + if (check_nfs4_error(nfs, status, data, res, "LSEEK")) { + 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; + + memset(&st, 0, sizeof(st)); + nfs_parse_attributes(nfs, data, &st, + garesok->obj_attributes.attr_vals.attrlist4_val, + garesok->obj_attributes.attr_vals.attrlist4_len); + + if (offset < 0 && + -offset > st.nfs_size) { + nfs_set_error(nfs, "Negative offset for lseek(" + "SEET_END)"); + data->cb(-EINVAL, nfs, &fh->offset, + data->private_data); + } else { + fh->offset = offset + st.nfs_size; + data->cb(0, nfs, &fh->offset, data->private_data); + } + + free_nfs4_cb_data(data); +} + +/* blob0.val is nfsfh + * blob1.val is offset + */ +int +nfs4_lseek_async(struct nfs_context *nfs, struct nfsfh *fh, int64_t offset, + int whence, nfs_cb cb, void *private_data) +{ + COMPOUND4args args; + nfs_argop4 op[2]; + struct nfs4_cb_data *data; + int i; + + if (whence == SEEK_SET) { + if (offset < 0) { + nfs_set_error(nfs, "Negative offset for lseek(" + "SEET_SET)"); + cb(-EINVAL, nfs, &fh->offset, private_data); + } else { + fh->offset = offset; + cb(0, nfs, &fh->offset, private_data); + } + return 0; + } + if (whence == SEEK_CUR) { + if (offset < 0 && + fh->offset < (uint64_t)(-offset)) { + nfs_set_error(nfs, "Negative offset for lseek(" + "SEET_CUR)"); + cb(-EINVAL, nfs, &fh->offset, private_data); + } else { + fh->offset += offset; + cb(0, nfs, &fh->offset, private_data); + } + return 0; + } + + data = malloc(sizeof(*data)); + if (data == NULL) { + nfs_set_error(nfs, "Out of memory."); + return -1; + } + memset(data, 0, sizeof(*data)); + + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + + data->filler.blob0.val = fh; + data->filler.blob0.free = NULL; + + data->filler.blob1.val = malloc(sizeof(uint64_t)); + if (data->filler.blob1.val == NULL) { + nfs_set_error(nfs, "Out of memory."); + free_nfs4_cb_data(data); + return -1; + } + memcpy(data->filler.blob1.val, &offset, sizeof(uint64_t)); + + i = nfs4_op_putfh(nfs, &op[0], fh); + i += nfs4_op_getattr(nfs, &op[i]); + + memset(&args, 0, sizeof(args)); + args.argarray.argarray_len = i; + args.argarray.argarray_val = op; + + if (rpc_nfs4_compound_async(nfs->rpc, nfs4_lseek_cb, &args, + data) != 0) { + free_nfs4_cb_data(data); + return -1; + } + + return 0; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index a5dc56c..2ce701d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,10 +4,10 @@ AM_CPPFLAGS = -I${srcdir}/../include -I${srcdir}/../include/nfsc \ AM_CFLAGS = $(WARN_CFLAGS) LDADD = ../lib/libnfs.la -noinst_PROGRAMS = prog_create prog_fstat prog_ftruncate prog_link prog_lstat \ - prog_mkdir prog_mknod prog_mount prog_open_read prog_open_write \ - prog_rename prog_rmdir prog_stat prog_symlink prog_timeout \ - prog_truncate prog_unlink +noinst_PROGRAMS = prog_create prog_fstat prog_ftruncate 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_symlink \ + prog_timeout prog_truncate prog_unlink EXTRA_PROGRAMS = ld_timeout CLEANFILES = ld_timeout.o ld_timeout.so diff --git a/tests/prog_lseek.c b/tests/prog_lseek.c new file mode 100644 index 0000000..3d4f279 --- /dev/null +++ b/tests/prog_lseek.c @@ -0,0 +1,178 @@ +/* -*- 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 "libnfs.h" + +void usage(void) +{ + fprintf(stderr, "Usage: prog_lseek \n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nfs_context *nfs; + struct nfsfh *nfsfh; + struct nfs_url *url; + struct nfs_stat_64 st; + uint64_t current; + + if (argc != 4) { + usage(); + } + + 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)); + exit(1); + } + + if (nfs_chdir(nfs, argv[2]) != 0) { + fprintf(stderr, "Failed to chdir to \"%s\" : %s\n", + argv[2], nfs_get_error(nfs)); + exit(1); + } + + if (nfs_open(nfs, argv[3], O_RDONLY, &nfsfh)) { + fprintf(stderr, "Failed to open file : %s\n", + nfs_get_error(nfs)); + exit(1); + } + + if (nfs_fstat64(nfs, nfsfh, &st)) { + fprintf(stderr, "Failed to stat file : %s\n", + nfs_get_error(nfs)); + exit(1); + } + + printf("File size:%" PRIu64 "\n", st.nfs_size); + + printf("Try lseek(SEEK_SET, 512)\n"); + if (nfs_lseek(nfs, nfsfh, 512, SEEK_SET, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != 512) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 512, current); + exit(1); + } + + printf("Try lseek(SEEK_CUR, 0)\n"); + if (nfs_lseek(nfs, nfsfh, 0, SEEK_CUR, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != 512) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 512, current); + exit(1); + } + + printf("Try lseek(SEEK_CUR, 4)\n"); + if (nfs_lseek(nfs, nfsfh, 4, SEEK_CUR, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != 516) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 516, current); + exit(1); + } + + printf("Try lseek(SEEK_CUR, -16)\n"); + if (nfs_lseek(nfs, nfsfh, -16, SEEK_CUR, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != 500) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 500, current); + exit(1); + } + + printf("Try lseek(SEEK_CUR, -500)\n"); + if (nfs_lseek(nfs, nfsfh, -500, SEEK_CUR, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != 0) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 0, current); + exit(1); + } + + printf("Try lseek(SEEK_CUR, -1)\n"); + if (nfs_lseek(nfs, nfsfh, -1, SEEK_CUR, ¤t) >= 0) { + fprintf(stderr, "lseek should have failed.\n"); + exit(1); + } + if (current != 0) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %d but got %" PRIu64 "\n", 0, current); + exit(1); + } + + printf("Try lseek(SEEK_END, -500)\n"); + if (nfs_lseek(nfs, nfsfh, -500, SEEK_END, ¤t)) { + fprintf(stderr, "lseek failed: %s\n", + nfs_get_error(nfs)); + exit(1); + } + if (current != st.nfs_size - 500) { + fprintf(stderr, "lseek returned wrong current offset." + "Expected %" PRIu64 " but got %" PRIu64 "\n", + st.nfs_size - 500, current); + exit(1); + } + + nfs_destroy_url(url); + nfs_close(nfs, nfsfh); + nfs_destroy_context(nfs); + + return 0; +} diff --git a/tests/test_0350_lseek.sh b/tests/test_0350_lseek.sh new file mode 100755 index 0000000..a80ba6c --- /dev/null +++ b/tests/test_0350_lseek.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +. ./functions.sh + +echo "NFSv${VERS} Basic lseek test." + +start_share + +truncate -s 1024 "${TESTDIR}/testfile" + +echo -n "test nfs_lseek() ... " +./prog_lseek "${TESTURL}/?version=${VERS}" "." /testfile > /dev/null || failure +success + + +stop_share + +exit 0 +