From 8ce1abc28aeb33928bd3fbd2ef65f097715adf49 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 29 Jul 2018 14:31:31 +0200 Subject: [PATCH] conf: improve rootfs setup Signed-off-by: Christian Brauner --- src/lxc/conf.c | 249 ++++++++++++++++++++++-------------------- src/lxc/conf.h | 5 +- src/lxc/criu.c | 3 +- src/lxc/start.c | 2 +- src/lxc/storage/dir.c | 16 ++- src/lxc/utils.c | 1 + 6 files changed, 149 insertions(+), 127 deletions(-) diff --git a/src/lxc/conf.c b/src/lxc/conf.c index ff9bd091b..7fa5a1504 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -1140,84 +1140,6 @@ on_error: 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. */ @@ -1401,17 +1323,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; } @@ -1449,15 +1370,18 @@ 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)) + if (!realpath(root, nroot)) { + SYSERROR("Failed to resolve \"%s\"", root); return -1; + } ret = chdir("/"); if (ret < 0) @@ -1466,15 +1390,15 @@ int prepare_ramfs_root(char *root) /* 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); + SYSERROR("Failed to mount \"%s\" onto \"/\" as MS_REC | MS_BIND", nroot); return -1; } ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL); if (ret < 0) { - SYSERROR("Failed to make \"/\" rprivate"); + SYSERROR("Failed to remount \"/\""); return -1; } @@ -1493,8 +1417,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)) { @@ -1534,53 +1458,141 @@ 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"); + SYSERROR("Failed to chdir(\"..\")"); return -1; } ret = chroot("."); if (ret < 0) { - SYSERROR("Unable to chroot"); + SYSERROR("Failed to chroot(\".\")"); return -1; } 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, ret = -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 -1; } - 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"); + 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) { + ret = -1; + SYSERROR("Failed to change to new rootfs \"%s\"", rootfs); + goto on_error; + } - DEBUG("Prepared ramfs root for container"); - return 0; + /* pivot_root into our new root fs */ + ret = pivot_root(".", "."); + if (ret < 0) { + ret = -1; + SYSERROR("Failed to pivot_root()"); + 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; + ret = -1; + SYSERROR("Failed to enter old root directory"); + 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) { + ret = -1; + SYSERROR("Failed to make oldroot rslave"); + goto on_error; + } + + ret = umount2(".", MNT_DETACH); + if (ret < 0) { + ret = -1; + SYSERROR("Failed to detach old root directory"); + goto on_error; + } + + ret = fchdir(newroot); + if (ret < 0) { + ret = -1; + SYSERROR("Failed to re-enter new root directory"); + 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; @@ -1938,7 +1950,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; @@ -3440,7 +3452,8 @@ out: /* 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; @@ -3453,7 +3466,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"); @@ -3468,7 +3481,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; @@ -3532,7 +3545,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; @@ -3671,7 +3684,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 6b299cab9..1eda2fe3d 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -413,8 +413,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); @@ -428,6 +428,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/criu.c b/src/lxc/criu.c index 64ea4f024..f28397f5d 100644 --- a/src/lxc/criu.c +++ b/src/lxc/criu.c @@ -1016,7 +1016,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 061e47917..92d3c64c6 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1995,7 +1995,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 acda4d8dc..be833b92e 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1330,6 +1330,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; } } -- 2.47.2