summaryrefslogtreecommitdiffstats
path: root/shlibs/mount/src/context_mount.c
diff options
context:
space:
mode:
authorKarel Zak2010-10-04 13:37:33 +0200
committerKarel Zak2011-01-03 12:28:44 +0100
commit1bb1d80b3e3512a4664cb9895c95f29a900f8aba (patch)
tree809c0338ce8ea4c6567b1fd41fff1020d2761f11 /shlibs/mount/src/context_mount.c
parentlibmount: cleanup helpers support (diff)
downloadkernel-qcow2-util-linux-1bb1d80b3e3512a4664cb9895c95f29a900f8aba.tar.gz
kernel-qcow2-util-linux-1bb1d80b3e3512a4664cb9895c95f29a900f8aba.tar.xz
kernel-qcow2-util-linux-1bb1d80b3e3512a4664cb9895c95f29a900f8aba.zip
libmount: split context.c
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'shlibs/mount/src/context_mount.c')
-rw-r--r--shlibs/mount/src/context_mount.c716
1 files changed, 716 insertions, 0 deletions
diff --git a/shlibs/mount/src/context_mount.c b/shlibs/mount/src/context_mount.c
new file mode 100644
index 000000000..d5f361a83
--- /dev/null
+++ b/shlibs/mount/src/context_mount.c
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "c.h"
+#include "mountP.h"
+
+static int is_remount(mnt_context *cxt)
+{
+ unsigned long fl = 0;
+
+ if (cxt->mountflags & MS_REMOUNT)
+ return 1;
+ if (!mnt_context_get_mountflags(cxt, &fl) && (fl & MS_REMOUNT))
+ return 1;
+ return 0;
+}
+
+static int apply_tab(mnt_context *cxt, mnt_tab *tb)
+{
+ mnt_fs *fs = NULL;
+ const char *src = NULL, *tgt = NULL;
+ int rc;
+
+ if (!cxt->fs)
+ return -EINVAL;
+
+ src = mnt_fs_get_source(cxt->fs);
+ tgt = mnt_fs_get_target(cxt->fs);
+
+ if (tgt && src)
+ ; /* TODO: search pair for MNT_OPTSMODE_FORCE */
+ else if (src)
+ fs = mnt_tab_find_source(tb, src, MNT_ITER_FORWARD);
+ else if (tgt)
+ fs = mnt_tab_find_target(tb, tgt, MNT_ITER_FORWARD);
+ else if (cxt->spec) {
+ fs = mnt_tab_find_source(tb, cxt->spec, MNT_ITER_FORWARD);
+
+ if (!fs && (strncmp(cxt->spec, "LABEL=", 6) ||
+ strncmp(cxt->spec, "UUID=", 5)))
+ fs = mnt_tab_find_target(tb, cxt->spec, MNT_ITER_FORWARD);
+ }
+
+ if (!fs)
+ return -EINVAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "apply entry:"));
+ DBG(CXT, mnt_fs_print_debug(fs, stderr));
+
+ /* copy from fstab to our FS description
+ */
+ rc = mnt_fs_set_source(cxt->fs, mnt_fs_get_source(fs));
+ if (!rc)
+ rc = mnt_fs_set_target(cxt->fs, mnt_fs_get_target(fs));
+
+ if (!rc && !mnt_fs_get_fstype(cxt->fs))
+ rc = mnt_fs_set_fstype(cxt->fs, mnt_fs_get_fstype(fs));
+
+ if (!rc && cxt->optsmode != MNT_OPTSMODE_IGNORE)
+ rc = mnt_fs_prepend_optstr(cxt->fs, mnt_fs_get_optstr(fs));
+
+ if (!rc)
+ cxt->flags |= MNT_FL_FSTAB_APPLIED;
+
+ return rc;
+}
+
+static int apply_fstab(mnt_context *cxt)
+{
+ int rc;
+ mnt_cache *cache;
+ const char *src = NULL, *tgt = NULL;
+
+ if (!cxt || (!cxt->spec && !cxt->fs))
+ return -EINVAL;
+
+ if (cxt->flags & MNT_FL_FSTAB_APPLIED)
+ return 0;
+
+ if (cxt->fs) {
+ src = mnt_fs_get_source(cxt->fs);
+ tgt = mnt_fs_get_target(cxt->fs);
+ }
+
+ /* fstab is not required if source and target are specified */
+ if (src && tgt && !(cxt->optsmode == MNT_OPTSMODE_FORCE ||
+ cxt->optsmode == MNT_OPTSMODE_MTABFORCE))
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt,
+ "trying to apply fstab (src=%s, target=%s, spec=%s)",
+ src, tgt, cxt->spec));
+
+ /* initialize fstab */
+ if (!cxt->fstab) {
+ cxt->fstab = mnt_new_tab();
+ if (!cxt->fstab)
+ goto errnomem;
+ cxt->flags &= ~MNT_FL_EXTERN_FSTAB;
+ rc = mnt_tab_parse_fstab(cxt->fstab);
+ if (rc)
+ goto err;
+ }
+
+ cache = mnt_context_get_cache(cxt); /* NULL if MNT_FL_NOCANONICALIZE is enabled */
+
+ /* never touch an external fstab */
+ if (!(cxt->flags & MNT_FL_EXTERN_FSTAB))
+ mnt_tab_set_cache(cxt->fstab, cache);
+
+ /* let's initialize cxt->fs */
+ mnt_context_get_fs(cxt);
+
+ /* try fstab */
+ rc = apply_tab(cxt, cxt->fstab);
+
+ /* try mtab */
+ if (rc || (cxt->optsmode == MNT_OPTSMODE_MTABFORCE && is_remount(cxt))) {
+
+ cxt->mtab = mnt_new_tab();
+ if (!cxt->mtab)
+ goto errnomem;
+ rc = mnt_tab_parse_mtab(cxt->mtab);
+ if (rc)
+ goto err;
+
+ mnt_tab_set_cache(cxt->mtab, cache);
+ rc = apply_tab(cxt, cxt->mtab);
+ if (rc)
+ goto err;
+ }
+ return 0;
+
+errnomem:
+ rc = ENOMEM;
+err:
+ DBG(CXT, mnt_debug_h(cxt, "failed to found entry in fstab/mtab"));
+ return rc;
+}
+
+/*
+ * this has to be called after mnt_context_evaluate_permissions()
+ */
+static int fix_optstr(mnt_context *cxt)
+{
+ int rc = 0, rem_se = 0;
+ char *next, **optstr;
+ char *name, *val;
+ size_t namesz, valsz;
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs)
+ return 0;
+
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ /*
+ * we directly work with optstr pointer here
+ */
+ optstr = &cxt->fs->optstr;
+ if (!optstr)
+ return 0;
+
+ /* The propagation flags should not be used together with any other flags */
+ if (cxt->mountflags & MS_PROPAGATION)
+ cxt->mountflags &= MS_PROPAGATION;
+
+ if (!mnt_optstr_get_option(*optstr, "user", &val, &valsz)) {
+ if (val) {
+ cxt->orig_user = strndup(val, valsz);
+ if (!cxt->orig_user) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ }
+ cxt->flags |= MNT_FL_SAVED_USER;
+ }
+
+ /*
+ * Sync mount options with mount flags
+ */
+ rc = mnt_optstr_apply_flags(optstr, cxt->mountflags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ if (rc)
+ goto done;
+
+ rc = mnt_optstr_apply_flags(optstr, cxt->user_mountflags,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+ if (rc)
+ goto done;
+
+ next = *optstr;
+
+#ifdef HAVE_LIBSELINUX
+ rem_se = (cxt->mountflags & MS_REMOUNT) || !is_selinux_enabled();
+#endif
+ DBG(CXT, mnt_debug_h(cxt, "fixing mount options: '%s'", *optstr));
+
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+
+ if (namesz == 3 && !strncmp(name, "uid", 3))
+ rc = mnt_optstr_fix_uid(optstr, val, valsz, &next);
+ else if (namesz == 3 && !strncmp(name, "gid", 3))
+ rc = mnt_optstr_fix_gid(optstr, val, valsz, &next);
+#ifdef HAVE_LIBSELINUX
+ else if (namesz >= 7 && (!strncmp(name, "context", 7) ||
+ !strncmp(name, "fscontext", 9) ||
+ !strncmp(name, "defcontext", 10) ||
+ !strncmp(name, "rootcontext", 11))) {
+ if (rem_se) {
+ /* remove context= option */
+ next = name;
+ rc = mnt_optstr_remove_option_at(optstr,
+ name, val + valsz);
+ } else
+ rc = mnt_optstr_fix_secontext(optstr,
+ val, valsz, &next);
+ }
+#endif
+ else if (namesz == 4 && (cxt->user_mountflags && MNT_MS_USER) &&
+ !strncmp(name, "user", 4)) {
+
+ rc = mnt_optstr_fix_user(optstr,
+ val ? val : name + namesz,
+ valsz, &next);
+ }
+ if (rc)
+ goto done;
+ }
+
+done:
+ __mnt_fs_set_optstr_ptr(cxt->fs, *optstr, TRUE);
+ DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: '%s'", rc, *optstr));
+ return rc;
+}
+
+/*
+ * Converts already evalulated and fixed options to the form that is comaptible
+ * with /sbin/mount.<type> helpers.
+ *
+ * Retursn newly allocated string.
+ */
+static int generate_helper_optstr(mnt_context *cxt, char **optstr)
+{
+ const char *o;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(optstr);
+
+ *optstr = NULL;
+ o = mnt_fs_get_optstr(cxt->fs);
+
+ if (o)
+ rc = mnt_optstr_append_option(optstr, o, NULL);
+ if (!rc && (cxt->flags & MNT_FL_SAVED_USER))
+ rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
+ if (rc) {
+ free(*optstr);
+ *optstr = NULL;
+ }
+ return rc;
+}
+
+
+/*
+ * this has to be called before mnt_context_fix_optstr()
+ */
+static int evaluate_permissions(mnt_context *cxt)
+{
+ unsigned long u_flags;
+ const char *srcpath;
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs)
+ return 0;
+
+ mnt_context_get_userspace_mountflags(cxt, &u_flags);
+
+ if (!mnt_context_is_restricted(cxt)) {
+ /*
+ * superuser mount
+ */
+ cxt->user_mountflags &= ~MNT_MS_OWNER;
+ cxt->user_mountflags &= ~MNT_MS_GROUP;
+ cxt->user_mountflags &= ~MNT_MS_USER;
+ cxt->user_mountflags &= ~MNT_MS_USERS;
+ } else {
+ /*
+ * user mount
+ */
+ if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP))
+ cxt->mountflags |= MS_OWNERSECURE;
+
+ if (u_flags & (MNT_MS_USER | MNT_MS_USERS))
+ cxt->mountflags |= MS_SECURE;
+
+ srcpath = mnt_fs_get_srcpath(cxt->fs);
+ if (!srcpath)
+ return -EINVAL;
+
+ /*
+ * MS_OWNER: Allow owners to mount when fstab contains the
+ * owner option. Note that this should never be used in a high
+ * security environment, but may be useful to give people at
+ * the console the possibility of mounting a floppy. MS_GROUP:
+ * Allow members of device group to mount. (Martin Dickopp)
+ */
+ if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
+ struct stat sb;
+
+ if (strncmp(srcpath, "/dev/", 5) == 0 &&
+ stat(srcpath, &sb) == 0 &&
+ (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
+ ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))
+
+ cxt->user_mountflags |= MNT_MS_USER;
+ }
+
+ if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
+ DBG(CXT, mnt_debug_h(cxt, "permissions evaluation ends with -EPERMS"));
+ return -EPERM;
+ }
+ }
+
+ return 0;
+}
+
+static int merge_mountflags(mnt_context *cxt)
+{
+ unsigned long fl = 0;
+ int rc;
+
+ rc = mnt_context_get_mountflags(cxt, &fl);
+ if (rc)
+ return rc;
+ cxt->mountflags = fl;
+
+ fl = 0;
+ rc = mnt_context_get_userspace_mountflags(cxt, &fl);
+ if (rc)
+ return rc;
+ cxt->user_mountflags = fl;
+
+ cxt->flags |= MNT_FL_MOUNTFLAGS_MERGED;
+ return 0;
+}
+
+static int exec_helper(mnt_context *cxt)
+{
+ char *o = NULL;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper);
+
+ rc = generate_helper_optstr(cxt, &o);
+ if (rc)
+ goto done;
+
+ DBG_FLUSH;
+
+ switch (fork()) {
+ case 0:
+ {
+ const char *args[12], *type;
+ int i = 0;
+
+ if (setgid(getgid()) < 0)
+ exit(EXIT_FAILURE);
+
+ if (setuid(getuid()) < 0)
+ exit(EXIT_FAILURE);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ args[i++] = cxt->helper; /* 1 */
+ args[i++] = mnt_fs_get_srcpath(cxt->fs); /* 2 */
+ args[i++] = mnt_fs_get_target(cxt->fs); /* 3 */
+
+ if (cxt->flags & MNT_FL_SLOPPY)
+ args[i++] = "-s"; /* 4 */
+ if (cxt->flags & MNT_FL_FAKE)
+ args[i++] = "-f"; /* 5 */
+ if (cxt->flags & MNT_FL_NOMTAB)
+ args[i++] = "-n"; /* 6 */
+ if (cxt->flags & MNT_FL_VERBOSE)
+ args[i++] = "-v"; /* 7 */
+ if (o) {
+ args[i++] = "-o"; /* 8 */
+ args[i++] = o; /* 9 */
+ }
+ if (type && !endswith(cxt->helper, type)) {
+ args[i++] = "-t"; /* 10 */
+ args[i++] = type; /* 11 */
+ }
+ args[i] = NULL; /* 12 */
+#ifdef CONFIG_LIBMOUNT_DEBUG
+ i = 0;
+ for (i = 0; args[i]; i++)
+ DBG(CXT, mnt_debug_h(cxt, "argv[%d] = \"%s\"",
+ i, args[i]));
+#endif
+ DBG_FLUSH;
+ execv(cxt->helper, (char * const *) args);
+ exit(EXIT_FAILURE);
+ }
+ default:
+ {
+ int st;
+ wait(&st);
+ cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;
+
+ DBG(CXT, mnt_debug_h(cxt, "%s executed [status=%d]",
+ cxt->helper, cxt->helper_status));
+ rc = 0;
+ break;
+ }
+
+ case -1:
+ rc = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
+ break;
+ }
+
+done:
+ free(o);
+ return rc;
+}
+
+/*
+ * The default is to use fstype from cxt->fs, this could be overwritten by
+ * @try_type argument.
+ */
+static int do_mount(mnt_context *cxt, const char *try_type)
+{
+ int rc;
+ const char *src, *target, *type;
+ unsigned long flags;
+
+ assert(cxt);
+ assert(cxt->fs);
+
+ if (try_type && !cxt->helper) {
+ rc = mnt_context_prepare_helper(cxt, "mount", try_type);
+ if (!rc)
+ return rc;
+ }
+ if (cxt->helper)
+ return exec_helper(cxt);
+
+ type = try_type ? : mnt_fs_get_fstype(cxt->fs);
+
+ flags = cxt->mountflags;
+ src = mnt_fs_get_srcpath(cxt->fs);
+ target = mnt_fs_get_target(cxt->fs);
+
+ if (!src || !target)
+ return -EINVAL;
+
+ if (!(flags & MS_MGC_MSK))
+ flags |= MS_MGC_VAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "calling mount(2) "
+ "[source=%s, target=%s, type=%s, "
+ " mountflags=%08lx, mountdata=%s]",
+ src, target, type,
+ flags, cxt->mountdata ? "yes" : "<none>"));
+
+ if (mount(src, target, type, flags, cxt->mountdata)) {
+ cxt->syscall_errno = errno;
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d]",
+ cxt->syscall_errno));
+ return -cxt->syscall_errno;
+ }
+
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
+ return 0;
+}
+
+/**
+ * mnt_context_prepare_mount:
+ * @cxt: mount context
+ *
+ * This function:
+ * - read information from fstab (if necessary)
+ * - cleanup mount options
+ * - check premissions
+ * - prepare device (e.g. loop device)
+ * - detect FS type (if necessary)
+ * - generate mount flags and mount data (if not set yet)
+ * - prepare for mtab update (if necessary)
+ *
+ * It's strongly recommended to use this function before mnt_context_mount_fs().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_prepare_mount(mnt_context *cxt)
+{
+ int rc = 0;
+
+ if (!cxt)
+ return -EINVAL;
+
+ if (!cxt->spec && !(cxt->fs && (mnt_fs_get_source(cxt->fs) ||
+ mnt_fs_get_target(cxt->fs))))
+ return -EINVAL;
+
+ rc = apply_fstab(cxt);
+ if (!rc)
+ rc = merge_mountflags(cxt);
+ if (!rc)
+ rc = evaluate_permissions(cxt);
+ if (!rc)
+ rc = fix_optstr(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_srcpath(cxt);
+ if (!rc)
+ rc = mnt_context_guess_fstype(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_helper(cxt, "mount", NULL);
+ if (!rc)
+ rc = mnt_context_prepare_update(cxt, MNT_ACT_MOUNT);
+
+ if (!rc) {
+ DBG(CXT, mnt_debug_h(cxt, "sucessfully prepared"));
+ return 0;
+ }
+
+ DBG(CXT, mnt_debug_h(cxt, "prepare failed"));
+ return rc;
+}
+
+/**
+ * mnt_context_do_mount:
+ * @cxt: mount context
+ *
+ * Mount filesystem by mount(2) or fork()+exec(/sbin/mount.<type>).
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_do_mount(mnt_context *cxt)
+{
+ int rc = -EINVAL;
+ const char *type;
+
+ if (!cxt || !cxt->fs || (cxt->fs->flags & MNT_FS_SWAP))
+ return -EINVAL;
+
+ if (!(cxt->flags & MNT_FL_MOUNTDATA))
+ cxt->mountdata = (char *) mnt_fs_get_fs_optstr(cxt->fs);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+
+ if (type && !strchr(type, ',')) {
+ rc = do_mount(cxt, NULL);
+ if (rc)
+ return rc;
+ }
+
+ /* TODO: try all filesystems from comma separated list of types */
+
+ /* TODO: try all filesystems from /proc/filesystems and /etc/filesystems */
+
+ return rc;
+}
+
+/**
+ * mnt_context_post_mount:
+ * @cxt: mount context
+ *
+ * Updates mtab, etc. This function should be always called after
+ * mnt_context_do_mount().
+ *
+ * Returns: 0 on success, and negative number in case of error.
+ */
+int mnt_context_post_mount(mnt_context *cxt)
+{
+ int rc = 0;
+
+ if (!cxt)
+ return -EINVAL;
+ /*
+ * Update /etc/mtab or /var/run/mount/mountinfo
+ */
+ if (!cxt->syscall_errno && !cxt->helper &&
+ !(cxt->flags & MNT_FL_NOMTAB) &&
+ cxt->update && !mnt_update_is_pointless(cxt->update)) {
+
+ /* TODO: if mtab update is expected then checkif the target is really
+ * mounted read-write to avoid 'ro' in mtab and 'rw' in /proc/mounts.
+ */
+ rc = mnt_update_file(cxt->update);
+ }
+
+ return rc;
+}
+
+/**
+ * mnt_context_mount_strerror
+ * @cxt: mount context
+ * @buf: buffer
+ * @bufsiz: size of the buffer
+ *
+ * Returns: 0 or negative number in case of error.
+ */
+int mnt_context_mount_strerror(mnt_context *cxt, char *buf, size_t bufsiz)
+{
+ /* TODO: based on cxt->syscall_errno or cxt->helper_status */
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+mnt_lock *lock;
+
+static void lock_fallback(void)
+{
+ if (lock) {
+ mnt_unlock_file(lock);
+ mnt_free_lock(lock);
+ }
+}
+
+int test_mount(struct mtest *ts, int argc, char *argv[])
+{
+ int idx = 1, rc = 0;
+ mnt_context *cxt;
+
+ if (argc < 2)
+ return -EINVAL;
+
+ cxt = mnt_new_context();
+ if (!cxt)
+ return -ENOMEM;
+
+ if (!strcmp(argv[idx], "-o")) {
+ mnt_context_set_optstr(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+ if (!strcmp(argv[idx], "-t")) {
+ /* TODO: use mnt_context_set_fstype_pattern() */
+ mnt_context_set_fstype(cxt, argv[idx + 1]);
+ idx += 2;
+ }
+
+ if (argc == idx + 1)
+ /* mount <spec> */
+ mnt_context_set_spec(cxt, argv[idx++]);
+
+ else if (argc == idx + 2) {
+ /* mount <device> <mountpoint> */
+ mnt_context_set_source(cxt, argv[idx++]);
+ mnt_context_set_target(cxt, argv[idx++]);
+ }
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (rc)
+ printf("failed to prepare mount\n");
+ else {
+ lock = mnt_context_get_lock(cxt);
+ if (lock)
+ atexit(lock_fallback);
+
+ rc = mnt_context_do_mount(cxt);
+ if (rc)
+ printf("failed to mount\n");
+ else {
+ printf("successfully mounted");
+ rc = mnt_context_post_mount(cxt);
+ if (rc)
+ printf("mtab update failed\n");
+ }
+ }
+
+ mnt_free_context(cxt);
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mtest tss[] = {
+ { "--mount", test_mount, "[-o <opts>] [-t <type>] <spec> | <src> <target>" },
+ { NULL }
+ };
+
+ return mnt_run_test(tss, argc, argv);
+}
+
+#endif /* TEST_PROGRAM */