Skip to content

Commit 8691a77

Browse files
committed
Fix build cache false positives when build context tar contains unnormalized paths
If a build context tar has path names of the form 'x/./y', they will be stored in this unnormalized form internally by tarsum. When the builder walks the untarred directory tree and queries hashes for each relative path, it will query paths of the form 'x/y', and they will not be found. To correct this, have tarsum normalize path names by calling Clean. Add a test to detect this caching false positive. Fixes moby#21715 Signed-off-by: Aaron Lehmann <[email protected]>
1 parent 1e1da2a commit 8691a77

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

builder/tarsum.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (c *tarSumContext) Walk(root string, walkFn WalkFunc) error {
138138
}
139139

140140
sum := rel
141-
if tsInfo := c.sums.GetFile(rel); tsInfo != nil {
141+
if tsInfo := c.sums.GetFile(filepath.ToSlash(rel)); tsInfo != nil {
142142
sum = tsInfo.Sum()
143143
}
144144
fi := &HashedFileInfo{PathFileInfo{FileInfo: info, FilePath: fullpath}, sum}

integration-cli/docker_api_build_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"archive/tar"
55
"bytes"
66
"net/http"
7+
"regexp"
8+
"strings"
79

810
"github.com/docker/docker/pkg/integration/checker"
911
"github.com/go-check/check"
@@ -255,3 +257,60 @@ func (s *DockerSuite) TestBuildApiDockerfileSymlink(c *check.C) {
255257
// a nonexistent file.
256258
c.Assert(string(out), checker.Contains, "Cannot locate specified Dockerfile: Dockerfile", check.Commentf("Didn't complain about leaving build context"))
257259
}
260+
261+
func (s *DockerSuite) TestBuildApiUnnormalizedTarPaths(c *check.C) {
262+
// Make sure that build context tars with entries of the form
263+
// x/./y don't cause caching false positives.
264+
265+
buildFromTarContext := func(fileContents []byte) string {
266+
buffer := new(bytes.Buffer)
267+
tw := tar.NewWriter(buffer)
268+
defer tw.Close()
269+
270+
dockerfile := []byte(`FROM busybox
271+
COPY dir /dir/`)
272+
err := tw.WriteHeader(&tar.Header{
273+
Name: "Dockerfile",
274+
Size: int64(len(dockerfile)),
275+
})
276+
//failed to write tar file header
277+
c.Assert(err, checker.IsNil)
278+
279+
_, err = tw.Write(dockerfile)
280+
// failed to write Dockerfile in tar file content
281+
c.Assert(err, checker.IsNil)
282+
283+
err = tw.WriteHeader(&tar.Header{
284+
Name: "dir/./file",
285+
Size: int64(len(fileContents)),
286+
})
287+
//failed to write tar file header
288+
c.Assert(err, checker.IsNil)
289+
290+
_, err = tw.Write(fileContents)
291+
// failed to write file contents in tar file content
292+
c.Assert(err, checker.IsNil)
293+
294+
// failed to close tar archive
295+
c.Assert(tw.Close(), checker.IsNil)
296+
297+
res, body, err := sockRequestRaw("POST", "/build", buffer, "application/x-tar")
298+
c.Assert(err, checker.IsNil)
299+
c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
300+
301+
out, err := readBody(body)
302+
c.Assert(err, checker.IsNil)
303+
lines := strings.Split(string(out), "\n")
304+
c.Assert(len(lines), checker.GreaterThan, 1)
305+
c.Assert(lines[len(lines)-2], checker.Matches, ".*Successfully built [0-9a-f]{12}.*")
306+
307+
re := regexp.MustCompile("Successfully built ([0-9a-f]{12})")
308+
matches := re.FindStringSubmatch(lines[len(lines)-2])
309+
return matches[1]
310+
}
311+
312+
imageA := buildFromTarContext([]byte("abc"))
313+
imageB := buildFromTarContext([]byte("def"))
314+
315+
c.Assert(imageA, checker.Not(checker.Equals), imageB)
316+
}

pkg/tarsum/tarsum.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"fmt"
2929
"hash"
3030
"io"
31+
"path"
3132
"strings"
3233
)
3334

@@ -235,7 +236,7 @@ func (ts *tarSum) Read(buf []byte) (int, error) {
235236
}
236237
return n, err
237238
}
238-
ts.currentFile = strings.TrimSuffix(strings.TrimPrefix(currentHeader.Name, "./"), "/")
239+
ts.currentFile = path.Clean(currentHeader.Name)
239240
if err := ts.encodeHeader(currentHeader); err != nil {
240241
return 0, err
241242
}

0 commit comments

Comments
 (0)