]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: allow to use propagation flags in fstab
authorKarel Zak <kzak@redhat.com>
Tue, 15 Jan 2013 12:55:29 +0000 (13:55 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 15 Jan 2013 12:55:29 +0000 (13:55 +0100)
Linux kernel does not allow to change more than one propagation flag
by one mount(2) syscall. The flags also cannot be mixed with another
mount options. It means that the propagation flags cannot be stored in
/etc/fstab, manual "mount --make-* <mountpoint>" is always necessary
after successful mount. Painful...

This patch implements additional mount(2) after previous successful
mount(2) (or exec /sbin/mount.<type>).

For example:

  mount /dev/sda1 /A -o private,unbindable,ro

or fstab entry:

  /dev/sda1   /A  auto  ro,private,unbindable

is implemented by three mount(2) calls:

- 1st mounts /dev/sda1 with MS_RDONLY
- 2nd sets MS_PRIVATE flag
        - 3rd sets MS_UNBINDABLE flag.

It's the same as as to manually call:

  mount /dev/sda1 /A -o ro
  mount --make-private /A
  mount --make-unbindable /A

This solution is not atomic, and umount(2) is not called if
propagation flags are not successfully applied, only error is
returned.

This change does not affect libmount API, so one beautiful day when
mount(2) syscall will be improved we can drop this nasty patch.

Signed-off-by: Karel Zak <kzak@redhat.com>
libmount/src/context.c
libmount/src/context_loopdev.c
libmount/src/context_mount.c
libmount/src/libmount.h.in
libmount/src/mountP.h
libmount/src/optmap.c

index 6a282910d069af41d4d81f5a97ab47e44a0afb4f..2e28d650a8505bb9f7f53f1e84cec70d7fc64a5b 100644 (file)
@@ -49,6 +49,8 @@ struct libmnt_context *mnt_new_context(void)
        if (!cxt)
                return NULL;
 
+       INIT_LIST_HEAD(&cxt->addmounts);
+
        ruid = getuid();
        euid = geteuid();
 
@@ -152,6 +154,14 @@ int mnt_reset_context(struct libmnt_context *cxt)
        cxt->mountdata = NULL;
        cxt->flags = MNT_FL_DEFAULT;
 
+       /* free additional mounts list */
+       while (!list_empty(&cxt->addmounts)) {
+               struct libmnt_addmount *ad = list_entry(cxt->addmounts.next,
+                                                 struct libmnt_addmount,
+                                                 mounts);
+               mnt_free_addmount(ad);
+       }
+
        mnt_context_reset_status(cxt);
        mnt_context_set_tabfilter(cxt, NULL, NULL);
 
@@ -1308,7 +1318,7 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
 
        src = mnt_fs_get_source(cxt->fs);
 
-       if (!src && (cxt->mountflags & MS_PROPAGATION))
+       if (!src && mnt_context_propagation_only(cxt))
                /* mount --make-{shared,private,...} */
                return mnt_fs_set_source(cxt->fs, "none");
 
@@ -1348,8 +1358,8 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt)
        if (!path)
                path = src;
 
