diff --git a/cache/contenthash/filehash.go b/cache/contenthash/filehash.go index 0b5267101..246f8f7f1 100644 --- a/cache/contenthash/filehash.go +++ b/cache/contenthash/filehash.go @@ -51,6 +51,8 @@ func NewFromStat(stat *fstypes.Stat) (hash.Hash, error) { hdr.Name = "" // note: empty name is different from current has in docker build. Name is added on recursive directory scan instead hdr.Devmajor = stat.Devmajor hdr.Devminor = stat.Devminor + hdr.Uid = int(stat.Uid) + hdr.Gid = int(stat.Gid) if len(stat.Xattrs) > 0 { hdr.PAXRecords = make(map[string]string, len(stat.Xattrs)) diff --git a/client/client_test.go b/client/client_test.go index 137c20544..6b9d0abd5 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -87,6 +87,7 @@ func TestIntegration(t *testing.T) { testFileOpCopyRm, testFileOpCopyIncludeExclude, testFileOpRmWildcard, + testFileOpCopyUIDCache, testCallDiskUsage, testBuildMultiMount, testBuildHTTPSource, @@ -1436,6 +1437,65 @@ func testFileOpCopyRm(t *testing.T, sb integration.Sandbox) { require.Equal(t, []byte("file2"), dt) } +// moby/buildkit#3291 +func testFileOpCopyUIDCache(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + st := llb.Scratch().File( + llb.Copy(llb.Image("alpine").Run(llb.Shlex(`sh -c 'echo 123 > /foo && chown 1000:1000 /foo'`)).Root(), "foo", "foo")) + + def, err := st.Marshal(sb.Context()) + require.NoError(t, err) + + var buf bytes.Buffer + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterTar, + Output: fixedWriteCloser(&nopWriteCloser{&buf}), + }, + }, + }, nil) + require.NoError(t, err) + + m, err := testutil.ReadTarToMap(buf.Bytes(), false) + require.NoError(t, err) + + fi, ok := m["foo"] + require.True(t, ok) + require.Equal(t, 1000, fi.Header.Uid) + require.Equal(t, 1000, fi.Header.Gid) + + // repeat to check cache does not apply for different uid + st = llb.Scratch().File( + llb.Copy(llb.Image("alpine").Run(llb.Shlex(`sh -c 'echo 123 > /foo'`)).Root(), "foo", "foo")) + + def, err = st.Marshal(sb.Context()) + require.NoError(t, err) + + buf = bytes.Buffer{} + _, err = c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterTar, + Output: fixedWriteCloser(&nopWriteCloser{&buf}), + }, + }, + }, nil) + require.NoError(t, err) + + m, err = testutil.ReadTarToMap(buf.Bytes(), false) + require.NoError(t, err) + + fi, ok = m["foo"] + require.True(t, ok) + require.Equal(t, 0, fi.Header.Uid) + require.Equal(t, 0, fi.Header.Gid) +} + func testFileOpCopyIncludeExclude(t *testing.T, sb integration.Sandbox) { requiresLinux(t) c, err := New(sb.Context(), sb.Address())