Repository: moby/buildkit

Issue:
flaky test:  TestIntegration/slice=1-4/TestCachedMounts/worker=containerd
https://github.com/moby/buildkit/actions/runs/14379949876/job/40321264606?pr=5904#step:8:544

```
=== FAIL: client TestIntegration/slice=1-4/TestCachedMounts/worker=containerd (25.87s)
    client_test.go:6846: 
        	Error Trace:	D:/a/buildkit/buildkit/client/client_test.go:6846
        	            				D:/a/buildkit/buildkit/util/testutil/integration/run.go:103
        	            				D:/a/buildkit/buildkit/util/testutil/integration/run.go:245
        	Error:      	Received unexpected error:
        	            	failed to compute cache key: failed to calculate checksum of ref lht661ikl1n3ucv52eoq4btsi::zpg95ubmeb1yebc6liy6klxco: failed to open /WcSandboxState/Hives/DefaultUser_Delta: open C:\Users\RUNNER~1\AppData\Local\Temp\buildkit-mount3558833864\WcSandboxState\Hives\DefaultUser_Delta: Access is denied.
        	            	github.com/moby/buildkit/util/stack.Enable
        	            		D:/a/buildkit/buildkit/util/stack/stack.go:82
        	            	github.com/moby/buildkit/util/grpcerrors.FromGRPC
        	            		D:/a/buildkit/buildkit/util/grpcerrors/grpcerrors.go:205
        	            	github.com/moby/buildkit/util/grpcerrors.UnaryClientInterceptor
        	            		D:/a/buildkit/buildkit/util/grpcerrors/intercept.go:41
        	            	google.golang.org/grpc.(*ClientConn).Invoke
        	            		D:/a/buildkit/buildkit/vendor/google.golang.org/grpc/call.go:35
        	            	github.com/moby/buildkit/api/services/control.(*controlClient).Solve
        	            		D:/a/buildkit/buildkit/api/services/control/control_grpc.pb.go:88
        	            	github.com/moby/buildkit/client.(*Client).solve.func2
        	            		D:/a/buildkit/buildkit/client/solve.go:267
        	            	golang.org/x/sync/errgroup.(*Group).Go.func1
        	            		D:/a/buildkit/buildkit/vendor/golang.org/x/sync/errgroup/errgroup.go:79
        	            	runtime.goexit
        	            		C:/hostedtoolcache/windows/go/1.23.8/x64/src/runtime/asm_amd64.s:1700
        	            	failed to solve
        	            	github.com/moby/buildkit/client.(*Client).solve.func2
        	            		D:/a/buildkit/buildkit/client/solve.go:283
        	            	golang.org/x/sync/errgroup.(*Group).Go.func1
        	            		D:/a/buildkit/buildkit/vendor/golang.org/x/sync/errgroup/errgroup.go:79
        	            	runtime.goexit
        	            		C:/hostedtoolcache/windows/go/1.23.8/x64/src/runtime/asm_amd64.s:1700
        	Test:       	TestIntegration/slice=1-4/TestCachedMounts/worker=containerd
```

Discussion:
@profnandaa: Taking a look.@profnandaa: Adding the stack-trace of where the error originates:
```
  sandbox.go:205: github.com/moby/buildkit/cache/contenthash.prepareDigest
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:1219
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:930
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:898
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:898
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:898
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).scanChecksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:859
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).lazyChecksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:822
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).Checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:426
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheManager).Checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:117
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.Checksum
    sandbox.go:205: 	/src/cache/contenthash/checksum.go:65
    sandbox.go:205: github.com/moby/buildkit/solver/llbsolver/ops.(*ExecOp).CacheMap.NewContentHashFunc.func2.1
    sandbox.go:205: 	/src/solver/llbsolver/ops/opsutils/contenthash.go:47
    sandbox.go:205: golang.org/x/sync/errgroup.(*Group).Go.func1
    sandbox.go:205: 	/src/vendor/golang.org/x/sync/errgroup/errgroup.go:79
    sandbox.go:205: runtime.goexit
    sandbox.go:205: 	/usr/local/go/src/runtime/asm_amd64.s:1700
```@profnandaa: This failure has resuffaced here too - https://github.com/moby/buildkit/actions/runs/14802211583/job/41563473233?pr=5955

Setting some time this week to dig into this. Sorry had to complete on some other stuff that took long.@profnandaa: Repro steps:

