[ostree] switchroot: Use MS_MOVE to / rather than chroot



commit c9f56564a374be6051e62ec97da3b340da21ff54
Author: Colin Walters <walters verbum org>
Date:   Mon Feb 11 20:46:53 2013 -0500

    switchroot: Use MS_MOVE to / rather than chroot
    
    This fixes a number of bugs; the two biggest are:
    
    1) On shutdown systemd can correctly umount /
    2) We can use linux-user-chroot inside the system, as is
       necessary to do upgrades
    
    See http://lists.freedesktop.org/archives/systemd-devel/2012-September/006703.html

 src/switchroot/ostree-switch-root.c |  111 ++++++++++++++++++-----------------
 1 files changed, 56 insertions(+), 55 deletions(-)
---
diff --git a/src/switchroot/ostree-switch-root.c b/src/switchroot/ostree-switch-root.c
index 86c6855..6e74373 100644
--- a/src/switchroot/ostree-switch-root.c
+++ b/src/switchroot/ostree-switch-root.c
@@ -153,8 +153,7 @@ main(int argc, char *argv[])
   const char *initramfs_move_mounts[] = { "/dev", "/proc", "/sys", "/run", NULL };
   const char *toproot_bind_mounts[] = { "/home", "/root", "/tmp", NULL };
   const char *ostree_bind_mounts[] = { "/var", NULL };
-  const char *readonly_bind_mounts[] = { "/bin", "/lib", "/sbin", "/usr",
-					 NULL };
+  const char *readonly_bind_mounts[] = { "/usr", NULL };
   const char *root_mountpoint = NULL;
   const char *ostree_target = NULL;
   const char *ostree_subinit = NULL;
@@ -194,16 +193,6 @@ main(int argc, char *argv[])
     }
   ostree_osname = strndup (ostree_target, p - ostree_target);
 
-  /* For now, we just remount the root filesystem read/write.  This is
-   * kind of ugly, but to do this properly we'd basically have to have
-   * to be fully integrated into the init process.
-   */
-  if (mount (NULL, root_mountpoint, NULL, MS_MGC_VAL|MS_REMOUNT, NULL) < 0)
-    {
-      perrorv ("Failed to remount %s read/write", root_mountpoint);
-      exit (1);
-    }
-
   snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s",
 	    root_mountpoint, ostree_target);
   if (stat (destpath, &stbuf) < 0)
@@ -212,53 +201,32 @@ main(int argc, char *argv[])
       exit (1);
     }
 
-  for (i = 0; initramfs_move_mounts[i] != NULL; i++)
-    {
-      const char *path = initramfs_move_mounts[i];
-      snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s%s", root_mountpoint, ostree_target, path);
-      if (mount (path, destpath, NULL, MS_MOVE, NULL) < 0)
-	{
-	  perrorv ("failed to move mount of %s to %s", path, destpath);
-	  exit (1);
-	}
-    }
-
-  if (chdir (root_mountpoint) < 0)
+  /* Work-around for a kernel bug: for some reason the kernel
+   * refuses switching root if any file systems are mounted
+   * MS_SHARED. Hence remount them MS_PRIVATE here as a
+   * work-around.
+   *
+   * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
+  if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
     {
-      perrorv ("failed to chdir to %s", root_mountpoint);
+      perrorv ("mount(/, MS_PRIVATE): ");
       exit (1);
     }
 
   initramfs_fd = open ("/", O_RDONLY);
 
-  if (mount (root_mountpoint, "/", NULL, MS_MOVE, NULL) < 0)
-    {
-      perrorv ("failed move %s to /", root_mountpoint);
-      return -1;
-    }
-
-  if (chroot (".") < 0)
-    {
-      perrorv ("failed to chroot to .");
-      exit (1);
-    }
-  
-  if (initramfs_fd >= 0)
+  for (i = 0; initramfs_move_mounts[i] != NULL; i++)
     {
-      cleanup_pid = fork ();
-      if (cleanup_pid == 0)
+      const char *path = initramfs_move_mounts[i];
+      snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s%s", root_mountpoint, ostree_target, path);
+      if (mount (path, destpath, NULL, MS_MOVE, NULL) < 0)
 	{
-	  recursive_remove (initramfs_fd);
-	  exit (0);
+	  perrorv ("failed to move mount of %s to %s", path, destpath);
+	  exit (1);
 	}
-      close (initramfs_fd);
     }
 
-  /* From this point on we're chrooted into the real root filesystem,
-   * so we no longer refer to root_mountpoint.
-   */
-
-  snprintf (destpath, sizeof(destpath), "/ostree/deploy/%s", ostree_target);
+  snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s", root_mountpoint, ostree_target);
   fprintf (stderr, "Examining %s\n", destpath);
   if (lstat (destpath, &stbuf) < 0)
     {
@@ -279,12 +247,20 @@ main(int argc, char *argv[])
   if (ostree_target_path[len-1] == '/')
     ostree_target_path[len-1] = '\0';
   fprintf (stderr, "Resolved OSTree target to: %s\n", ostree_target_path);
-  asprintf (&deploy_path, "/ostree/deploy/%s/%s", ostree_osname, ostree_target_path);
+  asprintf (&deploy_path, "%s/ostree/deploy/%s/%s", root_mountpoint,
+	    ostree_osname, ostree_target_path);
+  
+  /* Make deploy_path a bind mount, so we can move it later */
+  if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0)
+    {
+      perrorv ("failed to initial bind mount %s", deploy_path);
+      exit (1);
+    }
 
   snprintf (destpath, sizeof(destpath), "%s/sysroot", deploy_path);
