Browse code

Fix overlay2 busy error on mount

When mounting overlays which have children, enforce that
the mount is always performed as read only. Newer versions
of the kernel return a device busy error when a lower directory
is in use as an upper directory in another overlay mount.

Adds committed file to indicate when an overlay is being used
as a parent, ensuring it will no longer be mounted with an
upper directory.

Signed-off-by: Derek McGowan <derek@mcgstyle.net>

Derek McGowan authored on 2019/08/22 07:03:52
Showing 1 changed files
... ...
@@ -446,6 +446,10 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
446 446
 		return err
447 447
 	}
448 448
 
449
+	if err := ioutil.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0600); err != nil {
450
+		return err
451
+	}
452
+
449 453
 	lower, err := d.getLower(parent)
450 454
 	if err != nil {
451 455
 		return err
... ...
@@ -592,7 +596,20 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
592 592
 	for i, s := range splitLowers {
593 593
 		absLowers[i] = path.Join(d.home, s)
594 594
 	}
595
-	opts := indexOff + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir
595
+	var readonly bool
596
+	if _, err := os.Stat(path.Join(dir, "committed")); err == nil {
597
+		readonly = true
598
+	} else if !os.IsNotExist(err) {
599
+		return nil, err
600
+	}
601
+
602
+	var opts string
603
+	if readonly {
604
+		opts = indexOff + "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":")
605
+	} else {
606
+		opts = indexOff + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir
607
+	}
608
+
596 609
 	mountData := label.FormatMountLabel(opts, mountLabel)
597 610
 	mount := unix.Mount
598 611
 	mountTarget := mergedDir
... ...
@@ -612,7 +629,11 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
612 612
 	// fit within a page and relative links make the mount data much
613 613
 	// smaller at the expense of requiring a fork exec to chroot.
614 614
 	if len(mountData) > pageSize {
615
-		opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName)
615
+		if readonly {
616
+			opts = indexOff + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers)
617
+		} else {
618
+			opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName)
619
+		}
616 620
 		mountData = label.FormatMountLabel(opts, mountLabel)
617 621
 		if len(mountData) > pageSize {
618 622
 			return nil, fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
... ...
@@ -628,10 +649,12 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e
628 628
 		return nil, fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
629 629
 	}
630 630
 
631
-	// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
632
-	// user namespace requires this to move a directory from lower to upper.
633
-	if err := os.Chown(path.Join(workDir, workDirName), rootUID, rootGID); err != nil {
634
-		return nil, err
631
+	if !readonly {
632
+		// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
633
+		// user namespace requires this to move a directory from lower to upper.
634
+		if err := os.Chown(path.Join(workDir, workDirName), rootUID, rootGID); err != nil {
635
+			return nil, err
636
+		}
635 637
 	}
636 638
 
637 639
 	return containerfs.NewLocalContainerFS(mergedDir), nil