Skip to content

Commit

Permalink
prepare-root: avoid double-stacked /sysroot mount
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dsd authored and cgwalters committed Feb 4, 2015
1 parent 89a8b9b commit 4f75d4e
Showing 1 changed file with 24 additions and 5 deletions.
29 changes: 24 additions & 5 deletions src/switchroot/ostree-prepare-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -168,21 +176,21 @@ 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);
exit (EXIT_FAILURE);
}

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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 4f75d4e

Please sign in to comment.