]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
conf: improve rootfs setup
authorChristian Brauner <christian.brauner@ubuntu.com>
Sun, 29 Jul 2018 12:31:31 +0000 (14:31 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sun, 29 Jul 2018 12:32:15 +0000 (14:32 +0200)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/conf.c
src/lxc/conf.h
src/lxc/criu.c
src/lxc/start.c
src/lxc/storage/dir.c
src/lxc/utils.c

index ff9bd091b21e16397a32fa7b977f76ae52cefeb8..7fa5a150476507992d4cf2661b8e62ffa32b7e41 100644 (file)
@@ -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,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)) {
@@ -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;
@@ -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;
 
@@ -3440,7 +3452,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;
 
@@ -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");
@@ -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;
@@ -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;
@@ -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;
index 6b299cab97c291110b1a221e68ff6b5db78f0fa1..1eda2fe3d7cba0193c2ff0acf10992018e37bb01 100644 (file)
@@ -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);
@@ -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);
index 64ea4f0248d62e9dcf3c5385c134cd685999ce40..f28397f5dca76964e84f53347016994323e4ba32 100644 (file)
@@ -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)
index 061e479175bc5c2e2046dcb15da46689f435e8f7..92d3c64c61962cff58e20c47b6ff5ee54009abc1 100644 (file)
@@ -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;
index bfd3827deecf05f6afda6c7995f11fb7aee7c55f..2414f533e23a4742b17492ce084580597d0e88f4 100644 (file)
@@ -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);
        }
 
index acda4d8dc79bdb5413718686780a2a3bc7d081ce..be833b92eeabcfa318d66837d73047c4c97a61a0 100644 (file)
@@ -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;
                        }
                }