Browse code

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 <alexl@redhat.com> (github: alexlarsson)

Alexander Larsson authored on 2014/02/17 21:41:52
Showing 2 changed files
... ...
@@ -186,7 +186,7 @@ func addTarFile(path, name string, tw *tar.Writer) error {
186 186
 	return nil
187 187
 }
188 188
 
189
-func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error {
189
+func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader) error {
190 190
 	switch hdr.Typeflag {
191 191
 	case tar.TypeDir:
192 192
 		// Create directory unless it exists as a directory already.
... ...
@@ -2,7 +2,9 @@ package archive
2 2
 
3 3
 import (
4 4
 	"code.google.com/p/go/src/pkg/archive/tar"
5
+	"fmt"
5 6
 	"io"
7
+	"io/ioutil"
6 8
 	"os"
7 9
 	"path/filepath"
8 10
 	"strings"
... ...
@@ -42,6 +44,9 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
42 42
 
43 43
 	var dirs []*tar.Header
44 44
 
45
+	aufsTempdir := ""
46
+	aufsHardlinks := make(map[string]*tar.Header)
47
+
45 48
 	// Iterate through the files in the archive.
46 49
 	for {
47 50
 		hdr, err := tr.Next()
... ...
@@ -72,6 +77,22 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
72 72
 
73 73
 		// Skip AUFS metadata dirs
74 74
 		if strings.HasPrefix(hdr.Name, ".wh..wh.") {
75
+			// Regular files inside /.wh..wh.plnk can be used as hardlink targets
76
+			// We don't want this directory, but we need the files in them so that
77
+			// such hardlinks can be resolved.
78
+			if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg {
79
+				basename := filepath.Base(hdr.Name)
80
+				aufsHardlinks[basename] = hdr
81
+				if aufsTempdir == "" {
82
+					if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil {
83
+						return err
84
+					}
85
+					defer os.RemoveAll(aufsTempdir)
86
+				}
87
+				if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr); err != nil {
88
+					return err
89
+				}
90
+			}
75 91
 			continue
76 92
 		}
77 93
 
... ...
@@ -96,7 +117,26 @@ func ApplyLayer(dest string, layer ArchiveReader) error {
96 96
 				}
97 97
 			}
98 98
 
99
-			if err := createTarFile(path, dest, hdr, tr); err != nil {
99
+			srcData := io.Reader(tr)
100
+			srcHdr := hdr
101
+
102
+			// Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so
103
+			// we manually retarget these into the temporary files we extracted them into
104
+			if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") {
105
+				linkBasename := filepath.Base(hdr.Linkname)
106
+				srcHdr = aufsHardlinks[linkBasename]
107
+				if srcHdr == nil {
108
+					return fmt.Errorf("Invalid aufs hardlink")
109
+				}
110
+				tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename))
111
+				if err != nil {
112
+					return err
113
+				}
114
+				defer tmpFile.Close()
115
+				srcData = tmpFile
116
+			}
117
+
118
+			if err := createTarFile(path, dest, srcHdr, srcData); err != nil {
100 119
 				return err
101 120
 			}
102 121