]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
Specially handle block device rootfs
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Thu, 22 May 2014 20:49:15 +0000 (15:49 -0500)
committerStéphane Graber <stgraber@ubuntu.com>
Sun, 25 May 2014 14:43:50 +0000 (10:43 -0400)
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 <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>
src/lxc/bdev.c
src/lxc/bdev.h
src/lxc/conf.c
src/lxc/conf.h
src/lxc/start.c

index 751fa9fc6424d9af314173df34ef878a039dbcb9..20c4b55d5cb60c463dc20d2315369c102c8aed28 100644 (file)
@@ -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; i<numbdevs; i++) {
                int r;
                r = bdevs[i].ops->detect(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;
+}
index 9d03b102b3a44b9c22d333cc77dc72d1a139284d..651f7f316ab7d52a8b6fd23fcd343a7af8e1836d 100644 (file)
@@ -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
index 30be0c28d55d4fc0c97272f61e529c33febc041b..2b298a4a6a5d3872bffeebee143dc678ea855cce 100644 (file)
@@ -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);
        }
index 8247124e5d9cc9a86fa48e628a5722d5cdaed9d8..f5fab3d8420694ef4970269f156ed5559cfbc1e6 100644 (file)
@@ -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
  */
index a7fb1d38601876deaf2250bf85a264dcc0cbdef9..555e4c47e0b4102e0a2bb516bed1211fb163f07a 100644 (file)
@@ -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);