diff options
Diffstat (limited to 'xlators/mgmt/glusterd/src/glusterd-mountbroker.c')
| -rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-mountbroker.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/xlators/mgmt/glusterd/src/glusterd-mountbroker.c b/xlators/mgmt/glusterd/src/glusterd-mountbroker.c new file mode 100644 index 00000000000..645d845ee76 --- /dev/null +++ b/xlators/mgmt/glusterd/src/glusterd-mountbroker.c @@ -0,0 +1,721 @@ +/* + Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include <inttypes.h> +#include <fnmatch.h> +#include <pwd.h> + +#include <glusterfs/globals.h> +#include <glusterfs/glusterfs.h> +#include <glusterfs/compat.h> +#include <glusterfs/dict.h> +#include <glusterfs/list.h> +#include <glusterfs/logging.h> +#include <glusterfs/syscall.h> +#include <glusterfs/defaults.h> +#include <glusterfs/compat.h> +#include <glusterfs/compat-errno.h> +#include <glusterfs/run.h> +#include "glusterd-mem-types.h" +#include "glusterd.h" +#include "glusterd-utils.h" +#include <glusterfs/common-utils.h> +#include "glusterd-mountbroker.h" +#include "glusterd-op-sm.h" +#include "glusterd-messages.h" + +static int +seq_dict_foreach(dict_t *dict, int (*fn)(char *str, void *data), void *data) +{ + char index[] = "4294967296"; // 1<<32 + int i = 0; + char *val = NULL; + int ret = 0; + + for (;; i++) { + snprintf(index, sizeof(index), "%d", i); + ret = dict_get_str(dict, index, &val); + if (ret != 0) + return ret == -ENOENT ? 0 : ret; + ret = fn(val, data); + if (ret != 0) + return ret; + } +} + +int +parse_mount_pattern_desc(gf_mount_spec_t *mspec, char *pdesc) +#define SYNTAX_ERR -2 +{ + char *curs = NULL; + char *c2 = NULL; + char sc = '\0'; + char **cc = NULL; + gf_mount_pattern_t *pat = NULL; + int pnum = 0; + int ret = 0; + int lastsup = -1; + int incl = -1; + char **pcc = NULL; + int pnc = 0; + + skipwhite(&pdesc); + + /* a bow to theory */ + if (!*pdesc) + return 0; + + /* count number of components, separated by '&' */ + mspec->len = 0; + for (curs = pdesc; *curs; curs++) { + if (*curs == ')') + mspec->len++; + } + + mspec->patterns = GF_CALLOC(mspec->len, sizeof(*mspec->patterns), + gf_gld_mt_mount_pattern); + if (!mspec->patterns) { + gf_smsg("glusterd", GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL); + ret = -1; + goto out; + } + + pat = mspec->patterns; + curs = pdesc; + skipwhite(&curs); + for (;;) { + incl = -1; + + /* check for pattern signedness modifier */ + if (*curs == '-') { + pat->negative = _gf_true; + curs++; + } + + /* now should come condition specifier, + * then opening paren + */ + c2 = nwstrtail(curs, "SUB("); + if (c2) { + pat->condition = SET_SUB; + goto got_cond; + } + c2 = nwstrtail(curs, "SUP("); + if (c2) { + pat->condition = SET_SUPER; + lastsup = pat - mspec->patterns; + goto got_cond; + } + c2 = nwstrtail(curs, "EQL("); + if (c2) { + pat->condition = SET_EQUAL; + goto got_cond; + } + c2 = nwstrtail(curs, "MEET("); + if (c2) { + pat->condition = SET_INTERSECT; + goto got_cond; + } + c2 = nwstrtail(curs, "SUB+("); + if (c2) { + pat->condition = SET_SUB; + incl = lastsup; + goto got_cond; + } + + ret = SYNTAX_ERR; + goto out; + + got_cond: + curs = c2; + skipwhite(&curs); + /* count the number of components for pattern */ + pnum = *curs == ')' ? 0 : 1; + for (c2 = curs; *c2 != ')';) { + if (strchr("&|", *c2)) { + ret = SYNTAX_ERR; + goto out; + } + while (!strchr("|&)", *c2) && !isspace(*c2)) + c2++; + skipwhite(&c2); + switch (*c2) { + case ')': + break; + case '\0': + case '&': + ret = SYNTAX_ERR; + goto out; + case '|': + *c2 = ' '; + skipwhite(&c2); + /* fall through */ + default: + pnum++; + } + } + if (incl >= 0) { + pnc = 0; + for (pcc = mspec->patterns[incl].components; *pcc; pcc++) + pnc++; + pnum += pnc; + } + pat->components = GF_CALLOC(pnum + 1, sizeof(*pat->components), + gf_gld_mt_mount_comp_container); + if (!pat->components) { + ret = -1; + goto out; + } + + cc = pat->components; + /* copy over included component set */ + if (incl >= 0) { + memcpy(pat->components, mspec->patterns[incl].components, + pnc * sizeof(*pat->components)); + cc += pnc; + } + /* parse and add components */ + c2 = ""; /* reset c2 */ + while (*c2 != ')') { + c2 = curs; + while (!isspace(*c2) && *c2 != ')') + c2++; + sc = *c2; + *c2 = '\0'; + ; + *cc = gf_strdup(curs); + if (!*cc) { + ret = -1; + goto out; + } + *c2 = sc; + skipwhite(&c2); + curs = c2; + cc++; + } + + curs++; + skipwhite(&curs); + if (*curs == '&') { + curs++; + skipwhite(&curs); + } + + if (!*curs) + break; + pat++; + } + +out: + if (ret == SYNTAX_ERR) { + gf_msg("glusterd", GF_LOG_ERROR, EINVAL, GD_MSG_INVALID_ENTRY, + "cannot parse mount patterns %s", pdesc); + } + + /* We've allocted a lotta stuff here but don't bother with freeing + * on error, in that case we'll terminate anyway + */ + return ret ? -1 : 0; +} +#undef SYNTAX_ERR + +const char *georep_mnt_desc_template = + "SUP(" + "aux-gfid-mount " + "acl " + "volfile-server=localhost " + "client-pid=%d " + "user-map-root=%s " + ")" + "SUB+(" + "log-file=%s/" GEOREP + "*/* " + "log-level=* " + "volfile-id=* " + ")" + "MEET(" + "%s" + ")"; + +int +make_georep_mountspec(gf_mount_spec_t *mspec, const char *volnames, char *user, + char *logdir) +{ + char *georep_mnt_desc = NULL; + char *meetspec = NULL; + char *vols = NULL; + char *vol = NULL; + char *p = NULL; + char *savetok = NULL; + char *fa[3] = { + 0, + }; + size_t siz = 0; + int vc = 0; + int i = 0; + int ret = 0; + + vols = gf_strdup((char *)volnames); + if (!vols) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_STRDUP_FAILED, + "Volume name=%s", volnames, NULL); + goto out; + } + + for (vc = 1, p = vols; *p; p++) { + if (*p == ',') + vc++; + } + siz = strlen(volnames) + vc * SLEN("volfile-id="); + meetspec = GF_CALLOC(1, siz + 1, gf_gld_mt_georep_meet_spec); + if (!meetspec) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL); + goto out; + } + + for (p = vols;;) { + vol = strtok_r(p, ",", &savetok); + if (!vol) { + GF_ASSERT(vc == 0); + break; + } + p = NULL; + strcat(meetspec, "volfile-id="); + strcat(meetspec, vol); + if (--vc > 0) + strcat(meetspec, " "); + } + + ret = gf_asprintf(&georep_mnt_desc, georep_mnt_desc_template, + GF_CLIENT_PID_GSYNCD, user, logdir, meetspec); + if (ret == -1) { + georep_mnt_desc = NULL; + goto out; + } + + ret = parse_mount_pattern_desc(mspec, georep_mnt_desc); + +out: + fa[0] = meetspec; + fa[1] = vols; + fa[2] = georep_mnt_desc; + + for (i = 0; i < 3; i++) { + if (fa[i] == NULL) + ret = -1; + else + GF_FREE(fa[i]); + } + + return ret; +} + +static gf_boolean_t +match_comp(char *str, char *patcomp) +{ + char *c1 = patcomp; + char *c2 = str; + + GF_ASSERT(c1); + GF_ASSERT(c2); + + while (*c1 == *c2) { + if (!*c1) + return _gf_true; + c1++; + c2++; + if (c1[-1] == '=') + break; + } + + return fnmatch(c1, c2, 0) == 0 ? _gf_true : _gf_false; +} + +struct gf_set_descriptor { + gf_boolean_t priv[2]; + gf_boolean_t common; +}; + +static int +_gf_set_dict_iter1(char *val, void *data) +{ + void **dataa = data; + struct gf_set_descriptor *sd = dataa[0]; + char **curs = dataa[1]; + gf_boolean_t priv = _gf_true; + + while (*curs) { + if (match_comp(val, *curs)) { + priv = _gf_false; + sd->common = _gf_true; + } + curs++; + } + + if (priv) + sd->priv[0] = _gf_true; + + return 0; +} + +static int +_gf_set_dict_iter2(char *val, void *data) +{ + void **dataa = data; + gf_boolean_t *boo = dataa[0]; + char *comp = dataa[1]; + + if (match_comp(val, comp)) + *boo = _gf_true; + + return 0; +} + +static void +relate_sets(struct gf_set_descriptor *sd, dict_t *argdict, char **complist) +{ + void *dataa[] = {NULL, NULL}; + gf_boolean_t boo = _gf_false; + + memset(sd, 0, sizeof(*sd)); + + dataa[0] = sd; + dataa[1] = complist; + seq_dict_foreach(argdict, _gf_set_dict_iter1, dataa); + + while (*complist) { + boo = _gf_false; + dataa[0] = &boo; + dataa[1] = *complist; + seq_dict_foreach(argdict, _gf_set_dict_iter2, dataa); + + if (boo) + sd->common = _gf_true; + else + sd->priv[1] = _gf_true; + + complist++; + } +} + +static int +_arg_parse_uid(char *val, void *data) +{ + char *user = strtail(val, "user-map-root="); + struct passwd *pw = NULL; + + if (!user) + return 0; + pw = getpwnam(user); + if (!pw) + return -EINVAL; + + if (*(int *)data >= 0) + /* uid ambiguity, already found */ + return -EINVAL; + + *(int *)data = pw->pw_uid; + return 0; +} + +static int +evaluate_mount_request(xlator_t *this, gf_mount_spec_t *mspec, dict_t *argdict) +{ + struct gf_set_descriptor sd = { + { + 0, + }, + }; + int i = 0; + int uid = -1; + int ret = 0; + gf_boolean_t match = _gf_false; + + for (i = 0; i < mspec->len; i++) { + relate_sets(&sd, argdict, mspec->patterns[i].components); + switch (mspec->patterns[i].condition) { + case SET_SUB: + match = !sd.priv[0]; + break; + case SET_SUPER: + match = !sd.priv[1]; + break; + case SET_EQUAL: + match = (!sd.priv[0] && !sd.priv[1]); + break; + case SET_INTERSECT: + match = sd.common; + break; + default: + GF_ASSERT(!"unreached"); + } + if (mspec->patterns[i].negative) + match = !match; + + if (!match) { + gf_msg(this->name, GF_LOG_ERROR, EPERM, + GD_MSG_MNTBROKER_SPEC_MISMATCH, + "Mountbroker spec mismatch!!! SET: %d " + "COMPONENT: %d. Review the mount args passed", + mspec->patterns[i].condition, i); + return -EPERM; + } + } + + ret = seq_dict_foreach(argdict, _arg_parse_uid, &uid); + if (ret != 0) + return ret; + + return uid; +} + +static int +_volname_get(char *val, void *data) +{ + char **volname = data; + + *volname = strtail(val, "volfile-id="); + + return *volname ? 1 : 0; +} + +static int +_runner_add(char *val, void *data) +{ + runner_t *runner = data; + + runner_argprintf(runner, "--%s", val); + + return 0; +} + +int +glusterd_do_mount(char *label, dict_t *argdict, char **path, int *op_errno) +{ + glusterd_conf_t *priv = NULL; + char *mountbroker_root = NULL; + gf_mount_spec_t *mspec = NULL; + int uid = -ENOENT; + char *volname = NULL; + glusterd_volinfo_t *vol = NULL; + char *mtptemp = NULL; + char *mntlink = NULL; + char *cookieswitch = NULL; + char *cookie = NULL; + char *sla = NULL; + struct stat st = { + 0, + }; + runner_t runner = { + 0, + }; + int ret = 0; + xlator_t *this = THIS; + mode_t orig_umask = 0; + gf_boolean_t found_label = _gf_false; + + priv = this->private; + GF_ASSERT(priv); + + GF_ASSERT(op_errno); + *op_errno = 0; + + if (dict_get_strn(this->options, "mountbroker-root", + SLEN("mountbroker-root"), &mountbroker_root) != 0) { + *op_errno = ENOENT; + gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_DICT_GET_FAILED, + "'option mountbroker-root' " + "missing in glusterd vol file"); + goto out; + } + + GF_ASSERT(label); + if (!*label) { + *op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MNTBROKER_LABEL_NULL, + "label is NULL (%s)", strerror(*op_errno)); + goto out; + } + + /* look up spec for label */ + cds_list_for_each_entry(mspec, &priv->mount_specs, speclist) + { + if (strcmp(mspec->label, label) != 0) + continue; + + found_label = _gf_true; + uid = evaluate_mount_request(this, mspec, argdict); + break; + } + if (uid < 0) { + *op_errno = -uid; + if (!found_label) { + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + GD_MSG_MNTBROKER_LABEL_MISS, + "Missing mspec: Check the corresponding option " + "in glusterd vol file for mountbroker user: %s", + label); + } + goto out; + } + + /* some sanity check on arguments */ + seq_dict_foreach(argdict, _volname_get, &volname); + if (!volname) { + *op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, EINVAL, GD_MSG_DICT_GET_FAILED, + "Dict get failed for the key 'volname'"); + goto out; + } + if (glusterd_volinfo_find(volname, &vol) != 0 || + !glusterd_is_volume_started(vol)) { + *op_errno = ENOENT; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MOUNT_REQ_FAIL, + "Either volume is not started or volinfo not found"); + goto out; + } + + /* go do mount */ + + /** create actual mount dir */ + + /*** "overload" string name to be possible to used for cookie + creation, see below */ + ret = gf_asprintf(&mtptemp, "%s/user%d/mtpt-%s-XXXXXX/cookie", + mountbroker_root, uid, label); + if (ret == -1) { + mtptemp = NULL; + *op_errno = ENOMEM; + goto out; + } + /*** hide cookie part */ + cookieswitch = strrchr(mtptemp, '/'); + *cookieswitch = '\0'; + + sla = strrchr(mtptemp, '/'); + *sla = '\0'; + ret = sys_mkdir(mtptemp, 0700); + if (ret == 0) + ret = sys_chown(mtptemp, uid, 0); + else if (errno == EEXIST) + ret = 0; + if (ret == -1) { + *op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL, + "Mountbroker User directory creation failed"); + goto out; + } + ret = sys_lstat(mtptemp, &st); + if (ret == -1) { + *op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL, + "stat on mountbroker user directory failed"); + goto out; + } + if (!(S_ISDIR(st.st_mode) && (st.st_mode & ~S_IFMT) == 0700 && + st.st_uid == uid && st.st_gid == 0)) { + *op_errno = EACCES; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_MOUNT_REQ_FAIL, + "Incorrect mountbroker user directory attributes"); + goto out; + } + *sla = '/'; + + if (!mkdtemp(mtptemp)) { + *op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL, + "Mountbroker mount directory creation failed"); + goto out; + } + + /** create private "cookie" symlink */ + + /*** occupy an entry in the hive dir via mkstemp */ + ret = gf_asprintf(&cookie, "%s/" MB_HIVE "/mntXXXXXX", mountbroker_root); + if (ret == -1) { + cookie = NULL; + *op_errno = ENOMEM; + goto out; + } + orig_umask = umask(S_IRWXG | S_IRWXO); + ret = mkstemp(cookie); + umask(orig_umask); + if (ret == -1) { + *op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL, + "Mountbroker cookie file creation failed"); + goto out; + } + sys_close(ret); + + /*** assembly the path from cookie to mountpoint */ + sla = strchr(sla - 1, '/'); + GF_ASSERT(sla); + ret = gf_asprintf(&mntlink, "../user%d%s", uid, sla); + if (ret == -1) { + *op_errno = ENOMEM; + goto out; + } + + /*** create cookie link in (to-be) mountpoint, + move it over to the final place */ + *cookieswitch = '/'; + ret = sys_symlink(mntlink, mtptemp); + if (ret != -1) + ret = sys_rename(mtptemp, cookie); + *cookieswitch = '\0'; + if (ret == -1) { + *op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, GD_MSG_SYSCALL_FAIL, + "symlink or rename failed"); + goto out; + } + + /** invoke glusterfs on the mountpoint */ + + runinit(&runner); + runner_add_arg(&runner, SBIN_DIR "/glusterfs"); + seq_dict_foreach(argdict, _runner_add, &runner); + runner_add_arg(&runner, mtptemp); + ret = runner_run_reuse(&runner); + if (ret == -1) { + *op_errno = EIO; /* XXX hacky fake */ + runner_log(&runner, "", GF_LOG_ERROR, "command failed"); + } + runner_end(&runner); + +out: + + if (*op_errno) { + ret = -1; + gf_msg(this->name, GF_LOG_WARNING, *op_errno, GD_MSG_MOUNT_REQ_FAIL, + "unsuccessful mount request"); + if (mtptemp) { + *cookieswitch = '/'; + sys_unlink(mtptemp); + *cookieswitch = '\0'; + sys_rmdir(mtptemp); + } + if (cookie) { + sys_unlink(cookie); + GF_FREE(cookie); + } + + } else { + ret = 0; + *path = cookie; + } + + if (mtptemp) + GF_FREE(mtptemp); + if (mntlink) + GF_FREE(mntlink); + + return ret; +} |
