diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index bd824e2da6..dc66f06c27 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -360,10 +360,20 @@ func (a *Driver) Diff(id, parent string) (archive.Archive, error) { } func (a *Driver) applyDiff(id string, diff archive.Reader) error { - return chrootarchive.UntarUncompressed(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{ + dir := path.Join(a.rootPath(), "diff", id) + if err := chrootarchive.UntarUncompressed(diff, dir, &archive.TarOptions{ UIDMaps: a.uidMaps, GIDMaps: a.gidMaps, - }) + }); err != nil { + return err + } + + // show invalid whiteouts warning. + files, err := ioutil.ReadDir(path.Join(dir, archive.WhiteoutLinkDir)) + if err == nil && len(files) > 0 { + logrus.Warnf("Archive contains aufs hardlink references that are not supported.") + } + return nil } // DiffSize calculates the changes between the specified id @@ -493,7 +503,7 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro } if firstMount { - opts := "dio,xino=/dev/shm/aufs.xino" + opts := "dio,noplink,xino=/dev/shm/aufs.xino" if useDirperm() { opts += ",dirperm1" } diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index dbe747126c..bad7513ea7 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -638,6 +638,88 @@ func TestApplyDiff(t *testing.T) { } } +func TestHardlinks(t *testing.T) { + // Copy 2 layers that have linked files to new layers and check if hardlink are preserved + d := newDriver(t) + defer os.RemoveAll(tmp) + defer d.Cleanup() + + origFile := "test_file" + linkedFile := "linked_file" + + if err := d.Create("source-1", ""); err != nil { + t.Fatal(err) + } + + mountPath, err := d.Get("source-1", "") + if err != nil { + t.Fatal(err) + } + + f, err := os.Create(path.Join(mountPath, origFile)) + if err != nil { + t.Fatal(err) + } + f.Close() + + layerTar1, err := d.Diff("source-1", "") + if err != nil { + t.Fatal(err) + } + + if err := d.Create("source-2", "source-1"); err != nil { + t.Fatal(err) + } + + mountPath, err = d.Get("source-2", "") + if err != nil { + t.Fatal(err) + } + + if err := os.Link(path.Join(mountPath, origFile), path.Join(mountPath, linkedFile)); err != nil { + t.Fatal(err) + } + + layerTar2, err := d.Diff("source-2", "source-1") + if err != nil { + t.Fatal(err) + } + + if err := d.Create("target-1", ""); err != nil { + t.Fatal(err) + } + + if _, err := d.ApplyDiff("target-1", "", layerTar1); err != nil { + t.Fatal(err) + } + + if err := d.Create("target-2", "target-1"); err != nil { + t.Fatal(err) + } + + if _, err := d.ApplyDiff("target-2", "target-1", layerTar2); err != nil { + t.Fatal(err) + } + + mountPath, err = d.Get("target-2", "") + if err != nil { + t.Fatal(err) + } + + fi1, err := os.Lstat(path.Join(mountPath, origFile)) + if err != nil { + t.Fatal(err) + } + fi2, err := os.Lstat(path.Join(mountPath, linkedFile)) + if err != nil { + t.Fatal(err) + } + + if !os.SameFile(fi1, fi2) { + t.Fatal("Target files are not linked") + } +} + func hash(c string) string { h := sha256.New() fmt.Fprint(h, c)