[lxc-devel] [PATCH RFC] pivot_root: switch to a new mechanism
Serge Hallyn
serge.hallyn at ubuntu.com
Fri Sep 19 17:07:17 UTC 2014
This idea came from Andy Lutomirski. Instead of using a temporary
directory for the pivot_root put-old, use "." both for new-root and
old-root. Then fchdir into the old root temporarily in order to unmount
the old-root, and finally chdir back into our '/'.
This patch doesn't yet do it, but we can drop the lxc.pivotdir with this
approach altogether.
Only lightly tested. This might have subtle unforeseen side-effects
hence this is only RFC.
Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
src/lxc/conf.c | 211 +++++++++++----------------------------------------------
1 file changed, 39 insertions(+), 172 deletions(-)
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 5e61c35..e136185 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1025,199 +1025,66 @@ static int setup_tty(const struct lxc_rootfs *rootfs,
return 0;
}
-static int setup_rootfs_pivot_root_cb(char *buffer, void *data)
-{
- struct lxc_list *mountlist, *listentry, *iterator;
- char *pivotdir, *mountpoint, *mountentry, *saveptr = NULL;
- int found;
- void **cbparm;
-
- mountentry = buffer;
- cbparm = (void **)data;
-
- mountlist = cbparm[0];
- pivotdir = cbparm[1];
-
- /* parse entry, first field is mountname, ignore */
- mountpoint = strtok_r(mountentry, " ", &saveptr);
- if (!mountpoint)
- return -1;
-
- /* second field is mountpoint */
- mountpoint = strtok_r(NULL, " ", &saveptr);
- if (!mountpoint)
- return -1;
-
- /* only consider mountpoints below old root fs */
- if (strncmp(mountpoint, pivotdir, strlen(pivotdir)))
- return 0;
-
- /* filter duplicate mountpoints */
- found = 0;
- lxc_list_for_each(iterator, mountlist) {
- if (!strcmp(iterator->elem, mountpoint)) {
- found = 1;
- break;
- }
- }
- if (found)
- return 0;
-
- /* add entry to list */
- listentry = malloc(sizeof(*listentry));
- if (!listentry) {
- SYSERROR("malloc for mountpoint listentry failed");
- return -1;
- }
- listentry->elem = strdup(mountpoint);
- if (!listentry->elem) {
- SYSERROR("strdup failed");
- free(listentry);
- return -1;
- }
- lxc_list_add_tail(mountlist, listentry);
-
- return 0;
-}
-
-static int umount_oldrootfs(const char *oldrootfs)
+static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir)
{
- char path[MAXPATHLEN];
- void *cbparm[2];
- struct lxc_list mountlist, *iterator, *next;
- int ok, still_mounted, last_still_mounted;
- int rc;
+ int oldroot = -1, newroot = -1;
- /* read and parse /proc/mounts in old root fs */
- lxc_list_init(&mountlist);
-
- /* oldrootfs is on the top tree directory now */
- rc = snprintf(path, sizeof(path), "/%s", oldrootfs);
- if (rc >= sizeof(path)) {
- ERROR("rootfs name too long");
+ oldroot = open("/", O_DIRECTORY | O_RDONLY);
+ if (oldroot < 0) {
+ SYSERROR("Error opening old-/ for fchdir");
return -1;
}
- cbparm[0] = &mountlist;
-
- cbparm[1] = strdup(path);
- if (!cbparm[1]) {
- SYSERROR("strdup failed");
- return -1;
+ newroot = open(rootfs, O_DIRECTORY | O_RDONLY);
+ if (newroot < 0) {
+ SYSERROR("Error opening new-/ for fchdir");
+ goto fail;
}
- rc = snprintf(path, sizeof(path), "%s/proc/mounts", oldrootfs);
- if (rc >= sizeof(path)) {
- ERROR("container proc/mounts name too long");
- return -1;
- }
-
- ok = lxc_file_for_each_line(path,
- setup_rootfs_pivot_root_cb, &cbparm);
- if (ok < 0) {
- SYSERROR("failed to read or parse mount list '%s'", path);
- return -1;
- }
-
- /* umount filesystems until none left or list no longer shrinks */
- still_mounted = 0;
- do {
- last_still_mounted = still_mounted;
- still_mounted = 0;
-
- lxc_list_for_each_safe(iterator, &mountlist, next) {
-
- /* umount normally */
- if (!umount(iterator->elem)) {
- DEBUG("umounted '%s'", (char *)iterator->elem);
- lxc_list_del(iterator);
- continue;
- }
-
- still_mounted++;
- }
-
- } while (still_mounted > 0 && still_mounted != last_still_mounted);
-
-
- lxc_list_for_each(iterator, &mountlist) {
-
- /* let's try a lazy umount */
- if (!umount2(iterator->elem, MNT_DETACH)) {
- INFO("lazy unmount of '%s'", (char *)iterator->elem);
- continue;
- }
-
- /* be more brutal (nfs) */
- if (!umount2(iterator->elem, MNT_FORCE)) {
- INFO("forced unmount of '%s'", (char *)iterator->elem);
- continue;
- }
-
- WARN("failed to unmount '%s'", (char *)iterator->elem);
- }
-
- return 0;
-}
-
-static int setup_rootfs_pivot_root(const char *rootfs, const char *pivotdir)
-{
- char path[MAXPATHLEN];
- int remove_pivotdir = 0;
- int rc;
-
/* change into new root fs */
- if (chdir(rootfs)) {
+ if (fchdir(newroot)) {
SYSERROR("can't chdir to new rootfs '%s'", rootfs);
- return -1;
- }
-
- if (!pivotdir)
- pivotdir = "lxc_putold";
-
- /* compute the full path to pivotdir under rootfs */
- rc = snprintf(path, sizeof(path), "%s/%s", rootfs, pivotdir);
- if (rc >= sizeof(path)) {
- ERROR("pivot dir name too long");
- return -1;
+ goto fail;
}
- if (access(path, F_OK)) {
-
- if (mkdir_p(path, 0755) < 0) {
- SYSERROR("failed to create pivotdir '%s'", path);
- return -1;
- }
-
- remove_pivotdir = 1;
- DEBUG("created '%s' directory", path);
- }
-
- DEBUG("mountpoint for old rootfs is '%s'", path);
-
/* pivot_root into our new root fs */
- if (pivot_root(".", path)) {
+ if (pivot_root(".", ".")) {
SYSERROR("pivot_root syscall failed");
- return -1;
+ goto fail;
}
- if (chdir("/")) {
- SYSERROR("can't chdir to / after pivot_root");
- return -1;
+ /*
+ * 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
+ */
+ if (fchdir(oldroot) < 0) {
+ SYSERROR("Error entering oldroot");
+ goto fail;
+ }
+ if (umount2("/", MNT_DETACH) < 0) {
+ SYSERROR("Error detaching old root");
+ goto fail;
}
- DEBUG("pivot_root syscall to '%s' successful", rootfs);
+ if (fchdir(newroot) < 0) {
+ SYSERROR("Error re-entering newroot");
+ goto fail;
+ }
- /* we switch from absolute path to relative path */
- if (umount_oldrootfs(pivotdir))
- return -1;
+ close(oldroot);
+ close(newroot);
- /* remove temporary mount point, we don't consider the removing
- * as fatal */
- if (remove_pivotdir && rmdir(pivotdir))
- WARN("can't remove mountpoint '%s': %m", pivotdir);
+ DEBUG("pivot_root syscall to '%s' successful", rootfs);
return 0;
+
+fail:
+ if (oldroot != -1)
+ close(oldroot);
+ if (newroot != -1)
+ close(newroot);
+ return -1;
}
/*
--
2.1.0
More information about the lxc-devel
mailing list