From: Serge Hallyn Date: Thu, 22 May 2014 20:49:15 +0000 (-0500) Subject: Specially handle block device rootfs X-Git-Tag: lxc-1.1.0.alpha1~79 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35120d9c591c9e2b9e594c3271d93ada37dd00da;p=thirdparty%2Flxc.git Specially handle block device rootfs It is not possible to mount a block device from a non-init user namespace. Therefore if root on the host is starting a container with a uid mapping, and the rootfs is a block device, then mount the rootfs before we spawn the container init task. This addresses https://github.com/lxc/lxc/issues/221 Signed-off-by: Serge Hallyn Acked-by: Stéphane Graber --- diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 751fa9fc6..20c4b55d5 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -2745,11 +2745,9 @@ struct bdev *bdev_get(const char *type) return bdev; } -struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, const char *mntopts) +static const struct bdev_type *bdev_query(const char *src) { int i; - struct bdev *bdev; - for (i=0; idetect(src); @@ -2759,12 +2757,24 @@ struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, if (i == numbdevs) return NULL; + return &bdevs[i]; +} + +struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst, const char *mntopts) +{ + struct bdev *bdev; + const struct bdev_type *q; + + q = bdev_query(src); + if (!q) + return NULL; + bdev = malloc(sizeof(struct bdev)); if (!bdev) return NULL; memset(bdev, 0, sizeof(struct bdev)); - bdev->ops = bdevs[i].ops; - bdev->type = bdevs[i].name; + bdev->ops = q->ops; + bdev->type = q->name; if (mntopts) bdev->mntopts = strdup(mntopts); if (src) @@ -3087,3 +3097,22 @@ char *overlay_getlower(char *p) *p1 = '\0'; return p; } + +bool rootfs_is_blockdev(struct lxc_conf *conf) +{ + const struct bdev_type *q; + struct stat st; + int ret; + + ret = stat(conf->rootfs.path, &st); + if (ret == 0 && S_ISBLK(st.st_mode)) + return true; + q = bdev_query(conf->rootfs.path); + if (!q) + return false; + if (strcmp(q->name, "lvm") == 0 || + strcmp(q->name, "loop") == 0 || + strcmp(q->name, "nbd") == 0) + return true; + return false; +} diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index 9d03b102b..651f7f316 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -102,6 +102,8 @@ void bdev_put(struct bdev *bdev); bool attach_block_device(struct lxc_conf *conf); void detach_block_device(struct lxc_conf *conf); +bool rootfs_is_blockdev(struct lxc_conf *conf); + /* define constants if the kernel/glibc headers don't define them */ #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 30be0c28d..2b298a4a6 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -3811,15 +3811,26 @@ static void remount_all_slave(void) free(line); } -int lxc_setup(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) { - const char *name = handler->name; - struct lxc_conf *lxc_conf = handler->conf; - const char *lxcpath = handler->lxcpath; - void *data = handler->data; + if (conf->rootfs_setup) { + /* + * rootfs was set up in another namespace. bind-mount it + * to give us a mount in our own ns so we can pivot_root to it + */ + const char *path = conf->rootfs.mount; + if (mount(path, path, "rootfs", MS_BIND, NULL) < 0) { + ERROR("Failed to bind-mount container / onto itself"); + return false; + } + } if (detect_ramfs_rootfs()) { - if (chroot_into_slave(lxc_conf)) { + if (chroot_into_slave(conf)) { ERROR("Failed to chroot into slave /"); return -1; } @@ -3827,6 +3838,32 @@ int lxc_setup(struct lxc_handler *handler) remount_all_slave(); + if (run_lxc_hooks(name, "pre-mount", conf, lxcpath, NULL)) { + ERROR("failed to run pre-mount hooks for container '%s'.", name); + return -1; + } + + if (setup_rootfs(conf)) { + ERROR("failed to setup rootfs for '%s'", name); + return -1; + } + + conf->rootfs_setup = true; + return 0; +} + +int lxc_setup(struct lxc_handler *handler) +{ + const char *name = handler->name; + struct lxc_conf *lxc_conf = handler->conf; + const char *lxcpath = handler->lxcpath; + void *data = handler->data; + + if (do_rootfs_setup(lxc_conf, name, lxcpath) < 0) { + ERROR("Error setting up rootfs mount after spawn"); + return -1; + } + if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) { if (setup_utsname(lxc_conf->utsname)) { ERROR("failed to setup the utsname for '%s'", name); @@ -3839,16 +3876,6 @@ int lxc_setup(struct lxc_handler *handler) return -1; } - if (run_lxc_hooks(name, "pre-mount", lxc_conf, lxcpath, NULL)) { - ERROR("failed to run pre-mount hooks for container '%s'.", name); - return -1; - } - - if (setup_rootfs(lxc_conf)) { - ERROR("failed to setup rootfs for '%s'", name); - return -1; - } - if (lxc_conf->autodev < 0) { lxc_conf->autodev = check_autodev(lxc_conf->rootfs.mount, data); } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 8247124e5..f5fab3d84 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -335,6 +335,9 @@ struct lxc_conf { int start_order; struct lxc_list groups; int nbd_idx; + + /* set to true when rootfs has been setup */ + bool rootfs_setup; }; int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, @@ -371,6 +374,9 @@ extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); extern int lxc_clear_idmaps(struct lxc_conf *c); extern int lxc_clear_groups(struct lxc_conf *c); +extern int do_rootfs_setup(struct lxc_conf *conf, const char *name, + const char *lxcpath); + /* * Configure the container from inside */ diff --git a/src/lxc/start.c b/src/lxc/start.c index a7fb1d386..555e4c47e 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1060,6 +1060,21 @@ int __lxc_start(const char *name, struct lxc_conf *conf, goto out_fini_nonet; } + if (geteuid() == 0 && !lxc_list_empty(&conf->id_map)) { + /* if the backing store is a device, mount it here and now */ + if (rootfs_is_blockdev(conf)) { + if (unshare(CLONE_NEWNS) < 0) { + ERROR("Error unsharing mounts"); + goto out_fini_nonet; + } + if (do_rootfs_setup(conf, name, lxcpath) < 0) { + ERROR("Error setting up rootfs mount as root before spawn"); + goto out_fini_nonet; + } + INFO("Set up container rootfs as host root"); + } + } + err = lxc_spawn(handler); if (err) { ERROR("failed to spawn '%s'", name);