diff --git a/pkg/chunked/storage_linux.go b/pkg/chunked/storage_linux.go index 482c459199..9bad7bcbce 100644 --- a/pkg/chunked/storage_linux.go +++ b/pkg/chunked/storage_linux.go @@ -1280,6 +1280,13 @@ func typeToOsMode(typ string) (os.FileMode, error) { return 0, fmt.Errorf("unknown file type %q", typ) } +// cleanAbsDirectory removes any ".." and "." from the path +// and ensures it starts with a "/". If the path refers to the root +// directory, it returns "/". +func cleanAbsDirectory(path string) string { + return filepath.Clean("/" + path) +} + func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, differOpts *graphdriver.DifferOptions) (graphdriver.DriverWithDifferOutput, error) { defer c.layersCache.release() defer func() { @@ -1544,10 +1551,10 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff } } - r.Name = filepath.Clean(r.Name) + r.Name = cleanAbsDirectory(r.Name) // do not modify the value of symlinks if r.Linkname != "" && t != tar.TypeSymlink { - r.Linkname = filepath.Clean(r.Linkname) + r.Linkname = cleanAbsDirectory(r.Linkname) } if whiteoutConverter != nil { @@ -1595,7 +1602,7 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff } case tar.TypeDir: - if r.Name == "" || r.Name == "." { + if r.Name == "/" { output.RootDirMode = &mode } if err := safeMkdir(dirfd, mode, r.Name, r, options); err != nil { diff --git a/pkg/chunked/storage_linux_test.go b/pkg/chunked/storage_linux_test.go index 23a2e8de30..37656440a1 100644 --- a/pkg/chunked/storage_linux_test.go +++ b/pkg/chunked/storage_linux_test.go @@ -211,3 +211,39 @@ func TestTypeToOsMode(t *testing.T) { }) } } + +func TestCleanAbsDirectory(t *testing.T) { + tests := []struct { + path string + expected string + }{ + {"", "/"}, + {"./", "/"}, + {"../", "/"}, + {"/../", "/"}, + {"/./", "/"}, + {"foo", "/foo"}, + {"foo/bar", "/foo/bar"}, + {"/foo/bar/../baz", "/foo/baz"}, + {"/foo/./bar", "/foo/bar"}, + {"/foo/bar/../../baz", "/baz"}, + {"/././foo", "/foo"}, + {"../foo", "/foo"}, + {"./foo/bar/../..", "/"}, + {"foo/..", "/"}, + {"foo/../bar", "/bar"}, + {"//foo//bar", "/foo/bar"}, + {"foo/bar//baz/..", "/foo/bar"}, + {"../..", "/"}, + {".././..", "/"}, + {"../../.", "/"}, + {"/../../foo", "/foo"}, + {"../foo/bar/../baz", "/foo/baz"}, + {"../.././/.//../foo/./../bar/..", "/"}, + {"a/../.././/.//../foo/./../bar/..", "/"}, + } + + for _, test := range tests { + assert.Equal(t, test.expected, cleanAbsDirectory(test.path), fmt.Sprintf("path %q failed", test.path)) + } +}