summaryrefslogtreecommitdiffstats
path: root/libmount
diff options
context:
space:
mode:
authorKarel Zak2013-01-15 13:55:29 +0100
committerKarel Zak2013-01-15 13:55:29 +0100
commit6498ece0e777ae1aeab1319a21abec7457fb940f (patch)
tree52269349b81bcf2c37ed0e97603cb4b8dcb49d9f /libmount
parentkill: add note about threads to the man page (diff)
downloadkernel-qcow2-util-linux-6498ece0e777ae1aeab1319a21abec7457fb940f.tar.gz
kernel-qcow2-util-linux-6498ece0e777ae1aeab1319a21abec7457fb940f.tar.xz
kernel-qcow2-util-linux-6498ece0e777ae1aeab1319a21abec7457fb940f.zip
libmount: allow to use propagation flags in fstab
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>
Diffstat (limited to 'libmount')
-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 }
};