diff options
author | Shehjar Tikoo <shehjart@gluster.com> | 2010-06-15 00:39:25 +0000 |
---|---|---|
committer | Anand V. Avati <avati@dev.gluster.com> | 2010-07-04 22:52:51 -0700 |
commit | a8ef67630c48dfec336cc2088ac78cb29074022c (patch) | |
tree | 001788392afcd6b1a0287c8ac2bb57f34fb2a994 /xlators/nfs/server | |
parent | 2f15ffd6b5beef9abd501c594bc3cb38c2683f77 (diff) |
nfs: Support directory level exports
nfsx has followed traditional approach of exporting whole volumes
as NFS exports. The Platform requires and some users have approached us for
introducing exports of only specific directories instead of full Gluster
volumes. This commit introduces this support through two nfsx options:
Option 1:
=========
option nfs3.<volume-name>.export-dir <subdir1-in-vol>,<subdir2-in-vol>,..<subdirN-in-vol>
export-dir will allow the export of a particular dir as a single export
by itself. For eg.:
volume posix
type storage/posix
option directory /export/
end-volume
volume posix-ac
type features/access-control
subvolumes posix
end-volume
volume nfs
type nfs/server
subvolumes posix-ac
option rpc-auth.addr.allow *
option nfs3.posix-ac.export-dir /homes/shehjart
end-volume
A comma separate list of sub-directories will set up those dirs as separated exports.
At the nfs client, the mount command will be:
$ mount <nfsserver>:/posix-ac/homes/shehjart /mnt
Option 2:
=========
option nfs3.<volume-name>.export-volumes <on|off>
There can be situations where users only want the directory level
exports and require that volume exports be completely disabled. The
above option allows us to do this. By default, volume exports are
enabled. From the earlier example, replacing <volume-name> with
posix-ac, will disable mounting of the posix-ac volume as a whole.
Signed-off-by: Shehjar Tikoo <shehjart@gluster.com>
Signed-off-by: Anand V. Avati <avati@dev.gluster.com>
BUG: 989 (Support directory exports in nfsx)
URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=989
Diffstat (limited to 'xlators/nfs/server')
-rw-r--r-- | xlators/nfs/server/src/mount3.c | 694 | ||||
-rw-r--r-- | xlators/nfs/server/src/mount3.h | 36 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs-mem-types.h | 2 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs.c | 22 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3-fh.c | 3 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3-fh.h | 2 | ||||
-rw-r--r-- | xlators/nfs/server/src/nfs3.c | 2 |
7 files changed, 712 insertions, 49 deletions
diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c index 9b287cf907e..b943d1290c3 100644 --- a/xlators/nfs/server/src/mount3.c +++ b/xlators/nfs/server/src/mount3.c @@ -175,19 +175,19 @@ mnt3svc_set_mountres3 (mountstat3 stat, struct nfs3_fh *fh, int *authflavor, int mnt3svc_update_mountlist (struct mount3_state *ms, rpcsvc_request_t *req, - xlator_t *exportxl) + char *expname) { struct mountentry *me = NULL; int ret = -1; - if ((!ms) || (!req) || (!exportxl)) + if ((!ms) || (!req) || (!expname)) return -1; me = (struct mountentry *)GF_CALLOC (1, sizeof (*me), gf_nfs_mt_mountentry); if (!me) return -1; - strcpy (me->exname, exportxl->name); + 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. @@ -243,8 +243,8 @@ mnt3svc_lookup_mount_cbk (call_frame_t *frame, void *cookie, if (status != MNT3_OK) goto xmit_res; - fh = nfs3_fh_build_root_fh (ms->nfsx->children, this, *buf); - mnt3svc_update_mountlist (ms, req, this); + fh = nfs3_fh_build_root_fh (ms->nfsx->children, this); + mnt3svc_update_mountlist (ms, req, this->name); xmit_res: gf_log (GF_MNT, GF_LOG_DEBUG, "Mount reply status: %d", status); if (op_ret == 0) { @@ -261,26 +261,423 @@ xmit_res: int -mnt3svc_mount (rpcsvc_request_t *req, xlator_t *nfsx, xlator_t * xl) +mnt3_match_dirpath_export (char *expname, char *dirpath) { - loc_t oploc = {0, }; - int ret = -1; + int ret = 0; + int dlen = 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'; + + if (dirpath[0] != '/') + expname++; + + if (strcmp (expname, dirpath) == 0) + ret = 1; + + return ret; +} + + +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)) + if ((!req) || (!xl) || (!ms) || (!exportinode)) return ret; - ret = nfs_ino_loc_fill (xl->itable, 1, 0, &oploc); + ret = nfs_inode_loc_fill (exportinode, &exportloc); + if (ret < 0) { + gf_log (GF_MNT, GF_LOG_ERROR, "Loc fill failed for export inode" + ": ino %"PRIu64", gen: %"PRIu64", volume: %s", + exportinode->ino, exportinode->generation, 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 (nfsx, xl, &nfu, &oploc, mnt3svc_lookup_mount_cbk, - (void *)req); - nfs_loc_wipe (&oploc); + ret = nfs_lookup (ms->nfsx, xl, &nfu, &exportloc, + mnt3svc_lookup_mount_cbk, (void *)req); + + nfs_loc_wipe (&exportloc); +err: + 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; + + if ((!req) || (!exp) || (!ms)) + return ret; + + exportinode = inode_get (exp->vol->itable, 1, 0); + if (!exportinode) { + gf_log (GF_MNT, GF_LOG_ERROR, "Faild to get root inode"); + ret = -ENOENT; + goto err; + } + + ret = mnt3svc_mount_inode (req, ms, exp->vol, exportinode); + inode_unref (exportinode); + +err: + 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 + * needs to start from the directory path from the root of the volume. For that + * we need to strip out the volume name first. + */ +char * +__volume_subdir (char *dirpath) +{ + char *subdir = NULL; + + if (!dirpath) + return NULL; + + if (dirpath[0] == '/') + dirpath++; + + subdir = index (dirpath, (int)'/'); + + return subdir; +} + + +void +mnt3_resolve_state_wipe (mnt3_resolve_t *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) +{ + char *comp = NULL; + char *nextcomp = NULL; + + if ((!path) || (!component)) + return NULL; + + strcpy (component, path); + comp = index (component, (int)'/'); + if (!comp) + goto err; + + comp++; + nextcomp = index (comp, (int)'/'); + if (nextcomp) { + strcpy (path, nextcomp); + *nextcomp = '\0'; + } else + path[0] = '\0'; + +err: + 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); + +/* 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; + uint64_t parino = 0; + uint64_t pargen = 0; + nfs_user_t nfu = {0, }; + + if (!mres) + return ret; + + nextcomp = __setup_next_component (mres->remainingdir, dupsubdir); + if (!nextcomp) + goto err; + + parino = mres->resolveloc.inode->ino; + pargen = mres->resolveloc.inode->generation; + /* Wipe the contents of the previous component */ + nfs_loc_wipe (&mres->resolveloc); + ret = nfs_entry_loc_fill (mres->exp->vol->itable, parino, pargen, + 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 %"PRIu64", gen: %"PRIu64", entry %s", + parino, pargen, nextcomp); + ret = -EFAULT; + goto err; + } + + nfs_request_user_init (&nfu, mres->req); + ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu, + &mres->resolveloc, mnt3_resolve_subdir_cbk, mres); + +err: + return ret; +} + + +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_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, }; + + mres = frame->local; + + if (op_ret == -1) { + mntstat = mnt3svc_errno_to_mnterr (op_errno); + 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; + } + + mres->parentfh = fh; + op_ret = __mnt3_resolve_export_subdir_comp (mres); + if (op_ret < 0) + 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, this->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); + } + + return 0; +} + + + +/* 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 + * file handle of the directory. Our file handle creation code is designed with + * the assumption that to build a child file/dir fh, we'll always have the + * parent dir's fh available so that we may copy the hash array of the previous + * dir levels. + * + * Since we do not store the file handles anywhere, for every mount request we + * must resolve the file handles of every component so that the parent dir file + * of the exported directory can be built. + */ +int +__mnt3_resolve_export_subdir (mnt3_resolve_t *mres) +{ + char dupsubdir[MNTPATHLEN]; + char *firstcomp = NULL; + int ret = -EFAULT; + nfs_user_t nfu = {0, }; + + if (!mres) + return ret; + + firstcomp = __setup_next_component (mres->remainingdir, dupsubdir); + if (!firstcomp) + goto err; + + ret = nfs_entry_loc_fill (mres->exp->vol->itable, 1, 0, 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; + } + + nfs_request_user_init (&nfu, mres->req); + ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu, + &mres->resolveloc, mnt3_resolve_subdir_cbk, mres); + +err: + return ret; +} + + +int +mnt3_resolve_export_subdir (rpcsvc_request_t *req, struct mount3_state *ms, + struct mnt3_export *exp) +{ + mnt3_resolve_t *mres = NULL; + char *volume_subdir = NULL; + int ret = -EFAULT; + + if ((!req) || (!ms) || (!exp)) + return ret; + + volume_subdir = __volume_subdir (exp->expname); + if (!volume_subdir) + goto err; + + 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; + } + + mres->exp = exp; + mres->mstate = ms; + mres->req = req; + strcpy (mres->remainingdir, volume_subdir); + mres->parentfh = nfs3_fh_build_root_fh (mres->mstate->nfsx->children, + mres->exp->vol); + + ret = __mnt3_resolve_export_subdir (mres); + if (ret < 0) { + gf_log (GF_MNT, GF_LOG_ERROR, "Failed to resolve export dir: %s" + , mres->exp->expname); + GF_FREE (mres); + } + +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. +*/ +struct mnt3_export * +mnt3_mntpath_to_export (struct mount3_state *ms, char *dirpath) +{ + struct mnt3_export *exp = NULL; + struct mnt3_export *found = NULL; + + if ((!ms) || (!dirpath)) + return NULL; + + 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)) { + found = exp; + gf_log (GF_MNT, GF_LOG_DEBUG, "Found export volume: " + "%s", exp->vol->name); + goto foundexp; + } + } + gf_log (GF_MNT, GF_LOG_DEBUG, "Export not found"); +foundexp: + return found; +} + + +int +mnt3_check_client_net (struct mount3_state *ms, rpcsvc_request_t *req, + xlator_t *targetxl) +{ + rpcsvc_t *svc = NULL; + int ret = -1; + + if ((!ms) || (!req) || (!targetxl)) + return -1; + + svc = rpcsvc_request_service (req); + ret = rpcsvc_conn_peer_check (svc->options, targetxl->name, + rpcsvc_request_conn (req)); + if (ret == RPCSVC_AUTH_REJECT) { + gf_log (GF_MNT, GF_LOG_TRACE, "Peer not allowed"); + goto err; + } + + ret = rpcsvc_conn_privport_check (svc, targetxl->name, + rpcsvc_request_conn (req)); + if (ret == RPCSVC_AUTH_REJECT) { + gf_log (GF_MNT, GF_LOG_TRACE, "Unprivileged port not allowed"); + goto err; + } + + ret = 0; +err: return ret; } @@ -291,10 +688,9 @@ mnt3svc_mnt (rpcsvc_request_t *req) struct iovec pvec = {0, }; char path[MNTPATHLEN]; int ret = -1; - xlator_t *targetxl = NULL; struct mount3_state *ms = NULL; - rpcsvc_t *svc = NULL; mountstat3 mntstat = MNT3ERR_SERVERFAULT; + struct mnt3_export *exp = NULL; if (!req) return -1; @@ -318,35 +714,26 @@ mnt3svc_mnt (rpcsvc_request_t *req) ret = 0; gf_log (GF_MNT, GF_LOG_DEBUG, "dirpath: %s", path); - targetxl = nfs_mntpath_to_xlator (ms->nfsx->children, path); - if (!targetxl) { + exp = mnt3_mntpath_to_export (ms, path); + if (!exp) { ret = -1; mntstat = MNT3ERR_NOENT; goto mnterr; } - svc = rpcsvc_request_service (req); - ret = rpcsvc_conn_peer_check (svc->options, targetxl->name, - rpcsvc_request_conn (req)); - if (ret == RPCSVC_AUTH_REJECT) { - mntstat = MNT3ERR_ACCES; - ret = -1; - gf_log (GF_MNT, GF_LOG_TRACE, "Peer not allowed"); - goto mnterr; - } - - ret = rpcsvc_conn_privport_check (svc, targetxl->name, - rpcsvc_request_conn (req)); - if (ret == RPCSVC_AUTH_REJECT) { + ret = mnt3_check_client_net (ms, req, exp->vol); + if (ret == -1) { mntstat = MNT3ERR_ACCES; ret = -1; - gf_log (GF_MNT, GF_LOG_TRACE, "Unprivileged port not allowed"); + gf_log (GF_MNT, GF_LOG_DEBUG, "Client mount not allowed"); goto rpcerr; } - mnt3svc_mount (req, ms->nfsx, targetxl); + ret = mnt3svc_mount (req, ms, exp); + if (ret < 0) + mntstat = mnt3svc_errno_to_mnterr (-ret); mnterr: - if (ret == -1) { + if (ret < 0) { mnt3svc_mnt_error_reply (req, mntstat); ret = 0; } @@ -705,7 +1092,7 @@ rpcerr: exports -mnt3_xlchildren_to_exports (rpcsvc_t *svc, xlator_list_t *cl) +mnt3_xlchildren_to_exports (rpcsvc_t *svc, struct mount3_state *ms) { struct exportnode *elist = NULL; struct exportnode *prev = NULL; @@ -713,12 +1100,13 @@ mnt3_xlchildren_to_exports (rpcsvc_t *svc, xlator_list_t *cl) size_t namelen = 0; int ret = -1; char *addrstr = NULL; + struct mnt3_export *ent = NULL; - if ((!cl) || (!svc)) + if ((!ms) || (!svc)) return NULL; - while (cl) { - namelen = strlen (cl->xlator->name); + list_for_each_entry(ent, &ms->exportlist, explist) { + 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" @@ -734,10 +1122,9 @@ mnt3_xlchildren_to_exports (rpcsvc_t *svc, xlator_list_t *cl) goto free_list; } - strcpy (elist->ex_dir, "/"); - strcat (elist->ex_dir, cl->xlator->name); + strcpy (elist->ex_dir, ent->expname); - addrstr = rpcsvc_volume_allowed (svc->options,cl->xlator->name); + addrstr = rpcsvc_volume_allowed (svc->options, ent->vol->name); if (addrstr) addrstr = gf_strdup (addrstr); else @@ -760,8 +1147,6 @@ mnt3_xlchildren_to_exports (rpcsvc_t *svc, xlator_list_t *cl) if (!first) first = elist; - - cl = cl->next; } ret = 0; @@ -794,8 +1179,7 @@ mnt3svc_export (rpcsvc_request_t *req) } /* Using the children translator names, build the export list */ - elist = mnt3_xlchildren_to_exports (rpcsvc_request_service (req), - ms->nfsx->children); + elist = mnt3_xlchildren_to_exports (rpcsvc_request_service (req), ms); if (!elist) { gf_log (GF_MNT, GF_LOG_ERROR, "Failed to build exports list"); rpcsvc_request_seterr (req, SYSTEM_ERR); @@ -813,10 +1197,224 @@ err: } +struct mnt3_export * +mnt3_init_export_ent (struct mount3_state *ms, xlator_t *xl, 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; + } + + 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; + } + + 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); + } + + exp->vol = xl; +err: + return exp; +} + + +int +__mnt3_init_volume_direxports (struct mount3_state *ms, xlator_t *xlator, + char *optstr) +{ + 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; + } + + token = strtok_r (dupopt, ",", &savptr); + while (token) { + newexp = mnt3_init_export_ent (ms, xlator, token); + if (!newexp) { + gf_log (GF_MNT, GF_LOG_ERROR, "Failed to init dir " + "export: %s", token); + ret = -1; + goto err; + } + + list_add_tail (&newexp->explist, &ms->exportlist); + token = strtok_r (NULL, ",", &savptr); + } + + ret = 0; +err: + if (dupopt) + GF_FREE (dupopt); + + return ret; +} + + +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; + + if ((!ms) || (!xlator) || (!opts)) + return -1; + + ret = snprintf (searchstr, 1024, "nfs3.%s.export-dir", xlator->name); + 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; + } + + ret = __mnt3_init_volume_direxports (ms, xlator, optstr); + if (ret == -1) { + gf_log (GF_MNT, GF_LOG_ERROR, "Dir export setup failed" + " for volume: %s", xlator->name); + goto err; + } + + } + + if (ms->export_volumes) { + newexp = mnt3_init_export_ent (ms, xlator, NULL); + if (!newexp) { + ret = -1; + goto err; + } + + list_add_tail (&newexp->explist, &ms->exportlist); + } + ret = 0; + + +err: + return ret; +} + + +int +__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; + } + + 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; + } + + gf_string2boolean (optstr, &boolt); + ret = 0; + +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; +} + + +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); + 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; + } + + ret = 0; +err: + return ret; +} + + struct mount3_state * mnt3_init_state (xlator_t *nfsx) { struct mount3_state *ms = NULL; + int ret = -1; if (!nfsx) return NULL; @@ -829,7 +1427,13 @@ mnt3_init_state (xlator_t *nfsx) ms->iobpool = nfsx->ctx->iobuf_pool; ms->nfsx = nfsx; - ms->exports = nfsx->children; + 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; + } + INIT_LIST_HEAD (&ms->mountlist); LOCK_INIT (&ms->mountlock); diff --git a/xlators/nfs/server/src/mount3.h b/xlators/nfs/server/src/mount3.h index 7cfd2b0cb84..f555bc7f18a 100644 --- a/xlators/nfs/server/src/mount3.h +++ b/xlators/nfs/server/src/mount3.h @@ -33,6 +33,7 @@ #include "list.h" #include "xdr-nfs3.h" #include "locking.h" +#include "nfs3-fh.h" /* Registered with portmap */ #define GF_MOUNTV3_PORT 38465 @@ -60,12 +61,28 @@ struct mountentry { char hostname[MNTPATHLEN]; }; +#define MNT3_EXPTYPE_VOLUME 1 +#define MNT3_EXPTYPE_DIR 2 + +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; +}; + struct mount3_state { xlator_t *nfsx; /* The buffers for all network IO are got from this pool. */ struct iobuf_pool *iobpool; - xlator_list_t *exports; + + /* 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. @@ -74,5 +91,22 @@ struct mount3_state { /* Used to protect the mountlist. */ gf_lock_t mountlock; + + /* Set to 0 if exporting full volumes is disabled. On by default. */ + int export_volumes; }; + + +struct mount3_resolve_state { + struct mnt3_export *exp; + struct mount3_state *mstate; + rpcsvc_request_t *req; + + char remainingdir[MNTPATHLEN]; + loc_t resolveloc; + struct nfs3_fh parentfh; +}; + +typedef struct mount3_resolve_state mnt3_resolve_t; + #endif diff --git a/xlators/nfs/server/src/nfs-mem-types.h b/xlators/nfs/server/src/nfs-mem-types.h index 118ee2d23b0..8913ba38b3f 100644 --- a/xlators/nfs/server/src/nfs-mem-types.h +++ b/xlators/nfs/server/src/nfs-mem-types.h @@ -40,6 +40,8 @@ enum gf_nfs_mem_types_ { gf_nfs_mt_nfs_initer_list, gf_nfs_mt_xlator_t, gf_nfs_mt_list_head, + gf_nfs_mt_mnt3_resolve, + gf_nfs_mt_mnt3_export, gf_nfs_mt_end }; #endif diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c index cb5f19ef992..d7d2a5fd6e2 100644 --- a/xlators/nfs/server/src/nfs.c +++ b/xlators/nfs/server/src/nfs.c @@ -578,6 +578,11 @@ fini (xlator_t *this) struct xlator_cbks cbks = { }; struct xlator_fops fops = { }; +/* TODO: If needed, per-volume options below can be extended to be export ++ * specific also because after export-dir is introduced, a volume is not ++ * neccessarily an export whereas different subdirectories within that volume ++ * can be and may need these options to be specified separately. ++ */ struct volume_options options[] = { { .key = {"nfs3.read-size"}, .type = GF_OPTION_TYPE_SIZET, @@ -624,6 +629,23 @@ struct volume_options options[] = { " trusted-write behaviour. Off by default." }, + { .key = {"nfs3.*.export-dir"}, + .type = GF_OPTION_TYPE_STR, + .description = "By default, all subvolumes of nfs are exported as " + "individual exports. There are cases where a " + "subdirectory or subdirectories in the volume need to " + "be exported separately. This option can also be used " + "in conjunction with nfs3.export-volumes option to " + "restrict exports only to the subdirectories specified" + " through this option. Must be an absolute path." + }, + { .key = {"nfs3.export-volumes"}, + .type = GF_OPTION_TYPE_BOOL, + .description = "Enable or disable exporting whole volumes, instead " + "if used in conjunction with nfs3.export-dir, can " + "allow setting up only subdirectories as exports. On " + "by default." + }, { .key = {"rpc-auth.auth-unix"}, .type = GF_OPTION_TYPE_BOOL, .description = "Disable or enable the AUTH_UNIX authentication type." diff --git a/xlators/nfs/server/src/nfs3-fh.c b/xlators/nfs/server/src/nfs3-fh.c index c7eb78fb378..59a78641105 100644 --- a/xlators/nfs/server/src/nfs3-fh.c +++ b/xlators/nfs/server/src/nfs3-fh.c @@ -75,9 +75,10 @@ nfs3_fh_init (struct nfs3_fh *fh, struct iatt *buf) struct nfs3_fh -nfs3_fh_build_root_fh (xlator_list_t *cl, xlator_t *xl, struct iatt buf) +nfs3_fh_build_root_fh (xlator_list_t *cl, xlator_t *xl) { struct nfs3_fh fh = {{0}, }; + struct iatt buf = {0, }; if ((!cl) || (!xl)) return fh; diff --git a/xlators/nfs/server/src/nfs3-fh.h b/xlators/nfs/server/src/nfs3-fh.h index f526edf43b0..73e4b7817c9 100644 --- a/xlators/nfs/server/src/nfs3-fh.h +++ b/xlators/nfs/server/src/nfs3-fh.h @@ -84,7 +84,7 @@ extern xlator_t * nfs3_fh_to_xlator (xlator_list_t *cl, struct nfs3_fh *fh); extern struct nfs3_fh -nfs3_fh_build_root_fh (xlator_list_t *cl, xlator_t *xl, struct iatt buf); +nfs3_fh_build_root_fh (xlator_list_t *cl, xlator_t *xl); extern int nfs3_fh_is_root_fh (struct nfs3_fh *fh); diff --git a/xlators/nfs/server/src/nfs3.c b/xlators/nfs/server/src/nfs3.c index fda6d6413bf..131596e8fd2 100644 --- a/xlators/nfs/server/src/nfs3.c +++ b/xlators/nfs/server/src/nfs3.c @@ -903,7 +903,7 @@ nfs3svc_lookup_parentdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, nfs3_fh_build_parent_fh (&cs->fh, buf, &newfh); else newfh = nfs3_fh_build_root_fh (cs->nfs3state->exportslist, - cs->vol, *buf); + cs->vol); xmit_res: nfs3_log_newfh_res (rpcsvc_request_xid (cs->req), "LOOKUP", status, |