[lxc-devel] [lxc/master] conf: improve rootfs setup

brauner on Github lxc-bot at linuxcontainers.org
Tue Jul 17 09:59:38 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180717/c19182cc/attachment.bin>
-------------- next part --------------
From 6953bcb72e0f8bad30c651de4bd85f232bedacc9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Tue, 17 Jul 2018 11:50:11 +0200
Subject: [PATCH] conf: improve rootfs setup

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/conf.c        | 261 ++++++++++++++++++++++++++------------------------
 src/lxc/conf.h        |  13 ++-
 src/lxc/confile.c     |  26 ++++-
 src/lxc/criu.c        |   3 +-
 src/lxc/start.c       |   2 +-
 src/lxc/storage/dir.c |  16 +++-
 src/lxc/utils.c       |   1 +
 7 files changed, 187 insertions(+), 135 deletions(-)

diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 33beb43d1..5283d0300 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1104,84 +1104,6 @@ static int lxc_create_ttys(struct lxc_handler *handler)
 	return ret;
 }
 
-static int setup_rootfs_pivot_root(const char *rootfs)
-{
-	int ret;
-	int newroot = -1, oldroot = -1;
-
-	oldroot = open("/", O_DIRECTORY | O_RDONLY);
-	if (oldroot < 0) {
-		SYSERROR("Failed to open old root directory");
-		return -1;
-	}
-
-	newroot = open(rootfs, O_DIRECTORY | O_RDONLY);
-	if (newroot < 0) {
-		SYSERROR("Failed to open new root directory");
-		goto on_error;
-	}
-
-	/* change into new root fs */
-	ret = fchdir(newroot);
-	if (ret < 0) {
-		SYSERROR("Failed to change to new rootfs \"%s\"", rootfs);
-		goto on_error;
-	}
-
-	/* pivot_root into our new root fs */
-	ret = pivot_root(".", ".");
-	if (ret < 0) {
-		SYSERROR("Failed to pivot_root()");
-		goto on_error;
-	}
-
-	/* At this point the old-root is mounted on top of our new-root. To
-	 * unmounted it we must not be chdir'd into it, so escape back to
-	 * old-root.
-	 */
-	ret = fchdir(oldroot);
-	if (ret < 0) {
-		SYSERROR("Failed to enter old root directory");
-		goto on_error;
-	}
-
-	/* Make oldroot rslave to make sure our umounts don't propagate to the
-	 * host.
-	 */
-	ret = mount("", ".", "", MS_SLAVE | MS_REC, NULL);
-	if (ret < 0) {
-		SYSERROR("Failed to make oldroot rslave");
-		goto on_error;
-	}
-
-	ret = umount2(".", MNT_DETACH);
-	if (ret < 0) {
-		SYSERROR("Failed to detach old root directory");
-		goto on_error;
-	}
-
-	ret = fchdir(newroot);
-	if (ret < 0) {
-		SYSERROR("Failed to re-enter new root directory");
-		goto on_error;
-	}
-
-	close(oldroot);
-	close(newroot);
-
-	DEBUG("pivot_root(\"%s\") successful", rootfs);
-
-	return 0;
-
-on_error:
-	if (oldroot != -1)
-		close(oldroot);
-	if (newroot != -1)
-		close(newroot);
-
-	return -1;
-}
-
 /* Just create a path for /dev under $lxcpath/$name and in rootfs If we hit an
  * error, log it but don't fail yet.
  */
@@ -1365,17 +1287,16 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
 	return 0;
 }
 
