summaryrefslogtreecommitdiff
path: root/libmount/src/context_mount.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmount/src/context_mount.c')
-rw-r--r--libmount/src/context_mount.c935
1 files changed, 935 insertions, 0 deletions
diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
new file mode 100644
index 0000000..f3d8ff1
--- /dev/null
+++ b/libmount/src/context_mount.c
@@ -0,0 +1,935 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: context-mount
+ * @title: Mount context
+ * @short_description: high-level API to mount operation.
+ */
+
+#ifdef HAVE_LIBSELINUX
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#endif
+
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include "linux_version.h"
+#include "mountP.h"
+
+/*
+ * this has to be called after mnt_context_evaluate_permissions()
+ */
+static int fix_optstr(struct libmnt_context *cxt)
+{
+ int rc = 0;
+ char *next;
+ char *name, *val;
+ size_t namesz, valsz;
+ struct libmnt_fs *fs;
+#ifdef HAVE_LIBSELINUX
+ int se_fix = 0, se_rem = 0;
+#endif
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing optstr"));
+
+ fs = cxt->fs;
+
+ /* The propagation flags should not be used together with any other
+ * flags (except MS_REC and MS_SILENT) */
+ if (cxt->mountflags & MS_PROPAGATION)
+ cxt->mountflags &= (MS_PROPAGATION | MS_REC | MS_SILENT);
+
+ /*
+ * The "user" options is our business (so we can modify the option),
+ * but exception is command line for /sbin/mount.<type> helpers. Let's
+ * save the original user=<name> to call the helpers with unchanged
+ * "user" setting.
+ *
+ * Don't check for MNT_MS_USER in cxt->user_mountflags, the flag maybe
+ * removed by evaluate_permissions().
+ */
+ if (!mnt_optstr_get_option(fs->user_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
+ */
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing vfs optstr"));
+ rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
+ mnt_get_builtin_optmap(MNT_LINUX_MAP));
+ if (rc)
+ goto done;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: fixing user optstr"));
+ rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
+ mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
+ if (rc)
+ goto done;
+
+ if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
+ free(fs->vfs_optstr);
+ fs->vfs_optstr = NULL;
+ }
+ if (fs->user_optstr && *fs->user_optstr == '\0') {
+ free(fs->user_optstr);
+ fs->user_optstr = NULL;
+ }
+
+ next = fs->fs_optstr;
+
+#ifdef HAVE_LIBSELINUX
+ if (!is_selinux_enabled())
+ /* Always remove SELinux garbage if SELinux disabled */
+ se_rem = 1;
+ else if (cxt->mountflags & MS_REMOUNT)
+ /*
+ * Linux kernel < 2.6.39 does not allow to remount with any
+ * selinux specific mount options.
+ *
+ * Kernel 2.6.39 commits: ff36fe2c845cab2102e4826c1ffa0a6ebf487c65
+ * 026eb167ae77244458fa4b4b9fc171209c079ba7
+ * fix this odd behavior, so we don't have to care about it in
+ * userspace.
+ */
+ se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39);
+ else
+ /* For normal mount we have translate the contexts */
+ se_fix = 1;
+
+ if (!se_rem) {
+ /* de-duplicate SELinux options */
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "context");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "fscontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "defcontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "rootcontext");
+ mnt_optstr_deduplicate_option(&fs->fs_optstr, "seclabel");
+ }
+#endif
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+
+ if (namesz == 3 && !strncmp(name, "uid", 3))
+ rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
+ else if (namesz == 3 && !strncmp(name, "gid", 3))
+ rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
+#ifdef HAVE_LIBSELINUX
+ else if ((se_rem || se_fix) &&
+ namesz >= 7 && (!strncmp(name, "context", 7) ||
+ !strncmp(name, "fscontext", 9) ||
+ !strncmp(name, "defcontext", 10) ||
+ !strncmp(name, "rootcontext", 11) ||
+ !strncmp(name, "seclabel", 8))) {
+ if (se_rem) {
+ /* remove context= option */
+ next = name;
+ rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
+ name,
+ val ? val + valsz :
+ name + namesz);
+ } else if (se_fix && val && valsz)
+ /* translate selinux contexts */
+ rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
+ val, valsz, &next);
+ }
+#endif
+ if (rc)
+ goto done;
+ }
+
+ if (!rc && cxt->user_mountflags & MNT_MS_USER)
+ rc = mnt_optstr_fix_user(&fs->user_optstr);
+
+ /* refresh merged optstr */
+ free(fs->optstr);
+ fs->optstr = NULL;
+ fs->optstr = mnt_fs_strdup_options(fs);
+done:
+ cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;
+
+ DBG(CXT, mnt_debug_h(cxt, "fixed options [rc=%d]: "
+ "vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
+ fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));
+
+ if (rc)
+ rc = -MNT_ERR_MOUNTOPT;
+ return rc;
+}
+
+/*
+ * Converts already evaluated and fixed options to the form that is compatible
+ * with /sbin/mount.type helpers.
+ */
+static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
+{
+ struct libmnt_optmap const *maps[1];
+ char *next, *name, *val;
+ size_t namesz, valsz;
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(optstr);
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: generate helper mount options"));
+
+ *optstr = mnt_fs_strdup_options(cxt->fs);
+ if (!*optstr)
+ return -ENOMEM;
+
+ if (cxt->flags & MNT_FL_SAVED_USER)
+ rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
+ if (rc)
+ goto err;
+
+ /* remove userspace options with MNT_NOHLPS flag */
+ maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+ next = *optstr;
+
+ while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
+ const struct libmnt_optmap *ent;
+
+ mnt_optmap_get_entry(maps, 1, name, namesz, &ent);
+ if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
+ next = name;
+ rc = mnt_optstr_remove_option_at(optstr, name,
+ val ? val + valsz : name + namesz);
+ if (rc)
+ goto err;
+ }
+ }
+
+ return rc;
+err:
+ free(*optstr);
+ *optstr = NULL;
+ return rc;
+}
+
+/*
+ * this has to be called before fix_optstr()
+ *
+ * Note that user=<name> maybe be used by some filesystems as filesystem
+ * specific option (e.g. cifs). Yes, developers of such filesystems have
+ * allocated pretty hot place in hell...
+ */
+static int evaluate_permissions(struct libmnt_context *cxt)
+{
+ unsigned long u_flags = 0;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!cxt)
+ return -EINVAL;
+ if (!cxt->fs)
+ return 0;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: evaluating permissions"));
+
+ mnt_context_get_user_mflags(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 (!(cxt->flags & MNT_FL_TAB_APPLIED))
+ {
+ DBG(CXT, mnt_debug_h(cxt, "perms: fstab not applied, ignore user mount"));
+ return -EPERM;
+ }
+
+ /*
+ * MS_OWNERSECURE and MS_SECURE mount options are already
+ * applied by mnt_optstr_get_flags() in mnt_context_merge_mflags()
+ * if "user" (but no user=<name> !) options is set.
+ *
+ * Let's ignore all user=<name> (if <name> is set) requests.
+ */
+ if (cxt->user_mountflags & MNT_MS_USER) {
+ size_t valsz = 0;
+
+ if (!mnt_optstr_get_option(cxt->fs->user_optstr,
+ "user", NULL, &valsz) && valsz) {
+
+ DBG(CXT, mnt_debug_h(cxt, "perms: user=<name> detected, ignore"));
+ cxt->user_mountflags &= ~MNT_MS_USER;
+ }
+ }
+
+ /*
+ * 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;
+ struct libmnt_cache *cache = NULL;
+ char *xsrc = NULL;
+ const char *srcpath = mnt_fs_get_srcpath(cxt->fs);
+
+ if (!srcpath) { /* Ah... source is TAG */
+ cache = mnt_context_get_cache(cxt);
+ xsrc = mnt_resolve_spec(
+ mnt_context_get_source(cxt),
+ cache);
+ srcpath = xsrc;
+ }
+ if (!srcpath) {
+ DBG(CXT, mnt_debug_h(cxt, "perms: src undefined"));
+ return -EPERM;
+ }
+
+ 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 (!cache)
+ free(xsrc);
+ }
+
+ 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;
+}
+
+/*
+ * mnt_context_helper_setopt() backend
+ *
+ * This function applies mount.type command line option (for example parsed
+ * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
+ * then 1 is returned.
+ *
+ * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
+ */
+int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
+{
+ int rc = -EINVAL;
+
+ assert(cxt);
+ assert(cxt->action == MNT_ACT_MOUNT);
+
+ switch(c) {
+ case 'f':
+ rc = mnt_context_enable_fake(cxt, TRUE);
+ break;
+ case 'n':
+ rc = mnt_context_disable_mtab(cxt, TRUE);
+ break;
+ case 'r':
+ rc = mnt_context_append_options(cxt, "ro");
+ break;
+ case 'v':
+ rc = mnt_context_enable_verbose(cxt, TRUE);
+ break;
+ case 'w':
+ rc = mnt_context_append_options(cxt, "rw");
+ break;
+ case 'o':
+ if (arg)
+ rc = mnt_context_append_options(cxt, arg);
+ break;
+ case 's':
+ rc = mnt_context_enable_sloppy(cxt, TRUE);
+ break;
+ case 't':
+ if (arg)
+ rc = mnt_context_set_fstype(cxt, arg);
+ break;
+ default:
+ return 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int exec_helper(struct libmnt_context *cxt)
+{
+ char *o = NULL;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: executing helper %s", cxt->helper));
+
+ rc = generate_helper_optstr(cxt, &o);
+ if (rc)
+ return -EINVAL;
+
+ 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 */
+
+ /*
+ * TODO: remove the exception for "nfs", -s is documented
+ * for years should be usable everywhere.
+ */
+ if (mnt_context_is_sloppy(cxt) &&
+ type && startswith(type, "nfs"))
+ args[i++] = "-s"; /* 4 */
+ if (mnt_context_is_fake(cxt))
+ args[i++] = "-f"; /* 5 */
+ if (mnt_context_is_nomtab(cxt))
+ args[i++] = "-n"; /* 6 */
+ if (mnt_context_is_verbose(cxt))
+ 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
+ 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));
+ cxt->helper_exec_status = rc = 0;
+ break;
+ }
+
+ case -1:
+ cxt->helper_exec_status = rc = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "fork() failed"));
+ break;
+ }
+
+ free(o);
+ return rc;
+}
+
+/*
+ * The default is to use fstype from cxt->fs, this could be overwritten by
+ * @try_type argument.
+ *
+ * Returns: 0 on success,
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+static int do_mount(struct libmnt_context *cxt, const char *try_type)
+{
+ int rc = 0;
+ const char *src, *target, *type;
+ unsigned long flags;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ 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);
+
+ flags = cxt->mountflags;
+ src = mnt_fs_get_srcpath(cxt->fs);
+ target = mnt_fs_get_target(cxt->fs);
+
+ if (!target)
+ return -EINVAL;
+ if (!src) {
+ /* unnecessary, should be already resolved in
+ * mnt_context_prepare_srcpath(), but for sure... */
+ DBG(CXT, mnt_debug_h(cxt, "WARNING: source is NULL -- using \"none\"!"));
+ src = "none";
+ }
+ type = try_type ? : mnt_fs_get_fstype(cxt->fs);
+
+ if (!(flags & MS_MGC_MSK))
+ flags |= MS_MGC_VAL;
+
+ DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
+ "[source=%s, target=%s, type=%s, "
+ " mountflags=0x%08lx, mountdata=%s]",
+ mnt_context_is_fake(cxt) ? "(FAKE) " : "",
+ src, target, type,
+ flags, cxt->mountdata ? "yes" : "<none>"));
+
+ if (mnt_context_is_fake(cxt))
+ cxt->syscall_status = 0;
+ else {
+ if (mount(src, target, type, flags, cxt->mountdata)) {
+ cxt->syscall_status = -errno;
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]",
+ -cxt->syscall_status));
+ return -cxt->syscall_status;
+ }
+ DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
+ cxt->syscall_status = 0;
+ }
+
+ if (try_type && cxt->update) {
+ struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
+ if (fs)
+ rc = mnt_fs_set_fstype(fs, try_type);
+ }
+
+ return rc;
+}
+
+static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
+{
+ int neg = pattern && strncmp(pattern, "no", 2) == 0;
+ int rc = -EINVAL;
+ char **filesystems, **fp;
+
+ assert(cxt);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+
+ if (!neg && pattern) {
+ /*
+ * try all types from the list
+ */
+ char *p, *p0;
+
+ DBG(CXT, mnt_debug_h(cxt, "trying to mount by FS pattern list"));
+
+ p0 = p = strdup(pattern);
+ if (!p)
+ return -ENOMEM;
+ do {
+ char *end = strchr(p, ',');
+ if (end)
+ *end = '\0';
+ rc = do_mount(cxt, p);
+ p = end ? end + 1 : NULL;
+
+ } while (!mnt_context_get_status(cxt) && p);
+
+ free(p0);
+
+ if (mnt_context_get_status(cxt))
+ return rc;
+ }
+
+ /*
+ * try /etc/filesystems and /proc/filesystems
+ */
+ DBG(CXT, mnt_debug_h(cxt, "trying to mount by filesystems lists"));
+
+ rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
+ if (rc)
+ return rc;
+
+ for (fp = filesystems; *fp; fp++) {
+ rc = do_mount(cxt, *fp);
+ if (mnt_context_get_status(cxt))
+ break;
+ if (mnt_context_get_syscall_errno(cxt) != EINVAL)
+ break;
+ }
+ mnt_free_filesystems(filesystems);
+ return rc;
+}
+
+/**
+ * mnt_context_prepare_mount:
+ * @cxt: context
+ *
+ * Prepare context for mounting, unnecessary for mnt_context_mount().
+ *
+ * Returns: negative number on error, zero on success
+ */
+int mnt_context_prepare_mount(struct libmnt_context *cxt)
+{
+ int rc = -EINVAL;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
+ return -EINVAL;
+ if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
+ return -EINVAL;
+ if (cxt->flags & MNT_FL_PREPARED)
+ return 0;
+
+ cxt->action = MNT_ACT_MOUNT;
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: preparing"));
+
+ /* TODO: fstab is unnecessary for MS_{MOVE,BIND,STARED,...} */
+ rc = mnt_context_apply_fstab(cxt);
+ if (!rc)
+ rc = mnt_context_merge_mflags(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_prepare_target(cxt);
+ if (!rc)
+ rc = mnt_context_guess_fstype(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_helper(cxt, "mount", NULL);
+ if (rc) {
+ DBG(CXT, mnt_debug_h(cxt, "mount: preparing failed"));
+ return rc;
+ }
+ cxt->flags |= MNT_FL_PREPARED;
+ return rc;
+}
+
+/**
+ * mnt_context_do_mount
+ * @cxt: context
+ *
+ * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
+ *
+ * Note that this function could be called only once. If you want to mount
+ * another source or target than you have to call mnt_reset_context().
+ *
+ * If you want to call mount(2) for the same source and target with a different
+ * mount flags or fstype then call mnt_context_reset_status() and then try
+ * again mnt_context_do_mount().
+ *
+ * WARNING: non-zero return code does not mean that mount(2) syscall or
+ * mount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+*
+ * Returns: 0 on success;
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_do_mount(struct libmnt_context *cxt)
+{
+ const char *type;
+ int res;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert((cxt->flags & MNT_FL_PREPARED));
+ assert((cxt->action == MNT_ACT_MOUNT));
+
+ DBG(CXT, mnt_debug_h(cxt, "mount: do mount"));
+
+ if (!(cxt->flags & MNT_FL_MOUNTDATA))
+ cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);
+
+ type = mnt_fs_get_fstype(cxt->fs);
+ if (type)
+ res = do_mount(cxt, NULL);
+ else
+ res = do_mount_by_pattern(cxt, cxt->fstype_pattern);
+
+ if (mnt_context_get_status(cxt)
+ && !mnt_context_is_fake(cxt)
+ && !cxt->helper) {
+ /*
+ * Mounted by mount(2), do some post-mount checks
+ *
+ * Kernel allows to use MS_RDONLY for bind mounts, but the
+ * read-only request could be silently ignored. Check it to
+ * avoid 'ro' in mtab and 'rw' in /proc/mounts.
+ */
+ if ((cxt->mountflags & MS_BIND)
+ && (cxt->mountflags & MS_RDONLY)
+ && !mnt_is_readonly(mnt_context_get_target(cxt)))
+
+ mnt_context_set_mflags(cxt,
+ cxt->mountflags & ~MS_RDONLY);
+
+
+ /* Kernel can silently add MS_RDONLY flag when mounting file
+ * system that does not have write support. Check this to avoid
+ * 'ro' in /proc/mounts and 'rw' in mtab.
+ */
+ if (!(cxt->mountflags & (MS_RDONLY | MS_PROPAGATION | MS_MOVE))
+ && mnt_is_readonly(mnt_context_get_target(cxt)))
+
+ mnt_context_set_mflags(cxt,
+ cxt->mountflags | MS_RDONLY);
+ }
+
+ return res;
+}
+
+/**
+ * mnt_context_finalize_mount:
+ * @cxt: context
+ *
+ * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
+ * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
+ *
+ * Returns: negative number on error, 0 on success.
+ */
+int mnt_context_finalize_mount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
+ assert((cxt->flags & MNT_FL_PREPARED));
+
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);;
+ return rc;
+}
+
+/**
+ * mnt_context_mount:
+ * @cxt: mount context
+ *
+ * High-level, mounts filesystem by mount(2) or fork()+exec(/sbin/mount.type).
+ *
+ * This is similar to:
+ *
+ * mnt_context_prepare_mount(cxt);
+ * mnt_context_do_mount(cxt);
+ * mnt_context_finalize_mount(cxt);
+ *
+ * See also mnt_context_disable_helpers().
+ *
+ * Note that this function could be called only once. If you want to mount with
+ * different setting than you have to call mnt_reset_context(). It's NOT enough
+ * to call mnt_context_reset_status() if you want call this function more than
+ * once, whole context has to be reseted.
+ *
+ * WARNING: non-zero return code does not mean that mount(2) syscall or
+ * mount.type helper wasn't successfully called.
+ *
+ * Check mnt_context_get_status() after error!
+ *
+ * Returns: 0 on success;
+ * >0 in case of mount(2) error (returns syscall errno),
+ * <0 in case of other errors.
+ */
+int mnt_context_mount(struct libmnt_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->fs);
+ assert(cxt->helper_exec_status == 1);
+ assert(cxt->syscall_status == 1);
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (!rc)
+ rc = mnt_context_prepare_update(cxt);
+ if (!rc)
+ rc = mnt_context_do_mount(cxt);
+
+ /* TODO: if mtab update is expected then check if the
+ * target is really mounted read-write to avoid 'ro' in
+ * mtab and 'rw' in /proc/mounts.
+ */
+ if (!rc)
+ rc = mnt_context_update_tabs(cxt);
+ return rc;
+}
+
+/**
+ * mnt_context_next_mount:
+ * @cxt: context
+ * @itr: iterator
+ * @fs: returns the current filesystem
+ * @mntrc: returns the return code from mnt_context_mount()
+ * @ignored: returns 1 for not matching and 2 for already mounted filesystems
+ *
+ * This function tries to mount the next filesystem from fstab (as returned by
+ * mnt_context_get_fstab()). See also mnt_context_set_fstab().
+ *
+ * You can filter out filesystems by:
+ * mnt_context_set_options_pattern() to simulate mount -a -O pattern
+ * mnt_context_set_fstype_pattern() to simulate mount -a -t pattern
+ *
+ * If the filesystem is already mounted or does not match defined criteria,
+ * then the mnt_context_next_mount() function returns zero, but the @ignored is
+ * non-zero. Note that the root filesystem and filesystems with "noauto" option
+ * are always ignored.
+ *
+ * If mount(2) syscall or mount.type helper failed, then the
+ * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
+ * Use also mnt_context_get_status() to check if the filesystem was
+ * successfully mounted.
+ *
+ * Returns: 0 on success,
+ * <0 in case of error (!= mount(2) errors)
+ * 1 at the end of the list.
+ */
+int mnt_context_next_mount(struct libmnt_context *cxt,
+ struct libmnt_iter *itr,
+ struct libmnt_fs **fs,
+ int *mntrc,
+ int *ignored)
+{
+ struct libmnt_table *fstab, *mtab;
+ const char *o, *tgt;
+ int rc, mounted = 0;
+
+ if (ignored)
+ *ignored = 0;
+ if (mntrc)
+ *mntrc = 0;
+
+ if (!cxt || !fs || !itr)
+ return -EINVAL;
+
+ mtab = cxt->mtab;
+ cxt->mtab = NULL; /* do not reset mtab */
+ mnt_reset_context(cxt);
+ cxt->mtab = mtab;
+
+ rc = mnt_context_get_fstab(cxt, &fstab);
+ if (rc)
+ return rc;
+
+ rc = mnt_table_next_fs(fstab, itr, fs);
+ if (rc != 0)
+ return rc; /* more filesystems (or error) */
+
+ o = mnt_fs_get_user_options(*fs);
+ tgt = mnt_fs_get_target(*fs);
+
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: trying %s", tgt));
+
+ /* ignore swap */
+ if (mnt_fs_is_swaparea(*fs) ||
+
+ /* ignore root filesystem */
+ (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||
+
+ /* ignore noauto filesystems */
+ (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||
+
+ /* ignore filesystems not match with options patterns */
+ (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
+ cxt->fstype_pattern)) ||
+
+ /* ignore filesystems not match with type patterns */
+ (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
+ cxt->optstr_pattern))) {
+ if (ignored)
+ *ignored = 1;
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: not-match "
+ "[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
+ mnt_fs_get_fstype(*fs),
+ cxt->fstype_pattern,
+ mnt_fs_get_options(*fs),
+ cxt->optstr_pattern));
+ return 0;
+ }
+
+ /* ignore already mounted filesystems */
+ rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
+ if (rc)
+ return rc;
+ if (mounted) {
+ if (ignored)
+ *ignored = 2;
+ return 0;
+ }
+
+ if (mnt_context_is_fork(cxt)) {
+ rc = mnt_fork_context(cxt);
+ if (rc)
+ return rc; /* fork error */
+
+ if (mnt_context_is_parent(cxt)) {
+ return 0; /* parent */
+ }
+ }
+
+ /* child or non-forked */
+
+ rc = mnt_context_set_fs(cxt, *fs);
+ if (!rc) {
+ rc = mnt_context_mount(cxt);
+ if (mntrc)
+ *mntrc = rc;
+ }
+
+ if (mnt_context_is_child(cxt)) {
+ DBG(CXT, mnt_debug_h(cxt, "next-mount: child exit [rc=%d]", rc));
+ DBG_FLUSH;
+ exit(rc);
+ }
+ return 0;
+}
+