if (!cxt)
return NULL;
+ INIT_LIST_HEAD(&cxt->addmounts);
+
ruid = getuid();
euid = geteuid();
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);
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");
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;
}
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);
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;
}
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
#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()
*/
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
fs->user_optstr = NULL;
}
+ if (cxt->mountflags & MS_PROPAGATION) {
+ rc = init_propagation(cxt);
+ if (rc)
+ return rc;
+ }
+
next = fs->fs_optstr;
#ifdef HAVE_LIBSELINUX
*/
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;
/* 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,
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.
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) {
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]",
}
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) {
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);
* 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,