-static int lxc_setup_rootfs(struct lxc_conf *conf)
+static int lxc_mount_rootfs(struct lxc_conf *conf)
 {
 	int ret;
 	struct lxc_storage *bdev;
-	const struct lxc_rootfs *rootfs;
+	const struct lxc_rootfs *rootfs = &conf->rootfs;
 
-	rootfs = &conf->rootfs;
 	if (!rootfs->path) {
 		ret = mount("", "/", NULL, MS_SLAVE | MS_REC, 0);
 		if (ret < 0) {
-			SYSERROR("Failed to make / rslave");
+			SYSERROR("Failed to remount \"/\" MS_REC | MS_SLAVE");
 			return -1;
 		}
 
@@ -1413,33 +1334,36 @@ static int lxc_setup_rootfs(struct lxc_conf *conf)
 	return 0;
 }
 
-int prepare_ramfs_root(char *root)
+int lxc_chroot(const struct lxc_rootfs *rootfs)
 {
 	int i, ret;
 	char *p, *p2;
 	char buf[LXC_LINELEN], nroot[PATH_MAX];
 	FILE *f;
+	char *root = rootfs->mount;
 
-	if (!realpath(root, nroot))
-		return -1;
+	if (!realpath(root, nroot)) {
+		SYSERROR("Failed to resolve \"%s\"", root);
+		return -errno;
+	}
 
 	ret = chdir("/");
 	if (ret < 0)
-		return -1;
+		return -errno;
 
 	/* We could use here MS_MOVE, but in userns this mount is locked and
 	 * can't be moved.
 	 */
-	ret = mount(root, "/", NULL, MS_REC | MS_BIND, NULL);
+	ret = mount(nroot, "/", NULL, MS_REC | MS_BIND, NULL);
 	if (ret < 0) {
-		SYSERROR("Failed to move \"%s\" into \"/\"", root);
-		return -1;
+		SYSERROR("Failed to mount \"%s\" onto \"/\" as MS_REC | MS_BIND", nroot);
+		return -errno;
 	}
 
 	ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL);
 	if (ret < 0) {
-		SYSERROR("Failed to make \"/\" rprivate");
-		return -1;
+		SYSERROR("Failed to remount \"/\"");
+		return -errno;
 	}
 
 	/* The following code cleans up inhereted mounts which are not required
@@ -1457,8 +1381,8 @@ int prepare_ramfs_root(char *root)
 
 		f = fopen("./proc/self/mountinfo", "r");
 		if (!f) {
-			SYSERROR("Unable to open /proc/self/mountinfo");
-			return -1;
+			SYSERROR("Failed to open \"/proc/self/mountinfo\"");
+			return -errno;
 		}
 
 		while (fgets(buf, LXC_LINELEN, f)) {
@@ -1498,53 +1422,143 @@ int prepare_ramfs_root(char *root)
 	/* It is weird, but chdir("..") moves us in a new root */
 	ret = chdir("..");
 	if (ret < 0) {
-		SYSERROR("Unable to change working directory");
-		return -1;
+		SYSERROR("Failed to chdir(\"..\")");
+		return -errno;
 	}
 
 	ret = chroot(".");
 	if (ret < 0) {
-		SYSERROR("Unable to chroot");
-		return -1;
+		SYSERROR("Failed to chroot(\".\")");
+		return -errno;
 	}
 
 	return 0;
 }
 
