summaryrefslogtreecommitdiffstats
path: root/xlators
diff options
context:
space:
mode:
authorShehjar Tikoo <shehjart@gluster.com>2010-03-31 07:27:05 +0000
committerAnand V. Avati <avati@dev.gluster.com>2010-03-31 07:44:09 -0700
commitf66f04e3af1f31a4c51b6a87f5e32ea2e3c7b438 (patch)
tree450da8640fd35525a3b288893f86fbd33b0bd47e /xlators
parente63053803dca86b4283b71bc4ef4cb180ec72d89 (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
Diffstat (limited to 'xlators')
-rw-r--r--xlators/nfs/server/src/Makefile.am4
-rw-r--r--xlators/nfs/server/src/nfs.c8
-rw-r--r--xlators/nfs/server/src/nfs3-helpers.c2825
-rw-r--r--xlators/nfs/server/src/nfs3-helpers.h342
-rw-r--r--xlators/nfs/server/src/nfs3.c4836
-rw-r--r--xlators/nfs/server/src/nfs3.h202
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