Skip to content

Commit

Permalink
conf: improve rootfs setup
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
  • Loading branch information
Christian Brauner committed Jul 29, 2018
1 parent a4f181a commit 8ce1abc
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 127 deletions.
249 changes: 131 additions & 118 deletions src/lxc/conf.c
Expand Up @@ -1140,84 +1140,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.
*/
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand All @@ -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;
}

Expand All @@ -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)) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -3440,7 +3452,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;

Expand All @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/lxc/conf.h
Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 8ce1abc

Please sign in to comment.