diff options
author | Shehjar Tikoo <shehjart@gluster.com> | 2010-03-31 07:27:05 +0000 |
---|---|---|
committer | Anand V. Avati <avati@dev.gluster.com> | 2010-03-31 07:44:09 -0700 |
commit | f66f04e3af1f31a4c51b6a87f5e32ea2e3c7b438 (patch) | |
tree | 450da8640fd35525a3b288893f86fbd33b0bd47e | |
parent | e63053803dca86b4283b71bc4ef4cb180ec72d89 (diff) |
nfs: Add NFSv3 protocol support
Signed-off-by: Shehjar Tikoo <shehjart@gluster.com>
Signed-off-by: Anand V. Avati <avati@dev.gluster.com>
BUG: 399 (NFS translator with Mount v3 and NFS v3 support)
URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=399
-rw-r--r-- | xlators/nfs/server/src/Makefile.am | 4 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs.c | 8 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3-helpers.c | 2825 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3-helpers.h | 342 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3.c | 4836 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3.h | 202 |
6 files changed, 8215 insertions, 2 deletions
diff --git a/xlators/nfs/server/src/Makefile.am b/xlators/nfs/server/src/Makefile.am index 9661dd95d20..c51dad1e804 100644 --- a/xlators/nfs/server/src/Makefile.am +++ b/xlators/nfs/server/src/Makefile.am @@ -2,10 +2,10 @@ xlator_LTLIBRARIES = server.la xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/nfs rpclibdir = $(top_srcdir)/xlators/nfs/lib/src/ server_la_LDFLAGS = -module -avoidversion -server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c nfs-generics.c mount3.c nfs3-fh.c +server_la_SOURCES = nfs.c nfs-common.c nfs-fops.c nfs-inodes.c nfs-generics.c mount3.c nfs3-fh.c nfs3.c nfs3-helpers.c server_la_LIBADD = $(top_builddir)/xlators/nfs/lib/src/librpcsvc.la $(top_builddir)/libglusterfs/src/libglusterfs.la -noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h mount3.h nfs3-fh.h +noinst_HEADERS = nfs.h nfs-common.h nfs-fops.h nfs-inodes.h nfs-generics.h mount3.h nfs3-fh.h nfs3.h nfs3-helpers.h AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\ -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS)\ diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c index 74937903612..488ece419cb 100644 --- a/xlators/nfs/server/src/nfs.c +++ b/xlators/nfs/server/src/nfs.c @@ -37,6 +37,7 @@ #include "nfs-fops.h" #include "inode.h" #include "mount3.h" +#include "nfs3.h" /* Every NFS version must call this function with the init function * for its particular version. @@ -152,6 +153,13 @@ nfs_add_all_initiators (struct nfs_state *nfs) goto ret; } + ret = nfs_add_initer (&nfs->versions, nfs3svc_init); + if (ret == -1) { + gf_log (GF_NFS, GF_LOG_ERROR, "Failed to add protocol" + " initializer"); + goto ret; + } + ret = 0; ret: return ret; diff --git a/xlators/nfs/server/src/nfs3-helpers.c b/xlators/nfs/server/src/nfs3-helpers.c new file mode 100644 index 00000000000..5b2fdaca4ca --- /dev/null +++ b/xlators/nfs/server/src/nfs3-helpers.c @@ -0,0 +1,2825 @@ +/* + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS 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. + + GlusterFS 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/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <inttypes.h> + +#include "xlator.h" +#include "nfs3.h" +#include "nfs3-fh.h" +#include "msg-nfs3.h" +#include "rbthash.h" +#include "nfs-fops.h" +#include "nfs-inodes.h" +#include "nfs-generics.h" +#include "nfs3-helpers.h" +#include "iatt.h" +#include <string.h> + +#define nfs3_call_resume(cst) \ + do { \ + if (((cst)) && (cst)->resume_fn) \ + (cst)->resume_fn (cst); \ + } while (0) \ + +#define nfs3_call_resume_estale(csta) \ + do { \ + (csta)->resolve_ret = -1; \ + (csta)->resolve_errno = ESTALE; \ + nfs3_call_resume (csta); \ + } while (0) \ + +struct nfs3stat_strerror { + nfsstat3 stat; + char strerror[100]; +}; + + +struct nfs3stat_strerror nfs3stat_strerror_table[] = { + { NFS3_OK, "Call completed successfully." }, + { NFS3ERR_PERM, "Not owner" }, + { NFS3ERR_NOENT, "No such file or directory" }, + { NFS3ERR_IO, "I/O error" }, + { NFS3ERR_NXIO, "I/O error" }, + { NFS3ERR_ACCES, "Permission denied" }, + { NFS3ERR_EXIST, "File exists" }, + { NFS3ERR_XDEV, "Attempt to do a cross-device hard link"}, + { NFS3ERR_NODEV, "No such device" }, + { NFS3ERR_NOTDIR, "Not a directory" }, + { NFS3ERR_ISDIR, "Not a directory" }, + { NFS3ERR_INVAL, "Invalid argument for operation" }, + { NFS3ERR_FBIG, "File too large" }, + { NFS3ERR_NOSPC, "No space left on device" }, + { NFS3ERR_ROFS, "Read-only file system" }, + { NFS3ERR_MLINK, "Too many hard links" }, + { NFS3ERR_NAMETOOLONG, "Filename in operation was too long" }, + { NFS3ERR_NOTEMPTY, "Directory not empty" }, + { NFS3ERR_DQUOT, "Resource (quota) hard limit exceeded" }, + { NFS3ERR_STALE, "Invalid file handle" }, + { NFS3ERR_REMOTE, "Too many levels of remote in path" }, + { NFS3ERR_BADHANDLE, "Illegal NFS file handle" }, + { NFS3ERR_NOT_SYNC, "Update synchronization mismatch detected" }, + { NFS3ERR_BAD_COOKIE, "READDIR or READDIRPLUS cookie is stale"}, + { NFS3ERR_NOTSUPP, "Operation is not supported" }, + { NFS3ERR_TOOSMALL, "Buffer or request is too small" }, + { NFS3ERR_SERVERFAULT, "Error occurred on the server or IO Error" }, + { NFS3ERR_BADTYPE, "Type not supported by the server" }, + { NFS3ERR_JUKEBOX, "Cannot complete server initiated request" }, + { -1, "IO Error" }, + +}; + + +void +nfs3_map_xlid_to_statdev (struct iatt *ia, uint16_t xlid) +{ + if (!ia) + return; + + ia->ia_dev = xlid; +} + + +struct nfs3_fh +nfs3_extract_nfs3_fh (nfs_fh3 fh) +{ + struct nfs3_fh gfh; + + memcpy (&gfh, fh.data.data_val, fh.data.data_len); + return gfh; +} + + +struct nfs3_fh +nfs3_extract_lookup_fh (lookup3args *args) +{ + return nfs3_extract_nfs3_fh(args->what.dir); +} + + +char * +nfs3_extract_lookup_name (lookup3args *args) +{ + return args->what.name; +} + + +nfsstat3 +nfs3_errno_to_nfsstat3 (int errnum) +{ + nfsstat3 stat = NFS3_OK; + + switch (errnum) { + + case 0: + stat = NFS3_OK; + break; + + case EPERM: + stat = NFS3ERR_PERM; + break; + + case ENOENT: + stat = NFS3ERR_NOENT; + break; + + case EACCES: + stat = NFS3ERR_ACCES; + break; + + case EEXIST: + stat = NFS3ERR_EXIST; + break; + + case EXDEV: + stat = NFS3ERR_XDEV; + break; + + case ENODEV: + stat = NFS3ERR_NODEV; + break; + + case EIO: + stat = NFS3ERR_IO; + break; + + case ENXIO: + stat = NFS3ERR_NXIO; + break; + + case ENOTDIR: + stat = NFS3ERR_NOTDIR; + break; + + case EISDIR: + stat = NFS3ERR_ISDIR; + break; + + case EINVAL: + stat = NFS3ERR_INVAL; + break; + + case ENOSPC: + stat = NFS3ERR_NOSPC; + break; + + case EROFS: + stat = NFS3ERR_ROFS; + break; + + case EFBIG: + stat = NFS3ERR_FBIG; + break; + + case EMLINK: + stat = NFS3ERR_MLINK; + break; + + case ENAMETOOLONG: + stat = NFS3ERR_NAMETOOLONG; + break; + + case ENOTEMPTY: + stat = NFS3ERR_NOTEMPTY; + break; + + case EFAULT: + stat = NFS3ERR_SERVERFAULT; + break; + + case ENOSYS: + stat = NFS3ERR_NOTSUPP; + break; + + case EBADF: + stat = NFS3ERR_BADTYPE; + break; + + case ESTALE: + stat = NFS3ERR_STALE; + + default: + stat = NFS3ERR_SERVERFAULT; + break; + } + + return stat; +} + + +void +nfs3_fill_lookup3res_error (lookup3res *res, nfsstat3 stat, + struct iatt *dirstat) +{ + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (!dirstat) { + res->lookup3res_u.resfail.dir_attributes.attributes_follow = FALSE; + } + +} + +fattr3 +nfs3_stat_to_fattr3 (struct iatt *buf) +{ + fattr3 fa = {0, }; + + if (IA_ISDIR (buf->ia_type)) + fa.type = NF3DIR; + else if (IA_ISREG (buf->ia_type)) + fa.type = NF3REG; + else if (IA_ISCHR (buf->ia_type)) + fa.type = NF3CHR; + else if (IA_ISBLK (buf->ia_type)) + fa.type = NF3BLK; + else if (IA_ISFIFO (buf->ia_type)) + fa.type = NF3FIFO; + else if (IA_ISLNK (buf->ia_type)) + fa.type = NF3LNK; + else if (IA_ISSOCK (buf->ia_type)) + fa.type = NF3SOCK; + + if (IA_PROT_RUSR (buf->ia_prot)) + fa.mode |= NFS3MODE_ROWNER; + if (IA_PROT_WUSR (buf->ia_prot)) + fa.mode |= NFS3MODE_WOWNER; + if (IA_PROT_XUSR (buf->ia_prot)) + fa.mode |= NFS3MODE_XOWNER; + + if (IA_PROT_RGRP (buf->ia_prot)) + fa.mode |= NFS3MODE_RGROUP; + if (IA_PROT_WGRP (buf->ia_prot)) + fa.mode |= NFS3MODE_WGROUP; + if (IA_PROT_XGRP (buf->ia_prot)) + fa.mode |= NFS3MODE_XGROUP; + + if (IA_PROT_ROTH (buf->ia_prot)) + fa.mode |= NFS3MODE_ROTHER; + if (IA_PROT_WOTH (buf->ia_prot)) + fa.mode |= NFS3MODE_WOTHER; + if (IA_PROT_XOTH (buf->ia_prot)) + fa.mode |= NFS3MODE_XOTHER; + + if (IA_PROT_SUID (buf->ia_prot)) + fa.mode |= NFS3MODE_SETXUID; + if (IA_PROT_SGID (buf->ia_prot)) + fa.mode |= NFS3MODE_SETXGID; + if (IA_PROT_STCKY (buf->ia_prot)) + fa.mode |= NFS3MODE_SAVESWAPTXT; + + fa.nlink = buf->ia_nlink; + fa.uid = buf->ia_uid; + fa.gid = buf->ia_gid; + fa.size = buf->ia_size; + fa.used = (buf->ia_blocks * 512); + + if ((IA_ISCHR (buf->ia_type) || IA_ISBLK (buf->ia_type))) { + fa.rdev.specdata1 = ia_major (buf->ia_rdev); + fa.rdev.specdata2 = ia_minor (buf->ia_rdev); + } else { + fa.rdev.specdata1 = 0; + fa.rdev.specdata2 = 0; + } + + fa.fsid = buf->ia_dev; + fa.fileid = buf->ia_ino; + /* FIXME: Handle time resolutions for sub-second granularity */ + if (buf->ia_atime == 9669) { + fa.mtime.seconds = 0; + fa.mtime.nseconds = 0; + fa.atime.seconds = 0; + fa.atime.nseconds = 0; + } else { + fa.mtime.seconds = buf->ia_mtime; + fa.mtime.nseconds = 0; + fa.atime.seconds = buf->ia_atime; + fa.atime.seconds = 0; + fa.atime.nseconds = 0; + } + + fa.atime.seconds = buf->ia_atime; + fa.atime.nseconds = 0; + + fa.ctime.seconds = buf->ia_ctime; + fa.ctime.nseconds = 0; + + return fa; +} + + +post_op_attr +nfs3_stat_to_post_op_attr (struct iatt *buf) +{ + post_op_attr attr = {0, }; + if (!buf) + return attr; + + /* Some performance translators return zero-filled stats when they + * do not have up-to-date attributes. Need to handle this by not + * returning these zeroed out attrs. + */ + attr.attributes_follow = FALSE; + if (nfs_zero_filled_stat (buf)) + goto out; + + attr.post_op_attr_u.attributes = nfs3_stat_to_fattr3 (buf); + attr.attributes_follow = TRUE; + +out: + return attr; +} + + +pre_op_attr +nfs3_stat_to_pre_op_attr (struct iatt *pre) +{ + pre_op_attr poa = {0, }; + + /* Some performance translators return zero-filled stats when they + * do not have up-to-date attributes. Need to handle this by not + * returning these zeroed out attrs. + */ + poa.attributes_follow = FALSE; + if (nfs_zero_filled_stat (pre)) + goto out; + + poa.attributes_follow = TRUE; + poa.pre_op_attr_u.attributes.size = pre->ia_size; + if (pre->ia_atime == 9669) + poa.pre_op_attr_u.attributes.mtime.seconds = 0; + else + poa.pre_op_attr_u.attributes.mtime.seconds = pre->ia_mtime; + poa.pre_op_attr_u.attributes.ctime.seconds = pre->ia_ctime; + +out: + return poa; +} + +void +nfs3_fill_lookup3res_success (lookup3res *res, nfsstat3 stat, + struct nfs3_fh *fh, struct iatt *buf, + struct iatt *postparent) +{ + post_op_attr obj, dir; + uint32_t fhlen = 0; + + res->status = stat; + if (fh) { + res->lookup3res_u.resok.object.data.data_val = (void *)fh; + fhlen = nfs3_fh_compute_size (fh); + res->lookup3res_u.resok.object.data.data_len = fhlen; + } + + obj.attributes_follow = FALSE; + dir.attributes_follow = FALSE; + if (buf) { + nfs3_map_xlid_to_statdev (buf, fh->xlatorid); + obj = nfs3_stat_to_post_op_attr (buf); + } + + if (postparent) { + nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); + dir = nfs3_stat_to_post_op_attr (postparent); + } + + res->lookup3res_u.resok.obj_attributes = obj; + res->lookup3res_u.resok.dir_attributes = dir; +} + + +void +nfs3_fill_lookup3res (lookup3res *res, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *buf, struct iatt *postparent) +{ + + memset (res, 0, sizeof (*res)); + if (stat != NFS3_OK) + nfs3_fill_lookup3res_error (res, stat, postparent); + else + nfs3_fill_lookup3res_success (res, stat, newfh, buf, + postparent); +} + +struct nfs3_fh +nfs3_extract_getattr_fh (getattr3args *args) +{ + return nfs3_extract_nfs3_fh(args->object); +} + + +void +nfs3_fill_getattr3res (getattr3res *res, nfsstat3 stat, struct iatt *buf, + uint16_t xlid) +{ + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (buf, xlid); + res->getattr3res_u.resok.obj_attributes = nfs3_stat_to_fattr3 (buf); + +} + + +struct nfs3_fh +nfs3_extract_fsinfo_fh (fsinfo3args *args) +{ + return nfs3_extract_nfs3_fh (args->fsroot); +} + + +void +nfs3_fill_fsinfo3res (struct nfs3_state *nfs3, fsinfo3res *res, + nfsstat3 status, struct iatt *fsroot, uint16_t xlid) +{ + fsinfo3resok resok = {{0}, }; + nfstime3 tdelta = GF_NFS3_TIMEDELTA_SECS; + + memset (res, 0, sizeof (*res)); + res->status = status; + if (status != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (fsroot, xlid); + resok.obj_attributes = nfs3_stat_to_post_op_attr (fsroot); + resok.rtmax = nfs3->readsize; + resok.rtpref = nfs3->readsize; + resok.rtmult = GF_NFS3_RTMULT; + resok.wtmax = nfs3->writesize; + resok.wtpref = nfs3->writesize; + resok.wtmult = GF_NFS3_WTMULT; + resok.dtpref = nfs3->readdirsize; + resok.maxfilesize = GF_NFS3_MAXFILE; + resok.time_delta = tdelta; + resok.properties = GF_NFS3_FS_PROP; + + res->fsinfo3res_u.resok = resok; + +} + + +void +nfs3_prep_lookup3args (lookup3args *args, struct nfs3_fh *fh, char *name) +{ + memset (args, 0, sizeof (*args)); + args->what.dir.data.data_val = (void *)fh; + args->what.name = name; +} + + +void +nfs3_prep_getattr3args (getattr3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->object.data.data_val = (void *)fh; +} + + +void +nfs3_prep_fsinfo3args (fsinfo3args *args, struct nfs3_fh *root) +{ + memset (args, 0, sizeof (*args)); + args->fsroot.data.data_val = (void *)root; +} + + +char * +nfsstat3_strerror(int stat) +{ + int i; + for(i = 0; nfs3stat_strerror_table[i].stat != -1; i++) { + if (nfs3stat_strerror_table[i].stat == stat) + return nfs3stat_strerror_table[i].strerror; + } + + return nfs3stat_strerror_table[i].strerror; +} + + + +void +nfs3_prep_access3args (access3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->object.data.data_val = (void *)fh; +} + + +uint32_t +nfs3_owner_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ + uint32_t accresult = 0; + + if (IA_PROT_RUSR (prot) && (request & ACCESS3_READ)) + accresult |= ACCESS3_READ; + + if (request & ACCESS3_LOOKUP) + if ((IA_ISDIR (type)) && (IA_PROT_XUSR (prot))) + accresult |= ACCESS3_LOOKUP; + + if ((IA_PROT_WUSR (prot) && (request & ACCESS3_MODIFY))) + accresult |= ACCESS3_MODIFY; + + if ((IA_PROT_WUSR (prot) && (request & ACCESS3_EXTEND))) + accresult |= ACCESS3_EXTEND; + + /* ACCESS3_DELETE is ignored for now since that requires + * knowing the permissions on the parent directory. + */ + + if (request & ACCESS3_EXECUTE) + if (IA_PROT_XUSR (prot) && (!IA_ISDIR (type))) + accresult |= ACCESS3_EXECUTE; + + return accresult; +} + + +uint32_t +nfs3_group_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ + uint32_t accresult = 0; + + if (IA_PROT_RGRP (prot) && (request & ACCESS3_READ)) + accresult |= ACCESS3_READ; + + if (request & ACCESS3_LOOKUP) + if ((IA_ISDIR (type)) && IA_PROT_RGRP (prot)) + accresult |= ACCESS3_LOOKUP; + + if (IA_PROT_WGRP (prot) && (request & ACCESS3_MODIFY)) + accresult |= ACCESS3_MODIFY; + + if (IA_PROT_WGRP (prot) && (request & ACCESS3_EXTEND)) + accresult |= ACCESS3_EXTEND; + + /* ACCESS3_DELETE is ignored for now since that requires + * knowing the permissions on the parent directory. + */ + + if (request & ACCESS3_EXECUTE) + if (IA_PROT_XGRP (prot) && (!IA_ISDIR (type))) + accresult |= ACCESS3_EXECUTE; + + return accresult; +} + + +uint32_t +nfs3_other_accessbits (ia_prot_t prot, ia_type_t type, uint32_t request) +{ + uint32_t accresult = 0; + + if (IA_PROT_ROTH (prot) && (request & ACCESS3_READ)) + accresult |= ACCESS3_READ; + + if (request & ACCESS3_LOOKUP) + if (IA_ISDIR (type) && IA_PROT_ROTH (prot)) + accresult |= ACCESS3_LOOKUP; + + if (IA_PROT_WOTH (prot) && (request & ACCESS3_MODIFY)) + accresult |= ACCESS3_MODIFY; + + if (IA_PROT_WOTH (prot) && (request & ACCESS3_EXTEND)) + accresult |= ACCESS3_EXTEND; + + /* ACCESS3_DELETE is ignored for now since that requires + * knowing the permissions on the parent directory. + */ + + if (request & ACCESS3_EXECUTE) + if (IA_PROT_XOTH (prot) && (!IA_ISDIR (type))) + accresult |= ACCESS3_EXECUTE; + + return accresult; +} + + +uint32_t +nfs3_stat_to_accessbits (struct iatt *buf, uint32_t request, uid_t uid, + gid_t gid) +{ + uint32_t accresult = 0; + ia_prot_t prot = {0, }; + ia_type_t type = 0; + + prot = buf->ia_prot; + type = buf->ia_type; + + if (buf->ia_uid == uid) + accresult = nfs3_owner_accessbits (prot, type, request); + else if (buf->ia_gid == gid) + accresult = nfs3_group_accessbits (prot, type, request); + else + accresult = nfs3_other_accessbits (prot, type, request); + + return accresult; +} + + +void +nfs3_fill_access3res (access3res *res, nfsstat3 status, struct iatt *buf, + uint32_t accbits, uid_t uid, gid_t gid, uint16_t xlid) +{ + post_op_attr objattr; + uint32_t accres = 0; + + memset (res, 0, sizeof (*res)); + res->status = status; + if (status != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (buf, xlid); + objattr = nfs3_stat_to_post_op_attr (buf); + accres = nfs3_stat_to_accessbits (buf, accbits, uid, gid); + + res->access3res_u.resok.obj_attributes = objattr; + res->access3res_u.resok.access = accres; +} + +void +nfs3_prep_readdir3args (readdir3args *ra, struct nfs3_fh *fh) +{ + memset (ra, 0, sizeof (*ra)); + ra->dir.data.data_val = (void *)fh; +} + + +entry3 * +nfs3_fill_entry3 (gf_dirent_t *entry) +{ + entry3 *ent = NULL; + if (!entry) + return NULL; + + ent = CALLOC (1, sizeof (*ent)); + if (!ent) + return NULL; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry: %s", entry->d_name); + ent->fileid = entry->d_ino; + ent->cookie = entry->d_off; + ent->name = CALLOC ((strlen (entry->d_name) + 1), sizeof (char)); + if (!ent->name) { + FREE (ent); + ent = NULL; + goto err; + } + strcpy (ent->name, entry->d_name); + +err: + return ent; +} + + +void +nfs3_fill_post_op_fh3 (struct nfs3_fh *fh, post_op_fh3 *pfh) +{ + uint32_t fhlen = 0; + + if ((!fh) || (!pfh)) + return; + + pfh->handle_follows = 1; + fhlen = nfs3_fh_compute_size (fh); + pfh->post_op_fh3_u.handle.data.data_val = (void *)fh; + pfh->post_op_fh3_u.handle.data.data_len = fhlen; +} + + +post_op_fh3 +nfs3_fh_to_post_op_fh3 (struct nfs3_fh *fh) +{ + post_op_fh3 pfh = {0, }; + char *fhp = NULL; + + if (!fh) + return pfh; + + pfh.handle_follows = 1; + + fhp = CALLOC (1, sizeof (*fh)); + if (!fhp) + return pfh; + + memcpy (fhp, fh, sizeof (*fh)); + nfs3_fill_post_op_fh3 ((struct nfs3_fh *)fhp, &pfh); + return pfh; +} + + +entryp3 * +nfs3_fill_entryp3 (gf_dirent_t *entry, struct nfs3_fh *dirfh) +{ + entryp3 *ent = NULL; + struct nfs3_fh newfh = {{0}, }; + + if ((!entry) || (!dirfh)) + return NULL; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry: %s, ino: %"PRIu64, + entry->d_name, entry->d_ino); + ent = CALLOC (1, sizeof (*ent)); + if (!ent) + return NULL; + + ent->fileid = entry->d_ino; + ent->cookie = entry->d_off; + ent->name = CALLOC ((strlen (entry->d_name) + 1), sizeof (char)); + if (!ent->name) { + FREE (ent); + ent = NULL; + goto err; + } + strcpy (ent->name, entry->d_name); + + nfs3_fh_build_child_fh (dirfh, &entry->d_stat, &newfh); + nfs3_map_xlid_to_statdev (&entry->d_stat, dirfh->xlatorid); + ent->name_attributes = nfs3_stat_to_post_op_attr (&entry->d_stat); + ent->name_handle = nfs3_fh_to_post_op_fh3 (&newfh); +err: + return ent; +} + + +void +nfs3_fill_readdir3res (readdir3res *res, nfsstat3 stat, uint64_t cverf, + struct iatt *dirstat, gf_dirent_t *entries, count3 count, + int is_eof, uint16_t xlid) +{ + post_op_attr dirattr; + entry3 *ent = NULL; + entry3 *headentry = NULL; + entry3 *preventry = NULL; + count3 filled = 0; + gf_dirent_t *listhead = NULL; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (dirstat, xlid); + dirattr = nfs3_stat_to_post_op_attr (dirstat); + res->readdir3res_u.resok.dir_attributes = dirattr; + res->readdir3res_u.resok.reply.eof = (bool_t)is_eof; + memcpy (res->readdir3res_u.resok.cookieverf, &cverf, sizeof (cverf)); + + filled = NFS3_READDIR_RESOK_SIZE; + /* First entry is just the list head */ + listhead = entries; + entries = entries->next; + while (((entries) && (entries != listhead)) && (filled < count)) { + /* + if ((strcmp (entries->d_name, ".") == 0) || + (strcmp (entries->d_name, "..") == 0)) + goto nextentry; + */ + ent = nfs3_fill_entry3 (entries); + if (!ent) + break; + + if (!headentry) + headentry = ent; + + if (preventry) { + preventry->nextentry = ent; + preventry = ent; + } else + preventry = ent; + + filled += NFS3_ENTRY3_FIXED_SIZE + strlen (ent->name); +//nextentry: + entries = entries->next; + } + + res->readdir3res_u.resok.reply.entries = headentry; + + return; +} + + +void +nfs3_fill_readdirp3res (readdirp3res *res, nfsstat3 stat, struct nfs3_fh *dirfh, + uint64_t cverf, struct iatt *dirstat, + gf_dirent_t *entries, count3 dircount, count3 maxcount, + int is_eof) +{ + post_op_attr dirattr; + entryp3 *ent = NULL; + entryp3 *headentry = NULL; + entryp3 *preventry = NULL; + count3 filled = 0; + gf_dirent_t *listhead = NULL; + int fhlen = 0; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (dirstat, dirfh->xlatorid); + dirattr = nfs3_stat_to_post_op_attr (dirstat); + res->readdirp3res_u.resok.dir_attributes = dirattr; + res->readdirp3res_u.resok.reply.eof = (bool_t)is_eof; + memcpy (res->readdirp3res_u.resok.cookieverf, &cverf, sizeof (cverf)); + + filled = NFS3_READDIR_RESOK_SIZE; + /* First entry is just the list head */ + listhead = entries; + entries = entries->next; + while (((entries) && (entries != listhead)) && (filled < maxcount)) { + /* Linux does not display . and .. entries unless we provide + * these entries here. + */ +/* if ((strcmp (entries->d_name, ".") == 0) || + (strcmp (entries->d_name, "..") == 0)) + goto nextentry; + */ + ent = nfs3_fill_entryp3 (entries, dirfh); + if (!ent) + break; + + if (!headentry) + headentry = ent; + + if (preventry) { + preventry->nextentry = ent; + preventry = ent; + } else + preventry = ent; + + fhlen = ent->name_handle.post_op_fh3_u.handle.data.data_len; + filled += NFS3_ENTRYP3_FIXED_SIZE + fhlen + strlen (ent->name); +//nextentry: + entries = entries->next; + } + + res->readdirp3res_u.resok.reply.entries = headentry; + + return; +} + + +void +nfs3_prep_readdirp3args (readdirp3args *ra, struct nfs3_fh *fh) +{ + memset (ra, 0, sizeof (*ra)); + ra->dir.data.data_val = (void *)fh; +} + +void +nfs3_free_readdirp3res (readdirp3res *res) +{ + entryp3 *ent = NULL; + entryp3 *next = NULL; + + if (!res) + return; + + ent = res->readdirp3res_u.resok.reply.entries; + while (ent) { + + next = ent->nextentry; + FREE (ent->name); + FREE (ent->name_handle.post_op_fh3_u.handle.data.data_val); + FREE (ent); + ent = next; + } + + return; +} + + +void +nfs3_free_readdir3res (readdir3res *res) +{ + entry3 *ent = NULL; + entry3 *next = NULL; + + if (!res) + return; + + ent = res->readdir3res_u.resok.reply.entries; + while (ent) { + + next = ent->nextentry; + FREE (ent->name); + FREE (ent); + ent = next; + } + + return; +} + +void +nfs3_prep_fsstat3args (fsstat3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->fsroot.data.data_val = (char *)fh; +} + + +void +nfs3_fill_fsstat3res (fsstat3res *res, nfsstat3 stat, struct statvfs *fsbuf, + struct iatt *postbuf, uint16_t xlid) +{ + post_op_attr poa; + fsstat3resok resok; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (postbuf, xlid); + poa = nfs3_stat_to_post_op_attr (postbuf); + resok.tbytes = (size3)(fsbuf->f_frsize * fsbuf->f_blocks); + resok.fbytes = (size3)(fsbuf->f_bsize * fsbuf->f_bfree); + resok.abytes = (size3)(fsbuf->f_bsize * fsbuf->f_bavail); + resok.tfiles = (size3)(fsbuf->f_files); + resok.ffiles = (size3)(fsbuf->f_ffree); + resok.afiles = (size3)(fsbuf->f_favail); + resok.invarsec = 0; + + resok.obj_attributes = poa; + res->fsstat3res_u.resok = resok; +} + + +int32_t +nfs3_sattr3_to_setattr_valid (sattr3 *sattr, struct iatt *buf, mode_t *omode) +{ + int32_t valid = 0; + ia_prot_t prot = {0, }; + mode_t mode = 0; + + if (!sattr) + return 0; + + if (sattr->mode.set_it) { + valid |= GF_SET_ATTR_MODE; + + if (sattr->mode.set_mode3_u.mode & NFS3MODE_ROWNER) { + mode |= S_IRUSR; + prot.owner.read = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_WOWNER) { + mode |= S_IWUSR; + prot.owner.write = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_XOWNER) { + mode |= S_IXUSR; + prot.owner.exec = 1; + } + + if (sattr->mode.set_mode3_u.mode & NFS3MODE_RGROUP) { + mode |= S_IRGRP; + prot.group.read = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_WGROUP) { + mode |= S_IWGRP; + prot.group.write = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_XGROUP) { + mode |= S_IXGRP; + prot.group.exec = 1; + } + + if (sattr->mode.set_mode3_u.mode & NFS3MODE_ROTHER) { + mode |= S_IROTH; + prot.other.read = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_WOTHER) { + mode |= S_IWOTH; + prot.other.write = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_XOTHER) { + mode |= S_IXOTH; + prot.other.exec = 1; + } + + if (sattr->mode.set_mode3_u.mode & NFS3MODE_SETXUID) { + mode |= S_ISUID; + prot.suid = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_SETXGID) { + mode |= S_ISGID; + prot.sgid = 1; + } + if (sattr->mode.set_mode3_u.mode & NFS3MODE_SAVESWAPTXT) { + mode |= S_ISVTX; + prot.sticky = 1; + } + + if (buf) + buf->ia_prot = prot; + /* Create fop still requires the old mode_t style argument. */ + if (omode) + *omode = mode; + } + + if (sattr->uid.set_it) { + valid |= GF_SET_ATTR_UID; + if (buf) + buf->ia_uid = sattr->uid.set_uid3_u.uid; + } + + if (sattr->gid.set_it) { + valid |= GF_SET_ATTR_GID; + if (buf) + buf->ia_gid = sattr->gid.set_gid3_u.gid; + } + + if (sattr->size.set_it) { + valid |= GF_SET_ATTR_SIZE; + if (buf) + buf->ia_size = sattr->size.set_size3_u.size; + } + + if (sattr->atime.set_it == SET_TO_CLIENT_TIME) { + valid |= GF_SET_ATTR_ATIME; + if (buf) + buf->ia_atime = sattr->atime.set_atime_u.atime.seconds; + } + + if (sattr->atime.set_it == SET_TO_SERVER_TIME) { + valid |= GF_SET_ATTR_ATIME; + if (buf) + buf->ia_atime = time (NULL); + } + + if (sattr->mtime.set_it == SET_TO_CLIENT_TIME) { + valid |= GF_SET_ATTR_MTIME; + if (buf) + buf->ia_mtime = sattr->mtime.set_mtime_u.mtime.seconds; + } + + if (sattr->mtime.set_it == SET_TO_SERVER_TIME) { + valid |= GF_SET_ATTR_MTIME; + if (buf) + buf->ia_mtime = time (NULL); + } + + return valid; +} + + +wcc_data +nfs3_stat_to_wcc_data (struct iatt *pre, struct iatt *post) +{ + wcc_data wd = {{0}, }; + + if (post) + wd.after = nfs3_stat_to_post_op_attr (post); + if (pre) + wd.before = nfs3_stat_to_pre_op_attr (pre); + + return wd; +} + +void +nfs3_fill_create3res (create3res *res, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *newbuf, struct iatt *preparent, + struct iatt *postparent) +{ + post_op_attr poa = {0, }; + wcc_data dirwcc = {{0}, }; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_fill_post_op_fh3 (newfh, &res->create3res_u.resok.obj); + nfs3_map_xlid_to_statdev (newbuf, newfh->xlatorid); + poa = nfs3_stat_to_post_op_attr (newbuf); + res->create3res_u.resok.obj_attributes = poa; + if (preparent) { + nfs3_map_xlid_to_statdev (preparent, newfh->xlatorid); + nfs3_map_xlid_to_statdev (postparent, newfh->xlatorid); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + } + + res->create3res_u.resok.dir_wcc = dirwcc; +} + +void +nfs3_prep_create3args (create3args *args, struct nfs3_fh *fh, char *name) +{ + + memset (args, 0, sizeof (*args)); + args->where.dir.data.data_val = (void *)fh; + args->where.name = name; +} + +void +nfs3_prep_setattr3args (setattr3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->object.data.data_val = (void *)fh; +} + + +void +nfs3_fill_setattr3res (setattr3res *res, nfsstat3 stat, struct iatt *preop, + struct iatt *postop, uint16_t xlid) +{ + wcc_data wcc; + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (preop, xlid); + nfs3_map_xlid_to_statdev (postop, xlid); + wcc = nfs3_stat_to_wcc_data (preop, postop); + res->setattr3res_u.resok.obj_wcc = wcc; +} + + +void +nfs3_prep_mkdir3args (mkdir3args *args, struct nfs3_fh *dirfh, char *name) +{ + + memset (args, 0, sizeof (*args)); + args->where.dir.data.data_val = (void *)dirfh; + args->where.name = name; +} + + +void +nfs3_fill_mkdir3res (mkdir3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + wcc_data dirwcc; + post_op_attr poa; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_fill_post_op_fh3 (fh, &res->mkdir3res_u.resok.obj); + nfs3_map_xlid_to_statdev (buf, fh->xlatorid); + poa = nfs3_stat_to_post_op_attr (buf); + nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); + nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + res->mkdir3res_u.resok.obj_attributes = poa; + res->mkdir3res_u.resok.dir_wcc = dirwcc; + +} + + +void +nfs3_prep_symlink3args (symlink3args *args, struct nfs3_fh *dirfh, char *name, + char *target) +{ + memset (args, 0, sizeof (*args)); + args->where.dir.data.data_val = (void *)dirfh; + args->where.name = name; + args->symlink.symlink_data = target; +} + + +void +nfs3_fill_symlink3res (symlink3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + wcc_data dirwcc; + post_op_attr poa; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_fill_post_op_fh3 (fh, &res->symlink3res_u.resok.obj); + nfs3_map_xlid_to_statdev (buf, fh->xlatorid); + poa = nfs3_stat_to_post_op_attr (buf); + nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); + nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + res->symlink3res_u.resok.obj_attributes = poa; + res->symlink3res_u.resok.dir_wcc = dirwcc; + +} + + +void +nfs3_prep_readlink3args (readlink3args *args, struct nfs3_fh *fh) +{ + + memset (args, 0, sizeof (*args)); + args->symlink.data.data_val = (void *)fh; +} + + +void +nfs3_fill_readlink3res (readlink3res *res, nfsstat3 stat, char *path, + struct iatt *buf, uint16_t xlid) +{ + post_op_attr poa; + + memset (res, 0, sizeof (*res)); + res->status = stat; + + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (buf, xlid); + poa = nfs3_stat_to_post_op_attr (buf); + res->readlink3res_u.resok.data = (void *)path; + res->readlink3res_u.resok.symlink_attributes = poa; +} + + +void +nfs3_prep_mknod3args (mknod3args *args, struct nfs3_fh *fh, char *name) +{ + memset (args, 0, sizeof (*args)); + args->where.dir.data.data_val = (void *)fh; + args->where.name = name; + +} + +void +nfs3_fill_mknod3res (mknod3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + post_op_attr poa; + wcc_data wccdir; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_fill_post_op_fh3 (fh, &res->mknod3res_u.resok.obj); + nfs3_map_xlid_to_statdev (buf, fh->xlatorid); + poa = nfs3_stat_to_post_op_attr (buf); + nfs3_map_xlid_to_statdev (preparent, fh->xlatorid); + nfs3_map_xlid_to_statdev (postparent, fh->xlatorid); + wccdir = nfs3_stat_to_wcc_data (preparent, postparent); + res->mknod3res_u.resok.obj_attributes = poa; + res->mknod3res_u.resok.dir_wcc = wccdir; + +} + + +void +nfs3_fill_remove3res (remove3res *res, nfsstat3 stat, struct iatt *preparent, + struct iatt *postparent, uint16_t xlid) +{ + wcc_data dirwcc; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (preparent, xlid); + nfs3_map_xlid_to_statdev (postparent, xlid); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + res->remove3res_u.resok.dir_wcc = dirwcc; +} + + +void +nfs3_prep_remove3args (remove3args *args, struct nfs3_fh *fh, char *name) +{ + memset (args, 0, sizeof (*args)); + args->object.dir.data.data_val = (void *)fh; + args->object.name = name; +} + + +void +nfs3_prep_rmdir3args (rmdir3args *args, struct nfs3_fh *fh, char *name) +{ + memset (args, 0, sizeof (*args)); + args->object.dir.data.data_val = (void *)fh; + args->object.name = name; +} + + +void +nfs3_fill_rmdir3res (rmdir3res *res, nfsstat3 stat, struct iatt *preparent, + struct iatt *postparent, uint16_t xlid) +{ + wcc_data dirwcc; + memset (res, 0, sizeof (*res)); + res->status = stat; + + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (postparent, xlid); + nfs3_map_xlid_to_statdev (preparent, xlid); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + res->rmdir3res_u.resok.dir_wcc = dirwcc; +} + + +void +nfs3_prep_link3args (link3args *args, struct nfs3_fh *target, + struct nfs3_fh * dirfh, char *name) +{ + memset (args, 0, sizeof (*args)); + args->file.data.data_val = (void *)target; + args->link.dir.data.data_val = (void *)dirfh; + args->link.name = name; +} + + +void +nfs3_fill_link3res (link3res *res, nfsstat3 stat, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + uint16_t xlid) +{ + post_op_attr poa; + wcc_data dirwcc; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (preparent, xlid); + nfs3_map_xlid_to_statdev (postparent, xlid); + nfs3_map_xlid_to_statdev (buf, xlid); + poa = nfs3_stat_to_post_op_attr (buf); + dirwcc = nfs3_stat_to_wcc_data (preparent, postparent); + res->link3res_u.resok.file_attributes = poa; + res->link3res_u.resok.linkdir_wcc = dirwcc; +} + + + +void +nfs3_prep_rename3args (rename3args *args, struct nfs3_fh *olddirfh, + char *oldname, struct nfs3_fh *newdirfh, char *newname) +{ + memset (args, 0, sizeof (*args)); + + args->from.name = oldname; + args->from.dir.data.data_val = (void *)olddirfh; + args->to.name = newname; + args->to.dir.data.data_val = (void *)newdirfh; + +} + + +void +nfs3_fill_rename3res (rename3res *res, nfsstat3 stat, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + uint16_t xlid) + +{ + wcc_data dirwcc; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (preoldparent, xlid); + nfs3_map_xlid_to_statdev (postoldparent, xlid); + nfs3_map_xlid_to_statdev (prenewparent, xlid); + nfs3_map_xlid_to_statdev (postnewparent, xlid); + nfs3_map_xlid_to_statdev (buf, xlid); + dirwcc = nfs3_stat_to_wcc_data (preoldparent, postoldparent); + res->rename3res_u.resok.fromdir_wcc = dirwcc; + dirwcc = nfs3_stat_to_wcc_data (prenewparent, postnewparent); + res->rename3res_u.resok.todir_wcc = dirwcc; +} + + +void +nfs3_prep_write3args (write3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_write3res (write3res *res, nfsstat3 stat, count3 count, + stable_how stable, uint64_t wverf, struct iatt *prestat, + struct iatt *poststat, uint16_t xlid) +{ + write3resok resok; + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (prestat, xlid); + nfs3_map_xlid_to_statdev (poststat, xlid); + resok.file_wcc = nfs3_stat_to_wcc_data (prestat, poststat); + resok.count = count; + resok.committed = stable; + memcpy (resok.verf, &wverf, sizeof (wverf)); + + res->write3res_u.resok = resok; +} + + +void +nfs3_prep_commit3args (commit3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_commit3res (commit3res *res, nfsstat3 stat, uint64_t wverf, + struct iatt *prestat, struct iatt *poststat,uint16_t xlid) +{ + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (poststat, xlid); + nfs3_map_xlid_to_statdev (prestat, xlid); + res->commit3res_u.resok.file_wcc = nfs3_stat_to_wcc_data (prestat, + poststat); + memcpy (res->commit3res_u.resok.verf, &wverf, sizeof (wverf)); +} + +void +nfs3_fill_read3res (read3res *res, nfsstat3 stat, count3 count, + struct iatt *poststat, int is_eof, uint16_t xlid) +{ + post_op_attr poa; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (poststat, xlid); + poa = nfs3_stat_to_post_op_attr (poststat); + res->read3res_u.resok.file_attributes = poa; + res->read3res_u.resok.count = count; + res->read3res_u.resok.eof = is_eof; + res->read3res_u.resok.data.data_len = count; + +} + + +void +nfs3_prep_read3args (read3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->file.data.data_val = (void *)fh; +} + + +void +nfs3_fill_pathconf3res (pathconf3res *res, nfsstat3 stat, struct iatt *buf, + uint16_t xlid) +{ + pathconf3resok resok; + + memset (res, 0, sizeof (*res)); + res->status = stat; + if (stat != NFS3_OK) + return; + + nfs3_map_xlid_to_statdev (buf, xlid); + resok.obj_attributes = nfs3_stat_to_post_op_attr (buf); + resok.linkmax = 256; + resok.name_max = NFS_NAME_MAX; + resok.no_trunc = TRUE; + resok.chown_restricted = FALSE; + resok.case_insensitive = FALSE; + resok.case_preserving = TRUE; + + res->pathconf3res_u.resok = resok; +} + + +void +nfs3_prep_pathconf3args (pathconf3args *args, struct nfs3_fh *fh) +{ + memset (args, 0, sizeof (*args)); + args->object.data.data_val = (void *)fh; +} + + +int +nfs3_verify_dircookie (struct nfs3_state *nfs3, fd_t *dirfd, cookie3 cookie, + uint64_t cverf, nfsstat3 *stat) +{ + int ret = -1; + + if ((!nfs3) || (!dirfd)) + return -1; + + /* Can assume that this is first read on the dir, so cookie check + * is successful by default. + */ + if (cookie == 0) + return 0; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Verifying cookie: cverf: %"PRIu64 + ", cookie: %"PRIu64, cverf, cookie); + /* The cookie bad, no way cverf will be zero with a non-zero cookie. */ + if ((cverf == 0) && (cookie != 0)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Bad cookie requested"); + if (stat) + *stat = NFS3ERR_BAD_COOKIE; + goto err; + } + + /* Yes, its true, our cookie is simply the fd_t address. + * NOTE: We used have the check for cookieverf but VMWare client sends + * a readdirp requests even after we've told it that EOF has been + * reached on the directory. This causes a problem because we close a + * dir fd_t after reaching EOF. The next readdirp sent by VMWare + * contains the address of the closed fd_t as cookieverf. Since we + * closed that fd_t, this readdirp results in a new opendir which will + * give an fd_t that will fail this check below. + */ +/* if ((cverf != (uint64_t)dirfd)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Cookieverf does not match"); + if (stat) + *stat = NFS3ERR_BAD_COOKIE; + goto err; + } +*/ + gf_log (GF_NFS3, GF_LOG_TRACE, "Cookie verified"); + if (stat) + *stat = NFS3_OK; + ret = 0; +err: + return ret; +} + + +/* When remove a file, we need to unref the cached fd for an inode but this + * needs to happen only when the file was in fact opened. However, it is + * possible that fd_lookup on a file returns an fd because the file was in + * process of being created(which also returns an fd) but since this fd was not + * opened through this path, in the NFS3 remove path, we'll end up removing the + * reference that belongs to someone else. That means, nfs3 remove path should + * not unref unless it is sure that the file was cached open also. If it was, + * only then perform the fd_unref, else not. + * + * We determine that using a flag in the inode context. + */ +int +nfs3_set_inode_opened (xlator_t *nfsxl, inode_t *inode) +{ + if ((!nfsxl) || (!inode)) + return -1; + + inode_ctx_put (inode, nfsxl, GF_NFS3_FD_CACHED); + + return 0; +} + + +/* Returns 1 if inode was cached open, otherwise 0 */ +int +nfs3_cached_inode_opened (xlator_t *nfsxl, inode_t *inode) +{ + int ret = -1; + uint64_t cflag = 0; + + if ((!nfsxl) || (!inode)) + return -1; + + ret = inode_ctx_get (inode, nfsxl, &cflag); + if (ret == -1) + ret = 0; + else if (cflag == GF_NFS3_FD_CACHED) + ret = 1; + + return ret; +} + + +int32_t +nfs3_dir_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd) +{ + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + cs->resolve_ret = -1; + cs->resolve_errno = op_errno; + nfs3_call_resume (cs); + goto err; + } + + cs->fd = fd_ref (fd); + nfs3_set_inode_opened (cs->nfsx, cs->resolvedloc.inode); + gf_log (GF_NFS3, GF_LOG_TRACE, "FD_REF: %d", fd->refcount); + nfs3_call_resume (cs); +err: + return 0; +} + + +int +__nfs3_dir_open_and_resume (nfs3_call_state_t *cs) +{ + nfs_user_t nfu = {0, }; + int ret = -EFAULT; + + if (!cs) + return ret; + + nfs_user_root_create (&nfu); + ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, nfs3_dir_open_cbk, + cs); + return ret; +} + + +int +nfs3_dir_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume) +{ + fd_t *fd = NULL; + int ret = -EFAULT; + + if ((!cs)) + return ret; + + cs->resume_fn = resume; + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening: %s", cs->resolvedloc.path); + fd = fd_lookup (cs->resolvedloc.inode, 0); + if (fd) { + gf_log (GF_NFS3, GF_LOG_TRACE, "fd found in state: ref: %d", fd->refcount); + cs->fd = fd_ref (fd); /* Gets unrefd when the call state is wiped. */ + cs->resolve_ret = 0; + nfs3_call_resume (cs); + ret = 0; + goto err; + } + + ret = __nfs3_dir_open_and_resume (cs); + +err: + return ret; +} + + +int +nfs3_flush_open_wait_call_states (nfs3_call_state_t *cs, fd_t *openedfd) +{ + struct list_head *inode_q = NULL; + uint64_t ctxaddr = 0; + int ret = 0; + nfs3_call_state_t *cstmp = NULL; + + if (!cs) + return -1; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Flushing call state"); + ret = inode_ctx_get (cs->resolvedloc.inode, cs->nfsx, &ctxaddr); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "No inode queue present"); + goto out; + } + + inode_q = (struct list_head *)(long)ctxaddr; + if (!inode_q) + goto out; + + list_for_each_entry_safe (cs, cstmp, inode_q, openwait_q) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Calling resume"); + cs->resolve_ret = 0; + if (openedfd) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd done: %d", + openedfd->refcount); + cs->fd = fd_ref (openedfd); + } + nfs3_call_resume (cs); + } + +out: + return 0; +} + + + +int +__nfs3_fdcache_update_entry (struct nfs3_state *nfs3, fd_t *fd) +{ + uint64_t ctxaddr = 0; + struct nfs3_fd_entry *fde = NULL; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Updating fd: 0x%lx", (long int)fd); + fd_ctx_get (fd, nfs3->nfsx, &ctxaddr); + fde = (struct nfs3_fd_entry *)(long)ctxaddr; + list_del (&fde->list); + list_add_tail (&fde->list, &nfs3->fdlru); + + return 0; +} + + +int +nfs3_fdcache_update (struct nfs3_state *nfs3, fd_t *fd) +{ + if ((!nfs3) || (!fd)) + return -1; + + LOCK (&nfs3->fdlrulock); + { + __nfs3_fdcache_update_entry (nfs3, fd); + } + UNLOCK (&nfs3->fdlrulock); + + return 0; +} + + +int +__nfs3_fdcache_remove_entry (struct nfs3_state *nfs3, struct nfs3_fd_entry *fde) +{ + gf_log (GF_NFS3, GF_LOG_TRACE, "Removing fd: 0x%lx: %d", + (long int)fde->cachedfd, fde->cachedfd->refcount); + list_del (&fde->list); + fd_ctx_del (fde->cachedfd, nfs3->nfsx, NULL); + fd_unref (fde->cachedfd); + FREE (fde); + --nfs3->fdcount; + + return 0; +} + + +int +nfs3_fdcache_remove (struct nfs3_state *nfs3, fd_t *fd) +{ + struct nfs3_fd_entry *fde = NULL; + uint64_t ctxaddr = 0; + + if ((!nfs3) || (!fd)) + return -1; + + LOCK (&nfs3->fdlrulock); + { + fd_ctx_get (fd, nfs3->nfsx, &ctxaddr); + fde = (struct nfs3_fd_entry *)(long)ctxaddr; + __nfs3_fdcache_remove_entry (nfs3, fde); + } + UNLOCK (&nfs3->fdlrulock); + + return 0; +} + + +int +__nfs3_fdcache_replace (struct nfs3_state *nfs3) +{ + struct nfs3_fd_entry *fde = NULL; + struct nfs3_fd_entry *tmp = NULL; + + if (!nfs3) + return -1; + + if (nfs3->fdcount <= GF_NFS3_FDCACHE_SIZE) + return 0; + + list_for_each_entry_safe (fde, tmp, &nfs3->fdlru, list) + break; + + __nfs3_fdcache_remove_entry (nfs3, fde); + + return 0; +} + + + +int +nfs3_fdcache_add (struct nfs3_state *nfs3, fd_t *fd) +{ + struct nfs3_fd_entry *fde = NULL; + int ret = -1; + + if ((!nfs3) || (!fd)) + return -1; + + fde = CALLOC (1, sizeof (*fd)); + if (!fde) { + gf_log (GF_NFS3, GF_LOG_ERROR, "fd entry allocation failed"); + goto out; + } + + /* Already refd by caller. */ + fde->cachedfd = fd; + INIT_LIST_HEAD (&fde->list); + + LOCK (&nfs3->fdlrulock); + { + gf_log (GF_NFS3, GF_LOG_TRACE, "Adding fd: 0x%lx", + (long int) fd); + fd_ctx_set (fd, nfs3->nfsx, (uint64_t)fde); + fd_bind (fd); + list_add_tail (&fde->list, &nfs3->fdlru); + ++nfs3->fdcount; + __nfs3_fdcache_replace (nfs3); + } + UNLOCK (&nfs3->fdlrulock); + +out: + return ret; +} + + +int32_t +nfs3_file_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd) +{ + nfs3_call_state_t *cs = NULL; + struct nfs3_state *nfs3 = NULL; + + cs = frame->local; + if (op_ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd failed"); + cs->resolve_ret = -1; + cs->resolve_errno = op_errno; + fd = NULL; + } else { + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd done: %d", + fd->refcount); + } + + nfs3 = rpcsvc_request_program_private (cs->req); + nfs3_flush_open_wait_call_states (cs, fd); + nfs3_fdcache_add (nfs3, fd); + return 0; +} + +/* Returns 1 if the current call is the first one to be queued. If so, the + * caller will need to send the open fop. If this is a non-first call to be + * queued, it means the fd opening is in progress. + * + * Returns 0, if this is a non-first call. + */ +int +__nfs3_queue_call_state (nfs3_call_state_t *cs) +{ + struct list_head *inode_q = NULL; + int ret = -1; + uint64_t ctxaddr = 0; + + ret = __inode_ctx_get (cs->resolvedloc.inode, cs->nfsx, &ctxaddr); + if (ret == 0) { + inode_q = (struct list_head *)(long)ctxaddr; + goto attach_cs; + } + + inode_q = CALLOC (1, sizeof (*inode_q)); + if (!inode_q) + goto err; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Initing inode queue"); + INIT_LIST_HEAD (inode_q); + __inode_ctx_put (cs->resolvedloc.inode, cs->nfsx, (uint64_t)inode_q); + +attach_cs: + if (list_empty (inode_q)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "First call in queue"); + ret = 1; + } else + ret = 0; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Queueing call state"); + list_add_tail (&cs->openwait_q, inode_q); + +err: + return ret; +} + + +int +nfs3_queue_call_state (nfs3_call_state_t *cs) +{ + int ret = 0; + if (!cs) + return -1; + + LOCK (&cs->resolvedloc.inode->lock); + { + ret = __nfs3_queue_call_state (cs); + } + UNLOCK (&cs->resolvedloc.inode->lock); + + return ret; +} + + +int +__nfs3_file_open_and_resume (nfs3_call_state_t *cs) +{ + nfs_user_t nfu = {0, }; + int ret = -EFAULT; + + if (!cs) + return ret; + + ret = nfs3_queue_call_state (cs); + if (ret != 1) { + ret = 0; + goto out; + } + + nfs_user_root_create (&nfu); + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening uncached fd"); + ret = nfs_open (cs->vol, &nfu, &cs->resolvedloc, O_RDWR, + nfs3_file_open_cbk, cs); +out: + return ret; +} + + + +int +nfs3_file_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume) +{ + fd_t *fd = NULL; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + + if ((!cs)) + return ret; + + cs->resume_fn = resume; + gf_log (GF_NFS3, GF_LOG_TRACE, "Opening: %s", cs->resolvedloc.path); + fd = fd_lookup (cs->resolvedloc.inode, 0); + if (fd) { + nfs3 = rpcsvc_request_program_private (cs->req); + /* Already refd by fd_lookup, so no need to ref again. */ + gf_log (GF_NFS3, GF_LOG_TRACE, "fd found in state: %d", + fd->refcount); + nfs3_fdcache_update (nfs3, fd); + cs->fd = fd; /* Gets unrefd when the call state is wiped. */ + cs->resolve_ret = 0; + nfs3_call_resume (cs); + ret = 0; + goto err; + } + + ret = __nfs3_file_open_and_resume (cs); + +err: + return ret; +} + + +void +nfs3_stat_to_errstr (uint32_t xid, char *op, nfsstat3 stat, int pstat, + char *errstr) +{ + if ((!op) || (!errstr)) + return; + + sprintf (errstr, "XID: %x, %s: NFS: %d(%s), POSIX: %d(%s)", xid, op, + stat, nfsstat3_strerror (stat), pstat, strerror (pstat)); +} + +void +nfs3_log_common_call (uint32_t xid, char *op, struct nfs3_fh *fh) +{ + char fhstr[1024]; + + nfs3_fh_to_str (fh, fhstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s", xid, op, + fhstr); +} + + +void +nfs3_log_fh_entry_call (uint32_t xid, char *op, struct nfs3_fh *fh, + char *name) +{ + char fhstr[1024]; + + nfs3_fh_to_str (fh, fhstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, name: %s", xid, + op, fhstr, name); +} + + +void +nfs3_log_rename_call (uint32_t xid, struct nfs3_fh *src, char *sname, + struct nfs3_fh *dst, char *dname) +{ + char sfhstr[1024]; + char dfhstr[1024]; + + nfs3_fh_to_str (src, sfhstr); + nfs3_fh_to_str (dst, dfhstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, RENAME: args: Src: %s, " + "name: %s, Dst: %s, name: %s", xid, sfhstr, sname, dfhstr, + dname); +} + + + +void +nfs3_log_create_call (uint32_t xid, struct nfs3_fh *fh, char *name, + createmode3 mode) +{ + char fhstr[1024]; + char *modestr = NULL; + char exclmode[] = "EXCLUSIVE"; + char unchkd[] = "UNCHECKED"; + char guarded[] = "GUARDED"; + + nfs3_fh_to_str (fh, fhstr); + if (mode == EXCLUSIVE) + modestr = exclmode; + else if (mode == GUARDED) + modestr = guarded; + else + modestr = unchkd; + + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, CREATE: args: %s, name: %s," + " mode: %s", xid, fhstr, name, modestr); +} + + +void +nfs3_log_mknod_call (uint32_t xid, struct nfs3_fh *fh, char *name, int type) +{ + char fhstr[1024]; + char *modestr = NULL; + char chr[] = "CHAR"; + char blk[] = "BLK"; + char sock[] = "SOCK"; + char fifo[] = "FIFO"; + + nfs3_fh_to_str (fh, fhstr); + if (type == NF3CHR) + modestr = chr; + else if (type == NF3BLK) + modestr = blk; + else if (type == NF3SOCK) + modestr = sock; + else + modestr = fifo; + + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, MKNOD: args: %s, name: %s," + " type: %s", xid, fhstr, name, modestr); +} + + + +void +nfs3_log_symlink_call (uint32_t xid, struct nfs3_fh *fh, char *name, char *tgt) +{ + char fhstr[1024]; + + nfs3_fh_to_str (fh, fhstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, SYMLINK: args: %s, name: %s," + " target: %s", xid, fhstr, name, tgt); +} + + +void +nfs3_log_link_call (uint32_t xid, struct nfs3_fh *fh, char *name, + struct nfs3_fh *tgt) +{ + char dfhstr[1024]; + char tfhstr[1024]; + + nfs3_fh_to_str (fh, dfhstr); + nfs3_fh_to_str (tgt, tfhstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, LINK: args: %s, name: %s," + " target: %s", xid, dfhstr, name, tfhstr); +} + + +void +nfs3_log_rw_call (uint32_t xid, char *op, struct nfs3_fh *fh, offset3 offt, + count3 count, int stablewrite) +{ + char fhstr[1024]; + + nfs3_fh_to_str (fh, fhstr); + if (stablewrite == -1) + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, offset:" + " %"PRIu64", count: %"PRIu64, xid, op, fhstr, offt, + count); + else + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, %s: args: %s, offset:" + " %"PRIu64", count: %"PRIu64", %s", xid, op, fhstr, + offt, count, + (stablewrite == UNSTABLE)?"UNSTABLE":"STABLE"); + +} + + +void +nfs3_log_common_res (uint32_t xid, char *op, nfsstat3 stat, int pstat) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, op, stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s", errstr); +} + +void +nfs3_log_readlink_res (uint32_t xid, nfsstat3 stat, int pstat, char *linkpath) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "READLINK", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, target: %s", errstr, linkpath); + +} + +void +nfs3_log_read_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, + int is_eof) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "READ", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", is_eof: %d", + errstr, count, is_eof); +} + + +void +nfs3_log_write_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, + int stable, uint64_t wverf) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "WRITE", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", %s,wverf: %"PRIu64 + , errstr, count, (stable == UNSTABLE)?"UNSTABLE":"STABLE", + wverf); +} + + +void +nfs3_log_newfh_res (uint32_t xid, char *op, nfsstat3 stat, int pstat, + struct nfs3_fh *newfh) +{ + char errstr[1024]; + char fhstr[1024]; + + nfs3_stat_to_errstr (xid, op, stat, pstat, errstr); + nfs3_fh_to_str (newfh, fhstr); + + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, %s", errstr, fhstr); +} + + +void +nfs3_log_readdir_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, + count3 count, int is_eof) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "READDIR", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, count: %"PRIu64", cverf: %"PRIu64 + ", is_eof: %d", errstr, count, cverf, is_eof); +} + + +void +nfs3_log_readdirp_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, + count3 dircount, count3 maxcount, int is_eof) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "READDIRPLUS", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, dircount: %"PRIu64", maxcount: %" + PRIu64", cverf: %"PRIu64", is_eof: %d", errstr, dircount, + maxcount, cverf, is_eof); +} + + +void +nfs3_log_commit_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t wverf) +{ + char errstr[1024]; + + nfs3_stat_to_errstr (xid, "COMMIT", stat, pstat, errstr); + gf_log (GF_NFS3, GF_LOG_DEBUG, "%s, wverf: %"PRIu64, errstr, wverf); +} + + +void +nfs3_log_readdir_call (uint32_t xid, struct nfs3_fh *fh, count3 dircount, + count3 maxcount) +{ + char fhstr[1024]; + + nfs3_fh_to_str (fh, fhstr); + + if (maxcount == 0) + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, READDIR: args: %s," + " count: %d", xid, fhstr, (uint32_t)dircount); + else + gf_log (GF_NFS3, GF_LOG_DEBUG, "XID: %x, READDIRPLUS: args: %s," + " dircount: %d, maxcount: %d", xid, fhstr, + (uint32_t)dircount, (uint32_t)maxcount); +} + + +int +nfs3_fh_resolve_inode_done (nfs3_call_state_t *cs, inode_t *inode) +{ + int ret = -EFAULT; + + if ((!cs) || (!inode)) + return ret; + + gf_log (GF_NFS3, GF_LOG_TRACE, "FH inode resolved"); + ret = nfs_inode_loc_fill (inode, &cs->resolvedloc); + if (ret < 0) + goto err; + + nfs3_call_resume (cs); + +err: + return ret; +} + +#define GF_NFS3_FHRESOLVE_FOUND 1 +#define GF_NFS3_FHRESOLVE_NOTFOUND 2 +#define GF_NFS3_FHRESOLVE_DIRFOUND 3 + +int +nfs3_fh_resolve_check_entry (struct nfs3_fh *fh, gf_dirent_t *candidate, + int hashidx) +{ + struct iatt *ia = NULL; + int ret = GF_NFS3_FHRESOLVE_NOTFOUND; + nfs3_hash_entry_t entryhash = 0; + + if ((!fh) || (!candidate)) + return ret; + + if ((strcmp (candidate->d_name, ".") == 0) || + (strcmp (candidate->d_name, "..") == 0)) + goto found_entry; + + ia = &candidate->d_stat; + if ((ia->ia_gen == fh->gen) && (ia->ia_ino == fh->ino)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Found entry: gen: %"PRId64 + " ino: %"PRId64", name: %s", ia->ia_gen, ia->ia_ino, + candidate->d_name); + ret = GF_NFS3_FHRESOLVE_FOUND; + goto found_entry; + } + + /* This condition ensures that we never have to be afraid of having + * a directory hash conflict with a file hash. The consequence of + * this condition is that we can now have unlimited files in a directory + * and upto 65536 sub-directories in a directory. + */ + if (!IA_ISDIR (candidate->d_stat.ia_type)) + goto found_entry; + entryhash = fh->entryhash[hashidx]; + if (entryhash == nfs3_fh_hash_entry (ia->ia_ino, ia->ia_gen)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Found hash match: %s: %d", + candidate->d_name, entryhash); + ret = GF_NFS3_FHRESOLVE_DIRFOUND; + goto found_entry; + } + +found_entry: + + return ret; +} + + +int32_t +nfs3_fh_resolve_entry_lookup_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xattr, + struct iatt *postparent) +{ + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + cs->resolve_ret = op_ret; + cs->resolve_errno = op_errno; + + if (op_ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", + cs->resolvedloc.path, strerror (op_errno)); + goto err; + } else + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry looked up: %s", + cs->resolvedloc.path); + + inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); +err: + nfs3_call_resume (cs); + return 0; +} + + + +int32_t +nfs3_fh_resolve_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + gf_dirent_t *entries); + +int +nfs3_fh_resolve_found_entry (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ + uint64_t dirino = 0; + uint64_t dirgen = 0; + int ret = 0; + nfs_user_t nfu = {0, }; + + if ((!cs) || (!candidate)) + return -EFAULT; + + dirino = cs->resolvedloc.inode->ino; + dirgen = cs->resolvedloc.inode->generation; + + nfs_loc_wipe (&cs->resolvedloc); + ret = nfs_entry_loc_fill (cs->vol->itable, dirino, dirgen, + candidate->d_name, &cs->resolvedloc, + NFS_RESOLVE_CREATE); + if (ret == -ENOENT) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry not in itable, needs" + " lookup"); + nfs_user_root_create (&nfu); + ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_entry_lookup_cbk, + cs); + } else { + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry got from itable"); + nfs3_call_resume (cs); + } + + return ret; +} + + +int32_t +nfs3_fh_resolve_parent_lookup_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xattr, + struct iatt *postparent) +{ + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + cs->resolve_ret = op_ret; + cs->resolve_errno = op_errno; + + if (op_ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", + cs->resolvedloc.path, strerror (op_errno)); + nfs3_call_resume (cs); + goto err; + } else + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry looked up: %s", + cs->resolvedloc.path); + + inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); + nfs3_fh_resolve_entry_hard (cs); + +err: + return 0; +} + + +int +nfs3_fh_resolve_found_parent (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ + uint64_t dirino = 0; + uint64_t dirgen = 0; + int ret = 0; + nfs_user_t nfu = {0, }; + + if ((!cs) || (!candidate)) + return -EFAULT; + + dirino = cs->resolvedloc.inode->ino; + dirgen = cs->resolvedloc.inode->generation; + + nfs_loc_wipe (&cs->resolvedloc); + ret = nfs_entry_loc_fill (cs->vol->itable, dirino, dirgen, + candidate->d_name, &cs->resolvedloc, + NFS_RESOLVE_CREATE); + if (ret == -ENOENT) { + nfs_user_root_create (&nfu); + ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_parent_lookup_cbk, + cs); + } else + nfs3_fh_resolve_entry_hard (cs); + + return ret; +} + + +int +nfs3_fh_resolve_found (nfs3_call_state_t *cs, gf_dirent_t *candidate) +{ + int ret = 0; + + if ((!cs) || (!candidate)) + return -EFAULT; + + if (!cs->resolventry) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Candidate entry was found"); + ret = nfs3_fh_resolve_found_entry (cs, candidate); + } else { + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry's parent was found"); + ret = nfs3_fh_resolve_found_parent (cs, candidate); + } + + return ret; +} + + +int32_t +nfs3_fh_resolve_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd) +{ + nfs3_call_state_t *cs = NULL; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + cs = frame->local; + cs->resolve_ret = op_ret; + cs->resolve_errno = op_errno; + + if (op_ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir open failed: %s: %s", + cs->resolvedloc.path, strerror (op_errno)); + nfs3_call_resume (cs); + goto err; + } else + gf_log (GF_NFS3, GF_LOG_TRACE, "Reading directory: %s", + cs->resolvedloc.path); + + nfs_user_root_create (&nfu); + /* This function can be called in a recursive code path, so if another + * directory was opened in an earlier call, we must unref through this + * reference before opening another fd_t. + */ + if (cs->resolve_dir_fd) + fd_unref (cs->resolve_dir_fd); + + cs->resolve_dir_fd = fd_ref (fd); + ret = nfs_readdirp (cs->vol, &nfu, fd, GF_NFS3_DTPREF, 0, + nfs3_fh_resolve_readdir_cbk, cs); + +err: + return ret; +} + +int32_t +nfs3_fh_resolve_dir_lookup_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret,int32_t op_errno, + inode_t *inode, struct iatt *buf, dict_t *xattr, + struct iatt *postparent) +{ + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + cs = frame->local; + cs->resolve_ret = op_ret; + cs->resolve_errno = op_errno; + + if (op_ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Lookup failed: %s: %s", + cs->resolvedloc.path, strerror (op_errno)); + nfs3_call_resume (cs); + goto err; + } else + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", + cs->resolvedloc.path); + + nfs_user_root_create (&nfu); + inode_link (inode, cs->resolvedloc.parent, cs->resolvedloc.name, buf); + nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_opendir_cbk, cs); + +err: + return 0; +} + + + +int +nfs3_fh_resolve_dir_hard (nfs3_call_state_t *cs, uint64_t ino, uint64_t gen, + char *entry) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + cs->hashidx++; + nfs_loc_wipe (&cs->resolvedloc); + if (nfs3_fh_hash_index_is_beyond (&cs->resolvefh, cs->hashidx)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Hash index is beyond: idx %d, " + " fh idx: %d", cs->hashidx, cs->resolvefh.hashcount); + nfs3_call_resume_estale (cs); + ret = 0; + goto out; + } + + nfs_user_root_create (&nfu); + gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard dir resolution: ino:" + " %"PRIu64", gen: %"PRIu64", entry: %s, hashidx: %d", + ino, gen, entry, cs->hashidx); + ret = nfs_entry_loc_fill (cs->vol->itable, ino, gen, entry, + &cs->resolvedloc, NFS_RESOLVE_CREATE); + + if (ret == 0) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", + cs->resolvedloc.path); + ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_opendir_cbk, cs); + } else if (ret == -ENOENT) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir needs lookup: %s", + cs->resolvedloc.path); + ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_dir_lookup_cbk, cs); + } +out: + return ret; +} + + +int +nfs3_fh_resolve_check_response (nfs3_call_state_t *cs, gf_dirent_t *candidate, + int response, off_t last_offt) +{ + uint64_t dirino = 0; + uint64_t dirgen = 0; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + dirino = cs->resolvedloc.inode->ino; + dirgen = cs->resolvedloc.inode->generation; + + if (response == GF_NFS3_FHRESOLVE_DIRFOUND) + ret = nfs3_fh_resolve_dir_hard (cs, dirino, dirgen, + candidate->d_name); + else if (response == GF_NFS3_FHRESOLVE_FOUND) + nfs3_fh_resolve_found (cs, candidate); + else if (response == GF_NFS3_FHRESOLVE_NOTFOUND) { + nfs_user_root_create (&nfu); + ret = nfs_readdirp (cs->vol, &nfu, cs->resolve_dir_fd, + GF_NFS3_DTPREF, last_offt, + nfs3_fh_resolve_readdir_cbk, cs); + } + + return 0; +} + +int +nfs3_fh_resolve_search_dir (nfs3_call_state_t *cs, gf_dirent_t *entries) +{ + gf_dirent_t *candidate = NULL; + int ret = GF_NFS3_FHRESOLVE_NOTFOUND; + off_t lastoff = 0; + + if ((!cs) || (!entries)) + return -EFAULT; + + if (list_empty (&entries->list)) + goto not_found; + + list_for_each_entry (candidate, &entries->list, list) { + lastoff = candidate->d_off; + gf_log (GF_NFS3, GF_LOG_TRACE, "Candidate: %s, ino: %"PRIu64 + ", gen: %"PRIu64, candidate->d_name, candidate->d_ino, + candidate->d_stat.ia_gen); + ret = nfs3_fh_resolve_check_entry (&cs->resolvefh, candidate, + cs->hashidx); + if (ret != GF_NFS3_FHRESOLVE_NOTFOUND) + break; + } + +not_found: + nfs3_fh_resolve_check_response (cs, candidate, ret, lastoff); + return ret; +} + + +int32_t +nfs3_fh_resolve_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + gf_dirent_t *entries) +{ + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret <= 0) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Directory read done: %s: %s", + cs->resolvedloc.path, strerror (op_ret)); + cs->resolve_ret = -1; + cs->resolve_errno = ENOENT; + nfs3_call_resume (cs); + goto err; + } + + nfs3_fh_resolve_search_dir (cs, entries); + +err: + return 0; +} + +/* Needs no extra argument since it knows that the fh to be resolved is in + * resolvefh and that it needs to start looking from the root. + */ +int +nfs3_fh_resolve_inode_hard (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + cs->hashidx++; + nfs_loc_wipe (&cs->resolvedloc); + if (nfs3_fh_hash_index_is_beyond (&cs->resolvefh, cs->hashidx)) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Hash index is beyond: idx %d, " + " fh idx: %d", cs->hashidx, cs->resolvefh.hashcount); + nfs3_call_resume_estale (cs); + ret = 0; + goto out; + } + + nfs_user_root_create (&nfu); + gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard resolution: ino:" + " %"PRIu64", gen: %"PRIu64", hashidx: %d", cs->resolvefh.ino, + cs->resolvefh.gen, cs->hashidx); + ret = nfs_ino_loc_fill (cs->vol->itable, 1, 0, &cs->resolvedloc); + + if (ret == 0) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir will be opened: %s", + cs->resolvedloc.path); + ret = nfs_opendir (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_opendir_cbk, cs); + } else if (ret == -ENOENT) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Dir needs lookup: %s", + cs->resolvedloc.path); + ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_dir_lookup_cbk, cs); + } + +out: + return ret; +} + + +int +nfs3_fh_resolve_entry_hard (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + nfs_loc_wipe (&cs->resolvedloc); + nfs_user_root_create (&nfu); + gf_log (GF_NFS3, GF_LOG_TRACE, "FH hard resolution: ino:" + " %"PRIu64", gen: %"PRIu64", entry: %s, hashidx: %d", + cs->resolvefh.ino, cs->resolvefh.gen, cs->resolventry, + cs->hashidx); + + ret = nfs_entry_loc_fill (cs->vol->itable, cs->resolvefh.ino, + cs->resolvefh.gen, cs->resolventry, + &cs->resolvedloc, NFS_RESOLVE_CREATE); + + if (ret == -2) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry needs lookup: %s", + cs->resolvedloc.path); + ret = nfs_fop_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fh_resolve_entry_lookup_cbk, cs); + ret = 0; + } else if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Entry needs parent lookup: %s", + cs->resolvedloc.path); + ret = nfs3_fh_resolve_inode_hard (cs); + } else if (ret == 0) { + cs->resolve_ret = 0; + nfs3_call_resume (cs); + } + + return ret; +} + +int +nfs3_fh_resolve_inode (nfs3_call_state_t *cs) +{ + inode_t *inode = NULL; + int ret = -EFAULT; + + if (!cs) + return ret; + + gf_log (GF_NFS3, GF_LOG_TRACE, "FH needs inode resolution"); + inode = inode_get (cs->vol->itable, cs->resolvefh.ino, + cs->resolvefh.gen); + if (!inode) + ret = nfs3_fh_resolve_inode_hard (cs); + else + ret = nfs3_fh_resolve_inode_done (cs, inode); + + if (inode) + inode_unref (inode); + + return ret; +} + +int +nfs3_fh_resolve_entry (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + + if (!cs) + return ret; + + ret = nfs3_fh_resolve_entry_hard (cs); + if (ret < 0) { + cs->resolve_ret = -1; + cs->resolve_errno = ESTALE; + nfs3_call_resume (cs); + } + + return 0; +} + +int +nfs3_fh_resolve_and_resume (nfs3_call_state_t *cs, struct nfs3_fh *fh, + char *entry, nfs3_resume_fn_t resum_fn) +{ + int ret = -EFAULT; + + if ((!cs) || (!fh)) + return ret; + + cs->resume_fn = resum_fn; + cs->resolvefh = *fh; + cs->hashidx = 0; + + if (!entry) + ret = nfs3_fh_resolve_inode (cs); + else { + cs->resolventry = strdup (entry); + if (!cs->resolventry) + goto err; + + ret = nfs3_fh_resolve_entry (cs); + } + +err: + return ret; +} + + diff --git a/xlators/nfs/server/src/nfs3-helpers.h b/xlators/nfs/server/src/nfs3-helpers.h new file mode 100644 index 00000000000..a282bce6722 --- /dev/null +++ b/xlators/nfs/server/src/nfs3-helpers.h @@ -0,0 +1,342 @@ +/* + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS 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. + + GlusterFS 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/>. +*/ + +#ifndef _NFS3_HELPER_H_ +#define _NFS3_HELPER_H_ +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + + +#include "xlator.h" +#include "nfs3.h" +#include "nfs3-fh.h" +#include "msg-nfs3.h" +#include "xdr-nfs3.h" + +#include <sys/statvfs.h> + +#define GF_NFS3_FD_CACHED 0xcaced + +extern struct nfs3_fh +nfs3_extract_lookup_fh (lookup3args *args); + +extern char * +nfs3_extract_lookup_name (lookup3args *args); + +extern nfsstat3 +nfs3_errno_to_nfsstat3 (int errnum); + +extern void +nfs3_fill_lookup3res (lookup3res *res, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *stbuf, struct iatt *postparent); + +extern post_op_attr +nfs3_stat_to_post_op_attr (struct iatt *buf); + +extern struct nfs3_fh +nfs3_extract_getattr_fh (getattr3args *args); + +extern void +nfs3_fill_getattr3res (getattr3res *res, nfsstat3 stat, struct iatt *buf, + uint16_t xlid); + +extern struct nfs3_fh +nfs3_extract_fsinfo_fh (fsinfo3args *args); + +extern void +nfs3_fill_fsinfo3res (struct nfs3_state *nfs3, fsinfo3res *res, + nfsstat3 status, struct iatt *fsroot, uint16_t xlid); + +/* Functions containing _prep_ are used specifically to work around + * the memory allocations that happen inside Sun RPC library. + * In that library, there are numerous places where every NFS request + * can result in really tiny malloc calls. I fear the memory fragmentation + * that will follow. After studying the points at and the way in which malloc + * is called in Sun RPC, I've come up with this work-around. It is based on + * the idea that if the user/caller of the xdr_to_XXXXargs functions can provide + * already allocated memory or provide references to memory areas on its stack + * just for the short-term purpose of decoding the message from XDR format, we + * can avoid the memory allocations in Sun RPC. This is based on the fact + * that Sun RPC first checks whether structure members which require memory + * are NULL or not and only then calls malloc. In this case, if the caller + * provided references are non-NULL, then the if-branches containing malloc + * in Sun RPC will be avoided. + * PS: You're not expected to understand this unless you've spent some time + * looking through the glibc/sunrpc sources. + */ +extern void +nfs3_prep_lookup3args (lookup3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_prep_getattr3args (getattr3args *args, struct nfs3_fh *fh); + +extern void +nfs3_prep_fsinfo3args (fsinfo3args *args, struct nfs3_fh *root); + +extern char * +nfsstat3_strerror(int stat); + +extern void +nfs3_prep_access3args (access3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_access3res (access3res *res, nfsstat3 status, struct iatt *buf, + uint32_t accbits, uid_t uid, gid_t gid, uint16_t xlid); + +extern char * +nfs3_fhcache_getpath (struct nfs3_state *nfs3, struct nfs3_fh *fh); + +extern int +nfs3_fhcache_putpath (struct nfs3_state *nfs3, struct nfs3_fh *fh, char *path); + +extern void +nfs3_prep_readdir3args (readdir3args *ra, struct nfs3_fh *fh); + +extern void +nfs3_fill_readdir3res (readdir3res *res, nfsstat3 stat, uint64_t cverf, + struct iatt *dirstat, gf_dirent_t *entries,count3 count, + int is_eof, uint16_t xlid); + +extern void +nfs3_prep_readdirp3args (readdirp3args *ra, struct nfs3_fh *fh); + +extern void +nfs3_fill_readdirp3res (readdirp3res *res, nfsstat3 stat, struct nfs3_fh *dirfh, + uint64_t cverf, struct iatt *dirstat, + gf_dirent_t *entries, count3 dircount, count3 maxcount, + int is_eof); + +extern void +nfs3_free_readdirp3res (readdirp3res *res); + +extern void +nfs3_free_readdir3res (readdir3res *res); + +extern void +nfs3_prep_fsstat3args (fsstat3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_fsstat3res (fsstat3res *res, nfsstat3 stat, struct statvfs *fsbuf, + struct iatt *postbuf, uint16_t xlid); + +extern int32_t +nfs3_sattr3_to_setattr_valid (sattr3 *sattr, struct iatt *buf, mode_t *omode); +extern void +nfs3_fill_create3res (create3res *res, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *newbuf, struct iatt *preparent, + struct iatt *postparent); + +extern void +nfs3_prep_create3args (create3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_prep_setattr3args (setattr3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_setattr3res (setattr3res *res, nfsstat3 stat, struct iatt *preop, + struct iatt *postop, uint16_t xlid); + +extern void +nfs3_prep_mkdir3args (mkdir3args *args, struct nfs3_fh *dirfh, char *name); + +extern void +nfs3_fill_mkdir3res (mkdir3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent); + +extern void +nfs3_prep_symlink3args (symlink3args *args, struct nfs3_fh *dirfh, char *name, + char *target); + +extern void +nfs3_fill_symlink3res (symlink3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent); + +extern void +nfs3_prep_readlink3args (readlink3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_readlink3res (readlink3res *res, nfsstat3 stat, char *path, + struct iatt *buf, uint16_t xlid); + +extern void +nfs3_prep_mknod3args (mknod3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_mknod3res (mknod3res *res, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent); + +extern void +nfs3_fill_remove3res (remove3res *res, nfsstat3 stat, struct iatt *preparent, + struct iatt *postparent, uint16_t xlid); +extern void +nfs3_prep_remove3args (remove3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_rmdir3res (rmdir3res *res, nfsstat3 stat, struct iatt *preparent, + struct iatt *postparent, uint16_t xlid); + +extern void +nfs3_prep_rmdir3args (rmdir3args *args, struct nfs3_fh *fh, char *name); + +extern void +nfs3_fill_link3res (link3res *res, nfsstat3 stat, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + uint16_t xlid); + +extern void +nfs3_prep_link3args (link3args *args, struct nfs3_fh *target, + struct nfs3_fh * dirfh, char *name); + +extern void +nfs3_prep_rename3args (rename3args *args, struct nfs3_fh *olddirfh, + char *oldname, struct nfs3_fh *newdirfh, + char *newname); + +extern void +nfs3_fill_rename3res (rename3res *res, nfsstat3 stat, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + uint16_t xlid); + +extern void +nfs3_prep_write3args (write3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_write3res (write3res *res, nfsstat3 stat, count3 count, + stable_how stable, uint64_t wverf, struct iatt *prestat, + struct iatt *poststat, uint16_t xlid); + +extern void +nfs3_prep_commit3args (commit3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_commit3res (commit3res *res, nfsstat3 stat, uint64_t wverf, + struct iatt *prestat, struct iatt *poststat, + uint16_t xlid); + +extern void +nfs3_fill_read3res (read3res *res, nfsstat3 stat, count3 count, + struct iatt *poststat, int is_eof, uint16_t xlid); + +extern void +nfs3_prep_read3args (read3args *args, struct nfs3_fh *fh); + +extern void +nfs3_prep_pathconf3args (pathconf3args *args, struct nfs3_fh *fh); + +extern void +nfs3_fill_pathconf3res (pathconf3res *res, nfsstat3 stat, struct iatt *buf, + uint16_t xlid); + +extern int +nfs3_cached_inode_opened (xlator_t *nfsxl, inode_t *inode); + +extern void +nfs3_log_common_res (uint32_t xid, char *op, nfsstat3 stat, int pstat); + +extern void +nfs3_log_readlink_res (uint32_t xid, nfsstat3 stat, int pstat, char *linkpath); + +extern void +nfs3_log_read_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, + int is_eof); + +extern void +nfs3_log_write_res (uint32_t xid, nfsstat3 stat, int pstat, count3 count, + int stable, uint64_t wverf); + +extern void +nfs3_log_newfh_res (uint32_t xid, char *op, nfsstat3 stat, int pstat, + struct nfs3_fh *newfh); + +extern void +nfs3_log_readdir_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, + count3 count, int is_eof); + +extern void +nfs3_log_readdirp_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t cverf, + count3 dircount, count3 maxcount, int is_eof); + +extern void +nfs3_log_commit_res (uint32_t xid, nfsstat3 stat, int pstat, uint64_t wverf); + +extern void +nfs3_log_common_call (uint32_t xid, char *op, struct nfs3_fh *fh); + +extern void +nfs3_log_fh_entry_call (uint32_t xid, char *op, struct nfs3_fh *fh, + char *name); + +extern void +nfs3_log_rw_call (uint32_t xid, char *op, struct nfs3_fh *fh, offset3 offt, + count3 count, int stablewrite); + +extern void +nfs3_log_create_call (uint32_t xid, struct nfs3_fh *fh, char *name, + createmode3 mode); + +extern void +nfs3_log_symlink_call (uint32_t xid, struct nfs3_fh *fh, char *name, char *tgt); + +extern void +nfs3_log_mknod_call (uint32_t xid, struct nfs3_fh *fh, char *name, int type); + +extern void +nfs3_log_rename_call (uint32_t xid, struct nfs3_fh *src, char *sname, + struct nfs3_fh *dst, char *dname); + +extern void +nfs3_log_link_call (uint32_t xid, struct nfs3_fh *fh, char *name, + struct nfs3_fh *tgt); + +extern void +nfs3_log_readdir_call (uint32_t xid, struct nfs3_fh *fh, count3 dircount, + count3 maxcount); + +extern int +nfs3_fh_resolve_entry_hard (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_inode (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_entry (nfs3_call_state_t *cs); + +extern int +nfs3_fh_resolve_and_resume (nfs3_call_state_t *cs, struct nfs3_fh *fh, + char *entry, nfs3_resume_fn_t resum_fn); + +extern int +nfs3_file_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume); + +extern int +nfs3_dir_open_and_resume (nfs3_call_state_t *cs, nfs3_resume_fn_t resume); + +extern int +nfs3_verify_dircookie (struct nfs3_state *nfs3, fd_t *dirfd, cookie3 cookie, + uint64_t cverf, nfsstat3 *stat); + +extern int +nfs3_fdcache_remove (struct nfs3_state *nfs3, fd_t *fd); +#endif diff --git a/xlators/nfs/server/src/nfs3.c b/xlators/nfs/server/src/nfs3.c new file mode 100644 index 00000000000..41e474b396d --- /dev/null +++ b/xlators/nfs/server/src/nfs3.c @@ -0,0 +1,4836 @@ +/* + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS 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. + + GlusterFS 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/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "rpcsvc.h" +#include "dict.h" +#include "xlator.h" +#include "mount3.h" +#include "xdr-nfs3.h" +#include "msg-nfs3.h" +#include "iobuf.h" +#include "nfs3.h" +#include "mem-pool.h" +#include "logging.h" +#include "nfs-fops.h" +#include "nfs-inodes.h" +#include "nfs-generics.h" +#include "nfs3-helpers.h" + + +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/statvfs.h> +#include <time.h> + +#define nfs3_validate_strlen_or_goto(str, len, label, status, retval) \ + do { \ + if ((str)) { \ + if (strlen ((str)) > (len)) { \ + status = NFS3ERR_NAMETOOLONG; \ + retval = -ENAMETOOLONG; \ + goto label; \ + } \ + } \ + } while (0); \ + +#define nfs3_validate_nfs3_state(request, state, status, label, retval) \ + do { \ + state = rpcsvc_request_program_private (request); \ + if (!nfs3) { \ + gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state " \ + "missing from RPC request"); \ + status = NFS3ERR_SERVERFAULT; \ + ret = -EFAULT; \ + goto label; \ + } \ + } while (0); \ + +#define nfs3_export_access(nfs3state, xlid) ((nfs3state)->exports[xlid]).access + +#define nfs3_check_rw_volaccess(nfs3state, xlid, status, label) \ + do { \ + if (nfs3_export_access (nfs3state,xlid)!=GF_NFS3_VOLACCESS_RW){\ + gf_log (GF_NFS3, GF_LOG_TRACE, "No read-write access");\ + status = NFS3ERR_ROFS; \ + goto label; \ + } \ + } while (0) \ + + + +#define nfs3_map_fh_to_volume(nfs3state, handle, rqst, volume, status, label) \ + do { \ + volume = nfs3_fh_to_xlator ((nfs3state)->exportslist, handle); \ + if (!volume) { \ + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to map " \ + "FH to vol"); \ + status = NFS3ERR_STALE; \ + goto label; \ + } else { \ + gf_log (GF_NFS3, GF_LOG_TRACE, "FH to Volume: %s"\ + ,volume->name); \ + rpcsvc_request_set_private (req, volume); \ + } \ + } while (0); \ + + +#define nfs3_validate_gluster_fh(handle, status, errlabel) \ + do { \ + if ((handle)) { \ + if (!nfs3_fh_validate (handle)) { \ + status = NFS3ERR_BADHANDLE; \ + goto errlabel; \ + } \ + } \ + } while (0) \ + +#define nfs3_check_fh_resolve_status(cst, nfstat, erlabl) \ + do { \ + if ((cst)->resolve_ret == -1) { \ + nfstat = nfs3_errno_to_nfsstat3 (cst->resolve_errno);\ + goto erlabl; \ + } \ + } while (0) \ + +#define nfs3_check_new_fh_resolve_status(cst, nfstat, erlabl) \ + do { \ + if (((cst)->resolve_ret == -1) && \ + ((cst)->resolve_errno != ENOENT)) { \ + nfstat = nfs3_errno_to_nfsstat3 (cs->resolve_errno);\ + goto erlabl; \ + } \ + } while (0) \ + + +#define nfs3_funge_solaris_zerolen_fh(nfs3st, fhd, enam, nfsst, erl) \ + do { \ + xlator_t *fungexl = NULL; \ + fungexl =nfs_mntpath_to_xlator ((nfs3st)->exportslist,enam);\ + if (!fungexl) { \ + (nfsst) = NFS3ERR_NOENT; \ + goto erl; \ + } \ + \ + (fhd)->xlatorid = nfs_xlator_to_xlid ((nfs3st)->exportslist, \ + fungexl); \ + (fhd)->gen = 0; \ + (fhd)->ino = 1; \ + (enam) = NULL; \ + } while (0) \ + + +int +nfs3_solaris_zerolen_fh (struct nfs3_fh *fh, int fhlen) +{ + if (!fh) + return 0; + + if (nfs3_fh_validate (fh)) + return 0; + + if (fhlen == 0) + return 1; + + return 0; +} + + +/* Function pointer that represents the generic prototypes of functions used + * to serialize NFS3 message structures into the XDR format. + * For usage, see the nfs3svc_XXX_cbk functions. + */ +typedef ssize_t (*nfs3_serializer) (struct iovec outmsg, void *args); + +nfs3_call_state_t * +nfs3_call_state_init (struct nfs3_state *s, rpcsvc_request_t *req, xlator_t *v) +{ + nfs3_call_state_t *cs = NULL; + + if ((!s) || (!req) || (!v)) + return NULL; + + cs = (nfs3_call_state_t *) mem_get (s->localpool); + if (!cs) + return NULL; + + memset (cs, 0, sizeof (*cs)); + INIT_LIST_HEAD (&cs->entries.list); + INIT_LIST_HEAD (&cs->openwait_q); + cs->operrno = EINVAL; + cs->req = req; + cs->vol = v; + cs->nfsx = s->nfsx; + + return cs; +} + +void +nfs3_call_state_wipe (nfs3_call_state_t *cs) +{ + struct nfs3_state *nfs3 = NULL; + if (!cs) + return; + + nfs3 = rpcsvc_request_program_private (cs->req); + if (cs->fd) { + gf_log (GF_NFS3, GF_LOG_TRACE, "fd ref: %d", cs->fd->refcount); + fd_unref (cs->fd); + } + + if (cs->resolve_dir_fd) + fd_unref (cs->resolve_dir_fd); + + if (cs->resolventry) + FREE (cs->resolventry); + + if (cs->pathname) + FREE (cs->pathname); + + if (!list_empty (&cs->entries.list)) + gf_dirent_free (&cs->entries); + + list_del (&cs->openwait_q); + nfs_loc_wipe (&cs->oploc); + nfs_loc_wipe (&cs->resolvedloc); + if (cs->iob) + iobuf_unref (cs->iob); + memset (cs, 0, sizeof (*cs)); + mem_put (nfs3->localpool, cs); + /* Already refd by fd_lookup, so no need to ref again. */ +} + + +#define nfs3_handle_call_state_init(nfs3state, calls, rq, vl ,opstat, errlabel)\ + do { \ + calls = nfs3_call_state_init ((nfs3state), (rq), (vl)); \ + if (!calls) { \ + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to " \ + "init call state"); \ + opstat = NFS3ERR_SERVERFAULT; \ + goto errlabel; \ + } \ + } while (0) \ + + + +struct iobuf * +nfs3_serialize_reply (rpcsvc_request_t *req, void *arg, nfs3_serializer sfunc, + struct iovec *outmsg) +{ + struct nfs3_state *nfs3 = NULL; + struct iobuf *iob = NULL; + ssize_t retlen = -1; + + nfs3 = (struct nfs3_state *)rpcsvc_request_program_private (req); + if (!nfs3) { + gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state not found in RPC" + " request"); + goto ret; + } + + /* First, get the io buffer into which the reply in arg will + * be serialized. + */ + iob = iobuf_get (nfs3->iobpool); + if (!iob) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to get iobuf"); + goto ret; + } + + iobuf_to_iovec (iob, outmsg); + /* Use the given serializer to translate the give C structure in arg + * to XDR format which will be written into the buffer in outmsg. + */ + /* retlen is used to received the error since size_t is unsigned and we + * need -1 for error notification during encoding. + */ + retlen = sfunc (*outmsg, arg); + if (retlen == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to encode message"); + goto ret; + } + + outmsg->iov_len = retlen; +ret: + if (retlen == -1) { + iobuf_unref (iob); + iob = NULL; + } + + return iob; +} + + + +/* Generic reply function for NFSv3 specific replies. */ +int +nfs3svc_submit_reply (rpcsvc_request_t *req, void *arg, nfs3_serializer sfunc) +{ + struct iovec outmsg = {0, }; + struct iobuf *iob = NULL; + int ret = -1; + + if (!req) + return -1; + + iob = nfs3_serialize_reply (req, arg, sfunc, &outmsg); + if (!iob) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to serialize reply"); + goto ret; + } + + /* Then, submit the message for transmission. */ + ret = rpcsvc_submit_message (req, outmsg, iob); + + /* Now that we've done our job of handing the message to the RPC layer + * we can safely unref the iob in the hope that RPC layer must have + * ref'ed the iob on receiving into the txlist. + */ + iobuf_unref (iob); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Reply submission failed"); + goto ret; + } + + ret = 0; +ret: + return ret; +} + + +int +nfs3svc_submit_vector_reply (rpcsvc_request_t *req, void *arg, + nfs3_serializer sfunc, struct iovec *payload, + struct iobref *piobref) +{ + struct iovec outmsg = {0, }; + struct iobuf *iob = NULL; + int ret = -1; + + if (!req) + return -1; + + iob = nfs3_serialize_reply (req, arg, sfunc, &outmsg); + if (!iob) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to serialize reply"); + goto err; + } + + ret = rpcsvc_request_attach_vector (req, outmsg, iob, NULL, 0); + iobuf_unref (iob); + + if (piobref) + ret = rpcsvc_request_attach_vector (req, *payload, NULL, piobref + , 1); + + if (ret == -1) + goto err; + ret = rpcsvc_submit_vectors (req); +err: + + return ret; +} + + +uint16_t +nfs3_request_xlator_id (rpcsvc_request_t *rq) +{ + struct nfs3_state *nfs3 = NULL; + xlator_t *xl = NULL; + + if (!rq) + return 0; + + xl = rpcsvc_request_private (rq); + nfs3 = rpcsvc_request_program_private (rq); + return nfs_xlator_to_xlid (nfs3->exportslist, xl); +} + + +int +nfs3svc_null (rpcsvc_request_t *req) +{ + struct iovec dummyvec = {0, }; + if (!req) + return RPCSVC_ACTOR_ERROR; + + rpcsvc_submit_generic (req, dummyvec, NULL); + return RPCSVC_ACTOR_SUCCESS; +} + + +int +nfs3_getattr_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *buf) +{ + getattr3res res; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_getattr3res (&res, status, buf, xlid); + nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_getattr3res); + + return 0; +} + + +int32_t +nfs3svc_getattr_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xattr, + struct iatt *postparent) +{ + rpcsvc_request_t *req = NULL; + nfsstat3 status = NFS3_OK; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + req = cs->req; + + if (op_ret == -1) + status = nfs3_errno_to_nfsstat3 (op_errno); + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", status, + op_errno); + + nfs3_getattr_reply (cs->req, status, buf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int32_t +nfs3svc_getattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + rpcsvc_request_t *req = NULL; + nfsstat3 status = NFS3_OK; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + req = cs->req; + + if (op_ret == -1) + status = nfs3_errno_to_nfsstat3 (op_errno); + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", status, + op_errno); + + nfs3_getattr_reply (cs->req, status, buf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_getattr_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + /* If inode which is to be getattr'd is the root, we need to do a + * lookup instead because after a server reboot, it is not necessary + * for the root to have been looked up when the getattr on the root is + * sent. AND, this causes a problem for stat-prefetch in that it + * expects even the root inode to have been looked up. + */ + if (cs->resolvedloc.inode->ino == 1) + ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_getattr_lookup_cbk, cs); + else + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_getattr_stat_cbk, cs); + + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Stat fop failed: %s: %s", + cs->oploc.path, strerror (-ret)); + stat = nfs3_errno_to_nfsstat3 (-ret); + } + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "GETATTR", + stat, -ret); + nfs3_getattr_reply (cs->req, stat, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3_getattr (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cstate = NULL; + + if ((!req) || (!fh)) + return -1; + + nfs3_log_common_call (rpcsvc_request_xid (req), "GETATTR", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cstate, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cstate, fh, NULL,nfs3_getattr_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "GETATTR", stat, + -ret); + nfs3_getattr_reply (req, stat, NULL); + ret = 0; + nfs3_call_state_wipe (cstate); + } + + return ret; +} + + +int +nfs3svc_getattr (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + getattr3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_getattr3args (&args, &fh); + if (xdr_to_getattr3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_getattr (req, &fh); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "GETATTR procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_setattr_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preop, + struct iatt *postop) +{ + setattr3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_setattr3res (&res, stat, preop, postop, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) xdr_serialize_setattr3res); + return 0; +} + + +int32_t +nfs3svc_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + struct iatt *prestat = NULL; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + /* If the first stat was got from the guarded setattr callback, or + * from an earlier setattr call then we'll need to use that stat + * instead of the preop returned here. + */ + if (cs->preparent.ia_ino != 0) + prestat = &cs->preparent; + else + prestat = prebuf; + + stat = NFS3_OK; +nfs3err: + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", stat, + op_errno); + nfs3_setattr_reply (cs->req, stat, prestat, postbuf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int32_t +nfs3svc_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preop, + struct iatt *postop) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -1; + struct iatt *prebuf = NULL; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + /* If the first stat was got from the guarded setattr callback, then + * we'll need to use that stat instead of the preop returned here. + */ + if (cs->preparent.ia_ino != 0) + prebuf = &cs->preparent; + else { + prebuf = preop; + /* Store the current preop in case we need to send a truncate, + * in which case the preop to be returned will be this one. + */ + cs->preparent = *preop; + } + + ret = 0; + /* Only truncate if the size is not already same as the requested + * truncation and also only if this is not a directory. + */ + if ((gf_attr_size_set (cs->setattr_valid)) && + (!IA_ISDIR (postop->ia_type))) { + nfs_request_user_init (&nfu, cs->req); + ret = nfs_truncate (cs->vol, &nfu, &cs->resolvedloc, + cs->stbuf.ia_size, nfs3svc_truncate_cbk,cs); + + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + } else { + ret = -1; /* Force a reply in the branch below. */ + stat = NFS3_OK; + } + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", + stat, op_errno); + nfs3_setattr_reply (cs->req, stat, prebuf, postop); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + + +int32_t +nfs3svc_setattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + + int ret = -EFAULT; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + if (buf->ia_ctime != cs->timestamp.seconds) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Timestamps not in sync"); + stat = NFS3ERR_NOT_SYNC; + goto nfs3err; + } + + /* Not a clean way but no motivation to add a new member to local. */ + cs->preparent = *buf; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, + cs->setattr_valid, nfs3svc_setattr_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", + stat, op_errno); + nfs3_setattr_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_setattr_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + /* If no ctime check is required, head straight to setting the attrs. */ + if (cs->sattrguardcheck) + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_setattr_stat_cbk, cs); + else + ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, + cs->setattr_valid, nfs3svc_setattr_cbk, cs); + + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SETATTR", + stat, -ret); + nfs3_setattr_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_setattr (rpcsvc_request_t *req, struct nfs3_fh *fh, sattr3 *sattr, + sattrguard3 *guard) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh) || (!sattr) || (!guard)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_common_call (rpcsvc_request_xid (req), "SETATTR", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, &cs->stbuf, + NULL); + if (guard->check) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Guard check required"); + cs->timestamp = guard->sattrguard3_u.obj_ctime; + cs->sattrguardcheck = 1; + } else { + gf_log (GF_NFS3, GF_LOG_TRACE, "Guard check not required"); + cs->sattrguardcheck = 0; + } + + if (!cs->setattr_valid) { + ret = -EINVAL; /* Force a reply */ + stat = NFS3_OK; + goto nfs3err; + } + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_setattr_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "SETATTR", stat, + -ret); + nfs3_setattr_reply (req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + + +int +nfs3svc_setattr (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + setattr3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_setattr3args (&args, &fh); + if (xdr_to_setattr3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_setattr (req, &fh, &args.new_attributes, &args.guard); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "SETATTR procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; + +} + + +int +nfs3_lookup_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *stbuf, struct iatt *postparent) +{ + lookup3res res = {0, }; + + nfs3_fill_lookup3res (&res, stat, newfh, stbuf, postparent); + return nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_lookup3res); +} + + +int +nfs3svc_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xattr, struct iatt *postparent) +{ + struct nfs3_fh newfh = {{0}, }; + nfsstat3 status = NFS3_OK; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + status = nfs3_errno_to_nfsstat3 (op_errno); + goto xmit_res; + } + + nfs3_fh_build_child_fh (&cs->parent, buf, &newfh); + +xmit_res: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "LOOKUP", status, + op_errno, &newfh); + nfs3_lookup_reply (cs->req, status, &newfh, buf, postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3svc_lookup_parentdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xattr, + struct iatt *postparent) +{ + struct nfs3_fh newfh = {{0}, }; + nfsstat3 status = NFS3_OK; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + status = nfs3_errno_to_nfsstat3 (op_errno); + goto xmit_res; + } + + nfs3_fh_build_parent_fh (&cs->fh, buf, &newfh); + +xmit_res: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "LOOKUP", status, + op_errno, &newfh); + nfs3_lookup_reply (cs->req, status, &newfh, buf, postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + + + +int +nfs3_lookup_parentdir_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + inode_t *parent = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + + /* At this point now, the loc in cs is for the directory file handle + * sent by the client. This loc needs to be transformed into a loc that + * represents the parent dir of cs->resolvedloc.inode. + */ + nfs_request_user_init (&nfu, cs->req); + + /* Save the file handle from the LOOKUP request. We'll use this to + * build the file handle of the parent directory. + */ + cs->fh = cs->resolvefh; + parent = inode_ref (cs->resolvedloc.parent); + nfs_loc_wipe (&cs->resolvedloc); + ret = nfs_inode_loc_fill (parent, &cs->resolvedloc); + if (ret < 0) + goto errtostat; + + ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_lookup_parentdir_cbk, cs); +errtostat: + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LOOKUP", + stat, -ret); + nfs3_lookup_reply (cs->req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + if (parent) + inode_unref (parent); + + return ret; +} + + +int +nfs3_lookup_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + cs->parent = cs->resolvefh; + ret = nfs_lookup (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_lookup_cbk, + cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LOOKUP", + stat, -ret); + nfs3_lookup_reply (cs->req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_is_parentdir_entry (char *entry) +{ + int ret = 0; + + if (!entry) + return 0; + + if (strcmp (entry, "..") == 0) + ret = 1; + + return ret; +} + + +int +nfs3_lookup (rpcsvc_request_t *req, struct nfs3_fh *fh, int fhlen, char *name) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh) || (!name)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "LOOKUP", fh, name); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + if (nfs3_solaris_zerolen_fh (fh, fhlen)) + nfs3_funge_solaris_zerolen_fh (nfs3, fh, name, stat, nfs3err); + else + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + if (!nfs3_is_parentdir_entry (name)) + ret = nfs3_fh_resolve_and_resume (cs, fh, name, + nfs3_lookup_resume); + else + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, + nfs3_lookup_parentdir_resume); + + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "LOOKUP", stat, + -ret); + nfs3_lookup_reply (req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_lookup (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh fh = {{0}, }; + lookup3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_lookup3args (&args, &fh, name); + if (xdr_to_lookup3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_lookup (req, &fh, args.what.dir.data.data_len, name); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "LOOKUP procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_access_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *buf, + uint32_t accbits) +{ + access3res res; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_access3res (&res, status, buf, accbits, + rpcsvc_request_uid (req), rpcsvc_request_gid (req) + , xlid); + nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_access3res); + return 0; +} + + +int32_t +nfs3svc_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + nfsstat3 status = NFS3_OK; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + + if (op_ret == -1) + status = nfs3_errno_to_nfsstat3 (op_errno); + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "ACCESS", status, + op_errno); + nfs3_access_reply (cs->req, status, buf, cs->accessbits); + nfs3_call_state_wipe (cs); + + return 0; +} + +int +nfs3_access_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + cs->fh = cs->resolvefh; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_access_cbk,cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "ACCESS", + stat, -ret); + nfs3_access_reply (cs->req, stat, NULL, 0); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3_access (rpcsvc_request_t *req, struct nfs3_fh *fh, uint32_t accbits) +{ + xlator_t *vol = NULL; + struct nfs3_state *nfs3 = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) + return -1; + + nfs3_log_common_call (rpcsvc_request_xid (req), "ACCESS", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + cs->accessbits = accbits; + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_access_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "ACCESS", stat, + -ret); + nfs3_access_reply (req, stat, NULL, 0); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_access (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + access3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_access3args (&args, &fh); + if (xdr_to_access3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_access (req, &fh, args.access); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "ACCESS procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_readlink_reply (rpcsvc_request_t *req, nfsstat3 stat, char *path, + struct iatt *buf) +{ + readlink3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_readlink3res (&res, stat, path, buf, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_readlink3res); + + return 0; +} + + +int32_t +nfs3svc_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, const char *path, + struct iatt *buf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + stat = NFS3_OK; + +nfs3err: + nfs3_log_readlink_res (rpcsvc_request_xid (cs->req), stat, op_errno, + (char *)path); + nfs3_readlink_reply (cs->req, stat, (char *)path, buf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_readlink_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_readlink (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_readlink_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READLINK", + stat, -ret); + nfs3_readlink_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_readlink (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_common_call (rpcsvc_request_xid (req), "READLINK", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_readlink_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "READLINK", stat, + -ret); + nfs3_readlink_reply (req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_readlink (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + readlink3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_readlink3args (&args, &fh); + if (xdr_to_readlink3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_readlink (req, &fh); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "READLINK procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_read_reply (rpcsvc_request_t *req, nfsstat3 stat, + count3 count, struct iovec *vec, struct iobref *iobref, + struct iatt *poststat, int is_eof) +{ + read3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_read3res (&res, stat, count, poststat, is_eof, xlid); + if (stat == NFS3_OK) { + /* iob can be zero if the file size was zero. If so, op_ret + * would be 0 and count = 0. + */ + if (count != 0) { + xdr_bytes_round_up (vec, 1048576); + nfs3svc_submit_vector_reply (req, (void *)&res, + (nfs3_serializer) + xdr_serialize_read3res_nocopy, + vec, iobref); + } else + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) + xdr_serialize_read3res_nocopy); + } else + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) + xdr_serialize_read3res_nocopy); + + return 0; +} + + +int32_t +nfs3svc_read_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iovec *vector, + int32_t count, struct iatt *stbuf, struct iobref *iobref) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int is_eof = 0; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto err; + } else + stat = NFS3_OK; + + if (op_errno == ENOENT) + is_eof = 1; + +err: + nfs3_log_read_res (rpcsvc_request_xid (cs->req), stat, op_errno, + op_ret, is_eof); + nfs3_read_reply (cs->req, stat, op_ret, vector, iobref, stbuf, is_eof); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_read_fd_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_read (cs->vol, &nfu, cs->fd, cs->datacount, cs->dataoffset, + nfs3svc_read_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READ", stat, + -ret); + nfs3_read_reply (cs->req, stat, 0, NULL, NULL, NULL, 0); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_read_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + ret = nfs3_file_open_and_resume (cs, nfs3_read_fd_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READ", stat, + -ret); + nfs3_read_reply (cs->req, stat, 0, NULL, NULL, NULL, 0); + nfs3_call_state_wipe (cs); + } + + return ret; +} + +int +nfs3_read (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, + count3 count) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_rw_call (rpcsvc_request_xid (req), "READ", fh, offset, count, + -1); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->datacount = count; + cs->dataoffset = offset; + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_read_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "READ", stat, + -ret); + nfs3_read_reply (req, stat, 0, NULL, NULL, NULL, 0); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_read (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + read3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + + nfs3_prep_read3args (&args, &fh); + if (xdr_to_read3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_read (req, &fh, args.offset, args.count); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "READ procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_write_reply (rpcsvc_request_t *req, nfsstat3 stat, count3 count, + stable_how stable, uint64_t wverf, struct iatt *prestat, + struct iatt *poststat) +{ + write3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_write3res (&res, stat, count, stable, wverf, prestat, + poststat, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_write3res); + + return 0; +} + +int32_t +nfs3svc_write_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf) +{ + struct nfs3_state *nfs3 = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + nfs3 = rpcsvc_request_program_private (cs->req); + + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else + stat = NFS3_OK; + + nfs3_log_write_res (rpcsvc_request_xid (cs->req), stat, op_errno, + cs->maxcount, cs->writetype, nfs3->serverstart); + nfs3_write_reply (cs->req, stat, cs->maxcount, cs->writetype, + nfs3->serverstart, &cs->stbuf, postbuf); + nfs3_call_state_wipe (cs); + return 0; +} + + +int32_t +nfs3svc_write_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + struct nfs3_state *nfs3 = NULL; + + cs = frame->local; + nfs3 = rpcsvc_request_program_private (cs->req); + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto err; + } + + /* So that we do send a reply if an unstable write was requested. */ + ret = -1; + stat = NFS3_OK; + cs->maxcount = op_ret; + if (cs->writetype == UNSTABLE) + goto err; + + nfs_request_user_init (&nfu, cs->req); + /* Store the current preattr so that this can be used as the pre attr + * when fsync returns. We dont want to use the preattr in fsync because + * the write fop happened before the fsync. + */ + cs->stbuf = *prebuf; + ret = nfs_fsync (cs->vol, &nfu, cs->fd, 0, nfs3svc_write_fsync_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +err: + if (ret < 0) { + nfs3_log_write_res (rpcsvc_request_xid (cs->req), stat, + op_errno, cs->maxcount, cs->writetype, + nfs3->serverstart); + nfs3_write_reply (cs->req, stat, cs->maxcount, + cs->writetype, nfs3->serverstart, prebuf, + postbuf); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +__nfs3_write_resume (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + nfs_request_user_init (&nfu, cs->req); + /* It is possible that the RPC record contains more bytes than + * than the size of write requested in this request. This means, + * that in the RPC message buffer, there could be more bytes + * beyind the @count bytes. Since @payload is referring to the write + * data directly inside the RPC request buffer(..since we performed a + * no-copy deXDRing..), we might end up writing more data than + * requested, because till now payload.iov_len accounts for all the + * bytes not just the write request bytes. These extra bytes are present + * as a requirement of the XDR encoding to round up the all string and + * opaque data buffers to multiples of 4 bytes. + */ + cs->datavec.iov_len = cs->datacount; + ret = nfs_write (cs->vol, &nfu, cs->fd, cs->iob, &cs->datavec, 1, + cs->dataoffset, nfs3svc_write_cbk, cs); + + return ret; +} + + +int +nfs3_write_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + + ret = __nfs3_write_resume (cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "WRITE", + stat, -ret); + nfs3_write_reply (cs->req, stat, 0, cs->writetype, 0, NULL, + NULL); + nfs3_call_state_wipe (cs); + } + return ret; +} + + +int +nfs3_write_open_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + ret = nfs3_file_open_and_resume (cs, nfs3_write_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "WRITE", + stat, -ret); + nfs3_write_reply (cs->req, stat, 0, cs->writetype, 0, NULL, + NULL); + nfs3_call_state_wipe (cs); + } + return ret; +} + + + +int +nfs3_write (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, + count3 count, stable_how stable, struct iovec payload, + struct iobuf *iob) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh) || (!payload.iov_base)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_rw_call (rpcsvc_request_xid (req), "WRITE", fh, offset, count, + stable); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + cs->datacount = count; + cs->dataoffset = offset; + cs->writetype = stable; + cs->iob = iob; + cs->datavec = payload; + + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_write_open_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "WRITE", stat, + -ret); + nfs3_write_reply (req, stat, 0, stable, 0, NULL, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + +#define NFS3_VECWRITE_READFHLEN 1 +#define NFS3_VECWRITE_READFH 2 +#define NFS3_VECWRITE_READREST 3 + +#define NFS3_WRITE_POSTFH_SIZE 20 + + +int +nfs3svc_write_vecsizer (rpcsvc_request_t *req, ssize_t *readsize, int *newbuf) +{ + ssize_t ret = RPCSVC_ACTOR_ERROR; + int state = 0; + uint32_t fhlen = 0; + uint32_t fhlen_n = 0; + write3args *args = NULL; + + if (!req) + return ret; + + state = (long)rpcsvc_request_private (req); + *newbuf = 0; + if (state == 0) { + rpcsvc_request_set_private (req, NFS3_VECWRITE_READFHLEN); + *readsize = 4; + ret = 0; + } else if (state == NFS3_VECWRITE_READFHLEN) { + fhlen_n = *(uint32_t *)req->msg.iov_base; + fhlen = ntohl (fhlen_n); + *readsize = xdr_length_round_up (fhlen, NFS3_FHSIZE); + rpcsvc_request_set_private (req, NFS3_VECWRITE_READFH); + ret = 0; + } else if (state == NFS3_VECWRITE_READFH) { + *readsize = NFS3_WRITE_POSTFH_SIZE; + rpcsvc_request_set_private (req, NFS3_VECWRITE_READREST); + ret = 0; + } else if (state == NFS3_VECWRITE_READREST) { + args = CALLOC (1, sizeof (*args)); + if (!args) + goto rpcerr; + + if (xdr_to_write3args_nocopy (req->msg, args, NULL) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + rpcsvc_request_set_private (req, args); + ret = xdr_length_round_up (args->data.data_len, 1048576); + *readsize = ret; + *newbuf = 1; + ret = 0; + } + ret = 0; + +rpcerr: + return ret; +} + + +int +nfs3svc_write_vec (rpcsvc_request_t *req, struct iobuf *iob) +{ + write3args *args = NULL; + int ret = RPCSVC_ACTOR_ERROR; + struct iovec payload = {0, }; + + if ((!req) || (!iob)) + return ret; + + args = rpcsvc_request_private (req); + iobuf_to_iovec (iob, &payload); + iobuf_ref (iob); + ret = nfs3_write (req, (struct nfs3_fh *)args->file.data.data_val, + args->offset, args->count, args->stable, payload,iob); + xdr_free_write3args_nocopy (args); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "WRITE procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + + return ret; +} + + + +int +nfs3svc_write (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + write3args args; + int ret = RPCSVC_ACTOR_ERROR; + struct iovec payload = {0, }; + + if (!req) + return ret; + nfs3_prep_write3args (&args, &fh); + if (xdr_to_write3args_nocopy (req->msg, &args, &payload) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + /* To ensure that the iobuf for the current record does not + * get returned to the iobpool, we need to keep a reference for + * ourselves because the RPC call handler who called us will unref its + * own ref of the record's iobuf when it is done handling the request. + */ + rpcsvc_request_record_ref (req); + ret = nfs3_write (req, &fh, args.offset, args.count, args.stable, + payload, rpcsvc_request_record_iob (req)); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "WRITE procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_create_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *newfh, + struct iatt *newbuf, struct iatt *preparent, + struct iatt *postparent) +{ + create3res res = {0, }; + + nfs3_fill_create3res (&res, stat, newfh, newbuf, preparent, postparent); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_create3res); + return 0; +} + + +int32_t +nfs3svc_create_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preop, struct iatt *postop) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + stat = NFS3_OK; +nfs3err: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "CREATE", stat, + op_errno, &cs->fh); + nfs3_create_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, + &cs->postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int32_t +nfs3svc_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + + /* Means no attributes were required to be set. */ + if (!cs->setattr_valid) { + stat = NFS3_OK; + ret = -1; + goto nfs3err; + } + + cs->preparent = *preparent; + cs->postparent = *postparent; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, + cs->setattr_valid, nfs3svc_create_setattr_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "CREATE", + stat, op_errno, &cs->fh); + nfs3_create_reply (cs->req, stat, &cs->fh, buf, preparent, + postparent); + nfs3_call_state_wipe (cs); + } + + return 0; +} + +int +nfs3_create_common (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + int flags = 0; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + if ((cs->createmode == UNCHECKED) || (cs->createmode = EXCLUSIVE)) + flags = O_RDWR; + else if (cs->createmode == GUARDED) + flags = (O_RDWR | O_EXCL); + + nfs_request_user_init (&nfu, cs->req); + /* We can avoid sending the setattr call later if only the mode is + * required to be set. This is possible because the create fop allows + * us to specify a mode arg. + */ + if (cs->setattr_valid & GF_SET_ATTR_MODE) { + cs->setattr_valid &= ~GF_SET_ATTR_MODE; + ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, flags, + cs->mode, nfs3svc_create_cbk, cs); + } else + ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, flags, + NFS_DEFAULT_CREATE_MODE, nfs3svc_create_cbk, + cs); + + return ret; +} + + +int32_t +nfs3svc_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + int ret = -EFAULT; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + nfs_request_user_init (&nfu, cs->req); + if (op_ret == -1) { + ret = -op_errno; + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + if (cs->preparent.ia_mtime == buf->ia_mtime) + stat = NFS3_OK; + else + stat = NFS3ERR_EXIST; + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "CREATE", + stat, op_errno); + nfs3_create_reply (cs->req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_create_exclusive (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + /* HACK warning. */ + cs->preparent.ia_mtime = cs->cookieverf; + cs->preparent.ia_atime = 9669; + nfs_request_user_init (&nfu, cs->req); + + /* If the file already existed we need to get that attributes so we can + * compare and check whether a previous create operation was + * interrupted due to server failure or dropped packets. + */ + if ((cs->resolve_ret == 0) || + ((cs->resolve_ret == -1) && (cs->resolve_errno != ENOENT))) { + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_create_stat_cbk, cs); + goto nfs3err; + } + + if (cs->setattr_valid & GF_SET_ATTR_MODE) { + cs->setattr_valid &= ~GF_SET_ATTR_MODE; + ret = nfs_create (cs->vol, &nfu, &cs->resolvedloc, O_RDWR, + cs->mode, nfs3svc_create_cbk, cs); + } else + ret = nfs_create (cs->vol, &nfu, &cs->oploc, O_RDWR, + NFS_DEFAULT_CREATE_MODE, nfs3svc_create_cbk, + cs); + +nfs3err: + return ret; +} + + +int +nfs3_create_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + if (cs->createmode == EXCLUSIVE) + ret = nfs3_create_exclusive (cs); + else + ret = nfs3_create_common (cs); + + /* Handle a failure return from either of the create functions above. */ + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "CREATE", + stat, -ret); + nfs3_create_reply (cs->req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + +int +nfs3_create (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, + createmode3 mode, sattr3 *sattr, uint64_t cverf) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!dirfh) || (!name) || (!sattr)) + return -1; + + nfs3_log_create_call (rpcsvc_request_xid (req), dirfh, name, mode); + nfs3_validate_gluster_fh (dirfh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->cookieverf = cverf; + cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, + &cs->mode); + cs->createmode = mode; + cs->parent = *dirfh; + + ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_create_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "CREATE", stat, + -ret); + nfs3_create_reply (req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_create (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh dirfh = {{0}, }; + create3args args; + int ret = RPCSVC_ACTOR_ERROR; + uint64_t cverf = 0; + + if (!req) + return ret; + + nfs3_prep_create3args (&args, &dirfh, name); + if (xdr_to_create3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + cverf = *(uint64_t *)args.how.createhow3_u.verf; + ret = nfs3_create (req, &dirfh, name, args.how.mode, + &args.how.createhow3_u.obj_attributes, cverf); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "CREATE procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_mkdir_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + mkdir3res res = {0, }; + + nfs3_fill_mkdir3res (&res, stat, fh, buf, preparent, postparent); + nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_mkdir3res); + return 0; +} + +int32_t +nfs3svc_mkdir_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preop, struct iatt *postop) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + stat = NFS3_OK; +nfs3err: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKDIR", stat, + op_errno, &cs->fh); + nfs3_mkdir_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, + &cs->postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int32_t +nfs3svc_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + + /* Means no attributes were required to be set. */ + if (!cs->setattr_valid) { + stat = NFS3_OK; + goto nfs3err; + } + + cs->preparent = *preparent; + cs->postparent = *postparent; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, + cs->setattr_valid, nfs3svc_mkdir_setattr_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKDIR", stat, + op_errno, &cs->fh); + nfs3_mkdir_reply (cs->req, stat, &cs->fh, buf, preparent, + postparent); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_mkdir_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + + if (gf_attr_mode_set (cs->setattr_valid)) { + cs->setattr_valid &= ~GF_SET_ATTR_MODE; + ret = nfs_mkdir (cs->vol, &nfu, &cs->resolvedloc, + cs->mode, nfs3svc_mkdir_cbk, cs); + } else + ret = nfs_mkdir (cs->vol, &nfu, &cs->resolvedloc, + cs->mode, nfs3svc_mkdir_cbk, cs); + + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "MKDIR",stat, + -ret); + nfs3_mkdir_reply (cs->req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + + +int +nfs3_mkdir (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, + sattr3 *sattr) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!dirfh) || (!name) || (!sattr)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "MKDIR", dirfh, name); + nfs3_validate_gluster_fh (dirfh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->parent = *dirfh; + cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, + &cs->mode); + ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_mkdir_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "MKDIR", stat, + -ret); + nfs3_mkdir_reply (req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_mkdir (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh dirfh = {{0}, }; + mkdir3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_mkdir3args (&args, &dirfh, name); + if (xdr_to_mkdir3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_mkdir (req, &dirfh, name, &args.attributes); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "MKDIR procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_symlink_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + symlink3res res = {0, }; + + nfs3_fill_symlink3res (&res, stat, fh, buf, preparent, postparent); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_symlink3res); + + return 0; +} + + +int32_t +nfs3svc_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + stat = NFS3_OK; + +nfs3err: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "SYMLINK", stat, + op_errno, &cs->fh); + nfs3_symlink_reply (cs->req, stat, &cs->fh, buf, preparent, + postparent); + nfs3_call_state_wipe (cs); + return 0; +} + + +int +nfs3_symlink_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_symlink (cs->vol, &nfu, cs->pathname, &cs->resolvedloc, + nfs3svc_symlink_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "SYMLINK", + stat, -ret); + nfs3_symlink_reply (cs->req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_symlink (rpcsvc_request_t *req, struct nfs3_fh *dirfh, char *name, + char *target, sattr3 *sattr) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!dirfh) || (!name) || (!target) || (!sattr)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_symlink_call (rpcsvc_request_xid (req), dirfh, name, target); + nfs3_validate_gluster_fh (dirfh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->parent = *dirfh; + cs->pathname = strdup (target); + if (!cs->pathname) { + ret = -1; + stat = NFS3ERR_SERVERFAULT; + goto nfs3err; + } + + ret = nfs3_fh_resolve_and_resume (cs, dirfh, name, nfs3_symlink_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "SYMLINK", stat, + -ret); + nfs3_symlink_reply (req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_symlink (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh dirfh = {{0}, }; + char target[NFS_PATH_MAX]; + symlink3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_symlink3args (&args, &dirfh, name, target); + if (xdr_to_symlink3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_symlink (req, &dirfh, name, target, + &args.symlink.symlink_attributes); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "SYMLINK procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_mknod_reply (rpcsvc_request_t *req, nfsstat3 stat, struct nfs3_fh *fh, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + mknod3res res = {0, }; + nfs3_fill_mknod3res (&res, stat, fh, buf, preparent, postparent); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_mknod3res); + + return 0; +} + +int32_t +nfs3svc_mknod_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preop, struct iatt *postop) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + stat = NFS3_OK; +nfs3err: + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKNOD", stat, + op_errno, &cs->fh); + nfs3_mknod_reply (cs->req, stat, &cs->fh, postop, &cs->preparent, + &cs->postparent); + nfs3_call_state_wipe (cs); + return 0; +} + + + +int32_t +nfs3svc_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -1; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + nfs3_fh_build_child_fh (&cs->parent, buf, &cs->fh); + + /* Means no attributes were required to be set. */ + if (!cs->setattr_valid) { + stat = NFS3_OK; + ret = -1; + goto nfs3err; + } + + cs->preparent = *preparent; + cs->postparent = *postparent; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_setattr (cs->vol, &nfu, &cs->resolvedloc, &cs->stbuf, + cs->setattr_valid, nfs3svc_mknod_setattr_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "MKNOD", stat, + op_errno, &cs->fh); + nfs3_mknod_reply (cs->req, stat, &cs->fh, buf, preparent, + postparent); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_mknod_device (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + dev_t devnum = 0; + mode_t mode = 0; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + devnum = makedev (cs->devnums.specdata1, cs->devnums.specdata2); + if (cs->mknodtype == NF3CHR) + mode = S_IFCHR; + else + mode = S_IFBLK; + + nfs_request_user_init (&nfu, cs->req); + if (gf_attr_mode_set (cs->setattr_valid)) { + cs->setattr_valid &= ~GF_SET_ATTR_MODE; + mode |= cs->mode; + ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, devnum, + nfs3svc_mknod_cbk, cs); + } else + ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, devnum, + nfs3svc_mknod_cbk, cs); + + return ret; +} + + +int +nfs3_mknod_fifo (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + mode_t mode = S_IFIFO; + + if (!cs) + return ret; + + nfs_request_user_init (&nfu, cs->req); + if (gf_attr_mode_set (cs->setattr_valid)) { + cs->setattr_valid &= ~GF_SET_ATTR_MODE; + mode |= cs->mode; + ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, 0, + nfs3svc_mknod_cbk, cs); + } else + ret = nfs_mknod (cs->vol, &nfu, &cs->resolvedloc, mode, 0, + nfs3svc_mknod_cbk, cs); + + + return ret; +} + + +int +nfs3_mknod_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + switch (cs->mknodtype) { + + case NF3CHR: + case NF3BLK: + ret = nfs3_mknod_device (cs); + break; + case NF3SOCK: + case NF3FIFO: + ret = nfs3_mknod_fifo (cs); + break; + default: + ret = -EBADF; + break; + } + + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "MKNOD",stat, + -ret); + nfs3_mknod_reply (cs->req, stat, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_mknod (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name, + mknoddata3 *nodedata) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + sattr3 *sattr = NULL; + + if ((!req) || (!fh) || (!name) || (!nodedata)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_mknod_call (rpcsvc_request_xid (req), fh, name,nodedata->type); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->mknodtype = nodedata->type; + switch (nodedata->type) { + case NF3CHR: + case NF3BLK: + cs->devnums = nodedata->mknoddata3_u.device.spec; + sattr = &nodedata->mknoddata3_u.device.dev_attributes; + cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, + &cs->mode); + break; + case NF3SOCK: + case NF3FIFO: + sattr = &nodedata->mknoddata3_u.pipe_attributes; + cs->setattr_valid = nfs3_sattr3_to_setattr_valid (sattr, NULL, + &cs->mode); + break; + default: + ret = -EBADF; + break; + } + + cs->parent = *fh; + ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_mknod_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "MKNOD", stat, + -ret); + nfs3_mknod_reply (req, stat, NULL, NULL, NULL, NULL); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_mknod (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh fh = {{0}, }; + mknod3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_mknod3args (&args, &fh, name); + if (xdr_to_mknod3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_mknod (req, &fh, name, &args.what); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "MKNOD procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + +int +nfs3_remove_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preparent + , struct iatt *postparent) +{ + remove3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_remove3res (&res, stat, preparent, postparent, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_remove3res); + return 0; +} + + + +int32_t +nfs3svc_remove_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + fd_t *openfd = NULL; + nfs3_call_state_t *cs = NULL; + struct nfs3_state *nfs3 = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto do_not_unref_cached_fd; + } + stat = NFS3_OK; + /* Close any cached fds so that when any currently active write + * finishes, the file is finally removed. + */ + openfd = fd_lookup (cs->resolvedloc.inode, 0); + nfs3 = rpcsvc_request_program_private (cs->req); + if (openfd) { + fd_unref (openfd); + nfs3_fdcache_remove (nfs3, openfd); + } + +do_not_unref_cached_fd: + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "REMOVE", stat, + op_errno); + nfs3_remove_reply (cs->req, stat, preparent, postparent); + inode_unref (cs->resolvedloc.inode); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +__nfs3_remove (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + ia_type_t type = 0; + + if (!cs) + return ret; + type = cs->resolvedloc.inode->ia_type; + nfs_request_user_init (&nfu, cs->req); + if (IA_ISDIR (type)) + ret = nfs_rmdir (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_remove_cbk, cs); + else + ret = nfs_unlink (cs->vol, &nfu, &cs->resolvedloc, + nfs3svc_remove_cbk, cs); + + return ret; +} + + +int +nfs3_remove_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + ret = __nfs3_remove (cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "REMOVE", + stat, -ret); + nfs3_remove_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_remove (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh) || (!name)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "REMOVE", fh, name); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_remove_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "REMOVE", stat, + -ret); + nfs3_remove_reply (req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_remove (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh fh = {{0}, }; + remove3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_remove3args (&args, &fh, name); + if (xdr_to_remove3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_remove (req, &fh, name); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "REMOVE procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_rmdir_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *preparent, + struct iatt *postparent) +{ + rmdir3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_rmdir3res (&res, stat, preparent, postparent, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_rmdir3res); + return 0; +} + + +int32_t +nfs3svc_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else { + inode_unref (cs->resolvedloc.inode); + stat = NFS3_OK; + } + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RMDIR", stat, + op_errno); + nfs3_rmdir_reply (cs->req, stat, preparent, postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + +int +nfs3_rmdir_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_rmdir (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_rmdir_cbk,cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RMDIR", + stat, -ret); + nfs3_rmdir_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_rmdir (rpcsvc_request_t *req, struct nfs3_fh *fh, char *name) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh) || (!name)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_fh_entry_call (rpcsvc_request_xid (req), "RMDIR", fh, name); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto (name, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, name, nfs3_rmdir_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "RMDIR", stat, + -ret); + nfs3_rmdir_reply (req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_rmdir (rpcsvc_request_t *req) +{ + char name[NFS_PATH_MAX]; + struct nfs3_fh fh = {{0}, }; + rmdir3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_rmdir3args (&args, &fh, name); + if (xdr_to_rmdir3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_rmdir (req, &fh, name); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "RMDIR procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_rename_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent) +{ + rename3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_rename3res (&res, stat, buf, preoldparent, postoldparent, + prenewparent, postnewparent, xlid); + + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) xdr_serialize_rename3res); + + return 0; +} + + + +int32_t +nfs3svc_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent) +{ + int ret = -EFAULT; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + stat = NFS3_OK; +nfs3err: + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", stat,-ret); + nfs3_rename_reply (cs->req, stat, buf, preoldparent, postoldparent, + prenewparent, postnewparent); + nfs3_call_state_wipe (cs); + return 0; +} + + +int +nfs3_rename_resume_dst (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + cs->parent = cs->resolvefh; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_rename (cs->vol, &nfu, &cs->oploc, &cs->resolvedloc, + nfs3svc_rename_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", + stat, -ret); + nfs3_rename_reply (cs->req, stat, NULL, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_rename_resume_src (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + /* Copy the resolved loc for the source file into another loc + * for safekeeping till we resolve the dest loc. + */ + nfs_loc_copy (&cs->oploc, &cs->resolvedloc); + nfs_loc_wipe (&cs->resolvedloc); + + ret = nfs3_fh_resolve_and_resume (cs, &cs->fh, cs->pathname, + nfs3_rename_resume_dst); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "RENAME", + stat, -ret); + nfs3_rename_reply (cs->req, stat, NULL, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_rename (rpcsvc_request_t *req, struct nfs3_fh *olddirfh, char *oldname, + struct nfs3_fh *newdirfh, char *newname) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!olddirfh) || (!oldname) || (!newdirfh) || (!newname)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_rename_call (rpcsvc_request_xid (req), olddirfh, oldname, + newdirfh, newname); + nfs3_validate_gluster_fh (olddirfh, stat, nfs3err); + nfs3_validate_gluster_fh (newdirfh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto(oldname, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_validate_strlen_or_goto(newname, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, olddirfh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, olddirfh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + /* While we resolve the source (fh, name) pair, we need to keep a copy + * of the dest (fh,name) pair. + */ + cs->fh = *newdirfh; + cs->pathname = strdup (newname); + if (!cs->pathname) { + stat = NFS3ERR_SERVERFAULT; + ret = -1; + goto nfs3err; + } + + ret = nfs3_fh_resolve_and_resume (cs, olddirfh, oldname, + nfs3_rename_resume_src); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "RENAME", stat, + -ret); + nfs3_rename_reply (req, stat, NULL, NULL, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_rename (rpcsvc_request_t *req) +{ + char newname[NFS_PATH_MAX]; + char oldname[NFS_PATH_MAX]; + struct nfs3_fh olddirfh = {{0}, }; + struct nfs3_fh newdirfh = {{0}, }; + rename3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_rename3args (&args, &olddirfh, oldname, &newdirfh, newname); + if (xdr_to_rename3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_rename (req, &olddirfh, oldname, &newdirfh, newname); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "RENAME procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_link_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent) +{ + link3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_link3res (&res, stat, buf, preparent, postparent, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_link3res); + + return 0; +} + + +int32_t +nfs3svc_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else + stat = NFS3_OK; + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, + op_errno); + nfs3_link_reply (cs->req, stat, buf, preparent, postparent); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_link_resume_lnk (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_new_fh_resolve_status (cs, stat, nfs3err); + + nfs_request_user_init (&nfu, cs->req); + ret = nfs_link (cs->vol, &nfu, &cs->oploc, &cs->resolvedloc, + nfs3svc_link_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, + -ret); + nfs3_link_reply (cs->req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + return ret; +} + + +int +nfs3_link_resume_tgt (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_loc_copy (&cs->oploc, &cs->resolvedloc); + nfs_loc_wipe (&cs->resolvedloc); + + ret = nfs3_fh_resolve_and_resume (cs, &cs->fh, cs->pathname, + nfs3_link_resume_lnk); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "LINK", stat, + -ret); + nfs3_link_reply (cs->req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_link (rpcsvc_request_t *req, struct nfs3_fh *targetfh, + struct nfs3_fh *dirfh, char *newname) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!targetfh) || (!dirfh) || (!newname)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_validate_gluster_fh (dirfh, stat, nfs3err); + nfs3_validate_gluster_fh (targetfh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_validate_strlen_or_goto(newname, NFS_NAME_MAX, nfs3err, stat, ret); + nfs3_map_fh_to_volume (nfs3, dirfh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, dirfh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->fh = *dirfh; + cs->pathname = strdup (newname); + if (!cs->pathname) { + stat = NFS3ERR_SERVERFAULT; + ret = -1; + goto nfs3err; + } + + ret = nfs3_fh_resolve_and_resume (cs, targetfh, NULL, + nfs3_link_resume_tgt); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "LINK", stat, + -ret); + nfs3_link_reply (req, stat, NULL, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + +int +nfs3svc_link (rpcsvc_request_t *req) +{ + char newpath[NFS_PATH_MAX]; + struct nfs3_fh dirfh = {{0}, }; + struct nfs3_fh targetfh = {{0}, }; + link3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_link3args (&args, &targetfh, &dirfh, newpath); + if (xdr_to_link3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_link (req, &targetfh, &dirfh, newpath); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "LINK procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_readdirp_reply (rpcsvc_request_t *req, nfsstat3 stat,struct nfs3_fh *dirfh, + uint64_t cverf, struct iatt *dirstat, gf_dirent_t *entries, + count3 dircount, count3 maxcount, int is_eof) +{ + readdirp3res res = {0, }; + + nfs3_fill_readdirp3res (&res, stat, dirfh, cverf, dirstat, entries, + dircount, maxcount, is_eof); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) xdr_serialize_readdirp3res); + nfs3_free_readdirp3res (&res); + + return 0; +} + + +int +nfs3_readdir_reply (rpcsvc_request_t *req, nfsstat3 stat, uint64_t cverf, + struct iatt *dirstat, gf_dirent_t *entries, count3 count, + int is_eof) +{ + readdir3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_readdir3res (&res, stat, cverf, dirstat, entries, count, + is_eof, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer) xdr_serialize_readdir3res); + nfs3_free_readdir3res (&res); + + return 0; +} + + +int32_t +nfs3svc_readdir_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int is_eof = 0; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto nfs3err; + } + + /* Check whether we encountered a end of directory stream while + * readdir'ing. + */ + if (cs->operrno == ENOENT) { + gf_log (GF_NFS3, GF_LOG_TRACE, "Reached end-of-directory"); + is_eof = 1; + } + + stat = NFS3_OK; +nfs3err: + if (cs->maxcount == 0) { + nfs3_log_readdir_res (rpcsvc_request_xid (cs->req), stat, + op_errno, (uint64_t)cs->fd, + cs->dircount, is_eof); + nfs3_readdir_reply (cs->req, stat, (uint64_t)cs->fd, + buf, &cs->entries, cs->dircount, + is_eof); + } else { + nfs3_log_readdirp_res (rpcsvc_request_xid (cs->req), stat, + op_errno, (uint64_t)cs->fd, + cs->dircount, cs->maxcount, is_eof); + nfs3_readdirp_reply (cs->req, stat, &cs->parent, + (uint64_t)cs->fd, buf, + &cs->entries, cs->dircount, + cs->maxcount, is_eof); + } + + if (is_eof) { + gf_log (GF_NFS3, GF_LOG_TRACE, "EOF REF: %d", cs->fd->refcount); + fd_unref (cs->fd); + } + + gf_log (GF_NFS3, GF_LOG_TRACE, "CS WIPE REF: %d", cs->fd->refcount); + nfs3_call_state_wipe (cs); + return 0; +} + + +int32_t +nfs3svc_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, gf_dirent_t *entries) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto err; + } + + cs->operrno = op_errno; + list_splice_init (&entries->list, &cs->entries.list); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_fstat (cs->vol, &nfu, cs->fd, nfs3svc_readdir_fstat_cbk, cs); + if (ret < 0) { + op_ret = -1; + stat = nfs3_errno_to_nfsstat3 (-ret); + op_errno = -ret; + } + +err: + if (op_ret >= 0) + goto ret; + + if (cs->maxcount == 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READDIR", + stat, op_errno); + nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); + } else { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "READDIRP" + , stat, op_errno); + nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, + 0, 0, 0); + } + + /* For directories, we force a purge from the fd cache on close + * so that next time the dir is read, we'll get any changed directory + * entries. + */ + fd_unref (cs->fd); + nfs3_call_state_wipe (cs); +ret: + return 0; +} + +int +nfs3_readdir_process (nfs3_call_state_t *cs) +{ + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!cs) + return ret; + + nfs_request_user_init (&nfu, cs->req); + ret = nfs_readdirp (cs->vol, &nfu, cs->fd, cs->dircount, cs->cookie, + nfs3svc_readdir_cbk, cs); + return ret; +} + + +int +nfs3_readdir_read_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + struct nfs3_state *nfs3 = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs3 = rpcsvc_request_program_private (cs->req); + ret = nfs3_verify_dircookie (nfs3, cs->fd, cs->cookie, cs->cookieverf, + &stat); + if (ret < 0) /* Stat already set by verifier function above. */ + goto nfs3err; + + ret = nfs3_readdir_process (cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + if (cs->maxcount == 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), + "READDIR", stat, -ret); + nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); + } else { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), + "READDIRP", stat, -ret); + nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, + 0, 0, 0); + } + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_readdir_open_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + ret = nfs3_dir_open_and_resume (cs, nfs3_readdir_read_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + if (cs->maxcount == 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), + "READDIR", stat, -ret); + nfs3_readdir_reply (cs->req, stat, 0, NULL, NULL, 0, 0); + } else { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), + "READDIRP", stat, -ret); + nfs3_readdirp_reply (cs->req, stat, NULL, 0, NULL, NULL, + 0, 0, 0); + } + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_readdir (rpcsvc_request_t *req, struct nfs3_fh *fh, cookie3 cookie, + uint64_t cverf, count3 dircount, count3 maxcount) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_readdir_call (rpcsvc_request_xid (req), fh, dircount,maxcount); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->cookieverf = cverf; + cs->dircount = dircount; + cs->maxcount = maxcount; + cs->cookie = cookie; + cs->parent = *fh; + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, + nfs3_readdir_open_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + if (maxcount == 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "READDIR" + , stat, -ret); + nfs3_readdir_reply (req, stat, 0, NULL, NULL, 0, 0); + } else { + nfs3_log_common_res (rpcsvc_request_xid (req),"READDIRP" + , stat, -ret); + nfs3_readdirp_reply (req, stat, NULL, 0, NULL, NULL, 0, + 0, 0); + } + /* Ret must be NULL after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3svc_readdir (rpcsvc_request_t *req) +{ + readdir3args ra; + struct nfs3_fh fh = {{0},}; + int ret = RPCSVC_ACTOR_ERROR; + uint64_t verf = 0; + + if (!req) + return ret; + nfs3_prep_readdir3args (&ra, &fh); + if (xdr_to_readdir3args (req->msg, &ra) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + verf = *(uint64_t *)ra.cookieverf; + ret = nfs3_readdir (req, &fh, ra.cookie, verf, ra.count, 0); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "READDIR procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3svc_readdirp (rpcsvc_request_t *req) +{ + readdirp3args ra; + struct nfs3_fh fh = {{0},}; + int ret = RPCSVC_ACTOR_ERROR; + uint64_t cverf = 0; + + if (!req) + return ret; + nfs3_prep_readdirp3args (&ra, &fh); + if (xdr_to_readdirp3args (req->msg, &ra) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + cverf = *(uint64_t *)ra.cookieverf; + ret = nfs3_readdir (req, &fh, ra.cookie, cverf, ra.dircount, + ra.maxcount); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "READDIRP procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_fsstat_reply (rpcsvc_request_t *req, nfsstat3 stat, struct statvfs *fsbuf, + struct iatt *postbuf) +{ + fsstat3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_fsstat3res (&res, stat, fsbuf, postbuf, xlid); + return nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_fsstat3res); + +} + + +int32_t +nfs3_fsstat_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else + stat = NFS3_OK; + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", stat, + op_errno); + nfs3_fsstat_reply (cs->req, stat, &cs->fsstat, buf); + nfs3_call_state_wipe (cs); + return 0; +} + + +int32_t +nfs3_fsstat_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct statvfs *buf) +{ + nfs_user_t nfu = {0, }; + int ret = -EFAULT; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + if (op_ret == -1) { + ret = -op_errno; + stat = nfs3_errno_to_nfsstat3 (op_errno); + goto err; + } + + /* Then get the stat for the fs root in order to fill in the + * post_op_attr. + */ + cs->fsstat = *buf; + nfs_request_user_init (&nfu, cs->req); + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3_fsstat_stat_cbk, + cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", + stat, -ret); + nfs3_fsstat_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return 0; +} + + +int +nfs3_fsstat_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + nfs_user_t nfu = {0, }; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + /* First, we need to get the statvfs for the subvol */ + ret = nfs_statfs (cs->vol, &nfu, &cs->resolvedloc, + nfs3_fsstat_statfs_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSTAT", + stat, -ret); + nfs3_fsstat_reply (cs->req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_fsstat (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_common_call (rpcsvc_request_xid (req), "FSSTAT", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_fsstat_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "FSTAT", stat, + -ret); + nfs3_fsstat_reply (req, stat, NULL, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_fsstat (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + fsstat3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_fsstat3args (&args, &fh); + if (xdr_to_fsstat3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_fsstat (req, &fh); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "FSTAT procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_fsinfo_reply (rpcsvc_request_t *req, nfsstat3 status, struct iatt *fsroot) +{ + fsinfo3res res; + struct nfs3_state *nfs3 = NULL; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3 = rpcsvc_request_program_private (req); + nfs3_fill_fsinfo3res (nfs3, &res, status, fsroot, xlid); + + nfs3svc_submit_reply (req, &res, + (nfs3_serializer)xdr_serialize_fsinfo3res); + return 0; +} + + +int32_t +nfs3svc_fsinfo_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + nfsstat3 status = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + + cs = frame->local; + + if (op_ret == -1) + status = nfs3_errno_to_nfsstat3 (op_errno); + else + status = NFS3_OK; + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSINFO", status, + op_errno); + + nfs3_fsinfo_reply (cs->req, status, buf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_fsinfo_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc,nfs3svc_fsinfo_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "FSINFO", + stat, -ret); + nfs3_fsinfo_reply (cs->req, stat, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + +int +nfs3_fsinfo (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_common_call (rpcsvc_request_xid (req), "FSINFO", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_fsinfo_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "FSINFO", stat, + -ret); + nfs3_fsinfo_reply (req, stat, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3svc_fsinfo (rpcsvc_request_t *req) +{ + int ret = RPCSVC_ACTOR_ERROR; + fsinfo3args args; + struct nfs3_fh root = {{0}, }; + + if (!req) + return ret; + + nfs3_prep_fsinfo3args (&args, &root); + if (xdr_to_fsinfo3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding arguments"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_fsinfo (req, &root); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "FSINFO procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +int +nfs3_pathconf_reply (rpcsvc_request_t *req, nfsstat3 stat, struct iatt *buf) +{ + pathconf3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_pathconf3res (&res, stat, buf, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_pathconf3res); + return 0; +} + + +int32_t +nfs3svc_pathconf_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) +{ + struct iatt *sbuf = NULL; + nfs3_call_state_t *cs = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + + cs = frame->local; + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else { + /* If stat fop failed, we can still send the other components + * in a pathconf reply. + */ + sbuf = buf; + stat = NFS3_OK; + } + + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "PATHCONF", stat, + op_errno); + nfs3_pathconf_reply (cs->req, stat, sbuf); + nfs3_call_state_wipe (cs); + + return 0; +} + + +int +nfs3_pathconf_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_stat (cs->vol, &nfu, &cs->resolvedloc, nfs3svc_pathconf_cbk, + cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "PATHCONF", + stat, -ret); + nfs3_pathconf_reply (cs->req, stat, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + +int +nfs3_pathconf (rpcsvc_request_t *req, struct nfs3_fh *fh) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_common_call (rpcsvc_request_xid (req), "PATHCONF", fh); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, nfs3_pathconf_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "PATHCONF", stat, + -ret); + nfs3_pathconf_reply (req, stat, NULL); + nfs3_call_state_wipe (cs); + /* Ret must be 0 after this so that the caller does not + * also send an RPC reply. + */ + ret = 0; + } + + return ret; +} + + +int +nfs3svc_pathconf (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + pathconf3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_pathconf3args (&args, &fh); + if (xdr_to_pathconf3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_pathconf (req, &fh); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "PATHCONF procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + +int +nfs3_commit_reply (rpcsvc_request_t *req, nfsstat3 stat, uint64_t wverf, + struct iatt *prestat, struct iatt *poststat) +{ + commit3res res = {0, }; + uint16_t xlid = 0; + + xlid = nfs3_request_xlator_id (req); + nfs3_fill_commit3res (&res, stat, wverf, prestat, poststat, xlid); + nfs3svc_submit_reply (req, (void *)&res, + (nfs3_serializer)xdr_serialize_commit3res); + + return 0; +} + + +int32_t +nfs3svc_commit_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + nfs3_call_state_t *cs = NULL; + struct nfs3_state *nfs3 = NULL; + + cs = frame->local; + if (op_ret == -1) + stat = nfs3_errno_to_nfsstat3 (op_errno); + else + stat = NFS3_OK; + + nfs3 = rpcsvc_request_program_private (cs->req); + nfs3_log_commit_res (rpcsvc_request_xid (cs->req), stat, op_errno, + nfs3->serverstart); + nfs3_commit_reply (cs->req, stat, nfs3->serverstart, prebuf, postbuf); + nfs3_call_state_wipe (cs); + + return 0; +} + +int +nfs3_commit_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + nfs_request_user_init (&nfu, cs->req); + ret = nfs_fsync (cs->vol, &nfu, cs->fd, 0, nfs3svc_commit_cbk, cs); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "COMMIT", + stat, -ret); + nfs3_commit_reply (cs->req, stat, 0, NULL, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + +int +nfs3_commit_open_resume (void *carg) +{ + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + nfs3_call_state_t *cs = NULL; + + if (!carg) + return ret; + + cs = (nfs3_call_state_t *)carg; + nfs3_check_fh_resolve_status (cs, stat, nfs3err); + + ret = nfs3_file_open_and_resume (cs, nfs3_commit_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (cs->req), "COMMIT", + stat, -ret); + nfs3_commit_reply (cs->req, stat, 0, NULL, NULL); + nfs3_call_state_wipe (cs); + } + + return ret; +} + + + +int +nfs3_commit (rpcsvc_request_t *req, struct nfs3_fh *fh, offset3 offset, + count3 count) +{ + xlator_t *vol = NULL; + nfsstat3 stat = NFS3ERR_SERVERFAULT; + int ret = -EFAULT; + struct nfs3_state *nfs3 = NULL; + nfs3_call_state_t *cs = NULL; + + if ((!req) || (!fh)) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Bad arguments"); + return -1; + } + + nfs3_log_rw_call (rpcsvc_request_xid (req), "COMMIT", fh, offset, count, + -1); + nfs3_validate_gluster_fh (fh, stat, nfs3err); + nfs3_validate_nfs3_state (req, nfs3, stat, nfs3err, ret); + nfs3_map_fh_to_volume (nfs3, fh, req, vol, stat, nfs3err); + nfs3_check_rw_volaccess (nfs3, fh->xlatorid, stat, nfs3err); + nfs3_handle_call_state_init (nfs3, cs, req, vol, stat, nfs3err); + + cs->datacount = count; + cs->dataoffset = offset; + ret = nfs3_fh_resolve_and_resume (cs, fh, NULL, + nfs3_commit_open_resume); + if (ret < 0) + stat = nfs3_errno_to_nfsstat3 (-ret); + +nfs3err: + if (ret < 0) { + nfs3_log_common_res (rpcsvc_request_xid (req), "COMMIT", stat, + -ret); + nfs3_commit_reply (req, stat, 0, NULL, NULL); + nfs3_call_state_wipe (cs); + ret = 0; + } + + return ret; +} + + + +int +nfs3svc_commit (rpcsvc_request_t *req) +{ + struct nfs3_fh fh = {{0}, }; + commit3args args; + int ret = RPCSVC_ACTOR_ERROR; + + if (!req) + return ret; + nfs3_prep_commit3args (&args, &fh); + if (xdr_to_commit3args (req->msg, &args) <= 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Error decoding args"); + rpcsvc_request_seterr (req, GARBAGE_ARGS); + goto rpcerr; + } + + ret = nfs3_commit (req, &fh, args.offset, args.count); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "COMMIT procedure failed"); + rpcsvc_request_seterr (req, SYSTEM_ERR); + ret = RPCSVC_ACTOR_ERROR; + } + +rpcerr: + return ret; +} + + +rpcsvc_actor_t nfs3svc_actors[NFS3_PROC_COUNT] = { + {"NULL", NFS3_NULL, nfs3svc_null, NULL, NULL}, + {"GETATTR", NFS3_GETATTR, nfs3svc_getattr,NULL, NULL}, + {"SETATTR", NFS3_SETATTR, nfs3svc_setattr,NULL, NULL}, + {"LOOKUP", NFS3_LOOKUP, nfs3svc_lookup, NULL, NULL}, + {"ACCESS", NFS3_ACCESS, nfs3svc_access, NULL, NULL}, + {"READLINK", NFS3_READLINK, nfs3svc_readlink,NULL, NULL}, + {"READ", NFS3_READ, nfs3svc_read, NULL, NULL}, + {"WRITE", NFS3_WRITE, nfs3svc_write, nfs3svc_write_vec, nfs3svc_write_vecsizer}, + {"CREATE", NFS3_CREATE, nfs3svc_create, NULL, NULL}, + {"MKDIR", NFS3_MKDIR, nfs3svc_mkdir, NULL, NULL}, + {"SYMLINK", NFS3_SYMLINK, nfs3svc_symlink,NULL, NULL}, + {"MKNOD", NFS3_MKNOD, nfs3svc_mknod, NULL, NULL}, + {"REMOVE", NFS3_REMOVE, nfs3svc_remove, NULL, NULL}, + {"RMDIR", NFS3_RMDIR, nfs3svc_rmdir, NULL, NULL}, + {"RENAME", NFS3_RENAME, nfs3svc_rename, NULL, NULL}, + {"LINK", NFS3_LINK, nfs3svc_link, NULL, NULL}, + {"READDIR", NFS3_READDIR, nfs3svc_readdir,NULL, NULL}, + {"READDIRPLUS", NFS3_READDIRP, nfs3svc_readdirp,NULL, NULL}, + {"FSSTAT", NFS3_FSSTAT, nfs3svc_fsstat, NULL, NULL}, + {"FSINFO", NFS3_FSINFO, nfs3svc_fsinfo, NULL, NULL}, + {"PATHCONF", NFS3_PATHCONF, nfs3svc_pathconf,NULL, NULL}, + {"COMMIT", NFS3_COMMIT, nfs3svc_commit, NULL, NULL} +}; + + +rpcsvc_program_t nfs3prog = { + .progname = "NFS3", + .prognum = NFS_PROGRAM, + .progver = NFS_V3, + .progport = GF_NFS3_PORT, + .progaddrfamily = AF_INET, + .proghost = NULL, + .actors = nfs3svc_actors, + .numactors = NFS3_PROC_COUNT, + .conn_destroy = NULL, + .conn_init = NULL, + + /* Requests like FSINFO are sent before an auth scheme + * is inited by client. See RFC 2623, Section 2.3.2. */ + .min_auth = AUTH_NULL, +}; + + +int +nfs3_init_options (struct nfs3_state *nfs3, xlator_t *nfsx) +{ + int ret = -1; + char *optstr = NULL; + + if ((!nfs3) || (!nfsx)) + return -1; + + /* nfs3.read-size */ + nfs3->readsize = GF_NFS3_RTPREF; + if (dict_get (nfsx->options, "nfs3.read-size")) { + ret = dict_get_str (nfsx->options, "nfs3.read-size", &optstr); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " + " option: nfs3.read-size"); + ret = -1; + goto err; + } + + ret = gf_string2bytesize (optstr, &nfs3->readsize); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" + " option: nfs3.read-size"); + ret = -1; + goto err; + } + } + + /* nfs3.write-size */ + nfs3->writesize = GF_NFS3_WTPREF; + if (dict_get (nfsx->options, "nfs3.write-size")) { + ret = dict_get_str (nfsx->options, "nfs3.write-size", &optstr); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " + " option: nfs3.write-size"); + ret = -1; + goto err; + } + + ret = gf_string2bytesize (optstr, &nfs3->writesize); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" + " option: nfs3.write-size"); + ret = -1; + goto err; + } + } + + /* nfs3.readdir.size */ + nfs3->readdirsize = GF_NFS3_DTPREF; + if (dict_get (nfsx->options, "nfs3.readdir-size")) { + ret = dict_get_str (nfsx->options,"nfs3.readdir-size", &optstr); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read" + " option: nfs3.readdir-size"); + ret = -1; + goto err; + } + + ret = gf_string2bytesize (optstr, &nfs3->readdirsize); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to format" + " option: nfs3.readdir-size"); + ret = -1; + goto err; + } + } + + + /* We want to use the size of the biggest param for the io buffer size. + */ + nfs3->iobsize = nfs3->readsize; + if (nfs3->iobsize < nfs3->writesize) + nfs3->iobsize = nfs3->writesize; + if (nfs3->iobsize < nfs3->readdirsize) + nfs3->iobsize = nfs3->readdirsize; + + /* But this is the true size of each iobuf. We need this size to + * accomodate the NFS headers also in the same buffer. */ + nfs3->iobsize = nfs3->iobsize * 2; + + /* mem-factor */ + nfs3->memfactor = GF_NFS3_DEFAULT_MEMFACTOR; + ret = 0; +err: + return ret; +} + +int +nfs3_init_subvolume_options (struct nfs3_export *exp, dict_t *options) +{ + int ret = -1; + char *optstr = NULL; + char searchkey[1024]; + char *name = NULL; + + if ((!exp) || (!options)) + return -1; + + /* Volume Access */ + name = exp->subvol->name; + ret = snprintf (searchkey, 1024, "nfs3.%s.volume-access", name); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "snprintf failed"); + ret = -1; + goto err; + } + + exp->access = GF_NFS3_DEFAULT_VOLACCESS; + if (dict_get (options, searchkey)) { + ret = dict_get_str (options, searchkey, &optstr); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " + " option: %s", searchkey); + ret = -1; + goto err; + } + + if (strcmp (optstr, "read-only") == 0) + exp->access = GF_NFS3_VOLACCESS_RO; + } + + ret = snprintf (searchkey, 1024, "rpc-auth.%s.unix", name); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "snprintf failed"); + ret = -1; + goto err; + } + + if (dict_get (options, searchkey)) { + ret = dict_get_str (options, searchkey, &optstr); + if (ret < 0) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to read " + " option: %s", searchkey); + ret = -1; + goto err; + } + } + + gf_log (GF_NFS3, GF_LOG_TRACE, "%s: %s", exp->subvol->name, + (exp->access == GF_NFS3_VOLACCESS_RO)?"read-only":"read-write"); + ret = 0; +err: + return ret; +} + + +int +nfs3_init_subvolume (struct nfs3_state *nfs3, xlator_t *nfsx, xlator_t *subvol, + int xlid) +{ + int ret = -1; + struct nfs3_export *exp = NULL; + + if ((!nfs3) || (!nfsx) || (!subvol)) + return -1; + + exp = &nfs3->exports[xlid]; + exp->subvol = subvol; + + gf_log (GF_NFS3, GF_LOG_TRACE, "Initing state: %s", exp->subvol->name); + + ret = nfs3_init_subvolume_options (exp, nfsx->options); + if (ret == -1) + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init subvol"); + + return ret; +} + + +int +nfs3_init_subvolumes (struct nfs3_state *nfs3, xlator_t *nfsx) +{ + int xl_count = 0; + int ret = -1; + struct xlator_list *xl_list = NULL; + + if ((!nfs3) || (!nfsx)) + return -1; + + xl_list = nfsx->children; + while (xl_list) { + ++xl_count; + xl_list = xl_list->next; + } + + nfs3->exports = CALLOC (xl_count, sizeof (struct nfs3_export)); + if (!nfs3->exports) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Memory allocation failed"); + goto err; + } + + xl_list = nfsx->children; + xl_count = 0; /* Re-using xl_count. */ + while (xl_list) { + ret = nfs3_init_subvolume (nfs3, nfsx, xl_list->xlator, + xl_count); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init subvol: " + "%s", xl_list->xlator->name); + goto err; + } + xl_list = xl_list->next; + ++xl_count; + } + + ret = 0; +err: + return ret; +} + + +struct nfs3_state * +nfs3_init_state (xlator_t *nfsx) +{ + struct nfs3_state *nfs3 = NULL; + int ret = -1; + unsigned int localpool = 0; + + + if (!nfsx) + return NULL; + + nfs3 = (struct nfs3_state *)CALLOC (1, sizeof (*nfs3)); + if (!nfs3) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Memory allocation failed"); + return NULL; + } + + ret = nfs3_init_options (nfs3, nfsx); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init options"); + goto ret; + } + + nfs3->iobpool = nfsx->ctx->iobuf_pool; + + localpool = nfs3->memfactor * GF_NFS_CONCURRENT_OPS_MULT; + gf_log (GF_NFS3, GF_LOG_TRACE, "local pool: %d", localpool); + nfs3->localpool = mem_pool_new (nfs3_call_state_t, localpool); + if (!nfs3->localpool) { + gf_log (GF_NFS3, GF_LOG_ERROR, "local mempool creation failed"); + ret = -1; + goto ret; + } + + nfs3->nfsx = nfsx; + nfs3->exportslist = nfsx->children; + ret = nfs3_init_subvolumes (nfs3, nfsx); + if (ret == -1) { + gf_log (GF_NFS3, GF_LOG_ERROR, "Failed to init per-subvolume " + "state"); + goto free_localpool; + } + + nfs3->serverstart = (uint64_t)time (NULL); + INIT_LIST_HEAD (&nfs3->fdlru); + LOCK_INIT (&nfs3->fdlrulock); + nfs3->fdcount = 0; + + ret = 0; + +free_localpool: + if (ret == -1) + mem_pool_destroy (nfs3->localpool); + +ret: + if (ret == -1) { + FREE (nfs3); + nfs3 = NULL; + } + + return nfs3; +} + + +rpcsvc_program_t * +nfs3svc_init (xlator_t *nfsx) +{ + struct nfs3_state *nfs3 = NULL; + + if (!nfsx) + return NULL; + + nfs3 = nfs3_init_state (nfsx); + if (!nfs3) { + gf_log (GF_NFS3, GF_LOG_ERROR, "NFSv3 state init failed"); + return NULL; + } + + nfs3prog.private = nfs3; + + return &nfs3prog; +} + + diff --git a/xlators/nfs/server/src/nfs3.h b/xlators/nfs/server/src/nfs3.h new file mode 100644 index 00000000000..1ec5a20a802 --- /dev/null +++ b/xlators/nfs/server/src/nfs3.h @@ -0,0 +1,202 @@ +/* + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS 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. + + GlusterFS 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/>. +*/ + +#ifndef _NFS3_H_ +#define _NFS3_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "rpcsvc.h" +#include "dict.h" +#include "xlator.h" +#include "iobuf.h" +#include "nfs.h" +#include "nfs3-fh.h" +#include "nfs-common.h" +#include "xdr-nfs3.h" +#include "mem-pool.h" + +#include <sys/statvfs.h> + +#define GF_NFS3 GF_NFS"-nfsv3" +#define GF_NFS3_PORT 38467 + +#define GF_NFS3_DEFAULT_MEMFACTOR 15 +#define GF_NFS3_IOBPOOL_MULT GF_NFS_CONCURRENT_OPS_MULT +#define GF_NFS3_CLTABLE_BUCKETS_MULT 2 +#define GF_NFS3_FDTABLE_BUCKETS_MULT 2 + + +/* Static values used for FSINFO +FIXME: This should be configurable */ +#define GF_NFS3_RTMAX (64 * GF_UNIT_KB) +#define GF_NFS3_RTPREF (64 * GF_UNIT_KB) +#define GF_NFS3_RTMULT (4 * GF_UNIT_KB) +#define GF_NFS3_WTMAX (64 * GF_UNIT_KB) +#define GF_NFS3_WTPREF (64 * GF_UNIT_KB) +#define GF_NFS3_WTMULT (4 * GF_UNIT_KB) +#define GF_NFS3_DTMIN (4 * GF_UNIT_KB) +#define GF_NFS3_DTPREF (64 * GF_UNIT_KB) +#define GF_NFS3_MAXFILE (1 * GF_UNIT_PB) +/* FIXME: Handle time resolutions */ +#define GF_NFS3_TIMEDELTA_SECS {1,0} +#define GF_NFS3_TIMEDELTA_NSECS {0,1} +#define GF_NFS3_TIMEDELTA_MSECS {0,1000000} + +#define GF_NFS3_FS_PROP (FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME) + +#define GF_NFS3_DIRFD_VALID 1 +#define GF_NFS3_DIRFD_INVALID 0 + +#define GF_NFS3_VOLACCESS_RW 1 +#define GF_NFS3_VOLACCESS_RO 2 + + +#define GF_NFS3_FDCACHE_SIZE 512 +/* This should probably be moved to a more generic layer so that if needed + * different versions of NFS protocol can use the same thing. + */ +struct nfs3_fd_entry { + fd_t *cachedfd; + struct list_head list; +}; + +/* Per subvolume nfs3 specific state */ +struct nfs3_export { + xlator_t *subvol; + int access; +}; + +#define GF_NFS3_DEFAULT_VOLACCESS (GF_NFS3_VOLACCESS_RW) + +/* The NFSv3 protocol state */ +struct nfs3_state { + + /* The NFS xlator pointer. The NFS xlator can be running + * multiple versions of the NFS protocol. + */ + xlator_t *nfsx; + + /* The iob pool from which memory allocations are made for receiving + * and sending network messages. + */ + struct iobuf_pool *iobpool; + + /* List of child subvolumes for the NFSv3 protocol. + * Right now, is simply referring to the list of children in nfsx above. + */ + xlator_list_t *exportslist; + + struct nfs3_export *exports; + /* Mempool for allocations of struct nfs3_local */ + struct mem_pool *localpool; + + /* Server start-up timestamp, currently used for write verifier. */ + uint64_t serverstart; + + /* NFSv3 Protocol configurables */ + size_t readsize; + size_t writesize; + size_t readdirsize; + + /* Size of the iobufs used, depends on the sizes of the three params + * above. + */ + size_t iobsize; + + unsigned int memfactor; + + struct list_head fdlru; + gf_lock_t fdlrulock; + int fdcount; +}; + + +typedef int (*nfs3_resume_fn_t) (void *cs); +/* Structure used to communicate state between a fop and its callback. + * Not all members are used at all times. Usage is fop and NFS request + * dependent. + * + * I wish we could have a smaller structure for communicating state + * between callers and callbacks. It could be broken into smaller parts + * but I feel that will lead to a proliferation of types/structures and then + * we'll just be tracking down which structure is used by which fop, not + * to mention that having one type allows me to used a single mem-pool. + * Imagine the chaos if we need a mem-pool for each one of those sub-structures. + */ +struct nfs3_local { + rpcsvc_request_t *req; + xlator_t *vol; + nfs3_resume_fn_t resume_fn; + xlator_t *nfsx; + + /* The list hook to attach this call state to the inode's queue till + * the opening of the fd on the inode completes. + */ + struct list_head openwait_q; + + /* Per-NFSv3 Op state */ + struct nfs3_fh parent; + struct nfs3_fh fh; + fd_t *fd; + uint32_t accessbits; + int operrno; + count3 dircount; + count3 maxcount; + struct statvfs fsstat; + gf_dirent_t entries; + struct iatt stbuf; + struct iatt preparent; + struct iatt postparent; + int32_t setattr_valid; + nfstime3 timestamp; + loc_t oploc; + int writetype; + count3 datacount; + offset3 dataoffset; + struct iobuf *iob; + createmode3 createmode; + uint64_t cookieverf; + int sattrguardcheck; + char *pathname; + ftype3 mknodtype; + specdata3 devnums; + cookie3 cookie; + struct iovec datavec; + mode_t mode; + + /* NFSv3 FH resolver state */ + struct nfs3_fh resolvefh; + loc_t resolvedloc; + int resolve_ret; + int resolve_errno; + int hashidx; + fd_t *resolve_dir_fd; + char *resolventry; +}; + +typedef struct nfs3_local nfs3_call_state_t; + + +extern rpcsvc_program_t * +nfs3svc_init (xlator_t *nfsx); +#endif |