/doc/

sterfs.git/atom/xlators/nfs/server/src?h=master' type='application/atom+xml'/>
summaryrefslogtreecommitdiffstats
path: root/xlators/nfs/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/nfs/server/src')
-rw-r--r--xlators/nfs/server/src/Makefile.am39
-rw-r--r--xlators/nfs/server/src/acl3.c933
-rw-r--r--xlators/nfs/server/src/acl3.h42
-rw-r--r--xlators/nfs/server/src/auth-cache.c496
-rw-r--r--xlators/nfs/server/src/auth-cache.h52
-rw-r--r--xlators/nfs/server/src/exports.c1484
-rw-r--r--xlators/nfs/server/src/exports.h93
-rw-r--r--xlators/nfs/server/src/mount3-auth.c642
-rw-r--r--xlators/nfs/server/src/mount3-auth.h59
-rw-r--r--xlators/nfs/server/src/mount3.c5208
-rw-r--r--xlators/nfs/server/src/mount3.h210
-rw-r--r--xlators/nfs/server/src/mount3udp_svc.c238
-rw-r--r--xlators/nfs/server/src/netgroups.c1161
-rw-r--r--xlators/nfs/server/src/netgroups.h53
-rw-r--r--xlators/nfs/server/src/nfs-common.c635
-rw-r--r--xlators/nfs/server/src/nfs-common.h76
-rw-r--r--xlators/nfs/server/src/nfs-fops.c2215
-rw-r--r--xlators/nfs/server/src/nfs-fops.h319
-rw-r--r--xlators/nfs/server/src/nfs-generics.c354
-rw-r--r--xlators/nfs/server/src/nfs-generics.h167
-rw-r--r--xlators/nfs/server/src/nfs-inodes.c788
-rw-r--r--xlators/nfs/server/src/nfs-inodes.h82
-rw-r--r--xlators/nfs/server/src/nfs-mem-types.h71
-rw-r--r--xlators/nfs/server/src/nfs-messages.h102
-rw-r--r--xlators/nfs/server/src/nfs.c2707
-rw-r--r--xlators/nfs/server/src/nfs.h174
-rw-r--r--xlators/nfs/server/src/nfs3-fh.c349
-rw-r--r--xlators/nfs/server/src/nfs3-fh.h137
-rw-r--r--xlators/nfs/server/src/nfs3-helpers.c5291
-rw-r--r--xlators/nfs/server/src/nfs3-helpers.h297
-rw-r--r--xlators/nfs/server/src/nfs3.c9027
-rw-r--r--xlators/nfs/server/src/nfs3.h385
-rw-r--r--xlators/nfs/server/src/nfsserver.sym12
-rw-r--r--xlators/nfs/server/src/nlm4.c2786
-rw-r--r--xlators/nfs/server/src/nlm4.h111
-rw-r--r--xlators/nfs/server/src/nlmcbk_svc.c134
36 files changed, 24462 insertions, 12467 deletions
diff --git a/xlators/nfs/server/src/Makefile.am b/xlators/nfs/server/src/Makefile.am
index 8fd28638506..01071a79a21 100644
--- a/xlators/nfs/server/src/Makefile.am
+++ b/xlators/nfs/server/src/Makefile.am
@@ -1,17 +1,38 @@
+if WITH_SERVER
xlator_LTLIBRARIES = server.la
+endif
+
xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/nfs
nfsrpclibdir = $(top_srcdir)/rpc/rpc-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 nfs3.c nfs3-helpers.c
-server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
+server_la_LDFLAGS = -module \
+ -export-symbols $(top_srcdir)/xlators/nfs/server/src/nfsserver.sym \
+ $(GF_XLATOR_LDFLAGS)
+
+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 nlm4.c \
+ nlmcbk_svc.c mount3udp_svc.c acl3.c netgroups.c exports.c \
+ mount3-auth.c auth-cache.c
+
+server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la \
+ $(top_builddir)/api/src/libgfapi.la \
+ $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la \
+ $(top_builddir)/rpc/xdr/src/libgfxdr.la
-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 nfs-mem-types.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 nfs-mem-types.h nlm4.h \
+ acl3.h netgroups.h exports.h mount3-auth.h auth-cache.h nfs-messages.h
-AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\
+AM_CPPFLAGS = $(GF_CPPFLAGS) \
-DLIBDIR=\"$(libdir)/glusterfs/$(PACKAGE_VERSION)/auth\" \
- -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS)\
- -I$(nfsrpclibdir) -L$(xlatordir)/ -I$(CONTRIBDIR)/rbtree\
- -I$(top_srcdir)/rpc/xdr/src/ \
- -I$(top_srcdir)/contrib/md5/
+ -I$(top_srcdir)/libglusterfs/src -I$(top_srcdir)/api/src \
+ -I$(top_srcdir)/rpc/xdr/src/ -I$(top_builddir)/rpc/xdr/src/ \
+ -I$(nfsrpclibdir) -I$(CONTRIBDIR)/rbtree \
+ -DDATADIR=\"$(localstatedir)\"
+
+AM_CFLAGS = -Wall $(GF_CFLAGS)
+
+AM_LDFLAGS = -L$(xlatordir)
CLEANFILES =
+
+EXTRA_DIST = nfsserver.sym
diff --git a/xlators/nfs/server/src/acl3.c b/xlators/nfs/server/src/acl3.c
new file mode 100644
index 00000000000..7e3bbf16086
--- /dev/null
+++ b/xlators/nfs/server/src/acl3.c
@@ -0,0 +1,933 @@
+/*
+ * Copyright (c) 2012-2013 Red Hat, Inc. <http://www.redhat.com>
+ * This file is part of GlusterFS.
+ *
+ * This file is licensed to you under your choice of the GNU Lesser
+ * General Public License, version 3 or any later version (LGPLv3 or
+ * later), or the GNU General Public License, version 2 (GPLv2), in all
+ * cases as published by the Free Software Foundation.
+ */
+
+#include <glusterfs/defaults.h>
+#include "rpcsvc.h"
+#include <glusterfs/dict.h>
+#include <glusterfs/xlator.h>
+#include "nfs.h"
+#include <glusterfs/mem-pool.h>
+#include <glusterfs/logging.h>
+#include "nfs-fops.h"
+#include "nfs3.h"
+#include "nfs-mem-types.h"
+#include "nfs3-helpers.h"
+#include "nfs3-fh.h"
+#include "nfs-generics.h"
+#include "acl3.h"
+#include <glusterfs/byte-order.h>
+#include <glusterfs/compat-errno.h>
+#include "nfs-messages.h"
+
+static int
+acl3_nfs_acl_to_xattr(aclentry *ace, void *xattrbuf, int aclcount, int defacl);
+
+static int
+acl3_nfs_acl_from_xattr(aclentry *ace, void *xattrbuf, int bufsize, int defacl);
+
+typedef ssize_t (*acl3_serializer)(struct iovec outmsg, void *args);
+
+extern void
+nfs3_call_state_wipe(nfs3_call_state_t *cs);
+
+extern nfs3_call_state_t *
+nfs3_call_state_init(struct nfs3_state *s, rpcsvc_request_t *req, xlator_t *v);
+
+extern int
+nfs3_fh_validate(struct nfs3_fh *fh);
+
+extern void
+nfs3_stat_to_fattr3(struct iatt *buf, fattr3 *fa);
+
+#define acl3_validate_nfs3_state(request, state, status, label, retval) \
+ do { \
+ state = rpcsvc_request_program_private(request); \
+ if (!state) { \
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_STATE_MISSING, \
+ "NFSv3 state " \
+ "missing from RPC request"); \
+ rpcsvc_request_seterr(req, SYSTEM_ERR); \
+ status = NFS3ERR_SERVERFAULT; \
+ goto label; \
+ } \
+ } while (0);
+
+#define acl3_validate_gluster_fh(handle, status, errlabel) \
+ do { \
+ if (!nfs3_fh_validate(handle)) { \
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_BAD_HANDLE, \
+ "Bad Handle"); \
+ status = NFS3ERR_BADHANDLE; \
+ goto errlabel; \
+ } \
+ } while (0)
+
+extern xlator_t *
+nfs3_fh_to_xlator(struct nfs3_state *nfs3, struct nfs3_fh *fh);
+
+#define acl3_map_fh_to_volume(nfs3state, handle, req, volume, status, label) \
+ do { \
+ char exportid[256], gfid[256]; \
+ rpc_transport_t *trans = NULL; \
+ volume = nfs3_fh_to_xlator((nfs3state), handle); \
+ if (!volume) { \
+ gf_uuid_unparse(handle->exportid, exportid); \
+ gf_uuid_unparse(handle->gfid, gfid); \
+ trans = rpcsvc_request_transport(req); \
+ gf_msg(GF_ACL, GF_LOG_ERROR, 0, NFS_MSG_FH_TO_VOL_FAIL, \
+ "Failed to map " \
+ "FH to vol: client=%s, exportid=%s, gfid=%s", \
+ trans->peerinfo.identifier, exportid, gfid); \
+ gf_msg(GF_ACL, GF_LOG_ERROR, ESTALE, NFS_MSG_VOLUME_ERROR, \
+ "Stale nfs client %s must be trying to " \
+ "connect to a deleted volume, please " \
+ "unmount it.", \
+ trans->peerinfo.identifier); \
+ status = NFS3ERR_STALE; \
+ goto label; \
+ } else { \
+ gf_msg_trace(GF_ACL, 0, "FH to Volume: %s", volume->name); \
+ rpcsvc_request_set_private(req, volume); \
+ } \
+ } while (0);
+
+#define acl3_volume_started_check(nfs3state, vlm, rtval, erlbl) \
+ do { \
+ if ((!nfs_subvolume_started(nfs_state(nfs3state->nfsx), vlm))) { \
+ gf_msg(GF_ACL, GF_LOG_ERROR, 0, NFS_MSG_VOL_DISABLE, \
+ "Volume is disabled: %s", vlm->name); \
+ rtval = RPCSVC_ACTOR_IGNORE; \
+ goto erlbl; \
+ } \
+ } while (0)
+
+#define acl3_check_fh_resolve_status(cst, nfstat, erlabl) \
+ do { \
+ xlator_t *xlatorp = NULL; \
+ char buf[256], gfid[GF_UUID_BUF_SIZE]; \
+ rpc_transport_t *trans = NULL; \
+ if ((cst)->resolve_ret < 0) { \
+ trans = rpcsvc_request_transport(cst->req); \
+ xlatorp = nfs3_fh_to_xlator(cst->nfs3state, &cst->resolvefh); \
+ gf_uuid_unparse(cst->resolvefh.gfid, gfid); \
+ snprintf(buf, sizeof(buf), "(%s) %s : %s", \
+ trans->peerinfo.identifier, \
+ xlatorp ? xlatorp->name : "ERR", gfid); \
+ gf_msg(GF_ACL, GF_LOG_ERROR, cst->resolve_errno, \
+ NFS_MSG_RESOLVE_FH_FAIL, \
+ "Unable to resolve " \
+ "FH: %s", \
+ buf); \
+ nfstat = nfs3_errno_to_nfsstat3(cst->resolve_errno); \
+ goto erlabl; \
+ } \
+ } while (0)
+
+#define acl3_handle_call_state_init(nfs3state, calls, rq, v, opstat, errlabel) \
+ do { \
+ calls = nfs3_call_state_init((nfs3state), (rq), v); \
+ if (!calls) { \
+ gf_msg(GF_ACL, GF_LOG_ERROR, 0, NFS_MSG_INIT_CALL_STAT_FAIL, \
+ "Failed to " \
+ "init call state"); \
+ opstat = NFS3ERR_SERVERFAULT; \
+ rpcsvc_request_seterr(req, SYSTEM_ERR); \
+ goto errlabel; \
+ } \
+ } while (0)
+
+int
+acl3svc_submit_reply(rpcsvc_request_t *req, void *arg, acl3_serializer sfunc)
+{
+ struct iovec outmsg = {
+ 0,
+ };
+ struct iobuf *iob = NULL;
+ struct nfs3_state *nfs3 = NULL;
+ int ret = -1;
+ ssize_t msglen = 0;
+ struct iobref *iobref = NULL;
+
+ if (!req)
+ return -1;
+
+ nfs3 = (struct nfs3_state *)rpcsvc_request_program_private(req);
+ if (!nfs3) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "mount state not found");
+ goto ret;
+ }
+
+ /* First, get the io buffer into which the reply in arg will
+ * be serialized.
+ */
+ iob = iobuf_get(nfs3->iobpool);
+ if (!iob) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "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.
+ */
+ msglen = sfunc(outmsg, arg);
+ if (msglen < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_ENCODE_MSG_FAIL,
+ "Failed to encode message");
+ goto ret;
+ }
+ outmsg.iov_len = msglen;
+
+ iobref = iobref_new();
+ if (iobref == NULL) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to get iobref");
+ goto ret;
+ }
+
+ ret = iobref_add(iobref, iob);
+ if (ret) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to add iob to iobref");
+ goto ret;
+ }
+
+ /* Then, submit the message for transmission. */
+ ret = rpcsvc_submit_message(req, &outmsg, 1, NULL, 0, iobref);
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_REP_SUBMIT_FAIL,
+ "Reply submission failed");
+ goto ret;
+ }
+
+ ret = 0;
+ret:
+ if (iob)
+ iobuf_unref(iob);
+ if (iobref)
+ iobref_unref(iobref);
+
+ return ret;
+}
+
+int
+acl3svc_null(rpcsvc_request_t *req)
+{
+ struct iovec dummyvec = {
+ 0,
+ };
+
+ if (!req) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Got NULL request!");
+ return 0;
+ }
+ rpcsvc_submit_generic(req, &dummyvec, 1, NULL, 0, NULL);
+ return 0;
+}
+
+int
+acl3_getacl_reply(rpcsvc_request_t *req, getaclreply *reply)
+{
+ acl3svc_submit_reply(req, (void *)reply,
+ (acl3_serializer)xdr_serialize_getaclreply);
+ return 0;
+}
+
+int
+acl3_setacl_reply(rpcsvc_request_t *req, setaclreply *reply)
+{
+ acl3svc_submit_reply(req, (void *)reply,
+ (acl3_serializer)xdr_serialize_setaclreply);
+ return 0;
+}
+
+/* acl3_getacl_cbk: fetch and decode the ACL in the POSIX_ACL_ACCESS_XATTR
+ *
+ * The POSIX_ACL_ACCESS_XATTR can be set on files and directories.
+ */
+int
+acl3_getacl_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, dict_t *dict, dict_t *xdata)
+{
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ nfs3_call_state_t *cs = NULL;
+ data_t *data = NULL;
+ getaclreply *getaclreply = NULL;
+ int aclcount = 0;
+ int defacl = 1; /* DEFAULT ACL */
+
+ if (!frame->local) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid argument, frame->local NULL");
+ return -EINVAL;
+ }
+ cs = frame->local;
+ getaclreply = &cs->args.getaclreply;
+ if ((op_ret < 0) && (op_errno != ENODATA && op_errno != ENOATTR)) {
+ stat = nfs3_cbk_errno_status(op_ret, op_errno);
+ goto err;
+ } else if (!dict) {
+ /* no ACL has been set */
+ stat = NFS3_OK;
+ goto err;
+ }
+
+ getaclreply->aclentry.aclentry_val = cs->aclentry;
+
+ /* getfacl: NFS USER ACL */
+ data = dict_get(dict, POSIX_ACL_ACCESS_XATTR);
+ if (data && data->data) {
+ aclcount = acl3_nfs_acl_from_xattr(cs->aclentry, data->data, data->len,
+ !defacl);
+ if (aclcount < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, aclcount, NFS_MSG_GET_USER_ACL_FAIL,
+ "Failed to get USER ACL");
+ stat = nfs3_errno_to_nfsstat3(-aclcount);
+ goto err;
+ }
+ getaclreply->aclcount = aclcount;
+ getaclreply->aclentry.aclentry_len = aclcount;
+ }
+
+ acl3_getacl_reply(cs->req, getaclreply);
+ nfs3_call_state_wipe(cs);
+ return 0;
+
+err:
+ if (getaclreply)
+ getaclreply->status = stat;
+ acl3_getacl_reply(cs->req, getaclreply);
+ nfs3_call_state_wipe(cs);
+ return 0;
+}
+
+/* acl3_default_getacl_cbk: fetch and decode the ACL set in the
+ * POSIX_ACL_DEFAULT_XATTR xattr.
+ *
+ * The POSIX_ACL_DEFAULT_XATTR xattr is only set on directories, not on files.
+ *
+ * When done with POSIX_ACL_DEFAULT_XATTR, we also need to get and decode the
+ * ACL that can be set in POSIX_ACL_DEFAULT_XATTR.
+ */
+int
+acl3_default_getacl_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, dict_t *dict,
+ dict_t *xdata)
+{
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ nfs3_call_state_t *cs = NULL;
+ data_t *data = NULL;
+ getaclreply *getaclreply = NULL;
+ int aclcount = 0;
+ int defacl = 1; /* DEFAULT ACL */
+ nfs_user_t nfu = {
+ 0,
+ };
+ int ret = -1;
+
+ if (!frame->local) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid argument, frame->local NULL");
+ return -EINVAL;
+ }
+ cs = frame->local;
+ getaclreply = &cs->args.getaclreply;
+ if ((op_ret < 0) && (op_errno != ENODATA && op_errno != ENOATTR)) {
+ stat = nfs3_cbk_errno_status(op_ret, op_errno);
+ goto err;
+ } else if (!dict) {
+ /* no ACL has been set */
+ stat = NFS3_OK;
+ goto err;
+ }
+
+ getaclreply->daclentry.daclentry_val = cs->daclentry;
+
+ /* getfacl: NFS DEFAULT ACL */
+ data = dict_get(dict, POSIX_ACL_DEFAULT_XATTR);
+ if (data && data->data) {
+ aclcount = acl3_nfs_acl_from_xattr(cs->daclentry, data->data, data->len,
+ defacl);
+ if (aclcount < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, aclcount, NFS_MSG_GET_DEF_ACL_FAIL,
+ "Failed to get DEFAULT ACL");
+ stat = nfs3_errno_to_nfsstat3(-aclcount);
+ goto err;
+ }
+
+ getaclreply->daclcount = aclcount;
+ getaclreply->daclentry.daclentry_len = aclcount;
+ }
+
+ getaclreply->attr_follows = TRUE;
+ nfs_request_user_init(&nfu, cs->req);
+ ret = nfs_getxattr(cs->nfsx, cs->vol, &nfu, &cs->resolvedloc,
+ POSIX_ACL_ACCESS_XATTR, NULL, acl3_getacl_cbk, cs);
+ if (ret < 0) {
+ stat = nfs3_errno_to_nfsstat3(-ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (getaclreply)
+ getaclreply->status = stat;
+ acl3_getacl_reply(cs->req, getaclreply);
+ nfs3_call_state_wipe(cs);
+ return 0;
+}
+
+int
+acl3_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
+ int32_t op_errno, struct iatt *buf, dict_t *xdata)
+{
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ nfs3_call_state_t *cs = NULL;
+ getaclreply *getaclreply = NULL;
+ int ret = -1;
+ nfs_user_t nfu = {
+ 0,
+ };
+ uint64_t deviceid = 0;
+
+ if (!frame->local) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid argument, frame->local NULL");
+ return EINVAL;
+ }
+
+ cs = frame->local;
+ getaclreply = &cs->args.getaclreply;
+
+ if (op_ret == -1) {
+ stat = nfs3_cbk_errno_status(op_ret, op_errno);
+ goto err;
+ }
+
+ /* Fill the attrs before xattrs */
+ getaclreply->attr_follows = TRUE;
+ deviceid = nfs3_request_xlator_deviceid(cs->req);
+ nfs3_map_deviceid_to_statdev(buf, deviceid);
+ nfs3_stat_to_fattr3(buf, &(getaclreply->attr));
+
+ nfs_request_user_init(&nfu, cs->req);
+ if (buf->ia_type == IA_IFDIR) {
+ ret = nfs_getxattr(cs->nfsx, cs->vol, &nfu, &cs->resolvedloc,
+ POSIX_ACL_DEFAULT_XATTR, NULL,
+ acl3_default_getacl_cbk, cs);
+ } else {
+ ret = nfs_getxattr(cs->nfsx, cs->vol, &nfu, &cs->resolvedloc,
+ POSIX_ACL_ACCESS_XATTR, NULL, acl3_getacl_cbk, cs);
+ }
+
+ if (ret < 0) {
+ stat = nfs3_errno_to_nfsstat3(-ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ getaclreply->status = stat;
+ acl3_getacl_reply(cs->req, getaclreply);
+ nfs3_call_state_wipe(cs);
+ return 0;
+}
+
+int
+acl3_getacl_resume(void *carg)
+{
+ int ret = -1;
+ nfs3_call_state_t *cs = NULL;
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ nfs_user_t nfu = {
+ 0,
+ };
+
+ if (!carg)
+ return ret;
+
+ cs = (nfs3_call_state_t *)carg;
+ acl3_check_fh_resolve_status(cs, stat, acl3err);
+ nfs_request_user_init(&nfu, cs->req);
+
+ ret = nfs_stat(cs->nfsx, cs->vol, &nfu, &cs->resolvedloc, acl3_stat_cbk,
+ cs);
+ stat = -ret;
+acl3err:
+ if (ret < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, stat, NFS_MSG_OPEN_FAIL,
+ "unable to open_and_resume");
+ cs->args.getaclreply.status = nfs3_errno_to_nfsstat3(stat);
+ acl3_getacl_reply(cs->req, &cs->args.getaclreply);
+ nfs3_call_state_wipe(cs);
+ }
+
+ return ret;
+}
+
+int
+acl3svc_getacl(rpcsvc_request_t *req)
+{
+ xlator_t *vol = NULL;
+ struct nfs_state *nfs = NULL;
+ nfs3_state_t *nfs3 = NULL;
+ nfs3_call_state_t *cs = NULL;
+ int ret = RPCSVC_ACTOR_ERROR;
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ struct nfs3_fh fh, *fhp = NULL;
+ getaclargs getaclargs;
+ getaclreply getaclreply;
+
+ if (!req)
+ return ret;
+
+ acl3_validate_nfs3_state(req, nfs3, stat, rpcerr, ret);
+ nfs = nfs_state(nfs3->nfsx);
+ memset(&getaclargs, 0, sizeof(getaclargs));
+ memset(&getaclreply, 0, sizeof(getaclreply));
+ getaclargs.fh.n_bytes = (char *)&fh;
+ if (xdr_to_getaclargs(req->msg[0], &getaclargs) <= 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_ARGS_DECODE_ERROR,
+ "Error decoding args");
+ rpcsvc_request_seterr(req, GARBAGE_ARGS);
+ goto rpcerr;
+ }
+
+ /* Validate ACL mask */
+ if (getaclargs.mask & ~(NFS_ACL | NFS_ACLCNT | NFS_DFACL | NFS_DFACLCNT)) {
+ stat = NFS3ERR_INVAL;
+ goto acl3err;
+ }
+
+ fhp = &fh;
+ acl3_validate_gluster_fh(&fh, stat, acl3err);
+ acl3_map_fh_to_volume(nfs->nfs3state, fhp, req, vol, stat, acl3err);
+ acl3_volume_started_check(nfs3, vol, ret, rpcerr);
+ acl3_handle_call_state_init(nfs->nfs3state, cs, req, vol, stat, acl3err);
+
+ cs->vol = vol;
+ cs->args.getaclreply.mask = getaclargs.mask;
+
+ ret = nfs3_fh_resolve_and_resume(cs, fhp, NULL, acl3_getacl_resume);
+ stat = nfs3_errno_to_nfsstat3(-ret);
+
+acl3err:
+ if (ret < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, -ret, NFS_MSG_RESOLVE_ERROR,
+ "unable to resolve and resume");
+ getaclreply.status = stat;
+ acl3_getacl_reply(req, &getaclreply);
+ nfs3_call_state_wipe(cs);
+ return 0;
+ }
+
+rpcerr:
+ return ret;
+}
+
+int
+acl3_setacl_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, dict_t *xdata)
+{
+ nfs3_call_state_t *cs = NULL;
+ cs = frame->local;
+ if (op_ret < 0) {
+ nfsstat3 status = nfs3_cbk_errno_status(op_ret, op_errno);
+ cs->args.setaclreply.status = status;
+ }
+
+ acl3_setacl_reply(cs->req, &cs->args.setaclreply);
+
+ nfs3_call_state_wipe(cs);
+
+ return 0;
+}
+
+int
+acl3_setacl_resume(void *carg)
+{
+ int ret = -1;
+ nfs3_call_state_t *cs = NULL;
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ nfs_user_t nfu = {
+ 0,
+ };
+ dict_t *xattr = NULL;
+
+ if (!carg)
+ return ret;
+ cs = (nfs3_call_state_t *)carg;
+ acl3_check_fh_resolve_status(cs, stat, acl3err);
+ nfs_request_user_init(&nfu, cs->req);
+ xattr = dict_new();
+ if (xattr == NULL) {
+ gf_msg(GF_NLM, GF_LOG_ERROR, ENOMEM, NFS_MSG_GFID_DICT_CREATE_FAIL,
+ "dict allocation failed");
+ goto acl3err;
+ }
+
+ if (cs->aclcount)
+ ret = dict_set_static_bin(xattr, POSIX_ACL_ACCESS_XATTR, cs->aclxattr,
+ posix_acl_xattr_size(cs->aclcount));
+ if (cs->daclcount)
+ ret = dict_set_static_bin(xattr, POSIX_ACL_DEFAULT_XATTR, cs->daclxattr,
+ posix_acl_xattr_size(cs->daclcount));
+
+ ret = nfs_setxattr(cs->nfsx, cs->vol, &nfu, &cs->resolvedloc, xattr, 0,
+ NULL, acl3_setacl_cbk, cs);
+ dict_unref(xattr);
+
+acl3err:
+ if (ret < 0) {
+ stat = -ret;
+ gf_msg(GF_ACL, GF_LOG_ERROR, stat, NFS_MSG_OPEN_FAIL,
+ "unable to open_and_resume");
+ cs->args.setaclreply.status = nfs3_errno_to_nfsstat3(stat);
+ acl3_setacl_reply(cs->req, &cs->args.setaclreply);
+ nfs3_call_state_wipe(cs);
+ }
+
+ return ret;
+}
+
+int
+acl3svc_setacl(rpcsvc_request_t *req)
+{
+ xlator_t *vol = NULL;
+ struct nfs_state *nfs = NULL;
+ nfs3_state_t *nfs3 = NULL;
+ nfs3_call_state_t *cs = NULL;
+ int ret = RPCSVC_ACTOR_ERROR;
+ nfsstat3 stat = NFS3ERR_SERVERFAULT;
+ struct nfs3_fh fh;
+ struct nfs3_fh *fhp = NULL;
+ setaclargs setaclargs;
+ setaclreply setaclreply;
+ aclentry *daclentry = NULL;
+ aclentry *aclentry = NULL;
+ int aclerrno = 0;
+ int defacl = 1;
+
+ if (!req)
+ return ret;
+ aclentry = GF_CALLOC(NFS_ACL_MAX_ENTRIES, sizeof(*aclentry), gf_nfs_mt_arr);
+ if (!aclentry) {
+ goto rpcerr;
+ }
+ daclentry = GF_CALLOC(NFS_ACL_MAX_ENTRIES, sizeof(*daclentry),
+ gf_nfs_mt_arr);
+ if (!daclentry) {
+ goto rpcerr;
+ }
+
+ acl3_validate_nfs3_state(req, nfs3, stat, rpcerr, ret);
+ nfs = nfs_state(nfs3->nfsx);
+ memset(&setaclargs, 0, sizeof(setaclargs));
+ memset(&setaclreply, 0, sizeof(setaclreply));
+ memset(&fh, 0, sizeof(fh));
+ setaclargs.fh.n_bytes = (char *)&fh;
+ setaclargs.aclentry.aclentry_val = aclentry;
+ setaclargs.daclentry.daclentry_val = daclentry;
+ if (xdr_to_setaclargs(req->msg[0], &setaclargs) <= 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_ARGS_DECODE_ERROR,
+ "Error decoding args");
+ rpcsvc_request_seterr(req, GARBAGE_ARGS);
+ goto rpcerr;
+ }
+
+ /* Validate ACL mask */
+ if (setaclargs.mask & ~(NFS_ACL | NFS_ACLCNT | NFS_DFACL | NFS_DFACLCNT)) {
+ stat = NFS3ERR_INVAL;
+ goto acl3err;
+ }
+
+ fhp = &fh;
+ acl3_validate_gluster_fh(fhp, stat, acl3err);
+ acl3_map_fh_to_volume(nfs->nfs3state, fhp, req, vol, stat, acl3err);
+ acl3_volume_started_check(nfs3, vol, ret, rpcerr);
+ acl3_handle_call_state_init(nfs->nfs3state, cs, req, vol, stat, acl3err);
+
+ cs->vol = vol;
+ cs->aclcount = setaclargs.aclcount;
+ cs->daclcount = setaclargs.daclcount;
+
+ /* setfacl: NFS USER ACL */
+ aclerrno = acl3_nfs_acl_to_xattr(aclentry, cs->aclxattr, cs->aclcount,
+ !defacl);
+ if (aclerrno < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, -aclerrno, NFS_MSG_SET_USER_ACL_FAIL,
+ "Failed to set USER ACL");
+ stat = nfs3_errno_to_nfsstat3(-aclerrno);
+ goto acl3err;
+ }
+
+ /* setfacl: NFS DEFAULT ACL */
+ aclerrno = acl3_nfs_acl_to_xattr(daclentry, cs->daclxattr, cs->daclcount,
+ defacl);
+ if (aclerrno < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, -aclerrno, NFS_MSG_SET_DEF_ACL_FAIL,
+ "Failed to set DEFAULT ACL");
+ stat = nfs3_errno_to_nfsstat3(-aclerrno);
+ goto acl3err;
+ }
+
+ ret = nfs3_fh_resolve_and_resume(cs, fhp, NULL, acl3_setacl_resume);
+ stat = nfs3_errno_to_nfsstat3(-ret);
+
+acl3err:
+ if (ret < 0) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, -ret, NFS_MSG_RESOLVE_ERROR,
+ "unable to resolve and resume");
+ setaclreply.status = stat;
+ acl3_setacl_reply(req, &setaclreply);
+ nfs3_call_state_wipe(cs);
+ GF_FREE(aclentry);
+ GF_FREE(daclentry);
+ return 0;
+ }
+
+rpcerr:
+ if (ret < 0)
+ nfs3_call_state_wipe(cs);
+ if (aclentry)
+ GF_FREE(aclentry);
+ if (daclentry)
+ GF_FREE(daclentry);
+ return ret;
+}
+
+static rpcsvc_actor_t acl3svc_actors[ACL3_PROC_COUNT] = {
+ {"NULL", acl3svc_null, NULL, ACL3_NULL, DRC_NA, 0},
+ {"GETACL", acl3svc_getacl, NULL, ACL3_GETACL, DRC_NA, 0},
+ {"SETACL", acl3svc_setacl, NULL, ACL3_SETACL, DRC_NA, 0},
+};
+
+static rpcsvc_program_t acl3prog = {
+ .progname = "ACL3",
+ .prognum = ACL_PROGRAM,
+ .progver = ACLV3_VERSION,
+ .progport = GF_NFS3_PORT,
+ .actors = acl3svc_actors,
+ .numactors = ACL3_PROC_COUNT,
+ .min_auth = AUTH_NULL,
+};
+
+rpcsvc_program_t *
+acl3svc_init(xlator_t *nfsx)
+{
+ struct nfs3_state *ns = NULL;
+ struct nfs_state *nfs = NULL;
+ dict_t *options = NULL;
+ int ret = -1;
+ static gf_boolean_t acl3_inited = _gf_false;
+
+ /* Already inited */
+ if (acl3_inited)
+ return &acl3prog;
+
+ nfs = (struct nfs_state *)nfsx->private;
+
+ ns = nfs->nfs3state;
+ if (!ns) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, EINVAL, NFS_MSG_ACL_INIT_FAIL,
+ "ACL3 init failed");
+ goto err;
+ }
+ acl3prog.private = ns;
+
+ options = dict_new();
+ if (options == NULL) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, ENOMEM, NFS_MSG_GFID_DICT_CREATE_FAIL,
+ "dict allocation failed");
+ goto err;
+ }
+
+ ret = dict_set_str(options, "transport.socket.listen-port", GF_ACL3_PORT);
+ if (ret == -1)
+ goto err;
+ ret = dict_set_str(options, "transport-type", "socket");
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+
+ if (nfs->allow_insecure) {
+ ret = dict_set_str(options, "rpc-auth-allow-insecure", "on");
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ ret = dict_set_str(options, "rpc-auth.ports.insecure", "on");
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ }
+
+ ret = dict_set_str(options, "transport.address-family", "inet");
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+
+ ret = rpcsvc_create_listeners(nfs->rpcsvc, options, "ACL");
+ if (ret == -1) {
+ gf_msg(GF_ACL, GF_LOG_ERROR, errno, NFS_MSG_LISTENERS_CREATE_FAIL,
+ "Unable to create listeners");
+ goto err;
+ }
+
+ if (options)
+ dict_unref(options);
+
+ acl3_inited = _gf_true;
+ return &acl3prog;
+err:
+ if (options)
+ dict_unref(options);
+ return NULL;
+}
+
+static int
+acl3_nfs_acl_to_xattr(aclentry *ace, /* ACL entries to be read */
+ void *xattrbuf, /* XATTR buf to be populated */
+ int aclcount, /* No of ACLs to be read */
+ int defacl) /* 1 if DEFAULT ACL */
+{
+ int idx = 0;
+ posix_acl_xattr_header *xheader = NULL;
+ posix_acl_xattr_entry *xentry = NULL;
+
+ if ((!ace) || (!xattrbuf))
+ return (-EINVAL);
+
+ /* ACL count is ZERO, nothing to do */
+ if (!aclcount)
+ return (0);
+
+ if ((aclcount < 0) || (aclcount > NFS_ACL_MAX_ENTRIES))
+ return (-EINVAL);
+
+ xheader = (posix_acl_xattr_header *)(xattrbuf);
+ xentry = (posix_acl_xattr_entry *)(xheader + 1);
+
+ /*
+ * For "default ACL", NFSv3 handles the 'type' differently
+ * i.e. by logical OR'ing 'type' with NFS_ACL_DEFAULT.
+ * Which the backend File system does not understand and
+ * that needs to be masked OFF.
+ */
+ xheader->version = POSIX_ACL_XATTR_VERSION;
+
+ for (idx = 0; idx < aclcount; idx++) {
+ xentry->tag = ace->type;
+ if (defacl)
+ xentry->tag &= ~NFS_ACL_DEFAULT;
+ xentry->perm = ace->perm;
+
+ switch (xentry->tag) {
+ case POSIX_ACL_USER:
+ case POSIX_ACL_GROUP:
+ if (xentry->perm & ~S_IRWXO)
+ return (-EINVAL);
+ xentry->id = ace->uid;
+ break;
+ case POSIX_ACL_USER_OBJ:
+ case POSIX_ACL_GROUP_OBJ:
+ case POSIX_ACL_OTHER:
+ if (xentry->perm & ~S_IRWXO)
+ return (-EINVAL);
+ xentry->id = POSIX_ACL_UNDEFINED_ID;
+ break;
+ case POSIX_ACL_MASK:
+ /* Solaris sometimes sets additional bits in
+ * the mask.
+ */
+ xentry->perm &= S_IRWXO;
+ xentry->id = POSIX_ACL_UNDEFINED_ID;
+ break;
+ default:
+ return (-EINVAL);
+ }
+
+ xentry++;
+ ace++;
+ }
+
+ /* SUCCESS */
+ return (0);
+}
+
+static int
+acl3_nfs_acl_from_xattr(aclentry *ace, /* ACL entries to be filled */
+ void *xattrbuf, /* XATTR buf to be read */
+ int bufsize, /* Size of XATTR buffer */
+ int defacl) /* 1 if DEFAULT ACL */
+{
+ int idx = 0;
+ ssize_t aclcount = 0;
+ posix_acl_xattr_header *xheader = NULL;
+ posix_acl_xattr_entry *xentry = NULL;
+
+ if ((!xattrbuf) || (!ace))
+ return (-EINVAL);
+
+ aclcount = posix_acl_xattr_count(bufsize);
+ if ((aclcount < 0) || (aclcount > NFS_ACL_MAX_ENTRIES))
+ return (-EINVAL);
+
+ xheader = (posix_acl_xattr_header *)(xattrbuf);
+ xentry = (posix_acl_xattr_entry *)(xheader + 1);
+
+ /* Check for supported POSIX ACL xattr version */
+ if (xheader->version != POSIX_ACL_XATTR_VERSION)
+ return (-ENOSYS);
+
+ for (idx = 0; idx < (int)aclcount; idx++) {
+ ace->type = xentry->tag;
+ if (defacl) {
+ /*
+ * SET the NFS_ACL_DEFAULT flag for default
+ * ACL which was masked OFF during setfacl().
+ */
+ ace->type |= NFS_ACL_DEFAULT;
+ }
+ ace->perm = (xentry->perm & S_IRWXO);
+
+ switch (xentry->tag) {
+ case POSIX_ACL_USER:
+ case POSIX_ACL_GROUP:
+ ace->uid = xentry->id;
+ break;
+ case POSIX_ACL_USER_OBJ:
+ case POSIX_ACL_GROUP_OBJ:
+ case POSIX_ACL_MASK:
+ case POSIX_ACL_OTHER:
+ ace->uid = POSIX_ACL_UNDEFINED_ID;
+ break;
+ default:
+ return (-EINVAL);
+ }
+
+ xentry++;
+ ace++;
+ }
+
+ /* SUCCESS: ACL count */
+ return aclcount;
+}
diff --git a/xlators/nfs/server/src/acl3.h b/xlators/nfs/server/src/acl3.h
new file mode 100644
index 00000000000..762fbb04a0f
--- /dev/null
+++ b/xlators/nfs/server/src/acl3.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Red Hat, Inc. <http://www.redhat.com>
+ * This file is part of GlusterFS.
+ *
+ * This file is licensed to you under your choice of the GNU Lesser
+ * General Public License, version 3 or any later version (LGPLv3 or
+ * later), or the GNU General Public License, version 2 (GPLv2), in all
+ * cases as published by the Free Software Foundation.
+ */
+
+#ifndef _ACL3_H
+#define _ACL3_H
+
+#include <glusterfs/glusterfs-acl.h>
+
+#define ACL3_NULL 0
+#define ACL3_GETACL 1
+#define ACL3_SETACL 2
+#define ACL3_PROC_COUNT 3
+
+#define GF_ACL3_PORT "38469"
+#define GF_ACL GF_NFS "-ACL"
+
+/* Flags for the getacl/setacl mode */
+#define NFS_ACL 0x0001
+#define NFS_ACLCNT 0x0002
+#define NFS_DFACL 0x0004
+#define NFS_DFACLCNT 0x0008
+
+/*
+ * NFSv3, identifies the default ACL by NFS_ACL_DEFAULT. Gluster
+ * NFS needs to mask it OFF before sending it up to POSIX layer
+ * or File system layer.
+ */
+#define NFS_ACL_DEFAULT 0x1000
+
+#define NFS_ACL_MAX_ENTRIES 1024
+
+rpcsvc_program_t *
+acl3svc_init(xlator_t *nfsx);
+
+#endif
diff --git a/xlators/nfs/server/src/auth-cache.c b/xlators/nfs/server/src/auth-cache.c
new file mode 100644
index 00000000000..ffbf5b6cad6
--- /dev/null
+++ b/xlators/nfs/server/src/auth-cache.c
@@ -0,0 +1,496 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include <glusterfs/refcount.h>
+#include "auth-cache.h"
+#include "nfs3.h"
+#include "exports.h"
+#include "nfs-messages.h"
+
+enum auth_cache_lookup_results {
+ ENTRY_FOUND = 0,
+ ENTRY_NOT_FOUND = -1,
+ ENTRY_EXPIRED = -2,
+};
+
+struct auth_cache_entry {
+ GF_REF_DECL; /* refcounting */
+ data_t *data; /* data_unref() on refcount == 0 */
+
+ time_t timestamp;
+ struct export_item *item;
+};
+
+/* Given a filehandle and an ip, creates a colon delimited hashkey.
+ */
+static char *
+make_hashkey(struct nfs3_fh *fh, const char *host)
+{
+ char *hashkey = NULL;
+ char exportid[256] = {
+ 0,
+ };
+ char mountid[256] = {
+ 0,
+ };
+ size_t nbytes = 0;
+
+ gf_uuid_unparse(fh->exportid, exportid);
+ gf_uuid_unparse(fh->mountid, mountid);
+
+ nbytes = strlen(exportid) + strlen(host) + strlen(mountid) + 3;
+ hashkey = GF_MALLOC(nbytes, gf_common_mt_char);
+ if (!hashkey)
+ return NULL;
+
+ snprintf(hashkey, nbytes, "%s:%s:%s", exportid, mountid, host);
+
+ return hashkey;
+}
+
+/**
+ * auth_cache_init -- Initialize an auth cache and set the ttl_sec
+ *
+ * @ttl_sec : The TTL to set in seconds
+ *
+ * @return : allocated auth cache struct, NULL if allocation failed.
+ */
+struct auth_cache *
+auth_cache_init(time_t ttl_sec)
+{
+ struct auth_cache *cache = GF_CALLOC(1, sizeof(*cache),
+ gf_nfs_mt_auth_cache);
+
+ GF_VALIDATE_OR_GOTO("auth-cache", cache, out);
+
+ cache->cache_dict = dict_new();
+ if (!cache->cache_dict) {
+ GF_FREE(cache);
+ cache = NULL;
+ goto out;
+ }
+
+ LOCK_INIT(&cache->lock);
+ cache->ttl_sec = ttl_sec;
+out:
+ return cache;
+}
+
+/* auth_cache_entry_free -- called by refcounting subsystem on refcount == 0
+ *
+ * @to_free: auth_cache_entry that has refcount == 0 and needs to get free'd
+ */
+void
+auth_cache_entry_free(void *to_free)
+{
+ struct auth_cache_entry *entry = to_free;
+ data_t *entry_data = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NFS, entry, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, entry->data, out);
+
+ entry_data = entry->data;
+ /* set data_t->data to NULL, otherwise data_unref() tries to free it */
+ entry_data->data = NULL;
+ data_unref(entry_data);
+
+ GF_FREE(entry);
+out:
+ return;
+}
+
+/**
+ * auth_cache_entry_init -- Initialize an auth cache entry
+ *
+ * @return: Pointer to an allocated auth cache entry, NULL if allocation
+ * failed.
+ */
+static struct auth_cache_entry *
+auth_cache_entry_init()
+{
+ struct auth_cache_entry *entry = NULL;
+
+ entry = GF_CALLOC(1, sizeof(*entry), gf_nfs_mt_auth_cache_entry);
+ if (!entry)
+ gf_msg(GF_NFS, GF_LOG_WARNING, ENOMEM, NFS_MSG_NO_MEMORY,
+ "failed to allocate entry");
+ else
+ GF_REF_INIT(entry, auth_cache_entry_free);
+
+ return entry;
+}
+
+/**
+ * auth_cache_add -- Add an auth_cache_entry to the cache->dict
+ *
+ * @return: 0 on success, non-zero otherwise.
+ */
+static int
+auth_cache_add(struct auth_cache *cache, char *hashkey,
+ struct auth_cache_entry *entry)
+{
+ int ret = -1;
+ data_t *entry_data = NULL;
+ int hashkey_len;
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache->cache_dict, out);
+
+ /* FIXME: entry is passed as parameter, this can never fail? */
+ entry = GF_REF_GET(entry);
+ if (!entry) {
+ /* entry does not have any references */
+ ret = -1;
+ goto out;
+ }
+
+ entry_data = bin_to_data(entry, sizeof(*entry));
+ if (!entry_data) {
+ ret = -1;
+ GF_REF_PUT(entry);
+ goto out;
+ }
+
+ /* we'll take an extra ref on the data_t, it gets unref'd when the
+ * auth_cache_entry is released */
+ entry->data = data_ref(entry_data);
+
+ hashkey_len = strlen(hashkey);
+ LOCK(&cache->lock);
+ {
+ ret = dict_setn(cache->cache_dict, hashkey, hashkey_len, entry_data);
+ }
+ UNLOCK(&cache->lock);
+
+ if (ret) {
+ /* adding to dict failed */
+ GF_REF_PUT(entry);
+ }
+out:
+ return ret;
+}
+
+/**
+ * _auth_cache_expired -- Check if the auth_cache_entry has expired
+ *
+ * The auth_cache->lock should have been taken when this function is called.
+ *
+ * @return: true when the auth_cache_entry is expired, false otherwise.
+ */
+static int
+_auth_cache_expired(struct auth_cache *cache, struct auth_cache_entry *entry)
+{
+ return ((gf_time() - entry->timestamp) > cache->ttl_sec);
+}
+
+/**
+ * auth_cache_get -- Get the @hashkey entry from the cache->cache_dict
+ *
+ * @cache: The auth_cache that should contain the @entry.
+ * @haskkey: The key associated with the auth_cache_entry.
+ * @entry: The found auth_cache_entry, unmodified if not found/expired.
+ *
+ * The using the cache->dict requires locking, this function takes care of
+ * that. When the entry is found, but has expired, it will be removed from the
+ * cache_dict.
+ *
+ * @return: 0 when found, ENTRY_NOT_FOUND or ENTRY_EXPIRED otherwise.
+ */
+static enum auth_cache_lookup_results
+auth_cache_get(struct auth_cache *cache, char *hashkey,
+ struct auth_cache_entry **entry)
+{
+ enum auth_cache_lookup_results ret = ENTRY_NOT_FOUND;
+ data_t *entry_data = NULL;
+ struct auth_cache_entry *lookup_res = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache->cache_dict, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, hashkey, out);
+
+ LOCK(&cache->lock);
+ {
+ entry_data = dict_get(cache->cache_dict, hashkey);
+ if (!entry_data)
+ goto unlock;
+
+ /* FIXME: this is dangerous use of entry_data */
+ lookup_res = GF_REF_GET((struct auth_cache_entry *)entry_data->data);
+ if (lookup_res == NULL) {
+ /* entry has been free'd */
+ ret = ENTRY_EXPIRED;
+ goto unlock;
+ }
+
+ if (_auth_cache_expired(cache, lookup_res)) {
+ ret = ENTRY_EXPIRED;
+ GF_REF_PUT(lookup_res->item);
+ lookup_res->item = NULL;
+
+ /* free entry and remove from the cache */
+ GF_FREE(lookup_res);
+ entry_data->data = NULL;
+ dict_del(cache->cache_dict, hashkey);
+
+ goto unlock;
+ }
+
+ *entry = lookup_res;
+ ret = ENTRY_FOUND;
+ }
+unlock:
+ UNLOCK(&cache->lock);
+
+out:
+ return ret;
+}
+
+/**
+ * auth_cache_lookup -- Lookup an item from the cache
+ *
+ * @cache: cache to lookup from
+ * @fh : FH to use in lookup
+ * @host_addr: Address to use in lookup
+ * @timestamp: The timestamp to set when lookup succeeds
+ * @can_write: Is the host authorized to write to the filehandle?
+ *
+ * If the current time - entry time of the cache entry > ttl_sec,
+ * we remove the element from the dict and return ENTRY_EXPIRED.
+ *
+ * @return: ENTRY_EXPIRED if entry expired
+ * ENTRY_NOT_FOUND if entry not found in dict
+ * 0 if found
+ */
+enum auth_cache_lookup_results
+auth_cache_lookup(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr, time_t *timestamp,
+ gf_boolean_t *can_write)
+{
+ char *hashkey = NULL;
+ struct auth_cache_entry *lookup_res = NULL;
+ enum auth_cache_lookup_results ret = ENTRY_NOT_FOUND;
+
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, fh, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, host_addr, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, timestamp, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, can_write, out);
+
+ hashkey = make_hashkey(fh, host_addr);
+ if (!hashkey) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = auth_cache_get(cache, hashkey, &lookup_res);
+ switch (ret) {
+ case ENTRY_FOUND:
+ *timestamp = lookup_res->timestamp;
+ *can_write = lookup_res->item->opts->rw;
+ GF_REF_PUT(lookup_res);
+ break;
+
+ case ENTRY_NOT_FOUND:
+ gf_msg_debug(GF_NFS, 0, "could not find entry for %s", host_addr);
+ break;
+
+ case ENTRY_EXPIRED:
+ gf_msg_debug(GF_NFS, 0, "entry for host %s has expired", host_addr);
+ break;
+ }
+
+out:
+ GF_FREE(hashkey);
+
+ return ret;
+}
+
+/* auth_cache_entry_purge -- free up the auth_cache_entry
+ *
+ * This gets called through dict_foreach() by auth_cache_purge(). Each
+ * auth_cache_entry has a refcount which needs to be decremented. Once the
+ * auth_cache_entry reaches refcount == 0, auth_cache_entry_free() will call
+ * data_unref() to free the associated data_t.
+ *
+ * @d: dict that gets purged by auth_cache_purge()
+ * @k: hashkey of the current entry
+ * @v: data_t of the current entry
+ */
+int
+auth_cache_entry_purge(dict_t *d, char *k, data_t *v, void *_unused)
+{
+ struct auth_cache_entry *entry = (struct auth_cache_entry *)v->data;
+
+ if (entry)
+ GF_REF_PUT(entry);
+
+ return 0;
+}
+
+/**
+ * auth_cache_purge -- Purge the dict in the cache and create a new empty one.
+ *
+ * @cache: Cache to purge
+ *
+ */
+void
+auth_cache_purge(struct auth_cache *cache)
+{
+ dict_t *new_cache_dict = dict_new();
+ dict_t *old_cache_dict = NULL;
+
+ if (!cache || !new_cache_dict)
+ goto out;
+
+ LOCK(&cache->lock);
+ {
+ old_cache_dict = cache->cache_dict;
+ cache->cache_dict = new_cache_dict;
+ }
+ UNLOCK(&cache->lock);
+
+ /* walk all entries and refcount-- with GF_REF_PUT() */
+ dict_foreach(old_cache_dict, auth_cache_entry_purge, NULL);
+ dict_unref(old_cache_dict);
+out:
+ return;
+}
+
+/**
+ * is_nfs_fh_cached_and_writeable -- Checks if an NFS FH is cached for the given
+ * host
+ * @cache: The fh cache
+ * @host_addr: Address to use in lookup
+ * @fh: The fh to use in lookup
+ *
+ *
+ * @return: TRUE if cached, FALSE otherwise
+ *
+ */
+gf_boolean_t
+is_nfs_fh_cached(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr)
+{
+ int ret = 0;
+ time_t timestamp = 0;
+ gf_boolean_t cached = _gf_false;
+ gf_boolean_t can_write = _gf_false;
+
+ if (!fh)
+ goto out;
+
+ ret = auth_cache_lookup(cache, fh, host_addr, &timestamp, &can_write);
+ cached = (ret == ENTRY_FOUND);
+
+out:
+ return cached;
+}
+
+/**
+ * is_nfs_fh_cached_and_writeable -- Checks if an NFS FH is cached for the given
+ * host and writable
+ * @cache: The fh cache
+ * @host_addr: Address to use in lookup
+ * @fh: The fh to use in lookup
+ *
+ *
+ * @return: TRUE if cached & writable, FALSE otherwise
+ *
+ */
+gf_boolean_t
+is_nfs_fh_cached_and_writeable(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr)
+{
+ int ret = 0;
+ time_t timestamp = 0;
+ gf_boolean_t cached = _gf_false;
+ gf_boolean_t writable = _gf_false;
+
+ if (!fh)
+ goto out;
+
+ ret = auth_cache_lookup(cache, fh, host_addr, &timestamp, &writable);
+ cached = ((ret == ENTRY_FOUND) && writable);
+
+out:
+ return cached;
+}
+
+/**
+ * cache_nfs_fh -- Places the nfs file handle in the underlying dict as we are
+ * using as our cache. The key is "exportid:gfid:host_addr", the
+ * value is an entry struct containing the export item that
+ * was authorized for the operation and the file handle that was
+ * authorized.
+ *
+ * @cache: The cache to place fh's in
+ * @fh : The fh to cache
+ * @host_addr: The address of the host
+ * @export_item: The export item that was authorized
+ *
+ */
+int
+cache_nfs_fh(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr, struct export_item *export_item)
+{
+ int ret = -EINVAL;
+ char *hashkey = NULL;
+ time_t timestamp = 0;
+ gf_boolean_t can_write = _gf_false;
+ struct auth_cache_entry *entry = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NFS, host_addr, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, cache, out);
+ GF_VALIDATE_OR_GOTO(GF_NFS, fh, out);
+
+ /* If we could already find it in the cache, just return */
+ ret = auth_cache_lookup(cache, fh, host_addr, &timestamp, &can_write);
+ if (ret == 0) {
+ gf_msg_trace(GF_NFS, 0,
+ "found cached auth/fh for host "
+ "%s",
+ host_addr);
+ goto out;
+ }
+
+ hashkey = make_hashkey(fh, host_addr);
+ if (!hashkey) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ entry = auth_cache_entry_init();
+ if (!entry) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ entry->timestamp = gf_time();
+ /* Update entry->item if it is pointing to a different export_item */
+ if (entry->item && entry->item != export_item) {
+ GF_REF_PUT(entry->item);
+ }
+ entry->item = GF_REF_GET(export_item);
+
+ ret = auth_cache_add(cache, hashkey, entry);
+ GF_REF_PUT(entry);
+ if (ret)
+ goto out;
+
+ gf_msg_trace(GF_NFS, 0, "Caching file-handle (%s)", host_addr);
+ ret = 0;
+
+out:
+ GF_FREE(hashkey);
+
+ return ret;
+}
diff --git a/xlators/nfs/server/src/auth-cache.h b/xlators/nfs/server/src/auth-cache.h
new file mode 100644
index 00000000000..4c9d09207f8
--- /dev/null
+++ b/xlators/nfs/server/src/auth-cache.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _AUTH_CACHE_H_
+#define _AUTH_CACHE_H_
+
+#include "nfs-mem-types.h"
+#include "exports.h"
+#include <glusterfs/dict.h>
+#include "nfs3.h"
+
+struct auth_cache {
+ gf_lock_t lock; /* locking for the dict (and entries) */
+ dict_t *cache_dict; /* Dict holding fh -> authcache_entry */
+ time_t ttl_sec; /* TTL of the auth cache in seconds */
+};
+
+/* Initializes the cache */
+struct auth_cache *
+auth_cache_init(time_t ttl_sec);
+
+/* Inserts FH into cache */
+int
+cache_nfs_fh(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr, struct export_item *export_item);
+
+/* Checks if the filehandle cached & writable */
+gf_boolean_t
+is_nfs_fh_cached_and_writeable(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr);
+
+/* Checks if the filehandle is cached */
+gf_boolean_t
+is_nfs_fh_cached(struct auth_cache *cache, struct nfs3_fh *fh,
+ const char *host_addr);
+
+/* Purge the cache */
+void
+auth_cache_purge(struct auth_cache *cache);
+
+#endif /* _AUTH_CACHE_H_ */
diff --git a/xlators/nfs/server/src/exports.c b/xlators/nfs/server/src/exports.c
new file mode 100644
index 00000000000..d7e39934851
--- /dev/null
+++ b/xlators/nfs/server/src/exports.c
@@ -0,0 +1,1484 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include "exports.h"
+#include <glusterfs/hashfn.h>
+#include <glusterfs/parse-utils.h>
+#include "nfs-messages.h"
+
+static void
+_exp_dict_destroy(dict_t *ng_dict);
+static void
+_export_options_print(const struct export_options *opts);
+static void
+_export_options_deinit(struct export_options *opts);
+static void
+_export_dir_deinit(struct export_dir *dir);
+
+static struct parser *netgroup_parser;
+static struct parser *hostname_parser;
+static struct parser *options_parser;
+
+/**
+ * _exp_init_parsers -- Initialize parsers to be used in this file
+ *
+ * @return: success: 0
+ * failure: -1
+ */
+static int
+_exp_init_parsers()
+{
+ int ret = -1;
+
+ netgroup_parser = parser_init(NETGROUP_REGEX_PATTERN);
+ if (!netgroup_parser)
+ goto out;
+
+ hostname_parser = parser_init(HOSTNAME_REGEX_PATTERN);
+ if (!hostname_parser)
+ goto out;
+
+ options_parser = parser_init(OPTIONS_REGEX_PATTERN);
+ if (!options_parser)
+ goto out;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * _exp_deinit_parsers -- Free parsers used in this file
+ */
+static void
+_exp_deinit_parsers()
+{
+ parser_deinit(netgroup_parser);
+ parser_deinit(hostname_parser);
+ parser_deinit(options_parser);
+}
+
+/**
+ * _export_file_init -- Initialize an exports file structure.
+ *
+ * @return : success: Pointer to an allocated exports file struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+struct exports_file *
+_exports_file_init()
+{
+ struct exports_file *file = NULL;
+
+ file = GF_CALLOC(1, sizeof(*file), gf_common_mt_nfs_exports);
+ if (!file) {
+ gf_msg(GF_EXP, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate file struct!");
+ goto out;
+ }
+
+ file->exports_dict = dict_new();
+ file->exports_map = dict_new();
+ if (!file->exports_dict || !file->exports_map) {
+ gf_msg(GF_EXP, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate dict!");
+ goto free_and_out;
+ }
+
+ goto out;
+
+free_and_out:
+ if (file->exports_dict)
+ dict_unref(file->exports_dict);
+
+ GF_FREE(file);
+ file = NULL;
+out:
+ return file;
+}
+
+/**
+ * _exp_file_dict_destroy -- Delete each item in the dict
+ *
+ * @dict : Dict to free elements from
+ * @key : Key in the dict we are on
+ * @val : Value associated with that dict
+ * @tmp : Not used
+ *
+ * Not for external use.
+ */
+static int
+_exp_file_dict_destroy(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct export_dir *dir = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, dict, out);
+
+ if (val) {
+ dir = (struct export_dir *)val->data;
+
+ if (dir) {
+ _export_dir_deinit(dir);
+ val->data = NULL;
+ }
+ dict_del(dict, key);
+ }
+
+out:
+ return 0;
+}
+
+/**
+ * _exp_file_deinit -- Free memory used by an export file
+ *
+ * @expfile : Pointer to the exports file to free
+ *
+ * Externally usable.
+ */
+void
+exp_file_deinit(struct exports_file *expfile)
+{
+ if (!expfile)
+ goto out;
+
+ if (expfile->exports_dict) {
+ dict_foreach(expfile->exports_dict, _exp_file_dict_destroy, NULL);
+ dict_unref(expfile->exports_dict);
+ }
+
+ if (expfile->exports_map) {
+ dict_foreach(expfile->exports_map, _exp_file_dict_destroy, NULL);
+ dict_unref(expfile->exports_map);
+ }
+
+ GF_FREE(expfile->filename);
+ GF_FREE(expfile);
+out:
+ return;
+}
+
+/**
+ * _export_dir_init -- Initialize an export directory structure.
+ *
+ * @return : success: Pointer to an allocated exports directory struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct export_dir *
+_export_dir_init()
+{
+ struct export_dir *expdir = GF_CALLOC(1, sizeof(*expdir),
+ gf_common_mt_nfs_exports);
+
+ if (!expdir)
+ gf_msg(GF_EXP, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate export dir structure!");
+
+ return expdir;
+}
+
+/**
+ * _export_dir_deinit -- Free memory used by an export dir
+ *
+ * @expdir : Pointer to the export directory to free
+ *
+ * Not for external use.
+ */
+static void
+_export_dir_deinit(struct export_dir *dir)
+{
+ GF_VALIDATE_OR_GOTO(GF_EXP, dir, out);
+ GF_FREE(dir->dir_name);
+ _exp_dict_destroy(dir->netgroups);
+ dict_unref(dir->netgroups);
+ _exp_dict_destroy(dir->hosts);
+ dict_unref(dir->hosts);
+ GF_FREE(dir);
+
+out:
+ return;
+}
+
+/**
+ * _export_item_print -- Print the elements in the export item.
+ *
+ * @expdir : Pointer to the item struct to print out.
+ *
+ * Not for external use.
+ */
+static void
+_export_item_print(const struct export_item *item)
+{
+ GF_VALIDATE_OR_GOTO(GF_EXP, item, out);
+ printf("%s", item->name);
+ _export_options_print(item->opts);
+out:
+ return;
+}
+
+/**
+ * _export_item_deinit -- Free memory used by an export item
+ *
+ * @expdir : Pointer to the export item to free
+ *
+ * Not for external use.
+ */
+static void
+_export_item_deinit(struct export_item *item)
+{
+ if (!item)
+ return;
+
+ _export_options_deinit(item->opts);
+ GF_FREE(item->name);
+ GF_FREE(item);
+}
+
+/**
+ * _export_item_init -- Initialize an export item structure
+ *
+ * @return : success: Pointer to an allocated exports item struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct export_item *
+_export_item_init()
+{
+ struct export_item *item = GF_CALLOC(1, sizeof(*item),
+ gf_common_mt_nfs_exports);
+
+ if (item) {
+ GF_REF_INIT(item, _export_item_deinit);
+ } else {
+ gf_msg(GF_EXP, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate export item!");
+ }
+
+ return item;
+}
+
+/**
+ * _export_host_init -- Initialize an export options struct
+ *
+ * @return : success: Pointer to an allocated options struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct export_options *
+_export_options_init()
+{
+ struct export_options *opts = GF_CALLOC(1, sizeof(*opts),
+ gf_common_mt_nfs_exports);
+
+ if (!opts)
+ gf_msg(GF_EXP, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate options structure!");
+
+ return opts;
+}
+
+/**
+ * _export_options_deinit -- Free memory used by a options struct
+ *
+ * @expdir : Pointer to the options struct to free
+ *
+ * Not for external use.
+ */
+static void
+_export_options_deinit(struct export_options *opts)
+{
+ if (!opts)
+ return;
+
+ GF_FREE(opts->anon_uid);
+ GF_FREE(opts->sec_type);
+ GF_FREE(opts);
+}
+
+/**
+ * _export_options_print -- Print the elements in the options struct.
+ *
+ * @expdir : Pointer to the options struct to print out.
+ *
+ * Not for external use.
+ */
+static void
+_export_options_print(const struct export_options *opts)
+{
+ GF_VALIDATE_OR_GOTO(GF_EXP, opts, out);
+
+ printf("(");
+ if (opts->rw)
+ printf("rw,");
+ else
+ printf("ro,");
+
+ if (opts->nosuid)
+ printf("nosuid,");
+
+ if (opts->root)
+ printf("root,");
+
+ if (opts->anon_uid)
+ printf("anonuid=%s,", opts->anon_uid);
+
+ if (opts->sec_type)
+ printf("sec=%s,", opts->sec_type);
+
+ printf(") ");
+out:
+ return;
+}
+
+/**
+ * __exp_dict_free_walk -- Delete each item in the dict
+ *
+ * @dict : Dict to free elements from
+ * @key : Key in the dict we are on
+ * @val : Value associated with that dict
+ * @tmp : Not used
+ *
+ * Passed as a function pointer to dict_foreach()
+ *
+ * Not for external use.
+ */
+static int
+__exp_dict_free_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val) {
+ GF_REF_PUT((struct export_item *)val->data);
+ val->data = NULL;
+ dict_del(dict, key);
+ }
+ return 0;
+}
+
+/**
+ * _exp_dict_destroy -- Delete all the items from this dict
+ * through the helper function above.
+ *
+ * @ng_dict : Dict to free
+ *
+ * Not for external use.
+ */
+static void
+_exp_dict_destroy(dict_t *ng_dict)
+{
+ if (!ng_dict)
+ goto out;
+
+ dict_foreach(ng_dict, __exp_dict_free_walk, NULL);
+out:
+ return;
+}
+
+/**
+ * exp_file_dir_from_uuid -- Using a uuid as the key, retrieve an exports
+ * directory from the file.
+ *
+ * @file: File to retrieve data from
+ * @export_uuid: UUID of the export (mountid in the NFS xlator)
+ *
+ * @return : success: Pointer to an export dir struct
+ * failure: NULL
+ */
+struct export_dir *
+exp_file_dir_from_uuid(const struct exports_file *file,
+ const uuid_t export_uuid)
+{
+ char export_uuid_str[512] = {
+ 0,
+ };
+ data_t *dirdata = NULL;
+ struct export_dir *dir = NULL;
+
+ gf_uuid_unparse(export_uuid, export_uuid_str);
+
+ dirdata = dict_get(file->exports_map, export_uuid_str);
+ if (dirdata)
+ dir = (struct export_dir *)dirdata->data;
+
+ return dir;
+}
+
+/**
+ * _exp_file_insert -- Insert the exports directory into the file structure
+ * using the directory as a dict. Also hashes the dirname,
+ * stores it in a uuid type, converts the uuid type to a
+ * string and uses that as the key to the exports map.
+ * The exports map maps an export "uuid" to an export
+ * directory struct.
+ *
+ * @file : Exports file struct to insert into
+ * @dir : Export directory to insert
+ *
+ * Not for external use.
+ */
+static void
+_exp_file_insert(struct exports_file *file, struct export_dir *dir)
+{
+ data_t *dirdata = NULL;
+ uint32_t hashedval = 0;
+ uuid_t export_uuid = {
+ 0,
+ };
+ char export_uuid_str[512] = {
+ 0,
+ };
+ char *dirdup = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, file, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, dir, out);
+
+ dirdata = bin_to_data(dir, sizeof(*dir));
+ dict_set(file->exports_dict, dir->dir_name, dirdata);
+
+ dirdup = strdupa(dir->dir_name);
+ while (strlen(dirdup) > 0 && dirdup[0] == '/')
+ dirdup++;
+
+ hashedval = SuperFastHash(dirdup, strlen(dirdup));
+ memset(export_uuid, 0, sizeof(export_uuid));
+ memcpy(export_uuid, &hashedval, sizeof(hashedval));
+ gf_uuid_unparse(export_uuid, export_uuid_str);
+
+ dict_set(file->exports_map, export_uuid_str, dirdata);
+out:
+ return;
+}
+
+/**
+ * __exp_item_print_walk -- Print all the keys and values in the dict
+ *
+ * @dict : the dict to walk
+ * @key : the key in the dict we are currently on
+ * @val : the value in the dict associated with the key
+ * @tmp : Additional parameter data (not used)
+ *
+ * Passed as a function pointer to dict_foreach ().
+ *
+ * Not for external use.
+ */
+static int
+__exp_item_print_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val)
+ _export_item_print((struct export_item *)val->data);
+
+ return 0;
+}
+
+/**
+ * __exp_file_print_walk -- Print all the keys and values in the dict
+ *
+ * @dict : the dict to walk
+ * @key : the key in the dict we are currently on
+ * @val : the value in the dict associated with the key
+ * @tmp : Additional parameter data (not used)
+ *
+ * Passed as a function pointer to dict_foreach ().
+ *
+ * Not for external use.
+ */
+static int
+__exp_file_print_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val) {
+ struct export_dir *dir = (struct export_dir *)val->data;
+
+ printf("%s ", key);
+
+ if (dir->netgroups)
+ dict_foreach(dir->netgroups, __exp_item_print_walk, NULL);
+
+ if (dir->hosts)
+ dict_foreach(dir->hosts, __exp_item_print_walk, NULL);
+
+ printf("\n");
+ }
+ return 0;
+}
+
+/**
+ * exp_file_print -- Print out the contents of the exports file
+ *
+ * @file : Exports file to print
+ *
+ * Not for external use.
+ */
+void
+exp_file_print(const struct exports_file *file)
+{
+ GF_VALIDATE_OR_GOTO(GF_EXP, file, out);
+ dict_foreach(file->exports_dict, __exp_file_print_walk, NULL);
+out:
+ return;
+}
+
+#define __exp_line_get_opt_val(val, equals, ret, errlabel) \
+ do { \
+ (val) = (equals) + 1; \
+ if (!(*(val))) { \
+ (ret) = 1; \
+ goto errlabel; \
+ } \
+ } while (0)
+
+enum gf_exp_parse_status {
+ GF_EXP_PARSE_SUCCESS = 0,
+ GF_EXP_PARSE_ITEM_NOT_FOUND = 1,
+ GF_EXP_PARSE_ITEM_FAILURE = 2,
+ GF_EXP_PARSE_ITEM_NOT_IN_MOUNT_STATE = 3,
+ GF_EXP_PARSE_LINE_IGNORING = 4,
+};
+
+/**
+ * __exp_line_opt_key_value_parse -- Parse the key-value options in the options
+ * string.
+ *
+ * Given a string like (sec=sys,anonuid=0,rw), to parse, this function
+ * will get called once with 'sec=sys' and again with 'anonuid=0'.
+ * It will check for the '=', make sure there is data to be read
+ * after the '=' and copy the data into the options struct.
+ *
+ * @option : An option string like sec=sys or anonuid=0
+ * @opts : Pointer to an struct export_options that holds all the export
+ * options.
+ *
+ * @return: success: GF_EXP_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * -EINVAL on bad args, -ENOMEM on allocation errors.
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_opt_key_value_parse(char *option, struct export_options *opts)
+{
+ char *equals = NULL;
+ char *right = NULL;
+ char *strmatch = option;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, option, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, opts, out);
+
+ equals = strchr(option, '=');
+ if (!equals) {
+ ret = GF_EXP_PARSE_ITEM_FAILURE;
+ goto out;
+ }
+
+ *equals = 0;
+ /* Now that an '=' has been found the left side is the option and
+ * the right side is the value. We simply have to compare those and
+ * extract it.
+ */
+ if (strcmp(strmatch, "anonuid") == 0) {
+ *equals = '=';
+ /* Get the value for this option */
+ __exp_line_get_opt_val(right, equals, ret, out);
+ opts->anon_uid = gf_strdup(right);
+ GF_CHECK_ALLOC(opts->anon_uid, ret, out);
+ } else if (strcmp(strmatch, "sec") == 0) {
+ *equals = '=';
+ /* Get the value for this option */
+ __exp_line_get_opt_val(right, equals, ret, out);
+ opts->sec_type = gf_strdup(right);
+ GF_CHECK_ALLOC(opts->sec_type, ret, out);
+ } else {
+ *equals = '=';
+ ret = GF_EXP_PARSE_ITEM_FAILURE;
+ goto out;
+ }
+
+ ret = GF_EXP_PARSE_SUCCESS;
+out:
+ return ret;
+}
+
+/**
+ * __exp_line_opt_parse -- Parse the options part of an
+ * exports or netgroups string.
+ *
+ * @opt_str : The option string to parse
+ * @exp_opts : Double pointer to the options we are going
+ * to allocate and setup.
+ *
+ *
+ * @return: success: GF_EXP_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * -EINVAL on bad args, -ENOMEM on allocation errors.
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_opt_parse(const char *opt_str, struct export_options **exp_opts)
+{
+ struct export_options *opts = NULL;
+ char *strmatch = NULL;
+ int ret = -EINVAL;
+ char *equals = NULL;
+
+ ret = parser_set_string(options_parser, opt_str);
+ if (ret < 0)
+ goto out;
+
+ while ((strmatch = parser_get_next_match(options_parser))) {
+ if (!opts) {
+ /* If the options have not been allocated,
+ * allocate it.
+ */
+ opts = _export_options_init();
+ if (!opts) {
+ ret = -ENOMEM;
+ parser_unset_string(options_parser);
+ GF_FREE(strmatch);
+ goto out;
+ }
+ }
+
+ /* First, check for all the boolean options Second, check for
+ * an '=', and check the available options there. The string
+ * parsing here gets slightly messy, but the concept itself
+ * is pretty simple.
+ */
+ equals = strchr(strmatch, '=');
+ if (strcmp(strmatch, "root") == 0)
+ opts->root = _gf_true;
+ else if (strcmp(strmatch, "ro") == 0)
+ opts->rw = _gf_false;
+ else if (strcmp(strmatch, "rw") == 0)
+ opts->rw = _gf_true;
+ else if (strcmp(strmatch, "nosuid") == 0)
+ opts->nosuid = _gf_true;
+ else if (equals) {
+ ret = __exp_line_opt_key_value_parse(strmatch, opts);
+ if (ret < 0) {
+ /* This means invalid key value options were
+ * specified, or memory allocation failed.
+ * The ret value gets bubbled up to the caller.
+ */
+ GF_FREE(strmatch);
+ parser_unset_string(options_parser);
+ _export_options_deinit(opts);
+ goto out;
+ }
+ } else {
+ /* Cannot change to gf_msg.
+ * gf_msg not giving output to STDOUT
+ * Bug id : BZ1215017
+ */
+ gf_log(GF_EXP, GF_LOG_WARNING,
+ "Could not find any valid options for "
+ "string: %s",
+ strmatch);
+ }
+ GF_FREE(strmatch);
+ }
+
+ if (!opts) {
+ /* If opts is not allocated
+ * that means no matches were found
+ * which is a parse error. Not marking
+ * it as "not found" because it is a parse
+ * error to not have options.
+ */
+ ret = GF_EXP_PARSE_ITEM_FAILURE;
+ parser_unset_string(options_parser);
+ goto out;
+ }
+
+ *exp_opts = opts;
+ parser_unset_string(options_parser);
+ ret = GF_EXP_PARSE_SUCCESS;
+out:
+ return ret;
+}
+
+/**
+ * __exp_line_ng_host_str_parse -- Parse the netgroup or host string
+ *
+ * e.g. @mygroup(<options>), parsing @mygroup and (<options>)
+ * or myhost001.dom(<options>), parsing myhost001.dom and (<options>)
+ *
+ * @line : The line to parse
+ * @exp_item : Double pointer to a struct export_item
+ *
+ * @return: success: GF_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * -EINVAL on bad args, -ENOMEM on allocation errors.
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_ng_host_str_parse(char *str, struct export_item **exp_item)
+{
+ struct export_item *item = NULL;
+ int ret = -EINVAL;
+ char *parens = NULL;
+ char *optstr = NULL;
+ struct export_options *exp_opts = NULL;
+ char *item_name = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, str, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, exp_item, out);
+
+ /* A netgroup/host string looks like this:
+ * @test(sec=sys,rw,anonuid=0) or host(sec=sys,rw,anonuid=0)
+ * We want to extract the name, 'test' or 'host'
+ * Again, we could setup a regex and use it here,
+ * but its simpler to find the '(' and copy until
+ * there.
+ */
+ parens = strchr(str, '(');
+ if (!parens) {
+ /* Parse error if there are no parens. */
+ ret = GF_EXP_PARSE_ITEM_FAILURE;
+ goto out;
+ }
+
+ *parens = '\0'; /* Temporarily terminate it so we can do a copy */
+
+ if (strlen(str) > FQDN_MAX_LEN) {
+ ret = GF_EXP_PARSE_ITEM_FAILURE;
+ goto out;
+ }
+
+ /* Strip leading whitespaces */
+ while (*str == ' ' || *str == '\t')
+ str++;
+
+ item_name = gf_strdup(str);
+ GF_CHECK_ALLOC(item_name, ret, out);
+
+ gf_msg_trace(GF_EXP, 0, "found hostname/netgroup: %s", item_name);
+
+ /* Initialize an export item for this */
+ item = _export_item_init();
+ GF_CHECK_ALLOC(item, ret, free_and_out);
+ item->name = item_name;
+
+ *parens = '('; /* Restore the string */
+
+ /* Options start at the parentheses */
+ optstr = parens;
+
+ ret = __exp_line_opt_parse(optstr, &exp_opts);
+ if (ret != 0) {
+ /* Bubble up the error to the caller */
+ GF_REF_PUT(item);
+ goto out;
+ }
+
+ item->opts = exp_opts;
+
+ *exp_item = item;
+
+ ret = GF_EXP_PARSE_SUCCESS;
+ goto out;
+
+free_and_out:
+ GF_FREE(item_name);
+out:
+ return ret;
+}
+
+/**
+ * __exp_line_ng_parse -- Extract the netgroups in the line
+ * and call helper functions to parse
+ * the string.
+ *
+ * The call chain goes like this:
+ *
+ * 1) __exp_line_ng_parse ("/test @test(sec=sys,rw,anonuid=0)")
+ * 2) __exp_line_ng_str_parse ("@test(sec=sys,rw,anonuid=0)");
+ * 3) __exp_line_opt_parse("(sec=sys,rw,anonuid=0)");
+ *
+ *
+ * @line : The line to parse
+ * @ng_dict : Double pointer to the dict we want to
+ * insert netgroups into.
+ *
+ * Allocates the dict, extracts netgroup strings from the line,
+ * parses them into a struct export_item structure and inserts
+ * them in the dict.
+ *
+ * @return: success: GF_EXP_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * GF_EXP_PARSE_ITEM_NOT_FOUND if the netgroup was not found
+ * -EINVAL on bad args, -ENOMEM on allocation errors.
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_ng_parse(const char *line, dict_t **ng_dict)
+{
+ dict_t *netgroups = NULL;
+ char *strmatch = NULL;
+ int ret = -EINVAL;
+ struct export_item *exp_ng = NULL;
+ data_t *ngdata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, line, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, ng_dict, out);
+
+ *ng_dict = NULL; /* Will be set if parse is successful */
+
+ /* Initialize a parser with the line to parse
+ * and the regex used to parse it.
+ */
+ ret = parser_set_string(netgroup_parser, line);
+ if (ret < 0) {
+ goto out;
+ }
+
+ gf_msg_trace(GF_EXP, 0, "parsing line: %s", line);
+
+ while ((strmatch = parser_get_next_match(netgroup_parser))) {
+ if (!netgroups) {
+ /* Allocate a new dict to store the netgroups. */
+ netgroups = dict_new();
+ if (!netgroups) {
+ ret = -ENOMEM;
+ goto free_and_out;
+ }
+ }
+
+ gf_msg_trace(GF_EXP, 0, "parsing netgroup: %s", strmatch);
+
+ ret = __exp_line_ng_host_str_parse(strmatch, &exp_ng);
+
+ if (ret != 0) {
+ /* Parsing or other critical errors.
+ * caller will handle return value.
+ */
+ _exp_dict_destroy(netgroups);
+ goto free_and_out;
+ }
+
+ ngdata = bin_to_data(exp_ng, sizeof(*exp_ng));
+ dict_set(netgroups, exp_ng->name, ngdata);
+
+ /* Free this matched string and continue parsing. */
+ GF_FREE(strmatch);
+ }
+
+ /* If the netgroups dict was not allocated, then we know that
+ * no matches were found.
+ */
+ if (!netgroups) {
+ ret = GF_EXP_PARSE_ITEM_NOT_FOUND;
+ parser_unset_string(netgroup_parser);
+ goto out;
+ }
+
+ ret = GF_EXP_PARSE_SUCCESS;
+ *ng_dict = netgroups;
+
+free_and_out:
+ parser_unset_string(netgroup_parser);
+ GF_FREE(strmatch);
+out:
+ return ret;
+}
+
+/**
+ * __exp_line_host_parse -- Extract the hosts in the line
+ * and call helper functions to parse
+ * the string.
+ *
+ * The call chain goes like this:
+ *
+ * 1) __exp_line_host_parse ("/test hostip(sec=sys,rw,anonuid=0)")
+ * 2) __exp_line_ng_host_str_parse ("hostip(sec=sys,rw,anonuid=0)");
+ * 3) __exp_line_opt_parse("(sec=sys,rw,anonuid=0)");
+ *
+ *
+ * @line : The line to parse
+ * @ng_dict : Double pointer to the dict we want to
+ * insert hosts into.
+ *
+ * Allocates the dict, extracts host strings from the line,
+ * parses them into a struct export_item structure and inserts
+ * them in the dict.
+ *
+ * @return: success: GF_EXP_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * GF_EXP_PARSE_ITEM_NOT_FOUND if the host was not found,
+ * -EINVAL on bad args, -ENOMEM on allocation errors.
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_host_parse(const char *line, dict_t **host_dict)
+{
+ dict_t *hosts = NULL;
+ char *strmatch = NULL;
+ int ret = -EINVAL;
+ struct export_item *exp_host = NULL;
+ data_t *hostdata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, line, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, host_dict, out);
+
+ *host_dict = NULL;
+
+ /* Initialize a parser with the line to parse and the regex used to
+ * parse it.
+ */
+ ret = parser_set_string(hostname_parser, line);
+ if (ret < 0) {
+ goto out;
+ }
+
+ gf_msg_trace(GF_EXP, 0, "parsing line: %s", line);
+
+ while ((strmatch = parser_get_next_match(hostname_parser))) {
+ if (!hosts) {
+ /* Allocate a new dictto store the netgroups. */
+ hosts = dict_new();
+ GF_CHECK_ALLOC(hosts, ret, free_and_out);
+ }
+
+ gf_msg_trace(GF_EXP, 0, "parsing hostname: %s", strmatch);
+
+ ret = __exp_line_ng_host_str_parse(strmatch, &exp_host);
+
+ if (ret != 0) {
+ /* Parsing or other critical error, free allocated
+ * memory and exit. The caller will handle the errors.
+ */
+ _exp_dict_destroy(hosts);
+ goto free_and_out;
+ }
+
+ /* Insert export item structure into the hosts dict. */
+ hostdata = bin_to_data(exp_host, sizeof(*exp_host));
+ dict_set(hosts, exp_host->name, hostdata);
+
+ /* Free this matched string and continue parsing.*/
+ GF_FREE(strmatch);
+ }
+
+ /* If the hosts dict was not allocated, then we know that
+ * no matches were found.
+ */
+ if (!exp_host) {
+ ret = GF_EXP_PARSE_ITEM_NOT_FOUND;
+ parser_unset_string(hostname_parser);
+ goto out;
+ }
+
+ ret = GF_EXP_PARSE_SUCCESS;
+ *host_dict = hosts;
+
+free_and_out:
+ parser_unset_string(hostname_parser);
+ GF_FREE(strmatch);
+out:
+ return ret;
+}
+
+/**
+ * __exp_line_dir_parse -- Extract directory name from a line in the exports
+ * file.
+ *
+ * @line : The line to parse
+ * @dirname : Double pointer to the string we need to hold the directory name.
+ * If the parsing failed, the string will point to NULL, otherwise
+ * it will point to a valid memory region that is allocated by
+ * this function.
+ * @check_ms: If this variable is set then we cross check the directory line
+ * with what's in gluster's vol files and reject them if they don't
+ * match.
+ *
+ * @return : success: GF_EXP_PARSE_SUCCESS
+ * failure: GF_EXP_PARSE_ITEM_FAILURE on parse failure,
+ * -EINVAL on bad arguments, -ENOMEM on allocation failures,
+ * GF_EXP_PARSE_ITEM_NOT_IN_MOUNT_STATE if we failed to match
+ * with gluster's mountstate.
+ *
+ * The caller is responsible for freeing memory allocated by this function
+ *
+ * Not for external use.
+ */
+static int
+__exp_line_dir_parse(const char *line, char **dirname, struct mount3_state *ms)
+{
+ char *dir = NULL;
+ char *delim = NULL;
+ int ret = -EINVAL;
+ char *linedup = NULL;
+ struct mnt3_export *mnt3export = NULL;
+ size_t dirlen = 0;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, line, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, dirname, out);
+
+ /* Duplicate the line because we don't
+ * want to modify the original string.
+ */
+ linedup = strdupa(line);
+
+ /* We use strtok_r () here to split the string by space/tab and get the
+ * the result. We only need the first result of the split.
+ * a simple task. It is worth noting that dirnames always have to be
+ * validated against gluster's vol files so if they don't
+ * match it will be rejected.
+ */
+ dir = linedup;
+ delim = linedup + strcspn(linedup, " \t");
+ *delim = 0;
+
+ if (ms) {
+ /* Match the directory name with an existing
+ * export in the mount state.
+ */
+ mnt3export = mnt3_mntpath_to_export(ms, dir, _gf_true);
+ if (!mnt3export) {
+ gf_msg_debug(GF_EXP, 0,
+ "%s not in mount state, "
+ "ignoring!",
+ dir);
+ ret = GF_EXP_PARSE_ITEM_NOT_IN_MOUNT_STATE;
+ goto out;
+ }
+ }
+
+ /* Directories can be 1024 bytes in length, check
+ * that the argument provided adheres to
+ * that restriction.
+ */
+ if (strlen(dir) > DIR_MAX_LEN) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Copy the result of the split */
+ dir = gf_strdup(dir);
+ GF_CHECK_ALLOC(dir, ret, out);
+
+ /* Ensure that trailing slashes are stripped before storing the name */
+ dirlen = strlen(dir);
+ if (dirlen > 0 && dir[dirlen - 1] == '/')
+ dir[dirlen - 1] = '\0';
+
+ /* Set the argument to point to the allocated string */
+ *dirname = dir;
+ ret = GF_EXP_PARSE_SUCCESS;
+out:
+ return ret;
+}
+
+/**
+ * _exp_line_parse -- Parse a line in an exports file into a structure
+ * that holds all the parts of the line. An exports
+ * structure has a dict of netgroups and a dict of hosts.
+ *
+ * An export line looks something like this /test @test(sec=sys,rw,anonuid=0)
+ * or /test @test(sec=sys,rw,anonuid=0) hostA(sec=sys,rw,anonuid=0), etc.
+ *
+ * We use regexes to parse the line into three separate pieces:
+ * 1) The directory (exports.h -- DIRECTORY_REGEX_PATTERN)
+ * 2) The netgroup if it exists (exports.h -- NETGROUP_REGEX_PATTERN)
+ * 3) The host if it exists (exports.h -- HOST_REGEX_PATTERN)
+ *
+ * In this case, the netgroup would be @test(sec=sys,rw,anonuid=0)
+ * and the host would be hostA(sec=sys,rw,anonuid=0).
+ *
+ * @line : The line to parse
+ * @dir : Double pointer to the struct we need to parse the line into.
+ * If the parsing failed, the struct will point to NULL,
+ * otherwise it will point to a valid memory region that is
+ * allocated by this function.
+ * @parse_full : This parameter tells us whether we should parse all the lines
+ * in the file, even if they are not present in gluster's config.
+ * The gluster config holds the volumes that it exports so
+ * if parse_full is set to FALSE then we will ensure that
+ * the export file structure holds only those volumes
+ * that gluster has exported. It is important to note that
+ * If gluster exports a volume named '/test', '/test' and all
+ * of its subdirectories that may be in the exports file
+ * are valid exports.
+ * @ms : The mount state that holds the list of volumes that gluster
+ * currently exports.
+ *
+ * @return : success: GF_EXP_PARSE_SUCCESS on success, -EINVAL on bad arguments,
+ * -ENOMEM on memory allocation errors,
+ * GF_EXP_PARSE_LINE_IGNORING if we ignored the line,
+ * GF_EXP_PARSE_ITEM_FAILURE if there was error parsing
+ * failure: NULL
+ *
+ * The caller is responsible for freeing memory allocated by this function
+ * The caller should free this memory using the _exp_dir_deinit () function.
+ *
+ * Not for external use.
+ */
+static int
+_exp_line_parse(const char *line, struct export_dir **dir,
+ gf_boolean_t parse_full, struct mount3_state *ms)
+{
+ struct export_dir *expdir = NULL;
+ char *dirstr = NULL;
+ dict_t *netgroups = NULL;
+ dict_t *hosts = NULL;
+ int ret = -EINVAL;
+ gf_boolean_t netgroups_failed = _gf_false;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, line, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, dir, out);
+
+ if (*line == '#' || *line == ' ' || *line == '\t' || *line == '\0' ||
+ *line == '\n') {
+ ret = GF_EXP_PARSE_LINE_IGNORING;
+ goto out;
+ }
+
+ expdir = _export_dir_init();
+ if (!expdir) {
+ *dir = NULL;
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Get the directory string from the line */
+ ret = __exp_line_dir_parse(line, &dirstr, ms);
+ if (ret < 0) {
+ gf_msg(GF_EXP, GF_LOG_ERROR, 0, NFS_MSG_PARSE_DIR_FAIL,
+ "Parsing directory failed: %s", strerror(-ret));
+ /* If parsing the directory failed,
+ * we should simply fail because there's
+ * nothing else we can extract from the string to make
+ * the data valuable.
+ */
+ goto free_and_out;
+ }
+
+ /* Set the dir str */
+ expdir->dir_name = dirstr;
+
+ /* Parse the netgroup part of the string */
+ ret = __exp_line_ng_parse(line, &netgroups);
+ if (ret < 0) {
+ gf_msg(GF_EXP, GF_LOG_ERROR, -ret, NFS_MSG_PARSE_FAIL,
+ "Critical error: %s", strerror(-ret));
+ /* Return values less than 0
+ * indicate critical failures (null parameters,
+ * failure to allocate memory, etc).
+ */
+ goto free_and_out;
+ }
+ if (ret != 0) {
+ if (ret == GF_EXP_PARSE_ITEM_FAILURE)
+ /* Cannot change to gf_msg.
+ * gf_msg not giving output to STDOUT
+ * Bug id : BZ1215017
+ */
+ gf_log(GF_EXP, GF_LOG_WARNING, "Error parsing netgroups for: %s",
+ line);
+ /* Even though parsing failed for the netgroups we should let
+ * host parsing proceed.
+ */
+ netgroups_failed = _gf_true;
+ }
+
+ /* Parse the host part of the string */
+ ret = __exp_line_host_parse(line, &hosts);
+ if (ret < 0) {
+ gf_msg(GF_EXP, GF_LOG_ERROR, -ret, NFS_MSG_PARSE_FAIL,
+ "Critical error: %s", strerror(-ret));
+ goto free_and_out;
+ }
+ if (ret != 0) {
+ if (ret == GF_EXP_PARSE_ITEM_FAILURE)
+ gf_msg(GF_EXP, GF_LOG_WARNING, 0, NFS_MSG_PARSE_FAIL,
+ "Error parsing hosts for: %s", line);
+ /* If netgroups parsing failed, AND
+ * host parsing failed, then there's something really
+ * wrong with this line, so we're just going to
+ * log it and fail out.
+ */
+ if (netgroups_failed)
+ goto free_and_out;
+ }
+
+ expdir->hosts = hosts;
+ expdir->netgroups = netgroups;
+ *dir = expdir;
+ goto out;
+
+free_and_out:
+ _export_dir_deinit(expdir);
+out:
+ return ret;
+}
+
+struct export_item *
+exp_dir_get_netgroup(const struct export_dir *expdir, const char *netgroup)
+{
+ struct export_item *lookup_res = NULL;
+ data_t *dict_res = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, expdir, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, netgroup, out);
+
+ if (!expdir->netgroups)
+ goto out;
+
+ dict_res = dict_get(expdir->netgroups, (char *)netgroup);
+ if (!dict_res) {
+ gf_msg_debug(GF_EXP, 0, "%s not found for %s", netgroup,
+ expdir->dir_name);
+ goto out;
+ }
+
+ lookup_res = (struct export_item *)dict_res->data;
+out:
+ return lookup_res;
+}
+/**
+ * exp_dir_get_host -- Given a host string and an exports directory structure,
+ * find and return an struct export_item structure that
+ * represents the requested host.
+ *
+ * @expdir: Export directory to lookup from
+ * @host : Host string to lookup
+ *
+ * @return: success: Pointer to a export item structure
+ * failure: NULL
+ */
+struct export_item *
+exp_dir_get_host(const struct export_dir *expdir, const char *host)
+{
+ struct export_item *lookup_res = NULL;
+ data_t *dict_res = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, expdir, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, host, out);
+
+ if (!expdir->hosts)
+ goto out;
+
+ dict_res = dict_get(expdir->hosts, (char *)host);
+ if (!dict_res) {
+ gf_msg_debug(GF_EXP, 0, "%s not found for %s", host, expdir->dir_name);
+
+ /* Check if wildcards are allowed for the host */
+ dict_res = dict_get(expdir->hosts, "*");
+ if (!dict_res) {
+ goto out;
+ }
+ }
+
+ lookup_res = (struct export_item *)dict_res->data;
+out:
+ return lookup_res;
+}
+
+/**
+ * exp_file_get_dir -- Return an export dir given a directory name
+ * Does a lookup from the dict in the file structure.
+ *
+ * @file : Exports file structure to lookup from
+ * @dir : Directory name to lookup
+ *
+ * @return : success: Pointer to an export directory structure
+ * failure: NULL
+ */
+struct export_dir *
+exp_file_get_dir(const struct exports_file *file, const char *dir)
+{
+ struct export_dir *lookup_res = NULL;
+ data_t *dict_res = NULL;
+ char *dirdup = NULL;
+ size_t dirlen = 0;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, file, out);
+ GF_VALIDATE_OR_GOTO(GF_EXP, dir, out);
+
+ dirlen = strlen(dir);
+ if (dirlen <= 0)
+ goto out;
+
+ dirdup = (char *)dir; /* Point at the directory */
+
+ /* If directories don't contain a leading slash */
+ if (*dir != '/') {
+ dirdup = alloca(dirlen + 2); /* Leading slash & null byte */
+ snprintf(dirdup, dirlen + 2, "/%s", dir);
+ }
+
+ dict_res = dict_get(file->exports_dict, dirdup);
+ if (!dict_res) {
+ gf_msg_debug(GF_EXP, 0, "%s not found in %s", dirdup, file->filename);
+ goto out;
+ }
+
+ lookup_res = (struct export_dir *)dict_res->data;
+out:
+ return lookup_res;
+}
+
+/**
+ * exp_file_parse -- Parse an exports file into a structure
+ * that can be looked up through simple
+ * function calls.
+ *
+ * @filepath: Path to the exports file
+ * @ms : Current mount state (useful to match with gluster vol files)
+ *
+ * @return : success: 0
+ * failure: -1 on parsing failure, -EINVAL on bad arguments,
+ * -ENOMEM on allocation failures.
+ *
+ * The caller is responsible for freeing memory allocated by this function.
+ * The caller should free this memory using the exp_file_deinit () function.
+ * Calling GF_FREE ( ) on the pointer will NOT free all the allocated memory.
+ *
+ * Externally usable.
+ */
+int
+exp_file_parse(const char *filepath, struct exports_file **expfile,
+ struct mount3_state *ms)
+{
+ FILE *fp = NULL;
+ struct exports_file *file = NULL;
+ size_t len = 0;
+ int ret = -EINVAL;
+ unsigned long line_number = 0;
+ char *line = NULL;
+ struct export_dir *expdir = NULL;
+
+ /* Sets whether we we should parse the entire file or just that which
+ * is present in the mount state */
+ gf_boolean_t parse_complete_file = _gf_false;
+
+ GF_VALIDATE_OR_GOTO(GF_EXP, expfile, parse_done);
+
+ if (!ms) {
+ /* If mount state is null that means that we
+ * should go through and parse the whole file
+ * since we don't have anything to compare against.
+ */
+ parse_complete_file = _gf_true;
+ }
+
+ fp = fopen(filepath, "r");
+ if (!fp) {
+ ret = -errno;
+ goto parse_done;
+ }
+
+ ret = _exp_init_parsers();
+ if (ret < 0)
+ goto parse_done;
+
+ /* Process the file line by line, with each line being parsed into
+ * an struct export_dir struct. If 'parse_complete_file' is set to TRUE
+ * then
+ */
+ while (getline(&line, &len, fp) != -1) {
+ line_number++; /* Keeping track of line number allows us to
+ * to log which line numbers were wrong
+ */
+ strtok(line, "\n"); /* removes the newline character from
+ * the line
+ */
+
+ /* Parse the line from the file into an struct export_dir
+ * structure. The process is as follows:
+ * Given a line like :
+ * "/vol @test(sec=sys,rw,anonuid=0) 10.35.11.31(sec=sys,rw)"
+ *
+ * This function will allocate an export dir and set its name
+ * to '/vol', using the function _exp_line_dir_parse ().
+ *
+ * Then it will extract the netgroups from the line, in this
+ * case it would be '@test(sec=sys,rw,anonuid=0)', and set the
+ * item structure's name to '@test'.
+ * It will also extract the options from that string and parse
+ * them into an struct export_options which will be pointed
+ * to by the item structure. This will be put into a dict
+ * which will be pointed to by the export directory structure.
+ *
+ * The same process happens above for the host string
+ * '10.35.11.32(sec=sys,rw)'
+ */
+ ret = _exp_line_parse(line, &expdir, parse_complete_file, ms);
+ if (ret == -ENOMEM) {
+ /* If we get memory allocation errors, we really should
+ * not continue parsing, so just free the allocated
+ * memory and exit.
+ */
+ goto free_and_done;
+ }
+
+ if (ret < 0) {
+ gf_msg(GF_EXP, GF_LOG_ERROR, -ret, NFS_MSG_PARSE_FAIL,
+ "Failed to parse line #%lu", line_number);
+ continue; /* Skip entering this line and continue */
+ }
+
+ if (ret == GF_EXP_PARSE_LINE_IGNORING) {
+ /* This just means the line was empty or started with a
+ * '#' or a ' ' and we are ignoring it.
+ */
+ gf_msg_debug(GF_EXP, 0,
+ "Ignoring line #%lu because it started "
+ "with a %c",
+ line_number, *line);
+ continue;
+ }
+
+ if (!file) {
+ file = _exports_file_init();
+ GF_CHECK_ALLOC_AND_LOG(GF_EXP, file, ret,
+ "Allocation error while "
+ "allocating file struct",
+ free_and_done);
+
+ file->filename = gf_strdup(filepath);
+ GF_CHECK_ALLOC_AND_LOG(GF_EXP, file, ret,
+ "Allocation error while "
+ "duping filepath",
+ free_and_done);
+ }
+
+ /* If the parsing is successful store the export directory
+ * in the file structure.
+ */
+ _exp_file_insert(file, expdir);
+ }
+
+ /* line got allocated through getline(), don't use GF_FREE() for it */
+ free(line);
+
+ *expfile = file;
+ goto parse_done;
+
+free_and_done:
+ if (file)
+ exp_file_deinit(file);
+ _export_dir_deinit(expdir);
+
+parse_done:
+ if (fp)
+ fclose(fp);
+ _exp_deinit_parsers();
+ return ret;
+}
diff --git a/xlators/nfs/server/src/exports.h b/xlators/nfs/server/src/exports.h
new file mode 100644
index 00000000000..ad35ad8c3a0
--- /dev/null
+++ b/xlators/nfs/server/src/exports.h
@@ -0,0 +1,93 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _EXPORTS_H_
+#define _EXPORTS_H_
+
+#include "nfs-mem-types.h"
+#include <glusterfs/dict.h>
+#include "nfs.h"
+
+#define GF_EXP GF_NFS "-exports"
+
+#define NETGROUP_REGEX_PATTERN "(@([a-zA-Z0-9\\(=, .])+)())"
+#define HOSTNAME_REGEX_PATTERN "[[:space:]]([a-zA-Z0-9.\\(=,*/:)-]+)"
+#define OPTIONS_REGEX_PATTERN "([a-zA-Z0-9=\\.]+)"
+
+#define NETGROUP_MAX_LEN 128
+#define FQDN_MAX_LEN 256
+
+#define SEC_OPTION_MAX 10
+#define UID_MAX_LEN 6
+
+#define DIR_MAX_LEN 1024
+
+/* The following 2 definitions are in mount3.h
+ * but we don't want to include it because mount3.h
+ * depends on structs in this file so we get a cross
+ * dependency.
+ */
+struct mount3_state;
+
+extern struct mnt3_export *
+mnt3_mntpath_to_export(struct mount3_state *ms, const char *dirpath,
+ gf_boolean_t export_parsing_match);
+
+struct export_options {
+ gf_boolean_t rw; /* Read-write option */
+ gf_boolean_t nosuid; /* nosuid option */
+ gf_boolean_t root; /* root option */
+ char *anon_uid; /* anonuid option */
+ char *sec_type; /* X, for sec=X */
+};
+
+struct export_item {
+ char *name; /* Name of the export item */
+ struct export_options *opts; /* NFS Options */
+ GF_REF_DECL;
+};
+
+struct export_dir {
+ char *dir_name; /* Directory */
+ dict_t *netgroups; /* Dict of netgroups */
+ dict_t *hosts; /* Dict of hosts */
+};
+
+struct exports_file {
+ char *filename; /* Filename */
+ dict_t *exports_dict; /* Dict of export_dir_t */
+ dict_t *exports_map; /* Map of SuperFastHash(<export>) -> expdir */
+};
+
+void
+exp_file_deinit(struct exports_file *expfile);
+
+int
+exp_file_parse(const char *filepath, struct exports_file **expfile,
+ struct mount3_state *ms);
+
+struct export_dir *
+exp_file_get_dir(const struct exports_file *file, const char *dir);
+
+struct export_item *
+exp_dir_get_host(const struct export_dir *expdir, const char *host);
+
+struct export_item *
+exp_dir_get_netgroup(const struct export_dir *expdir, const char *netgroup);
+
+struct export_dir *
+exp_file_dir_from_uuid(const struct exports_file *file,
+ const uuid_t export_uuid);
+
+#endif /* _EXPORTS_H_ */
diff --git a/xlators/nfs/server/src/mount3-auth.c b/xlators/nfs/server/src/mount3-auth.c
new file mode 100644
index 00000000000..10e57c84cdb
--- /dev/null
+++ b/xlators/nfs/server/src/mount3-auth.c
@@ -0,0 +1,642 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+/* This file contains code for handling mount authentication.
+ * The primary structure here is 'mnt3_auth_params' which contains
+ * 3 important fields: 1) Pointer to a netgroups file struct, 2) Pointer to an
+ * exports file struct. 3) Pointer to a mount state struct.
+ *
+ * - The auth parameter struct belongs to a mount state so the mount state
+ * pointer represents the mount state that this auth parameter struct belongs
+ * to.
+ *
+ * - Currently, the only supported mount auth parameters are an exports file
+ * and a netgroups file. The two pointers in the struct represent the files
+ * we are to authenticate against.
+ *
+ * - To initialize a struct, make a call to mnt3_auth_params_init () with a mnt
+ * state as a parameter.
+ *
+ * - To set an exports file authentication parameter, call
+ * mnt3_auth_set_exports_auth () with an exports file as a parameter.
+ *
+ * - Same goes for the netgroups file parameter, except use the netgroups file
+ * as the parameter.
+ */
+
+#include "mount3-auth.h"
+#include "exports.h"
+#include "netgroups.h"
+#include <glusterfs/mem-pool.h>
+#include "nfs-messages.h"
+
+/**
+ * mnt3_auth_params_init -- Initialize the mount3 authorization parameters
+ * and return the allocated struct. The mount3_state
+ * parameter is pointed to by a field in the struct.
+ *
+ * @ms: Mount state that is needed for auth.
+ *
+ * @return: success: Pointer to the allocated struct
+ * failure: NULL
+ *
+ * For external use.
+ */
+struct mnt3_auth_params *
+mnt3_auth_params_init(struct mount3_state *ms)
+{
+ struct mnt3_auth_params *auth_params = NULL;
+
+ auth_params = GF_MALLOC(sizeof(*auth_params), gf_nfs_mt_mnt3_auth_params);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, auth_params, out);
+
+ auth_params->ngfile = NULL;
+ auth_params->expfile = NULL;
+ auth_params->ms = ms;
+out:
+ return auth_params;
+}
+
+/**
+ * mnt3_auth_params_deinit -- Free the memory used by the struct.
+ *
+ * @auth_params: Pointer to the struct we want to free
+ *
+ * For external use.
+ */
+void
+mnt3_auth_params_deinit(struct mnt3_auth_params *auth_params)
+{
+ if (!auth_params)
+ goto out;
+
+ /* Atomically set the auth params in the mount state to NULL
+ * so subsequent fops will be denied while the auth params
+ * are being cleaned up.
+ */
+ (void)__sync_lock_test_and_set(&auth_params->ms->auth_params, NULL);
+
+ ng_file_deinit(auth_params->ngfile);
+ exp_file_deinit(auth_params->expfile);
+ auth_params->ms = NULL;
+ GF_FREE(auth_params);
+out:
+ return;
+}
+
+/**
+ * mnt3_set_exports_auth -- Set the exports auth file
+ *
+ * @auth_params : Pointer to the auth params struct
+ * @filename : File name to load from disk and parse
+ *
+ * @return : success: 0
+ * failure: -1
+ *
+ * For external use.
+ */
+int
+mnt3_auth_set_exports_auth(struct mnt3_auth_params *auth_params,
+ const char *filename)
+{
+ struct exports_file *expfile = NULL;
+ struct exports_file *oldfile = NULL;
+ int ret = -EINVAL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, auth_params, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, filename, out);
+
+ /* Parse the exports file and set the auth parameter */
+ ret = exp_file_parse(filename, &expfile, auth_params->ms);
+ if (ret < 0) {
+ gf_msg(GF_MNT_AUTH, GF_LOG_ERROR, 0, NFS_MSG_LOAD_PARSE_ERROR,
+ "Failed to load & parse file"
+ " %s, see logs for more information",
+ filename);
+ goto out;
+ }
+
+ /* Atomically set the file pointer */
+ oldfile = __sync_lock_test_and_set(&auth_params->expfile, expfile);
+ exp_file_deinit(oldfile);
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * mnt3_set_netgroups_auth -- Set netgroups auth file
+ *
+ * @auth_params : Pointer to the auth params struct.
+ * @filename : File name to load from disk and parse
+ *
+ * @return : success: 0
+ * failure: -1
+ *
+ * For external use.
+ */
+int
+mnt3_auth_set_netgroups_auth(struct mnt3_auth_params *auth_params,
+ const char *filename)
+{
+ struct netgroups_file *ngfile = NULL;
+ struct netgroups_file *oldfile = NULL;
+ int ret = -EINVAL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, auth_params, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, filename, out);
+
+ ngfile = ng_file_parse(filename);
+ if (!ngfile) {
+ gf_msg(GF_MNT_AUTH, GF_LOG_ERROR, 0, NFS_MSG_LOAD_PARSE_ERROR,
+ "Failed to load file %s, see logs for more "
+ "information",
+ filename);
+ ret = -1;
+ goto out;
+ }
+
+ /* Atomically set the file pointer */
+ oldfile = __sync_lock_test_and_set(&auth_params->ngfile, ngfile);
+ ng_file_deinit(oldfile);
+ ret = 0;
+out:
+ return ret;
+}
+
+/* Struct used to pass parameters to
+ * _mnt3_auth_subnet_match () which
+ * checks if an IP matches a subnet
+ */
+struct _mnt3_subnet_match_s {
+ char *ip; /* IP address to match */
+ struct export_item **host; /* Host structure to set */
+};
+
+/**
+ * _mnt3_auth_subnet_match -- Check if an ip (specified in the parameter tmp)
+ * is in the subnet specified by key.
+ *
+ * @dict: The dict to walk
+ * @key : The key we are on
+ * @val : The value we are on
+ * @tmp : Parameter that points to the above struct
+ *
+ */
+static int
+_mnt3_auth_subnet_match(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct _mnt3_subnet_match_s *match = NULL;
+
+ match = (struct _mnt3_subnet_match_s *)tmp;
+
+ if (!match)
+ return 0;
+
+ if (!match->host)
+ return 0;
+
+ if (!match->ip)
+ return 0;
+
+ /* Already found the host */
+ if (*(match->host))
+ return 0;
+
+ /* Don't process anything that's not in CIDR */
+ if (!strchr(key, '/'))
+ return 0;
+
+ /* Strip out leading whitespaces */
+ while (*key == ' ')
+ key++;
+
+ /* If we found that the IP was in the network, set the host
+ * to point to the value in the dict.
+ */
+ if (gf_is_ip_in_net(key, match->ip)) {
+ *(match->host) = (struct export_item *)val->data;
+ }
+ return 0;
+}
+
+/**
+ * _find_host_in_export -- Find a host in the exports file.
+ *
+ * Case 1: FH is non-null
+ * -----------------------
+ * The lookup process is two-step: The FH has a mountid which represents the
+ * export that was mounted by the client. The export is defined as an entry in
+ * the exports file. The FH's 'mountid' is hashed in the exports file to lookup
+ * an export directory.
+ *
+ * Case 2: FH is null
+ * -------------------
+ * The lookup process is two-step: You need a directory and a hostname
+ * to do the lookup. We first lookup the export directory in the file
+ * and then do a lookup on the directory to find the host. If the host
+ * is not found, we must finally check for subnets and then do a match.
+ *
+ * @file: Exports file to lookup in
+ * @dir : Directory to do the lookup
+ * @host: Host to lookup in the directory
+ *
+ * Not for external use.
+ */
+static struct export_item *
+_mnt3_auth_check_host_in_export(const struct exports_file *file,
+ const char *dir, const char *host,
+ struct nfs3_fh *fh)
+{
+ struct export_dir *expdir = NULL;
+ struct export_item *host_s = NULL;
+ struct _mnt3_subnet_match_s snet_match_s = {
+ 0,
+ };
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, file, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, host, out);
+
+ /* If the filehandle is defined, use that to perform authentication.
+ * All file operations that need authentication must follow this
+ * code path.
+ */
+ if (fh) {
+ expdir = exp_file_dir_from_uuid(file, fh->mountid);
+ if (!expdir)
+ goto out;
+ } else {
+ /* Get the exports directory from the exports file */
+ expdir = exp_file_get_dir(file, dir);
+ if (!expdir)
+ goto out;
+ }
+
+ /* Extract the host from the export directory */
+ host_s = exp_dir_get_host(expdir, host);
+ if (!host_s)
+ goto subnet_match;
+ else
+ goto out;
+
+ /* If the host is not found, we need to walk through the hosts
+ * in the exports directory and see if any of the "hosts" are actually
+ * networks (e.g. 10.5.153.0/24). If they are we should match the
+ * incoming network.
+ */
+subnet_match:
+ if (!expdir->hosts)
+ goto out;
+ snet_match_s.ip = (char *)host;
+ snet_match_s.host = &host_s;
+ dict_foreach(expdir->hosts, _mnt3_auth_subnet_match, &snet_match_s);
+out:
+ return host_s;
+}
+
+/* This struct represents all the parameters necessary to search through a
+ * netgroups file to find a host.
+ */
+struct ng_auth_search {
+ const char *search_for; /* strings to search for */
+ gf_boolean_t found; /* mark true once found */
+ const struct netgroups_file *file; /* netgroups file to search */
+ const char *expdir;
+ struct export_item *expitem; /* pointer to the export */
+ const struct exports_file *expfile;
+ gf_boolean_t _is_host_dict; /* searching a host dict? */
+ struct netgroup_entry *found_entry; /* the entry we found! */
+};
+
+/**
+ * __netgroup_dict_search -- Function to search the netgroups dict.
+ *
+ * @dict: The dict we are walking
+ * @key : The key we are on
+ * @val : The value associated with that key
+ * @data: Additional parameters. We pass a pointer to ng_auth_search_s
+ *
+ * This is passed as a function pointer to dict_foreach ().
+ *
+ * Not for external use.
+ */
+static int
+__netgroup_dict_search(dict_t *dict, char *key, data_t *val, void *data)
+{
+ struct ng_auth_search *ngsa = NULL;
+ struct netgroup_entry *ngentry = NULL;
+ data_t *hdata = NULL;
+
+ /* 'ngsa' is the search params */
+ ngsa = (struct ng_auth_search *)data;
+ ngentry = (struct netgroup_entry *)val->data;
+
+ if (ngsa->_is_host_dict) {
+ /* If are on a host dict, we can simply hash the search key
+ * against the host dict and see if we find anything.
+ */
+ hdata = dict_get(dict, (char *)ngsa->search_for);
+ if (hdata) {
+ /* If it was found, log the message, mark the search
+ * params dict as found and return.
+ */
+ gf_msg_debug(GF_MNT_AUTH, errno,
+ "key %s was hashed "
+ "and found",
+ key);
+ ngsa->found = _gf_true;
+ ngsa->found_entry = (struct netgroup_entry *)hdata->data;
+ goto out;
+ }
+ }
+
+ /* If the key is what we are searching for, mark the item as
+ * found and return.
+ */
+ if (strcmp(key, ngsa->search_for) == 0) {
+ ngsa->found = _gf_true;
+ ngsa->found_entry = ngentry;
+ goto out;
+ }
+
+ /* If we have a netgroup hosts dict, then search the dict using this
+ * same function.
+ */
+ if (ngentry->netgroup_hosts) {
+ ngsa->_is_host_dict = _gf_true;
+ dict_foreach(ngentry->netgroup_hosts, __netgroup_dict_search, ngsa);
+ }
+
+ /* If that search was successful, just return */
+ if (ngsa->found)
+ goto out;
+
+ /* If we have a netgroup dict, then search the dict using this same
+ * function.
+ */
+ if (ngentry->netgroup_ngs) {
+ ngsa->_is_host_dict = _gf_false;
+ dict_foreach(ngentry->netgroup_ngs, __netgroup_dict_search, ngsa);
+ }
+out:
+ return 0;
+}
+
+/**
+ * __export_dir_lookup_netgroup -- Function to search an exports directory
+ * for a host name.
+ *
+ * This function walks all the netgroups & hosts in an export directory
+ * and tries to match it with the search key. This function calls the above
+ * netgroup search function to search through the netgroups.
+ *
+ * This function is very similar to the above function, but both are necessary
+ * since we are walking two different dicts. For each netgroup in _this_ dict
+ * (the exports dict) we are going to find the corresponding netgroups dict
+ * and walk that (nested) structure until we find the host we are looking for.
+ *
+ * @dict: The dict we are walking
+ * @key : The key we are on
+ * @val : The value associated with that key
+ * @data: Additional parameters. We pass a pointer to ng_auth_search_s
+ *
+ * This is passed as a function pointer to dict_foreach ().
+ *
+ * Not for external use.
+ */
+static int
+__export_dir_lookup_netgroup(dict_t *dict, char *key, data_t *val, void *data)
+{
+ struct ng_auth_search *ngsa = NULL; /* Search params */
+ struct netgroups_file *nfile = NULL; /* Netgroups file to search */
+ struct netgroup_entry *ngentry = NULL; /* Entry in the netgroups file */
+ struct export_dir *tmpdir = NULL;
+
+ ngsa = (struct ng_auth_search *)data;
+ nfile = (struct netgroups_file *)ngsa->file;
+
+ GF_ASSERT((*key == '@'));
+
+ /* We use ++key here because keys start with '@' for ngs */
+ ngentry = ng_file_get_netgroup(nfile, (key + 1));
+ if (!ngentry) {
+ gf_msg_debug(GF_MNT_AUTH, 0, "%s not found in %s", key,
+ nfile->filename);
+ goto out;
+ }
+
+ tmpdir = exp_file_get_dir(ngsa->expfile, ngsa->expdir);
+ if (!tmpdir)
+ goto out;
+
+ ngsa->expitem = exp_dir_get_netgroup(tmpdir, key);
+ if (!ngsa->expitem)
+ goto out;
+
+ /* Run through the host dict */
+ if (ngentry->netgroup_hosts) {
+ ngsa->_is_host_dict = _gf_true;
+ dict_foreach(ngentry->netgroup_hosts, __netgroup_dict_search, ngsa);
+ }
+
+ /* If the above search was successful, just return */
+ if (ngsa->found)
+ goto out;
+
+ /* Run through the netgroups dict */
+ if (ngentry->netgroup_ngs) {
+ ngsa->_is_host_dict = _gf_false;
+ dict_foreach(ngentry->netgroup_ngs, __netgroup_dict_search, ngsa);
+ }
+out:
+ return 0;
+}
+
+/**
+ * _mnt3_auth_setup_search_param -- This function sets up an ng_auth_search
+ * struct with host and file as the parameters.
+ * Host is what we are searching for and file
+ * is what we are searching in.
+ * @params: Search params to setup
+ * @host : The host to set
+ * @nfile : The netgroups file to set
+ *
+ */
+void
+_mnt3_auth_setup_search_params(struct ng_auth_search *params, const char *host,
+ const char *dir,
+ const struct netgroups_file *nfile,
+ const struct exports_file *expfile)
+{
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, params, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, host, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, nfile, out);
+
+ params->search_for = host;
+ params->found = _gf_false;
+ params->file = nfile;
+ params->_is_host_dict = _gf_false;
+ params->found_entry = NULL;
+ params->expitem = NULL;
+ params->expfile = expfile;
+ params->expdir = dir;
+out:
+ return;
+}
+
+/**
+ * _mnt3_auth_find_host_in_netgroup -- Given a host name for an directory
+ * find if that hostname is in the
+ * directory's dict of netgroups.
+ * @nfile: Netgroups file to search
+ * @efile: Exports file to search
+ * @dir : The exports directory name (used to lookup in exports file)
+ * @host : The host we are searching for
+ *
+ * Search procedure:
+ *
+ * - Lookup directory string against exports file structure,
+ * get an exports directory structure.
+ * - Walk the export file structure's netgroup dict. This dict
+ * holds each netgroup that is authorized to mount that directory.
+ * - We then have to walk the netgroup structure, which is a set of
+ * nested dicts until we find the host we are looking for.
+ *
+ * @return: success: Pointer to the netgroup entry found
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroup_entry *
+_mnt3_auth_check_host_in_netgroup(const struct mnt3_auth_params *auth_params,
+ struct nfs3_fh *fh, const char *host,
+ const char *dir, struct export_item **item)
+{
+ struct export_dir *expdir = NULL;
+ struct ng_auth_search ngsa = {
+ 0,
+ };
+ struct netgroup_entry *found_entry = NULL;
+ struct exports_file *efile = auth_params->expfile;
+ struct netgroups_file *nfile = auth_params->ngfile;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, nfile, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, efile, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, host, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, item, out);
+
+ if (fh) {
+ expdir = exp_file_dir_from_uuid(efile, fh->mountid);
+ if (!expdir)
+ goto out;
+ } else {
+ /* Get the exports directory */
+ expdir = exp_file_get_dir(efile, dir);
+ if (!expdir)
+ goto out;
+ }
+
+ /* Setup search struct */
+ _mnt3_auth_setup_search_params(&ngsa, host, expdir->dir_name, nfile, efile);
+
+ /* Do the search */
+ dict_foreach(expdir->netgroups, __export_dir_lookup_netgroup, &ngsa);
+ found_entry = ngsa.found_entry;
+ *item = ngsa.expitem;
+out:
+ return found_entry;
+}
+
+/**
+ * check_rw_access -- Checks if the export item
+ * has read-write access.
+ *
+ * @host_item : The export item to check
+ *
+ * @return -EROFS if it does not have rw access, 0 otherwise
+ *
+ */
+int
+check_rw_access(struct export_item *item)
+{
+ struct export_options *opts = NULL;
+ int ret = -EROFS;
+
+ if (!item)
+ goto out;
+
+ opts = item->opts;
+ if (!opts)
+ goto out;
+
+ if (opts->rw)
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * mnt3_auth_host -- Check if a host is authorized for a directory
+ *
+ * @auth_params : Auth parameters to authenticate against
+ * @host: Host requesting the directory
+ * @dir : Directory that the host requests
+ * @fh : The filehandle passed from an fop to authenticate
+ *
+ * 'fh' is null on mount requests and 'dir' is null on fops
+ *
+ * Procedure:
+ *
+ * - Check if the host is in the exports directory.
+ * - If not, check if the host is in the netgroups file for the
+ * netgroups authorized for the exports.
+ *
+ * @return: 0 if authorized
+ * -EACCES for completely unauthorized fop
+ * -EROFS for unauthorized write operations (rm, mkdir, write) *
+ */
+int
+mnt3_auth_host(const struct mnt3_auth_params *auth_params, const char *host,
+ struct nfs3_fh *fh, const char *dir, gf_boolean_t is_write_op,
+ struct export_item **save_item)
+{
+ int auth_status_code = -EACCES;
+ struct export_item *item = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, auth_params, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT_AUTH, host, out);
+
+ /* Find the host in the exports file */
+ item = _mnt3_auth_check_host_in_export(auth_params->expfile, dir, host, fh);
+ if (item) {
+ auth_status_code = (is_write_op) ? check_rw_access(item) : 0;
+ goto out;
+ }
+
+ /* Find the host in the netgroups file for the exports directory */
+ if (_mnt3_auth_check_host_in_netgroup(auth_params, fh, host, dir, &item)) {
+ auth_status_code = (is_write_op) ? check_rw_access(item) : 0;
+ goto out;
+ }
+
+out:
+ if (save_item)
+ *save_item = item;
+
+ return auth_status_code;
+}
diff --git a/xlators/nfs/server/src/mount3-auth.h b/xlators/nfs/server/src/mount3-auth.h
new file mode 100644
index 00000000000..e50af5847d3
--- /dev/null
+++ b/xlators/nfs/server/src/mount3-auth.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _MOUNT3_AUTH
+#define _MOUNT3_AUTH
+
+#include "nfs-mem-types.h"
+#include "netgroups.h"
+#include "exports.h"
+#include "mount3.h"
+#include "nfs.h"
+
+#define GF_MNT_AUTH GF_NFS "-mount3-auth"
+
+struct mnt3_auth_params {
+ struct netgroups_file *ngfile; /* The netgroup file to auth against */
+ struct exports_file *expfile; /* The export file to auth against */
+ struct mount3_state *ms; /* The mount state that owns this */
+};
+
+/* Initialize auth params struct */
+struct mnt3_auth_params *
+mnt3_auth_params_init(struct mount3_state *ms);
+
+/* Set the netgroups file to use in the auth */
+int
+mnt3_auth_set_netgroups_auth(struct mnt3_auth_params *aps,
+ const char *filename);
+
+/* Set the exports file to use in the auth */
+int
+mnt3_auth_set_exports_auth(struct mnt3_auth_params *aps, const char *filename);
+
+/* Check if a host is authorized to perform a mount / nfs-fop */
+int
+mnt3_auth_host(const struct mnt3_auth_params *aps, const char *host,
+ struct nfs3_fh *fh, const char *dir, gf_boolean_t is_write_op,
+ struct export_item **save_item);
+
+/* Free resources used by the auth params struct */
+void
+mnt3_auth_params_deinit(struct mnt3_auth_params *aps);
+
+int
+mnt3_auth_fop_options_verify(const struct mnt3_auth_params *auth_params,
+ const char *host, const char *dir);
+
+#endif /* _MOUNT3_AUTH */
diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c
index 0f14c8d5a03..a34d9104c17 100644
--- a/xlators/nfs/server/src/mount3.c
+++ b/xlators/nfs/server/src/mount3.c
@@ -2,408 +2,944 @@
Copyright (c) 2010-2011 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/>.
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
*/
-#ifndef _CONFIG_H
-#define _CONFIG_H
-#include "config.h"
-#endif
-
#include "rpcsvc.h"
-#include "dict.h"
-#include "xlator.h"
+#include <glusterfs/dict.h>
+#include <glusterfs/xlator.h>
#include "mount3.h"
#include "xdr-nfs3.h"
#include "msg-nfs3.h"
-#include "iobuf.h"
+#include <glusterfs/iobuf.h>
#include "nfs-common.h"
#include "nfs3-fh.h"
#include "nfs-fops.h"
#include "nfs-inodes.h"
#include "nfs-generics.h"
-#include "locking.h"
-#include "iatt.h"
+#include <glusterfs/locking.h>
+#include <glusterfs/iatt.h>
#include "nfs-mem-types.h"
#include "nfs.h"
-#include "common-utils.h"
-
+#include <glusterfs/common-utils.h>
+#include <glusterfs/store.h>
+#include "glfs-internal.h"
+#include "glfs.h"
+#include "mount3-auth.h"
+#include <glusterfs/hashfn.h>
+#include "nfs-messages.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/uio.h>
-typedef ssize_t (*mnt3_serializer) (struct iovec outmsg, void *args);
-
+/* This macro will assist in freeing up entire link list
+ * of host_auth_spec structure.
+ */
+#define FREE_HOSTSPEC(exp) \
+ do { \
+ struct host_auth_spec *host = exp->hostspec; \
+ while (NULL != host) { \
+ struct host_auth_spec *temp = host; \
+ host = host->next; \
+ if (NULL != temp->host_addr) { \
+ GF_FREE(temp->host_addr); \
+ } \
+ GF_FREE(temp); \
+ } \
+ exp->hostspec = NULL; \
+ } while (0)
+
+/* Paths for export and netgroup files */
+const char *exports_file_path = GLUSTERD_DEFAULT_WORKDIR "/nfs/exports";
+const char *netgroups_file_path = GLUSTERD_DEFAULT_WORKDIR "/nfs/netgroups";
+
+typedef ssize_t (*mnt3_serializer)(struct iovec outmsg, void *args);
+
+extern void *
+mount3udp_thread(void *argv);
+
+static void
+mnt3_export_free(struct mnt3_export *exp)
+{
+ if (!exp)
+ return;
+
+ if (exp->exptype == MNT3_EXPTYPE_DIR)
+ FREE_HOSTSPEC(exp);
+ GF_FREE(exp->expname);
+ GF_FREE(exp->fullpath);
+ GF_FREE(exp);
+}
/* Generic reply function for MOUNTv3 specific replies. */
int
-mnt3svc_submit_reply (rpcsvc_request_t *req, void *arg, mnt3_serializer sfunc)
+mnt3svc_submit_reply(rpcsvc_request_t *req, void *arg, mnt3_serializer sfunc)
{
- struct iovec outmsg = {0, };
- struct iobuf *iob = NULL;
- struct mount3_state *ms = NULL;
- int ret = -1;
- struct iobref *iobref = NULL;
-
- if (!req)
- return -1;
-
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "mount state not found");
- goto ret;
- }
+ struct iovec outmsg = {
+ 0,
+ };
+ struct iobuf *iob = NULL;
+ struct mount3_state *ms = NULL;
+ int ret = -1;
+ ssize_t msglen = 0;
+ struct iobref *iobref = NULL;
+
+ if (!req)
+ return -1;
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "mount state not found");
+ goto ret;
+ }
+
+ /* First, get the io buffer into which the reply in arg will
+ * be serialized.
+ */
+ /* TODO: use 'xdrproc_t' instead of 'sfunc' to get the xdr-size */
+ iob = iobuf_get(ms->iobpool);
+ if (!iob) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "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.
+ */
+ msglen = sfunc(outmsg, arg);
+ if (msglen < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_ENCODE_MSG_FAIL,
+ "Failed to encode message");
+ goto ret;
+ }
+ outmsg.iov_len = msglen;
+
+ iobref = iobref_new();
+ if (iobref == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to get iobref");
+ goto ret;
+ }
+
+ ret = iobref_add(iobref, iob);
+ if (ret) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to add iob to iobref");
+ goto ret;
+ }
+
+ /* Then, submit the message for transmission. */
+ ret = rpcsvc_submit_message(req, &outmsg, 1, NULL, 0, iobref);
+ if (ret == -1) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_REP_SUBMIT_FAIL,
+ "Reply submission failed");
+ goto ret;
+ }
+
+ ret = 0;
+ret:
+ if (NULL != iob)
+ iobuf_unref(iob);
+ if (NULL != iobref)
+ iobref_unref(iobref);
- /* First, get the io buffer into which the reply in arg will
- * be serialized.
- */
- iob = iobuf_get (ms->iobpool);
- if (!iob) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to get iobuf");
- goto ret;
- }
+ return ret;
+}
+/**
+ * __mountdict_insert -- Insert a mount entry into the mount state
+ *
+ * @ms: The mount state holding the entries
+ * @me: The mount entry to insert
+ *
+ * Not for external use.
+ */
+void
+__mountdict_insert(struct mount3_state *ms, struct mountentry *me)
+{
+ char *exname = NULL;
+ char *fpath = NULL;
+ data_t *medata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_MNT, ms, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT, me, out);
+
+ /* We don't want export names with leading slashes */
+ exname = me->exname;
+ while (exname[0] == '/')
+ exname++;
+
+ /* Get the fullpath for the export */
+ fpath = me->fullpath;
+ if (me->has_full_path) {
+ while (fpath[0] == '/')
+ fpath++;
+
+ /* Export names can either be just volumes or paths inside that
+ * volume. */
+ exname = fpath;
+ }
+
+ snprintf(me->hashkey, sizeof(me->hashkey), "%s:%s", exname, me->hostname);
+
+ medata = bin_to_data(me, sizeof(*me));
+ dict_set(ms->mountdict, me->hashkey, medata);
+ gf_msg_trace(GF_MNT, 0, "Inserted into mountdict: %s", me->hashkey);
+out:
+ return;
+}
- 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.
- */
- outmsg.iov_len = sfunc (outmsg, arg);
+/**
+ * __mountdict_remove -- Remove a mount entry from the mountstate.
+ *
+ * @ms: The mount state holding the entries
+ * @me: The mount entry to remove
+ *
+ * Not for external use.
+ */
+void
+__mountdict_remove(struct mount3_state *ms, struct mountentry *me)
+{
+ dict_del(ms->mountdict, me->hashkey);
+}
- iobref = iobref_new ();
- if (iobref == NULL) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to get iobref");
- goto ret;
- }
+/* Generic error reply function, just pass the err status
+ * and it will do the rest, including transmission.
+ */
+int
+mnt3svc_mnt_error_reply(rpcsvc_request_t *req, int mntstat)
+{
+ mountres3 res;
- iobref_add (iobref, iob);
+ if (!req)
+ return -1;
- /* Then, submit the message for transmission. */
- ret = rpcsvc_submit_message (req, &outmsg, 1, NULL, 0, iobref);
- iobuf_unref (iob);
- iobref_unref (iobref);
- if (ret == -1) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Reply submission failed");
- goto ret;
- }
+ res.fhs_status = mntstat;
+ mnt3svc_submit_reply(req, (void *)&res,
+ (mnt3_serializer)xdr_serialize_mountres3);
- ret = 0;
-ret:
- return ret;
+ return 0;
}
+mountstat3
+mnt3svc_errno_to_mnterr(int32_t errnum)
+{
+ mountstat3 stat;
+
+ switch (errnum) {
+ case 0:
+ stat = MNT3_OK;
+ break;
+ case ENOENT:
+ stat = MNT3ERR_NOENT;
+ break;
+ case EPERM:
+ stat = MNT3ERR_PERM;
+ break;
+ case EIO:
+ stat = MNT3ERR_IO;
+ break;
+ case EACCES:
+ stat = MNT3ERR_ACCES;
+ break;
+ case ENOTDIR:
+ stat = MNT3ERR_NOTDIR;
+ break;
+ case EINVAL:
+ stat = MNT3ERR_INVAL;
+ break;
+ case ENOSYS:
+ stat = MNT3ERR_NOTSUPP;
+ break;
+ case ENOMEM:
+ stat = MNT3ERR_SERVERFAULT;
+ break;
+ default:
+ stat = MNT3ERR_SERVERFAULT;
+ break;
+ }
+
+ return stat;
+}
-/* Generic error reply function, just pass the err status
- * and it will do the rest, including transmission.
- */
-int
-mnt3svc_mnt_error_reply (rpcsvc_request_t *req, int mntstat)
+mountres3
+mnt3svc_set_mountres3(mountstat3 stat, struct nfs3_fh *fh, int *authflavor,
+ u_int aflen)
{
- mountres3 res;
+ mountres3 res = {
+ 0,
+ };
+ uint32_t fhlen = 0;
- if (!req)
- return -1;
+ res.fhs_status = stat;
- res.fhs_status = mntstat;
- mnt3svc_submit_reply (req, (void *)&res,
- (mnt3_serializer)xdr_serialize_mountres3);
+ if (fh)
+ fhlen = nfs3_fh_compute_size();
- return 0;
+ res.mountres3_u.mountinfo.fhandle.fhandle3_len = fhlen;
+ res.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *)fh;
+ res.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = authflavor;
+ res.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = aflen;
+
+ return res;
}
+/* Read the rmtab from the store_handle and append (or not) the entries to the
+ * mountlist.
+ *
+ * Requires the store_handle to be locked.
+ */
+static int
+__mount_read_rmtab(gf_store_handle_t *sh, struct list_head *mountlist,
+ gf_boolean_t append)
+{
+ int ret = 0;
+ unsigned int idx = 0;
+ struct mountentry *me = NULL, *tmp = NULL;
+ /* me->hostname is a char[MNTPATHLEN] */
+ char key[MNTPATHLEN + 11];
+
+ GF_ASSERT(sh && mountlist);
+
+ if (!gf_store_locked_local(sh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_READ_LOCKED,
+ "Not reading unlocked %s", sh->path);
+ return -1;
+ }
+
+ if (!append) {
+ list_for_each_entry_safe(me, tmp, mountlist, mlist)
+ {
+ list_del(&me->mlist);
+ GF_FREE(me);
+ }
+ me = NULL;
+ }
+
+ for (;;) {
+ char *value = NULL;
+
+ if (me && append) {
+ /* do not add duplicates */
+ list_for_each_entry(tmp, mountlist, mlist)
+ {
+ if (!strcmp(tmp->hostname, me->hostname) &&
+ !strcmp(tmp->exname, me->exname)) {
+ GF_FREE(me);
+ goto dont_add;
+ }
+ }
+ list_add_tail(&me->mlist, mountlist);
+ } else if (me) {
+ list_add_tail(&me->mlist, mountlist);
+ }
-mountstat3
-mnt3svc_errno_to_mnterr (int32_t errnum)
-{
- mountstat3 stat;
-
- switch (errnum) {
-
- case 0:
- stat = MNT3_OK;
- break;
- case ENOENT:
- stat = MNT3ERR_NOENT;
- break;
- case EPERM:
- stat = MNT3ERR_PERM;
- break;
- case EIO:
- stat = MNT3ERR_IO;
- break;
- case EACCES:
- stat = MNT3ERR_ACCES;
- break;
- case ENOTDIR:
- stat = MNT3ERR_NOTDIR;
- break;
- case EINVAL:
- stat = MNT3ERR_INVAL;
- break;
- case ENOSYS:
- stat = MNT3ERR_NOTSUPP;
- break;
- case ENOMEM:
- stat = MNT3ERR_SERVERFAULT;
- break;
- default:
- stat = MNT3ERR_SERVERFAULT;
- break;
+ dont_add:
+ me = GF_CALLOC(1, sizeof(*me), gf_nfs_mt_mountentry);
+ if (!me) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Out of memory");
+ ret = -1;
+ goto out;
}
- return stat;
+ INIT_LIST_HEAD(&me->mlist);
+
+ snprintf(key, 9 + MNTPATHLEN, "hostname-%d", idx);
+ ret = gf_store_retrieve_value(sh, key, &value);
+ if (ret)
+ break;
+ snprintf(me->hostname, MNTPATHLEN, "%s", value);
+ GF_FREE(value);
+
+ snprintf(key, 11 + MNTPATHLEN, "mountpoint-%d", idx);
+ ret = gf_store_retrieve_value(sh, key, &value);
+ if (ret)
+ break;
+ snprintf(me->exname, MNTPATHLEN, "%s", value);
+ GF_FREE(value);
+
+ idx++;
+ gf_msg_trace(GF_MNT, 0, "Read entries %s:%s", me->hostname, me->exname);
+ }
+ gf_msg_debug(GF_MNT, 0, "Read %d entries from '%s'", idx, sh->path);
+ GF_FREE(me);
+out:
+ return ret;
}
+/* Overwrite the contents of the rwtab with the in-memory client list.
+ * Fail gracefully if the stora_handle is not locked.
+ */
+static void
+__mount_rewrite_rmtab(struct mount3_state *ms, gf_store_handle_t *sh)
+{
+ struct mountentry *me = NULL;
+ char key[16];
+ int fd, ret;
+ unsigned int idx = 0;
+
+ if (!gf_store_locked_local(sh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_MODIFY_LOCKED,
+ "Not modifying unlocked %s", sh->path);
+ return;
+ }
+
+ fd = gf_store_mkstemp(sh);
+ if (fd == -1) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Failed to open %s", sh->path);
+ return;
+ }
+
+ list_for_each_entry(me, &ms->mountlist, mlist)
+ {
+ snprintf(key, 16, "hostname-%d", idx);
+ ret = gf_store_save_value(fd, key, me->hostname);
+ if (ret)
+ goto fail;
+
+ snprintf(key, 16, "mountpoint-%d", idx);
+ ret = gf_store_save_value(fd, key, me->exname);
+ if (ret)
+ goto fail;
+
+ idx++;
+ }
+
+ gf_msg_debug(GF_MNT, 0, "Updated rmtab with %d entries", idx);
+
+ if (gf_store_rename_tmppath(sh))
+ gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_RWTAB_OVERWRITE_FAIL,
+ "Failed to overwrite rwtab %s", sh->path);
+
+ return;
+
+fail:
+ gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_UPDATE_FAIL,
+ "Failed to update %s", sh->path);
+ gf_store_unlink_tmppath(sh);
+}
-mountres3
-mnt3svc_set_mountres3 (mountstat3 stat, struct nfs3_fh *fh, int *authflavor,
- u_int aflen)
+static gf_boolean_t
+mount_open_rmtab(const char *rmtab, gf_store_handle_t **sh)
{
- mountres3 res = {0, };
- uint32_t fhlen = 0;
+ int ret = -1;
- res.fhs_status = stat;
- fhlen = nfs3_fh_compute_size (fh);
- res.mountres3_u.mountinfo.fhandle.fhandle3_len = fhlen;
- res.mountres3_u.mountinfo.fhandle.fhandle3_val = (char *)fh;
- res.mountres3_u.mountinfo.auth_flavors.auth_flavors_val = authflavor;
- res.mountres3_u.mountinfo.auth_flavors.auth_flavors_len = aflen;
+ /* updating the rmtab is disabled, use in-memory only */
+ if (!rmtab || rmtab[0] == '\0')
+ return _gf_false;
- return res;
+ ret = gf_store_handle_new(rmtab, sh);
+ if (ret) {
+ gf_log(GF_MNT, GF_LOG_WARNING, "Failed to open '%s'", rmtab);
+ return _gf_false;
+ }
+
+ return _gf_true;
}
+/* Read the rmtab into a clean ms->mountlist.
+ */
+static void
+mount_read_rmtab(struct mount3_state *ms)
+{
+ gf_store_handle_t *sh = NULL;
+ struct nfs_state *nfs = NULL;
+ gf_boolean_t read_rmtab = _gf_false;
-int
-mnt3svc_update_mountlist (struct mount3_state *ms, rpcsvc_request_t *req,
- char *expname)
+ nfs = (struct nfs_state *)ms->nfsx->private;
+
+ read_rmtab = mount_open_rmtab(nfs->rmtab, &sh);
+ if (!read_rmtab)
+ return;
+
+ if (gf_store_lock(sh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_LOCK_FAIL,
+ "Failed to lock '%s'", nfs->rmtab);
+ goto out;
+ }
+
+ __mount_read_rmtab(sh, &ms->mountlist, _gf_false);
+ gf_store_unlock(sh);
+
+out:
+ gf_store_handle_destroy(sh);
+}
+
+/* Write the ms->mountlist to the rmtab.
+ *
+ * The rmtab could be empty, or it can exists and have been updated by a
+ * different storage server without our knowing.
+ *
+ * 0. if opening the nfs->rmtab fails, return gracefully
+ * 1. takes the store_handle lock on the current rmtab
+ * - blocks if an other storage server rewrites the rmtab at the same time
+ * 2. [if new_rmtab] takes the store_handle lock on the new rmtab
+ * 3. reads/merges the entries from the current rmtab
+ * 4. [if new_rmtab] reads/merges the entries from the new rmtab
+ * 5. [if new_rmtab] writes the new rmtab
+ * 6. [if not new_rmtab] writes the current rmtab
+ * 7 [if new_rmtab] replaces nfs->rmtab to point to the new location
+ * 8. [if new_rmtab] releases the store_handle lock of the new rmtab
+ * 9. releases the store_handle lock of the old rmtab
+ */
+void
+mount_rewrite_rmtab(struct mount3_state *ms, char *new_rmtab)
{
- struct mountentry *me = NULL;
- int ret = -1;
+ gf_store_handle_t *sh = NULL, *nsh = NULL;
+ struct nfs_state *nfs = NULL;
+ int ret;
+ char *rmtab = NULL;
+ gf_boolean_t got_old_rmtab = _gf_false;
+
+ nfs = (struct nfs_state *)ms->nfsx->private;
+
+ got_old_rmtab = mount_open_rmtab(nfs->rmtab, &sh);
+ if (!got_old_rmtab && !new_rmtab)
+ return;
+
+ if (got_old_rmtab && gf_store_lock(sh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_REWRITE_ERROR,
+ "Not rewriting '%s'", nfs->rmtab);
+ goto free_sh;
+ }
+
+ if (new_rmtab) {
+ ret = gf_store_handle_new(new_rmtab, &nsh);
+ if (ret) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_OPEN_FAIL,
+ "Failed to open '%s'", new_rmtab);
+ goto unlock_sh;
+ }
- if ((!ms) || (!req) || (!expname))
- return -1;
+ if (gf_store_lock(nsh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_REWRITE_ERROR,
+ "Not rewriting '%s'", new_rmtab);
+ goto free_nsh;
+ }
+ }
+
+ /* always read the currently used rmtab */
+ if (got_old_rmtab)
+ __mount_read_rmtab(sh, &ms->mountlist, _gf_true);
+
+ if (new_rmtab) {
+ /* read the new rmtab and write changes to the new location */
+ __mount_read_rmtab(nsh, &ms->mountlist, _gf_true);
+ __mount_rewrite_rmtab(ms, nsh);
+
+ /* replace the nfs->rmtab reference to the new rmtab */
+ rmtab = gf_strdup(new_rmtab);
+ if (rmtab == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_NO_MEMORY,
+ "Out of memory, keeping %s as rmtab", nfs->rmtab);
+ } else {
+ GF_FREE(nfs->rmtab);
+ nfs->rmtab = rmtab;
+ }
- me = (struct mountentry *)GF_CALLOC (1, sizeof (*me),
- gf_nfs_mt_mountentry);
- if (!me)
- return -1;
+ gf_store_unlock(nsh);
+ } else {
+ /* rewrite the current (unchanged location) rmtab */
+ __mount_rewrite_rmtab(ms, sh);
+ }
+
+free_nsh:
+ if (new_rmtab)
+ gf_store_handle_destroy(nsh);
+unlock_sh:
+ if (got_old_rmtab)
+ gf_store_unlock(sh);
+free_sh:
+ if (got_old_rmtab)
+ gf_store_handle_destroy(sh);
+}
- strcpy (me->exname, expname);
- INIT_LIST_HEAD (&me->mlist);
- /* Must get the IP or hostname of the client so we
- * can map it into the mount entry.
- */
- ret = rpcsvc_transport_peername (req->trans, me->hostname, MNTPATHLEN);
- if (ret == -1)
- goto free_err;
+/* Add a new NFS-client to the ms->mountlist and update the rmtab if we can.
+ *
+ * A NFS-client will only be removed from the ms->mountlist in case the
+ * NFS-client sends a unmount request. It is possible that a NFS-client
+ * crashed/rebooted had network loss or something else prevented the NFS-client
+ * to unmount cleanly. In this case, a duplicate entry would be added to the
+ * ms->mountlist, which is wrong and we should prevent.
+ *
+ * It is fully acceptable that the ms->mountlist is not 100% correct, this is a
+ * common issue for all(?) NFS-servers.
+ */
+int
+mnt3svc_update_mountlist(struct mount3_state *ms, rpcsvc_request_t *req,
+ const char *expname, const char *fullpath)
+{
+ struct mountentry *me = NULL;
+ struct mountentry *cur = NULL;
+ int ret = -1;
+ char *colon = NULL;
+ struct nfs_state *nfs = NULL;
+ gf_store_handle_t *sh = NULL;
+ gf_boolean_t update_rmtab = _gf_false;
+
+ if ((!ms) || (!req) || (!expname))
+ return -1;
+
+ me = (struct mountentry *)GF_CALLOC(1, sizeof(*me), gf_nfs_mt_mountentry);
+ if (!me)
+ return -1;
+
+ nfs = (struct nfs_state *)ms->nfsx->private;
+
+ update_rmtab = mount_open_rmtab(nfs->rmtab, &sh);
+
+ snprintf(me->exname, MNTPATHLEN, "%s", expname);
+ /* Sometimes we don't care about the full path
+ * so a NULL value for fullpath is valid.
+ */
+ if (fullpath) {
+ if (strlen(fullpath) < MNTPATHLEN) {
+ strcpy(me->fullpath, fullpath);
+ me->has_full_path = _gf_true;
+ }
+ }
+
+ INIT_LIST_HEAD(&me->mlist);
+ /* Must get the IP or hostname of the client so we
+ * can map it into the mount entry.
+ */
+ ret = rpcsvc_transport_peername(req->trans, me->hostname, MNTPATHLEN);
+ if (ret == -1)
+ goto free_err;
+
+ colon = strrchr(me->hostname, ':');
+ if (colon) {
+ *colon = '\0';
+ }
+ LOCK(&ms->mountlock);
+ {
+ /* in case locking fails, we just don't write the rmtab */
+ if (update_rmtab && gf_store_lock(sh)) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_LOCK_FAIL,
+ "Failed to lock '%s', changes will not be "
+ "written",
+ nfs->rmtab);
+ } else if (update_rmtab) {
+ __mount_read_rmtab(sh, &ms->mountlist, _gf_false);
+ }
- LOCK (&ms->mountlock);
+ /* do not add duplicates */
+ list_for_each_entry(cur, &ms->mountlist, mlist)
{
- list_add_tail (&me->mlist, &ms->mountlist);
+ if (!strcmp(cur->hostname, me->hostname) &&
+ !strcmp(cur->exname, me->exname)) {
+ GF_FREE(me);
+ goto dont_add;
+ }
}
- UNLOCK (&ms->mountlock);
+ list_add_tail(&me->mlist, &ms->mountlist);
+ __mountdict_insert(ms, me);
+
+ /* only write the rmtab in case it was locked */
+ if (update_rmtab && gf_store_locked_local(sh))
+ __mount_rewrite_rmtab(ms, sh);
+ }
+dont_add:
+ if (update_rmtab && gf_store_locked_local(sh))
+ gf_store_unlock(sh);
+
+ UNLOCK(&ms->mountlock);
free_err:
- if (ret == -1)
- GF_FREE (me);
+ if (update_rmtab)
+ gf_store_handle_destroy(sh);
- return ret;
-}
+ if (ret == -1)
+ GF_FREE(me);
+ return ret;
+}
int
-__mnt3_get_volume_id (struct mount3_state *ms, xlator_t *mntxl,
- uuid_t volumeid)
+__mnt3_get_volume_id(struct mount3_state *ms, xlator_t *mntxl, uuid_t volumeid)
{
- int ret = -1;
- struct mnt3_export *exp = NULL;
+ int ret = -1;
+ struct mnt3_export *exp = NULL;
- if ((!ms) || (!mntxl))
- return ret;
+ if ((!ms) || (!mntxl))
+ return ret;
- list_for_each_entry (exp, &ms->exportlist, explist) {
- if (exp->vol == mntxl) {
- uuid_copy (volumeid, exp->volumeid);
- ret = 0;
- goto out;
- }
+ LOCK(&ms->mountlock);
+ list_for_each_entry(exp, &ms->exportlist, explist)
+ {
+ if (exp->vol == mntxl) {
+ gf_uuid_copy(volumeid, exp->volumeid);
+ ret = 0;
+ goto out;
}
+ }
out:
- return ret;
+ UNLOCK(&ms->mountlock);
+ return ret;
}
+int
+__mnt3_build_mountid_from_path(const char *path, uuid_t mountid)
+{
+ uint32_t hashed_path = 0;
+ int ret = -1;
-int32_t
-mnt3svc_lookup_mount_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)
-{
- mountres3 res = {0, };
- rpcsvc_request_t *req = NULL;
- struct nfs3_fh fh = {{0}, };
- struct mount3_state *ms = NULL;
- mountstat3 status = 0;
- int autharr[10];
- int autharrlen = 0;
- rpcsvc_t *svc = NULL;
- xlator_t *mntxl = NULL;
- uuid_t volumeid = {0, };
- char fhstr[1024];
-
- req = (rpcsvc_request_t *)frame->local;
-
- if (!req)
- return -1;
-
- mntxl = (xlator_t *)cookie;
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "mount state not found");
- op_ret = -1;
- op_errno = EINVAL;
- }
+ if (!path)
+ goto out;
- if (op_ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "error=%s", strerror (op_errno));
- status = mnt3svc_errno_to_mnterr (op_errno);
- }
- if (status != MNT3_OK)
- goto xmit_res;
+ while (strlen(path) > 0 && path[0] == '/')
+ path++;
- mnt3svc_update_mountlist (ms, req, mntxl->name);
- if (gf_nfs_dvm_off (nfs_state (ms->nfsx))) {
- fh = nfs3_fh_build_indexed_root_fh (ms->nfsx->children, mntxl);
- goto xmit_res;
- }
+ /* Clear the mountid */
+ gf_uuid_clear(mountid);
- __mnt3_get_volume_id (ms, mntxl, volumeid);
- fh = nfs3_fh_build_uuid_root_fh (volumeid);
+ hashed_path = SuperFastHash(path, strlen(path));
+ if (hashed_path == 1) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_HASH_PATH_FAIL,
+ "failed to hash path: %s", path);
+ goto out;
+ }
-xmit_res:
- nfs3_fh_to_str (&fh, fhstr);
- gf_log (GF_MNT, GF_LOG_DEBUG, "MNT reply: fh %s, status: %d", fhstr,
- status);
- if (op_ret == 0) {
- svc = rpcsvc_request_service (req);
- autharrlen = rpcsvc_auth_array (svc, mntxl->name, autharr,
- 10);
- }
+ memcpy(mountid, &hashed_path, sizeof(hashed_path));
+ ret = 0;
+out:
+ return ret;
+}
- res = mnt3svc_set_mountres3 (status, &fh, autharr, autharrlen);
- mnt3svc_submit_reply (req, (void *)&res,
- (mnt3_serializer)xdr_serialize_mountres3);
+int
+__mnt3_get_mount_id(xlator_t *mntxl, uuid_t mountid)
+{
+ int ret = -1;
+ uint32_t hashed_path = 0;
- return 0;
+ /* first clear the mountid */
+ gf_uuid_clear(mountid);
+
+ hashed_path = SuperFastHash(mntxl->name, strlen(mntxl->name));
+ if (hashed_path == 1) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_HASH_XLATOR_FAIL,
+ "failed to hash xlator name: %s", mntxl->name);
+ goto out;
+ }
+
+ memcpy(mountid, &hashed_path, sizeof(hashed_path));
+ ret = 0;
+out:
+ return ret;
}
+int32_t
+mnt3svc_lookup_mount_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)
+{
+ mountres3 res = {
+ 0,
+ };
+ rpcsvc_request_t *req = NULL;
+ struct nfs3_fh fh = {
+ {0},
+ };
+ struct mount3_state *ms = NULL;
+ mountstat3 status = 0;
+ int autharr[10];
+ int autharrlen = 0;
+ rpcsvc_t *svc = NULL;
+ xlator_t *mntxl = NULL;
+ uuid_t volumeid = {
+ 0,
+ };
+ char *path = NULL;
+ uuid_t mountid = {
+ 1,
+ };
+ char fhstr[1536];
+ int alloclen = 0;
+
+ req = (rpcsvc_request_t *)frame->local;
+
+ if (!req)
+ return -1;
+
+ mntxl = (xlator_t *)cookie;
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "mount state not found");
+ op_ret = -1;
+ op_errno = EINVAL;
+ }
+
+ if (op_ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, op_errno, NFS_MSG_LOOKUP_MNT_ERROR,
+ "error=%s", strerror(op_errno));
+ status = mnt3svc_errno_to_mnterr(op_errno);
+ }
+ if (status != MNT3_OK)
+ goto xmit_res;
+
+ alloclen = strlen(mntxl->name) + 2;
+ path = GF_MALLOC(alloclen, gf_nfs_mt_char);
+ if (!path) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed.");
+ goto xmit_res;
+ }
+
+ snprintf(path, alloclen, "/%s", mntxl->name);
+ mnt3svc_update_mountlist(ms, req, path, NULL);
+ GF_FREE(path);
+ if (gf_nfs_dvm_off(nfs_state(ms->nfsx))) {
+ fh = nfs3_fh_build_indexed_root_fh(ms->nfsx->children, mntxl);
+ goto xmit_res;
+ }
+
+ __mnt3_get_mount_id(mntxl, mountid);
+ __mnt3_get_volume_id(ms, mntxl, volumeid);
+ fh = nfs3_fh_build_uuid_root_fh(volumeid, mountid);
+
+xmit_res:
+ nfs3_fh_to_str(&fh, fhstr, sizeof(fhstr));
+ gf_msg_debug(GF_MNT, 0, "MNT reply: fh %s, status: %d", fhstr, status);
+ if (op_ret == 0) {
+ svc = rpcsvc_request_service(req);
+ autharrlen = rpcsvc_auth_array(svc, mntxl->name, autharr, 10);
+ }
+
+ res = mnt3svc_set_mountres3(status, &fh, autharr, autharrlen);
+ mnt3svc_submit_reply(req, (void *)&res,
+ (mnt3_serializer)xdr_serialize_mountres3);
+
+ return 0;
+}
int
-mnt3_match_dirpath_export (char *expname, char *dirpath)
+mnt3_match_dirpath_export(const char *expname, const char *dirpath,
+ gf_boolean_t export_parsing_match)
{
- int ret = 0;
- int dlen = 0;
+ int ret = 0;
+ size_t dlen;
+ char *fullpath = NULL;
+ char *second_slash = NULL;
+ char *dirdup = NULL;
- if ((!expname) || (!dirpath))
- return 0;
+ if ((!expname) || (!dirpath))
+ return 0;
- /* Some clients send a dirpath for mount that includes the slash at the
- * end. String compare for searching the export will fail because our
- * exports list does not include that slash. Remove the slash to
- * compare.
- */
- dlen = strlen (dirpath);
- if (dirpath [dlen - 1] == '/')
- dirpath [dlen - 1] = '\0';
+ dirdup = strdupa(dirpath);
- if (dirpath[0] != '/')
- expname++;
+ /* Some clients send a dirpath for mount that includes the slash at the
+ * end. String compare for searching the export will fail because our
+ * exports list does not include that slash. Remove the slash to
+ * compare.
+ */
+ dlen = strlen(dirdup);
+ if (dlen && dirdup[dlen - 1] == '/')
+ dirdup[dlen - 1] = '\0';
- if (strcmp (expname, dirpath) == 0)
- ret = 1;
+ /* Here we try to match fullpaths with export names */
+ fullpath = dirdup;
- return ret;
-}
+ if (export_parsing_match) {
+ if (dirdup[0] == '/')
+ fullpath = dirdup + 1;
+ second_slash = strchr(fullpath, '/');
+ if (second_slash)
+ *second_slash = '\0';
+ }
-int
-mnt3svc_mount_inode (rpcsvc_request_t *req, struct mount3_state *ms,
- xlator_t * xl, inode_t *exportinode)
-{
- int ret = -EFAULT;
- nfs_user_t nfu = {0, };
- loc_t exportloc = {0, };
+ /* The export name begins with a slash so move it forward by one
+ * to ignore the slash when we want to compare the fullpath and
+ * export.
+ */
+ if (fullpath[0] != '/')
+ expname++;
- if ((!req) || (!xl) || (!ms) || (!exportinode))
- return ret;
+ if (strcmp(expname, fullpath) == 0)
+ ret = 1;
- ret = nfs_inode_loc_fill (exportinode, &exportloc, NFS_RESOLVE_EXIST);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Loc fill failed for export inode"
- ": gfid %s, volume: %s",
- uuid_utoa (exportinode->gfid), xl->name);
- goto err;
- }
+ return ret;
+}
- /* To service the mount request, all we need to do
- * is to send a lookup fop that returns the stat
- * for the root of the child volume. This is
- * used to build the root fh sent to the client.
- */
- nfs_request_user_init (&nfu, req);
- ret = nfs_lookup (ms->nfsx, xl, &nfu, &exportloc,
- mnt3svc_lookup_mount_cbk, (void *)req);
+int
+mnt3svc_mount_inode(rpcsvc_request_t *req, struct mount3_state *ms,
+ xlator_t *xl, inode_t *exportinode)
+{
+ int ret = -EFAULT;
+ nfs_user_t nfu = {
+ 0,
+ };
+ loc_t exportloc = {
+ 0,
+ };
+
+ if ((!req) || (!xl) || (!ms) || (!exportinode))
+ return ret;
- nfs_loc_wipe (&exportloc);
+ ret = nfs_inode_loc_fill(exportinode, &exportloc, NFS_RESOLVE_EXIST);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_INODE_LOC_FILL_ERROR,
+ "Loc fill failed for export inode"
+ ": gfid %s, volume: %s",
+ uuid_utoa(exportinode->gfid), xl->name);
+ goto err;
+ }
+
+ /* To service the mount request, all we need to do
+ * is to send a lookup fop that returns the stat
+ * for the root of the child volume. This is
+ * used to build the root fh sent to the client.
+ */
+ nfs_request_user_init(&nfu, req);
+ ret = nfs_lookup(ms->nfsx, xl, &nfu, &exportloc, mnt3svc_lookup_mount_cbk,
+ (void *)req);
+
+ nfs_loc_wipe(&exportloc);
err:
- return ret;
+ return ret;
}
-
/* For a volume mount request, we just have to create loc on the root inode,
* and send a lookup. In the lookup callback the mount reply is send along with
* the file handle.
*/
int
-mnt3svc_volume_mount (rpcsvc_request_t *req, struct mount3_state *ms,
- struct mnt3_export *exp)
-{
- inode_t *exportinode = NULL;
- int ret = -EFAULT;
- uuid_t rootgfid = {0, };
-
- if ((!req) || (!exp) || (!ms))
- return ret;
-
- rootgfid[15] = 1;
- exportinode = inode_find (exp->vol->itable, rootgfid);
- if (!exportinode) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to get root inode");
- ret = -ENOENT;
- goto err;
- }
+mnt3svc_volume_mount(rpcsvc_request_t *req, struct mount3_state *ms,
+ struct mnt3_export *exp)
+{
+ inode_t *exportinode = NULL;
+ int ret = -EFAULT;
+ static uuid_t rootgfid = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+
+ if ((!req) || (!exp) || (!ms))
+ return ret;
+
+ exportinode = inode_find(exp->vol->itable, rootgfid);
+ if (!exportinode) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOENT, NFS_MSG_GET_ROOT_INODE_FAIL,
+ "Failed to get root inode");
+ ret = -ENOENT;
+ goto err;
+ }
- ret = mnt3svc_mount_inode (req, ms, exp->vol, exportinode);
- inode_unref (exportinode);
+ ret = mnt3svc_mount_inode(req, ms, exp->vol, exportinode);
+ inode_unref(exportinode);
err:
- return ret;
+ return ret;
}
-
/* The catch with directory exports is that the first component of the export
* name will be the name of the volume.
* Any lookup that needs to be performed to build the directory's file handle
@@ -411,184 +947,407 @@ err:
* we need to strip out the volume name first.
*/
char *
-__volume_subdir (char *dirpath, char **volname)
+mnt3_get_volume_subdir(char *dirpath, char **volname)
{
- char *subdir = NULL;
- int volname_len = 0;
-
- if (!dirpath)
- return NULL;
-
- if (dirpath[0] == '/')
- dirpath++;
-
- subdir = index (dirpath, (int)'/');
- if (!subdir)
- goto out;
+ /* subdir points to the first / after the volume name while dirpath
+ * points to the first char of the volume name.
+ */
+ char *subdir = NULL;
+ int volname_len = 0;
+ static char *root = "/";
+
+ /* all callers are expected to pass a valid *dirpath */
+ GF_ASSERT(dirpath);
+
+ if (dirpath[0] == '/')
+ dirpath++;
+
+ subdir = index(dirpath, (int)'/');
+ if (!subdir) {
+ subdir = root;
+ volname_len = strlen(dirpath);
+ } else {
+ volname_len = subdir - dirpath;
+ }
- if (!volname)
- goto out;
+ if (!volname)
+ goto out;
- if (!*volname)
- goto out;
+ if (!*volname)
+ goto out;
- /* subdir points to the first / after the volume name while dirpath
- * points to the first char of the volume name.
- */
- volname_len = subdir - dirpath;
- strncpy (*volname, dirpath, volname_len);
- *(*volname + volname_len) = '\0';
+ strncpy(*volname, dirpath, volname_len);
+ *(*volname + volname_len) = '\0';
out:
- return subdir;
+ return subdir;
}
-
void
-mnt3_resolve_state_wipe (mnt3_resolve_t *mres)
+mnt3_resolve_state_wipe(mnt3_resolve_t *mres)
{
- if (!mres)
- return;
-
- nfs_loc_wipe (&mres->resolveloc);
- GF_FREE (mres);
+ if (!mres)
+ return;
+ nfs_loc_wipe(&mres->resolveloc);
+ GF_FREE(mres);
}
-
/* Sets up the component argument to contain the next component in the path and
* sets up path as an absolute path starting from the next component.
*/
-char *
-__setup_next_component (char *path, char *component)
+static char *
+setup_next_component(char *path, size_t plen, char *component, size_t clen)
{
- char *comp = NULL;
- char *nextcomp = NULL;
+ char *comp = NULL;
+ char *nextcomp = NULL;
- if ((!path) || (!component))
- return NULL;
+ if ((!path) || (!component))
+ return NULL;
- strcpy (component, path);
- comp = index (component, (int)'/');
- if (!comp)
- goto err;
+ strncpy(component, path, clen);
+ comp = index(component, (int)'/');
+ if (!comp)
+ goto err;
- comp++;
- nextcomp = index (comp, (int)'/');
- if (nextcomp) {
- strcpy (path, nextcomp);
- *nextcomp = '\0';
- } else
- path[0] = '\0';
+ comp++;
+ nextcomp = index(comp, (int)'/');
+ if (nextcomp) {
+ strncpy(path, nextcomp, plen);
+ *nextcomp = '\0';
+ } else
+ path[0] = '\0';
err:
- return comp;
+ return comp;
}
int32_t
-mnt3_resolve_subdir_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);
+mnt3_resolve_subdir_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);
+
+int32_t
+mnt3_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, dict_t *xdata);
/* There are multiple components in the directory export path and each one
* needs to be looked up one after the other.
*/
int
-__mnt3_resolve_export_subdir_comp (mnt3_resolve_t *mres)
-{
- char dupsubdir[MNTPATHLEN];
- char *nextcomp = NULL;
- int ret = -EFAULT;
- nfs_user_t nfu = {0, };
- uuid_t gfid = {0, };
-
- if (!mres)
- return ret;
-
- nextcomp = __setup_next_component (mres->remainingdir, dupsubdir);
- if (!nextcomp)
- goto err;
-
- /* Wipe the contents of the previous component */
- uuid_copy (gfid, mres->resolveloc.inode->gfid);
- nfs_loc_wipe (&mres->resolveloc);
- ret = nfs_entry_loc_fill (mres->exp->vol->itable, gfid, nextcomp,
- &mres->resolveloc, NFS_RESOLVE_CREATE);
- if ((ret < 0) && (ret != -2)) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve and create "
- "inode: parent gfid %s, entry %s",
- uuid_utoa (mres->resolveloc.inode->gfid), nextcomp);
- ret = -EFAULT;
- goto err;
- }
+__mnt3_resolve_export_subdir_comp(mnt3_resolve_t *mres)
+{
+ char dupsubdir[MNTPATHLEN];
+ char *nextcomp = NULL;
+ int ret = -EFAULT;
+ nfs_user_t nfu = {
+ 0,
+ };
+ uuid_t gfid = {
+ 0,
+ };
+
+ if (!mres)
+ return ret;
- nfs_request_user_init (&nfu, mres->req);
- ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu,
- &mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
+ nextcomp = setup_next_component(mres->remainingdir,
+ sizeof(mres->remainingdir), dupsubdir,
+ sizeof(dupsubdir));
+ if (!nextcomp)
+ goto err;
+
+ /* Wipe the contents of the previous component */
+ gf_uuid_copy(gfid, mres->resolveloc.inode->gfid);
+ nfs_loc_wipe(&mres->resolveloc);
+ ret = nfs_entry_loc_fill(mres->mstate->nfsx, mres->exp->vol->itable, gfid,
+ nextcomp, &mres->resolveloc, NFS_RESOLVE_CREATE,
+ NULL);
+ if ((ret < 0) && (ret != -2)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EFAULT, NFS_MSG_RESOLVE_INODE_FAIL,
+ "Failed to resolve and "
+ "create inode: parent gfid %s, entry %s",
+ uuid_utoa(gfid), nextcomp);
+ ret = -EFAULT;
+ goto err;
+ }
+
+ nfs_request_user_init(&nfu, mres->req);
+ if (IA_ISLNK(mres->resolveloc.inode->ia_type)) {
+ ret = nfs_readlink(mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_readlink_cbk, mres);
+ gf_msg_debug(GF_MNT, 0,
+ "Symlink found , need to resolve"
+ " into directory handle");
+ goto err;
+ }
+ ret = nfs_lookup(mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
err:
- return ret;
+ return ret;
}
+int
+__mnt3_resolve_subdir(mnt3_resolve_t *mres);
+
+/*
+ * Per the AFR2 comments, this function performs the "fresh" lookup
+ * by deleting the inode from cache and calling __mnt3_resolve_subdir
+ * again.
+ */
+int
+__mnt3_fresh_lookup(mnt3_resolve_t *mres)
+{
+ inode_unlink(mres->resolveloc.inode, mres->resolveloc.parent,
+ mres->resolveloc.name);
+ strncpy(mres->remainingdir, mres->resolveloc.path,
+ strlen(mres->resolveloc.path));
+ nfs_loc_wipe(&mres->resolveloc);
+ return __mnt3_resolve_subdir(mres);
+}
int32_t
-mnt3_resolve_subdir_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)
+mnt3_resolve_subdir_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)
{
- mnt3_resolve_t *mres = NULL;
- mountstat3 mntstat = MNT3ERR_SERVERFAULT;
- struct nfs3_fh fh = {{0}, };
- int autharr[10];
- int autharrlen = 0;
- rpcsvc_t *svc = NULL;
- mountres3 res = {0, };
- xlator_t *mntxl = NULL;
-
- mres = frame->local;
- mntxl = (xlator_t *)cookie;
- if (op_ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "path=%s (%s)",
- mres->resolveloc.path, strerror (op_errno));
- mntstat = mnt3svc_errno_to_mnterr (op_errno);
- goto err;
+ mnt3_resolve_t *mres = NULL;
+ mountstat3 mntstat = MNT3ERR_SERVERFAULT;
+ struct nfs3_fh fh = {
+ {0},
+ };
+ int autharr[10];
+ int autharrlen = 0;
+ rpcsvc_t *svc = NULL;
+ mountres3 res = {
+ 0,
+ };
+ xlator_t *mntxl = NULL;
+ char *path = NULL;
+ struct mount3_state *ms = NULL;
+ int authcode = 0;
+ char *authorized_host = NULL;
+ char *authorized_path = NULL;
+ inode_t *linked_inode = NULL;
+
+ mres = frame->local;
+ ms = mres->mstate;
+ mntxl = (xlator_t *)cookie;
+ if (op_ret == -1 && op_errno == ESTALE) {
+ /* Nuke inode from cache and try the LOOKUP
+ * request again. */
+ return __mnt3_fresh_lookup(mres);
+ } else if (op_ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, op_errno, NFS_MSG_RESOLVE_SUBDIR_FAIL,
+ "path=%s (%s)", mres->resolveloc.path, strerror(op_errno));
+ mntstat = mnt3svc_errno_to_mnterr(op_errno);
+ goto err;
+ }
+
+ linked_inode = inode_link(mres->resolveloc.inode, mres->resolveloc.parent,
+ mres->resolveloc.name, buf);
+
+ if (linked_inode)
+ nfs_fix_generation(this, linked_inode);
+
+ nfs3_fh_build_child_fh(&mres->parentfh, buf, &fh);
+ if (strlen(mres->remainingdir) <= 0) {
+ int alloclen;
+ op_ret = -1;
+ mntstat = MNT3_OK;
+
+ /* Construct the full path */
+ int resolveloc_path_len = strlen(mres->resolveloc.path);
+ alloclen = strlen(mres->exp->expname) + resolveloc_path_len + 1;
+ mres->exp->fullpath = GF_MALLOC(alloclen, gf_nfs_mt_char);
+ if (!mres->exp->fullpath) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed.");
+ goto err;
+ }
+ snprintf(mres->exp->fullpath, alloclen, "%s%s", mres->exp->expname,
+ mres->resolveloc.path);
+
+ /* Check if this path is authorized to be mounted */
+ authcode = mnt3_authenticate_request(
+ ms, mres->req, NULL, NULL, mres->exp->fullpath, &authorized_path,
+ &authorized_host, FALSE);
+ if (authcode != 0) {
+ mntstat = MNT3ERR_ACCES;
+ gf_msg_debug(GF_MNT, 0, "Client mount not allowed");
+ op_ret = -1;
+ goto err;
}
- inode_link (mres->resolveloc.inode, mres->resolveloc.parent,
- mres->resolveloc.name, buf);
-
- nfs3_fh_build_child_fh (&mres->parentfh, buf, &fh);
- if (strlen (mres->remainingdir) <= 0) {
- op_ret = -1;
- mntstat = MNT3_OK;
- mnt3svc_update_mountlist (mres->mstate, mres->req,
- mres->exp->expname);
- goto err;
+ alloclen = strlen(mres->exp->vol->name) + resolveloc_path_len + 2;
+ path = GF_MALLOC(alloclen, gf_nfs_mt_char);
+ if (!path) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
}
+ /* Build mountid from the authorized path and stick it in the
+ * filehandle that will get passed back to the client
+ */
+ __mnt3_build_mountid_from_path(authorized_path, fh.mountid);
+
+ snprintf(path, alloclen, "/%s%s", mres->exp->vol->name,
+ mres->resolveloc.path);
+ mnt3svc_update_mountlist(mres->mstate, mres->req, path,
+ mres->exp->fullpath);
+ GF_FREE(path);
+ } else {
mres->parentfh = fh;
- op_ret = __mnt3_resolve_export_subdir_comp (mres);
+ op_ret = __mnt3_resolve_export_subdir_comp(mres);
if (op_ret < 0)
- mntstat = mnt3svc_errno_to_mnterr (-op_ret);
+ mntstat = mnt3svc_errno_to_mnterr(-op_ret);
+ }
err:
- if (op_ret == -1) {
- gf_log (GF_MNT, GF_LOG_DEBUG, "Mount reply status: %d",
- mntstat);
- svc = rpcsvc_request_service (mres->req);
- autharrlen = rpcsvc_auth_array (svc, mntxl->name, autharr,
- 10);
-
- res = mnt3svc_set_mountres3 (mntstat, &fh, autharr, autharrlen);
- mnt3svc_submit_reply (mres->req, (void *)&res,
- (mnt3_serializer)xdr_serialize_mountres3);
- mnt3_resolve_state_wipe (mres);
- }
+ if (op_ret == -1) {
+ gf_msg_debug(GF_MNT, 0, "Mount reply status: %d", mntstat);
+ svc = rpcsvc_request_service(mres->req);
+ autharrlen = rpcsvc_auth_array(svc, mntxl->name, autharr, 10);
- return 0;
+ res = mnt3svc_set_mountres3(mntstat, &fh, autharr, autharrlen);
+ mnt3svc_submit_reply(mres->req, (void *)&res,
+ (mnt3_serializer)xdr_serialize_mountres3);
+ mnt3_resolve_state_wipe(mres);
+ }
+
+ GF_FREE(authorized_path);
+ GF_FREE(authorized_host);
+
+ return 0;
}
+/* This function resolves symbolic link into directory path from
+ * the mount and restart the parsing process from the beginning
+ *
+ * Note : Path specified in the symlink should be relative to the
+ * symlink, because that is the one which is consistent through
+ * out the file system.
+ * If the symlink resolves into another symlink ,then same process
+ * will be repeated.
+ * If symbolic links points outside the file system are not considered
+ * here.
+ *
+ * TODO : 1.) This function cannot handle symlinks points to path which
+ * goes out of the filesystem and comes backs again to same.
+ * For example, consider vol is exported volume.It contains
+ * dir,
+ * symlink1 which points to ../vol/dir,
+ * symlink2 which points to ../mnt/../vol/dir,
+ * symlink1 and symlink2 are not handled right now.
+ *
+ * 2.) udp mount routine is much simpler from tcp routine and resolves
+ * symlink directly.May be ,its better we change this routine
+ * similar to udp
+ */
+int32_t
+mnt3_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, dict_t *xdata)
+{
+ mnt3_resolve_t *mres = NULL;
+ int ret = -EFAULT;
+ char *real_loc = NULL;
+ size_t path_len = 0;
+ size_t parent_path_len = 0;
+ char *parent_path = NULL;
+ char *absolute_path = NULL;
+ char *relative_path = NULL;
+ int mntstat = 0;
+
+ GF_ASSERT(frame);
+
+ mres = frame->local;
+ if (!mres || !path || (path[0] == '/') || (op_ret < 0))
+ goto mnterr;
+
+ /* Finding current location of symlink */
+ parent_path_len = strlen(mres->resolveloc.path) -
+ strlen(mres->resolveloc.name);
+ parent_path = gf_strndup(mres->resolveloc.path, parent_path_len);
+ if (!parent_path) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+
+ relative_path = gf_strdup(path);
+ if (!relative_path) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+ /* Resolving into absolute path */
+ ret = gf_build_absolute_path(parent_path, relative_path, &absolute_path);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SYMLINK_ERROR,
+ "Cannot resolve symlink, path is out of boundary "
+ "from current location %s and with relative path "
+ "%s pointed by symlink",
+ parent_path, relative_path);
+
+ goto mnterr;
+ }
+
+ /* Building the actual mount path to be mounted */
+ path_len = strlen(mres->exp->vol->name) + strlen(absolute_path) +
+ strlen(mres->remainingdir) + 1;
+ real_loc = GF_MALLOC(path_len, gf_nfs_mt_char);
+ if (!real_loc) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+ snprintf(real_loc, path_len, "%s%s", mres->exp->vol->name, absolute_path);
+ gf_path_strip_trailing_slashes(real_loc);
+
+ /* There may entries after symlink in the mount path,
+ * we should include remaining entries too */
+ if (strlen(mres->remainingdir) > 0)
+ strcat(real_loc, mres->remainingdir);
+
+ gf_msg_debug(GF_MNT, 0,
+ "Resolved path is : %s%s "
+ "and actual mount path is %s",
+ absolute_path, mres->remainingdir, real_loc);
+
+ /* After the resolving the symlink , parsing should be done
+ * for the populated mount path
+ */
+ ret = mnt3_parse_dir_exports(mres->req, mres->mstate, real_loc, _gf_true);
+
+ if (ret) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_RESOLVE_ERROR,
+ "Resolved into an unknown path %s%s "
+ "from the current location of symlink %s",
+ absolute_path, mres->remainingdir, parent_path);
+ }
+
+ GF_FREE(real_loc);
+ GF_FREE(absolute_path);
+ GF_FREE(parent_path);
+ GF_FREE(relative_path);
+
+ return ret;
+mnterr:
+ if (mres) {
+ mntstat = mnt3svc_errno_to_mnterr(-ret);
+ mnt3svc_mnt_error_reply(mres->req, mntstat);
+ } else
+ gf_msg(GF_MNT, GF_LOG_CRITICAL, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "mres == NULL, this should *never* happen");
+ if (absolute_path)
+ GF_FREE(absolute_path);
+ if (parent_path)
+ GF_FREE(parent_path);
+ if (relative_path)
+ GF_FREE(relative_path);
+ return ret;
+}
/* We will always have to perform a hard lookup on all the components of a
* directory export for a mount request because in the mount reply we need the
@@ -602,1326 +1361,2907 @@ err:
* of the exported directory can be built.
*/
int
-__mnt3_resolve_subdir (mnt3_resolve_t *mres)
-{
- char dupsubdir[MNTPATHLEN];
- char *firstcomp = NULL;
- int ret = -EFAULT;
- nfs_user_t nfu = {0, };
- uuid_t rootgfid = {0, };
-
- if (!mres)
- return ret;
-
- firstcomp = __setup_next_component (mres->remainingdir, dupsubdir);
- if (!firstcomp)
- goto err;
-
- rootgfid[15] = 1;
- ret = nfs_entry_loc_fill (mres->exp->vol->itable, rootgfid, firstcomp,
- &mres->resolveloc, NFS_RESOLVE_CREATE);
- if ((ret < 0) && (ret != -2)) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve and create "
- "inode for volume root: %s", mres->exp->vol->name);
- ret = -EFAULT;
- goto err;
- }
+__mnt3_resolve_subdir(mnt3_resolve_t *mres)
+{
+ char dupsubdir[MNTPATHLEN];
+ char *firstcomp = NULL;
+ int ret = -EFAULT;
+ nfs_user_t nfu = {
+ 0,
+ };
+ static uuid_t rootgfid = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+
+ if (!mres)
+ return ret;
- nfs_request_user_init (&nfu, mres->req);
- ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu,
- &mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
+ firstcomp = setup_next_component(mres->remainingdir,
+ sizeof(mres->remainingdir), dupsubdir,
+ sizeof(dupsubdir));
+ if (!firstcomp)
+ goto err;
+
+ ret = nfs_entry_loc_fill(mres->mstate->nfsx, mres->exp->vol->itable,
+ rootgfid, firstcomp, &mres->resolveloc,
+ NFS_RESOLVE_CREATE, NULL);
+ if ((ret < 0) && (ret != -2)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EFAULT, NFS_MSG_RESOLVE_INODE_FAIL,
+ "Failed to resolve and "
+ "create inode for volume root: %s",
+ mres->exp->vol->name);
+ ret = -EFAULT;
+ goto err;
+ }
+
+ nfs_request_user_init(&nfu, mres->req);
+ if (IA_ISLNK(mres->resolveloc.inode->ia_type)) {
+ ret = nfs_readlink(mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_readlink_cbk, mres);
+ gf_msg_debug(GF_MNT, 0,
+ "Symlink found , need to resolve "
+ "into directory handle");
+ goto err;
+ }
+ ret = nfs_lookup(mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
err:
- return ret;
+ return ret;
}
+static gf_boolean_t
+mnt3_match_subnet_v4(struct addrinfo *ai, uint32_t saddr, uint32_t mask)
+{
+ for (; ai; ai = ai->ai_next) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+
+ if (sin->sin_family != AF_INET)
+ continue;
+
+ if (mask_match(saddr, sin->sin_addr.s_addr, mask))
+ return _gf_true;
+ }
+
+ return _gf_false;
+}
+/**
+ * This function will verify if the client is allowed to mount
+ * the directory or not. Client's IP address will be compared with
+ * allowed IP list or range present in mnt3_export structure.
+ *
+ * @param client_addr - This structure contains client's IP address.
+ * @param export - mnt3_export structure. Contains allowed IP list/range.
+ *
+ * @return 0 - on Success and -EACCES on failure.
+ *
+ * TODO: Support IPv6 subnetwork
+ */
int
-mnt3_resolve_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
- struct mnt3_export *exp, char *subdir)
+mnt3_verify_auth(struct sockaddr_in *client_addr, struct mnt3_export *export)
{
- mnt3_resolve_t *mres = NULL;
- int ret = -EFAULT;
- struct nfs3_fh pfh = GF_NFS3FH_STATIC_INITIALIZER;
+ int retvalue = -EACCES;
+ int ret = 0;
+ struct host_auth_spec *host = NULL;
+ struct sockaddr_in *allowed_addr = NULL;
+ struct addrinfo *allowed_addrinfo = NULL;
+
+ struct addrinfo hint = {
+ .ai_family = AF_INET,
+ .ai_protocol = (int)IPPROTO_TCP,
+ .ai_flags = AI_CANONNAME,
+ };
+
+ /* Sanity check */
+ if ((NULL == client_addr) || (NULL == export) ||
+ (NULL == export->hostspec)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid argument");
+ return retvalue;
+ }
+
+ host = export->hostspec;
+
+ /*
+ * Currently IPv4 subnetwork is supported i.e. AF_INET.
+ * TODO: IPv6 subnetwork i.e. AF_INET6.
+ */
+ if (client_addr->sin_family != AF_INET) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EAFNOSUPPORT, NFS_MSG_UNSUPPORTED_VERSION,
+ "Only IPv4 is supported for subdir-auth");
+ return retvalue;
+ }
+
+ /* Try to see if the client IP matches the allowed IP list.*/
+ while (NULL != host) {
+ GF_ASSERT(host->host_addr);
+
+ if (NULL != allowed_addrinfo) {
+ freeaddrinfo(allowed_addrinfo);
+ allowed_addrinfo = NULL;
+ }
- if ((!req) || (!ms) || (!exp) || (!subdir))
- return ret;
+ /* Get the addrinfo for the allowed host (host_addr). */
+ ret = getaddrinfo(host->host_addr, NULL, &hint, &allowed_addrinfo);
+ if (0 != ret) {
+ /*
+ * getaddrinfo() FAILED for the host IP addr. Continue
+ * to search other allowed hosts in the hostspec list.
+ */
+ gf_msg_debug(GF_MNT, 0, "getaddrinfo: %s\n", gai_strerror(ret));
+ host = host->next;
+ continue;
+ }
- mres = GF_CALLOC (1, sizeof (mnt3_resolve_t), gf_nfs_mt_mnt3_resolve);
- if (!mres) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
- goto err;
+ allowed_addr = (struct sockaddr_in *)(allowed_addrinfo->ai_addr);
+ if (NULL == allowed_addr) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid structure");
+ break;
}
- mres->exp = exp;
- mres->mstate = ms;
- mres->req = req;
- strcpy (mres->remainingdir, subdir);
- if (gf_nfs_dvm_off (nfs_state (ms->nfsx)))
- pfh = nfs3_fh_build_indexed_root_fh (mres->mstate->nfsx->children, mres->exp->vol);
- else
- pfh = nfs3_fh_build_uuid_root_fh (exp->volumeid);
-
- mres->parentfh = pfh;
- ret = __mnt3_resolve_subdir (mres);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve export dir: %s"
- , mres->exp->expname);
- GF_FREE (mres);
+ /* Check if the network addr of both IPv4 socket match */
+ if (mnt3_match_subnet_v4(allowed_addrinfo, client_addr->sin_addr.s_addr,
+ host->netmask)) {
+ retvalue = 0;
+ break;
}
-err:
- return ret;
-}
+ /* No match yet, continue the search */
+ host = host->next;
+ }
+ /* FREE the dynamic memory allocated by getaddrinfo() */
+ if (NULL != allowed_addrinfo) {
+ freeaddrinfo(allowed_addrinfo);
+ }
+
+ return retvalue;
+}
int
-mnt3_resolve_export_subdir (rpcsvc_request_t *req, struct mount3_state *ms,
- struct mnt3_export *exp)
+mnt3_resolve_subdir(rpcsvc_request_t *req, struct mount3_state *ms,
+ struct mnt3_export *exp, char *subdir,
+ gf_boolean_t send_reply)
{
- char *volume_subdir = NULL;
- int ret = -EFAULT;
+ mnt3_resolve_t *mres = NULL;
+ int ret = -EFAULT;
+ struct nfs3_fh pfh = GF_NFS3FH_STATIC_INITIALIZER;
+ struct sockaddr_in *sin = NULL;
- if ((!req) || (!ms) || (!exp))
- return ret;
+ if ((!req) || (!ms) || (!exp) || (!subdir))
+ return ret;
- volume_subdir = __volume_subdir (exp->expname, NULL);
- if (!volume_subdir)
- goto err;
+ sin = (struct sockaddr_in *)(&(req->trans->peerinfo.sockaddr));
- ret = mnt3_resolve_subdir (req, ms, exp, volume_subdir);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve export dir: %s"
- , exp->expname);
- goto err;
+ /* Need to check AUTH */
+ if (NULL != exp->hostspec) {
+ ret = mnt3_verify_auth(sin, exp);
+ if (0 != ret) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EACCES, NFS_MSG_AUTH_VERIFY_FAILED,
+ "AUTH verification failed");
+ return ret;
}
+ }
+
+ /* no reply is needed (WebNFS permissions checking), just return */
+ if (!send_reply)
+ return 0; /* no error, mnt3_verify_auth() allowed it */
+
+ mres = GF_CALLOC(1, sizeof(mnt3_resolve_t), gf_nfs_mt_mnt3_resolve);
+ if (!mres) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
+ }
+
+ mres->exp = exp;
+ mres->mstate = ms;
+ mres->req = req;
+
+ snprintf(mres->remainingdir, MNTPATHLEN, "%s", subdir);
+ gf_path_strip_trailing_slashes(mres->remainingdir);
+
+ if (gf_nfs_dvm_off(nfs_state(ms->nfsx)))
+ pfh = nfs3_fh_build_indexed_root_fh(mres->mstate->nfsx->children,
+ mres->exp->vol);
+ else
+ pfh = nfs3_fh_build_uuid_root_fh(exp->volumeid, exp->mountid);
+
+ mres->parentfh = pfh;
+ ret = __mnt3_resolve_subdir(mres);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SUBDIR_FAIL,
+ "Failed to resolve export dir: %s", mres->exp->expname);
+ GF_FREE(mres);
+ }
err:
- return ret;
+ return ret;
}
-
int
-mnt3svc_mount (rpcsvc_request_t *req, struct mount3_state *ms,
- struct mnt3_export *exp)
+mnt3_resolve_export_subdir(rpcsvc_request_t *req, struct mount3_state *ms,
+ struct mnt3_export *exp)
{
- int ret = -EFAULT;
+ char *volume_subdir = NULL;
+ int ret = -EFAULT;
- if ((!req) || (!ms) || (!exp))
- return ret;
+ if ((!req) || (!ms) || (!exp))
+ return ret;
- if (exp->exptype == MNT3_EXPTYPE_VOLUME)
- ret = mnt3svc_volume_mount (req, ms, exp);
- else if (exp->exptype == MNT3_EXPTYPE_DIR)
- ret = mnt3_resolve_export_subdir (req, ms, exp);
+ volume_subdir = mnt3_get_volume_subdir(exp->expname, NULL);
- return ret;
+ ret = mnt3_resolve_subdir(req, ms, exp, volume_subdir, _gf_true);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SUBDIR_FAIL,
+ "Failed to resolve export dir: %s", exp->expname);
+ goto err;
+ }
+
+err:
+ return ret;
}
+int
+mnt3svc_mount(rpcsvc_request_t *req, struct mount3_state *ms,
+ struct mnt3_export *exp)
+{
+ int ret = -EFAULT;
+
+ if ((!req) || (!ms) || (!exp))
+ return ret;
+
+ if (exp->exptype == MNT3_EXPTYPE_VOLUME)
+ ret = mnt3svc_volume_mount(req, ms, exp);
+ else if (exp->exptype == MNT3_EXPTYPE_DIR)
+ ret = mnt3_resolve_export_subdir(req, ms, exp);
+
+ return ret;
+}
/* mnt3_mntpath_to_xlator sets this to 1 if the mount is for a full
-* volume or 2 for a subdir in the volume.
-*/
+ * volume or 2 for a subdir in the volume.
+ *
+ * The parameter 'export_parsing_match' indicates whether this function
+ * is being called by an exports parser or whether it is being called
+ * during mount. The behavior is different since we don't have to resolve
+ * the path when doing the parse.
+ */
struct mnt3_export *
-mnt3_mntpath_to_export (struct mount3_state *ms, char *dirpath)
+mnt3_mntpath_to_export(struct mount3_state *ms, const char *dirpath,
+ gf_boolean_t export_parsing_match)
{
- struct mnt3_export *exp = NULL;
- struct mnt3_export *found = NULL;
+ struct mnt3_export *exp = NULL;
+ struct mnt3_export *found = NULL;
- if ((!ms) || (!dirpath))
- return NULL;
-
- list_for_each_entry (exp, &ms->exportlist, explist) {
+ if ((!ms) || (!dirpath))
+ return NULL;
- /* Search for the an exact match with the volume */
- if (mnt3_match_dirpath_export (exp->expname, dirpath)) {
- found = exp;
- gf_log (GF_MNT, GF_LOG_DEBUG, "Found export volume: "
- "%s", exp->vol->name);
- goto foundexp;
- }
+ LOCK(&ms->mountlock);
+ list_for_each_entry(exp, &ms->exportlist, explist)
+ {
+ /* Search for the an exact match with the volume */
+ if (mnt3_match_dirpath_export(exp->expname, dirpath,
+ export_parsing_match)) {
+ found = exp;
+ gf_msg_debug(GF_MNT, 0,
+ "Found export volume: "
+ "%s",
+ exp->vol->name);
+ goto foundexp;
}
+ }
- gf_log (GF_MNT, GF_LOG_DEBUG, "Export not found");
+ gf_msg_debug(GF_MNT, 0, "Export not found");
foundexp:
- return found;
+ UNLOCK(&ms->mountlock);
+ return found;
}
+static int
+mnt3_check_client_net_check(rpcsvc_t *svc, char *expvol, char *ipaddr,
+ uint16_t port)
+{
+ int ret = RPCSVC_AUTH_REJECT;
+
+ if ((!svc) || (!expvol) || (!ipaddr))
+ goto err;
+
+ ret = rpcsvc_auth_check(svc, expvol, ipaddr);
+ if (ret == RPCSVC_AUTH_REJECT) {
+ gf_msg(GF_MNT, GF_LOG_INFO, 0, NFS_MSG_PEER_NOT_ALLOWED,
+ "Peer %s not allowed", ipaddr);
+ goto err;
+ }
+
+ ret = rpcsvc_transport_privport_check(svc, expvol, port);
+ if (ret == RPCSVC_AUTH_REJECT) {
+ gf_msg(GF_MNT, GF_LOG_INFO, errno, NFS_MSG_PEER_NOT_ALLOWED,
+ "Peer %s rejected. Unprivileged "
+ "port %d not allowed",
+ ipaddr, port);
+ goto err;
+ }
+
+ ret = RPCSVC_AUTH_ACCEPT;
+err:
+ return ret;
+}
int
-mnt3_check_client_net (struct mount3_state *ms, rpcsvc_request_t *req,
- xlator_t *targetxl)
+mnt3_check_client_net_tcp(rpcsvc_request_t *req, char *volname)
+{
+ rpcsvc_t *svc = NULL;
+ rpc_transport_t *trans = NULL;
+ union gf_sock_union sock_union;
+ socklen_t socksize = sizeof(struct sockaddr_in);
+ char peer[RPCSVC_PEER_STRLEN] = {
+ 0,
+ };
+ char *ipaddr = NULL;
+ uint16_t port = 0;
+ int ret = RPCSVC_AUTH_REJECT;
+
+ if ((!req) || (!volname))
+ goto err;
+
+ svc = rpcsvc_request_service(req);
+ trans = rpcsvc_request_transport(req);
+ if ((!svc) || (!trans))
+ goto err;
+
+ ret = rpcsvc_transport_peeraddr(trans, peer, RPCSVC_PEER_STRLEN,
+ &sock_union.storage, socksize);
+ if (ret != 0) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, ENOENT, NFS_MSG_GET_PEER_ADDR_FAIL,
+ "Failed to get peer "
+ "addr: %s",
+ gai_strerror(ret));
+ ret = RPCSVC_AUTH_REJECT;
+ goto err;
+ }
+
+ /* peer[] gets IP:PORT formar, slash the port out */
+ if (!get_host_name((char *)peer, &ipaddr))
+ ipaddr = peer;
+
+ port = ntohs(sock_union.sin.sin_port);
+
+ ret = mnt3_check_client_net_check(svc, volname, ipaddr, port);
+err:
+ return ret;
+}
+
+static int
+mnt3_check_client_net_udp(struct svc_req *req, char *volname, xlator_t *nfsx)
{
+ rpcsvc_t *svc = NULL;
+ struct sockaddr_in *sin = NULL;
+ char ipaddr[INET_ADDRSTRLEN + 1] = {
+ 0,
+ };
+ uint16_t port = 0;
+ int ret = RPCSVC_AUTH_REJECT;
+ struct nfs_state *nfs = NULL;
+
+ if ((!req) || (!volname) || (!nfsx))
+ goto err;
+
+#if !defined(_TIRPC_SVC_H)
+ sin = svc_getcaller(req->rq_xprt);
+#else
+ sin = (struct sockaddr_in *)svc_getcaller(req->rq_xprt);
+ /* TIRPC's svc_getcaller() returns a pointer to a sockaddr_in6, even
+ * though it might actually be an IPv4 address. It ought return a
+ * struct sockaddr and make the caller upcast it to the proper
+ * address family. Sigh.
+ */
+#endif
+ if (!sin)
+ goto err;
+ /* And let's make sure that it's actually an IPv4 address. */
- rpcsvc_t *svc = NULL;
- int ret = -1;
+ GF_ASSERT(sin->sin_family == AF_INET);
- if ((!ms) || (!req) || (!targetxl))
- return -1;
+ (void)inet_ntop(AF_INET, &sin->sin_addr, ipaddr, INET_ADDRSTRLEN);
- svc = rpcsvc_request_service (req);
- ret = rpcsvc_transport_peer_check (svc->options, targetxl->name,
- rpcsvc_request_transport (req));
- if (ret == RPCSVC_AUTH_REJECT) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Peer not allowed");
- goto err;
- }
+ port = ntohs(sin->sin_port);
- ret = rpcsvc_transport_privport_check (svc, targetxl->name,
- rpcsvc_request_transport (req));
- if (ret == RPCSVC_AUTH_REJECT) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Unprivileged port not allowed");
- goto err;
- }
+ nfs = (struct nfs_state *)nfsx->private;
+ if (nfs != NULL)
+ svc = nfs->rpcsvc;
- ret = 0;
+ ret = mnt3_check_client_net_check(svc, volname, ipaddr, port);
err:
- return ret;
+ return ret;
}
-
int
-mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
- char *subdir)
+mnt3_parse_dir_exports(rpcsvc_request_t *req, struct mount3_state *ms,
+ char *path, gf_boolean_t send_reply)
{
- char volname[1024];
- struct mnt3_export *exp = NULL;
- char *volname_ptr = NULL;
- int ret = -1;
-
- if ((!ms) || (!subdir))
- return -1;
-
- volname_ptr = volname;
- subdir = __volume_subdir (subdir, &volname_ptr);
- if (!subdir)
- goto err;
-
- exp = mnt3_mntpath_to_export (ms, volname);
- if (!exp)
- goto err;
-
- ret = mnt3_resolve_subdir (req, ms, exp, subdir);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve export dir: %s"
- , subdir);
- goto err;
+ char volname[1024] = {
+ 0,
+ };
+ struct mnt3_export *exp = NULL;
+ char *volname_ptr = NULL;
+ char *subdir = NULL;
+ int ret = -ENOENT;
+ struct nfs_state *nfs = NULL;
+
+ if ((!ms) || (!path))
+ return -1;
+
+ volname_ptr = volname;
+ subdir = mnt3_get_volume_subdir(path, &volname_ptr);
+
+ /* first try to match the full export/subdir */
+ exp = mnt3_mntpath_to_export(ms, path, _gf_false);
+ if (!exp) {
+ gf_msg_trace(GF_MNT, 0,
+ "Could not find exact matching export "
+ "for path=%s",
+ path);
+ /* if no exact match is found, look for a fallback */
+ exp = mnt3_mntpath_to_export(ms, volname, _gf_true);
+ if (!exp) {
+ gf_msg_trace(GF_MNT, 0,
+ "Could not find export for "
+ "volume %s",
+ volname);
+ goto err;
}
+ }
+ gf_msg_trace(GF_MNT, 0,
+ "volume %s and export %s will be used for "
+ "path %s",
+ exp->vol->name, exp->expname, path);
+
+ nfs = (struct nfs_state *)ms->nfsx->private;
+ if (!nfs)
+ goto err;
+
+ if (!nfs_subvolume_started(nfs, exp->vol)) {
+ gf_msg_debug(GF_MNT, 0, "Volume %s not started", exp->vol->name);
+ goto err;
+ }
+
+ ret = mnt3_check_client_net_tcp(req, exp->vol->name);
+ if (ret == RPCSVC_AUTH_REJECT) {
+ gf_msg_debug(GF_MNT, 0, "Client mount not allowed");
+ ret = -EACCES;
+ goto err;
+ }
+
+ ret = mnt3_resolve_subdir(req, ms, exp, subdir, send_reply);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RESOLVE_SUBDIR_FAIL,
+ "Failed to resolve export dir: %s", subdir);
+ goto err;
+ }
err:
- return ret;
+ return ret;
}
-
int
-mnt3_find_export (rpcsvc_request_t *req, char *path, struct mnt3_export **e)
+mnt3_find_export(rpcsvc_request_t *req, char *path, struct mnt3_export **e)
{
- int ret = -EFAULT;
- struct mount3_state *ms = NULL;
- struct mnt3_export *exp = NULL;
-
- if ((!req) || (!path) || (!e))
- return -1;
-
- ms = (struct mount3_state *) rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount state not present");
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto err;
- }
-
- gf_log (GF_MNT, GF_LOG_DEBUG, "dirpath: %s", path);
- exp = mnt3_mntpath_to_export (ms, path);
- if (exp) {
- ret = 0;
- *e = exp;
- goto err;
- }
+ int ret = -EFAULT;
+ struct mount3_state *ms = NULL;
+ struct mnt3_export *exp = NULL;
+
+ if ((!req) || (!path) || (!e))
+ return -1;
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "Mount state not present");
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ goto err;
+ }
+
+ gf_msg_debug(GF_MNT, 0, "dirpath: %s", path);
+ exp = mnt3_mntpath_to_export(ms, path, _gf_false);
+ if (exp) {
+ ret = 0;
+ *e = exp;
+ goto err;
+ }
- if (!gf_mnt3_export_dirs(ms)) {
- ret = -1;
- goto err;
- }
+ if (!gf_mnt3_export_dirs(ms)) {
+ ret = -1;
+ goto err;
+ }
- ret = mnt3_parse_dir_exports (req, ms, path);
- if (ret == 0) {
- ret = -2;
- goto err;
- }
+ ret = mnt3_parse_dir_exports(req, ms, path, _gf_true);
err:
- return ret;
+ return ret;
}
+/**
+ * _mnt3_get_peer_addr -- Take an rpc request object and return an allocated
+ * peer address. A peer address is host:port.
+ *
+ * @req: An rpc svc request object to extract the peer address from
+ *
+ * @return: success: Pointer to an allocated string containing the peer address
+ * failure: NULL
+ */
+char *
+_mnt3_get_peer_addr(const rpcsvc_request_t *req)
+{
+ rpc_transport_t *trans = NULL;
+ struct sockaddr_storage sastorage = {
+ 0,
+ };
+ char peer[RPCSVC_PEER_STRLEN] = {
+ 0,
+ };
+ char *peerdup = NULL;
+ int ret = 0;
+
+ GF_VALIDATE_OR_GOTO(GF_NFS, req, out);
+
+ trans = rpcsvc_request_transport(req);
+ ret = rpcsvc_transport_peeraddr(trans, peer, RPCSVC_PEER_STRLEN, &sastorage,
+ sizeof(sastorage));
+ if (ret != 0)
+ goto out;
+
+ peerdup = gf_strdup(peer);
+out:
+ return peerdup;
+}
+
+/**
+ * _mnt3_get_host_from_peer -- Take a peer address and get an allocated
+ * hostname. The hostname is the string on the
+ * left side of the colon.
+ *
+ * @peer_addr: The peer address to get a hostname from
+ *
+ * @return: success: Allocated string containing the hostname
+ * failure: NULL
+ *
+ */
+char *
+_mnt3_get_host_from_peer(const char *peer_addr)
+{
+ char *part = NULL;
+ size_t host_len = 0;
+ char *colon = NULL;
+
+ colon = strrchr(peer_addr, ':');
+ if (!colon) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_BAD_PEER, "Bad peer %s",
+ peer_addr);
+ goto out;
+ }
+
+ host_len = colon - peer_addr;
+ if (host_len < RPCSVC_PEER_STRLEN)
+ part = gf_strndup(peer_addr, host_len);
+ else
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_PEER_TOO_LONG,
+ "Peer too long %s", peer_addr);
+out:
+ return part;
+}
+/**
+ * mnt3_check_cached_fh -- Check if FH is cached.
+ *
+ * Calls auxiliary functions based on whether we are checking
+ * a write operation.
+ *
+ */
int
-mnt3svc_mnt (rpcsvc_request_t *req)
-{
- struct iovec pvec = {0, };
- char path[MNTPATHLEN];
- int ret = -1;
- struct mount3_state *ms = NULL;
- mountstat3 mntstat = MNT3ERR_SERVERFAULT;
- struct mnt3_export *exp = NULL;
- struct nfs_state *nfs = NULL;
-
- if (!req)
- return -1;
-
- pvec.iov_base = path;
- pvec.iov_len = MNTPATHLEN;
- ret = xdr_to_mountpath (pvec, req->msg[0]);
- if (ret == -1) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to decode args");
- rpcsvc_request_seterr (req, GARBAGE_ARGS);
- goto rpcerr;
- }
+mnt3_check_cached_fh(struct mount3_state *ms, struct nfs3_fh *fh,
+ const char *host_addr, gf_boolean_t is_write_op)
+{
+ if (!is_write_op)
+ return is_nfs_fh_cached(ms->authcache, fh, host_addr);
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount state not present");
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- ret = -1;
- goto rpcerr;
- }
+ return is_nfs_fh_cached_and_writeable(ms->authcache, fh, host_addr);
+}
- ret = 0;
- nfs = (struct nfs_state *)ms->nfsx->private;
- gf_log (GF_MNT, GF_LOG_DEBUG, "dirpath: %s", path);
- ret = mnt3_find_export (req, path, &exp);
- if (ret == -2) {
- ret = 0;
- goto rpcerr;
- } else if (ret < 0) {
- ret = -1;
- mntstat = MNT3ERR_NOENT;
- goto mnterr;
- }
+/**
+ * _mnt3_authenticate_req -- Given an RPC request and a path OR a filehandle
+ * check if the host is authorized to make the
+ * request. Uses exports/netgroups auth model to
+ * do this check.
+ *
+ * @ms : The mount state
+ * @req : The RPC request
+ * @fh : The NFS FH to authenticate (set when authenticating an FOP)
+ * @path: The path to authenticate (set when authenticating a mount req)
+ * @authorized_export: Allocate and fill this value when an export is authorized
+ * @authorized_host: Allocate and fill this value when a host is authorized
+ * @is_write_op: Is this a write op that we are authenticating?
+ *
+ * @return: 0 if authorized
+ * -EACCES for completely unauthorized fop
+ * -EROFS for unauthorized write operations (rm, mkdir, write)
+ */
+int
+_mnt3_authenticate_req(struct mount3_state *ms, rpcsvc_request_t *req,
+ struct nfs3_fh *fh, const char *path,
+ char **authorized_export, char **authorized_host,
+ gf_boolean_t is_write_op)
+{
+ char *peer_addr = NULL;
+ char *host_addr_ip = NULL;
+ char *host_addr_fqdn = NULL;
+ int auth_status_code = -EACCES;
+ char *pathdup = NULL;
+ size_t dlen = 0;
+ char *auth_host = NULL;
+ gf_boolean_t fh_cached = _gf_false;
+ struct export_item *expitem = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_MNT, ms, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT, req, out);
+
+ peer_addr = _mnt3_get_peer_addr(req);
+
+ if (!peer_addr)
+ goto free_and_out;
+
+ host_addr_ip = _mnt3_get_host_from_peer(peer_addr);
+
+ if (!host_addr_ip)
+ goto free_and_out;
+
+ if (path) {
+ /* Need to strip out trailing '/' */
+ pathdup = strdupa(path);
+ dlen = strlen(pathdup);
+ if (dlen > 0 && pathdup[dlen - 1] == '/')
+ pathdup[dlen - 1] = '\0';
+ }
+
+ /* Check if the filehandle is cached */
+ fh_cached = mnt3_check_cached_fh(ms, fh, host_addr_ip, is_write_op);
+ if (fh_cached) {
+ gf_msg_trace(GF_MNT, 0, "Found cached FH for %s", host_addr_ip);
+ auth_status_code = 0;
+ goto free_and_out;
+ }
+
+ /* Check if the IP is authorized */
+ auth_status_code = mnt3_auth_host(ms->auth_params, host_addr_ip, fh,
+ pathdup, is_write_op, &expitem);
+
+ gf_msg_debug(GF_MNT, 0, "access from IP %s is %s", host_addr_ip,
+ auth_status_code ? "denied" : "allowed");
+
+ if (auth_status_code != 0) {
+ /* If not, check if the FQDN is authorized */
+ host_addr_fqdn = gf_rev_dns_lookup(host_addr_ip);
+ auth_status_code = mnt3_auth_host(ms->auth_params, host_addr_fqdn, fh,
+ pathdup, is_write_op, &expitem);
+
+ gf_msg_debug(GF_MNT, 0, "access from FQDN %s is %s", host_addr_fqdn,
+ auth_status_code ? "denied" : "allowed");
+
+ if (auth_status_code == 0)
+ auth_host = host_addr_fqdn;
+ } else
+ auth_host = host_addr_ip;
+
+ /* Skip the lines that set authorized export &
+ * host if they are null.
+ */
+ if (!authorized_export || !authorized_host) {
+ /* Cache the file handle if it was authorized */
+ if (fh && auth_status_code == 0)
+ cache_nfs_fh(ms->authcache, fh, host_addr_ip, expitem);
+
+ goto free_and_out;
+ }
+
+ if (!fh && auth_status_code == 0) {
+ *authorized_export = gf_strdup(pathdup);
+ if (!*authorized_export)
+ gf_msg(GF_MNT, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Allocation error when copying "
+ "authorized path");
+
+ *authorized_host = gf_strdup(auth_host);
+ if (!*authorized_host)
+ gf_msg(GF_MNT, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Allocation error when copying "
+ "authorized host");
+ }
+
+free_and_out:
+ /* Free allocated strings after doing the auth */
+ GF_FREE(peer_addr);
+ GF_FREE(host_addr_fqdn);
+ GF_FREE(host_addr_ip);
+out:
+ return auth_status_code;
+}
- if (!nfs_subvolume_started (nfs, exp->vol)) {
- gf_log (GF_MNT, GF_LOG_DEBUG, "Volume %s not started",
- exp->vol->name);
- ret = -1;
- mntstat = MNT3ERR_NOENT;
- goto mnterr;
- }
+/**
+ * mnt3_authenticate_request -- Given an RPC request and a path, check if the
+ * host is authorized to make the request. This
+ * function calls _mnt3_authenticate_req_path ()
+ * in a loop for the parent of each path while
+ * the authentication check for that path is
+ * failing.
+ *
+ * E.g. If the requested path is /patchy/L1, and /patchy is authorized, but
+ * /patchy/L1 is not, it follows this code path :
+ *
+ * _mnt3_authenticate_req ("/patchy/L1") -> F
+ * _mnt3_authenticate_req ("/patchy"); -> T
+ * return T;
+ *
+ * @ms : The mount state
+ * @req : The RPC request
+ * @path: The requested path
+ * @authorized_path: This gets allocated and populated with the authorized path
+ * @authorized_host: This gets allocated and populated with the authorized host
+ * @return: 0 if authorized
+ * -EACCES for completely unauthorized fop
+ * -EROFS for unauthorized write operations (rm, mkdir, write)
+ */
+int
+mnt3_authenticate_request(struct mount3_state *ms, rpcsvc_request_t *req,
+ struct nfs3_fh *fh, const char *volname,
+ const char *path, char **authorized_path,
+ char **authorized_host, gf_boolean_t is_write_op)
+{
+ int auth_status_code = -EACCES;
+ char *parent_path = NULL;
+ const char *parent_old = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_MNT, ms, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT, req, out);
+
+ /* If this option is not set, just allow it through */
+ if (!ms->nfs->exports_auth) {
+ /* This function is called in a variety of use-cases (mount
+ * + each fop) so path/authorized_path are not always present.
+ * For the cases which it _is_ present we need to populate the
+ * authorized_path. */
+ if (path && authorized_path)
+ *authorized_path = gf_strdup(path);
+
+ auth_status_code = 0;
+ goto out;
+ }
+
+ /* First check if the path is allowed */
+ auth_status_code = _mnt3_authenticate_req(
+ ms, req, fh, path, authorized_path, authorized_host, is_write_op);
+
+ /* If the filehandle is set, just exit since we have to make only
+ * one call to the function above
+ */
+ if (fh)
+ goto out;
+
+ parent_old = path;
+ while (auth_status_code != 0) {
+ /* Get the path's parent */
+ parent_path = gf_resolve_path_parent(parent_old);
+ if (!parent_path) /* Nothing left in the path to resolve */
+ goto out;
+
+ /* Authenticate it */
+ auth_status_code = _mnt3_authenticate_req(ms, req, fh, parent_path,
+ authorized_path,
+ authorized_host, is_write_op);
+
+ parent_old = strdupa(parent_path); /* Copy the parent onto the
+ * stack.
+ */
+
+ GF_FREE(parent_path); /* Free the allocated parent string */
+ }
- ret = mnt3_check_client_net (ms, req, exp->vol);
- if (ret == RPCSVC_AUTH_REJECT) {
- mntstat = MNT3ERR_ACCES;
- gf_log (GF_MNT, GF_LOG_DEBUG, "Client mount not allowed");
- ret = -1;
- goto mnterr;
- }
+out:
+ return auth_status_code;
+}
- ret = mnt3svc_mount (req, ms, exp);
- if (ret < 0)
- mntstat = mnt3svc_errno_to_mnterr (-ret);
+int
+mnt3svc_mnt(rpcsvc_request_t *req)
+{
+ struct iovec pvec = {
+ 0,
+ };
+ char path[MNTPATHLEN];
+ int ret = -1;
+ struct mount3_state *ms = NULL;
+ mountstat3 mntstat = MNT3ERR_SERVERFAULT;
+ struct mnt3_export *exp = NULL;
+ struct nfs_state *nfs = NULL;
+ int authcode = 0;
+
+ if (!req)
+ return -1;
+
+ pvec.iov_base = path;
+ pvec.iov_len = MNTPATHLEN;
+ ret = xdr_to_mountpath(pvec, req->msg[0]);
+ if (ret == -1) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_ARGS_DECODE_ERROR,
+ "Failed to decode args");
+ rpcsvc_request_seterr(req, GARBAGE_ARGS);
+ goto rpcerr;
+ }
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "Mount state not present");
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ ret = -1;
+ goto rpcerr;
+ }
+
+ nfs = (struct nfs_state *)ms->nfsx->private;
+ gf_msg_debug(GF_MNT, 0, "dirpath: %s", path);
+ ret = mnt3_find_export(req, path, &exp);
+ if (ret < 0) {
+ mntstat = mnt3svc_errno_to_mnterr(-ret);
+ goto mnterr;
+ } else if (!exp) {
+ /*
+ * SPECIAL CASE: exp is NULL if "path" is subdir in
+ * call to mnt3_find_export().
+ *
+ * This is subdir mount, we are already DONE!
+ * nfs_subvolume_started() and mnt3_check_client_net_tcp()
+ * validation are done in mnt3_parse_dir_exports()
+ * which is invoked through mnt3_find_export().
+ *
+ * TODO: All mount should happen thorugh mnt3svc_mount()
+ * It needs more clean up.
+ */
+ return (0);
+ }
+
+ if (!nfs_subvolume_started(nfs, exp->vol)) {
+ gf_msg_debug(GF_MNT, 0, "Volume %s not started", exp->vol->name);
+ ret = -1;
+ mntstat = MNT3ERR_NOENT;
+ goto mnterr;
+ }
+
+ ret = mnt3_check_client_net_tcp(req, exp->vol->name);
+ if (ret == RPCSVC_AUTH_REJECT) {
+ mntstat = MNT3ERR_ACCES;
+ gf_msg_debug(GF_MNT, 0, "Client mount not allowed");
+ ret = -1;
+ goto mnterr;
+ }
+
+ /* The second authentication check is the exports/netgroups
+ * check.
+ */
+ authcode = mnt3_authenticate_request(ms, req, NULL, NULL, path, NULL, NULL,
+ _gf_false);
+ if (authcode != 0) {
+ mntstat = MNT3ERR_ACCES;
+ gf_msg_debug(GF_MNT, 0, "Client mount not allowed");
+ ret = -1;
+ goto mnterr;
+ }
+
+ ret = mnt3svc_mount(req, ms, exp);
+
+ if (ret < 0)
+ mntstat = mnt3svc_errno_to_mnterr(-ret);
mnterr:
- if (ret < 0) {
- mnt3svc_mnt_error_reply (req, mntstat);
- ret = 0;
- }
+ if (ret < 0) {
+ mnt3svc_mnt_error_reply(req, mntstat);
+ ret = 0;
+ }
rpcerr:
- return ret;
+ return ret;
}
-
int
-mnt3svc_null (rpcsvc_request_t *req)
+mnt3svc_null(rpcsvc_request_t *req)
{
- struct iovec dummyvec = {0, };
+ struct iovec dummyvec = {
+ 0,
+ };
- if (!req) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Got NULL request!");
- return 0;
- }
- rpcsvc_submit_generic (req, &dummyvec, 1, NULL, 0, NULL);
+ if (!req) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Got NULL request!");
return 0;
+ }
+ rpcsvc_submit_generic(req, &dummyvec, 1, NULL, 0, NULL);
+ return 0;
}
-
mountlist
-__build_mountlist (struct mount3_state *ms, int *count)
-{
- struct mountbody *mlist = NULL;
- struct mountbody *prev = NULL;
- struct mountbody *first = NULL;
- size_t namelen = 0;
- int ret = -1;
- struct mountentry *me = NULL;
-
- if ((!ms) || (!count))
- return NULL;
-
- *count = 0;
- gf_log (GF_MNT, GF_LOG_DEBUG, "Building mount list:");
- list_for_each_entry (me, &ms->mountlist, mlist) {
- namelen = strlen (me->exname);
- mlist = GF_CALLOC (1, sizeof (*mlist), gf_nfs_mt_mountbody);
- if (!mlist) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+__build_mountlist(struct mount3_state *ms, int *count)
+{
+ struct mountbody *mlist = NULL;
+ struct mountbody *prev = NULL;
+ struct mountbody *first = NULL;
+ size_t namelen = 0;
+ int ret = -1;
+ struct mountentry *me = NULL;
+
+ if ((!ms) || (!count))
+ return NULL;
- mlist->ml_directory = GF_CALLOC (namelen + 2, sizeof (char),
- gf_nfs_mt_char);
- if (!mlist->ml_directory) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+ /* read rmtab, other peers might have updated it */
+ mount_read_rmtab(ms);
- strcpy (mlist->ml_directory, "/");
- strcat (mlist->ml_directory, me->exname);
+ *count = 0;
+ gf_msg_debug(GF_MNT, 0, "Building mount list:");
+ list_for_each_entry(me, &ms->mountlist, mlist)
+ {
+ namelen = strlen(me->exname);
+ mlist = GF_CALLOC(1, sizeof(*mlist), gf_nfs_mt_mountbody);
+ if (!mlist) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto free_list;
+ }
+ if (!first)
+ first = mlist;
+
+ mlist->ml_directory = GF_MALLOC(namelen + 2, gf_nfs_mt_char);
+ if (!mlist->ml_directory) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto free_list;
+ }
- namelen = strlen (me->hostname);
- mlist->ml_hostname = GF_CALLOC (namelen + 2, sizeof (char),
- gf_nfs_mt_char);
- if (!mlist->ml_hostname) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+ strcpy(mlist->ml_directory, me->exname);
- strcat (mlist->ml_hostname, me->hostname);
+ namelen = strlen(me->hostname);
+ mlist->ml_hostname = GF_MALLOC(namelen + 2, gf_nfs_mt_char);
+ if (!mlist->ml_hostname) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto free_list;
+ }
- gf_log (GF_MNT, GF_LOG_DEBUG, "mount entry: dir: %s, host: %s",
- mlist->ml_directory, mlist->ml_hostname);
- if (prev) {
- prev->ml_next = mlist;
- prev = mlist;
- } else
- prev = mlist;
+ strcpy(mlist->ml_hostname, me->hostname);
- if (!first)
- first = mlist;
+ gf_msg_debug(GF_MNT, 0, "mount entry: dir: %s, host: %s",
+ mlist->ml_directory, mlist->ml_hostname);
+ if (prev) {
+ prev->ml_next = mlist;
+ prev = mlist;
+ } else
+ prev = mlist;
- (*count)++;
- }
+ (*count)++;
+ }
- ret = 0;
+ ret = 0;
free_list:
- if (ret == -1) {
- xdr_free_mountlist (first);
- first = NULL;
- }
+ if (ret == -1) {
+ xdr_free_mountlist(first);
+ first = NULL;
+ }
- return first;
+ return first;
}
-
mountlist
-mnt3svc_build_mountlist (struct mount3_state *ms, int *count)
+mnt3svc_build_mountlist(struct mount3_state *ms, int *count)
{
- struct mountbody *first = NULL;
+ struct mountbody *first = NULL;
- LOCK (&ms->mountlock);
- {
- first = __build_mountlist (ms, count);
- }
- UNLOCK (&ms->mountlock);
+ LOCK(&ms->mountlock);
+ {
+ first = __build_mountlist(ms, count);
+ }
+ UNLOCK(&ms->mountlock);
- return first;
+ return first;
}
-
int
-mnt3svc_dump (rpcsvc_request_t *req)
+mnt3svc_dump(rpcsvc_request_t *req)
{
- int ret = -1;
- struct mount3_state *ms = NULL;
- mountlist mlist;
- mountstat3 mstat = 0;
- mnt3_serializer sfunc = NULL;
- void *arg = NULL;
+ int ret = -1;
+ struct mount3_state *ms = NULL;
+ mountlist mlist;
+ mountstat3 mstat = 0;
+ mnt3_serializer sfunc = NULL;
+ void *arg = NULL;
+
+ if (!req)
+ return -1;
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ goto rpcerr;
+ }
+
+ sfunc = (mnt3_serializer)xdr_serialize_mountlist;
+ mlist = mnt3svc_build_mountlist(ms, &ret);
+ arg = &mlist;
+
+ if (!mlist) {
+ if (ret != 0) {
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ ret = -1;
+ goto rpcerr;
+ } else {
+ arg = &mstat;
+ sfunc = (mnt3_serializer)xdr_serialize_mountstat3;
+ }
+ }
+ mnt3svc_submit_reply(req, arg, sfunc);
- if (!req)
- return -1;
+ xdr_free_mountlist(mlist);
+ ret = 0;
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto rpcerr;
+rpcerr:
+ return ret;
+}
+
+int
+mnt3svc_umount(struct mount3_state *ms, char *dirpath, char *hostname)
+{
+ struct mountentry *me = NULL;
+ int ret = -1;
+ gf_store_handle_t *sh = NULL;
+ struct nfs_state *nfs = NULL;
+ gf_boolean_t update_rmtab = _gf_false;
+
+ if ((!ms) || (!dirpath) || (!hostname))
+ return -1;
+
+ nfs = (struct nfs_state *)ms->nfsx->private;
+
+ update_rmtab = mount_open_rmtab(nfs->rmtab, &sh);
+ if (update_rmtab) {
+ ret = gf_store_lock(sh);
+ if (ret)
+ goto out_free;
+ }
+
+ LOCK(&ms->mountlock);
+ {
+ if (update_rmtab)
+ __mount_read_rmtab(sh, &ms->mountlist, _gf_false);
+
+ if (list_empty(&ms->mountlist)) {
+ ret = 0;
+ goto out_unlock;
}
- sfunc = (mnt3_serializer)xdr_serialize_mountlist;
- mlist = mnt3svc_build_mountlist (ms, &ret);
- arg = &mlist;
+ ret = -1;
+ list_for_each_entry(me, &ms->mountlist, mlist)
+ {
+ if ((strcmp(me->exname, dirpath) == 0) &&
+ (strcmp(me->hostname, hostname) == 0)) {
+ ret = 0;
+ break;
+ }
+ }
- if (!mlist) {
- if (ret != 0) {
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- ret = -1;
- goto rpcerr;
- } else {
- arg = &mstat;
- sfunc = (mnt3_serializer)xdr_serialize_mountstat3;
- }
+ /* Need this check here because at the end of the search me
+ * might still be pointing to the last entry, which may not be
+ * the one we're looking for.
+ */
+ if (ret == -1) { /* Not found in list. */
+ gf_msg_trace(GF_MNT, 0, "Export not found");
+ goto out_unlock;
}
- mnt3svc_submit_reply (req, arg, sfunc);
+ if (!me)
+ goto out_unlock;
+
+ gf_msg_debug(GF_MNT, 0, "Unmounting: dir %s, host: %s", me->exname,
+ me->hostname);
+
+ list_del(&me->mlist);
+ GF_FREE(me);
- xdr_free_mountlist (mlist);
+ if (update_rmtab)
+ __mount_rewrite_rmtab(ms, sh);
+ }
+out_unlock:
+ UNLOCK(&ms->mountlock);
+
+ if (update_rmtab)
+ gf_store_unlock(sh);
+
+out_free:
+ if (update_rmtab)
+ gf_store_handle_destroy(sh);
+
+ return ret;
+}
+
+int
+mnt3svc_umnt(rpcsvc_request_t *req)
+{
+ char hostname[MNTPATHLEN];
+ char dirpath[MNTPATHLEN];
+ struct iovec pvec = {
+ 0,
+ };
+ int ret = -1;
+ struct mount3_state *ms = NULL;
+ mountstat3 mstat = MNT3_OK;
+ char *colon = NULL;
+
+ if (!req)
+ return -1;
+
+ /* Remove the mount point from the exports list. */
+ pvec.iov_base = dirpath;
+ pvec.iov_len = MNTPATHLEN;
+ ret = xdr_to_mountpath(pvec, req->msg[0]);
+ if (ret == -1) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_ARGS_DECODE_ERROR,
+ "Failed decode args");
+ rpcsvc_request_seterr(req, GARBAGE_ARGS);
+ goto rpcerr;
+ }
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "Mount state not present");
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ ret = -1;
+ goto rpcerr;
+ }
+
+ ret = rpcsvc_transport_peername(req->trans, hostname, MNTPATHLEN);
+ if (ret != 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOENT, NFS_MSG_GET_REMOTE_NAME_FAIL,
+ "Failed to get remote name: %s", gai_strerror(ret));
+ goto rpcerr;
+ }
+
+ colon = strrchr(hostname, ':');
+ if (colon) {
+ *colon = '\0';
+ }
+ gf_path_strip_trailing_slashes(dirpath);
+ gf_msg_debug(GF_MNT, 0, "dirpath: %s, hostname: %s", dirpath, hostname);
+ ret = mnt3svc_umount(ms, dirpath, hostname);
+
+ if (ret == -1) {
ret = 0;
+ mstat = MNT3ERR_NOENT;
+ }
+ /* FIXME: also take care of the corner case where the
+ * client was resolvable at mount but not at the umount - vice-versa.
+ */
+ mnt3svc_submit_reply(req, &mstat,
+ (mnt3_serializer)xdr_serialize_mountstat3);
rpcerr:
- return ret;
+ return ret;
}
-
int
-__mnt3svc_umount (struct mount3_state *ms, char *dirpath, char *hostname)
+__mnt3svc_umountall(struct mount3_state *ms)
{
- struct mountentry *me = NULL;
- char *exname = NULL;
- int ret = -1;
+ struct mountentry *me = NULL;
+ struct mountentry *tmp = NULL;
- if ((!ms) || (!dirpath) || (!hostname))
- return -1;
+ if (!ms)
+ return -1;
- if (list_empty (&ms->mountlist))
- return 0;
+ if (list_empty(&ms->mountlist))
+ return 0;
- if (dirpath[0] == '/')
- exname = dirpath+1;
- else
- exname = dirpath;
+ list_for_each_entry_safe(me, tmp, &ms->mountlist, mlist)
+ {
+ list_del(&me->mlist); /* Remove from the mount list */
+ __mountdict_remove(ms, me); /* Remove from the mount dict */
+ GF_FREE(me);
+ }
- list_for_each_entry (me, &ms->mountlist, mlist) {
- if ((strcmp (me->exname, exname) == 0) &&
- (strcmp (me->hostname, hostname) == 0)) {
- ret = 0;
- break;
- }
- }
+ return 0;
+}
- /* Need this check here because at the end of the search me might still
- * be pointing to the last entry, which may not be the one we're
- * looking for.
- */
- if (ret == -1) {/* Not found in list. */
- gf_log (GF_MNT, GF_LOG_DEBUG, "Export not found");
- goto ret;
- }
+int
+mnt3svc_umountall(struct mount3_state *ms)
+{
+ int ret = -1;
+ if (!ms)
+ return -1;
- if (!me)
- goto ret;
+ LOCK(&ms->mountlock);
+ {
+ ret = __mnt3svc_umountall(ms);
+ }
+ UNLOCK(&ms->mountlock);
- gf_log (GF_MNT, GF_LOG_DEBUG, "Unmounting: dir %s, host: %s",
- me->exname, me->hostname);
- list_del (&me->mlist);
- GF_FREE (me);
- ret = 0;
-ret:
- return ret;
+ return ret;
}
-
-
int
-mnt3svc_umount (struct mount3_state *ms, char *dirpath, char *hostname)
+mnt3svc_umntall(rpcsvc_request_t *req)
{
- int ret = -1;
- if ((!ms) || (!dirpath) || (!hostname))
- return -1;
-
- LOCK (&ms->mountlock);
- {
- ret = __mnt3svc_umount (ms, dirpath, hostname);
- }
- UNLOCK (&ms->mountlock);
+ int ret = RPCSVC_ACTOR_ERROR;
+ struct mount3_state *ms = NULL;
+ mountstat3 mstat = MNT3_OK;
+ if (!req)
return ret;
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "Mount state not present");
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ goto rpcerr;
+ }
+
+ mnt3svc_umountall(ms);
+ mnt3svc_submit_reply(req, &mstat,
+ (mnt3_serializer)xdr_serialize_mountstat3);
+
+ ret = RPCSVC_ACTOR_SUCCESS;
+rpcerr:
+ return ret;
}
+exports
+mnt3_xlchildren_to_exports(rpcsvc_t *svc, struct mount3_state *ms)
+{
+ struct exportnode *elist = NULL;
+ struct exportnode *prev = NULL;
+ struct exportnode *first = NULL;
+ size_t namelen = 0;
+ int ret = -1;
+ char *addrstr = NULL;
+ struct mnt3_export *ent = NULL;
+ struct nfs_state *nfs = NULL;
+
+ if ((!ms) || (!svc))
+ return NULL;
-int
-mnt3svc_umnt (rpcsvc_request_t *req)
-{
- char hostname[MNTPATHLEN];
- char dirpath[MNTPATHLEN];
- struct iovec pvec = {0, };
- int ret = -1;
- struct mount3_state *ms = NULL;
- mountstat3 mstat = MNT3_OK;
-
- if (!req)
- return -1;
-
- /* Remove the mount point from the exports list. */
- pvec.iov_base = dirpath;
- pvec.iov_len = MNTPATHLEN;
- ret = xdr_to_mountpath (pvec, req->msg[0]);
- if (ret == -1) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed decode args");
- rpcsvc_request_seterr (req, GARBAGE_ARGS);
- goto rpcerr;
- }
+ nfs = (struct nfs_state *)ms->nfsx->private;
+ if (!nfs)
+ return NULL;
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount state not present");
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- ret = -1;
- goto rpcerr;
- }
+ LOCK(&ms->mountlock);
+ list_for_each_entry(ent, &ms->exportlist, explist)
+ {
+ /* If volume is not started yet, do not list it for tools like
+ * showmount.
+ */
+ if (!nfs_subvolume_started(nfs, ent->vol))
+ continue;
- ret = rpcsvc_transport_peername (req->trans, hostname, MNTPATHLEN);
- if (ret != 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to get remote name: %s",
- gai_strerror (ret));
- goto try_umount_with_addr;
+ elist = GF_CALLOC(1, sizeof(*elist), gf_nfs_mt_exportnode);
+ if (!elist) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto free_list;
}
+ if (!first)
+ first = elist;
+ namelen = strlen(ent->expname);
+ elist->ex_dir = GF_MALLOC(namelen + 2, gf_nfs_mt_char);
+ if (!elist->ex_dir) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ if (first == elist)
+ first = NULL;
+ xdr_free_exports_list(elist);
+ elist = NULL;
+ goto free_list;
+ }
+ strcpy(elist->ex_dir, ent->expname);
+
+ addrstr = rpcsvc_volume_allowed(svc->options, ent->vol->name);
+ if (addrstr) {
+ /* create a groupnode per allowed client */
+ char *pos = NULL;
+ char *addr = NULL;
+ char *addrs = NULL;
+ struct groupnode *group = NULL;
+ struct groupnode *prev_group = NULL;
+
+ /* strtok_r() modifies the string, dup it */
+ addrs = gf_strdup(addrstr);
+ if (!addrs)
+ goto free_list;
+
+ while (1) {
+ /* only pass addrs on the 1st call */
+ addr = strtok_r(group ? NULL : addrs, ",", &pos);
+ if (addr == NULL)
+ /* no mode clients */
+ break;
+
+ group = GF_CALLOC(1, sizeof(struct groupnode),
+ gf_nfs_mt_groupnode);
+ if (!group) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory "
+ "allocation failed");
+ GF_FREE(addrs);
+ goto free_list;
+ }
- gf_log (GF_MNT, GF_LOG_DEBUG, "dirpath: %s, hostname: %s", dirpath,
- hostname);
- ret = mnt3svc_umount (ms, dirpath, hostname);
-
- /* Unmount succeeded with the given hostname. */
- if (ret == 0)
- goto snd_reply;
+ group->gr_name = gf_strdup(addr);
+ if (!group->gr_name) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory "
+ "allocation failed");
+ GF_FREE(group);
+ GF_FREE(addrs);
+ goto free_list;
+ }
-try_umount_with_addr:
- if (ret != 0)
- ret = rpcsvc_transport_peeraddr (req->trans, hostname,
- MNTPATHLEN, NULL, 0);
+ /* chain the groups together */
+ if (!elist->ex_groups)
+ elist->ex_groups = group;
+ else if (prev_group && !prev_group->gr_next)
+ prev_group->gr_next = group;
+ prev_group = group;
+ }
- if (ret != 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to get remote addr: %s",
- gai_strerror (ret));
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto rpcerr;
+ GF_FREE(addrs);
+ } else {
+ elist->ex_groups = GF_CALLOC(1, sizeof(struct groupnode),
+ gf_nfs_mt_groupnode);
+ if (!elist->ex_groups) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation "
+ "failed");
+ goto free_list;
+ }
+
+ addrstr = gf_strdup("No Access");
+ if (!addrstr)
+ goto free_list;
+
+ elist->ex_groups->gr_name = addrstr;
}
- gf_log (GF_MNT, GF_LOG_DEBUG, "dirpath: %s, hostname: %s", dirpath,
- hostname);
- ret = mnt3svc_umount (ms, dirpath, hostname);
- if (ret == -1)
- mstat = MNT3ERR_INVAL;
+ if (prev) {
+ prev->ex_next = elist;
+ prev = elist;
+ } else
+ prev = elist;
+ }
- ret = 0;
-snd_reply:
- mnt3svc_submit_reply (req, &mstat,
- (mnt3_serializer)xdr_serialize_mountstat3);
+ ret = 0;
-rpcerr:
- return ret;
-}
+free_list:
+ UNLOCK(&ms->mountlock);
+ if (ret == -1) {
+ xdr_free_exports_list(first);
+ first = NULL;
+ }
+ return first;
+}
int
-__mnt3svc_umountall (struct mount3_state *ms)
+mnt3svc_export(rpcsvc_request_t *req)
{
- struct mountentry *me = NULL;
- struct mountentry *tmp = NULL;
+ struct mount3_state *ms = NULL;
+ exports elist = NULL;
+ int ret = -1;
+
+ if (!req)
+ return -1;
+
+ ms = (struct mount3_state *)rpcsvc_request_program_private(req);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_NOT_FOUND,
+ "mount state not found");
+ rpcsvc_request_seterr(req, SYSTEM_ERR);
+ goto err;
+ }
+
+ /* Using the children translator names, build the export list */
+ elist = mnt3_xlchildren_to_exports(rpcsvc_request_service(req), ms);
+ /* Do not return error when exports list is empty. An exports list can
+ * be empty when no subvolumes have come up. No point returning error
+ * and confusing the user.
+ if (!elist) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Failed to build exports list");
+ nfs_rpcsvc_request_seterr (req, SYSTEM_ERR);
+ goto err;
+ }
+ */
+
+ /* Note how the serializer is passed to the generic reply function. */
+ mnt3svc_submit_reply(req, &elist, (mnt3_serializer)xdr_serialize_exports);
+
+ xdr_free_exports_list(elist);
+ ret = 0;
+err:
+ return ret;
+}
- if (!ms)
- return -1;
+/*
+ * __mnt3udp_get_mstate() Fetches mount3_state from xlator
+ * Linkage: Static
+ * Usage: Used only for UDP MOUNT codepath
+ */
+static struct mount3_state *
+__mnt3udp_get_mstate(xlator_t *nfsx)
+{
+ struct nfs_state *nfs = NULL;
+ struct mount3_state *ms = NULL;
- if (list_empty (&ms->mountlist))
- return 0;
+ if (nfsx == NULL)
+ return NULL;
- list_for_each_entry_safe (me, tmp, &ms->mountlist, mlist) {
- list_del (&me->mlist);
- GF_FREE (me);
- }
+ nfs = (struct nfs_state *)nfsx->private;
+ if (nfs == NULL)
+ return NULL;
- return 0;
+ ms = (struct mount3_state *)nfs->mstate;
+ return ms;
}
+extern int
+glfs_resolve_at(struct glfs *, xlator_t *, inode_t *, const char *, loc_t *,
+ struct iatt *, int, int);
-int
-mnt3svc_umountall (struct mount3_state *ms)
+extern struct glfs *
+glfs_new_from_ctx(glusterfs_ctx_t *);
+
+extern void
+glfs_free_from_ctx(struct glfs *);
+
+static inode_t *
+__mnt3udp_get_export_subdir_inode(struct svc_req *req, char *subdir,
+ char *expname, /* OUT */
+ struct mnt3_export *exp)
{
- int ret = -1;
- if (!ms)
- return -1;
+ inode_t *inode = NULL;
+ loc_t loc = {
+ 0,
+ };
+ struct iatt buf = {
+ 0,
+ };
+ int ret = -1;
+ glfs_t *fs = NULL;
+
+ if ((!req) || (!subdir) || (!expname) || (!exp))
+ return NULL;
- LOCK (&ms->mountlock);
- {
- ret = __mnt3svc_umountall (ms);
+ /* AUTH check for subdir i.e. nfs.export-dir */
+ if (exp->hostspec) {
+ struct sockaddr_in *sin = NULL;
+
+#if !defined(_TIRPC_SVC_H)
+ sin = svc_getcaller(req->rq_xprt);
+#else
+ sin = (struct sockaddr_in *)svc_getcaller(req->rq_xprt);
+ /* TIRPC's svc_getcaller() returns a pointer to a
+ * sockaddr_in6, even though it might actually be an
+ * IPv4 address. It ought return a struct sockaddr and
+ * make the caller upcast it to the proper address family.
+ */
+#endif
+ /* And let's make sure that it's actually an IPv4 address. */
+ GF_ASSERT(sin->sin_family == AF_INET);
+
+ ret = mnt3_verify_auth(sin, exp);
+ if (ret) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EACCES, NFS_MSG_AUTH_VERIFY_FAILED,
+ "AUTH(nfs.export-dir) verification failed");
+ errno = EACCES;
+ return NULL;
}
- UNLOCK (&ms->mountlock);
+ }
+
+ /*
+ * IMP: glfs_t fs object is not used by glfs_resolve_at (). The main
+ * purpose is to not change the ABI of glfs_resolve_at () and not to
+ * pass a NULL object.
+ *
+ * TODO: Instead of linking against libgfapi.so, just for one API
+ * i.e. glfs_resolve_at(), It would be cleaner if PATH name to
+ * inode resolution code can be moved to libglusterfs.so or so.
+ * refer bugzilla for more details :
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1161573
+ */
+ fs = glfs_new_from_ctx(exp->vol->ctx);
+ if (!fs)
+ return NULL;
- return ret;
-}
+ ret = glfs_resolve_at(fs, exp->vol, NULL, subdir, &loc, &buf,
+ 1 /* Follow link */, 0 /* Hard lookup */);
+ glfs_free_from_ctx(fs);
-int
-mnt3svc_umntall (rpcsvc_request_t *req)
+ if (ret != 0) {
+ loc_wipe(&loc);
+ return NULL;
+ }
+
+ inode = inode_ref(loc.inode);
+ snprintf(expname, PATH_MAX, "/%s%s", exp->vol->name, loc.path);
+
+ loc_wipe(&loc);
+
+ return inode;
+}
+
+static inode_t *
+__mnt3udp_get_export_volume_inode(struct svc_req *req, char *volpath,
+ char *expname, /* OUT */
+ struct mnt3_export *exp)
{
- int ret = RPCSVC_ACTOR_ERROR;
- struct mount3_state *ms = NULL;
- mountstat3 mstat = MNT3_OK;
+ char *rpath = NULL;
+ inode_t *inode = NULL;
- if (!req)
- return ret;
+ if ((!req) || (!volpath) || (!expname) || (!exp))
+ return NULL;
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount state not present");
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto rpcerr;
- }
+ rpath = strchr(volpath, '/');
+ if (rpath == NULL)
+ rpath = "/";
- mnt3svc_umountall (ms);
- mnt3svc_submit_reply (req, &mstat,
- (mnt3_serializer)xdr_serialize_mountstat3);
+ inode = inode_from_path(exp->vol->itable, rpath);
+ snprintf(expname, PATH_MAX, "/%s", exp->vol->name);
- ret = RPCSVC_ACTOR_SUCCESS;
-rpcerr:
- return ret;
+ return inode;
}
+/*
+ * nfs3_rootfh() is used for NFS MOUNT over UDP i.e. mountudpproc3_mnt_3_svc().
+ * Especially in mount3udp_thread() THREAD. Gluster NFS starts this thread
+ * when nfs.mount-udp is ENABLED (set to TRUE/ON).
+ */
+struct nfs3_fh *
+nfs3_rootfh(struct svc_req *req, xlator_t *nfsx, char *path,
+ char *expname /* OUT */)
+{
+ struct nfs3_fh *fh = NULL;
+ inode_t *inode = NULL;
+ struct mnt3_export *exp = NULL;
+ struct mount3_state *ms = NULL;
+ struct nfs_state *nfs = NULL;
+ int mnt3type = MNT3_EXPTYPE_DIR;
+ int ret = RPCSVC_AUTH_REJECT;
+
+ if ((!req) || (!nfsx) || (!path) || (!expname)) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ /*
+ * 1. First check if the MOUNT is for whole volume.
+ * i.e. __mnt3udp_get_export_volume_inode ()
+ * 2. If NOT, then TRY for SUBDIR MOUNT.
+ * i.e. __mnt3udp_get_export_subdir_inode ()
+ * 3. If a subdir is exported using nfs.export-dir,
+ * then the mount type would be MNT3_EXPTYPE_DIR,
+ * so make sure to find the proper path to be
+ * resolved using mnt3_get_volume_subdir()
+ * 3. Make sure subdir export is allowed.
+ */
+ ms = __mnt3udp_get_mstate(nfsx);
+ if (!ms) {
+ errno = EFAULT;
+ return NULL;
+ }
-exports
-mnt3_xlchildren_to_exports (rpcsvc_t *svc, struct mount3_state *ms)
-{
- struct exportnode *elist = NULL;
- struct exportnode *prev = NULL;
- struct exportnode *first = NULL;
- size_t namelen = 0;
- int ret = -1;
- char *addrstr = NULL;
- struct mnt3_export *ent = NULL;
- struct nfs_state *nfs = NULL;
-
- if ((!ms) || (!svc))
- return NULL;
-
- nfs = (struct nfs_state *)ms->nfsx->private;
- list_for_each_entry(ent, &ms->exportlist, explist) {
-
- /* If volume is not started yet, do not list it for tools like
- * showmount.
- */
- if (!nfs_subvolume_started (nfs, ent->vol))
- continue;
-
- namelen = strlen (ent->expname) + 1;
- elist = GF_CALLOC (1, sizeof (*elist), gf_nfs_mt_exportnode);
- if (!elist) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+ exp = mnt3_mntpath_to_export(ms, path, _gf_false);
+ if (exp != NULL)
+ mnt3type = exp->exptype;
- elist->ex_dir = GF_CALLOC (namelen + 2, sizeof (char),
- gf_nfs_mt_char);
- if (!elist->ex_dir) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+ if (mnt3type == MNT3_EXPTYPE_DIR) {
+ char volname[MNTPATHLEN] = {
+ 0,
+ };
+ char *volptr = volname;
- strcpy (elist->ex_dir, ent->expname);
-
- addrstr = rpcsvc_volume_allowed (svc->options,
- ent->vol->name);
- if (addrstr)
- addrstr = gf_strdup (addrstr);
- else
- addrstr = gf_strdup ("No Access");
-
- elist->ex_groups = GF_CALLOC (1, sizeof (struct groupnode),
- gf_nfs_mt_groupnode);
- if (!elist->ex_groups) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation"
- " failed");
- goto free_list;
- }
+ /* Subdir export (nfs3.export-dirs) check */
+ if (!gf_mnt3_export_dirs(ms)) {
+ errno = EACCES;
+ return NULL;
+ }
- elist->ex_groups->gr_name = addrstr;
- if (prev) {
- prev->ex_next = elist;
- prev = elist;
- } else
- prev = elist;
+ path = mnt3_get_volume_subdir(path, &volptr);
+ if (exp == NULL)
+ exp = mnt3_mntpath_to_export(ms, volname, _gf_false);
+ }
- if (!first)
- first = elist;
- }
+ if (exp == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
- ret = 0;
+ nfs = (struct nfs_state *)nfsx->private;
+ if (!nfs_subvolume_started(nfs, exp->vol)) {
+ errno = ENOENT;
+ return NULL;
+ }
-free_list:
- if (ret == -1) {
- xdr_free_exports_list (first);
- first = NULL;
- }
+ /* AUTH check: respect nfs.rpc-auth-allow/reject */
+ ret = mnt3_check_client_net_udp(req, exp->vol->name, nfsx);
+ if (ret == RPCSVC_AUTH_REJECT) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ switch (mnt3type) {
+ case MNT3_EXPTYPE_VOLUME:
+ inode = __mnt3udp_get_export_volume_inode(req, path, expname, exp);
+ break;
+
+ case MNT3_EXPTYPE_DIR:
+ inode = __mnt3udp_get_export_subdir_inode(req, path, expname, exp);
+ break;
+
+ default:
+ /* Never reachable */
+ gf_msg(GF_MNT, GF_LOG_ERROR, EFAULT, NFS_MSG_UNKNOWN_MNT_TYPE,
+ "Unknown MOUNT3 type");
+ errno = EFAULT;
+ goto err;
+ }
+
+ if (inode == NULL) {
+ /* Don't over-write errno */
+ if (!errno)
+ errno = ENOENT;
+ goto err;
+ }
+
+ /* Build the inode from FH */
+ fh = GF_CALLOC(1, sizeof(*fh), gf_nfs_mt_nfs3_fh);
+ if (fh == NULL) {
+ errno = ENOMEM;
+ goto err;
+ }
+
+ (void)nfs3_build_fh(inode, exp->volumeid, fh);
- return first;
+err:
+ if (inode)
+ inode_unref(inode);
+
+ return fh;
}
+int
+mount3udp_add_mountlist(xlator_t *nfsx, char *host, char *export)
+{
+ struct mountentry *me = NULL;
+ struct mount3_state *ms = NULL;
+
+ if ((!host) || (!export) || (!nfsx))
+ return -1;
+
+ ms = __mnt3udp_get_mstate(nfsx);
+ if (!ms)
+ return -1;
+
+ me = GF_CALLOC(1, sizeof(*me), gf_nfs_mt_mountentry);
+ if (!me)
+ return -1;
+
+ snprintf(me->exname, MNTPATHLEN, "%s", export);
+ snprintf(me->hostname, MNTPATHLEN, "%s", host);
+ INIT_LIST_HEAD(&me->mlist);
+ LOCK(&ms->mountlock);
+ {
+ list_add_tail(&me->mlist, &ms->mountlist);
+ mount_rewrite_rmtab(ms, NULL);
+ }
+ UNLOCK(&ms->mountlock);
+ return 0;
+}
int
-mnt3svc_export (rpcsvc_request_t *req)
+mount3udp_delete_mountlist(xlator_t *nfsx, char *hostname, char *export)
{
- struct mount3_state *ms = NULL;
- exports elist = NULL;
- int ret = -1;
+ struct mount3_state *ms = NULL;
- if (!req)
- return -1;
+ if ((!hostname) || (!export) || (!nfsx))
+ return -1;
- ms = (struct mount3_state *)rpcsvc_request_program_private (req);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "mount state not found");
- rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto err;
- }
+ ms = __mnt3udp_get_mstate(nfsx);
+ if (!ms)
+ return -1;
- /* Using the children translator names, build the export list */
- elist = mnt3_xlchildren_to_exports (rpcsvc_request_service (req),
- ms);
- /* Do not return error when exports list is empty. An exports list can
- * be empty when no subvolumes have come up. No point returning error
- * and confusing the user.
- if (!elist) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to build exports list");
- nfs_rpcsvc_request_seterr (req, SYSTEM_ERR);
- goto err;
+ mnt3svc_umount(ms, export, hostname);
+ return 0;
+}
+
+/**
+ * This function will parse the hostip (IP address, IP range, or hostname)
+ * and fill the host_auth_spec structure.
+ *
+ * @param hostspec - struct host_auth_spec
+ * @param hostip - IP address, IP range (CIDR format) or hostname
+ *
+ * @return 0 - on success and -1 on failure
+ *
+ * NB: This does not support IPv6 currently.
+ */
+int
+mnt3_export_fill_hostspec(struct host_auth_spec *hostspec, const char *hostip)
+{
+ char *ipdupstr = NULL;
+ char *savptr = NULL;
+ char *endptr = NULL;
+ char *ip = NULL;
+ char *token = NULL;
+ int ret = -1;
+ long prefixlen = IPv4_ADDR_SIZE; /* default */
+ uint32_t shiftbits = 0;
+ size_t length = 0;
+
+ /* Create copy of the string so that the source won't change
+ */
+ ipdupstr = gf_strdup(hostip);
+ if (NULL == ipdupstr) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
+ }
+
+ ip = strtok_r(ipdupstr, "/", &savptr);
+ /* Validate the Hostname or IPv4 address
+ * TODO: IPv6 support for subdir auth.
+ */
+ length = strlen(ip);
+ if ((!valid_ipv4_address(ip, (int)length, _gf_false)) &&
+ (!valid_host_name(ip, (int)length))) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid hostname or IPv4 address: %s", ip);
+ goto err;
+ }
+
+ hostspec->host_addr = gf_strdup(ip);
+ if (NULL == hostspec->host_addr) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
+ }
+
+ /**
+ * User provided CIDR address (xx.xx.xx.xx/n format) is split
+ * into HOST (IP addr or hostname) and network prefix(n) from
+ * which netmask would be calculated. This CIDR address may
+ * denote a single, distinct interface address or the beginning
+ * address of an entire network.
+ *
+ * e.g. the IPv4 block 192.168.100.0/24 represents the 256
+ * IPv4 addresses from 192.168.100.0 to 192.168.100.255.
+ * Therefore to check if an IP matches 192.168.100.0/24
+ * we should mask the IP with FFFFFF00 and compare it with
+ * host address part of CIDR.
+ *
+ * Refer: mask_match() in common-utils.c.
+ */
+ token = strtok_r(NULL, "/", &savptr);
+ if (token != NULL) {
+ prefixlen = strtol(token, &endptr, 10);
+ if ((errno != 0) || (*endptr != '\0') || (prefixlen < 0) ||
+ (prefixlen > IPv4_ADDR_SIZE)) {
+ gf_msg(THIS->name, GF_LOG_WARNING, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid IPv4 subnetwork mask");
+ goto err;
}
- */
+ }
- /* Note how the serializer is passed to the generic reply function. */
- mnt3svc_submit_reply (req, &elist,
- (mnt3_serializer)xdr_serialize_exports);
+ /*
+ * 1. Calculate the network mask address.
+ * 2. Convert it into Big-Endian format.
+ * 3. Store it in hostspec netmask.
+ */
+ shiftbits = IPv4_ADDR_SIZE - prefixlen;
+ hostspec->netmask = htonl((uint32_t)~0 << shiftbits);
- xdr_free_exports_list (elist);
- ret = 0;
+ ret = 0; /* SUCCESS */
err:
- return ret;
+ if (NULL != ipdupstr) {
+ GF_FREE(ipdupstr);
+ }
+ return ret;
}
-
-struct mnt3_export *
-mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, char *exportpath,
- uuid_t volumeid)
+/**
+ * This function will parse the AUTH parameter passed along with
+ * "export-dir" option. If AUTH parameter is present then it will be
+ * stripped from exportpath and stored in mnt3_export (exp) structure.
+ *
+ * @param exp - mnt3_export structure. Holds information needed for mount.
+ * @param exportpath - Value of "export-dir" key. Holds both export path
+ * and AUTH parameter for the path.
+ * exportpath format: <abspath>[(hostdesc[|hostspec|...])]
+ *
+ * @return This function will return 0 on success and -1 on failure.
+ */
+int
+mnt3_export_parse_auth_param(struct mnt3_export *exp, char *exportpath)
{
- struct mnt3_export *exp = NULL;
- int alloclen = 0;
- int ret = -1;
-
- if ((!ms) || (!xl))
- return NULL;
-
- exp = GF_CALLOC (1, sizeof (*exp), gf_nfs_mt_mnt3_export);
- if (!exp) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
- return NULL;
+ char *token = NULL;
+ char *savPtr = NULL;
+ char *hostip = NULL;
+ struct host_auth_spec *host = NULL;
+ int ret = 0;
+
+ if (exportpath == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_PARSE_HOSTSPEC_FAIL,
+ "Export path is NULL");
+ return -1;
+ }
+
+ /* Using exportpath directly in strtok_r because we want
+ * to strip off AUTH parameter from exportpath. */
+ token = strtok_r(exportpath, "(", &savPtr);
+
+ /* Get the next token, which will be the AUTH parameter. */
+ token = strtok_r(NULL, ")", &savPtr);
+
+ if (NULL == token) {
+ /* If AUTH is not present then we should return success. */
+ return 0;
+ }
+
+ /* Free any previously allocated hostspec structure. */
+ if (NULL != exp->hostspec) {
+ GF_FREE(exp->hostspec);
+ exp->hostspec = NULL;
+ }
+
+ exp->hostspec = GF_CALLOC(1, sizeof(*(exp->hostspec)), gf_nfs_mt_auth_spec);
+ if (NULL == exp->hostspec) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ return -1;
+ }
+
+ /* AUTH parameter can have multiple entries. For each entry
+ * a host_auth_spec structure is created. */
+ host = exp->hostspec;
+
+ hostip = strtok_r(token, "|", &savPtr);
+
+ /* Parse all AUTH parameters separated by '|' */
+ while (NULL != hostip) {
+ ret = mnt3_export_fill_hostspec(host, hostip);
+ if (0 != ret) {
+ gf_msg(GF_MNT, GF_LOG_WARNING, 0, NFS_MSG_PARSE_HOSTSPEC_FAIL,
+ "Failed to parse hostspec: %s", hostip);
+ goto err;
}
- INIT_LIST_HEAD (&exp->explist);
- if (exportpath)
- alloclen = strlen (xl->name) + 2 + strlen (exportpath);
- else
- alloclen = strlen (xl->name) + 2;
-
- exp->expname = GF_CALLOC (alloclen, sizeof (char), gf_nfs_mt_char);
- if (!exp->expname) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
- GF_FREE (exp);
- exp = NULL;
- goto err;
+ hostip = strtok_r(NULL, "|", &savPtr);
+ if (NULL == hostip) {
+ break;
}
- if (exportpath) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Initing dir export: %s:%s",
- xl->name, exportpath);
- exp->exptype = MNT3_EXPTYPE_DIR;
- ret = snprintf (exp->expname, alloclen, "/%s%s", xl->name,
- exportpath);
- } else {
- gf_log (GF_MNT, GF_LOG_TRACE, "Initing volume export: %s",
- xl->name);
- exp->exptype = MNT3_EXPTYPE_VOLUME;
- ret = snprintf (exp->expname, alloclen, "/%s", xl->name);
+ host->next = GF_CALLOC(1, sizeof(*(host)), gf_nfs_mt_auth_spec);
+ if (NULL == host->next) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
}
- if (ret < 0) {
- gf_log (xl->name, GF_LOG_WARNING,
- "failed to get the export name");
- }
- /* Just copy without discrimination, we'll determine whether to
- * actually use it when a mount request comes in and a file handle
- * needs to be built.
- */
- uuid_copy (exp->volumeid, volumeid);
- exp->vol = xl;
+ host = host->next;
+ }
+
+ /* In case of success return from here */
+ return 0;
err:
- return exp;
+ /* In case of failure free up hostspec structure. */
+ FREE_HOSTSPEC(exp);
+
+ return -1;
}
+/**
+ * exportpath will also have AUTH options (ip address, subnet address or
+ * hostname) mentioned.
+ * exportpath format: <abspath>[(hostdesc[|hostspec|...])]
+ */
+struct mnt3_export *
+mnt3_init_export_ent(struct mount3_state *ms, xlator_t *xl, char *exportpath,
+ uuid_t volumeid)
+{
+ struct mnt3_export *exp = NULL;
+ int alloclen = 0;
+ int ret = -1;
-int
-__mnt3_init_volume_direxports (struct mount3_state *ms, xlator_t *xlator,
- char *optstr, uuid_t volumeid)
-{
- struct mnt3_export *newexp = NULL;
- int ret = -1;
- char *savptr = NULL;
- char *dupopt = NULL;
- char *token = NULL;
-
- if ((!ms) || (!xlator) || (!optstr))
- return -1;
-
- dupopt = gf_strdup (optstr);
- if (!dupopt) {
- gf_log (GF_MNT, GF_LOG_ERROR, "gf_strdup failed");
- goto err;
- }
+ if ((!ms) || (!xl))
+ return NULL;
- token = strtok_r (dupopt, ",", &savptr);
- while (token) {
- newexp = mnt3_init_export_ent (ms, xlator, token, volumeid);
- if (!newexp) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to init dir "
- "export: %s", token);
- ret = -1;
- goto err;
- }
+ exp = GF_CALLOC(1, sizeof(*exp), gf_nfs_mt_mnt3_export);
+ if (!exp) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ return NULL;
+ }
- list_add_tail (&newexp->explist, &ms->exportlist);
- token = strtok_r (NULL, ",", &savptr);
+ if (NULL != exportpath) {
+ /* If exportpath is not NULL then we should check if AUTH
+ * parameter is present or not. If AUTH parameter is present
+ * then it will be stripped and stored in mnt3_export (exp)
+ * structure.
+ */
+ if (0 != mnt3_export_parse_auth_param(exp, exportpath)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_PARSE_AUTH_PARAM_FAIL,
+ "Failed to parse auth param");
+ goto err;
}
-
- ret = 0;
+ }
+
+ INIT_LIST_HEAD(&exp->explist);
+ if (exportpath)
+ alloclen = strlen(xl->name) + 2 + strlen(exportpath);
+ else
+ alloclen = strlen(xl->name) + 2;
+
+ exp->expname = GF_MALLOC(alloclen, gf_nfs_mt_char);
+ if (!exp->expname) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ goto err;
+ }
+
+ if (exportpath) {
+ gf_msg_trace(GF_MNT, 0, "Initing dir export: %s:%s", xl->name,
+ exportpath);
+ exp->exptype = MNT3_EXPTYPE_DIR;
+ ret = snprintf(exp->expname, alloclen, "/%s%s", xl->name, exportpath);
+ } else {
+ gf_msg_trace(GF_MNT, 0, "Initing volume export: %s", xl->name);
+ exp->exptype = MNT3_EXPTYPE_VOLUME;
+ ret = snprintf(exp->expname, alloclen, "/%s", xl->name);
+ }
+ if (ret < 0) {
+ gf_msg(xl->name, GF_LOG_ERROR, ret, NFS_MSG_SET_EXP_FAIL,
+ "Failed to set the export name");
+ goto err;
+ }
+ /* Just copy without discrimination, we'll determine whether to
+ * actually use it when a mount request comes in and a file handle
+ * needs to be built.
+ */
+ gf_uuid_copy(exp->volumeid, volumeid);
+ exp->vol = xl;
+
+ /* On success we should return from here*/
+ return exp;
err:
- if (dupopt)
- GF_FREE (dupopt);
+ /* On failure free exp and it's members.*/
+ if (NULL != exp) {
+ mnt3_export_free(exp);
+ exp = NULL;
+ }
- return ret;
+ return exp;
}
-
int
-__mnt3_init_volume (struct mount3_state *ms, dict_t *opts, xlator_t *xlator)
+__mnt3_init_volume_direxports(struct mount3_state *ms, xlator_t *xlator,
+ char *optstr, uuid_t volumeid)
{
- struct mnt3_export *newexp = NULL;
- int ret = -1;
- char searchstr[1024];
- char *optstr = NULL;
- uuid_t volumeid = {0, };
+ struct mnt3_export *newexp = NULL;
+ int ret = -1;
+ char *savptr = NULL;
+ char *dupopt = NULL;
+ char *token = NULL;
+
+ if ((!ms) || (!xlator) || (!optstr))
+ return -1;
+
+ dupopt = strdupa(optstr);
+
+ token = strtok_r(dupopt, ",", &savptr);
+ while (token) {
+ newexp = mnt3_init_export_ent(ms, xlator, token, volumeid);
+ if (!newexp) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_INIT_DIR_EXP_FAIL,
+ "Failed to init dir "
+ "export: %s",
+ token);
+ ret = -1;
+ goto err;
+ }
- if ((!ms) || (!xlator) || (!opts))
- return -1;
+ list_add_tail(&newexp->explist, &ms->exportlist);
+ token = strtok_r(NULL, ",", &savptr);
+ }
- uuid_clear (volumeid);
- if (gf_nfs_dvm_off (nfs_state (ms->nfsx)))
- goto no_dvm;
+ ret = 0;
+err:
+ return ret;
+}
- ret = snprintf (searchstr, 1024, "nfs3.%s.volume-id", xlator->name);
+int
+__mnt3_init_volume(struct mount3_state *ms, dict_t *opts, xlator_t *xlator)
+{
+ struct mnt3_export *newexp = NULL;
+ int ret = -1;
+ char searchstr[1024];
+ char *optstr = NULL;
+ uuid_t volumeid = {
+ 0,
+ };
+
+ if ((!ms) || (!xlator) || (!opts))
+ return -1;
+
+ gf_uuid_clear(volumeid);
+ if (gf_nfs_dvm_off(nfs_state(ms->nfsx)))
+ goto no_dvm;
+
+ ret = snprintf(searchstr, 1024, "nfs3.%s.volume-id", xlator->name);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_SNPRINTF_FAIL,
+ "snprintf failed");
+ ret = -1;
+ goto err;
+ }
+
+ if (dict_get(opts, searchstr)) {
+ ret = dict_get_str(opts, searchstr, &optstr);
if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "snprintf failed");
- ret = -1;
- goto err;
- }
-
- if (dict_get (opts, searchstr)) {
- ret = dict_get_str (opts, searchstr, &optstr);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to read option"
- ": %s", searchstr);
- ret = -1;
- goto err;
- }
- } else {
- gf_log (GF_MNT, GF_LOG_ERROR, "DVM is on but volume-id not "
- "given for volume: %s", xlator->name);
- ret = -1;
- goto err;
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_DICT_GET_FAILED,
+ "Failed to read "
+ "option: %s",
+ searchstr);
+ ret = -1;
+ goto err;
}
-
- if (optstr) {
- ret = uuid_parse (optstr, volumeid);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to parse volume "
- "UUID");
- ret = -1;
- goto err;
- }
+ } else {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_VOLID_MISSING,
+ "DVM is on but volume-id not "
+ "given for volume: %s",
+ xlator->name);
+ ret = -1;
+ goto err;
+ }
+
+ if (optstr) {
+ ret = gf_uuid_parse(optstr, volumeid);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_PARSE_VOL_UUID_FAIL,
+ "Failed to parse "
+ "volume UUID");
+ ret = -1;
+ goto err;
}
+ }
no_dvm:
- ret = snprintf (searchstr, 1024, "nfs3.%s.export-dir", xlator->name);
+ ret = snprintf(searchstr, 1024, "nfs3.%s.export-dir", xlator->name);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_SNPRINTF_FAIL,
+ "snprintf failed");
+ ret = -1;
+ goto err;
+ }
+
+ if (dict_get(opts, searchstr)) {
+ ret = dict_get_str(opts, searchstr, &optstr);
if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "snprintf failed");
- ret = -1;
- goto err;
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_DICT_GET_FAILED,
+ "Failed to read "
+ "option: %s",
+ searchstr);
+ ret = -1;
+ goto err;
}
- if (dict_get (opts, searchstr)) {
- ret = dict_get_str (opts, searchstr, &optstr);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to read option: "
- "%s", searchstr);
- ret = -1;
- goto err;
- }
-
- ret = __mnt3_init_volume_direxports (ms, xlator, optstr,
- volumeid);
- if (ret == -1) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Dir export setup failed"
- " for volume: %s", xlator->name);
- goto err;
- }
+ ret = __mnt3_init_volume_direxports(ms, xlator, optstr, volumeid);
+ if (ret == -1) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_DIR_EXP_SETUP_FAIL,
+ "Dir export "
+ "setup failed for volume: %s",
+ xlator->name);
+ goto err;
}
+ }
- if (ms->export_volumes) {
- newexp = mnt3_init_export_ent (ms, xlator, NULL, volumeid);
- if (!newexp) {
- ret = -1;
- goto err;
- }
-
- list_add_tail (&newexp->explist, &ms->exportlist);
+ if (ms->export_volumes) {
+ newexp = mnt3_init_export_ent(ms, xlator, NULL, volumeid);
+ if (!newexp) {
+ ret = -1;
+ goto err;
}
- ret = 0;
+ list_add_tail(&newexp->explist, &ms->exportlist);
+ }
+ ret = 0;
err:
- return ret;
+ return ret;
}
-
int
-__mnt3_init_volume_export (struct mount3_state *ms, dict_t *opts)
+__mnt3_init_volume_export(struct mount3_state *ms, dict_t *opts)
{
- int ret = -1;
- char *optstr = NULL;
- /* On by default. */
- gf_boolean_t boolt = _gf_true;
-
- if ((!ms) || (!opts))
- return -1;
-
- if (!dict_get (opts, "nfs3.export-volumes")) {
- ret = 0;
- goto err;
- }
+ int ret = -1;
+ char *optstr = NULL;
+ /* On by default. */
+ gf_boolean_t boolt = _gf_true;
- ret = dict_get_str (opts, "nfs3.export-volumes", &optstr);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to read option: "
- "nfs3.export-volumes");
- ret = -1;
- goto err;
- }
+ if ((!ms) || (!opts))
+ return -1;
- gf_string2boolean (optstr, &boolt);
+ if (!dict_get(opts, "nfs3.export-volumes")) {
ret = 0;
+ goto err;
+ }
+
+ ret = dict_get_str(opts, "nfs3.export-volumes", &optstr);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_DICT_GET_FAILED,
+ "Failed to read option: nfs3.export-volumes");
+ ret = -1;
+ goto err;
+ }
+
+ ret = gf_string2boolean(optstr, &boolt);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_STR2BOOL_FAIL,
+ "Failed to convert string to boolean");
+ }
err:
- if (boolt == _gf_false) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Volume exports disabled");
- ms->export_volumes = 0;
- } else {
- gf_log (GF_MNT, GF_LOG_TRACE, "Volume exports enabled");
- ms->export_volumes = 1;
- }
-
- return ret;
+ if (boolt == _gf_false) {
+ gf_msg_trace(GF_MNT, 0, "Volume exports disabled");
+ ms->export_volumes = 0;
+ } else {
+ gf_msg_trace(GF_MNT, 0, "Volume exports enabled");
+ ms->export_volumes = 1;
+ }
+
+ return ret;
}
-
int
-__mnt3_init_dir_export (struct mount3_state *ms, dict_t *opts)
+__mnt3_init_dir_export(struct mount3_state *ms, dict_t *opts)
{
- int ret = -1;
- char *optstr = NULL;
- /* On by default. */
- gf_boolean_t boolt = _gf_true;
+ int ret = -1;
+ char *optstr = NULL;
+ /* On by default. */
+ gf_boolean_t boolt = _gf_true;
- if ((!ms) || (!opts))
- return -1;
+ if ((!ms) || (!opts))
+ return -1;
- if (!dict_get (opts, "nfs3.export-dirs")) {
- ret = 0;
- goto err;
- }
-
- ret = dict_get_str (opts, "nfs3.export-dirs", &optstr);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Failed to read option: "
- "nfs3.export-dirs");
- ret = -1;
- goto err;
- }
-
- gf_string2boolean (optstr, &boolt);
+ if (!dict_get(opts, "nfs3.export-dirs")) {
ret = 0;
+ goto err;
+ }
+
+ ret = dict_get_str(opts, "nfs3.export-dirs", &optstr);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_DICT_GET_FAILED,
+ "Failed to read option: nfs3.export-dirs");
+ ret = -1;
+ goto err;
+ }
+
+ ret = gf_string2boolean(optstr, &boolt);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_STR2BOOL_FAIL,
+ "Failed to convert string to boolean");
+ }
err:
- if (boolt == _gf_false) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Dir exports disabled");
- ms->export_dirs = 0;
- } else {
- gf_log (GF_MNT, GF_LOG_TRACE, "Dir exports enabled");
- ms->export_dirs = 1;
- }
-
- return ret;
+ if (boolt == _gf_false) {
+ gf_msg_trace(GF_MNT, 0, "Dir exports disabled");
+ ms->export_dirs = 0;
+ } else {
+ gf_msg_trace(GF_MNT, 0, "Dir exports enabled");
+ ms->export_dirs = 1;
+ }
+
+ return ret;
}
-
int
-mnt3_init_options (struct mount3_state *ms, dict_t *options)
-{
- xlator_list_t *volentry = NULL;
- int ret = -1;
-
- if ((!ms) || (!options))
- return -1;
-
- __mnt3_init_volume_export (ms, options);
- __mnt3_init_dir_export (ms, options);
- volentry = ms->nfsx->children;
- while (volentry) {
- gf_log (GF_MNT, GF_LOG_TRACE, "Initing options for: %s",
- volentry->xlator->name);
- ret = __mnt3_init_volume (ms, options, volentry->xlator);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Volume init failed");
- goto err;
- }
-
- volentry = volentry->next;
+mnt3_init_options(struct mount3_state *ms, dict_t *options)
+{
+ xlator_list_t *volentry = NULL;
+ int ret = -1;
+
+ if ((!ms) || (!options))
+ return -1;
+
+ __mnt3_init_volume_export(ms, options);
+ __mnt3_init_dir_export(ms, options);
+ volentry = ms->nfsx->children;
+ while (volentry) {
+ gf_msg_trace(GF_MNT, 0, "Initing options for: %s",
+ volentry->xlator->name);
+ ret = __mnt3_init_volume(ms, options, volentry->xlator);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_VOL_INIT_FAIL,
+ "Volume init failed");
+ goto err;
}
- ret = 0;
+ volentry = volentry->next;
+ }
+
+ ret = 0;
err:
- return ret;
+ return ret;
}
-
struct mount3_state *
-mnt3_init_state (xlator_t *nfsx)
+mnt3_init_state(xlator_t *nfsx)
{
- struct mount3_state *ms = NULL;
- int ret = -1;
-
- if (!nfsx)
- return NULL;
+ struct mount3_state *ms = NULL;
+ int ret = -1;
- ms = GF_CALLOC (1, sizeof (*ms), gf_nfs_mt_mount3_state);
- if (!ms) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");
- return NULL;
- }
+ if (!nfsx)
+ return NULL;
- ms->iobpool = nfsx->ctx->iobuf_pool;
- ms->nfsx = nfsx;
- INIT_LIST_HEAD (&ms->exportlist);
- ret = mnt3_init_options (ms, nfsx->options);
- if (ret < 0) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Options init failed");
- return NULL;
- }
+ ms = GF_CALLOC(1, sizeof(*ms), gf_nfs_mt_mount3_state);
+ if (!ms) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Memory allocation failed");
+ return NULL;
+ }
+
+ ms->iobpool = nfsx->ctx->iobuf_pool;
+ ms->nfsx = nfsx;
+ INIT_LIST_HEAD(&ms->exportlist);
+ ret = mnt3_init_options(ms, nfsx->options);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_OPT_INIT_FAIL,
+ "Options init failed");
+ return NULL;
+ }
- INIT_LIST_HEAD (&ms->mountlist);
- LOCK_INIT (&ms->mountlock);
+ INIT_LIST_HEAD(&ms->mountlist);
+ LOCK_INIT(&ms->mountlock);
- return ms;
+ return ms;
}
int
-mount_init_state (xlator_t *nfsx)
+mount_init_state(xlator_t *nfsx)
{
- int ret = -1;
- struct nfs_state *nfs = NULL;
-
- if (!nfsx)
- goto out;
-
- nfs = (struct nfs_state *)nfs_state (nfsx);
- /*Maintaining global state for MOUNT1 and MOUNT3*/
- nfs->mstate = mnt3_init_state (nfsx);
- if (!nfs->mstate) {
- gf_log (GF_NFS, GF_LOG_ERROR, "Failed to allocate"
- "mount state");
- goto out;
- }
- ret = 0;
+ int ret = -1;
+ struct nfs_state *nfs = NULL;
+
+ if (!nfsx)
+ goto out;
+
+ nfs = (struct nfs_state *)nfs_state(nfsx);
+ /*Maintaining global state for MOUNT1 and MOUNT3*/
+ nfs->mstate = mnt3_init_state(nfsx);
+ if (!nfs->mstate) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate mount state");
+ goto out;
+ }
+ ret = 0;
out:
- return ret;
+ return ret;
}
-rpcsvc_actor_t mnt3svc_actors[MOUNT3_PROC_COUNT] = {
- {"NULL", MOUNT3_NULL, mnt3svc_null, NULL, NULL, 0},
- {"MNT", MOUNT3_MNT, mnt3svc_mnt, NULL, NULL, 0},
- {"DUMP", MOUNT3_DUMP, mnt3svc_dump, NULL, NULL, 0},
- {"UMNT", MOUNT3_UMNT, mnt3svc_umnt, NULL, NULL, 0},
- {"UMNTALL", MOUNT3_UMNTALL, mnt3svc_umntall, NULL, NULL, 0},
- {"EXPORT", MOUNT3_EXPORT, mnt3svc_export, NULL, NULL, 0}
-};
-
-
+static rpcsvc_actor_t mnt3svc_actors[MOUNT3_PROC_COUNT] = {
+ {
+ "NULL",
+ mnt3svc_null,
+ NULL,
+ MOUNT3_NULL,
+ DRC_NA,
+ },
+ {"MNT", mnt3svc_mnt, NULL, MOUNT3_MNT, DRC_NA, 0},
+ {"DUMP", mnt3svc_dump, NULL, MOUNT3_DUMP, DRC_NA, 0},
+ {"UMNT", mnt3svc_umnt, NULL, MOUNT3_UMNT, DRC_NA, 0},
+ {"UMNTALL", mnt3svc_umntall, NULL, MOUNT3_UMNTALL, DRC_NA, 0},
+ {"EXPORT", mnt3svc_export, NULL, MOUNT3_EXPORT, DRC_NA, 0}};
/* Static init parts are assigned here, dynamic ones are done in
* mnt3svc_init and mnt3_init_state.
+ * Making MOUNT3 a synctask so that the blocking DNS calls during rpc auth
+ * gets offloaded to syncenv, keeping the main/poll thread unblocked
*/
-rpcsvc_program_t mnt3prog = {
- .progname = "MOUNT3",
- .prognum = MOUNT_PROGRAM,
- .progver = MOUNT_V3,
- .progport = GF_MOUNTV3_PORT,
- .actors = mnt3svc_actors,
- .numactors = MOUNT3_PROC_COUNT,
- .min_auth = AUTH_NULL,
+static rpcsvc_program_t mnt3prog = {
+ .progname = "MOUNT3",
+ .prognum = MOUNT_PROGRAM,
+ .progver = MOUNT_V3,
+ .progport = GF_MOUNTV3_PORT,
+ .actors = mnt3svc_actors,
+ .numactors = MOUNT3_PROC_COUNT,
+ .min_auth = AUTH_NULL,
+ .synctask = _gf_true,
};
-
-rpcsvc_program_t *
-mnt3svc_init (xlator_t *nfsx)
+/**
+ * __mnt3_mounted_exports_walk -- Walk through the mounted export directories
+ * and unmount the directories that are no
+ * longer authorized to be mounted.
+ * @dict: The dict to walk
+ * @key : The key we are on
+ * @val : The value associated with that key
+ * @tmp : Additional params (pointer to an auth params struct passed here)
+ *
+ */
+int
+__mnt3_mounted_exports_walk(dict_t *dict, char *key, data_t *val, void *tmp)
{
- struct mount3_state *mstate = NULL;
- struct nfs_state *nfs = NULL;
- dict_t *options = NULL;
- char *portstr = NULL;
- int ret = -1;
+ char *path = NULL;
+ char *host_addr_ip = NULL;
+ char *host_addr_fqdn = NULL;
+ char *keydup = NULL;
+ char *colon = NULL;
+ struct mnt3_auth_params *auth_params = NULL;
+ int ret = 0;
+ int auth_status_code = 0;
+
+ gf_msg_trace(GF_MNT, 0, "Checking if key %s is authorized.", key);
+
+ auth_params = (struct mnt3_auth_params *)tmp;
+
+ /* Since we haven't obtained a lock around the mount dict
+ * here, we want to duplicate the key and then process it.
+ * Otherwise we would potentially have a race condition
+ * by modifying the key in the dict when other threads
+ * are accessing it.
+ */
+ keydup = strdupa(key);
+
+ colon = strchr(keydup, ':');
+ if (!colon)
+ return 0;
- if (!nfsx || !nfsx->private)
- return NULL;
+ *colon = '\0';
+
+ path = alloca(strlen(keydup) + 2);
+ snprintf(path, strlen(keydup) + 2, "/%s", keydup);
+
+ /* Host is one character after ':' */
+ host_addr_ip = colon + 1;
+
+ /* Check if the IP is authorized */
+ auth_status_code = mnt3_auth_host(auth_params, host_addr_ip, NULL, path,
+ FALSE, NULL);
+ if (auth_status_code == 0) {
+ goto out;
+ }
+
+ ret = gf_get_hostname_from_ip(host_addr_ip, &host_addr_fqdn);
+ if (ret != 0) {
+ gf_msg(GF_MNT, GF_LOG_DEBUG, 0, NFS_MSG_AUTH_ERROR,
+ "Authorization failed for IP [%s], but name "
+ "resolution also failed!",
+ host_addr_ip);
+ goto unmount;
+ }
+
+ /* If not, check if the FQDN is authorized */
+ gf_msg(GF_MNT, GF_LOG_DEBUG, 0, NFS_MSG_AUTH_ERROR,
+ "Authorization failed for IP [%s], attempting to"
+ " auth hostname [%s]...",
+ host_addr_ip, host_addr_fqdn);
+
+ auth_status_code = mnt3_auth_host(auth_params, host_addr_fqdn, NULL, path,
+ FALSE, NULL);
+ if (auth_status_code == 0) {
+ gf_msg(GF_MNT, GF_LOG_DEBUG, 0, NFS_MSG_AUTH_ERROR,
+ "Authorization succeeded for "
+ "Client [IP=%s, Hostname=%s].",
+ host_addr_ip, host_addr_fqdn);
+ goto out;
+ }
+
+unmount:
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_AUTH_ERROR,
+ "Client [IP=%s, Hostname=%s] not authorized for this mount. "
+ "Unmounting!",
+ host_addr_ip, host_addr_fqdn);
+ mnt3svc_umount(auth_params->ms, path, host_addr_ip);
+out:
+ GF_FREE(host_addr_fqdn);
+ return 0;
+}
- nfs = (struct nfs_state *)nfsx->private;
+/**
+ * _mnt3_invalidate_old_mounts -- Calls __mnt3_mounted_exports_walk which checks
+ * checks if hosts are authorized to be mounted
+ * and umounts them.
+ *
+ * @ms: The mountstate for this service that holds all the information we need
+ *
+ */
+void
+_mnt3_invalidate_old_mounts(struct mount3_state *ms)
+{
+ gf_msg_debug(GF_MNT, 0, "Invalidating old mounts ...");
+ dict_foreach(ms->mountdict, __mnt3_mounted_exports_walk, ms->auth_params);
+}
- gf_log (GF_MNT, GF_LOG_DEBUG, "Initing Mount v3 state");
- mstate = (struct mount3_state *)nfs->mstate;
- if (!mstate) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount v3 state init failed");
- goto err;
- }
+/**
+ * _mnt3_has_file_changed -- Checks if a file has changed on disk
+ *
+ * @path: The path of the file on disk
+ * @oldmtime: The previous mtime of the file
+ *
+ * @return: file changed: TRUE
+ * otherwise : FALSE
+ *
+ * Uses get_file_mtime () in common-utils.c
+ */
+gf_boolean_t
+_mnt3_has_file_changed(const char *path, time_t *oldmtime)
+{
+ gf_boolean_t changed = _gf_false;
+ time_t mtime = {0};
+ int ret = 0;
- mnt3prog.private = mstate;
- options = dict_new ();
+ GF_VALIDATE_OR_GOTO(GF_MNT, path, out);
+ GF_VALIDATE_OR_GOTO(GF_MNT, oldmtime, out);
- ret = gf_asprintf (&portstr, "%d", GF_MOUNTV3_PORT);
- if (ret == -1)
- goto err;
+ ret = get_file_mtime(path, &mtime);
+ if (ret < 0)
+ goto out;
- ret = dict_set_dynstr (options, "transport.socket.listen-port", portstr);
- if (ret == -1)
- goto err;
- ret = dict_set_str (options, "transport-type", "socket");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
- }
+ if (mtime != *oldmtime) {
+ changed = _gf_true;
+ *oldmtime = mtime;
+ }
+out:
+ return changed;
+}
- if (nfs->allow_insecure) {
- ret = dict_set_str (options, "rpc-auth-allow-insecure", "on");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
- }
- ret = dict_set_str (options, "rpc-auth.ports.insecure", "on");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
- }
+/**
+ * _mnt_auth_param_refresh_thread - Started using pthread_create () in
+ * mnt3svc_init (). Reloads exports/netgroups
+ * files from disk and sets the auth params
+ * structure in the mount state to reflect
+ * any changes from disk.
+ * @argv: Unused argument
+ * @return: Always returns NULL
+ */
+void *
+_mnt3_auth_param_refresh_thread(void *argv)
+{
+ struct mount3_state *mstate = (struct mount3_state *)argv;
+ char *exp_file_path = NULL;
+ char *ng_file_path = NULL;
+ size_t nbytes = 0;
+ time_t exp_time = 0;
+ time_t ng_time = 0;
+ gf_boolean_t any_file_changed = _gf_false;
+ int ret = 0;
+
+ nbytes = strlen(exports_file_path) + 1;
+ exp_file_path = alloca(nbytes);
+ snprintf(exp_file_path, nbytes, "%s", exports_file_path);
+
+ nbytes = strlen(netgroups_file_path) + 1;
+ ng_file_path = alloca(nbytes);
+ snprintf(ng_file_path, nbytes, "%s", netgroups_file_path);
+
+ /* Set the initial timestamps to avoid reloading right after
+ * mnt3svc_init () spawns this thread */
+ get_file_mtime(exp_file_path, &exp_time);
+ get_file_mtime(ng_file_path, &ng_time);
+
+ while (_gf_true) {
+ if (mstate->stop_refresh)
+ break;
+ any_file_changed = _gf_false;
+
+ /* Sleep before checking the file again */
+ sleep(mstate->nfs->auth_refresh_time_secs);
+
+ if (_mnt3_has_file_changed(exp_file_path, &exp_time)) {
+ gf_msg(GF_MNT, GF_LOG_INFO, 0, NFS_MSG_UPDATING_EXP,
+ "File %s changed, updating exports,", exp_file_path);
+
+ ret = mnt3_auth_set_exports_auth(mstate->auth_params,
+ exp_file_path);
+ if (ret)
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SET_EXP_AUTH_PARAM_FAIL,
+ "Failed to set export auth params.");
+ else
+ any_file_changed = _gf_true;
}
- rpcsvc_create_listeners (nfs->rpcsvc, options, nfsx->name);
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "Unable to create listeners");
- dict_unref (options);
- goto err;
+ if (_mnt3_has_file_changed(ng_file_path, &ng_time)) {
+ gf_msg(GF_MNT, GF_LOG_INFO, 0, NFS_MSG_UPDATING_NET_GRP,
+ "File %s changed,"
+ "updating netgroups",
+ ng_file_path);
+
+ ret = mnt3_auth_set_netgroups_auth(mstate->auth_params,
+ ng_file_path);
+ if (ret)
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SET_NET_GRP_FAIL,
+ "Failed to set netgroup auth params.");
+ else
+ any_file_changed = _gf_true;
}
- return &mnt3prog;
-err:
- return NULL;
-}
+ /* If no files changed, go back to sleep */
+ if (!any_file_changed)
+ continue;
+ gf_msg(GF_MNT, GF_LOG_INFO, 0, NFS_MSG_PURGING_AUTH_CACHE,
+ "Purging auth cache.");
+ auth_cache_purge(mstate->authcache);
-rpcsvc_actor_t mnt1svc_actors[MOUNT1_PROC_COUNT] = {
- {"NULL", MOUNT1_NULL, mnt3svc_null, NULL, NULL, 0},
- {{0, 0}, },
- {"DUMP", MOUNT1_DUMP, mnt3svc_dump, NULL, NULL, 0},
- {"UMNT", MOUNT1_UMNT, mnt3svc_umnt, NULL, NULL, 0},
- {{0, 0}, },
- {"EXPORT", MOUNT1_EXPORT, mnt3svc_export, NULL, NULL, 0}
-};
+ /* Walk through mounts that are no longer authorized
+ * and unmount them on the server side. This will
+ * cause subsequent file ops to fail with access denied.
+ */
+ _mnt3_invalidate_old_mounts(mstate);
+ }
-rpcsvc_program_t mnt1prog = {
- .progname = "MOUNT1",
- .prognum = MOUNT_PROGRAM,
- .progver = MOUNT_V1,
- .progport = GF_MOUNTV1_PORT,
- .actors = mnt1svc_actors,
- .numactors = MOUNT1_PROC_COUNT,
- .min_auth = AUTH_NULL,
-};
+ return NULL;
+}
+/**
+ * _mnt3_init_auth_params -- Initialize authentication parameters by allocating
+ * the struct and setting the exports & netgroups
+ * files as parameters.
+ *
+ * @mstate : The mount state we are going to set the auth parameters in it.
+ *
+ * @return : success: 0 for success
+ * failure: -EINVAL for bad args, -ENOMEM for allocation errors, < 0
+ * for other errors (parsing the files, etc.) These are
+ * bubbled up from the functions we call to set the params.
+ */
+int
+_mnt3_init_auth_params(struct mount3_state *mstate)
+{
+ int ret = -EINVAL;
+ char *exp_file_path = NULL;
+ char *ng_file_path = NULL;
+ size_t nbytes = 0;
+
+ GF_VALIDATE_OR_GOTO(GF_MNT, mstate, out);
+
+ mstate->auth_params = mnt3_auth_params_init(mstate);
+ if (!mstate->auth_params) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to init mount auth params.");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ nbytes = strlen(exports_file_path) + 1;
+ exp_file_path = alloca(nbytes);
+ snprintf(exp_file_path, nbytes, "%s", exports_file_path);
+
+ ret = mnt3_auth_set_exports_auth(mstate->auth_params, exp_file_path);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_SET_EXP_AUTH_PARAM_FAIL,
+ "Failed to set export auth params.");
+ goto out;
+ }
+
+ nbytes = strlen(netgroups_file_path) + 1;
+ ng_file_path = alloca(nbytes);
+ snprintf(ng_file_path, nbytes, "%s", netgroups_file_path);
+
+ ret = mnt3_auth_set_netgroups_auth(mstate->auth_params, ng_file_path);
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_SET_EXP_AUTH_PARAM_FAIL,
+ "Failed to set netgroup auth params.");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
-rpcsvc_program_t *
-mnt1svc_init (xlator_t *nfsx)
+/**
+ * mnt3svc_deinit -- Function called by the nfs translator to cleanup all state
+ *
+ * @nfsx : The NFS translator used to perform the cleanup
+ * This structure holds all the pointers to memory that we need to free
+ * as well as the threads that have been started.
+ */
+void
+mnt3svc_deinit(xlator_t *nfsx)
{
- struct mount3_state *mstate = NULL;
- struct nfs_state *nfs = NULL;
- dict_t *options = NULL;
- char *portstr = NULL;
- int ret = -1;
+ struct mount3_state *mstate = NULL;
+ struct nfs_state *nfs = NULL;
+
+ if (!nfsx || !nfsx->private)
+ return;
+
+ nfs = (struct nfs_state *)nfsx->private;
+ mstate = (struct mount3_state *)nfs->mstate;
+
+ if (nfs->refresh_auth) {
+ /* Mark as true and wait for thread to exit */
+ mstate->stop_refresh = _gf_true;
+ pthread_join(mstate->auth_refresh_thread, NULL);
+ }
+
+ if (nfs->exports_auth)
+ mnt3_auth_params_deinit(mstate->auth_params);
+
+ /* Unmount everything and clear mountdict */
+ LOCK(&mstate->mountlock);
+ {
+ __mnt3svc_umountall(mstate);
+ dict_unref(mstate->mountdict);
+ }
+ UNLOCK(&mstate->mountlock);
+}
- if (!nfsx || !nfsx->private)
- return NULL;
+rpcsvc_program_t *
+mnt3svc_init(xlator_t *nfsx)
+{
+ struct mount3_state *mstate = NULL;
+ struct nfs_state *nfs = NULL;
+ dict_t *options = NULL;
+ char *portstr = NULL;
+ int ret = -1;
+ pthread_t udp_thread;
+
+ if (!nfsx || !nfsx->private)
+ return NULL;
- nfs = (struct nfs_state *)nfsx->private;
+ nfs = (struct nfs_state *)nfsx->private;
- gf_log (GF_MNT, GF_LOG_DEBUG, "Initing Mount v1 state");
- mstate = (struct mount3_state *)nfs->mstate;
- if (!mstate) {
- gf_log (GF_MNT, GF_LOG_ERROR, "Mount v3 state init failed");
- goto err;
- }
+ gf_msg_debug(GF_MNT, 0, "Initing Mount v3 state");
+ mstate = (struct mount3_state *)nfs->mstate;
+ if (!mstate) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_MNT_STATE_INIT_FAIL,
+ "Mount v3 state init failed");
+ goto err;
+ }
- mnt1prog.private = mstate;
+ mstate->nfs = nfs;
- options = dict_new ();
+ mstate->mountdict = dict_new();
+ if (!mstate->mountdict) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to setup mount dict. Allocation error.");
+ goto err;
+ }
- ret = gf_asprintf (&portstr, "%d", GF_MOUNTV1_PORT);
- if (ret == -1)
- goto err;
+ if (nfs->exports_auth) {
+ ret = _mnt3_init_auth_params(mstate);
+ if (ret < 0)
+ goto err;
- ret = dict_set_dynstr (options, "transport.socket.listen-port", portstr);
- if (ret == -1)
- goto err;
- ret = dict_set_str (options, "transport-type", "socket");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
+ mstate->authcache = auth_cache_init(nfs->auth_cache_ttl_sec);
+ if (!mstate->authcache) {
+ ret = -ENOMEM;
+ goto err;
}
- if (nfs->allow_insecure) {
- ret = dict_set_str (options, "rpc-auth-allow-insecure", "on");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
- }
- ret = dict_set_str (options, "rpc-auth.ports.insecure", "on");
- if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "dict_set_str error");
- goto err;
- }
+ mstate->stop_refresh = _gf_false; /* Allow thread to run */
+ ret = gf_thread_create(&mstate->auth_refresh_thread, NULL,
+ _mnt3_auth_param_refresh_thread, mstate,
+ "nfsauth");
+ if (ret) {
+ gf_msg_debug(GF_MNT, GF_LOG_DEBUG, "Thread creation failed");
}
- rpcsvc_create_listeners (nfs->rpcsvc, options, nfsx->name);
+ } else
+ gf_msg(GF_MNT, GF_LOG_INFO, 0, NFS_MSG_EXP_AUTH_DISABLED,
+ "Exports auth has been disabled!");
+
+ mnt3prog.private = mstate;
+ options = dict_new();
+ if (options == NULL) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOMEM, NFS_MSG_GFID_DICT_CREATE_FAIL,
+ "dict allocation failed");
+ goto err;
+ }
+
+ ret = gf_asprintf(&portstr, "%d", GF_MOUNTV3_PORT);
+ if (ret == -1)
+ goto err;
+
+ ret = dict_set_dynstr(options, "transport.socket.listen-port", portstr);
+ if (ret == -1)
+ goto err;
+
+ ret = dict_set_str(options, "transport-type", "socket");
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+
+ if (nfs->allow_insecure) {
+ ret = dict_set_str(options, "rpc-auth-allow-insecure", "on");
if (ret == -1) {
- gf_log (GF_NFS, GF_LOG_ERROR, "Unable to create listeners");
- dict_unref (options);
- goto err;
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ ret = dict_set_str(options, "rpc-auth.ports.insecure", "on");
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ }
+
+ ret = rpcsvc_create_listeners(nfs->rpcsvc, options, nfsx->name);
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_LISTENERS_CREATE_FAIL,
+ "Unable to create listeners");
+ goto err;
+ }
+
+ if (nfs->mount_udp) {
+ ret = gf_thread_create(&udp_thread, NULL, mount3udp_thread, nfsx,
+ "nfsudp");
+ if (ret) {
+ gf_msg_debug(GF_MNT, GF_LOG_DEBUG, "Thread creation failed");
}
+ }
+ if (options)
+ dict_unref(options);
- return &mnt1prog;
+ return &mnt3prog;
err:
- return NULL;
+ if (options)
+ dict_unref(options);
+ return NULL;
}
+static rpcsvc_actor_t mnt1svc_actors[MOUNT1_PROC_COUNT] = {
+ {"NULL", mnt3svc_null, NULL, MOUNT1_NULL, DRC_NA, 0},
+ {"MNT", NULL, NULL, MOUNT1_MNT, DRC_NA, 0},
+ {"DUMP", mnt3svc_dump, NULL, MOUNT1_DUMP, DRC_NA, 0},
+ {"UMNT", mnt3svc_umnt, NULL, MOUNT1_UMNT, DRC_NA, 0},
+ {"UMNTALL", NULL, NULL, MOUNT1_UMNTALL, DRC_NA, 0},
+ {"EXPORT", mnt3svc_export, NULL, MOUNT1_EXPORT, DRC_NA, 0}};
+
+static rpcsvc_program_t mnt1prog = {
+ .progname = "MOUNT1",
+ .prognum = MOUNT_PROGRAM,
+ .progver = MOUNT_V1,
+ .progport = GF_MOUNTV1_PORT,
+ .actors = mnt1svc_actors,
+ .numactors = MOUNT1_PROC_COUNT,
+ .min_auth = AUTH_NULL,
+ .synctask = _gf_true,
+};
+
+rpcsvc_program_t *
+mnt1svc_init(xlator_t *nfsx)
+{
+ struct mount3_state *mstate = NULL;
+ struct nfs_state *nfs = NULL;
+ dict_t *options = NULL;
+ char *portstr = NULL;
+ int ret = -1;
+
+ if (!nfsx || !nfsx->private)
+ return NULL;
+
+ nfs = (struct nfs_state *)nfsx->private;
+
+ gf_msg_debug(GF_MNT, GF_LOG_DEBUG, "Initing Mount v1 state");
+ mstate = (struct mount3_state *)nfs->mstate;
+ if (!mstate) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, EINVAL, NFS_MSG_MNT_STATE_INIT_FAIL,
+ "Mount v3 state init failed");
+ goto err;
+ }
+
+ mnt1prog.private = mstate;
+
+ options = dict_new();
+ if (options == NULL) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOMEM, NFS_MSG_GFID_DICT_CREATE_FAIL,
+ "dict allocation failed");
+ goto err;
+ }
+
+ ret = gf_asprintf(&portstr, "%d", GF_MOUNTV1_PORT);
+ if (ret == -1)
+ goto err;
+
+ ret = dict_set_dynstr(options, "transport.socket.listen-port", portstr);
+ if (ret == -1)
+ goto err;
+ ret = dict_set_str(options, "transport-type", "socket");
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+
+ if (nfs->allow_insecure) {
+ ret = dict_set_str(options, "rpc-auth-allow-insecure", "on");
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ ret = dict_set_str(options, "rpc-auth.ports.insecure", "on");
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_DICT_SET_FAILED,
+ "dict_set_str error");
+ goto err;
+ }
+ }
+
+#ifdef IPV6_DEFAULT
+ ret = dict_set_str(options, "transport.address-family", "inet6");
+ if (ret == -1) {
+ gf_log(GF_NFS, GF_LOG_ERROR,
+ "dict_set_str error when trying to enable ipv6");
+ goto err;
+ }
+#endif
+
+ ret = rpcsvc_create_listeners(nfs->rpcsvc, options, nfsx->name);
+ if (ret == -1) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, errno, NFS_MSG_LISTENERS_CREATE_FAIL,
+ "Unable to create listeners");
+ dict_unref(options);
+ goto err;
+ }
+
+ return &mnt1prog;
+err:
+ return NULL;
+}
+int
+mount_reconfigure_state(xlator_t *nfsx, dict_t *options)
+{
+ int ret = -1;
+ struct nfs_state *nfs = NULL;
+ struct mount3_state *ms = NULL;
+ struct mnt3_export *exp = NULL;
+ struct mnt3_export *texp = NULL;
+
+ if ((!nfsx) || (!options))
+ return (-1);
+
+ nfs = (struct nfs_state *)nfs_state(nfsx);
+ if (!nfs)
+ return (-1);
+
+ ms = nfs->mstate;
+ if (!ms)
+ return (-1);
+
+ /*
+ * Free() up the old export list. mnt3_init_options() will
+ * rebuild the export list from scratch. Do it with locking
+ * to avoid unnecessary race conditions.
+ */
+ LOCK(&ms->mountlock);
+ list_for_each_entry_safe(exp, texp, &ms->exportlist, explist)
+ {
+ list_del(&exp->explist);
+ mnt3_export_free(exp);
+ }
+ ret = mnt3_init_options(ms, options);
+ UNLOCK(&ms->mountlock);
+
+ if (ret < 0) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ret, NFS_MSG_RECONF_FAIL,
+ "Options reconfigure failed");
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/xlators/nfs/server/src/mount3.h b/xlators/nfs/server/src/mount3.h
index c0eae36440f..b185df835ae 100644
--- a/xlators/nfs/server/src/mount3.h
+++ b/xlators/nfs/server/src/mount3.h
@@ -2,121 +2,187 @@
Copyright (c) 2010-2011 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/>.
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
*/
#ifndef _MOUNT3_H_
#define _MOUNT3_H_
-#ifndef _CONFIG_H
-#define _CONFIG_H
-#include "config.h"
-#endif
-
#include "rpcsvc.h"
-#include "dict.h"
-#include "xlator.h"
-#include "iobuf.h"
+#include <glusterfs/dict.h>
+#include <glusterfs/xlator.h>
+#include <glusterfs/iobuf.h>
#include "nfs.h"
-#include "list.h"
+#include <glusterfs/list.h>
#include "xdr-nfs3.h"
-#include "locking.h"
+#include <glusterfs/locking.h>
#include "nfs3-fh.h"
-#include "uuid.h"
+#include <glusterfs/compat-uuid.h>
+#include "exports.h"
+#include "mount3-auth.h"
+#include "auth-cache.h"
/* Registered with portmap */
-#define GF_MOUNTV3_PORT 38465
-#define GF_MOUNTV3_IOB (2 * GF_UNIT_KB)
-#define GF_MOUNTV3_IOBPOOL (GF_MOUNTV3_IOB * 50)
+#define GF_MOUNTV3_PORT 38465
+#define GF_MOUNTV3_IOB (2 * GF_UNIT_KB)
+#define GF_MOUNTV3_IOBPOOL (GF_MOUNTV3_IOB * 50)
-#define GF_MOUNTV1_PORT 38466
-#define GF_MNT GF_NFS"-mount"
+#define GF_MOUNTV1_PORT 38466
+#define GF_MNT GF_NFS "-mount"
extern rpcsvc_program_t *
-mnt3svc_init (xlator_t *nfsx);
+mnt3svc_init(xlator_t *nfsx);
extern rpcsvc_program_t *
-mnt1svc_init (xlator_t *nfsx);
+mnt1svc_init(xlator_t *nfsx);
+
+extern void
+mnt3svc_deinit(xlator_t *nfsx);
extern int
-mount_init_state (xlator_t *nfsx);
+mount_init_state(xlator_t *nfsx);
+
+extern int
+mount_reconfigure_state(xlator_t *nfsx, dict_t *options);
+
+void
+mount_rewrite_rmtab(struct mount3_state *ms, char *new_rmtab);
+
+struct mnt3_export *
+mnt3_mntpath_to_export(struct mount3_state *ms, const char *dirpath,
+ gf_boolean_t export_parsing_match);
+
+extern int
+mnt3svc_update_mountlist(struct mount3_state *ms, rpcsvc_request_t *req,
+ const char *expname, const char *fullpath);
+
+int
+mnt3_authenticate_request(struct mount3_state *ms, rpcsvc_request_t *req,
+ struct nfs3_fh *fh, const char *volname,
+ const char *path, char **authorized_path,
+ char **authorized_host, gf_boolean_t is_write_op);
/* Data structure used to store the list of mounts points currently
* in use by NFS clients.
*/
struct mountentry {
- /* Links to mount3_state->mountlist. */
- struct list_head mlist;
+ /* Links to mount3_state->mountlist. */
+ struct list_head mlist;
+
+ /* The export name */
+ char exname[MNTPATHLEN];
+ char hostname[MNTPATHLEN];
+ char fullpath[MNTPATHLEN];
- /* The export name */
- char exname[MNTPATHLEN];
- char hostname[MNTPATHLEN];
+ gf_boolean_t has_full_path;
+
+ /* Since this is stored in a dict, we want to be able
+ * to find easily get the key we used to store
+ * the struct in our dict
+ */
+ char hashkey[MNTPATHLEN * 2 + 2];
};
-#define MNT3_EXPTYPE_VOLUME 1
-#define MNT3_EXPTYPE_DIR 2
+#define MNT3_EXPTYPE_VOLUME 1
+#define MNT3_EXPTYPE_DIR 2
+
+/* Structure to hold export-dir AUTH parameter */
+struct host_auth_spec {
+ char *host_addr; /* Allowed IP or host name */
+ uint32_t netmask; /* Network mask (Big-Endian) */
+ struct host_auth_spec *next; /* Pointer to next AUTH struct */
+};
struct mnt3_export {
- struct list_head explist;
-
- /* The string that may contain either the volume name if the full volume
- * is exported or the subdirectory in the volume.
- */
- char *expname;
- xlator_t *vol;
- int exptype;
-
- /* Extracted from nfs volume options if nfs.dynamicvolumes is on.
- */
- uuid_t volumeid;
+ struct list_head explist;
+
+ /* The string that may contain either the volume name if the full volume
+ * is exported or the subdirectory in the volume.
+ */
+ char *expname;
+ /*
+ * IP address, hostname or subnets who are allowed to connect to expname
+ * subvolume or subdirectory
+ */
+ struct host_auth_spec *hostspec;
+ xlator_t *vol;
+ int exptype;
+
+ /* This holds the full path that the client requested including
+ * the volume name AND the subdirectory in the volume.
+ */
+ char *fullpath;
+
+ /* Extracted from nfs volume options if nfs.dynamicvolumes is on.
+ */
+ uuid_t volumeid;
+ uuid_t mountid;
};
struct mount3_state {
- xlator_t *nfsx;
+ xlator_t *nfsx;
+
+ /* The NFS state that this belongs to */
+ struct nfs_state *nfs;
+
+ /* The buffers for all network IO are got from this pool. */
+ struct iobuf_pool *iobpool;
- /* The buffers for all network IO are got from this pool. */
- struct iobuf_pool *iobpool;
+ /* List of exports, can be volumes or directories in those volumes. */
+ struct list_head exportlist;
- /* List of exports, can be volumes or directories in those volumes. */
- struct list_head exportlist;
+ /* List of current mount points over all the exports from this
+ * server.
+ */
+ struct list_head mountlist;
- /* List of current mount points over all the exports from this
- * server.
- */
- struct list_head mountlist;
+ /* Dict of current mount points over all the exports from this
+ * server. Mirrors the mountlist above, but can be used for
+ * faster lookup in the event that there are several mounts.
+ * Currently, each NFSOP is validated against this dict: each
+ * op is checked to see if the host that operates on the path
+ * does in fact have an entry in the mount dict.
+ */
+ dict_t *mountdict;
- /* Used to protect the mountlist. */
- gf_lock_t mountlock;
+ /* Used to protect the mountlist & the mount dict */
+ gf_lock_t mountlock;
- /* Set to 0 if exporting full volumes is disabled. On by default. */
- int export_volumes;
- int export_dirs;
+ /* Used to insert additional authentication parameters */
+ struct mnt3_auth_params *auth_params;
+
+ /* Set to 0 if exporting full volumes is disabled. On by default. */
+ gf_boolean_t export_volumes;
+ gf_boolean_t export_dirs;
+
+ pthread_t auth_refresh_thread;
+ gf_boolean_t stop_refresh;
+
+ struct auth_cache *authcache;
};
-#define gf_mnt3_export_dirs(mst) ((mst)->export_dirs)
+#define gf_mnt3_export_dirs(mst) ((mst)->export_dirs)
struct mount3_resolve_state {
- struct mnt3_export *exp;
- struct mount3_state *mstate;
- rpcsvc_request_t *req;
+ struct mnt3_export *exp;
+ struct mount3_state *mstate;
+ rpcsvc_request_t *req;
- char remainingdir[MNTPATHLEN];
- loc_t resolveloc;
- struct nfs3_fh parentfh;
+ char remainingdir[MNTPATHLEN];
+ loc_t resolveloc;
+ struct nfs3_fh parentfh;
};
typedef struct mount3_resolve_state mnt3_resolve_t;
+int
+mnt3_parse_dir_exports(rpcsvc_request_t *req, struct mount3_state *ms,
+ char *subdir, gf_boolean_t send_reply);
+
+char *
+mnt3_get_volume_subdir(char *path, char **volname);
+
#endif
diff --git a/xlators/nfs/server/src/mount3udp_svc.c b/xlators/nfs/server/src/mount3udp_svc.c
new file mode 100644
index 00000000000..1a2b0f85453
--- /dev/null
+++ b/xlators/nfs/server/src/mount3udp_svc.c
@@ -0,0 +1,238 @@
+/*
+ Copyright (c) 2012 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include "xdr-nfs3.h"
+#include <glusterfs/logging.h>
+#include <glusterfs/mem-pool.h>
+#include "nfs-mem-types.h"
+#include "nfs-messages.h"
+#include "mount3.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/pmap_clnt.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+extern struct nfs3_fh *
+nfs3_rootfh(struct svc_req *req, xlator_t *nfsx, char *dp, char *expname);
+
+extern mountres3
+mnt3svc_set_mountres3(mountstat3 stat, struct nfs3_fh *fh, int *authflavor,
+ u_int aflen);
+extern int
+mount3udp_add_mountlist(xlator_t *nfsx, char *host, char *expname);
+
+extern int
+mount3udp_delete_mountlist(xlator_t *nfsx, char *host, char *expname);
+
+extern mountstat3
+mnt3svc_errno_to_mnterr(int32_t errnum);
+
+/* only this thread will use this, no locking needed */
+char mnthost[INET_ADDRSTRLEN + 1];
+
+#define MNT3UDP_AUTH_LEN 1 /* Only AUTH_UNIX for now */
+
+mountres3 *
+mountudpproc3_mnt_3_svc(dirpath **dpp, struct svc_req *req)
+{
+ struct mountres3 *res = NULL;
+ int *autharr = NULL;
+ struct nfs3_fh *fh = NULL;
+ char *mpath = NULL;
+ xlator_t *nfsx = THIS;
+ char expname[PATH_MAX] = {
+ 0,
+ };
+ mountstat3 stat = MNT3ERR_SERVERFAULT;
+
+ errno = 0; /* RESET errno */
+
+ mpath = (char *)*dpp;
+ while (*mpath == '/')
+ mpath++;
+
+ res = GF_CALLOC(1, sizeof(*res), gf_nfs_mt_mountres3);
+ if (res == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Unable to allocate memory");
+ goto err;
+ }
+ autharr = GF_CALLOC(MNT3UDP_AUTH_LEN, sizeof(int), gf_nfs_mt_int);
+ if (autharr == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Unable to allocate memory");
+ goto err;
+ }
+
+ autharr[0] = AUTH_UNIX;
+
+ fh = nfs3_rootfh(req, nfsx, mpath, (char *)expname);
+
+ /* FAILURE: No FH */
+ if (fh == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, errno, NFS_MSG_GET_FH_FAIL,
+ "Unable to get fh for %s", mpath);
+ if (errno)
+ stat = mnt3svc_errno_to_mnterr(errno);
+ *res = mnt3svc_set_mountres3(stat, NULL /* fh */, autharr,
+ MNT3UDP_AUTH_LEN);
+ return res;
+ }
+
+ /* SUCCESS */
+ stat = MNT3_OK;
+ *res = mnt3svc_set_mountres3(stat, fh, autharr, MNT3UDP_AUTH_LEN);
+ (void)mount3udp_add_mountlist(nfsx, mnthost, (char *)expname);
+ return res;
+
+err:
+ GF_FREE(fh);
+ GF_FREE(res);
+ GF_FREE(autharr);
+ return NULL;
+}
+
+mountstat3 *
+mountudpproc3_umnt_3_svc(dirpath **dp, struct svc_req *req)
+{
+ mountstat3 *stat = NULL;
+ char *mpath = (char *)*dp;
+ xlator_t *nfsx = THIS;
+
+ stat = GF_MALLOC(sizeof(mountstat3), gf_nfs_mt_mountstat3);
+ if (stat == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Unable to allocate memory");
+ return NULL;
+ }
+ *stat = MNT3_OK;
+ (void)mount3udp_delete_mountlist(nfsx, mnthost, mpath);
+ return stat;
+}
+
+static void
+mountudp_program_3(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ union {
+ dirpath mountudpproc3_mnt_3_arg;
+ } argument;
+ char *result = NULL;
+ xdrproc_t _xdr_argument = NULL, _xdr_result = NULL;
+ char *(*local)(char *, struct svc_req *) = NULL;
+ mountres3 *res = NULL;
+ struct sockaddr_in *sin = NULL;
+
+#if !defined(_TIRPC_SVC_H)
+ sin = svc_getcaller(transp);
+#else
+ sin = (struct sockaddr_in *)svc_getcaller(transp);
+ /* TIRPC's svc_getcaller() returns a pointer to a sockaddr_in6, even
+ * though it might actually be an IPv4 address. It ought return a
+ * struct sockaddr and make the caller upcast it to the proper
+ * address family. Sigh.
+ */
+#endif
+ /* And let's make sure that it's actually an IPv4 address. */
+ GF_ASSERT(sin->sin_family == AF_INET);
+
+ inet_ntop(AF_INET, &sin->sin_addr, mnthost, INET_ADDRSTRLEN + 1);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ return;
+
+ case MOUNT3_MNT:
+ _xdr_argument = (xdrproc_t)xdr_dirpath;
+ _xdr_result = (xdrproc_t)xdr_mountres3;
+ local = (char *(*)(char *,
+ struct svc_req *))mountudpproc3_mnt_3_svc;
+ break;
+
+ case MOUNT3_UMNT:
+ _xdr_argument = (xdrproc_t)xdr_dirpath;
+ _xdr_result = (xdrproc_t)xdr_mountstat3;
+ local = (char *(*)(char *,
+ struct svc_req *))mountudpproc3_umnt_3_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ memset((char *)&argument, 0, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)_xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)((char *)&argument, rqstp);
+ if (result == NULL) {
+ gf_msg_debug(GF_MNT, 0, "PROC returned error");
+ svcerr_systemerr(transp);
+ }
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)_xdr_result, result)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
+ "svc_sendreply returned error");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)_xdr_argument, (caddr_t)&argument)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_ARG_FREE_FAIL,
+ "Unable to free arguments");
+ }
+ if (result == NULL)
+ return;
+ /* free the result */
+ switch (rqstp->rq_proc) {
+ case MOUNT3_MNT:
+ res = (mountres3 *)result;
+ GF_FREE(res->mountres3_u.mountinfo.fhandle.fhandle3_val);
+ GF_FREE(res->mountres3_u.mountinfo.auth_flavors.auth_flavors_val);
+ GF_FREE(res);
+ break;
+
+ case MOUNT3_UMNT:
+ GF_FREE(result);
+ break;
+ }
+ return;
+}
+
+void *
+mount3udp_thread(void *argv)
+{
+ xlator_t *nfsx = argv;
+ register SVCXPRT *transp = NULL;
+
+ GF_ASSERT(nfsx);
+
+ THIS = nfsx;
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
+ "svcudp_create error");
+ return NULL;
+ }
+ if (!svc_register(transp, MOUNT_PROGRAM, MOUNT_V3, mountudp_program_3,
+ IPPROTO_UDP)) {
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_ERROR,
+ "svc_register error");
+ return NULL;
+ }
+
+ svc_run();
+ gf_msg(GF_MNT, GF_LOG_ERROR, 0, NFS_MSG_SVC_RUN_RETURNED,
+ "svc_run returned");
+ return NULL;
+}
diff --git a/xlators/nfs/server/src/netgroups.c b/xlators/nfs/server/src/netgroups.c
new file mode 100644
index 00000000000..f68a7f14a26
--- /dev/null
+++ b/xlators/nfs/server/src/netgroups.c
@@ -0,0 +1,1161 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2),in all
+ cases as published by the Free Software Foundation.
+*/
+
+#include "netgroups.h"
+#include <glusterfs/parse-utils.h>
+#include "nfs-messages.h"
+
+static void
+_nge_print(const struct netgroup_entry *nge);
+static void
+_netgroup_entry_deinit(struct netgroup_entry *ptr);
+static void
+_netgroup_host_deinit(struct netgroup_host *host);
+
+static dict_t *__deleted_entries;
+static struct parser *ng_file_parser;
+static struct parser *ng_host_parser;
+
+/**
+ * _ng_init_parser -- Initialize the parsers used in this file
+ *
+ * @return: success: 0 (on success the parsers are initialized)
+ * failure: -1
+ */
+static int
+_ng_init_parsers()
+{
+ int ret = -1;
+
+ /* Initialize the parsers. The only reason this should
+ * ever fail is because of 1) memory allocation errors
+ * 2) the regex in netgroups.h has been changed and no
+ * longer compiles.
+ */
+ ng_file_parser = parser_init(NG_FILE_PARSE_REGEX);
+ if (!ng_file_parser)
+ goto out;
+
+ ng_host_parser = parser_init(NG_HOST_PARSE_REGEX);
+ if (!ng_host_parser)
+ goto out;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * _ng_deinit_parsers - Free the parsers used in this file
+ */
+static void
+_ng_deinit_parsers()
+{
+ parser_deinit(ng_file_parser);
+ parser_deinit(ng_host_parser);
+}
+
+/**
+ * _netgroups_file_init - allocate a netgroup file struct
+ * @return: success: Pointer to an allocated netgroup file struct
+ * failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroups_file *
+_netgroups_file_init()
+{
+ struct netgroups_file *file = GF_MALLOC(sizeof(*file),
+ gf_common_mt_nfs_netgroups);
+
+ if (!file)
+ goto out;
+
+ file->filename = NULL;
+ file->ng_file_dict = NULL;
+out:
+ return file;
+}
+
+/**
+ * __ngf_free_walk - walk the netgroup file dict and free each element
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngf_free_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *nge = NULL;
+
+ if (val) {
+ nge = (struct netgroup_entry *)val->data;
+ _netgroup_entry_deinit(nge);
+ val->data = NULL;
+ dict_del(dict, key); /* Remove the key from this dict */
+ }
+ return 0;
+}
+
+/**
+ * __deleted_entries_free_walk - free the strings in the temporary dict
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__deleted_entries_free_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ dict_del(dict, key);
+ return 0;
+}
+
+/**
+ * ng_file_deinit - Free the netgroup file struct and any memory
+ * that is allocated for its members.
+ *
+ * @ngfile : Pointer to the netgroup file structure that needs to be freed
+ * @return : Nothing
+ *
+ * External facing function.
+ *
+ * Should be called by the caller of ng_file_parse () in order to free
+ * the memory allocated when parsing the file.
+ */
+void
+ng_file_deinit(struct netgroups_file *ngfile)
+{
+ if (!ngfile) {
+ return;
+ }
+
+ __deleted_entries = dict_new();
+ GF_VALIDATE_OR_GOTO(GF_NG, __deleted_entries, out);
+
+ GF_FREE(ngfile->filename);
+ dict_foreach(ngfile->ng_file_dict, __ngf_free_walk, NULL);
+ dict_unref(ngfile->ng_file_dict);
+ GF_FREE(ngfile);
+
+ /* Clean up temporary dict we used to store "freed" names */
+ dict_foreach(__deleted_entries, __deleted_entries_free_walk, NULL);
+ dict_unref(__deleted_entries);
+ __deleted_entries = NULL;
+out:
+ return;
+}
+
+/**
+ * _netgroup_entry_init - Initializes a netgroup entry struct.
+ * A netgroup entry struct represents a single line in a netgroups file.
+ *
+ * @return : success: Pointer to a netgroup entry struct
+ * : failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroup_entry *
+_netgroup_entry_init()
+{
+ struct netgroup_entry *entry = GF_CALLOC(1, sizeof(*entry),
+ gf_common_mt_nfs_netgroups);
+ return entry;
+}
+
+/**
+ * __ngh_free_walk - walk the netgroup host dict and free the host
+ * structure associated with the key.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngh_free_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_host *ngh = NULL;
+
+ if (val) {
+ ngh = (struct netgroup_host *)val->data;
+ _netgroup_host_deinit(ngh);
+ val->data = NULL;
+ dict_del(dict, key);
+ }
+ return 0;
+}
+
+/**
+ * __nge_free_walk - walk the netgroup entry dict and free the netgroup entry
+ * structure associated with the key.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__nge_free_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *nge = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, dict, out);
+
+ if (val) {
+ nge = (struct netgroup_entry *)val->data;
+ if (!dict_get(__deleted_entries, key)) {
+ _netgroup_entry_deinit(nge);
+ val->data = NULL;
+ }
+ dict_del(dict, key);
+ }
+
+out:
+ return 0;
+}
+
+/**
+ * _netgroup_entry_deinit - Free memory pointed to by the parameter
+ * and any memory allocated for members
+ * in the struct. This function walks the
+ * netgroups and hosts dicts if they
+ * are allocated and frees them.
+ *
+ * @ngentry: Pointer to a netgroup entry struct that needs to be freed
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static void
+_netgroup_entry_deinit(struct netgroup_entry *ngentry)
+{
+ dict_t *ng_dict = NULL;
+ dict_t *host_dict = NULL;
+ char *name = NULL;
+ data_t *dint = NULL;
+
+ if (!ngentry)
+ return;
+
+ ng_dict = ngentry->netgroup_ngs;
+ host_dict = ngentry->netgroup_hosts;
+
+ if (ng_dict) {
+ /* Free the dict of netgroup entries */
+ dict_foreach(ng_dict, __nge_free_walk, NULL);
+ dict_unref(ng_dict);
+ ngentry->netgroup_ngs = NULL;
+ }
+
+ if (host_dict) {
+ /* Free the dict of host entries */
+ dict_foreach(host_dict, __ngh_free_walk, NULL);
+ dict_unref(host_dict);
+ ngentry->netgroup_hosts = NULL;
+ }
+
+ if (ngentry->netgroup_name) {
+ /* Keep track of the netgroup names we've deallocated
+ * We need to do this because of the nature of this data
+ * structure. This data structure may hold multiple
+ * pointers to an already freed object, but these are
+ * uniquely identifiable by the name. We keep track
+ * of these names so when we encounter a key who has
+ * an association to an already freed object, we don't
+ * free it twice.
+ */
+ name = strdupa(ngentry->netgroup_name);
+
+ dint = int_to_data(1);
+ dict_set(__deleted_entries, name, dint);
+
+ GF_FREE(ngentry->netgroup_name);
+ ngentry->netgroup_name = NULL;
+ }
+
+ GF_FREE(ngentry);
+}
+
+/**
+ * _netgroup_host_init - Initializes a netgroup host structure.
+ * A netgroup host struct represents an item in a line of a netgroups file that
+ * looks like this : (hostname,user,domain)
+ *
+ * @return : success: Pointer to a netgroup host struct
+ * : failure: NULL
+ *
+ * Not for external use.
+ */
+static struct netgroup_host *
+_netgroup_host_init()
+{
+ struct netgroup_host *host = GF_CALLOC(1, sizeof(*host),
+ gf_common_mt_nfs_netgroups);
+ return host;
+}
+
+/**
+ * _netgroup_host_deinit - Free memory pointed to by the parameter
+ * and any memory allocated for members in the struct.
+ *
+ * @nghost : Pointer to a netgroup host struct that needs to be freed
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static void
+_netgroup_host_deinit(struct netgroup_host *host)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_NG, host, err);
+
+ GF_FREE(host->hostname);
+ host->hostname = NULL;
+
+ GF_FREE(host->user);
+ host->user = NULL;
+
+ GF_FREE(host->domain);
+ host->domain = NULL;
+
+ GF_FREE(host);
+err:
+ return;
+}
+
+/**
+ * _nge_dict_get - Lookup a netgroup entry from the dict based
+ * on the netgroup name.
+ *
+ * @dict : The dict we are looking up from. This function makes the
+ * assumption that the type of underlying data in the dict is of type
+ * struct netgroup_entry. The behavior is not defined otherwise.
+ *
+ * @ngname : Key used to lookup in the dict.
+ *
+ * @return : success: Pointer to a netgroup entry
+ * failure: NULL (if no such key exists in the dict)
+ *
+ * Not for external use.
+ */
+static struct netgroup_entry *
+_nge_dict_get(dict_t *dict, const char *ngname)
+{
+ data_t *ngdata = NULL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO(GF_NG, ngname, err);
+
+ ngdata = dict_get(dict, (char *)ngname);
+ if (ngdata)
+ return (struct netgroup_entry *)ngdata->data;
+err:
+ return NULL;
+}
+
+/**
+ * _nge_dict_insert - Insert a netgroup entry into the dict using
+ * the netgroup name as the key.
+ *
+ * @dict : The dict we are inserting into.
+ *
+ * @nge : The data to insert into the dict.
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_nge_dict_insert(dict_t *dict, struct netgroup_entry *nge)
+{
+ data_t *ngdata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO(GF_NG, nge, err);
+
+ ngdata = bin_to_data(nge, sizeof(*nge));
+ dict_set(dict, nge->netgroup_name, ngdata);
+err:
+ return;
+}
+
+/**
+ * _ngh_dict_get - Lookup a netgroup host entry from the dict based
+ * on the hostname.
+ *
+ * @dict : The dict we are looking up from. This function makes the
+ * assumption that the type of underlying data in the dict is of type
+ * struct netgroup_host. The behavior is not defined otherwise.
+ *
+ * @ngname : Key used to lookup in the dict.
+ *
+ * @return : success: Pointer to a netgroup host entry
+ * failure: NULL (if no such key exists in the dict)
+ *
+ * Externally usable.
+ */
+struct netgroup_host *
+ngh_dict_get(dict_t *dict, const char *hostname)
+{
+ data_t *ngdata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO(GF_NG, hostname, err);
+
+ ngdata = dict_get(dict, (char *)hostname);
+ if (!ngdata)
+ goto err;
+
+ return (struct netgroup_host *)ngdata->data;
+
+err:
+ return NULL;
+}
+
+/**
+ * _ngh_dict_insert - Insert a netgroup host entry into the dict using
+ * the netgroup name as the key.
+ *
+ * @dict : The dict we are inserting into.
+ *
+ * @nge : The data to insert into the dict.
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_ngh_dict_insert(dict_t *dict, struct netgroup_host *ngh)
+{
+ data_t *ngdata = NULL;
+
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_NG, dict, err);
+ GF_VALIDATE_OR_GOTO(GF_NG, ngh, err);
+
+ ngdata = bin_to_data(ngh, sizeof(*ngh));
+ dict_set(dict, ngh->hostname, ngdata);
+err:
+ return;
+}
+
+/**
+ * _ngh_print - Prints the netgroup host in the
+ * format '(hostname,user,domain)'
+ *
+ * @ngh : The netgroup host to print out
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_ngh_print(const struct netgroup_host *ngh)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_NG, ngh, err);
+
+ printf("(%s,%s,%s)", ngh->hostname, ngh->user ? ngh->user : "",
+ ngh->domain ? ngh->domain : "");
+err:
+ return;
+}
+
+/**
+ * __nge_print_walk - walk the netgroup entry dict and print each entry
+ * associated with the key. This function prints
+ * entries of type 'struct netgroup_entry'.
+ *
+ * This is passed as a function pointer to dict_foreach ()
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__nge_print_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val)
+ _nge_print((struct netgroup_entry *)val->data);
+
+ return 0;
+}
+
+/**
+ * __ngh_print_walk - walk the netgroup entry dict and print each entry
+ * associated with the key. This function prints entries
+ * of type 'struct netgroup_host'
+ *
+ * This is passed as a function pointer to dict_foreach (),
+ * which is called from _nge_print ().
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngh_print_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ if (val)
+ _ngh_print((struct netgroup_host *)val->data);
+
+ return 0;
+}
+
+/**
+ * _nge_print - Prints the netgroup entry in the
+ * format '<netgroup name> <following entries>'
+ *
+ * @ngh : The netgroup entry to print out
+ *
+ * @return : nothing
+ *
+ * Not for external use.
+ */
+static void
+_nge_print(const struct netgroup_entry *nge)
+{
+ /* Validate args */
+ GF_VALIDATE_OR_GOTO(GF_NG, nge, err);
+
+ printf("%s ", nge->netgroup_name);
+ if (nge->netgroup_ngs)
+ dict_foreach(nge->netgroup_ngs, __nge_print_walk, NULL);
+
+ if (nge->netgroup_hosts)
+ dict_foreach(nge->netgroup_hosts, __ngh_print_walk, NULL);
+
+err:
+ return;
+}
+
+/**
+ * __ngf_print_walk - walk through each entry in the netgroups file and print it
+ * out. This calls helper functions _nge_print () to print
+ * the netgroup entries.
+ *
+ * This is passed as a function pointer to dict_foreach (),
+ * which is called from ng_file_print ().
+ *
+ * @dict: the dict we are walking
+ * @key : the key we are processing in the dict
+ * @val : the corresponding value in the dict
+ * @tmp : Pointer to additional data that may be passed in (not used)
+ *
+ * @return : Nothing
+ *
+ * Not for external use.
+ */
+static int
+__ngf_print_walk(dict_t *dict, char *key, data_t *val, void *tmp)
+{
+ struct netgroup_entry *snge = NULL;
+
+ if (val) {
+ snge = (struct netgroup_entry *)val->data;
+ _nge_print(snge);
+ printf("\n");
+ }
+ return 0;
+}
+
+/**
+ * ng_file_print - Prints the netgroup file in the
+ * format '<netgroup name> <following entries>', etc.
+ * The netgroup file is a dict of netgroup entries
+ * which, in turn is a combination of a other 'sub' netgroup
+ * entries and host entries. This function prints
+ * all of that out by calling the corresponding print functions
+ *
+ * @ngfile : The netgroup file to print out
+ *
+ * @return : nothing
+ *
+ * External facing function.
+ *
+ * Can be called on any valid 'struct netgroups_file *' type.
+ */
+void
+ng_file_print(const struct netgroups_file *ngfile)
+{
+ dict_foreach(ngfile->ng_file_dict, __ngf_print_walk, NULL);
+}
+
+/**
+ * ng_file_get_netgroup - Look up a netgroup entry from the netgroups file
+ * based on the netgroup name and return a pointer
+ * to the netgroup entry.
+ *
+ * @ngfile : The netgroup file to lookup from.
+ * @netgroup : The netgroup name used to lookup from the netgroup file.
+ *
+ * @return : nothing
+ *
+ * External facing function.
+ *
+ * Can be called on any valid 'struct netgroups_file *' type with a valid 'char
+ * *' as the lookup key.
+ */
+struct netgroup_entry *
+ng_file_get_netgroup(const struct netgroups_file *ngfile, const char *netgroup)
+{
+ data_t *ndata = NULL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, ngfile, err);
+ GF_VALIDATE_OR_GOTO(GF_NG, netgroup, err);
+
+ ndata = dict_get(ngfile->ng_file_dict, (char *)netgroup);
+ if (!ndata)
+ goto err;
+
+ return (struct netgroup_entry *)ndata->data;
+
+err:
+ return NULL;
+}
+
+/**
+ * __check_host_entry_str - Check if the host string which should be
+ * in the format '(host,user,domain)' is
+ * valid to be parsed. Currently checks
+ * if the # of commas is correct and there
+ * are no spaces in the string, but more
+ * checks can be added.
+ *
+ * @host_str : String to check
+ * @return : success: TRUE if valid
+ * failure: FALSE if not
+ *
+ * Not for external use.
+ */
+static gf_boolean_t
+__check_host_entry_str(const char *host_str)
+{
+ unsigned int comma_count = 0;
+ unsigned int i = 0;
+ gf_boolean_t str_valid = _gf_true;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, host_str, out);
+
+ for (i = 0; i < strlen(host_str); i++) {
+ if (host_str[i] == ',')
+ comma_count++;
+
+ /* Spaces are not allowed in this string. e.g, (a,b,c) is valid
+ * but (a, b,c) is not.
+ */
+ if (host_str[i] == ' ') {
+ str_valid = _gf_false;
+ goto out;
+ }
+ }
+
+ str_valid = (comma_count == 2);
+out:
+ return str_valid;
+}
+
+/**
+ * _parse_ng_host - Parse the netgroup host string into a netgroup host struct.
+ * The netgroup host string is structured as follows:
+ * (host, user, domain)
+ *
+ * @ng_str : String to parse
+ * @return : success: 0 if the parsing succeeded
+ * failure: -EINVAL for bad args, -ENOMEM for allocation errors,
+ * 1 for parsing errors.
+ *
+ * Not for external use.
+ */
+static int
+_parse_ng_host(char *ng_str, struct netgroup_host **ngh)
+{
+ struct netgroup_host *ng_host = NULL;
+ unsigned int parts = 0;
+ char *match = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, ng_str, out);
+ GF_VALIDATE_OR_GOTO(GF_NG, ngh, out);
+
+ if (!__check_host_entry_str(ng_str)) {
+ ret = 1; /* Parse failed */
+ goto out;
+ }
+
+ ret = parser_set_string(ng_host_parser, ng_str);
+ if (ret < 0)
+ goto out;
+
+ gf_msg_trace(GF_NG, 0, "parsing host string: %s", ng_str);
+
+ ng_host = _netgroup_host_init();
+ GF_CHECK_ALLOC(ng_host, ret, free_and_out); /* Sets ret to -ENOMEM on
+ * failure.
+ */
+ while ((match = parser_get_next_match(ng_host_parser)) != NULL) {
+ gf_msg_trace(GF_NG, 0, "found match: %s (parts=%d)", match, parts);
+
+ switch (parts) {
+ case 0:
+ ng_host->hostname = match;
+ break;
+ case 1:
+ ng_host->user = match;
+ break;
+ case 2:
+ ng_host->domain = match;
+ break;
+ default:
+ GF_FREE(match);
+ break;
+ };
+
+ /* We only allow three parts in the host string;
+ * The format for the string is (a,b,c)
+ */
+ parts++;
+ if (parts > 2)
+ break;
+ }
+
+ /* Set the parameter */
+ *ngh = ng_host;
+ ret = 0;
+
+free_and_out:
+ parser_unset_string(ng_host_parser);
+out:
+ return ret;
+}
+
+/**
+ * _ng_handle_host_part - Parse the host string that looks like this :
+ * '(dev1763.prn2.facebook.com,,)' into a host
+ * struct and insert it into the parent netgroup's
+ * host dict.
+ * @match : The host string
+ * @ngp : The parent netgroup
+ *
+ * @return: success: 0 if parsing succeeded
+ * failure: -EINVAL for bad args, other errors bubbled up
+ * from _parse_ng_host.
+ *
+ *
+ * Not for external use.
+ */
+static int
+_ng_handle_host_part(char *match, struct netgroup_entry *ngp)
+{
+ struct netgroup_host *ngh = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, match, out);
+ GF_VALIDATE_OR_GOTO(GF_NG, ngp, out);
+
+ if (!ngp->netgroup_name) {
+ gf_msg(GF_NG, GF_LOG_WARNING, EINVAL, NFS_MSG_INVALID_ENTRY,
+ "Invalid: Line starts with hostname!");
+ goto out;
+ }
+
+ /* Parse the host string and get a struct for it */
+ ret = _parse_ng_host(match, &ngh);
+ if (ret < 0) {
+ gf_msg(GF_NG, GF_LOG_CRITICAL, -ret, NFS_MSG_PARSE_FAIL,
+ "Critical error : %s", strerror(-ret));
+ goto out;
+ }
+ if (ret != 0) {
+ /* Cannot change to gf_msg
+ * gf_msg not giving output to STDOUT
+ * Bug id : BZ1215017
+ */
+ gf_log(GF_NG, GF_LOG_WARNING, "Parse error for: %s", match);
+ goto out;
+ }
+
+ /* Make dict for the parent entry's netgroup hosts */
+ if (!ngp->netgroup_hosts) {
+ ngp->netgroup_hosts = dict_new();
+ GF_CHECK_ALLOC(ngp->netgroup_hosts, ret, out);
+ }
+
+ /* Insert this entry into the parent netgroup dict */
+ _ngh_dict_insert(ngp->netgroup_hosts, ngh);
+
+out:
+ return ret;
+}
+
+/**
+ * _ng_handle_netgroup_part - Parse the netgroup string that should just be one
+ * string. This may insert the netgroup into the file
+ * struct if it does not already exist. Frees the
+ * parameter match if the netgroup was already found
+ * in the file.
+ *
+ * @match : The netgroup string
+ * @ngp : The netgroup file we may insert the entry into
+ * @ng_entry : Double pointer to the netgroup entry we want to allocate and set.
+ *
+ * @return: success: 0 if parsing succeeded
+ * failure: -EINVAL for bad args, other errors bubbled up
+ * from _parse_ng_host.
+ *
+ *
+ * Not for external use.
+ */
+static int
+_ng_setup_netgroup_entry(char *match, struct netgroups_file *file,
+ struct netgroup_entry **ng_entry)
+{
+ struct netgroup_entry *nge = NULL;
+ int ret = -EINVAL;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, match, out);
+ GF_VALIDATE_OR_GOTO(GF_NG, file, out);
+ GF_VALIDATE_OR_GOTO(GF_NG, ng_entry, out);
+
+ nge = _netgroup_entry_init();
+ GF_CHECK_ALLOC(nge, ret, out);
+
+ nge->netgroup_name = match;
+
+ /* Insert this new entry into the file dict */
+ _nge_dict_insert(file->ng_file_dict, nge);
+
+ *ng_entry = nge;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * _parse_ng_line - Parse a line in the netgroups file into a netgroup entry
+ * struct. The netgroup line is structured as follows:
+ * 'netgroupx netgroupy (hosta,usera,domaina)...' OR
+ * 'netgroupx netgroupy netgroupz...' OR
+ * 'netgroupx (hosta,usera,domaina) (hostb,userb,domainb)'
+ * This function parses this into a netgroup entry
+ * which will hold either a dict of netgroups and/or
+ * a dict of hosts that make up this netgroup.
+ *
+ * In general terms, the data structure to represent a netgroups file
+ * is a set of nested dictionaries. Each line in the netgroups file
+ * is compiled into a struct netgroup_entry structure that holds a dict
+ * of netgroups and a dict of hostnames. The first string in the netgroups
+ * line is the parent netgroup entry and the rest of the items in the line
+ * are the children of that parent netgroup entry. (Hence variables ngp
+ * and nge).
+ *
+ * A sample netgroup file may look like this:
+ *
+ * async async.ash3 async.ash4
+ * async.ash3 async.04.ash3
+ * async04.ash3 (async001.ash3.facebook.com,,) (async002.ash3.facebook.com,,)
+ *
+ * _parse_ng_line will get called on each line, so on the first call to this
+ * function, our data structure looks like this:
+ *
+ *
+ * dict [
+ * 'async' --> dict [
+ * 'async.ash3'
+ * 'async.ash4'
+ * ]
+ * ]
+ *
+ * On the second call to the function with the second line, our data structure
+ * looks like this:
+ *
+ * dict [
+ * 'async' --> dict [
+ * 'async.ash3' -> dict [ 'async.04.ash3' ]
+ * 'async.ash4' ^
+ * ] |
+ * |
+ * 'async.ash3' ------------------------------
+ * ]
+ *
+ * And so on.
+ *
+ * The obvious answer to storing this file in a data structure may be a tree
+ * but lookups from a tree are expensive and since we may be looking up stuff
+ * in this file in the I/O path, we can't afford expensive lookups.
+ *
+ * @ng_str : String to parse
+ * @file : Netgroup file to put the parsed line into
+ * @ng_entry : Double pointer to struct that we are going to allocate and fill
+ *
+ * The string gets parsed into a structure pointed to by
+ * the parameter 'ng_entry'
+ *
+ * @return : success: 0 if parsing succeeded
+ * failure: NULL if not
+ *
+ * Not for external use.
+ */
+static int
+_parse_ng_line(char *ng_str, struct netgroups_file *file,
+ struct netgroup_entry **ng_entry)
+{
+ struct netgroup_entry *ngp = NULL; /* Parent netgroup entry */
+ struct netgroup_entry *nge = NULL; /* Generic netgroup entry */
+ char *match = NULL;
+ int ret = -EINVAL;
+ unsigned int num_entries = 0;
+
+ /* Validate arguments */
+ GF_VALIDATE_OR_GOTO(GF_NG, ng_str, out);
+ GF_VALIDATE_OR_GOTO(GF_NG, file, out);
+
+ if (*ng_str == ' ' || *ng_str == '\0' || *ng_str == '\n') {
+ ret = 0;
+ goto out;
+ }
+
+ ret = parser_set_string(ng_file_parser, ng_str);
+ if (ret < 0)
+ goto out;
+
+ /* This is the first name in the line, and should be the
+ * parent netgroup entry.
+ */
+ match = parser_get_next_match(ng_file_parser);
+ if (!match) {
+ ret = 1;
+ gf_msg(GF_NG, GF_LOG_WARNING, 0, NFS_MSG_FIND_FIRST_MATCH_FAIL,
+ "Unable to find "
+ "first match.");
+ gf_msg(GF_NG, GF_LOG_WARNING, 0, NFS_MSG_PARSE_FAIL,
+ "Error parsing str: %s", ng_str);
+ goto out;
+ }
+
+ /* Lookup to see if the match already exists,
+ * if not, set the parent.
+ */
+ ngp = _nge_dict_get(file->ng_file_dict, match);
+ if (!ngp) {
+ ret = _ng_setup_netgroup_entry(match, file, &ngp);
+ if (ret < 0) {
+ /* Bubble up error to caller. We don't need to free ngp
+ * here because this can only fail if allocating the
+ * struct fails.
+ */
+ goto out;
+ }
+ } else
+ GF_FREE(match);
+
+ if (!ngp->netgroup_ngs) {
+ /* If a netgroup dict has not been allocated
+ * for this parent, allocate it.
+ */
+ ngp->netgroup_ngs = dict_new();
+ GF_CHECK_ALLOC(ngp->netgroup_ngs, ret, out);
+ /* No need to free anything here since ngp is already
+ * a part of the file. When the file gets
+ * deallocated, we will free ngp.
+ */
+ }
+
+ while ((match = parser_get_next_match(ng_file_parser)) != NULL) {
+ num_entries++;
+ /* This means that we hit a host entry in the line */
+ if (*match == '(') {
+ ret = _ng_handle_host_part(match, ngp);
+ GF_FREE(match);
+ if (ret != 0) {
+ /* If parsing the host fails, bubble the error
+ * code up to the caller.
+ */
+ goto out;
+ }
+ } else {
+ nge = _nge_dict_get(file->ng_file_dict, match);
+ if (!nge) {
+ ret = _ng_setup_netgroup_entry(match, file, &nge);
+ if (ret < 0) {
+ /* Bubble up error to caller. We don't
+ * need to free nge here because this
+ * can only fail if allocating the
+ * struct fails.
+ */
+ goto out;
+ }
+ } else
+ GF_FREE(match);
+
+ /* Insert the netgroup into the parent's dict */
+ _nge_dict_insert(ngp->netgroup_ngs, nge);
+ }
+ }
+
+ /* If there are no entries on the RHS, log an error, but continue */
+ if (!num_entries) {
+ /* Cannot change to gf_msg
+ * gf_msg not giving output to STDOUT
+ * Bug id : BZ1215017
+ */
+ gf_log(GF_NG, GF_LOG_WARNING,
+ "No netgroups were specified except for the parent.");
+ }
+
+ *ng_entry = ngp;
+ ret = 0;
+
+out:
+ parser_unset_string(ng_file_parser);
+ return ret;
+}
+
+/**
+ * ng_file_parse - Parse a netgroups file into a the netgroups file struct.
+ * This is the external facing function that must be called
+ * to parse a netgroups file. This function returns a netgroup
+ * file struct that is allocated and must be freed using
+ * ng_file_deinit.
+ *
+ * @filepath : Path to the netgroups file we need to parse
+ *
+ * @return : success: Pointer to a netgroup file struct if parsing succeeded
+ * failure: NULL if not
+ *
+ * Externally facing function
+ */
+struct netgroups_file *
+ng_file_parse(const char *filepath)
+{
+ FILE *fp = NULL;
+ size_t len = 0;
+ size_t read = 0;
+ char *line = NULL;
+ struct netgroups_file *file = NULL;
+ struct netgroup_entry *nge = NULL;
+ int ret = 0;
+
+ GF_VALIDATE_OR_GOTO(GF_NG, filepath, err);
+
+ fp = fopen(filepath, "r");
+ if (!fp)
+ goto err;
+
+ file = _netgroups_file_init();
+ if (!file)
+ goto err;
+
+ file->ng_file_dict = dict_new();
+ if (!file->ng_file_dict) {
+ gf_msg(GF_NG, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to allocate netgroup file dict");
+ goto err;
+ }
+
+ file->filename = gf_strdup(filepath);
+ if (!file->filename) {
+ gf_msg(GF_NG, GF_LOG_CRITICAL, errno, NFS_MSG_FILE_OP_FAILED,
+ "Failed to duplicate filename");
+ goto err;
+ }
+
+ ret = _ng_init_parsers();
+ if (ret < 0)
+ goto err;
+
+ /* Read the file line-by-line and parse it */
+ while ((read = getline(&line, &len, fp)) != -1) {
+ if (*line == '#') /* Lines starting with # are comments */
+ continue;
+
+ /* Parse the line into a netgroup entry */
+ ret = _parse_ng_line(line, file, &nge);
+ if (ret == -ENOMEM) {
+ gf_msg(GF_NG, GF_LOG_CRITICAL, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Allocation error "
+ "while parsing line!");
+ goto err;
+ }
+ if (ret != 0) {
+ gf_msg_debug(GF_NG, 0, "Failed to parse line %s", line);
+ continue;
+ }
+ }
+
+ /* line got allocated through getline(), don't use GF_FREE() for it */
+ free(line);
+
+ if (fp)
+ fclose(fp);
+
+ _ng_deinit_parsers();
+
+ return file;
+
+err:
+ if (line)
+ free(line);
+
+ if (file)
+ ng_file_deinit(file);
+
+ _ng_deinit_parsers();
+
+ if (fp)
+ fclose(fp);
+ return NULL;
+}
diff --git a/xlators/nfs/server/src/netgroups.h b/xlators/nfs/server/src/netgroups.h
new file mode 100644
index 00000000000..9c715f75d3c
--- /dev/null
+++ b/xlators/nfs/server/src/netgroups.h
@@ -0,0 +1,53 @@
+/*
+ Copyright 2014-present Facebook. All Rights Reserved
+
+ This file is part of GlusterFS.
+
+ Author :
+ Shreyas Siravara <shreyas.siravara@gmail.com>
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+#ifndef _NETGROUPS_H
+#define _NETGROUPS_H
+
+#include "nfs-mem-types.h"
+#include <glusterfs/dict.h>
+#include "nfs.h"
+
+#define GF_NG GF_NFS "-netgroup"
+
+#define NG_FILE_PARSE_REGEX "([a-zA-Z0-9.(,)-]+)"
+#define NG_HOST_PARSE_REGEX "([a-zA-Z0-9.-]+)"
+
+struct netgroup_host {
+ char *hostname; /* Hostname of entry */
+ char *user; /* User field in the entry */
+ char *domain; /* Domain field in the entry */
+};
+
+struct netgroup_entry {
+ char *netgroup_name; /* Name of the netgroup */
+ dict_t *netgroup_ngs; /* Dict of netgroups in this netgroup */
+ dict_t *netgroup_hosts; /* Dict of hosts in this netgroup. */
+};
+
+struct netgroups_file {
+ char *filename; /* Filename on disk */
+ dict_t *ng_file_dict; /* Dict of netgroup entries */
+};
+
+struct netgroups_file *
+ng_file_parse(const char *filepath);
+
+struct netgroup_entry *
+ng_file_get_netgroup(const struct netgroups_file *ngfile, const char *netgroup);
+
+void
+ng_file_deinit(struct netgroups_file *ngfile);
+
+#endif /* _NETGROUPS_H */
diff --git a/xlators/nfs/server/src/nfs-common.c b/xlators/nfs/server/src/nfs-common.c
index b5c56d03df6..b8f6b6f318e 100644
--- a/xlators/nfs/server/src/nfs-common.c
+++ b/xlators/nfs/server/src/nfs-common.c
@@ -2,313 +2,304 @@
Copyright (c) 2010-2011 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/>.
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
*/
-#ifndef _CONFIG_H
-#define _CONFIG_H
-#include "config.h"
-#endif
-
#include "rpcsvc.h"
-#include "dict.h"
-#include "xlator.h"
+#include <glusterfs/dict.h>
+#include <glusterfs/xlator.h>
#include "xdr-nfs3.h"
#include "msg-nfs3.h"
-#include "iobuf.h"
+#include <glusterfs/iobuf.h>
#include "nfs-common.h"
#include "nfs-fops.h"
#include "nfs-mem-types.h"
#include "rpcsvc.h"
-#include "iatt.h"
+#include <glusterfs/iatt.h>
+#include "nfs-messages.h"
#include <libgen.h>
xlator_t *
-nfs_xlid_to_xlator (xlator_list_t *cl, uint8_t xlid)
+nfs_xlid_to_xlator(xlator_list_t *cl, uint8_t xlid)
{
- xlator_t *xl = NULL;
- uint8_t id = 0;
-
- while (id <= xlid) {
- if (!cl) {
- xl = NULL;
- break;
- }
-
- xl = cl->xlator;
- cl = cl->next;
- id++;
+ xlator_t *xl = NULL;
+ uint8_t id = 0;
+
+ while (id <= xlid) {
+ if (!cl) {
+ xl = NULL;
+ break;
}
- return xl;
-}
+ xl = cl->xlator;
+ cl = cl->next;
+ id++;
+ }
+ return xl;
+}
xlator_t *
-nfs_path_to_xlator (xlator_list_t *cl, char *path)
+nfs_path_to_xlator(xlator_list_t *cl, char *path)
{
- return NULL;
+ return NULL;
}
-
uint16_t
-nfs_xlator_to_xlid (xlator_list_t *cl, xlator_t *xl)
+nfs_xlator_to_xlid(xlator_list_t *cl, xlator_t *xl)
{
- uint16_t xlid = 0;
+ uint16_t xlid = 0;
- if ((!cl) || (!xl))
- return 0;
+ if ((!cl) || (!xl))
+ return 0;
- while (cl) {
- if (xl == cl->xlator)
- break;
- cl = cl->next;
- ++xlid;
- }
+ while (cl) {
+ if (xl == cl->xlator)
+ break;
+ cl = cl->next;
+ ++xlid;
+ }
- return xlid;
+ return xlid;
}
-
xlator_t *
-nfs_mntpath_to_xlator (xlator_list_t *cl, char *path)
+nfs_mntpath_to_xlator(xlator_list_t *cl, char *path)
{
- char volname[MNTPATHLEN];
- char *volptr = NULL;
- int pathlen = 0;
- xlator_t *targetxl = NULL;
-
- if ((!cl) || (!path))
- return NULL;
-
- strcpy (volname, path);
- pathlen = strlen (volname);
- gf_log (GF_NFS, GF_LOG_TRACE, "Subvolume search: %s", path);
- if (volname[0] == '/')
- volptr = &volname[1];
- else
- volptr = &volname[0];
-
- if (volname[pathlen - 1] == '/')
- volname[pathlen - 1] = '\0';
-
- while (cl) {
- if (strcmp (volptr, cl->xlator->name) == 0) {
- targetxl = cl->xlator;
- break;
- }
-
- cl = cl->next;
- }
+ char *volname = NULL; /* volume name only */
+ char *volptr = NULL; /* ptr to original volname */
+ size_t pathlen = -1;
+ xlator_t *targetxl = NULL;
+ int i = 0;
- return targetxl;
+ if ((!cl) || (!path))
+ return NULL;
-}
+ gf_msg_trace(GF_NFS, 0, "Subvolume search: %s", path);
+ volname = volptr = gf_strdup(path);
+ if (!volname)
+ return NULL;
-/* Returns 1 if the stat seems to be filled with zeroes. */
-int
-nfs_zero_filled_stat (struct iatt *buf)
-{
- if (!buf)
- return 1;
+ if (volname[0] == '/')
+ volname++;
+
+ pathlen = strlen(volname);
+ for (i = 0; i < pathlen; i++) {
+ if (volname[i] == '/') {
+ volname[i] = '\0';
+ break;
+ }
+ }
- /* Do not use st_dev because it is transformed to store the xlator id
- * in place of the device number. Do not use st_ino because by this time
- * we've already mapped the root ino to 1 so it is not guaranteed to be
- * 0.
- */
- if ((buf->ia_nlink == 0) && (buf->ia_ctime == 0))
- return 1;
+ while (cl) {
+ gf_msg_trace(GF_NFS, 0, "Volname: %s and cl->xlator->name: %s", volname,
+ cl->xlator->name);
- return 0;
-}
+ if (strcmp(volname, cl->xlator->name) == 0) {
+ targetxl = cl->xlator;
+ break;
+ }
+
+ cl = cl->next;
+ }
+ GF_FREE(volptr);
+
+ return targetxl;
+}
void
-nfs_loc_wipe (loc_t *loc)
+nfs_loc_wipe(loc_t *loc)
{
- loc_wipe (loc);
+ loc_wipe(loc);
}
-
int
-nfs_loc_copy (loc_t *dst, loc_t *src)
+nfs_loc_copy(loc_t *dst, loc_t *src)
{
- int ret = -1;
+ int ret = -1;
- ret = loc_copy (dst, src);
+ ret = loc_copy(dst, src);
- return ret;
+ return ret;
}
-
int
-nfs_loc_fill (loc_t *loc, inode_t *inode, inode_t *parent, char *path)
+nfs_loc_fill(loc_t *loc, inode_t *inode, inode_t *parent, char *path)
{
- int ret = -EFAULT;
+ int ret = -EFAULT;
- if (!loc)
- return ret;
-
- if (inode) {
- loc->inode = inode_ref (inode);
- if (!uuid_is_null (inode->gfid))
- uuid_copy (loc->gfid, inode->gfid);
- }
+ if (!loc)
+ return ret;
- if (parent)
- loc->parent = inode_ref (parent);
-
- if (path) {
- loc->path = gf_strdup (path);
- if (!loc->path) {
- gf_log (GF_NFS, GF_LOG_ERROR, "strdup failed");
- goto loc_wipe;
- }
- loc->name = strrchr (loc->path, '/');
- if (loc->name)
- loc->name++;
+ if (inode) {
+ loc->inode = inode_ref(inode);
+ if (!gf_uuid_is_null(inode->gfid))
+ gf_uuid_copy(loc->gfid, inode->gfid);
+ }
+
+ if (parent)
+ loc->parent = inode_ref(parent);
+
+ if (path) {
+ loc->path = gf_strdup(path);
+ if (!loc->path) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "strdup failed");
+ goto loc_wipe;
}
+ loc->name = strrchr(loc->path, '/');
+ if (loc->name)
+ loc->name++;
+ }
- ret = 0;
+ ret = 0;
loc_wipe:
- if (ret < 0)
- nfs_loc_wipe (loc);
+ if (ret < 0)
+ nfs_loc_wipe(loc);
- return ret;
+ return ret;
}
-
int
-nfs_inode_loc_fill (inode_t *inode, loc_t *loc, int how)
+nfs_inode_loc_fill(inode_t *inode, loc_t *loc, int how)
{
- char *resolvedpath = NULL;
- inode_t *parent = NULL;
- int ret = -EFAULT;
+ char *resolvedpath = NULL;
+ inode_t *parent = NULL;
+ int ret = -EFAULT;
- if ((!inode) || (!loc))
- return ret;
-
- ret = inode_path (inode, NULL, &resolvedpath);
- if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "path resolution failed %s",
- resolvedpath);
- goto err;
- }
+ if ((!inode) || (!loc))
+ return ret;
- ret = nfs_loc_fill (loc, inode, parent, resolvedpath);
+ /* If gfid is not null, then the inode is already linked to
+ * the inode table, and not a newly created one. For newly
+ * created inode, inode_path returns null gfid as the path.
+ */
+ if (!gf_uuid_is_null(inode->gfid)) {
+ ret = inode_path(inode, NULL, &resolvedpath);
if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "loc fill resolution failed %s",
- resolvedpath);
- goto err;
+ gf_msg(GF_NFS, GF_LOG_ERROR, 0, NFS_MSG_PATH_RESOLVE_FAIL,
+ "path resolution "
+ "failed %s",
+ resolvedpath);
+ goto err;
}
-
- ret = 0;
+ }
+
+ if (resolvedpath == NULL) {
+ char tmp_path[GFID_STR_PFX_LEN + 1] = {
+ 0,
+ };
+ snprintf(tmp_path, sizeof(tmp_path), "<gfid:%s>", uuid_utoa(loc->gfid));
+ resolvedpath = gf_strdup(tmp_path);
+ } else {
+ parent = inode_parent(inode, loc->pargfid, NULL);
+ }
+
+ ret = nfs_loc_fill(loc, inode, parent, resolvedpath);
+ if (ret < 0) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, -ret, NFS_MSG_LOC_FILL_RESOLVE_FAIL,
+ "loc fill resolution failed %s", resolvedpath);
+ goto err;
+ }
+
+ ret = 0;
err:
- if (parent)
- inode_unref (parent);
+ if (parent)
+ inode_unref(parent);
- if (resolvedpath)
- GF_FREE (resolvedpath);
+ GF_FREE(resolvedpath);
- return ret;
+ return ret;
}
int
-nfs_gfid_loc_fill (inode_table_t *itable, uuid_t gfid, loc_t *loc, int how)
+nfs_gfid_loc_fill(inode_table_t *itable, uuid_t gfid, loc_t *loc, int how)
{
- int ret = -EFAULT;
- inode_t *inode = NULL;
-
- if (!loc)
- return ret;
-
- inode = inode_find (itable, gfid);
- if (!inode) {
- gf_log (GF_NFS, GF_LOG_TRACE, "Inode not found in itable, will try to create one.");
- if (how == NFS_RESOLVE_CREATE) {
- gf_log (GF_NFS, GF_LOG_TRACE, "Inode needs to be created.");
- inode = inode_new (itable);
- if (!inode) {
- gf_log (GF_NFS, GF_LOG_ERROR, "Failed to "
- "allocate memory");
- ret = -ENOMEM;
- goto err;
- }
-
- } else {
- gf_log (GF_NFS, GF_LOG_ERROR, "Inode not found in itable and no creation was requested.");
- ret = -ENOENT;
- goto err;
- }
+ int ret = -EFAULT;
+ inode_t *inode = NULL;
+
+ if (!loc)
+ return ret;
+
+ inode = inode_find(itable, gfid);
+ if (!inode) {
+ gf_msg_trace(GF_NFS, 0,
+ "Inode not found in itable, will "
+ "try to create one.");
+ if (how == NFS_RESOLVE_CREATE) {
+ gf_msg_trace(GF_NFS, 0, "Inode needs to be created.");
+ inode = inode_new(itable);
+ if (!inode) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "Failed to "
+ "allocate memory");
+ ret = -ENOMEM;
+ goto err;
+ }
+
} else {
- gf_log (GF_NFS, GF_LOG_TRACE, "Inode was found in the itable.");
- }
+ gf_msg(GF_NFS, GF_LOG_ERROR, ENOENT, NFS_MSG_INODE_NOT_FOUND,
+ "Inode not found in "
+ "itable and no creation was requested.");
+ ret = -ENOENT;
+ goto err;
+ }
+ } else {
+ gf_msg_trace(GF_NFS, 0, "Inode was found in the itable.");
+ }
- uuid_copy (loc->gfid, gfid);
+ gf_uuid_copy(loc->gfid, gfid);
- ret = nfs_inode_loc_fill (inode, loc, how);
- if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "Inode loc filling failed.: %s", strerror (-ret));
- goto err;
- }
+ ret = nfs_inode_loc_fill(inode, loc, how);
+ if (ret < 0) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, -ret, NFS_MSG_INODE_LOC_FILL_ERROR,
+ "Inode loc filling failed.: %s", strerror(-ret));
+ goto err;
+ }
err:
- if (inode)
- inode_unref (inode);
- return ret;
+ if (inode)
+ inode_unref(inode);
+ return ret;
}
-
int
-nfs_root_loc_fill (inode_table_t *itable, loc_t *loc)
+nfs_root_loc_fill(inode_table_t *itable, loc_t *loc)
{
- uuid_t rootgfid = {0, };
+ static uuid_t rootgfid = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
- rootgfid[15] = 1;
- return nfs_gfid_loc_fill (itable, rootgfid, loc, NFS_RESOLVE_EXIST);
+ return nfs_gfid_loc_fill(itable, rootgfid, loc, NFS_RESOLVE_EXIST);
}
-
-
int
-nfs_parent_inode_loc_fill (inode_t *parent, inode_t *entryinode, char *entry,
- loc_t *loc)
+nfs_parent_inode_loc_fill(inode_t *parent, inode_t *entryinode, char *entry,
+ loc_t *loc)
{
- int ret = -EFAULT;
- char *path = NULL;
+ int ret = -EFAULT;
+ char *path = NULL;
- if ((!parent) || (!entry) || (!loc) || (!entryinode))
- return ret;
+ if ((!parent) || (!entry) || (!loc) || (!entryinode))
+ return ret;
- ret = inode_path (parent, entry, &path);
- if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "path resolution failed %s",
- path);
- goto err;
- }
+ ret = inode_path(parent, entry, &path);
+ if (ret < 0) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, -ret, NFS_MSG_PATH_RESOLVE_FAIL,
+ "path resolution failed %s", path);
+ goto err;
+ }
- ret = nfs_loc_fill (loc, entryinode, parent, path);
- GF_FREE (path);
+ ret = nfs_loc_fill(loc, entryinode, parent, path);
+ GF_FREE(path);
err:
- return ret;
+ return ret;
}
-
/* Returns -1 if parent is not available, return -2 if the entry is not
* available. In case the return is going to be -2, and how = NFS_RESOLVE_CREATE
* it does however fill in the loc so that it can be used to perform a lookup
@@ -316,108 +307,144 @@ err:
* On other errors, return -3. 0 on success.
*/
int
-nfs_entry_loc_fill (inode_table_t *itable, uuid_t pargfid, char *entry,
- loc_t *loc, int how)
+nfs_entry_loc_fill(xlator_t *this, inode_table_t *itable, uuid_t pargfid,
+ char *entry, loc_t *loc, int how, gf_boolean_t *freshlookup)
{
- inode_t *parent = NULL;
- inode_t *entryinode = NULL;
- int ret = -3;
- char *resolvedpath = NULL;
- int pret = -3;
-
- if ((!itable) || (!entry) || (!loc))
- return ret;
-
- parent = inode_find (itable, pargfid);
-
- ret = -1;
- /* Will need hard resolution now */
- if (!parent)
- goto err;
-
- uuid_copy (loc->pargfid, pargfid);
-
- ret = -2;
- entryinode = inode_grep (itable, parent, entry);
- if (!entryinode) {
- if (how == NFS_RESOLVE_CREATE) {
- /* Even though we'll create the inode and the loc for
- * a missing inode, we still need to return -2 so
- * that the caller can use the filled loc to call
- * lookup.
- */
- entryinode = inode_new (itable);
- /* Cannot change ret because that must
- * continue to have -2.
- */
- pret = nfs_parent_inode_loc_fill (parent, entryinode,
- entry, loc);
- /* Only if parent loc fill fails, should we notify error
- * through ret, otherwise, we still need to force a
- * lookup by returning -2.
- */
- if (pret < 0)
- ret = -3;
- }
- goto err;
- }
+ inode_t *parent = NULL;
+ inode_t *entryinode = NULL;
+ int ret = -3;
+ char *resolvedpath = NULL;
+ int pret = -3;
- ret = inode_path (parent, entry, &resolvedpath);
- if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "path resolution failed %s",
- resolvedpath);
- ret = -3;
- goto err;
- }
+ if ((!itable) || (!entry) || (!loc))
+ return ret;
- ret = nfs_loc_fill (loc, entryinode, parent, resolvedpath);
- if (ret < 0) {
- gf_log (GF_NFS, GF_LOG_ERROR, "loc_fill failed %s",
- resolvedpath);
+ parent = inode_find(itable, pargfid);
+
+ ret = -1;
+ /* Will need hard resolution now */
+ if (!parent || inode_ctx_get(parent, this, NULL))
+ goto err;
+
+ gf_uuid_copy(loc->pargfid, pargfid);
+
+ ret = -2;
+ entryinode = inode_grep(itable, parent, entry);
+ if (!entryinode || inode_ctx_get(entryinode, this, NULL)) {
+ if (how == NFS_RESOLVE_CREATE) {
+ /* Even though we'll create the inode and the loc for
+ * a missing inode, we still need to return -2 so
+ * that the caller can use the filled loc to call
+ * lookup.
+ */
+ if (!entryinode) {
+ entryinode = inode_new(itable);
+ if (freshlookup)
+ *freshlookup = _gf_true;
+ }
+ /* Cannot change ret because that must
+ * continue to have -2.
+ */
+ pret = nfs_parent_inode_loc_fill(parent, entryinode, entry, loc);
+ /* Only if parent loc fill fails, should we notify error
+ * through ret, otherwise, we still need to force a
+ * lookup by returning -2.
+ */
+ if (pret < 0)
ret = -3;
}
+ goto err;
+ }
+
+ ret = inode_path(parent, entry, &resolvedpath);
+ if (ret < 0) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, -ret, NFS_MSG_PATH_RESOLVE_FAIL,
+ "path resolution failed %s", resolvedpath);
+ ret = -3;
+ goto err;
+ }
+
+ ret = nfs_loc_fill(loc, entryinode, parent, resolvedpath);
+ if (ret < 0) {
+ gf_msg(GF_NFS, GF_LOG_ERROR, 0, NFS_MSG_INODE_LOC_FILL_ERROR,
+ "loc_fill failed %s", resolvedpath);
+ ret = -3;
+ }
err:
- if (parent)
- inode_unref (parent);
+ if (parent)
+ inode_unref(parent);
- if (entryinode)
- inode_unref (entryinode);
+ if (entryinode)
+ inode_unref(entryinode);
- if (resolvedpath)
- GF_FREE (resolvedpath);
+ GF_FREE(resolvedpath);
- return ret;
+ return ret;
}
-
uint32_t
-nfs_hash_gfid (uuid_t gfid)
+nfs_hash_gfid(uuid_t gfid)
{
- uint32_t hash = 0;
- uint64_t msb64 = 0;
- uint64_t lsb64 = 0;
- uint32_t a1 = 0;
- uint32_t a2 = 0;
- uint32_t a3 = 0;
- uint32_t a4 = 0;
- uint32_t b1 = 0;
- uint32_t b2 = 0;
-
- memcpy (&msb64, &gfid[8], 8);
- memcpy (&lsb64, &gfid[0], 8);
-
- a1 = (msb64 << 32);
- a2 = (msb64 >> 32);
- a3 = (lsb64 << 32);
- a4 = (lsb64 >> 32);
-
- b1 = a1 ^ a4;
- b2 = a2 ^ a3;
-
- hash = b1 ^ b2;
-
- return hash;
-}
+ uint32_t hash = 0;
+ uint64_t msb64 = 0;
+ uint64_t lsb64 = 0;
+ uint32_t a1 = 0;
+ uint32_t a2 = 0;
+ uint32_t a3 = 0;
+ uint32_t a4 = 0;
+ uint32_t b1 = 0;
+ uint32_t b2 = 0;
+
+ if (__is_root_gfid(gfid))
+ return 0x1;
+ memcpy(&msb64, &gfid[8], 8);
+ memcpy(&lsb64, &gfid[0], 8);
+ a1 = (msb64 << 32);
+ a2 = (msb64 >> 32);
+ a3 = (lsb64 << 32);
+ a4 = (lsb64 >> 32);
+
+ b1 = a1 ^ a4;
+ b2 = a2 ^ a3;
+
+ hash = b1 ^ b2;
+
+ return hash;
+}
+
+void
+nfs_fix_generation(xlator_t *this, inode_t *inode)
+{
+ uint64_t raw_ctx = 0;
+ struct nfs_inode_ctx *ictx = NULL;
+ struct nfs_state *priv = NULL;
+ int ret = -1;
+
+ if (!inode) {
+ return;
+ }
+ priv = this->private;
+
+ if (inode_ctx_get(inode, this, &raw_ctx) == 0) {
+ ictx = (struct nfs_inode_ctx *)(uintptr_t)raw_ctx;
+ ictx->generation = priv->generation;
+ } else {
+ ictx = GF_CALLOC(1, sizeof(struct nfs_inode_ctx), gf_nfs_mt_inode_ctx);
+ if (!ictx) {
+ gf_msg(this->name, GF_LOG_ERROR, ENOMEM, NFS_MSG_NO_MEMORY,
+ "could not allocate nfs inode ctx");
+ return;
+ }
+ INIT_LIST_HEAD(&ictx->shares);
+ ictx->generation = priv->generation;
+ ret = inode_ctx_put(inode, this, (uint64_t)(uintptr_t)ictx);
+ if (ret) {
+ gf_msg(this->name, GF_LOG_ERROR, 0, NFS_MSG_INODE_CTX_STORE_FAIL,
+ "could not store nfs inode ctx");
+ return;
+ }
+ }
+}
diff --git a/xlators/nfs/server/src/nfs-common.h b/xlators/nfs/server/src/nfs-common.h
index 58dea70d0c7..bd80d8b3be5 100644
--- a/xlators/nfs/server/src/nfs-common.h
+++ b/xlators/nfs/server/src/nfs-common.h
@@ -2,86 +2,72 @@
Copyright (c) 2010-2011 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/>.
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
*/
#ifndef _NFS_COMMON_H_
#define _NFS_COMMON_H_
-#ifndef _CONFIG_H
-#define _CONFIG_H
-#include "config.h"
-#endif
-
#include <unistd.h>
-#include "xlator.h"
+#include <glusterfs/xlator.h>
#include "rpcsvc.h"
-#include "iatt.h"
-#include "uuid.h"
+#include <glusterfs/iatt.h>
+#include <glusterfs/compat-uuid.h>
-//NFS_PATH_MAX hard-coded to 4096 as a work around for bug 2476.
-//nfs server crashes when path received is longer than PATH_MAX
-#define NFS_PATH_MAX 4096
-#define NFS_NAME_MAX NAME_MAX
+// NFS_PATH_MAX hard-coded to 4096 as a work around for bug 2476.
+// nfs server crashes when path received is longer than PATH_MAX
+#define NFS_PATH_MAX 4096
+#define NFS_NAME_MAX NAME_MAX
-#define NFS_DEFAULT_CREATE_MODE 0644
+#define NFS_DEFAULT_CREATE_MODE 0600
extern xlator_t *
-nfs_xlid_to_xlator (xlator_list_t *cl, uint8_t xlid);
+nfs_xlid_to_xlator(xlator_list_t *cl, uint8_t xlid);
extern uint16_t
-nfs_xlator_to_xlid (xlator_list_t *cl, xlator_t *xl);
+nfs_xlator_to_xlid(xlator_list_t *cl, xlator_t *xl);
extern xlator_t *
-nfs_path_to_xlator (xlator_list_t *cl, char *path);
+nfs_path_to_xlator(xlator_list_t *cl, char *path);
extern xlator_t *
-nfs_mntpath_to_xlator (xlator_list_t *cl, char *path);
-
-extern int
-nfs_zero_filled_stat (struct iatt *buf);
+nfs_mntpath_to_xlator(xlator_list_t *cl, char *path);
extern void
-nfs_loc_wipe (loc_t *loc);
+nfs_loc_wipe(loc_t *loc);
extern int
-nfs_loc_copy (loc_t *dst, loc_t *src);
+nfs_loc_copy(loc_t *dst, loc_t *src);
extern int
-nfs_loc_fill (loc_t *loc, inode_t *inode, inode_t *parent, char *path);
+nfs_loc_fill(loc_t *loc, inode_t *inode, inode_t *parent, char *path);
-#define NFS_RESOLVE_EXIST 1
-#define NFS_RESOLVE_CREATE 2
+#define NFS_RESOLVE_EXIST 1
+#define NFS_RESOLVE_CREATE 2
extern int
-nfs_inode_loc_fill (inode_t *inode, loc_t *loc, int how);
+nfs_inode_loc_fill(inode_t *inode, loc_t *loc, int how);
extern int
-nfs_ino_loc_fill (inode_table_t *itable, uuid_t gfid, loc_t *l);
+nfs_ino_loc_fill(inode_table_t *itable, uuid_t gfid, loc_t *l);
extern int
-nfs_entry_loc_fill (inode_table_t *itable, uuid_t pargfid, char *entry,
- loc_t *loc, int how);
+nfs_entry_loc_fill(xlator_t *this, inode_table_t *itable, uuid_t pargfid,
+ char *entry, loc_t *loc, int how, gf_boolean_t *freshlookup);
extern int
-nfs_root_loc_fill (inode_table_t *itable, loc_t *loc);
+nfs_root_loc_fill(inode_table_t *itable, loc_t *loc);
extern uint32_t
-nfs_hash_gfid (uuid_t gfid);
+nfs_hash_gfid(uuid_t gfid);
extern int
-nfs_gfid_loc_fill (inode_table_t *itable, uuid_t gfid, loc_t *loc, int how);
+nfs_gfid_loc_fill(inode_table_t *itable, uuid_t gfid, loc_t *loc, int how);
+
+void
+nfs_fix_generation(xlator_t *this, inode_t *inode);
#endif
diff --git a/xlators/nfs/server/src/nfs-fops.c b/xlators/nfs/server/src/nfs-fops.c
index 428c82a8fda..4d8540c2c3e 100644
--- a/xlators/nfs/server/src/nfs-fops.c
+++ b/xlators/nfs/server/src/nfs-fops.c
@@ -2,280 +2,373 @@
Copyright (c) 2010-2011 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 dist