Commit 830105a0 by Serge Hallyn Committed by Stéphane Graber

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/221Signed-off-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com> Acked-by: 's avatarStéphane Graber <stgraber@ubuntu.com>
parent 8b1ef754
...@@ -2454,11 +2454,9 @@ struct bdev *bdev_get(const char *type) ...@@ -2454,11 +2454,9 @@ struct bdev *bdev_get(const char *type)
return bdev; return bdev;
} }
struct bdev *bdev_init(const char *src, const char *dst, const char *mntopts) static const struct bdev_type *bdev_query(const char *src)
{ {
int i; int i;
struct bdev *bdev;
for (i=0; i<numbdevs; i++) { for (i=0; i<numbdevs; i++) {
int r; int r;
r = bdevs[i].ops->detect(src); r = bdevs[i].ops->detect(src);
...@@ -2468,12 +2466,24 @@ struct bdev *bdev_init(const char *src, const char *dst, const char *mntopts) ...@@ -2468,12 +2466,24 @@ struct bdev *bdev_init(const char *src, const char *dst, const char *mntopts)
if (i == numbdevs) if (i == numbdevs)
return NULL; return NULL;
return &bdevs[i];
}
struct bdev *bdev_init(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)); bdev = malloc(sizeof(struct bdev));
if (!bdev) if (!bdev)
return NULL; return NULL;
memset(bdev, 0, sizeof(struct bdev)); memset(bdev, 0, sizeof(struct bdev));
bdev->ops = bdevs[i].ops; bdev->ops = q->ops;
bdev->type = bdevs[i].name; bdev->type = q->name;
if (mntopts) if (mntopts)
bdev->mntopts = strdup(mntopts); bdev->mntopts = strdup(mntopts);
if (src) if (src)
...@@ -2794,3 +2804,22 @@ char *overlay_getlower(char *p) ...@@ -2794,3 +2804,22 @@ char *overlay_getlower(char *p)
*p1 = '\0'; *p1 = '\0';
return p; 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;
}
...@@ -93,6 +93,8 @@ struct bdev *bdev_create(const char *dest, const char *type, ...@@ -93,6 +93,8 @@ struct bdev *bdev_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs); const char *cname, struct bdev_specs *specs);
void bdev_put(struct bdev *bdev); void bdev_put(struct bdev *bdev);
bool rootfs_is_blockdev(struct lxc_conf *conf);
/* define constants if the kernel/glibc headers don't define them */ /* define constants if the kernel/glibc headers don't define them */
#ifndef MS_DIRSYNC #ifndef MS_DIRSYNC
#define MS_DIRSYNC 128 #define MS_DIRSYNC 128
......
...@@ -3810,15 +3810,26 @@ static void remount_all_slave(void) ...@@ -3810,15 +3810,26 @@ static void remount_all_slave(void)
free(line); 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; if (conf->rootfs_setup) {
struct lxc_conf *lxc_conf = handler->conf; /*
const char *lxcpath = handler->lxcpath; * rootfs was set up in another namespace. bind-mount it
void *data = handler->data; * 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 (detect_ramfs_rootfs()) {
if (chroot_into_slave(lxc_conf)) { if (chroot_into_slave(conf)) {
ERROR("Failed to chroot into slave /"); ERROR("Failed to chroot into slave /");
return -1; return -1;
} }
...@@ -3826,6 +3837,32 @@ int lxc_setup(struct lxc_handler *handler) ...@@ -3826,6 +3837,32 @@ int lxc_setup(struct lxc_handler *handler)
remount_all_slave(); 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 (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) {
if (setup_utsname(lxc_conf->utsname)) { if (setup_utsname(lxc_conf->utsname)) {
ERROR("failed to setup the utsname for '%s'", name); ERROR("failed to setup the utsname for '%s'", name);
...@@ -3838,16 +3875,6 @@ int lxc_setup(struct lxc_handler *handler) ...@@ -3838,16 +3875,6 @@ int lxc_setup(struct lxc_handler *handler)
return -1; 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) { if (lxc_conf->autodev < 0) {
lxc_conf->autodev = check_autodev(lxc_conf->rootfs.mount, data); lxc_conf->autodev = check_autodev(lxc_conf->rootfs.mount, data);
} }
......
...@@ -334,6 +334,10 @@ struct lxc_conf { ...@@ -334,6 +334,10 @@ struct lxc_conf {
int start_delay; int start_delay;
int start_order; int start_order;
struct lxc_list groups; 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, int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf,
...@@ -370,6 +374,9 @@ extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); ...@@ -370,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_idmaps(struct lxc_conf *c);
extern int lxc_clear_groups(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 * Configure the container from inside
*/ */
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
#include "namespace.h" #include "namespace.h"
#include "lxcseccomp.h" #include "lxcseccomp.h"
#include "caps.h" #include "caps.h"
#include "bdev.h"
#include "lsm/lsm.h" #include "lsm/lsm.h"
lxc_log_define(lxc_start, lxc); lxc_log_define(lxc_start, lxc);
...@@ -1054,6 +1055,21 @@ int __lxc_start(const char *name, struct lxc_conf *conf, ...@@ -1054,6 +1055,21 @@ int __lxc_start(const char *name, struct lxc_conf *conf,
handler->conf->need_utmp_watch = 0; handler->conf->need_utmp_watch = 0;
} }
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); err = lxc_spawn(handler);
if (err) { if (err) {
ERROR("failed to spawn '%s'", name); ERROR("failed to spawn '%s'", name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment