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>
| ... | ... |
@@ -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 |