summaryrefslogtreecommitdiffstats
path: root/xlators/nfs/server/src/nfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/nfs/server/src/nfs.c')
-rw-r--r--xlators/nfs/server/src/nfs.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c
new file mode 100644
index 00000000000..ca6fda69c29
--- /dev/null
+++ b/xlators/nfs/server/src/nfs.c
@@ -0,0 +1,645 @@
+/*
+ Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+/* This is the primary translator source for NFS.
+ * Every other protocol version gets initialized from here.
+ */
+
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "defaults.h"
+#include "rpcsvc.h"
+#include "dict.h"
+#include "xlator.h"
+#include "nfs.h"
+#include "mem-pool.h"
+#include "logging.h"
+#include "nfs-fops.h"
+#include "inode.h"
+
+/* Every NFS version must call this function with the init function
+ * for its particular version.
+ */
+int
+nfs_add_initer (struct list_head *list, nfs_version_initer_t init)
+{
+ struct nfs_initer_list *new = NULL;
+ if ((!list) || (!init))
+ return -1;
+
+ new = CALLOC (1, sizeof (*new));
+ if (!new) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "Memory allocation failed");
+ return -1;
+ }
+
+ new->init = init;
+ list_add_tail (&new->list, list);
+ return 0;
+}
+
+
+int
+nfs_deinit_versions (struct list_head *versions, xlator_t *this)
+{
+ struct nfs_initer_list *version = NULL;
+ struct nfs_initer_list *tmp = NULL;
+ struct nfs_state *nfs = NULL;
+
+ if ((!versions) || (!this))
+ return -1;
+
+ nfs = (struct nfs_state *)this->private;
+ list_for_each_entry_safe (version, tmp, versions, list) {
+ /* TODO: Add version specific destructor.
+ * if (!version->deinit)
+ goto err;
+
+ version->deinit (this);
+ */
+ if (version->program)
+ rpcsvc_program_unregister (nfs->rpcsvc,
+ *(version->program));
+
+ list_del (&version->list);
+ FREE (version);
+ }
+
+ return 0;
+}
+
+
+int
+nfs_init_versions (struct nfs_state *nfs, xlator_t *this)
+{
+ struct nfs_initer_list *version = NULL;
+ struct nfs_initer_list *tmp = NULL;
+ rpcsvc_program_t *prog = NULL;
+ int ret = -1;
+ struct list_head *versions = NULL;
+
+ if ((!nfs) || (!this))
+ return -1;
+
+ gf_log (GF_NFS, GF_LOG_DEBUG, "Initing protocol versions");
+ versions = &nfs->versions;
+ list_for_each_entry_safe (version, tmp, versions, list) {
+ if (!version->init) {
+ ret = -1;
+ goto err;
+ }
+
+ prog = version->init (this);
+ version->program = prog;
+ if (!prog) {
+ ret = -1;
+ goto err;
+ }
+
+ gf_log (GF_NFS, GF_LOG_DEBUG, "Starting program: %s",
+ prog->progname);
+ ret = rpcsvc_program_register (nfs->rpcsvc, *prog);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "Program init failed");
+ goto err;
+ }
+ }
+
+ ret = 0;
+err:
+ return ret;
+}
+
+
+int
+nfs_add_all_initiators (struct nfs_state *nfs)
+{
+ /* Add the initializers for all versions. */
+ return 0;
+}
+
+
+int
+nfs_subvolume_started (struct nfs_state *nfs, xlator_t *xl)
+{
+ int x = 0;
+ int started = 0;
+
+ if ((!nfs) || (!xl))
+ return 1;
+
+ LOCK (&nfs->svinitlock);
+ {
+ for (;x < nfs->allsubvols; ++x) {
+ if (nfs->initedxl[x] == xl) {
+ started = 1;
+ goto unlock;
+ }
+ }
+ }
+unlock:
+ UNLOCK (&nfs->svinitlock);
+
+ return started;
+}
+
+
+int
+nfs_subvolume_set_started (struct nfs_state *nfs, xlator_t *xl)
+{
+ int x = 0;
+
+ if ((!nfs) || (!xl))
+ return 1;
+
+ LOCK (&nfs->svinitlock);
+ {
+ for (;x < nfs->allsubvols; ++x) {
+ if (nfs->initedxl[x] == NULL) {
+ nfs->initedxl[x] = xl;
+ ++nfs->upsubvols;
+ gf_log (GF_NFS, GF_LOG_DEBUG, "Starting up: %s "
+ ", vols started till now: %d", xl->name,
+ nfs->upsubvols);
+ goto unlock;
+ }
+ }
+ }
+unlock:
+ UNLOCK (&nfs->svinitlock);
+
+ return 0;
+}
+
+
+int32_t
+nfs_start_subvol_lookup_cbk (call_frame_t *frame, void *cookie,
+ xlator_t *this, int32_t op_ret, int32_t op_errno,
+ inode_t *inode, struct iatt *buf, dict_t *xattr,
+ struct iatt *postparent)
+{
+ struct nfs_state *nfs = NULL;
+ xlator_t *startedxl = NULL;
+
+ if (op_ret == -1) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to lookup root: %s",
+ strerror (op_errno));
+ goto err;
+ }
+
+ nfs = frame->local;
+ startedxl = cookie;
+ gf_log (GF_NFS, GF_LOG_TRACE, "Started %s", startedxl->name);
+err:
+ return 0;
+}
+
+
+int
+nfs_startup_subvolume (struct nfs_state *nfs, xlator_t *xl)
+{
+ int ret = -1;
+ loc_t rootloc = {0, };
+ nfs_user_t nfu = {0, };
+
+ if ((!nfs) || (!xl))
+ return -1;
+
+ if (nfs_subvolume_started (nfs, xl)) {
+ gf_log (GF_NFS,GF_LOG_TRACE, "Subvolume already started: %s",
+ xl->name);
+ ret = 0;
+ goto err;
+ }
+
+ nfs_subvolume_set_started (nfs, xl);
+ ret = nfs_inode_loc_fill (xl->itable->root, &rootloc);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to init root loc");
+ goto err;
+ }
+
+ nfs_user_root_create (&nfu);
+ ret = nfs_fop_lookup (xl, &nfu, &rootloc, nfs_start_subvol_lookup_cbk,
+ (void *)nfs);
+ if (ret < 0) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to lookup root: %s",
+ strerror (-ret));
+ goto err;
+ }
+
+ nfs_loc_wipe (&rootloc);
+
+err:
+ return ret;
+}
+
+int
+nfs_startup_subvolumes (struct nfs_state *nfs)
+{
+ int ret = -1;
+ xlator_list_t *cl = NULL;
+ if (!nfs)
+ return -1;
+
+ cl = nfs->subvols;
+ while (cl) {
+ gf_log (GF_NFS, GF_LOG_DEBUG, "Starting subvolume: %s",
+ cl->xlator->name);
+ ret = nfs_startup_subvolume (nfs, cl->xlator);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to start-up "
+ "xlator: %s", cl->xlator->name);
+ goto err;
+ }
+ cl = cl->next;
+ }
+
+ ret = 0;
+err:
+ return ret;
+}
+
+
+int
+nfs_init_subvolume (struct nfs_state *nfs, xlator_t *xl)
+{
+ unsigned int lrusize = 0;
+ int ret = -1;
+
+ if ((!nfs) || (!xl))
+ return -1;
+
+ lrusize = nfs->memfactor * GF_NFS_INODE_LRU_MULT;
+ xl->itable = inode_table_new (lrusize, xl);
+ if (!xl->itable) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to allocate "
+ "inode table");
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+int
+nfs_init_subvolumes (struct nfs_state *nfs, xlator_list_t *cl)
+{
+ int ret = -1;
+ unsigned int lrusize = 0;
+ int svcount = 0;
+
+ if ((!nfs) || (!cl))
+ return -1;
+
+ lrusize = nfs->memfactor * GF_NFS_INODE_LRU_MULT;
+ nfs->subvols = cl;
+ gf_log (GF_NFS, GF_LOG_TRACE, "inode table lru: %d", lrusize);
+
+ while (cl) {
+ gf_log (GF_NFS, GF_LOG_DEBUG, "Initing subvolume: %s",
+ cl->xlator->name);
+ ret = nfs_init_subvolume (nfs, cl->xlator);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to init "
+ "xlator: %s", cl->xlator->name);
+ goto err;
+ }
+ ++svcount;
+ cl = cl->next;
+ }
+
+ LOCK_INIT (&nfs->svinitlock);
+ nfs->initedxl = CALLOC (svcount, sizeof (xlator_t *));
+ if (!nfs->initedxl) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "Failed to allocated inited xls");
+ ret = -1;
+ goto err;
+ }
+
+ gf_log (GF_NFS, GF_LOG_TRACE, "Inited volumes: %d", svcount);
+ nfs->allsubvols = svcount;
+ ret = 0;
+err:
+ return ret;
+}
+
+
+int
+nfs_user_root_create (nfs_user_t *newnfu)
+{
+ if (!newnfu)
+ return -1;
+
+ newnfu->uid = 0;
+ newnfu->gids[0] = 0;
+ newnfu->ngrps = 1;
+
+ return 0;
+}
+
+
+int
+nfs_user_create (nfs_user_t *newnfu, uid_t uid, gid_t gid, gid_t *auxgids,
+ int auxcount)
+{
+ int x = 1;
+ int y = 0;
+
+ /* We test for GF_REQUEST_MAXGROUPS instead of NFS_FOP_NGROUPS because
+ * the latter accounts for the @gid being in @auxgids, which is not the
+ * case here.
+ */
+ if ((!newnfu) || (auxcount > GF_REQUEST_MAXGROUPS))
+ return -1;
+
+ newnfu->uid = uid;
+ newnfu->gids[0] = gid;
+ newnfu->ngrps = 1;
+
+ gf_log (GF_NFS, GF_LOG_TRACE, "uid: %d, gid %d, gids: %d", uid, gid,
+ auxcount);
+
+ if (!auxgids)
+ return 0;
+
+ for (; y < auxcount; ++x,++y) {
+ newnfu->gids[x] = auxgids[y];
+ ++newnfu->ngrps;
+ gf_log (GF_NFS, GF_LOG_TRACE, "gid: %d", auxgids[y]);
+ }
+
+ return 0;
+}
+
+
+void
+nfs_request_user_init (nfs_user_t *nfu, rpcsvc_request_t *req)
+{
+ gid_t *gidarr = NULL;
+ int gids = 0;
+
+ if ((!req) || (!nfu))
+ return;
+
+ gidarr = rpcsvc_auth_unix_auxgids (req, &gids);
+ nfs_user_create (nfu, rpcsvc_request_uid (req), rpcsvc_request_gid (req)
+ , gidarr, gids);
+
+ return;
+}
+
+
+int
+init (xlator_t *this) {
+
+ struct nfs_state *nfs = NULL;
+ int ret = -1;
+ unsigned int fopspoolsize = 0;
+
+ if (!this)
+ return -1;
+
+ if ((!this->children) || (!this->children->xlator)) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "nfs must have at least one"
+ " child subvolume");
+ return -1;
+ }
+
+ nfs = CALLOC (1, sizeof (*nfs));
+ if (!nfs) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "memory allocation failed");
+ return -1;
+ }
+
+ /* RPC service needs to be started before NFS versions can be inited. */
+ nfs->rpcsvc = rpcsvc_init (this->ctx, this->options);
+ if (!nfs->rpcsvc) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "RPC service init failed");
+ goto free_nfs;
+ }
+
+ nfs->memfactor = GF_NFS_DEFAULT_MEMFACTOR;
+ fopspoolsize = nfs->memfactor * GF_NFS_CONCURRENT_OPS_MULT;
+ /* FIXME: Really saddens me to see this as xlator wide. */
+ nfs->foppool = mem_pool_new (struct nfs_fop_local, fopspoolsize);
+ if (!nfs->foppool) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to allocate fops local"
+ " pool");
+ goto free_rpcsvc;
+ }
+
+ this->private = (void *)nfs;
+ INIT_LIST_HEAD (&nfs->versions);
+ ret = nfs_add_all_initiators (nfs);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_ERROR, "Failed to add initiators");
+ goto free_nfs;
+ }
+
+ this->ctx->top = this;
+ ret = nfs_init_subvolumes (nfs, this->children);
+ if (ret == -1) {
+ gf_log (GF_NFS, GF_LOG_CRITICAL, "Failed to init NFS exports");
+ goto free_rpcsvc;
+ }
+
+free_rpcsvc:
+ /*
+ * rpcsvc_deinit */
+free_nfs:
+ if (ret == -1)
+ FREE (nfs);
+
+ gf_log (GF_NFS, GF_LOG_DEBUG, "NFS service started");
+ return ret;
+}
+
+
+int
+notify (xlator_t *this, int32_t event, void *data, ...)
+{
+ struct nfs_state *nfs = NULL;
+ xlator_t *subvol = NULL;
+ int ret = -1;
+
+ nfs = (struct nfs_state *)this->private;
+ subvol = (xlator_t *)data;
+
+ gf_log (GF_NFS, GF_LOG_TRACE, "Notification received: %d",
+ event);
+ switch (event)
+ {
+ case GF_EVENT_CHILD_UP:
+ {
+ nfs_startup_subvolume (nfs, subvol);
+ if ((nfs->upsubvols == nfs->allsubvols) &&
+ (!nfs->subvols_started)) {
+ nfs->subvols_started = 1;
+ gf_log (GF_NFS, GF_LOG_TRACE, "All children up,"
+ " starting RPC");
+ ret = nfs_init_versions (nfs, this);
+ if (ret == -1)
+ gf_log (GF_NFS, GF_LOG_CRITICAL,
+ "Failed to initialize "
+ "protocols");
+ }
+ break;
+ }
+
+ case GF_EVENT_PARENT_UP:
+ {
+ default_notify (this, GF_EVENT_PARENT_UP, data);
+ break;
+ }
+ }
+ return 0;
+}
+
+
+int
+fini (xlator_t *this)
+{
+
+ struct nfs_state *nfs = NULL;
+
+ nfs = (struct nfs_state *)this->private;
+ gf_log (GF_NFS, GF_LOG_DEBUG, "NFS service going down");
+ nfs_deinit_versions (&nfs->versions, this);
+ return 0;
+}
+
+struct xlator_cbks cbks = { };
+struct xlator_mops mops = { };
+struct xlator_fops fops = { };
+
+struct volume_options options[] = {
+ { .key = {"nfs3.read-size"},
+ .type = GF_OPTION_TYPE_SIZET,
+ .description = "Size in which the client should issue read requests"
+ " to the Gluster NFSv3 server. Must be a multiple of"
+ " 4KiB."
+ },
+ { .key = {"nfs3.write-size"},
+ .type = GF_OPTION_TYPE_SIZET,
+ .description = "Size in which the client should issue write requests"
+ " to the Gluster NFSv3 server. Must be a multiple of"
+ " 4KiB."
+ },
+ { .key = {"nfs3.readdir-size"},
+ .type = GF_OPTION_TYPE_SIZET,
+ .description = "Size in which the client should issue directory "
+ " reading requests."
+ },
+ { .key = {"nfs3.*.volume-access"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Type of access desired for this subvolume: "
+ " read-only, read-write(default)"
+ },
+ { .key = {"rpc-auth.auth-unix"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Disable or enable the AUTH_UNIX authentication type."
+ "Must always be enabled for better interoperability."
+ "However, can be disabled if needed. Enabled by"
+ "default"
+ },
+ { .key = {"rpc-auth.auth-null"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Disable or enable the AUTH_NULL authentication type."
+ "Must always be enabled. This option is here only to"
+ " avoid unrecognized option warnings"
+ },
+ { .key = {"rpc-auth.auth-unix.*"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Disable or enable the AUTH_UNIX authentication type "
+ "for a particular exported volume over-riding defaults"
+ " and general setting for AUTH_UNIX scheme. Must "
+ "always be enabled for better interoperability."
+ "However, can be disabled if needed. Enabled by"
+ "default."
+ },
+ { .key = {"rpc-auth.auth-null.*"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Disable or enable the AUTH_NULL authentication type "
+ "for a particular exported volume over-riding defaults"
+ " and general setting for AUTH_NULL. Must always be "
+ "enabled. This option is here only to avoid "
+ "unrecognized option warnings."
+ },
+ { .key = {"rpc-auth.addr.allow"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Allow a comma separated list of addresses and/or"
+ " hostnames to connect to the server. By default, all"
+ " connections are disallowed. This allows users to "
+ "define a general rule for all exported volumes."
+ },
+ { .key = {"rpc-auth.addr.reject"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Reject a comma separated list of addresses and/or"
+ " hostnames from connecting to the server. By default,"
+ " all connections are disallowed. This allows users to"
+ "define a general rule for all exported volumes."
+ },
+ { .key = {"rpc-auth.addr.*.allow"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Allow a comma separated list of addresses and/or"
+ " hostnames to connect to the server. By default, all"
+ " connections are disallowed. This allows users to "
+ "define a rule for a specific exported volume."
+ },
+ { .key = {"rpc-auth.addr.*.reject"},
+ .type = GF_OPTION_TYPE_STR,
+ .description = "Reject a comma separated list of addresses and/or"
+ " hostnames from connecting to the server. By default,"
+ " all connections are disallowed. This allows users to"
+ "define a rule for a specific exported volume."
+ },
+ { .key = {"rpc-auth.ports.insecure"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Allow client connections from unprivileged ports. By "
+ "default only privileged ports are allowed. This is a"
+ "global setting in case insecure ports are to be "
+ "enabled for all exports using a single option."
+ },
+ { .key = {"rpc-auth.ports.*.insecure"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Allow client connections from unprivileged ports. By "
+ "default only privileged ports are allowed. Use this"
+ " option to set enable or disable insecure ports for "
+ "a specific subvolume and to over-ride global setting "
+ " set by the previous option."
+ },
+ { .key = {"rpc-auth.addr.namelookup"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .description = "Users have the option of turning off name lookup for"
+ " incoming client connections using this option. In some "
+ "setups, the name server can take too long to reply to DNS "
+ "queries resulting in timeouts of mount requests. Use this "
+ "option to turn off name lookups during address "
+ "authentication. Note, turning this off will prevent you from"
+ " using hostnames in rpc-auth.addr.* filters. By default, "
+ " name lookup is on."
+ },
+ { .key = {NULL} },
+};
+