-       if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION | MS_REMOUNT)) ||
-           mnt_fs_is_pseudofs(cxt->fs)) {
+       if ((cxt->mountflags & (MS_BIND | MS_MOVE | MS_REMOUNT))
+           || mnt_fs_is_pseudofs(cxt->fs)) {
                DBG(CXT, mnt_debug_h(cxt, "REMOUNT/BIND/MOVE/pseudo FS source: %s", path));
                return rc;
        }
@@ -1471,7 +1481,8 @@ int mnt_context_guess_fstype(struct libmnt_context *cxt)
        if (!cxt || !cxt->fs)
                return -EINVAL;
 
-       if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
+       if ((cxt->mountflags & (MS_BIND | MS_MOVE))
+           || mnt_context_propagation_only(cxt))
                goto none;
 
        type = (char *) mnt_fs_get_fstype(cxt->fs);
@@ -1622,8 +1633,8 @@ int mnt_context_prepare_update(struct libmnt_context *cxt)
 
        DBG(CXT, mnt_debug_h(cxt, "prepare update"));
 
-       if (cxt->mountflags & MS_PROPAGATION) {
-               DBG(CXT, mnt_debug_h(cxt, "skip update: MS_PROPAGATION"));
+       if (mnt_context_propagation_only(cxt)) {
+               DBG(CXT, mnt_debug_h(cxt, "skip update: only MS_PROPAGATION"));
                return 0;
        }
 
@@ -1880,6 +1891,30 @@ int mnt_context_tab_applied(struct libmnt_context *cxt)
        return cxt->flags & MNT_FL_TAB_APPLIED;
 }
 
+/*
+ * This is not public function!
+ *
+ * Returns 1 if *only propagation flags* change is requested.
+ */
+int mnt_context_propagation_only(struct libmnt_context *cxt)
+{
+       assert(cxt);
+       assert(cxt->fs);
+
+       if (cxt->action != MNT_ACT_MOUNT)
+               return 0;
+
+       /* has to be called after context_mount.c: fix_opts() */
+       assert((cxt->flags & MNT_FL_MOUNTOPTS_FIXED));
+
+       /* all propagation mounts are in cxt->addmount */
+       return !list_empty(&cxt->addmounts)
+              && (cxt->mountflags == 0 || cxt->mountflags == MS_SILENT)
+              && cxt->fs
+              && (!cxt->fs->fstype || strcmp(cxt->fs->fstype, "none") == 0)
+              && (!cxt->fs->source || strcmp(cxt->fs->source, "none") == 0);
+}
+
 /**
  * mnt_context_get_status:
  * @cxt: mount context
index 369f81dc0f7d48b5c7d73c371e9f7e0be1d8f982..576b7f55aba4f220212f244a3ed281f9c6cb3c58 100644 (file)
@@ -40,7 +40,8 @@ int mnt_context_is_loopdev(struct libmnt_context *cxt)
                return 1;
        }
 
-       if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION))
+       if ((cxt->mountflags & (MS_BIND | MS_MOVE))
+           || mnt_context_propagation_only(cxt))
                return 0;
 
        /* Automatically create a loop device from a regular file if a
index 196e3a249c554765682812773aa06c6ab79eac6f..00738b4b5edef12e3d2ddb0cd287b74d97226bfb 100644 (file)
 #include "linux_version.h"
 #include "mountP.h"
 
+/*
+ * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
+ * to bypass this restriction we call mount(2) per flag. It's realy not a perfect
+ * solution, but it's the same like to execute multiple mount(8) commands.
+ *
+ * We use cxt->addmounts (additional mounts) list to keep order of the requested
+ * flags changes.
+ */
+struct libmnt_addmount *mnt_new_addmount(void)
+{
+       struct libmnt_addmount *ad = calloc(1, sizeof(*ad));
+       if (!ad)
+               return NULL;
+
+       INIT_LIST_HEAD(&ad->mounts);
+       return ad;
+}
+
+void mnt_free_addmount(struct libmnt_addmount *ad)
+{
+       if (!ad)
+               return;
+       list_del(&ad->mounts);
+       free(ad);
+}
+
+static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
+                                              struct libmnt_addmount *ad)
+{
+       assert(cxt);
+       assert(ad);
+
+       DBG(CXT, mnt_debug_h(cxt,
+                       "mount: add additional flag: 0x%08lx",
+                       ad->mountflags));
+
+       list_add_tail(&ad->mounts, &cxt->addmounts);
+       return 0;
+}
+
+static int init_propagation(struct libmnt_context *cxt)
+{
+       char *name;
+       char *opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
+       size_t namesz;
+       struct libmnt_optmap const *maps[1];
+
+       if (!opts)
+               return 0;
+
+       DBG(CXT, mnt_debug_h(cxt, "mount: initialize additional propagation mounts"));
+
+       maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
+
+       while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
+               const struct libmnt_optmap *ent;
+               struct libmnt_addmount *ad;
+               int rc;
+
+               if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent)
+                   || !ent
+                   || !(ent->id & MS_PROPAGATION))
+                       continue;
+
+               ad = mnt_new_addmount();
+               if (!ad)
+                       return -ENOMEM;
+
+               ad->mountflags = ent->id;
+               rc = mnt_context_append_additional_mount(cxt, ad);
+               if (rc)
+                       return rc;
+
+               cxt->mountflags &= ~ent->id;
+       }
+
+       return 0;
+}
+
 /*
  * this has to be called after mnt_context_evaluate_permissions()
  */
@@ -48,11 +127,6 @@ static int fix_optstr(struct libmnt_context *cxt)
 
        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
