diff options
-rw-r--r-- | libmount/src/context.c | 47 | ||||
-rw-r--r-- | libmount/src/context_loopdev.c | 3 | ||||
-rw-r--r-- | libmount/src/context_mount.c | 177 | ||||
-rw-r--r-- | libmount/src/libmount.h.in | 1 | ||||
-rw-r--r-- | libmount/src/mountP.h | 16 | ||||
-rw-r--r-- | libmount/src/optmap.c | 10 |
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 } }; |