From 6d97339ca23ada27812572016ad4ff9ccffa8b09 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 13 Nov 2014 19:57:28 +0200 Subject: [PATCH] Fix AUFS silent mount errors on many layers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1171 Fixes #6465 Data passed to mount(2) is clipped to PAGE_SIZE if its bigger. Previous implementation checked if error was returned and then started to append layers one by one. But if the PAGE_SIZE clipping appeared in between the paths, in the permission sections or in xino definition the call would not error and remaining layers would just be skipped(or some other unknown situation). This also optimizes system calls as it tries to mount as much as possible with the first mount. Signed-off-by: Tõnis Tiigi (github: tonistiigi) --- daemon/graphdriver/aufs/aufs.go | 65 +++++++++++++++------------- daemon/graphdriver/aufs/aufs_test.go | 32 ++++++++++++-- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 4da42cd233..da3c720d16 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -412,39 +412,44 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro } }() - if err = a.tryMount(ro, rw, target, mountLabel); err != nil { - if err = a.mountRw(rw, target, mountLabel); err != nil { - return - } + // Mount options are clipped to page size(4096 bytes). If there are more + // layers then these are remounted individually using append. - for _, layer := range ro { - data := label.FormatMountLabel(fmt.Sprintf("append:%s=ro+wh", layer), mountLabel) - if err = mount("none", target, "aufs", MsRemount, data); err != nil { - return + b := make([]byte, syscall.Getpagesize()-len(mountLabel)-50) // room for xino & mountLabel + bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) + + firstMount := true + i := 0 + + for { + for ; i < len(ro); i++ { + layer := fmt.Sprintf(":%s=ro+wh", ro[i]) + + if firstMount { + if bp+len(layer) > len(b) { + break + } + bp += copy(b[bp:], layer) + } else { + data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) + if err = mount("none", target, "aufs", MsRemount, data); err != nil { + return + } } } + + if firstMount { + data := label.FormatMountLabel(fmt.Sprintf("%s,xino=/dev/shm/aufs.xino", string(b[:bp])), mountLabel) + if err = mount("none", target, "aufs", 0, data); err != nil { + return + } + firstMount = false + } + + if i == len(ro) { + break + } } + return } - -// Try to mount using the aufs fast path, if this fails then -// append ro layers. -func (a *Driver) tryMount(ro []string, rw, target, mountLabel string) (err error) { - var ( - rwBranch = fmt.Sprintf("%s=rw", rw) - roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:")) - data = label.FormatMountLabel(fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches), mountLabel) - ) - return mount("none", target, "aufs", 0, data) -} - -func (a *Driver) mountRw(rw, target, mountLabel string) error { - data := label.FormatMountLabel(fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw), mountLabel) - return mount("none", target, "aufs", 0, data) -} - -func rollbackMount(target string, err error) { - if err != nil { - Unmount(target) - } -} diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index cc5b3a2030..971d448af8 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -635,9 +635,13 @@ func hash(c string) string { return hex.EncodeToString(h.Sum(nil)) } -func TestMountMoreThan42Layers(t *testing.T) { - d := newDriver(t) - defer os.RemoveAll(tmp) +func testMountMoreThan42Layers(t *testing.T, mountPath string) { + if err := os.MkdirAll(mountPath, 0755); err != nil { + t.Fatal(err) + } + + d := testInit(mountPath, t).(*Driver) + defer os.RemoveAll(mountPath) defer d.Cleanup() var last string var expected int @@ -695,3 +699,25 @@ func TestMountMoreThan42Layers(t *testing.T) { t.Fatalf("Expected %d got %d", expected, len(files)) } } + +func TestMountMoreThan42Layers(t *testing.T) { + testMountMoreThan42Layers(t, tmp) +} + +func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) { + tmp := "aufs-tests" + for { + // This finds a mount path so that when combined into aufs mount options + // 4096 byte boundary would be in between the paths or in permission + // section. For '/tmp' it will use '/tmp/aufs-tests00000000/aufs' + mountPath := path.Join(os.TempDir(), tmp, "aufs") + pathLength := 77 + len(mountPath) + + if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 { + t.Logf("Using path: %s", mountPath) + testMountMoreThan42Layers(t, mountPath) + return + } + tmp += "0" + } +}