summaryrefslogtreecommitdiffstats
path: root/libmount/src/context_mount.c
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/src/context_mount.c
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/src/context_mount.c')
-rw-r--r--libmount/src/context_mount.c177
1 files changed, 164 insertions, 13 deletions
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,