Browse code

Add basic support for .wh..wh..opq

This fixes the case where directory is removed in
aufs and then the same layer is imported to a
different graphdriver.

Currently when you do `rm -rf /foo && mkdir /foo`
in a layer in aufs the files under `foo` would
only be be hidden on aufs.

The problems with this fix:

1) When a new diff is recreated from non-aufs driver
the `opq` files would not be there. This should not
mean layer differences for the user but still
different content in the tar (one would have one
`opq` file, the others would have `.wh.*` for every
file inside that folder). This difference also only
happens if the tar-split file isn’t stored for the
layer.

2) New files that have the filenames before `.wh..wh..opq`
when they are sorted do not get picked up by non-aufs
graphdrivers. Fixing this would require a bigger
refactoring that is planned in the future.


Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>

Tonis Tiigi authored on 2015/09/22 06:36:35
Showing 2 changed files
... ...
@@ -322,7 +322,7 @@ func (a *Driver) Diff(id, parent string) (archive.Archive, error) {
322 322
 	// AUFS doesn't need the parent layer to produce a diff.
323 323
 	return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
324 324
 		Compression:     archive.Uncompressed,
325
-		ExcludePatterns: []string{".wh..wh.*"},
325
+		ExcludePatterns: []string{".wh..wh.*", "!.wh..wh..opq"},
326 326
 	})
327 327
 }
328 328
 
... ...
@@ -100,7 +100,10 @@ func UnpackLayer(dest string, layer Reader) (size int64, err error) {
100 100
 					return 0, err
101 101
 				}
102 102
 			}
103
-			continue
103
+
104
+			if hdr.Name != ".wh..wh..opq" {
105
+				continue
106
+			}
104 107
 		}
105 108
 		path := filepath.Join(dest, hdr.Name)
106 109
 		rel, err := filepath.Rel(dest, path)
... ...
@@ -116,9 +119,23 @@ func UnpackLayer(dest string, layer Reader) (size int64, err error) {
116 116
 
117 117
 		if strings.HasPrefix(base, ".wh.") {
118 118
 			originalBase := base[len(".wh."):]
119
-			originalPath := filepath.Join(filepath.Dir(path), originalBase)
120
-			if err := os.RemoveAll(originalPath); err != nil {
121
-				return 0, err
119
+			dir := filepath.Dir(path)
120
+			if originalBase == ".wh..opq" {
121
+				fi, err := os.Lstat(dir)
122
+				if err != nil && !os.IsNotExist(err) {
123
+					return 0, err
124
+				}
125
+				if err := os.RemoveAll(dir); err != nil {
126
+					return 0, err
127
+				}
128
+				if err := os.Mkdir(dir, fi.Mode()&os.ModePerm); err != nil {
129
+					return 0, err
130
+				}
131
+			} else {
132
+				originalPath := filepath.Join(dir, originalBase)
133
+				if err := os.RemoveAll(originalPath); err != nil {
134
+					return 0, err
135
+				}
122 136
 			}
123 137
 		} else {
124 138
 			// If path exits we almost always just want to remove and replace it.