diff options
Diffstat (limited to 'xlators/nfs/server/src/mount3.c')
| -rw-r--r-- | xlators/nfs/server/src/mount3.c | 5208 |
1 files changed, 3774 insertions, 1434 deletions
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); +} |