-static int setup_pivot_root(const struct lxc_rootfs *rootfs)
+/* (The following explanation is copied verbatim from the kernel.)
+ *
+ * pivot_root Semantics:
+ * Moves the root file system of the current process to the directory put_old,
+ * makes new_root as the new root file system of the current process, and sets
+ * root/cwd of all processes which had them on the current root to new_root.
+ *
+ * Restrictions:
+ * The new_root and put_old must be directories, and  must not be on the
+ * same file  system as the current process root. The put_old  must  be
+ * underneath new_root,  i.e. adding a non-zero number of /.. to the string
+ * pointed to by put_old must yield the same directory as new_root. No other
+ * file system may be mounted on put_old. After all, new_root is a mountpoint.
+ *
+ * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
+ * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives
+ * in this situation.
+ *
+ * Notes:
+ *  - we don't move root/cwd if they are not at the root (reason: if something
+ *    cared enough to change them, it's probably wrong to force them elsewhere)
+ *  - it's okay to pick a root that isn't the root of a file system, e.g.
+ *    /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
+ *    though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
+ *    first.
+ */
+static int lxc_pivot_root(const char *rootfs)
 {
 	int ret;
+	int newroot = -1, oldroot = -1;
 
-	if (!rootfs->path) {
-		DEBUG("Container does not have a rootfs");
-		return 0;
+	oldroot = open("/", O_DIRECTORY | O_RDONLY);
+	if (oldroot < 0) {
+		SYSERROR("Failed to open old root directory");
+		return -errno;
 	}
 
-	if (detect_ramfs_rootfs()) {
-		DEBUG("Detected that container is on ramfs");
+	newroot = open(rootfs, O_DIRECTORY | O_RDONLY);
+	if (newroot < 0) {
+		SYSERROR("Failed to open new root directory");
+		ret = -errno;
+		goto on_error;
+	}
 
-		ret = prepare_ramfs_root(rootfs->mount);
-		if (ret < 0) {
-			ERROR("Failed to prepare minimal ramfs root");
-			return -1;
-		}
+	/* change into new root fs */
+	ret = fchdir(newroot);
+	if (ret < 0) {
+		SYSERROR("Failed to change to new rootfs \"%s\"", rootfs);
+		ret = -errno;
+		goto on_error;
+	}
 
-		DEBUG("Prepared ramfs root for container");
-		return 0;
+	/* pivot_root into our new root fs */
+	ret = pivot_root(".", ".");
+	if (ret < 0) {
+		SYSERROR("Failed to pivot_root()");
+		ret = -errno;
+		goto on_error;
 	}
 
-	ret = setup_rootfs_pivot_root(rootfs->mount);
+	/* At this point the old-root is mounted on top of our new-root. To
+	 * unmounted it we must not be chdir'd into it, so escape back to
+	 * old-root.
+	 */
+	ret = fchdir(oldroot);
 	if (ret < 0) {
-		ERROR("Failed to pivot_root()");
-		return -1;
+		SYSERROR("Failed to enter old root directory");
+		ret = -errno;
+		goto on_error;
 	}
 
-	DEBUG("Finished pivot_root()");
-	return 0;
+	/* Make oldroot rslave to make sure our umounts don't propagate to the
+	 * host.
+	 */
+	ret = mount("", ".", "", MS_SLAVE | MS_REC, NULL);
+	if (ret < 0) {
+		SYSERROR("Failed to make oldroot rslave");
+		ret = -errno;
+		goto on_error;
+	}
+
+	ret = umount2(".", MNT_DETACH);
+	if (ret < 0) {
+		SYSERROR("Failed to detach old root directory");
+		ret = -errno;
+		goto on_error;
+	}
+
+	ret = fchdir(newroot);
+	if (ret < 0) {
+		SYSERROR("Failed to re-enter new root directory");
+		ret = -errno;
+		goto on_error;
+	}
+
+	ret = 0;
+
+	TRACE("pivot_root(\"%s\") successful", rootfs);
+
+on_error:
+	if (oldroot != -1)
+		close(oldroot);
+	if (newroot != -1)
+		close(newroot);
+
+	return ret;
 }
 
-static const struct id_map *find_mapped_nsid_entry(struct lxc_conf *conf, unsigned id,
-					     enum idtype idtype)
+static int lxc_setup_rootfs_switch_root(const struct lxc_rootfs *rootfs)
+{
+	if (!rootfs->path) {
+		DEBUG("Container does not have a rootfs");
+		return 0;
+	}
+
+	if (detect_ramfs_rootfs())
+		return lxc_chroot(rootfs);
+
+	return lxc_pivot_root(rootfs->mount);
+}
+
+static const struct id_map *find_mapped_nsid_entry(struct lxc_conf *conf,
+						   unsigned id,
+						   enum idtype idtype)
 {
 	struct lxc_list *it;
 	struct id_map *map;
@@ -1903,7 +1917,7 @@ static void parse_propagationopt(char *opt, unsigned long *flags)
 	}
 }
 
-static int parse_propagationopts(const char *mntopts, unsigned long *pflags)
+int parse_propagationopts(const char *mntopts, unsigned long *pflags)
 {
 	char *p, *s;
 	char *saveptr = NULL;
@@ -3379,7 +3393,8 @@ static int lxc_execute_bind_init(struct lxc_handler *handler)
 /* This does the work of remounting / if it is shared, calling the container
  * pre-mount hooks, and mounting the rootfs.
  */
-int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath)
+int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name,
+				  const char *lxcpath)
 {
 	int ret;
 
@@ -3392,7 +3407,7 @@ int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath
 		ret = mount(path, path, "rootfs", MS_BIND, NULL);
 		if (ret < 0) {
 			ERROR("Failed to bind mount container / onto itself");
-			return -1;
+			return -errno;
 		}
 
 		TRACE("Bind mounted container / onto itself");
@@ -3407,7 +3422,7 @@ int do_rootfs_setup(struct lxc_conf *conf, const char *name, const char *lxcpath
 		return -1;
 	}
 
-	ret = lxc_setup_rootfs(conf);
+	ret = lxc_mount_rootfs(conf);
 	if (ret < 0) {
 		ERROR("Failed to setup rootfs for");
 		return -1;
@@ -3471,7 +3486,7 @@ int lxc_setup(struct lxc_handler *handler)
 	const char *lxcpath = handler->lxcpath, *name = handler->name;
 	struct lxc_conf *lxc_conf = handler->conf;
 
-	ret = do_rootfs_setup(lxc_conf, name, lxcpath);
+	ret = lxc_setup_rootfs_prepare_root(lxc_conf, name, lxcpath);
 	if (ret < 0) {
 		ERROR("Failed to setup rootfs");
 		return -1;
@@ -3610,7 +3625,7 @@ int lxc_setup(struct lxc_handler *handler)
 		return -1;
 	}
 
-	ret = setup_pivot_root(&lxc_conf->rootfs);
+	ret = lxc_setup_rootfs_switch_root(&lxc_conf->rootfs);
 	if (ret < 0) {
 		ERROR("Failed to pivot root into rootfs");
 		return -1;
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index f7a879c30..1c029feac 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -150,14 +150,18 @@ struct lxc_tty_info {
  * optionals pivot_root, rootfs mount paths
  * @path       : the rootfs source (directory or device)
  * @mount      : where it is mounted
- * @options    : mount options
  * @bev_type   : optional backing store type
+ * @options    : mount options
+ * @mountflags : the portion of @options that are flags
+ * @data       : the porition of @options that are not flags
  */
 struct lxc_rootfs {
 	char *path;
 	char *mount;
-	char *options;
 	char *bdev_type;
+	char *options;
+	unsigned long mountflags;
+	char *data;
 };
 
 /*
@@ -399,8 +403,8 @@ extern int lxc_clear_environment(struct lxc_conf *c);
 extern int lxc_clear_limits(struct lxc_conf *c, const char *key);
 extern int lxc_delete_autodev(struct lxc_handler *handler);
 extern void lxc_clear_includes(struct lxc_conf *conf);
-extern int do_rootfs_setup(struct lxc_conf *conf, const char *name,
-			   const char *lxcpath);
+extern int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf,
+					 const char *name, const char *lxcpath);
 extern int lxc_setup(struct lxc_handler *handler);
 extern int lxc_setup_parent(struct lxc_handler *handler);
 extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
@@ -414,6 +418,7 @@ extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *),
 			    void *data, const char *fn_name);
 extern int parse_mntopts(const char *mntopts, unsigned long *mntflags,
 			 char **mntdata);
+extern int parse_propagationopts(const char *mntopts, unsigned long *pflags);
 extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
 extern void remount_all_slave(void);
 extern void suggest_default_idmap(void);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 5a18d11bf..61a63f5dd 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -2073,7 +2073,27 @@ static int set_config_rootfs_mount(const char *key, const char *value,
 static int set_config_rootfs_options(const char *key, const char *value,
 				     struct lxc_conf *lxc_conf, void *data)
 {
-	return set_config_string_item(&lxc_conf->rootfs.options, value);
+	int ret;
+	unsigned long pflags = 0;
+	struct lxc_rootfs *rootfs = &lxc_conf->rootfs;
+
+	ret = set_config_string_item(&rootfs->options, value);
+	if (ret < 0)
+		return -EINVAL;
+
+	ret = parse_mntopts(value, &rootfs->mountflags, &rootfs->data);
+	if (ret < 0)
+		return -EINVAL;
+
+	ret = parse_propagationopts(value, &pflags);
+	if (ret < 0) {
+               free(rootfs->data);
+		return -EINVAL;
+       }
+
+	rootfs->mountflags |= pflags;
+
+	return 0;
 }
 
 static int set_config_uts_name(const char *key, const char *value,
@@ -3856,6 +3876,10 @@ static inline int clr_config_rootfs_options(const char *key, struct lxc_conf *c,
 {
 	free(c->rootfs.options);
 	c->rootfs.options = NULL;
+
+	free(c->rootfs.data);
+	c->rootfs.data = NULL;
+
 	return 0;
 }
 
diff --git a/src/lxc/criu.c b/src/lxc/criu.c
index c36421627..0feaeac07 100644
--- a/src/lxc/criu.c
+++ b/src/lxc/criu.c
@@ -1015,7 +1015,8 @@ static void do_restore(struct lxc_container *c, int status_pipe, struct migrate_
 		rootfs = &c->lxc_conf->rootfs;
 
 		if (rootfs_is_blockdev(c->lxc_conf)) {
-			if (do_rootfs_setup(c->lxc_conf, c->name, c->config_path) < 0)
+			if (lxc_setup_rootfs_prepare_root(c->lxc_conf, c->name,
+							  c->config_path) < 0)
 				goto out_fini_handler;
 		} else {
 			if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST)
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 180a37ab4..65a5e1eba 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -1882,7 +1882,7 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
 			INFO("Unshared CLONE_NEWNS");
 
 			remount_all_slave();
-			ret = do_rootfs_setup(conf, name, lxcpath);
+			ret = lxc_setup_rootfs_prepare_root(conf, name, lxcpath);
 			if (ret < 0) {
 				ERROR("Error setting up rootfs mount as root before spawn");
 				goto out_fini_nonet;
diff --git a/src/lxc/storage/dir.c b/src/lxc/storage/dir.c
index bfd3827de..2414f533e 100644
--- a/src/lxc/storage/dir.c
+++ b/src/lxc/storage/dir.c
@@ -157,7 +157,7 @@ bool dir_detect(const char *path)
 int dir_mount(struct lxc_storage *bdev)
 {
 	int ret;
-	unsigned long mflags, mntflags;
+	unsigned long mflags = 0, mntflags = 0, pflags = 0;
 	char *mntdata;
 	const char *src;
 
@@ -171,17 +171,23 @@ int dir_mount(struct lxc_storage *bdev)
 	if (ret < 0) {
 		ERROR("Failed to parse mount options \"%s\"", bdev->mntopts);
 		free(mntdata);
-		return -22;
+		return -EINVAL;
+	}
+
+	ret = parse_propagationopts(bdev->mntopts, &pflags);
+	if (ret < 0) {
+		ERROR("Failed to parse propagation options \"%s\"", bdev->mntopts);
+		free(mntdata);
+		return -EINVAL;
 	}
 
 	src = lxc_storage_get_path(bdev->src, bdev->type);
 
-	ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags,
-		    mntdata);
+	ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags | pflags, mntdata);
 	if ((0 == ret) && (mntflags & MS_RDONLY)) {
 		DEBUG("Remounting \"%s\" on \"%s\" readonly",
 		      src ? src : "(none)", bdev->dest ? bdev->dest : "(none)");
-		mflags = add_required_remount_flags(src, bdev->dest, MS_BIND | MS_REC | mntflags | MS_REMOUNT);
+		mflags = add_required_remount_flags(src, bdev->dest, MS_BIND | MS_REC | mntflags | pflags | MS_REMOUNT);
 		ret = mount(src, bdev->dest, "bind", mflags, mntdata);
 	}
 
diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index 6bb05df00..af18791a0 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -1324,6 +1324,7 @@ bool detect_ramfs_rootfs(void)
 			if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) {
 				free(line);
 				fclose(f);
+				INFO("Rootfs is located on ramfs");
 				return true;
 			}
 		}


More information about the lxc-devel mailing list