From d840039ecf7159920fc2457c0c9f0339253db814 Mon Sep 17 00:00:00 2001 From: Yifeng Tan Date: Mon, 12 Feb 2018 20:01:32 +0800 Subject: [PATCH] conf: support mount propagation Closes #810. Signed-off-by: Yifeng Tan Signed-off-by: Christian Brauner --- doc/lxc.container.conf.sgml.in | 5 ++- src/lxc/conf.c | 81 ++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index ccc6348c4..34b8117bb 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -1059,10 +1059,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - specify a mount point corresponding to a line in the + Specify a mount point corresponding to a line in the fstab format. - Moreover lxc add two options to mount. + Moreover lxc supports mount propagation, such as rslave or + rprivate, and adds three additional mount options. don't fail if mount does not work. or to create dir (or file) when the point will be mounted. diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 05b17a499..01f11422a 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -193,6 +193,18 @@ static struct mount_opt mount_opt[] = { { NULL, 0, 0 }, }; +static struct mount_opt propagation_opt[] = { + { "private", 0, MS_PRIVATE }, + { "shared", 0, MS_SHARED }, + { "slave", 0, MS_SLAVE }, + { "unbindable", 0, MS_UNBINDABLE }, + { "rprivate", 0, MS_PRIVATE|MS_REC }, + { "rshared", 0, MS_SHARED|MS_REC }, + { "rslave", 0, MS_SLAVE|MS_REC }, + { "runbindable", 0, MS_UNBINDABLE|MS_REC }, + { NULL, 0, 0 }, +}; + #if HAVE_LIBCAP static struct caps_opt caps_opt[] = { { "chown", CAP_CHOWN }, @@ -1773,6 +1785,46 @@ int parse_mntopts(const char *mntopts, unsigned long *mntflags, return 0; } +static void parse_propagationopt(char *opt, unsigned long *flags) +{ + struct mount_opt *mo; + + /* If opt is found in propagation_opt, set or clear flags. */ + + for (mo = &propagation_opt[0]; mo->name != NULL; mo++) { + if (strncmp(opt, mo->name, strlen(mo->name)) == 0) { + if (mo->clear) + *flags &= ~mo->flag; + else + *flags |= mo->flag; + return; + } + } +} + +static int parse_propagationopts(const char *mntopts, unsigned long *pflags) +{ + char *s; + char *p, *saveptr = NULL; + *pflags = 0L; + + if (!mntopts) + return 0; + + s = strdup(mntopts); + if (!s) { + SYSERROR("Failed to allocate memory"); + return -ENOMEM; + } + + for (p = strtok_r(s, ",", &saveptr); p != NULL; + p = strtok_r(NULL, ",", &saveptr)) + parse_propagationopt(p, pflags); + + free(s); + return 0; +} + static void null_endofword(char *word) { while (*word && *word != ' ' && *word != '\t') @@ -1800,8 +1852,8 @@ static char *get_field(char *src, int nfields) static int mount_entry(const char *fsname, const char *target, const char *fstype, unsigned long mountflags, - const char *data, bool optional, bool dev, - bool relative, const char *rootfs) + unsigned long pflags, const char *data, bool optional, + bool dev, bool relative, const char *rootfs) { int ret; char srcbuf[MAXPATHLEN]; @@ -1893,6 +1945,23 @@ static int mount_entry(const char *fsname, const char *target, } } + if (pflags) { + ret = mount(NULL, target, NULL, pflags, NULL); + if (ret < 0) { + if (optional) { + INFO("%s - Failed to change mount propagation " + "for \"%s\" (optional)", strerror(errno), target); + return 0; + } else { + SYSERROR("Failed to change mount propagation " + "for \"%s\" (optional)", target); + return -1; + } + } + DEBUG("Changed mount propagation for \"%s\"", target); + } + + #ifdef HAVE_STATVFS skipremount: #endif @@ -1984,7 +2053,7 @@ static inline int mount_entry_on_generic(struct mntent *mntent, const char *lxc_path) { int ret; - unsigned long mntflags; + unsigned long mntflags, pflags; char *mntdata; bool dev, optional, relative; char *rootfs_path = NULL; @@ -2006,12 +2075,16 @@ static inline int mount_entry_on_generic(struct mntent *mntent, } cull_mntent_opt(mntent); + ret = parse_propagationopts(mntent->mnt_opts, &pflags); + if (ret < 0) + return -1; + ret = parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata); if (ret < 0) return -1; ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags, - mntdata, optional, dev, relative, rootfs_path); + pflags, mntdata, optional, dev, relative, rootfs_path); free(mntdata); return ret;