@@ -95,6 +169,12 @@ static int fix_optstr(struct libmnt_context *cxt)
                fs->user_optstr = NULL;
        }
 
+       if (cxt->mountflags & MS_PROPAGATION) {
+               rc = init_propagation(cxt);
+               if (rc)
+                       return rc;
+       }
+
        next = fs->fs_optstr;
 
 #ifdef HAVE_LIBSELINUX
@@ -180,7 +260,7 @@ done:
  */
 static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
 {
-       struct libmnt_optmap const *maps[1];
+       struct libmnt_optmap const *maps[2];
        char *next, *name, *val;
        size_t namesz, valsz;
        int rc = 0;
@@ -202,12 +282,13 @@ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
 
        /* remove userspace options with MNT_NOHLPS flag */
        maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
+       maps[1] = mnt_get_builtin_optmap(MNT_LINUX_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);
+               mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
                if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
                        next = name;
                        rc = mnt_optstr_remove_option_at(optstr, name,
@@ -467,6 +548,43 @@ static int exec_helper(struct libmnt_context *cxt)
        return rc;
 }
 
+static int do_mount_additional(struct libmnt_context *cxt,
+                              const char *target,
+                              unsigned long flags,
+                              int *syserr)
+{
+       struct list_head *p;
+
+       assert(cxt);
+       assert(target);
+
+       if (syserr)
+               *syserr = 0;
+
+       list_for_each(p, &cxt->addmounts) {
+               int rc;
+               struct libmnt_addmount *ad =
+                               list_entry(p, struct libmnt_addmount, mounts);
+
+               DBG(CXT, mnt_debug_h(cxt, "mount(2) changing flag: 0x%08lx %s",
+                               ad->mountflags,
+                               ad->mountflags & MS_REC ? " (recursive)" : ""));
+
+               rc = mount("none", target, NULL,
+                               ad->mountflags | (flags & MS_SILENT), NULL);
+               if (rc) {
+                       if (syserr)
+                               *syserr = -errno;
+                       DBG(CXT, mnt_debug_h(cxt,
+                                       "mount(2) failed [errno=%d %m]",
+                                       errno));
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * The default is to use fstype from cxt->fs, this could be overwritten by
  * @try_type argument.
@@ -490,13 +608,23 @@ static int do_mount(struct libmnt_context *cxt, const char *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 (cxt->helper) {
+               rc = exec_helper(cxt);
+
+               if (mnt_context_helper_executed(cxt)
+                   && mnt_context_get_helper_status(cxt) == 0
+                   && !list_empty(&cxt->addmounts)
+                   && do_mount_additional(cxt, target, flags, NULL))
+
+                       return -MNT_ERR_APPLYFLAGS;
+               return rc;
+       }
+
        if (!target)
                return -EINVAL;
        if (!src) {
@@ -517,9 +645,22 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type)
                        src, target, type,
                        flags, cxt->mountdata ? "yes" : "<none>"));
 
-       if (mnt_context_is_fake(cxt))
+       if (mnt_context_is_fake(cxt)) {
+               /*
+                * fake
+                */
                cxt->syscall_status = 0;
-       else {
+
+       } else if (mnt_context_propagation_only(cxt)) {
+               /*
+                * propagation flags *only*
+                */
+               if (do_mount_additional(cxt, target, flags, &cxt->syscall_status))
+                       return -MNT_ERR_APPLYFLAGS;
+       } else {
+               /*
+                * regular mount
+                */
                if (mount(src, target, type, flags, cxt->mountdata)) {
                        cxt->syscall_status = -errno;
                        DBG(CXT, mnt_debug_h(cxt, "mount(2) failed [errno=%d %m]",
@@ -528,6 +669,16 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type)
                }
                DBG(CXT, mnt_debug_h(cxt, "mount(2) success"));
                cxt->syscall_status = 0;
+
+               /*
+                * additional mounts for extra propagation flags
+                */
+               if (!list_empty(&cxt->addmounts)
+                   && do_mount_additional(cxt, target, flags, NULL)) {
+
+                       /* TODO: call umount? */
+                       return -MNT_ERR_APPLYFLAGS;
+               }
        }
 
        if (try_type && cxt->update) {
@@ -626,7 +777,6 @@ int mnt_context_prepare_mount(struct libmnt_context *cxt)
 
        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);
@@ -722,7 +872,8 @@ int mnt_context_do_mount(struct libmnt_context *cxt)
                 * 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))
+               if (!(cxt->mountflags & (MS_RDONLY | MS_MOVE))
+                   && !mnt_context_propagation_only(cxt)
                    && mnt_is_readonly(mnt_context_get_target(cxt)))
 
                        mnt_context_set_mflags(cxt,
index e716f8f0cfd72d33f06a6296b8ee03a45e48843a..6d3a12f611a9265e4b55df4f2c4d2b5c68c392be 100644 (file)
@@ -128,6 +128,7 @@ enum {
 #define MNT_ERR_NOSOURCE     5002      /* required mount source undefined */
 #define MNT_ERR_LOOPDEV             5003       /* loopdev setup failed, errno set by libc */
 #define MNT_ERR_MOUNTOPT     5004      /* failed to parse/use userspace mount options */
+#define MNT_ERR_APPLYFLAGS   5005      /* failed to apply MS_PROPAGATION flags */
 
 #ifndef __GNUC_PREREQ
 # if defined __GNUC__ && defined __GNUC_MINOR__
index edef94c1b0dc40719c076837c9395c2ed962aee3..0fe5b1d86f960bd6564d805ac9dafaf15a6c85bc 100644 (file)
@@ -283,6 +283,14 @@ enum {
        MNT_FMT_SWAPS                   /* /proc/swaps */
 };
 
+/*
+ * Additional mounts
+ */
+struct libmnt_addmount {
+       unsigned long mountflags;
+
+       struct list_head        mounts;
+};
 
 /*
  * Mount context -- high-level API
@@ -317,6 +325,8 @@ struct libmnt_context
 
        unsigned long   user_mountflags;        /* MNT_MS_* (loop=, user=, ...) */
 
+       struct list_head        addmounts;      /* additional mounts */
+
        struct libmnt_cache     *cache; /* paths cache */
        struct libmnt_lock      *lock;  /* mtab lock */
        struct libmnt_update    *update;/* mtab/utab update */
@@ -416,6 +426,12 @@ extern int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg
 extern int mnt_context_is_loopdev(struct libmnt_context *cxt)
                        __attribute__((nonnull));
 
+extern int mnt_context_propagation_only(struct libmnt_context *cxt)
+                       __attribute__((nonnull));
+
+extern struct libmnt_addmount *mnt_new_addmount(void);
+extern void mnt_free_addmount(struct libmnt_addmount *ad);
+
 extern int mnt_context_setup_loopdev(struct libmnt_context *cxt);
 extern int mnt_context_delete_loopdev(struct libmnt_context *cxt);
 extern int mnt_context_clear_loopdev(struct libmnt_context *cxt);
index 68217cebe509da41f397fa50e5f9270e533a86ce..7c0a235088b833ec18a7495c9d26918f157af1a1 100644 (file)
@@ -111,6 +111,16 @@ static const struct libmnt_optmap linux_flags_map[] =
 #ifdef MS_STRICTATIME
    { "strictatime", MS_STRICTATIME },         /* Strict atime semantics */
    { "nostrictatime", MS_STRICTATIME, MNT_INVERT }, /* kernel default atime */
+#endif
+#ifdef MS_PROPAGATION
+   { "unbindable",  MS_UNBINDABLE,          MNT_NOHLPS | MNT_NOMTAB }, /* Unbindable */
+   { "runbindable", MS_UNBINDABLE | MS_REC, MNT_NOHLPS | MNT_NOMTAB },
+   { "private",     MS_PRIVATE,             MNT_NOHLPS | MNT_NOMTAB }, /* Private */
+   { "rprivate",    MS_PRIVATE | MS_REC,    MNT_NOHLPS | MNT_NOMTAB },
+   { "slave",       MS_SLAVE,               MNT_NOHLPS | MNT_NOMTAB }, /* Slave */
+   { "rslave",      MS_SLAVE | MS_REC,      MNT_NOHLPS | MNT_NOMTAB },
+   { "shared",      MS_SHARED,              MNT_NOHLPS | MNT_NOMTAB }, /* Shared */
+   { "rshared",     MS_SHARED | MS_REC,     MNT_NOHLPS | MNT_NOMTAB },
 #endif
    { NULL, 0, 0 }
 };