summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmount/src/context.c47
-rw-r--r--libmount/src/context_loopdev.c3
-rw-r--r--libmount/src/context_mount.c177
-rw-r--r--libmount/src/libmount.h.in1
-rw-r--r--libmount/src/mountP.h16
-rw-r--r--libmount/src/optmap.c10
6 files changed, 234 insertions, 20 deletions
diff --git a/libmount/src/context.c b/libmount/src/context.c
index 6a282910d..2e28d650a 100644
--- a/libmount/src/context.c
+++ b/libmount/src/context.c
@@ -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
diff --git a/libmount/src/context_loopdev.c b/libmount/src/context_loopdev.c
index 369f81dc0..576b7f55a 100644
--- a/libmount/src/context_loopdev.c
+++ b/libmount/src/context_loopdev.c
@@ -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
diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
index 196e3a249..00738b4b5 100644
--- a/libmount/src/context_mount.c
+++ b/libmount/src/context_mount.c
@@ -23,6 +23,85 @@
#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()
*/
static int fix_optstr(struct libmnt_context *cxt)
@@ -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,
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
index e716f8f0c..6d3a12f61 100644
--- a/libmount/src/libmount.h.in
+++ b/libmount/src/libmount.h.in
@@ -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__
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index edef94c1b..0fe5b1d86 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -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);
diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c
index 68217cebe..7c0a23508 100644
--- a/libmount/src/optmap.c
+++ b/libmount/src/optmap.c
@@ -112,6 +112,16 @@ static const struct libmnt_optmap linux_flags_map[] =
{ "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 }
};