-  if (mount ("/", destpath, NULL, MS_BIND, NULL) < 0)
+  if (mount (root_mountpoint, destpath, NULL, MS_BIND, NULL) < 0)
     {
-      perrorv ("Failed to bind mount / to '%s'", destpath);
+      perrorv ("Failed to bind mount %s to '%s'", root_mountpoint, destpath);
       exit (1);
     }
 
@@ -298,8 +274,9 @@ main(int argc, char *argv[])
 
   for (i = 0; toproot_bind_mounts[i] != NULL; i++)
     {
+      snprintf (srcpath, sizeof(srcpath), "%s%s", root_mountpoint, toproot_bind_mounts[i]);
       snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, toproot_bind_mounts[i]);
-      if (mount (toproot_bind_mounts[i], destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0)
+      if (mount (srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0)
 	{
 	  perrorv ("failed to bind mount (class:toproot) %s to %s", toproot_bind_mounts[i], destpath);
 	  exit (1);
@@ -308,7 +285,8 @@ main(int argc, char *argv[])
 
   for (i = 0; ostree_bind_mounts[i] != NULL; i++)
     {
-      snprintf (srcpath, sizeof(srcpath), "/ostree/deploy/%s%s", ostree_osname, ostree_bind_mounts[i]);
+      snprintf (srcpath, sizeof(srcpath), "%s/ostree/deploy/%s%s", root_mountpoint,
+		ostree_osname, ostree_bind_mounts[i]);
       snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, ostree_bind_mounts[i]);
       if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
 	{
@@ -332,7 +310,19 @@ main(int argc, char *argv[])
 	}
     }
 
-  if (chroot (deploy_path) < 0)
+  if (chdir (deploy_path) < 0)
+    {
+      perrorv ("failed to chdir to subroot (initial)");
+      exit (1);
+    }
+
+  if (mount (deploy_path, "/", NULL, MS_MOVE, NULL) < 0)
+    {
+      perrorv ("failed to MS_MOVE %s to /", deploy_path);
+      exit (1);
+    }
+
+  if (chroot (".") < 0)
     {
       perrorv ("failed to change root to '%s'", deploy_path);
       exit (1);
@@ -340,10 +330,21 @@ main(int argc, char *argv[])
 
   if (chdir ("/") < 0)
     {
-      perrorv ("failed to chdir to subroot");
+      perrorv ("failed to chdir to / (after MS_MOVE of /)");
       exit (1);
     }
 
+  if (initramfs_fd >= 0)
+    {
+      cleanup_pid = fork ();
+      if (cleanup_pid == 0)
+	{
+	  recursive_remove (initramfs_fd);
+	  exit (0);
+	}
+      close (initramfs_fd);
+    }
+
   init_argv = malloc (sizeof (char*)*((argc-before_init_argc)+2));
   init_argv[0] = (char*)ostree_subinit;
   for (i = 0; i < argc-before_init_argc; i++)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]