[ostree] prepare-root: avoid double-stacked /sysroot mount



commit 4f75d4ea0b640dd69f13fd31976d6ddfb42c977a
Author: Daniel Drake <drake endlessm com>
Date:   Mon Feb 2 15:12:47 2015 -0600

    prepare-root: avoid double-stacked /sysroot mount
    
    prepare-root works with the mount that has been set up at /sysroot.
    It creates a bind-mount within /sysroot (the deployment) and then moves
    that mount to /sysroot.
    
    Now we have 2 mounts both at /sysroot, and once we do switch_root, we will
    never be able to unmount both of them. I'm not sure if this is ultimately
    a kernel bug, but either way, ostree could do a bit more tidying up
    after itself.
    http://thread.gmane.org/gmane.linux.file-systems/92411
    
    Easy way to reproduce:
    1. Boot with rd.break param
    2. At initramfs shell, run: ostree-prepare-root /sysroot
    3. Observe two /sysroot mounts in /proc/mounts
    
    Fix this by setting up the mounts at /sysroot.tmp, and unmounting the
    original /sysroot before our new mount is MS_MOVEd on top of it.

 src/switchroot/ostree-prepare-root.c |   29 ++++++++++++++++++++++++-----
 1 files changed, 24 insertions(+), 5 deletions(-)
---
diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c
index 13320d8..5cc9ed1 100644
--- a/src/switchroot/ostree-prepare-root.c
+++ b/src/switchroot/ostree-prepare-root.c
@@ -117,6 +117,7 @@ main(int argc, char *argv[])
   char *deploy_path = NULL;
   char srcpath[PATH_MAX];
   char destpath[PATH_MAX];
+  char newroot[PATH_MAX];
   struct stat stbuf;
   int i;
 
@@ -135,6 +136,13 @@ main(int argc, char *argv[])
       exit (EXIT_FAILURE);
     }
 
+  snprintf (newroot, sizeof(newroot), "%s.tmp", root_mountpoint);
+  if (mkdir (newroot, 0755) < 0)
+    {
+      perrorv ("Couldn't create temporary sysroot '%s': ", newroot);
+      exit (EXIT_FAILURE);
+    }
+
   snprintf (destpath, sizeof(destpath), "%s/%s", root_mountpoint, ostree_target);
   fprintf (stderr, "Examining %s\n", destpath);
   if (lstat (destpath, &stbuf) < 0)
@@ -168,13 +176,13 @@ main(int argc, char *argv[])
     }
 
   /* Make deploy_path a bind mount, so we can move it later */
-  if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0)
+  if (mount (deploy_path, newroot, NULL, MS_BIND, NULL) < 0)
     {
       perrorv ("failed to initial bind mount %s", deploy_path);
       exit (EXIT_FAILURE);
     }
 
-  snprintf (destpath, sizeof(destpath), "%s/sysroot", deploy_path);
+  snprintf (destpath, sizeof(destpath), "%s/sysroot", newroot);
   if (mount (root_mountpoint, destpath, NULL, MS_BIND, NULL) < 0)
     {
       perrorv ("Failed to bind mount %s to '%s'", root_mountpoint, destpath);
@@ -182,7 +190,7 @@ main(int argc, char *argv[])
     }
 
   snprintf (srcpath, sizeof(srcpath), "%s/../../var", deploy_path);
-  snprintf (destpath, sizeof(destpath), "%s/var", deploy_path);
+  snprintf (destpath, sizeof(destpath), "%s/var", newroot);
   if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
     {
       perrorv ("failed to bind mount %s to %s", srcpath, destpath);
@@ -191,7 +199,7 @@ main(int argc, char *argv[])
 
   for (i = 0; readonly_bind_mounts[i] != NULL; i++)
     {
-      snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, readonly_bind_mounts[i]);
+      snprintf (destpath, sizeof(destpath), "%s%s", newroot, readonly_bind_mounts[i]);
       if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0)
        {
          perrorv ("failed to bind mount (class:readonly) %s", destpath);
@@ -206,11 +214,22 @@ main(int argc, char *argv[])
 
   touch_run_ostree ();
 
+  /* In preparation for the hack below, unmount the original /sysroot.
+   * Otherwise, we would be placing our newroot mount on top of an
+   * existing mount, and after we do a switch_root, we would no longer
+   * be able to ever unmount the original mount.
+   */
+  if (umount (root_mountpoint) < 0)
+    {
+      perrorv ("failed to umount original mount at %s", root_mountpoint);
+      exit (EXIT_FAILURE);
+    }
+
   /* This is a bit hacky - move our deployment to /sysroot, since
    * systemd's initrd-switch-root target hardcodes looking for it
    * there.
    */
-  if (mount (deploy_path, root_mountpoint, NULL, MS_MOVE, NULL) < 0)
+  if (mount (newroot, root_mountpoint, NULL, MS_MOVE, NULL) < 0)
     {
       perrorv ("failed to MS_MOVE %s to %s", deploy_path, root_mountpoint);
       exit (EXIT_FAILURE);


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