Unverified Commit 01fc55d5 by Stéphane Graber Committed by GitHub

Merge pull request #2475 from brauner/2018-07-16/monitor_signal_pdeath

conf: improve rootfs setup
parents e6b4213b dccffc82
......@@ -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,7 +1417,7 @@ int prepare_ramfs_root(char *root)
f = fopen("./proc/self/mountinfo", "r");
if (!f) {
SYSERROR("Unable to open /proc/self/mountinfo");
SYSERROR("Failed to open \"/proc/self/mountinfo\"");
return -1;
}
......@@ -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 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)
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;
......@@ -1949,7 +1961,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;
......@@ -3451,7 +3463,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;
......@@ -3479,7 +3492,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;
......@@ -3543,7 +3556,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;
......@@ -3682,7 +3695,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;
......
......@@ -150,14 +150,18 @@ struct lxc_tty_info {
* optionals pivot_root, rootfs mount paths
* @path : the rootfs source (directory or device)
* @mount : where it is mounted
* @options : mount options
* @bev_type : optional backing store type
* @options : mount options
* @mountflags : the portion of @options that are flags
* @data : the porition of @options that are not flags
*/
struct lxc_rootfs {
char *path;
char *mount;
char *options;
char *bdev_type;
char *options;
unsigned long mountflags;
char *data;
};
/*
......@@ -413,8 +417,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 +432,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);
......
......@@ -2143,7 +2143,32 @@ static int set_config_rootfs_mount(const char *key, const char *value,
static int set_config_rootfs_options(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
return set_config_string_item(&lxc_conf->rootfs.options, value);
int ret;
unsigned long mflags = 0, pflags = 0;
char *mdata = NULL, *opts = NULL;
struct lxc_rootfs *rootfs = &lxc_conf->rootfs;
ret = parse_mntopts(value, &mflags, &mdata);
if (ret < 0)
return -EINVAL;
ret = parse_propagationopts(value, &pflags);
if (ret < 0) {
free(mdata);
return -EINVAL;
}
ret = set_config_string_item(&opts, value);
if (ret < 0) {
free(mdata);
return -ENOMEM;
}
rootfs->mountflags = mflags | pflags;
rootfs->options = opts;
rootfs->data = mdata;
return 0;
}
static int set_config_uts_name(const char *key, const char *value,
......@@ -3964,6 +3989,10 @@ static inline int clr_config_rootfs_options(const char *key, struct lxc_conf *c,
{
free(c->rootfs.options);
c->rootfs.options = NULL;
free(c->rootfs.data);
c->rootfs.data = NULL;
return 0;
}
......
......@@ -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)
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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;
}
}
......
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