```powershell
$cmd = "go test --count=1 -v --run=TestIntegration/TestCachedMounts/worker=containerd ./client"
for ($i = 1; $i -le 100; $i++) { iex $cmd }
```@profnandaa: **UPDATE:**

This happens just right after the privilege is disabled, some sort of a race condition. Added a log-line to see:

```
    sandbox.go:205: time="2025-05-19T04:24:05-07:00" level=info msg="===> Privilege disabled!!"
    sandbox.go:205: time="2025-05-19T04:24:06-07:00" level=error msg="/moby.buildkit.v1.Control/Solve returned error: rpc error: code = Unknown desc = failed to compute cache key: failed to calculate checksum of ref szz6mcq63r4fsfhs4y4ynctw6::idei7kbxkyrnqp1h3nibcnfar: failed to open /WcSandboxState/Hives/DefaultUser_Delta: open C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\2\\buildkit-mount3029327637\\WcSandboxState\\Hives\\DefaultUser_Delta: Access is denied." spanID=07037f166d560fcd traceID=61696a44866dc0511eaed677506bb6c2
    sandbox.go:205: failed to compute cache key: failed to calculate checksum of ref szz6mcq63r4fsfhs4y4ynctw6::idei7kbxkyrnqp1h3nibcnfar: failed to open /WcSandboxState/Hives/DefaultUser_Delta: open C:\Users\ADMINI~1\AppData\Local\Temp\2\buildkit-mount3029327637\WcSandboxState\Hives\DefaultUser_Delta: Access is denied.
    sandbox.go:205: 6392 v0.0.0+unknown buildkitd --containerd-worker-gc=false --containerd-worker=true --containerd-worker-addr npipe:////./pipe/containerd-bktest_containerd1745300450 --containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true --config=C:\Users\ADMINI~1\AppData\Local\Temp\2\bktest_config1107440939\buildkitd.toml --root C:\Users\ADMINI~1\AppData\Local\Temp\2\bktest_buildkitd925949042 --addr npipe:////./pipe/buildkitd-bktest_buildkitd925949042 --debug
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.prepareDigest
    sandbox.go:205: 	C:/dev/buildkit/cache/contenthash/checksum.go:1216
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
    sandbox.go:205: 	C:/dev/buildkit/cache/contenthash/checksum.go:927
    sandbox.go:205: github.com/moby/buildkit/cache/contenthash.(*cacheContext).checksum
```

Solution patch:
diff --git a/.github/workflows/test-os.yml b/.github/workflows/test-os.yml
index f6859912f..243e5ec7a 100644
--- a/.github/workflows/test-os.yml
+++ b/.github/workflows/test-os.yml
@@ -260,6 +260,7 @@ jobs:
           vagrant ssh -- "sudo cat /vagrant/.tmp/logs/containerd"
 
   sandbox-build:
+    if: ${{ github.repository == 'moby/buildkit' }}
     runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
     strategy:
       fail-fast: false
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index a99017106..38143b991 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -27,33 +27,67 @@ jobs:
   prepare:
     runs-on: ubuntu-24.04
     outputs:
-      targets: ${{ steps.generate.outputs.targets }}
+      includes: ${{ steps.matrix.outputs.includes }}
     steps:
       -
         name: Checkout
         uses: actions/checkout@v4
       -
-        name: List targets
-        id: generate
-        uses: docker/bake-action/subaction/list-targets@v6
+        name: Matrix
+        id: matrix
+        uses: actions/github-script@v7
+        env:
+          INPUT_PRIVATE-REPO: ${{ github.event.repository.private }}
         with:
-          target: validate
+          script: |
+            let def = {};
+            const privateRepo = core.getBooleanInput('private-repo');
+            await core.group(`Parsing definition`, async () => {
+              const printEnv = Object.assign({}, process.env, {
+                GOLANGCI_LINT_MULTIPLATFORM: process.env.GITHUB_REPOSITORY === 'moby/buildkit' ? '1' : '',
+                ARCHUTIL_MULTIPLATFORM: process.env.GITHUB_REPOSITORY === 'moby/buildkit' ? '1' : ''
+              });
+              const resPrint = await exec.getExecOutput('docker', ['buildx', 'bake', 'validate', '--print'], {
+                ignoreReturnCode: true,
+                env: printEnv
+              });
+              if (resPrint.stderr.length > 0 && resPrint.exitCode != 0) {
+                throw new Error(res.stderr);
+              }
+              def = JSON.parse(resPrint.stdout.trim());
+            });
+            await core.group(`Generating matrix`, async () => {
+              const includes = [];
+              for (const targetName of Object.keys(def.target)) {
+                const target = def.target[targetName];
+                if (target.platforms && target.platforms.length > 0) {
+                  target.platforms.forEach(platform => {
+                    includes.push({
+                      target: targetName,
+                      platform: platform,
+                      runner: platform.startsWith('linux/arm') && !privateRepo ? 'ubuntu-24.04-arm' : 'ubuntu-24.04'
+                    });
+                  });
+                } else {
+                  includes.push({
+                    target: targetName,
+                    runner: 'ubuntu-24.04'
+                  });
+                }
+              }
+              core.info(JSON.stringify(includes, null, 2));
+              core.setOutput('includes', JSON.stringify(includes));
+            });
 
   validate:
