From 6889cd9f9cfa3a6439b7702d73f27ab3fac3d3ef Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 17 Feb 2014 13:41:52 +0100 Subject: [PATCH] archive: Handle aufs plink hardlinks in ApplyLayer Files in the .wh..wh.plnk directory are ignored, but other files inside the tarfile can be hardlinks to these files. This is not something that normally happens, as on aufs unmount such files are supposed to be dropped via the "auplink" too, yet images on the index (such as shipyard/shipyard, e.g. layer f73c835af6d58b6fc827b400569f79a8f28e54f5bb732be063e1aacefbc374d0) contains such files. We handle these by extracting these files to a temporary directory and resolve such hardlinks via the temporary files. This fixes https://github.com/dotcloud/docker/issues/3884 Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- archive/archive.go | 2 +- archive/diff.go | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/archive/archive.go b/archive/archive.go index 8212621bb9..c0551d9fa3 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -186,7 +186,7 @@ func addTarFile(path, name string, tw *tar.Writer) error { return nil } -func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error { +func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader) error { switch hdr.Typeflag { case tar.TypeDir: // Create directory unless it exists as a directory already. diff --git a/archive/diff.go b/archive/diff.go index de93fc69b2..6a778390bb 100644 --- a/archive/diff.go +++ b/archive/diff.go @@ -2,7 +2,9 @@ package archive import ( "code.google.com/p/go/src/pkg/archive/tar" + "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" @@ -42,6 +44,9 @@ func ApplyLayer(dest string, layer ArchiveReader) error { var dirs []*tar.Header + aufsTempdir := "" + aufsHardlinks := make(map[string]*tar.Header) + // Iterate through the files in the archive. for { hdr, err := tr.Next() @@ -72,6 +77,22 @@ func ApplyLayer(dest string, layer ArchiveReader) error { // Skip AUFS metadata dirs if strings.HasPrefix(hdr.Name, ".wh..wh.") { + // Regular files inside /.wh..wh.plnk can be used as hardlink targets + // We don't want this directory, but we need the files in them so that + // such hardlinks can be resolved. + if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg { + basename := filepath.Base(hdr.Name) + aufsHardlinks[basename] = hdr + if aufsTempdir == "" { + if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { + return err + } + defer os.RemoveAll(aufsTempdir) + } + if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr); err != nil { + return err + } + } continue } @@ -96,7 +117,26 @@ func ApplyLayer(dest string, layer ArchiveReader) error { } } - if err := createTarFile(path, dest, hdr, tr); err != nil { + srcData := io.Reader(tr) + srcHdr := hdr + + // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so + // we manually retarget these into the temporary files we extracted them into + if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") { + linkBasename := filepath.Base(hdr.Linkname) + srcHdr = aufsHardlinks[linkBasename] + if srcHdr == nil { + return fmt.Errorf("Invalid aufs hardlink") + } + tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) + if err != nil { + return err + } + defer tmpFile.Close() + srcData = tmpFile + } + + if err := createTarFile(path, dest, srcHdr, srcData); err != nil { return err }