--- /dev/null
+/*
+ nfs library for dbench
+
+ Copyright (C) 2008 by Ronnie Sahlberg (ronniesahlberg@gmail.com)
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "mount.h"
+#include "nfs.h"
+#include "libnfs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+
+typedef struct _data_t {
+ const char *dptr;
+ int dsize;
+} data_t;
+
+typedef struct _tree_t {
+ data_t key;
+ data_t fh;
+ struct _tree_t *parent;
+ struct _tree_t *left;
+ struct _tree_t *right;
+} tree_t;
+
+
+struct nfsio {
+ CLIENT *mnt_clnt;
+ CLIENT *nfs_clnt;
+ tree_t *fhandles;
+};
+
+static void free_node(tree_t *t)
+{
+ free(discard_const(t->key.dptr));
+ free(discard_const(t->fh.dptr));
+ free(t);
+}
+
+static tree_t *find_fhandle(tree_t *tree, const char *key)
+{
+ int i;
+
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ i = strcmp(key, tree->key.dptr);
+ if (i == 0) {
+ return tree;
+ }
+ if (i < 0) {
+ return find_fhandle(tree->left, key);
+ }
+
+ return find_fhandle(tree->right, key);
+}
+
+static data_t *lookup_fhandle(struct nfsio *nfsio, const char *name)
+{
+ tree_t *t;
+
+ while (name[0] == '.') name++;
+
+ if (name[0] == 0) {
+ name = "/";
+ }
+
+ t = find_fhandle(nfsio->fhandles, name);
+ if (t == NULL) {
+ return NULL;
+ }
+
+ return &t->fh;
+}
+
+static void delete_fhandle(struct nfsio *nfsio, const char *name)
+{
+ tree_t *t;
+
+ while (name[0] == '.') name++;
+
+ t = find_fhandle(nfsio->fhandles, name);
+ if (t == NULL) {
+ return;
+ }
+
+ /* we have a left child */
+ if (t->left) {
+ tree_t *tmp_tree;
+
+ for(tmp_tree=t->left;tmp_tree->right;tmp_tree=tmp_tree->right)
+ ;
+ tmp_tree->right = t->right;
+
+ if (t->parent == NULL) {
+ nfsio->fhandles = tmp_tree;
+ free_node(t);
+ return;
+ }
+
+ if (t->parent->left == t) {
+ t->parent->left = t->left;
+ free_node(t);
+ return;
+ }
+
+ t->parent->right = t->left;
+ free_node(t);
+ return;
+ }
+
+ /* we only have a right child */
+ if (t->right) {
+ tree_t *tmp_tree;
+
+ for(tmp_tree=t->right;tmp_tree->left;tmp_tree=tmp_tree->left)
+ ;
+ tmp_tree->left = t->left;
+
+ if (t->parent == NULL) {
+ nfsio->fhandles = tmp_tree;
+ free_node(t);
+ return;
+ }
+
+ if (t->parent->left == t) {
+ t->parent->left = t->right;
+ free_node(t);
+ return;
+ }
+
+ t->parent->right = t->right;
+ free_node(t);
+ return;
+ }
+
+ /* we are a leaf node */
+ if (t->parent == NULL) {
+ nfsio->fhandles = NULL;
+ } else {
+ if (t->parent->left == t) {
+ t->parent->left = NULL;
+ } else {
+ t->parent->right = NULL;
+ }
+ }
+ free_node(t);
+ return;
+}
+
+static void insert_fhandle(struct nfsio *nfsio, const char *name, const char *fhandle, int length)
+{
+ tree_t *tmp_t;
+ tree_t *t;
+ int i;
+
+ while (name[0] == '.') name++;
+
+ t = malloc(sizeof(tree_t));
+ if (t == NULL) {
+ fprintf(stderr, "MALLOC failed to allocate tree_t in insert_fhandle\n");
+ exit(10);
+ }
+
+ t->key.dptr = strdup(name);
+ if (t->key.dptr == NULL) {
+ fprintf(stderr, "STRDUP failed to allocate key in insert_fhandle\n");
+ exit(10);
+ }
+ t->key.dsize = strlen(name);
+
+
+ t->fh.dptr = malloc(length);
+ if (t->key.dptr == NULL) {
+ fprintf(stderr, "MALLOC failed to allocate fhandle in insert_fhandle\n");
+ exit(10);
+ }
+ memcpy(discard_const(t->fh.dptr), fhandle, length);
+ t->fh.dsize = length;
+
+ t->left = NULL;
+ t->right = NULL;
+ t->parent = NULL;
+
+ if (nfsio->fhandles == NULL) {
+ nfsio->fhandles = t;
+ return;
+ }
+
+ tmp_t = nfsio->fhandles;
+again:
+ i = strcmp(t->key.dptr, tmp_t->key.dptr);
+ if (i == 0) {
+ free(discard_const(tmp_t->fh.dptr));
+ tmp_t->fh.dsize = t->fh.dsize;
+ tmp_t->fh.dptr = t->fh.dptr;
+ free(discard_const(t->key.dptr));
+ free(t);
+ return;
+ }
+ if (i < 0) {
+ if (tmp_t->left == NULL) {
+ tmp_t->left = t;
+ t->parent = tmp_t;
+ return;
+ }
+ tmp_t = tmp_t->left;
+ goto again;
+ }
+ if (tmp_t->right == NULL) {
+ tmp_t->right = t;
+ t->parent = tmp_t;
+ return;
+ }
+ tmp_t = tmp_t->right;
+ goto again;
+}
+
+
+struct nfs_errors {
+ const char *err;
+ int idx;
+};
+
+static const struct nfs_errors nfs_errors[] = {
+ {"NFS3_OK", 0},
+ {"NFS3ERR_PERM", 1},
+ {"NFS3ERR_NOENT", 2},
+ {"NFS3ERR_IO", 5},
+ {"NFS3ERR_NXIO", 6},
+ {"NFS3ERR_ACCES", 13},
+ {"NFS3ERR_EXIST", 17},
+ {"NFS3ERR_XDEV", 18},
+ {"NFS3ERR_NODEV", 19},
+ {"NFS3ERR_NOTDIR", 20},
+ {"NFS3ERR_ISDIR", 21},
+ {"NFS3ERR_INVAL", 22},
+ {"NFS3ERR_FBIG", 27},
+ {"NFS3ERR_NOSPC", 28},
+ {"NFS3ERR_ROFS", 30},
+ {"NFS3ERR_MLINK", 31},
+ {"NFS3ERR_NAMETOOLONG", 63},
+ {"NFS3ERR_NOTEMPTY", 66},
+ {"NFS3ERR_DQUOT", 69},
+ {"NFS3ERR_STALE", 70},
+ {"NFS3ERR_REMOTE", 71},
+ {"NFS3ERR_BADHANDLE", 10001},
+ {"NFS3ERR_NOT_SYNC", 10002},
+ {"NFS3ERR_BAD_COOKIE", 10003},
+ {"NFS3ERR_NOTSUPP", 10004},
+ {"NFS3ERR_TOOSMALL", 10005},
+ {"NFS3ERR_SERVERFAULT", 10006},
+ {"NFS3ERR_BADTYPE", 10007},
+ {"NFS3ERR_JUKEBOX", 10008},
+};
+
+
+
+const char *nfs_error(int error)
+{
+ unsigned int i;
+
+ for(i=0;i<sizeof(nfs_errors)/sizeof(struct nfs_errors);i++) {
+ if (error == nfs_errors[i].idx) {
+ return nfs_errors[i].err;
+ }
+ }
+ return "Unknown NFS error";
+}
+
+
+
+
+void nfsio_disconnect(struct nfsio *nfsio)
+{
+ if (nfsio->mnt_clnt != NULL) {
+ clnt_destroy(nfsio->mnt_clnt);
+ nfsio->mnt_clnt = NULL;
+ }
+ if (nfsio->nfs_clnt != NULL) {
+ clnt_destroy(nfsio->nfs_clnt);
+ nfsio->nfs_clnt = NULL;
+ }
+ // qqq free the tree*/
+
+ free(nfsio);
+}
+
+
+
+
+struct nfsio *nfsio_connect(const char *server, const char *export, const char *protocol)
+{
+ dirpath mountdir=discard_const(export);
+ struct nfsio *nfsio;
+ mountres3 *mountres;
+ fhandle3 *fh;
+
+ nfsio = malloc(sizeof(struct nfsio));
+ if (nfsio == NULL) {
+ fprintf(stderr, "Failed to malloc nfsio\n");
+ return NULL;
+ }
+ bzero(nfsio, sizeof(struct nfsio));
+
+ nfsio->mnt_clnt = clnt_create(server, MOUNT_PROGRAM, MOUNT_V3, "udp");
+ if (nfsio->mnt_clnt == NULL) {
+ printf("ERROR: failed to connect to MOUNT daemon on %s\n", server);
+ nfsio_disconnect(nfsio);
+ return NULL;
+ }
+ nfsio->mnt_clnt->cl_auth = authunix_create_default();
+
+ mountres=mountproc3_mnt_3(&mountdir, nfsio->mnt_clnt);
+ if (mountres == NULL) {
+ printf("ERROR: failed to call the MNT procedure\n");
+ nfsio_disconnect(nfsio);
+ return NULL;
+ }
+ if (mountres->fhs_status != MNT3_OK) {
+ printf("ERROR: Server returned error %d when trying to MNT\n",mountres->fhs_status);
+ nfsio_disconnect(nfsio);
+ return NULL;
+ }
+
+ fh = &mountres->mountres3_u.mountinfo.fhandle;
+ insert_fhandle(nfsio, "/", fh->fhandle3_val, fh->fhandle3_len);
+
+ nfsio->nfs_clnt = clnt_create(server, NFS_PROGRAM, NFS_V3, protocol);
+ if (nfsio->nfs_clnt == NULL) {
+ fprintf(stderr, "Failed to initialize nfs client structure\n");
+ nfsio_disconnect(nfsio);
+ return NULL;
+ }
+ nfsio->nfs_clnt->cl_auth = authunix_create_default();
+
+ return nfsio;
+}
+
+
+nfsstat3 nfsio_getattr(struct nfsio *nfsio, const char *name, fattr3 *attributes)
+{
+ struct GETATTR3args GETATTR3args;
+ struct GETATTR3res *GETATTR3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_getattr\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ GETATTR3args.object.data.data_len = fh->dsize;
+ GETATTR3args.object.data.data_val = discard_const(fh->dptr);
+
+ GETATTR3res = nfsproc3_getattr_3(&GETATTR3args, nfsio->nfs_clnt);
+
+ if (GETATTR3res == NULL) {
+ fprintf(stderr, "nfsproc3_getattr_3 failed in getattr\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (GETATTR3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_getattr_3 failed in getattr. status:%d\n", GETATTR3res->status);
+ return GETATTR3res->status;
+ }
+
+ if (attributes) {
+ memcpy(attributes, &GETATTR3res->GETATTR3res_u.resok.obj_attributes, sizeof(fattr3));
+ }
+
+ return NFS3_OK;
+}
+
+nfsstat3 nfsio_lookup(struct nfsio *nfsio, const char *name, fattr3 *attributes)
+{
+
+ struct LOOKUP3args LOOKUP3args;
+ struct LOOKUP3res *LOOKUP3res;
+ char *tmp_name = NULL;
+ int ret = NFS3_OK;
+ data_t *fh;
+ char *ptr;
+
+ tmp_name = strdup(name);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_lookup\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_lookup\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle for '%s' in nfsio_lookup\n", tmp_name);
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ LOOKUP3args.what.dir.data.data_len = fh->dsize;
+ LOOKUP3args.what.dir.data.data_val = discard_const(fh->dptr);
+ LOOKUP3args.what.name = ptr;
+
+ LOOKUP3res = nfsproc3_lookup_3(&LOOKUP3args, nfsio->nfs_clnt);
+
+ if (LOOKUP3res == NULL) {
+ fprintf(stderr, "nfsproc3_lookup_3 failed in lookup\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (LOOKUP3res->status != NFS3_OK) {
+ ret = LOOKUP3res->status;
+ goto finished;
+ }
+
+
+ insert_fhandle(nfsio, name,
+ LOOKUP3res->LOOKUP3res_u.resok.object.data.data_val,
+ LOOKUP3res->LOOKUP3res_u.resok.object.data.data_len);
+
+ if (attributes) {
+ memcpy(attributes, &LOOKUP3res->LOOKUP3res_u.resok.obj_attributes.post_op_attr_u.attributes, sizeof(fattr3));
+ }
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+nfsstat3 nfsio_access(struct nfsio *nfsio, const char *name, uint32 desired, uint32 *access)
+{
+
+ struct ACCESS3args ACCESS3args;
+ struct ACCESS3res *ACCESS3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_access\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ ACCESS3args.object.data.data_val = discard_const(fh->dptr);
+ ACCESS3args.object.data.data_len = fh->dsize;
+ ACCESS3args.access = desired;
+
+ ACCESS3res = nfsproc3_access_3(&ACCESS3args, nfsio->nfs_clnt);
+
+ if (ACCESS3res == NULL) {
+ fprintf(stderr, "nfsproc3_access_3 failed in access\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (ACCESS3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_access_3 failed. status:%d\n",
+ACCESS3res->status);
+ return ACCESS3res->status;
+ }
+
+ if (access) {
+ *access = ACCESS3res->ACCESS3res_u.resok.access;
+ }
+
+ return NFS3_OK;
+}
+
+
+
+nfsstat3 nfsio_create(struct nfsio *nfsio, const char *name)
+{
+
+ struct CREATE3args CREATE3args;
+ struct CREATE3res *CREATE3res;
+ char *tmp_name = NULL;
+ data_t *fh;
+ char *ptr;
+ int ret = NFS3_OK;
+
+ tmp_name = strdup(name);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_create\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_create\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_create\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ CREATE3args.where.dir.data.data_len = fh->dsize;
+ CREATE3args.where.dir.data.data_val = discard_const(fh->dptr);
+ CREATE3args.where.name = ptr;
+
+ CREATE3args.how.mode = UNCHECKED;
+ CREATE3args.how.createhow3_u.obj_attributes.mode.set_it = TRUE;
+ CREATE3args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = 0777;
+ CREATE3args.how.createhow3_u.obj_attributes.uid.set_it = TRUE;
+ CREATE3args.how.createhow3_u.obj_attributes.uid.set_uid3_u.uid = 0;
+ CREATE3args.how.createhow3_u.obj_attributes.gid.set_it = TRUE;
+ CREATE3args.how.createhow3_u.obj_attributes.gid.set_gid3_u.gid = 0;
+ CREATE3args.how.createhow3_u.obj_attributes.size.set_it = FALSE;
+ CREATE3args.how.createhow3_u.obj_attributes.atime.set_it = FALSE;
+ CREATE3args.how.createhow3_u.obj_attributes.mtime.set_it = FALSE;
+
+ CREATE3res = nfsproc3_create_3(&CREATE3args, nfsio->nfs_clnt);
+
+ if (CREATE3res == NULL) {
+ fprintf(stderr, "nfsproc3_create_3 failed in nfsio_create\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (CREATE3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_create_3 failed in nfsio_create. status:%d\n", CREATE3res->status);
+ ret = CREATE3res->status;
+ goto finished;
+ }
+
+
+ insert_fhandle(nfsio, name,
+ CREATE3res->CREATE3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
+ CREATE3res->CREATE3res_u.resok.obj.post_op_fh3_u.handle.data.data_len);
+
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+nfsstat3 nfsio_remove(struct nfsio *nfsio, const char *name)
+{
+
+ struct REMOVE3args REMOVE3args;
+ struct REMOVE3res *REMOVE3res;
+ int ret = NFS3_OK;
+ char *tmp_name = NULL;
+ data_t *fh;
+ char *ptr;
+
+ tmp_name = strdup(name);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_remove\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_remove\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_remove\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ REMOVE3args.object.dir.data.data_len = fh->dsize;
+ REMOVE3args.object.dir.data.data_val = discard_const(fh->dptr);
+ REMOVE3args.object.name = ptr;
+
+ REMOVE3res = nfsproc3_remove_3(&REMOVE3args, nfsio->nfs_clnt);
+
+ if (REMOVE3res == NULL) {
+ fprintf(stderr, "nfsproc3_remove_3 failed in nfsio_remove\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (REMOVE3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_remove_3 failed in nfsio_remove. status:%d\n", REMOVE3res->status);
+ ret = REMOVE3res->status;
+ goto finished;
+ }
+
+
+ delete_fhandle(nfsio, name);
+
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+nfsstat3 nfsio_write(struct nfsio *nfsio, const char *name, char *buf, uint32 offset, int len, int stable)
+{
+ struct WRITE3args WRITE3args;
+ struct WRITE3res *WRITE3res;
+ int ret = NFS3_OK;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_write\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ WRITE3args.file.data.data_len = fh->dsize;
+ WRITE3args.file.data.data_val = discard_const(fh->dptr);
+ WRITE3args.offset = offset;
+ WRITE3args.count = len;
+ WRITE3args.stable = stable;
+ WRITE3args.data.data_len = len;
+ WRITE3args.data.data_val = buf;
+
+
+ WRITE3res = nfsproc3_write_3(&WRITE3args, nfsio->nfs_clnt);
+
+ if (WRITE3res == NULL) {
+ fprintf(stderr, "nfsproc3_write_3 failed in nfsio_write\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (WRITE3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_write_3 failed in getattr. status:%d\n", WRITE3res->status);
+ ret = WRITE3res->status;
+ }
+
+finished:
+ return ret;
+}
+
+nfsstat3 nfsio_read(struct nfsio *nfsio, const char *name, char *buf, uint32 offset, int len, int *count, int *eof)
+{
+ struct READ3args READ3args;
+ struct READ3res *READ3res;
+ int ret = NFS3_OK;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_read\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ READ3args.file.data.data_len = fh->dsize;
+ READ3args.file.data.data_val = discard_const(fh->dptr);
+ READ3args.offset = offset;
+ READ3args.count = len;
+
+ READ3res = nfsproc3_read_3(&READ3args, nfsio->nfs_clnt);
+
+ if (READ3res == NULL) {
+ fprintf(stderr, "nfsproc3_read_3 failed in nfsio_read\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (READ3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_read_3 failed in nfsio_read. status:%d\n", READ3res->status);
+ ret = READ3res->status;
+ goto finished;
+ }
+
+ if (count) {
+ *count = READ3res->READ3res_u.resok.count;
+ }
+ if (eof) {
+ *eof = READ3res->READ3res_u.resok.eof;
+ }
+ memcpy(buf, &READ3res->READ3res_u.resok.data.data_val,
+ READ3res->READ3res_u.resok.count);
+
+
+finished:
+ return ret;
+}
+
+
+nfsstat3 nfsio_commit(struct nfsio *nfsio, const char *name)
+{
+ struct COMMIT3args COMMIT3args;
+ struct COMMIT3res *COMMIT3res;
+ int ret = NFS3_OK;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_commit\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ COMMIT3args.file.data.data_len = fh->dsize;
+ COMMIT3args.file.data.data_val = discard_const(fh->dptr);
+ COMMIT3args.offset = 0;
+ COMMIT3args.count = 0;
+
+
+ COMMIT3res = nfsproc3_commit_3(&COMMIT3args, nfsio->nfs_clnt);
+
+ if (COMMIT3res == NULL) {
+ fprintf(stderr, "nfsproc3_commit_3 failed in nfsio_commit\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (COMMIT3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_commit_3 failed in nfsio_commit. status:%d\n", COMMIT3res->status);
+ ret = COMMIT3res->status;
+ goto finished;
+ }
+
+finished:
+ return ret;
+}
+
+nfsstat3 nfsio_fsinfo(struct nfsio *nfsio)
+{
+ struct FSINFO3args FSINFO3args;
+ struct FSINFO3res *FSINFO3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, "/");
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_fsinfo\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ FSINFO3args.fsroot.data.data_len = fh->dsize;
+ FSINFO3args.fsroot.data.data_val = discard_const(fh->dptr);
+
+ FSINFO3res = nfsproc3_fsinfo_3(&FSINFO3args, nfsio->nfs_clnt);
+
+ if (FSINFO3res == NULL) {
+ fprintf(stderr, "nfsproc3_fsinfo_3 failed in nfsio_fsinfo\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (FSINFO3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_fsinfo_3 failed in nfsio_fsinfo. status:%d\n", FSINFO3res->status);
+ return FSINFO3res->status;
+ }
+
+ return NFS3_OK;
+}
+
+
+nfsstat3 nfsio_fsstat(struct nfsio *nfsio)
+{
+ struct FSSTAT3args FSSTAT3args;
+ struct FSSTAT3res *FSSTAT3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, "/");
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_fsstat\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ FSSTAT3args.fsroot.data.data_len = fh->dsize;
+ FSSTAT3args.fsroot.data.data_val = discard_const(fh->dptr);
+
+ FSSTAT3res = nfsproc3_fsstat_3(&FSSTAT3args, nfsio->nfs_clnt);
+
+ if (FSSTAT3res == NULL) {
+ fprintf(stderr, "nfsproc3_fsstat_3 failed in nfsio_fsstat\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (FSSTAT3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_fsstat_3 failed in nfsio_fsstat. status:%d\n", FSSTAT3res->status);
+ return FSSTAT3res->status;
+ }
+
+ return NFS3_OK;
+}
+
+nfsstat3 nfsio_pathconf(struct nfsio *nfsio, char *name)
+{
+ struct PATHCONF3args PATHCONF3args;
+ struct PATHCONF3res *PATHCONF3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_pathconf\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ PATHCONF3args.object.data.data_len = fh->dsize;
+ PATHCONF3args.object.data.data_val = discard_const(fh->dptr);
+
+ PATHCONF3res = nfsproc3_pathconf_3(&PATHCONF3args, nfsio->nfs_clnt);
+
+ if (PATHCONF3res == NULL) {
+ fprintf(stderr, "nfsproc3_pathconf_3 failed in nfsio_pathconf\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (PATHCONF3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_pathconf_3 failed in nfsio_pathconf. status:%d\n", PATHCONF3res->status);
+ return PATHCONF3res->status;
+ }
+
+ return NFS3_OK;
+}
+
+
+nfsstat3 nfsio_symlink(struct nfsio *nfsio, const char *old, const char *new)
+{
+
+ struct SYMLINK3args SYMLINK3args;
+ struct SYMLINK3res *SYMLINK3res;
+ int ret = NFS3_OK;
+ char *tmp_name = NULL;
+ data_t *fh;
+ char *ptr;
+
+ tmp_name = strdup(old);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_symlink\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_symlink\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_symlink\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ SYMLINK3args.where.dir.data.data_len = fh->dsize;
+ SYMLINK3args.where.dir.data.data_val = discard_const(fh->dptr);
+ SYMLINK3args.where.name = ptr;
+
+ SYMLINK3args.symlink.symlink_attributes.mode.set_it = TRUE;
+ SYMLINK3args.symlink.symlink_attributes.mode.set_mode3_u.mode = 0777;
+ SYMLINK3args.symlink.symlink_attributes.uid.set_it = TRUE;
+ SYMLINK3args.symlink.symlink_attributes.uid.set_uid3_u.uid= 0;
+ SYMLINK3args.symlink.symlink_attributes.gid.set_it = TRUE;
+ SYMLINK3args.symlink.symlink_attributes.gid.set_gid3_u.gid = 0;
+ SYMLINK3args.symlink.symlink_attributes.size.set_it = FALSE;
+ SYMLINK3args.symlink.symlink_attributes.atime.set_it = FALSE;
+ SYMLINK3args.symlink.symlink_attributes.mtime.set_it = FALSE;
+ SYMLINK3args.symlink.symlink_data = discard_const(new);
+
+
+ SYMLINK3res = nfsproc3_symlink_3(&SYMLINK3args, nfsio->nfs_clnt);
+
+ if (SYMLINK3res == NULL) {
+ fprintf(stderr, "nfsproc3_symlink_3 failed in nfsio_symlink\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (SYMLINK3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_symlink_3 failed in nfsio_symlink. status:%d\n", SYMLINK3res->status);
+ ret = SYMLINK3res->status;
+ goto finished;
+ }
+
+
+ insert_fhandle(nfsio, old,
+ SYMLINK3res->SYMLINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
+ SYMLINK3res->SYMLINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_len);
+
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+nfsstat3 nfsio_link(struct nfsio *nfsio, const char *old, const char *new)
+{
+
+ struct LINK3args LINK3args;
+ struct LINK3res *LINK3res;
+ int ret = NFS3_OK;
+ char *tmp_name = NULL;
+ data_t *fh, *new_fh;
+ char *ptr;
+
+ tmp_name = strdup(old);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_link\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_link\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_link\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ new_fh = lookup_fhandle(nfsio, new);
+ if (new_fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_link\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ LINK3args.file.data.data_len = new_fh->dsize;
+ LINK3args.file.data.data_val = discard_const(new_fh->dptr);
+
+
+ LINK3args.link.dir.data.data_len = fh->dsize;
+ LINK3args.link.dir.data.data_val = discard_const(fh->dptr);
+ LINK3args.link.name = ptr;
+
+ LINK3res = nfsproc3_link_3(&LINK3args, nfsio->nfs_clnt);
+
+ if (LINK3res == NULL) {
+ fprintf(stderr, "nfsproc3_link_3 failed in nfsio_link\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (LINK3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_link_3 failed in nfsio_link. status:%d\n", LINK3res->status);
+ ret = LINK3res->status;
+ goto finished;
+ }
+
+
+// insert_fhandle(nfsio, old,
+// LINK3res->LINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
+// LINK3res->LINK3res_u.resok.obj.post_op_fh3_u.handle.data.data_len);
+
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+
+nfsstat3 nfsio_readlink(struct nfsio *nfsio, char *name, char **link_name)
+{
+ struct READLINK3args READLINK3args;
+ struct READLINK3res *READLINK3res;
+ data_t *fh;
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle in nfsio_readlink\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+
+ READLINK3args.symlink.data.data_len = fh->dsize;
+ READLINK3args.symlink.data.data_val = discard_const(fh->dptr);
+
+ READLINK3res = nfsproc3_readlink_3(&READLINK3args, nfsio->nfs_clnt);
+
+ if (READLINK3res == NULL) {
+ fprintf(stderr, "nfsproc3_readlink_3 failed in nfsio_readlink\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ if (READLINK3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_readlink_3 failed in nfsio_readlink. status:%d\n", READLINK3res->status);
+ return READLINK3res->status;
+ }
+
+ if (link_name) {
+ *link_name = strdup(READLINK3res->READLINK3res_u.resok.data);
+ }
+
+ return NFS3_OK;
+}
+
+
+nfsstat3 nfsio_rmdir(struct nfsio *nfsio, const char *name)
+{
+
+ struct RMDIR3args RMDIR3args;
+ struct RMDIR3res *RMDIR3res;
+ int ret = NFS3_OK;
+ char *tmp_name = NULL;
+ data_t *fh;
+ char *ptr;
+
+ tmp_name = strdup(name);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_rmdir\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_rmdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_rmdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ RMDIR3args.object.dir.data.data_len = fh->dsize;
+ RMDIR3args.object.dir.data.data_val = discard_const(fh->dptr);
+ RMDIR3args.object.name = ptr;
+
+ RMDIR3res = nfsproc3_rmdir_3(&RMDIR3args, nfsio->nfs_clnt);
+
+ if (RMDIR3res == NULL) {
+ fprintf(stderr, "nfsproc3_rmdir_3 failed in nfsio_rmdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (RMDIR3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_rmdir_3(%s) failed in nfsio_rmdir. status:%s(%d)\n", name, nfs_error(RMDIR3res->status), RMDIR3res->status);
+ ret = RMDIR3res->status;
+ goto finished;
+ }
+
+
+ delete_fhandle(nfsio, name);
+
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+
+nfsstat3 nfsio_mkdir(struct nfsio *nfsio, const char *name)
+{
+
+ struct MKDIR3args MKDIR3args;
+ struct MKDIR3res *MKDIR3res;
+ int ret = NFS3_OK;
+ char *tmp_name = NULL;
+ data_t *fh;
+ char *ptr;
+
+ tmp_name = strdup(name);
+ if (tmp_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_mkdir\n");
+ return NFS3ERR_SERVERFAULT;
+ }
+
+ ptr = rindex(tmp_name, '/');
+ if (ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_mkdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *ptr = 0;
+ ptr++;
+
+ fh = lookup_fhandle(nfsio, tmp_name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_mkdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ MKDIR3args.where.dir.data.data_len = fh->dsize;
+ MKDIR3args.where.dir.data.data_val = discard_const(fh->dptr);
+ MKDIR3args.where.name = ptr;
+
+ MKDIR3args.attributes.mode.set_it = TRUE;
+ MKDIR3args.attributes.mode.set_mode3_u.mode = 0777;
+ MKDIR3args.attributes.uid.set_it = TRUE;
+ MKDIR3args.attributes.uid.set_uid3_u.uid = 0;
+ MKDIR3args.attributes.gid.set_it = TRUE;
+ MKDIR3args.attributes.gid.set_gid3_u.gid = 0;
+ MKDIR3args.attributes.size.set_it = FALSE;
+ MKDIR3args.attributes.atime.set_it = FALSE;
+ MKDIR3args.attributes.mtime.set_it = FALSE;
+
+ MKDIR3res = nfsproc3_mkdir_3(&MKDIR3args, nfsio->nfs_clnt);
+
+ if (MKDIR3res == NULL) {
+ fprintf(stderr, "nfsproc3_mkdir_3 failed in nfsio_mkdir\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (MKDIR3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_mkdir_3(%s) failed in nfsio_mkdir. status:%s(%d)\n", name, nfs_error(MKDIR3res->status), MKDIR3res->status);
+ ret = MKDIR3res->status;
+ goto finished;
+ }
+
+ insert_fhandle(nfsio, name,
+ MKDIR3res->MKDIR3res_u.resok.obj.post_op_fh3_u.handle.data.data_val,
+ MKDIR3res->MKDIR3res_u.resok.obj.post_op_fh3_u.handle.data.data_len);
+
+finished:
+ if (tmp_name) {
+ free(tmp_name);
+ }
+ return ret;
+}
+
+
+nfsstat3 nfsio_readdirplus(struct nfsio *nfsio, const char *name, nfs3_dirent_cb cb, void *private_data)
+{
+ struct READDIRPLUS3args READDIRPLUS3args;
+ struct READDIRPLUS3res *READDIRPLUS3res;
+ int ret = NFS3_OK;
+ data_t *fh;
+ entryplus3 *e, *last_e = NULL;
+ char *dir = NULL;
+
+ dir = strdup(name);
+ while(strlen(dir)){
+ if(dir[strlen(dir)-1] != '/'){
+ break;
+ }
+ dir[strlen(dir)-1] = 0;
+ }
+
+ fh = lookup_fhandle(nfsio, name);
+ if (fh == NULL) {
+ fprintf(stderr, "failed to fetch handle for '%s' in nfsio_readdirplus\n", name);
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ READDIRPLUS3args.dir.data.data_len = fh->dsize;
+ READDIRPLUS3args.dir.data.data_val = discard_const(fh->dptr);
+ READDIRPLUS3args.cookie = 0;
+ bzero(&READDIRPLUS3args.cookieverf, NFS3_COOKIEVERFSIZE);
+ READDIRPLUS3args.dircount = 6000;
+ READDIRPLUS3args.maxcount = 8192;
+
+again:
+ READDIRPLUS3res = nfsproc3_readdirplus_3(&READDIRPLUS3args, nfsio->nfs_clnt);
+
+ if (READDIRPLUS3res == NULL) {
+ fprintf(stderr, "nfsproc3_readdirplus_3 failed in readdirplus\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (READDIRPLUS3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_readdirplus_3 failed in readdirplus. status:%d\n", READDIRPLUS3res->status);
+ ret = READDIRPLUS3res->status;
+ goto finished;
+ }
+
+ for(e = READDIRPLUS3res->READDIRPLUS3res_u.resok.reply.entries;e;e=e->nextentry){
+ char *new_name;
+
+ if(!strcmp(e->name, ".")){
+ continue;
+ }
+ if(!strcmp(e->name, "..")){
+ continue;
+ }
+ if(e->name_handle.handle_follows == 0){
+ continue;
+ }
+
+ last_e = e;
+
+ asprintf(&new_name, "%s/%s", dir, e->name);
+ insert_fhandle(nfsio, new_name,
+ e->name_handle.post_op_fh3_u.handle.data.data_val,
+ e->name_handle.post_op_fh3_u.handle.data.data_len);
+ free(new_name);
+
+ if (cb) {
+ cb(e, private_data);
+ }
+ }
+
+ if (READDIRPLUS3res->READDIRPLUS3res_u.resok.reply.eof == 0) {
+ if (READDIRPLUS3args.cookie == 0) {
+ memcpy(&READDIRPLUS3args.cookieverf,
+ &READDIRPLUS3res->READDIRPLUS3res_u.resok.cookieverf,
+ NFS3_COOKIEVERFSIZE);
+ }
+
+ READDIRPLUS3args.cookie = last_e->cookie;
+
+ goto again;
+ }
+
+
+finished:
+ if (dir) {
+ free(dir);
+ }
+ return ret;
+}
+
+
+nfsstat3 nfsio_rename(struct nfsio *nfsio, const char *old, const char *new)
+{
+
+ struct RENAME3args RENAME3args;
+ struct RENAME3res *RENAME3res;
+ int ret = NFS3_OK;
+ char *tmp_old_name = NULL;
+ char *tmp_new_name = NULL;
+ data_t *old_fh, *new_fh;
+ char *old_ptr, *new_ptr;
+
+ tmp_old_name = strdup(old);
+ if (tmp_old_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ old_ptr = rindex(tmp_old_name, '/');
+ if (old_ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *old_ptr = 0;
+ old_ptr++;
+
+ old_fh = lookup_fhandle(nfsio, tmp_old_name);
+ if (old_fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ tmp_new_name = strdup(new);
+ if (tmp_new_name == NULL) {
+ fprintf(stderr, "failed to strdup name in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ new_ptr = rindex(tmp_new_name, '/');
+ if (new_ptr == NULL) {
+ fprintf(stderr, "name did not contain '/' in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ *new_ptr = 0;
+ new_ptr++;
+
+ new_fh = lookup_fhandle(nfsio, tmp_new_name);
+ if (new_fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ RENAME3args.from.dir.data.data_len = old_fh->dsize;
+ RENAME3args.from.dir.data.data_val = discard_const(old_fh->dptr);
+ RENAME3args.from.name = old_ptr;
+
+ RENAME3args.to.dir.data.data_len = new_fh->dsize;
+ RENAME3args.to.dir.data.data_val = discard_const(new_fh->dptr);
+ RENAME3args.to.name = new_ptr;
+
+
+ RENAME3res = nfsproc3_rename_3(&RENAME3args, nfsio->nfs_clnt);
+
+ if (RENAME3res == NULL) {
+ fprintf(stderr, "nfsproc3_rename_3 failed in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+ if (RENAME3res->status != NFS3_OK) {
+ fprintf(stderr, "nfsproc3_rename_3 failed in nfsio_rename. status:%d\n", RENAME3res->status);
+ ret = RENAME3res->status;
+ goto finished;
+ }
+
+
+ old_fh = lookup_fhandle(nfsio, old);
+ if (old_fh == NULL) {
+ fprintf(stderr, "failed to fetch parent handle in nfsio_rename\n");
+ ret = NFS3ERR_SERVERFAULT;
+ goto finished;
+ }
+
+
+ insert_fhandle(nfsio, new, old_fh->dptr, old_fh->dsize);
+ delete_fhandle(nfsio, old);
+
+
+finished:
+ if (tmp_old_name) {
+ free(tmp_old_name);
+ }
+ if (tmp_new_name) {
+ free(tmp_new_name);
+ }
+ return ret;
+}
+
--- /dev/null
+/*
+ nfs backend for dbench
+
+ Copyright (C) 2008 by Ronnie Sahlberg (ronniesahlberg@gmail.com)
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#undef _GNU_SOURCE
+
+#include "mount.h"
+#include "nfs.h"
+#include "libnfs.h"
+#include "dbench.h"
+
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+
+#define MAX_FILES 200
+
+static char rw_buf[65536];
+
+
+void nb_sleep(struct child_struct *child, int usec, const char *status)
+{
+ (void)child;
+ (void)usec;
+ (void)status;
+ usleep(usec);
+}
+
+struct cb_data {
+ struct nfsio *nfsio;
+ char *dirname;
+};
+
+static void dirent_cb(struct entryplus3 *e, void *private_data)
+{
+ struct cb_data *cbd = private_data;
+ nfsstat3 res;
+ char *objname;
+
+ if (!strcmp(cbd->dirname,".")) {
+ return;
+ }
+ if (!strcmp(cbd->dirname,"..")) {
+ return;
+ }
+
+ asprintf(&objname, "%s/%s", cbd->dirname, e->name);
+ if (objname == NULL) {
+ printf("Failed to talloc ne object name in dirent_cb\n");
+ exit(10);
+ }
+
+ if (e->name_attributes.post_op_attr_u.attributes.type == NF3DIR) {
+ struct cb_data *new_cbd = malloc(sizeof(struct cb_data));
+
+ new_cbd->nfsio = cbd->nfsio;
+ new_cbd->dirname = strdup(objname);
+
+ nfsio_readdirplus(cbd->nfsio, objname, dirent_cb, new_cbd);
+
+ res = nfsio_rmdir(cbd->nfsio, objname);
+ if (res != NFS3_OK) {
+ printf("Failed to remove object : \"%s\" %s (%d)\n", objname, nfs_error(res), res);
+ free(objname);
+ free(new_cbd->dirname);
+ free(new_cbd);
+ exit(10);
+ }
+
+
+ free(objname);
+ free(new_cbd->dirname);
+ free(new_cbd);
+ return;
+ }
+
+ res = nfsio_remove(cbd->nfsio, objname);
+ if (res != NFS3_OK) {
+ printf("Failed to remove object : \"%s\" %s %d\n", objname, nfs_error(res), res);
+ free(objname);
+ exit(10);
+ }
+
+ free(objname);
+}
+
+static void nfs3_deltree(struct child_struct *child, const char *dname)
+{
+ struct cb_data *cbd;
+ nfsstat3 res;
+
+ cbd = malloc(sizeof(struct cb_data));
+
+ cbd->nfsio = child->private;
+ cbd->dirname = discard_const(dname);
+
+ res = nfsio_lookup(cbd->nfsio, cbd->dirname, NULL);
+ if (res != NFS3ERR_NOENT) {
+ nfsio_readdirplus(cbd->nfsio, cbd->dirname, dirent_cb, cbd);
+ nfsio_rmdir(cbd->nfsio, cbd->dirname);
+ }
+
+ res = nfsio_lookup(cbd->nfsio, cbd->dirname, NULL);
+ if (res != NFS3ERR_NOENT) {
+ printf("Directory \"%s\" not empty. Aborting\n");
+ free(cbd);
+ exit(10);
+ }
+ free(cbd);
+}
+
+static int expected_status(const char *status)
+{
+ if (strncmp(status, "0x", 2) == 0) {
+ return strtol(status, NULL, 16);
+ }
+}
+
+static void failed(struct child_struct *child)
+{
+ child->failed = 1;
+ printf("ERROR: child %d failed at line %d\n", child->id, child->line);
+ exit(1);
+}
+
+static void nfs3_getattr(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_getattr(child->private, fname, NULL);
+ if (res != expected_status(status)) {
+ printf("[%d] GETATTR \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+
+static void nfs3_lookup(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_lookup(child->private, fname, NULL);
+ if (res != expected_status(status)) {
+ printf("[%d] LOOKUP \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_create(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_create(child->private, fname);
+ if (res != expected_status(status)) {
+ printf("[%d] CREATE \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_write(struct child_struct *child, const char *fname, int offset, int len, int stable, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_write(child->private, fname, rw_buf, offset, len, stable);
+ if (res != expected_status(status)) {
+ printf("[%d] WRITE \"%s\" failed (%x) - expected %x\n",
+ child->line, fname,
+ res, expected_status(status));
+ failed(child);
+ }
+ child->bytes += len;
+}
+
+static void nfs3_commit(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_commit(child->private, fname);
+ if (res != expected_status(status)) {
+ printf("[%d] COMMIT \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+
+static void nfs3_read(struct child_struct *child, const char *fname, int offset, int len, const char *status)
+{
+ nfsstat3 res = 0;
+
+ res = nfsio_read(child->private, fname, rw_buf, offset, len, NULL, NULL);
+ if (res != expected_status(status)) {
+ printf("[%d] READ \"%s\" failed (%x) - expected %x\n",
+ child->line, fname,
+ res, expected_status(status));
+ failed(child);
+ }
+ child->bytes += len;
+}
+
+static void nfs3_access(struct child_struct *child, const char *fname, int desired, int granted, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_access(child->private, fname, 0, NULL);
+ if (res != expected_status(status)) {
+ printf("[%d] ACCESS \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_mkdir(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_mkdir(child->private, fname);
+ if (res != expected_status(status)) {
+ printf("[%d] MKDIR \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_rmdir(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_rmdir(child->private, fname);
+ if (res != expected_status(status)) {
+ printf("[%d] RMDIR \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_fsstat(struct child_struct *child, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_fsstat(child->private);
+ if (res != expected_status(status)) {
+ printf("[%d] FSSTAT failed (%x) - expected %x\n",
+ child->line, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_fsinfo(struct child_struct *child, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_fsinfo(child->private);
+ if (res != expected_status(status)) {
+ printf("[%d] FSINFO failed (%x) - expected %x\n",
+ child->line, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_cleanup(struct child_struct *child)
+{
+ char *dname;
+
+ asprintf(&dname, "/clients/client%d", child->id);
+ nfs3_deltree(child, dname);
+ free(dname);
+}
+
+static void nfs3_setup(struct child_struct *child)
+{
+ const char *status = "0x00000000";
+
+ child->rate.last_time = timeval_current();
+ child->rate.last_bytes = 0;
+
+
+ srandom(getpid() ^ time(NULL));
+ child->private = nfsio_connect(options.server, options.export, options.protocol);
+
+ if (child->private == NULL) {
+ child->failed = 1;
+ printf("nfsio_connect() failed\n");
+ exit(10);
+ }
+
+}
+
+static void nfs3_symlink(struct child_struct *child, const char *fname, const char *fname2, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_symlink(child->private, fname, fname2);
+ if (res != expected_status(status)) {
+ printf("[%d] SYMLINK \"%s\"->\"%s\" failed (%x) - expected %x\n",
+ child->line, fname, fname2,
+ res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_remove(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_remove(child->private, fname);
+ if (res != expected_status(status)) {
+ printf("[%d] REMOVE \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_readdirplus(struct child_struct *child, const char *fname, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_readdirplus(child->private, fname, NULL, NULL);
+ if (res != expected_status(status)) {
+ printf("[%d] READDIRPLUS \"%s\" failed (%x) - expected %x\n",
+ child->line, fname, res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_link(struct child_struct *child, const char *fname, const char *fname2, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_link(child->private, fname, fname2);
+ if (res != expected_status(status)) {
+ printf("[%d] LINK \"%s\"->\"%s\" failed (%x) - expected %x\n",
+ child->line, fname, fname2,
+ res, expected_status(status));
+ failed(child);
+ }
+}
+
+static void nfs3_rename(struct child_struct *child, const char *fname, const char *fname2, const char *status)
+{
+ nfsstat3 res;
+
+ res = nfsio_rename(child->private, fname, fname2);
+ if (res != expected_status(status)) {
+ printf("[%d] RENAME \"%s\"->\"%s\" failed (%x) - expected %x\n",
+ child->line, fname, fname2,
+ res, expected_status(status));
+ failed(child);
+ }
+}
+
+struct nb_operations nb_ops = {
+ .setup = nfs3_setup,
+ .deltree = nfs3_deltree,
+ .cleanup = nfs3_cleanup,
+
+ .getattr3 = nfs3_getattr,
+ .lookup3 = nfs3_lookup,
+ .create3 = nfs3_create,
+ .write3 = nfs3_write,
+ .commit3 = nfs3_commit,
+ .read3 = nfs3_read,
+ .access3 = nfs3_access,
+ .mkdir3 = nfs3_mkdir,
+ .rmdir3 = nfs3_rmdir,
+ .fsstat3 = nfs3_fsstat,
+ .fsinfo3 = nfs3_fsinfo,
+ .symlink3 = nfs3_symlink,
+ .remove3 = nfs3_remove,
+ .readdirplus3 = nfs3_readdirplus,
+ .rename3 = nfs3_rename,
+ .link3 = nfs3_link,
+};