-    runs-on: ubuntu-24.04
+    runs-on: ${{ matrix.runner }}
     needs:
       - prepare
     strategy:
       fail-fast: false
       matrix:
-        target: ${{ fromJson(needs.prepare.outputs.targets) }}
+        include: ${{ fromJson(needs.prepare.outputs.includes) }}
     steps:
-      -
-        name: Prepare
-        run: |
-          if [ "$GITHUB_REPOSITORY" = "moby/buildkit" ]; then
-            echo "GOLANGCI_LINT_MULTIPLATFORM=1" >> $GITHUB_ENV
-          fi
       -
         name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v3
@@ -66,21 +100,5 @@ jobs:
         uses: docker/bake-action@v6
         with:
           targets: ${{ matrix.target }}
-
-  archutil-arm64:
-    runs-on: ubuntu-24.04-arm
-    steps:
-      -
-        name: Set up Docker Buildx
-        uses: docker/setup-buildx-action@v3
-        with:
-          version: ${{ env.SETUP_BUILDX_VERSION }}
-          driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
-          buildkitd-flags: --debug
-      -
-        name: Validate
-        uses: docker/bake-action@v6
-        with:
-          targets: validate-archutil
           set: |
-            *.platform=linux/arm64
+            *.platform=${{ matrix.platform }}
diff --git a/README.md b/README.md
index 7bc0d6e3d..abc54852e 100644
--- a/README.md
+++ b/README.md
@@ -205,6 +205,7 @@ Currently, the following high-level languages have been implemented for LLB:
 -   [Bass](https://github.com/vito/bass)
 -   [kraft.yaml (Unikraft)](https://github.com/unikraft/kraftkit/tree/staging/tools/dockerfile-llb-frontend)
 -   [r2d4/llb (JSON Gateway)](https://github.com/r2d4/llb)
+-   [Massé](https://github.com/marxarelli/masse)
 -   (open a PR to add your own language)
 
 ### Exploring Dockerfiles
diff --git a/cache/contenthash/checksum_windows.go b/cache/contenthash/checksum_windows.go
index be0b539b3..b49642757 100644
--- a/cache/contenthash/checksum_windows.go
+++ b/cache/contenthash/checksum_windows.go
@@ -2,11 +2,16 @@ package contenthash
 
 import (
 	"path/filepath"
+	"sync"
 
 	"github.com/Microsoft/go-winio"
 )
 
-var privileges = []string{winio.SeBackupPrivilege}
+var (
+	privileges        = []string{winio.SeBackupPrivilege}
+	privilegeLock     sync.Mutex
+	privilegeRefCount int
+)
 
 func (cc *cacheContext) walk(scanPath string, walkFunc filepath.WalkFunc) error {
 	// elevating the admin privileges to walk special files/directory
@@ -19,11 +24,25 @@ func (cc *cacheContext) walk(scanPath string, walkFunc filepath.WalkFunc) error
 // Adds the SeBackupPrivilege to the process
 // to be able to access some special files and directories.
 func enableProcessPrivileges() {
-	_ = winio.EnableProcessPrivileges(privileges)
+	privilegeLock.Lock()
+	defer privilegeLock.Unlock()
+
+	if privilegeRefCount == 0 {
+		_ = winio.EnableProcessPrivileges(privileges)
+	}
+	privilegeRefCount++
 }
 
 // Disables the SeBackupPrivilege on the process
 // once the group of functions that needed it is complete.
 func disableProcessPrivileges() {
-	_ = winio.DisableProcessPrivileges(privileges)
+	privilegeLock.Lock()
+	defer privilegeLock.Unlock()
+
+	if privilegeRefCount > 0 {
+		privilegeRefCount--
+		if privilegeRefCount == 0 {
+			_ = winio.DisableProcessPrivileges(privileges)
+		}
+	}
 }
diff --git a/docker-bake.hcl b/docker-bake.hcl
index 82e4872fd..9559e2075 100644
--- a/docker-bake.hcl
+++ b/docker-bake.hcl
@@ -42,6 +42,10 @@ variable "GOLANGCI_LINT_MULTIPLATFORM" {
   default = null
 }
 
+variable "ARCHUTIL_MULTIPLATFORM" {
+  default = null
+}
+
 # Defines the output folder
 variable "DESTDIR" {
   default = ""
@@ -214,6 +218,10 @@ target "validate-archutil" {
   dockerfile = "./hack/dockerfiles/archutil.Dockerfile"
   target = "validate"
   output = ["type=cacheonly"]
+  platforms = ARCHUTIL_MULTIPLATFORM != null ? [
+    "linux/amd64",
+    "linux/arm64"
+  ] : []
 }
 
 target "validate-shfmt" {
diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go
index b52c03155..9ba03f66a 100644
--- a/frontend/dockerfile/dockerfile2llb/convert.go
+++ b/frontend/dockerfile/dockerfile2llb/convert.go
@@ -948,6 +948,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
 			link:            c.Link,
 			keepGitDir:      c.KeepGitDir,
 			checksum:        c.Checksum,
+			unpack:          c.Unpack,
 			location:        c.Location(),
 			ignoreMatcher:   opt.dockerIgnoreMatcher,
 			opt:             opt,
@@ -1554,9 +1555,15 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
 
 			st := llb.HTTP(src, llb.Filename(f), llb.WithCustomName(pgName), llb.Checksum(checksum), dfCmd(cfg.params))
 
+			var unpack bool
+			if cfg.unpack != nil {
+				unpack = *cfg.unpack
+			}
+
 			opts := append([]llb.CopyOption{&llb.CopyInfo{
 				Mode:           chopt,
 				CreateDestPath: true,
+				AttemptUnpack:  unpack,
 			}}, copyOpt...)
 
 			if a == nil {
@@ -1590,12 +1597,17 @@ func dispatchCopy(d *dispatchState, cfg copyConfig) error {
 				return errors.Wrap(err, "removing drive letter")
 			}
 
+			unpack := cfg.isAddCommand
+			if cfg.unpack != nil {
+				unpack = *cfg.unpack
+			}
+
 			opts := append([]llb.CopyOption{&llb.CopyInfo{
 				Mode:                chopt,
 				FollowSymlinks:      true,
 				CopyDirContentsOnly: true,
 				IncludePatterns:     patterns,
-				AttemptUnpack:       cfg.isAddCommand,
+				AttemptUnpack:       unpack,
 				CreateDestPath:      true,
 				AllowWildcard:       true,
 				AllowEmptyWildcard:  true,
@@ -1684,6 +1696,7 @@ type copyConfig struct {
 	location        []parser.Range
 	ignoreMatcher   *patternmatcher.PatternMatcher
 	opt             dispatchOpt
+	unpack          *bool
 }
 
 func dispatchMaintainer(d *dispatchState, c *instructions.MaintainerCommand) error {
diff --git a/frontend/dockerfile/instructions/commands.go b/frontend/dockerfile/instructions/commands.go
index bebce6361..f82f07f9d 100644
--- a/frontend/dockerfile/instructions/commands.go
+++ b/frontend/dockerfile/instructions/commands.go
@@ -246,6 +246,7 @@ type AddCommand struct {
 	ExcludePatterns []string
 	KeepGitDir      bool // whether to keep .git dir, only meaningful for git sources
 	Checksum        string
+	Unpack          *bool
 }
 
 func (c *AddCommand) Expand(expander SingleWordExpander) error {
diff --git a/frontend/dockerfile/instructions/parse.go b/frontend/dockerfile/instructions/parse.go
index b44a6a384..ccb0820fa 100644
--- a/frontend/dockerfile/instructions/parse.go
+++ b/frontend/dockerfile/instructions/parse.go
@@ -338,6 +338,7 @@ func parseAdd(req parseRequest) (*AddCommand, error) {
 	flLink := req.flags.AddBool("link", false)
 	flKeepGitDir := req.flags.AddBool("keep-git-dir", false)
 	flChecksum := req.flags.AddString("checksum", "")
+	flUnpack := req.flags.AddBool("unpack", false)
 	if err := req.flags.Parse(); err != nil {
 		return nil, err
 	}
@@ -347,6 +348,12 @@ func parseAdd(req parseRequest) (*AddCommand, error) {
 		return nil, err
 	}
 
+	var unpack *bool
+	if _, ok := req.flags.used["unpack"]; ok {
+		b := flUnpack.Value == "true"
+		unpack = &b
+	}
+
 	return &AddCommand{
 		withNameAndCode: newWithNameAndCode(req),
 		SourcesAndDest:  *sourcesAndDest,
@@ -356,6 +363,7 @@ func parseAdd(req parseRequest) (*AddCommand, error) {
 		KeepGitDir:      flKeepGitDir.Value == "true",
 		Checksum:        flChecksum.Value,
 		ExcludePatterns: stringValuesFromFlagIfPossible(flExcludes),
+		Unpack:          unpack,
 	}, nil
 }
 
diff --git a/solver/llbsolver/file/backend.go b/solver/llbsolver/file/backend.go
index 61550b84f..c4a512ffb 100644
--- a/solver/llbsolver/file/backend.go
+++ b/solver/llbsolver/file/backend.go
@@ -255,7 +255,7 @@ func docopy(ctx context.Context, src, dest string, action *pb.FileActionCopy, u
 
 	for _, s := range m {
 		if action.AttemptUnpackDockerCompatibility {
-			if ok, err := unpack(src, s, dest, destPath, ch, timestampToTime(action.Timestamp), idmap); err != nil {
+			if ok, err := unpack(src, s, dest, destPath, ch, u, timestampToTime(action.Timestamp), idmap); err != nil {
 				return errors.WithStack(err)
 			} else if ok {
 				continue
diff --git a/solver/llbsolver/file/unpack.go b/solver/llbsolver/file/unpack.go
index d8698b780..de2770500 100644
--- a/solver/llbsolver/file/unpack.go
+++ b/solver/llbsolver/file/unpack.go
@@ -13,7 +13,7 @@ import (
 	copy "github.com/tonistiigi/fsutil/copy"
 )
 
-func unpack(srcRoot string, src string, destRoot string, dest string, ch copy.Chowner, tm *time.Time, idmap *user.IdentityMapping) (bool, error) {
+func unpack(srcRoot string, src string, destRoot string, dest string, ch copy.Chowner, u *copy.User, tm *time.Time, idmap *user.IdentityMapping) (bool, error) {
 	src, err := fs.RootPath(srcRoot, src)
 	if err != nil {
 		return false, err
@@ -42,6 +42,12 @@ func unpack(srcRoot string, src string, destRoot string, dest string, ch copy.Ch
 	if idmap != nil {
 		opts.IDMap = *idmap
 	}
+	if u != nil {
+		opts.ChownOpts = &archive.ChownOpts{
+			UID: u.UID,
+			GID: u.GID,
+		}
+	}
 	return true, chrootarchive.Untar(file, dest, opts)
 }
 



Test patch:
diff --git a/client/client_test.go b/client/client_test.go
index 04d696405..83cc70dac 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -6883,9 +6883,6 @@ func readFileInImage(ctx context.Context, t *testing.T, c *Client, ref, path str
 }
 
 func testCachedMounts(t *testing.T, sb integration.Sandbox) {
-	// TODO(profnandaa): skipping on Windows, investigating. #5906
-	integration.SkipOnPlatform(t, "windows")
-
 	c, err := New(sb.Context(), sb.Address())
 	require.NoError(t, err)
 	defer c.Close()
diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go
index 1293044f3..0eb3e59ca 100644
--- a/frontend/dockerfile/dockerfile_test.go
+++ b/frontend/dockerfile/dockerfile_test.go
@@ -83,6 +83,7 @@ var allTests = integration.TestFuncs(
 	testDockerfileInvalidCommand,
 	testDockerfileADDFromURL,
 	testDockerfileAddArchive,
+	testDockerfileAddChownArchive,
 	testDockerfileScratchConfig,
 	testExportedHistory,
 	testExportedHistoryFlattenArgs,
@@ -3220,6 +3221,34 @@ ADD t.tar.gz /
 	require.NoError(t, err)
 	require.Equal(t, expectedContent, dt)
 
+	// add with unpack=false
+	dockerfile = fmt.Appendf(nil, `
+	FROM %s
+	ADD --unpack=false t.tar.gz /
+	`, baseImage)
+
+	dir = integration.Tmpdir(
+		t,
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+		fstest.CreateFile("t.tar.gz", buf2.Bytes(), 0600),
+	)
+
+	args, trace = f.DFCmdArgs(dir.Name, dir.Name)
+	defer os.RemoveAll(trace)
+
+	destDir = t.TempDir()
+
+	cmd = sb.Cmd(args + fmt.Sprintf(" --output type=local,dest=%s", destDir))
+	require.NoError(t, cmd.Run())
+
+	_, err = os.Stat(filepath.Join(destDir, "foo"))
+	require.Error(t, err)
+	require.True(t, os.IsNotExist(err))
+
+	dt, err = os.ReadFile(filepath.Join(destDir, "t.tar.gz"))
+	require.NoError(t, err)
+	require.Equal(t, buf2.Bytes(), dt)
+
 	// COPY doesn't extract
 	dockerfile = fmt.Appendf(nil, `
 FROM %s
@@ -3277,6 +3306,29 @@ ADD %s /
 	require.NoError(t, err)
 	require.Equal(t, buf2.Bytes(), dt)
 
+	// ADD from URL with --unpack=true
+	dockerfile = fmt.Appendf(nil, `
+FROM %s
+ADD --unpack=true %s /dest/
+`, baseImage, server.URL+"/t.tar.gz")
+
+	dir = integration.Tmpdir(
+		t,
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+	)
+
+	args, trace = f.DFCmdArgs(dir.Name, dir.Name)
+	defer os.RemoveAll(trace)
+
+	destDir = t.TempDir()
+
+	cmd = sb.Cmd(args + fmt.Sprintf(" --output type=local,dest=%s", destDir))
+	require.NoError(t, cmd.Run())
+
+	dt, err = os.ReadFile(filepath.Join(destDir, "dest/foo"))
+	require.NoError(t, err)
+	require.Equal(t, expectedContent, dt)
+
 	// https://github.com/moby/buildkit/issues/386
 	dockerfile = fmt.Appendf(nil, `
 FROM %s
@@ -3301,6 +3353,70 @@ ADD %s /newname.tar.gz
 	require.Equal(t, buf2.Bytes(), dt)
 }
 
+func testDockerfileAddChownArchive(t *testing.T, sb integration.Sandbox) {
+	integration.SkipOnPlatform(t, "windows")
+	f := getFrontend(t, sb)
+
+	buf := bytes.NewBuffer(nil)
+	tw := tar.NewWriter(buf)
+	content := []byte("content0")
+	err := tw.WriteHeader(&tar.Header{
+		Name:     "foo",
+		Typeflag: tar.TypeReg,
+		Size:     int64(len(content)),
+		Mode:     0644,
+	})
+	require.NoError(t, err)
+	_, err = tw.Write(content)
+	require.NoError(t, err)
+	err = tw.Close()
+	require.NoError(t, err)
+
+	dockerfile := []byte(`
+FROM scratch
+ADD --chown=100:200 t.tar /out/
+`)
+
+	dir := integration.Tmpdir(
+		t,
+		fstest.CreateFile("Dockerfile", dockerfile, 0600),
+		fstest.CreateFile("t.tar", buf.Bytes(), 0600),
+	)
+
+	c, err := client.New(sb.Context(), sb.Address())
+	require.NoError(t, err)
+	defer c.Close()
+
+	outBuf := &bytes.Buffer{}
+	_, err = f.Solve(sb.Context(), c, client.SolveOpt{
+		Exports: []client.ExportEntry{
+			{
+				Type:   client.ExporterTar,
+				Output: fixedWriteCloser(&nopWriteCloser{outBuf}),
+			},
+		},
+		LocalMounts: map[string]fsutil.FS{
+			dockerui.DefaultLocalNameDockerfile: dir,
+			dockerui.DefaultLocalNameContext:    dir,
+		},
+	}, nil)
+	require.NoError(t, err)
+
+	m, err := testutil.ReadTarToMap(outBuf.Bytes(), false)
+	require.NoError(t, err)
+
+	mi, ok := m["out/foo"]
+	require.True(t, ok)
+	require.Equal(t, "content0", string(mi.Data))
+	require.Equal(t, 100, mi.Header.Uid)
+	require.Equal(t, 200, mi.Header.Gid)
+
+	mi, ok = m["out/"]
+	require.True(t, ok)
+	require.Equal(t, 100, mi.Header.Uid)
+	require.Equal(t, 200, mi.Header.Gid)
+}
+
 func testDockerfileAddArchiveWildcard(t *testing.T, sb integration.Sandbox) {
 	f := getFrontend(t, sb)