Browse code

vendor: update buildkit to v0.22.0-rc1

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

Tonis Tiigi authored on 2025/05/15 09:16:15
Showing 153 changed files
... ...
@@ -62,7 +62,7 @@ require (
62 62
 	github.com/miekg/dns v1.1.61
63 63
 	github.com/mistifyio/go-zfs/v3 v3.0.1
64 64
 	github.com/mitchellh/copystructure v1.2.0
65
-	github.com/moby/buildkit v0.21.1
65
+	github.com/moby/buildkit v0.22.0-rc1
66 66
 	github.com/moby/docker-image-spec v1.3.1
67 67
 	github.com/moby/go-archive v0.1.0
68 68
 	github.com/moby/ipvs v1.1.0
... ...
@@ -146,6 +146,7 @@ require (
146 146
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
147 147
 	github.com/cilium/ebpf v0.17.3 // indirect
148 148
 	github.com/container-storage-interface/spec v1.5.0 // indirect
149
+	github.com/containerd/accelerated-container-image v1.2.3 // indirect
149 150
 	github.com/containerd/console v1.0.4 // indirect
150 151
 	github.com/containerd/errdefs/pkg v0.3.0 // indirect
151 152
 	github.com/containerd/go-cni v1.1.12 // indirect
... ...
@@ -208,7 +209,7 @@ require (
208 208
 	github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
209 209
 	github.com/tinylib/msgp v1.1.8 // indirect
210 210
 	github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
211
-	github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 // indirect
211
+	github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
212 212
 	github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f // indirect
213 213
 	github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
214 214
 	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
... ...
@@ -119,6 +119,8 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
119 119
 github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
120 120
 github.com/container-storage-interface/spec v1.5.0 h1:lvKxe3uLgqQeVQcrnL2CPQKISoKjTJxojEs9cBk+HXo=
121 121
 github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
122
+github.com/containerd/accelerated-container-image v1.2.3 h1:tAIoP7Z7b2xGhb7QCM5Fa+2xqWfPqRmyi5lodbsGGRA=
123
+github.com/containerd/accelerated-container-image v1.2.3/go.mod h1:EvKVWor6ZQNUyYp0MZm5hw4k21ropuz7EegM+m/Jb/Q=
122 124
 github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
123 125
 github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
124 126
 github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
... ...
@@ -175,8 +177,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
175 175
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
176 176
 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
177 177
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
178
-github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
179
-github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
178
+github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
179
+github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
180 180
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
181 181
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
182 182
 github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
... ...
@@ -383,8 +385,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
383 383
 github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
384 384
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
385 385
 github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs=
386
-github.com/moby/buildkit v0.21.1 h1:wTjVLfirh7skZt9piaIlNo8WdiPjza1CDl2EArDV9bA=
387
-github.com/moby/buildkit v0.21.1/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw=
386
+github.com/moby/buildkit v0.22.0-rc1 h1:Q47jZZws7+0WhucTcm35NRV8NcO6n1SwIikzfqcGKLo=
387
+github.com/moby/buildkit v0.22.0-rc1/go.mod h1:j4pP5hxiTWcz7xuTK2cyxQislHl/N2WWHzOy43DlLJw=
388 388
 github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
389 389
 github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
390 390
 github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
... ...
@@ -555,8 +557,8 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
555 555
 github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
556 556
 github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
557 557
 github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
558
-github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 h1:mK+ZskNt7SG4dxfKIi27C7qHAQzyjAVt1iyTf0hmsNc=
559
-github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
558
+github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q=
559
+github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
560 560
 github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f h1:q/SWz3Bz0KtAsqaBo73CHVXjaz5O8PDnmD2JHVhgYnE=
561 561
 github.com/tonistiigi/go-actions-cache v0.0.0-20250228231703-3e9a6642607f/go.mod h1:h0oRlVs3NoFIHysRQ4rU1+RG4QmU0M2JVSwTYrB4igk=
562 562
 github.com/tonistiigi/go-archvariant v1.0.0 h1:5LC1eDWiBNflnTF1prCiX09yfNHIxDC/aukdhCdTyb0=
563 563
new file mode 100644
... ...
@@ -0,0 +1,201 @@
0
+                                 Apache License
1
+                           Version 2.0, January 2004
2
+                        http://www.apache.org/licenses/
3
+
4
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5
+
6
+   1. Definitions.
7
+
8
+      "License" shall mean the terms and conditions for use, reproduction,
9
+      and distribution as defined by Sections 1 through 9 of this document.
10
+
11
+      "Licensor" shall mean the copyright owner or entity authorized by
12
+      the copyright owner that is granting the License.
13
+
14
+      "Legal Entity" shall mean the union of the acting entity and all
15
+      other entities that control, are controlled by, or are under common
16
+      control with that entity. For the purposes of this definition,
17
+      "control" means (i) the power, direct or indirect, to cause the
18
+      direction or management of such entity, whether by contract or
19
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+      outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+      "You" (or "Your") shall mean an individual or Legal Entity
23
+      exercising permissions granted by this License.
24
+
25
+      "Source" form shall mean the preferred form for making modifications,
26
+      including but not limited to software source code, documentation
27
+      source, and configuration files.
28
+
29
+      "Object" form shall mean any form resulting from mechanical
30
+      transformation or translation of a Source form, including but
31
+      not limited to compiled object code, generated documentation,
32
+      and conversions to other media types.
33
+
34
+      "Work" shall mean the work of authorship, whether in Source or
35
+      Object form, made available under the License, as indicated by a
36
+      copyright notice that is included in or attached to the work
37
+      (an example is provided in the Appendix below).
38
+
39
+      "Derivative Works" shall mean any work, whether in Source or Object
40
+      form, that is based on (or derived from) the Work and for which the
41
+      editorial revisions, annotations, elaborations, or other modifications
42
+      represent, as a whole, an original work of authorship. For the purposes
43
+      of this License, Derivative Works shall not include works that remain
44
+      separable from, or merely link (or bind by name) to the interfaces of,
45
+      the Work and Derivative Works thereof.
46
+
47
+      "Contribution" shall mean any work of authorship, including
48
+      the original version of the Work and any modifications or additions
49
+      to that Work or Derivative Works thereof, that is intentionally
50
+      submitted to Licensor for inclusion in the Work by the copyright owner
51
+      or by an individual or Legal Entity authorized to submit on behalf of
52
+      the copyright owner. For the purposes of this definition, "submitted"
53
+      means any form of electronic, verbal, or written communication sent
54
+      to the Licensor or its representatives, including but not limited to
55
+      communication on electronic mailing lists, source code control systems,
56
+      and issue tracking systems that are managed by, or on behalf of, the
57
+      Licensor for the purpose of discussing and improving the Work, but
58
+      excluding communication that is conspicuously marked or otherwise
59
+      designated in writing by the copyright owner as "Not a Contribution."
60
+
61
+      "Contributor" shall mean Licensor and any individual or Legal Entity
62
+      on behalf of whom a Contribution has been received by Licensor and
63
+      subsequently incorporated within the Work.
64
+
65
+   2. Grant of Copyright License. Subject to the terms and conditions of
66
+      this License, each Contributor hereby grants to You a perpetual,
67
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
68
+      copyright license to reproduce, prepare Derivative Works of,
69
+      publicly display, publicly perform, sublicense, and distribute the
70
+      Work and such Derivative Works in Source or Object form.
71
+
72
+   3. Grant of Patent License. Subject to the terms and conditions of
73
+      this License, each Contributor hereby grants to You a perpetual,
74
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75
+      (except as stated in this section) patent license to make, have made,
76
+      use, offer to sell, sell, import, and otherwise transfer the Work,
77
+      where such license applies only to those patent claims licensable
78
+      by such Contributor that are necessarily infringed by their
79
+      Contribution(s) alone or by combination of their Contribution(s)
80
+      with the Work to which such Contribution(s) was submitted. If You
81
+      institute patent litigation against any entity (including a
82
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
83
+      or a Contribution incorporated within the Work constitutes direct
84
+      or contributory patent infringement, then any patent licenses
85
+      granted to You under this License for that Work shall terminate
86
+      as of the date such litigation is filed.
87
+
88
+   4. Redistribution. You may reproduce and distribute copies of the
89
+      Work or Derivative Works thereof in any medium, with or without
90
+      modifications, and in Source or Object form, provided that You
91
+      meet the following conditions:
92
+
93
+      (a) You must give any other recipients of the Work or
94
+          Derivative Works a copy of this License; and
95
+
96
+      (b) You must cause any modified files to carry prominent notices
97
+          stating that You changed the files; and
98
+
99
+      (c) You must retain, in the Source form of any Derivative Works
100
+          that You distribute, all copyright, patent, trademark, and
101
+          attribution notices from the Source form of the Work,
102
+          excluding those notices that do not pertain to any part of
103
+          the Derivative Works; and
104
+
105
+      (d) If the Work includes a "NOTICE" text file as part of its
106
+          distribution, then any Derivative Works that You distribute must
107
+          include a readable copy of the attribution notices contained
108
+          within such NOTICE file, excluding those notices that do not
109
+          pertain to any part of the Derivative Works, in at least one
110
+          of the following places: within a NOTICE text file distributed
111
+          as part of the Derivative Works; within the Source form or
112
+          documentation, if provided along with the Derivative Works; or,
113
+          within a display generated by the Derivative Works, if and
114
+          wherever such third-party notices normally appear. The contents
115
+          of the NOTICE file are for informational purposes only and
116
+          do not modify the License. You may add Your own attribution
117
+          notices within Derivative Works that You distribute, alongside
118
+          or as an addendum to the NOTICE text from the Work, provided
119
+          that such additional attribution notices cannot be construed
120
+          as modifying the License.
121
+
122
+      You may add Your own copyright statement to Your modifications and
123
+      may provide additional or different license terms and conditions
124
+      for use, reproduction, or distribution of Your modifications, or
125
+      for any such Derivative Works as a whole, provided Your use,
126
+      reproduction, and distribution of the Work otherwise complies with
127
+      the conditions stated in this License.
128
+
129
+   5. Submission of Contributions. Unless You explicitly state otherwise,
130
+      any Contribution intentionally submitted for inclusion in the Work
131
+      by You to the Licensor shall be under the terms and conditions of
132
+      this License, without any additional terms or conditions.
133
+      Notwithstanding the above, nothing herein shall supersede or modify
134
+      the terms of any separate license agreement you may have executed
135
+      with Licensor regarding such Contributions.
136
+
137
+   6. Trademarks. This License does not grant permission to use the trade
138
+      names, trademarks, service marks, or product names of the Licensor,
139
+      except as required for reasonable and customary use in describing the
140
+      origin of the Work and reproducing the content of the NOTICE file.
141
+
142
+   7. Disclaimer of Warranty. Unless required by applicable law or
143
+      agreed to in writing, Licensor provides the Work (and each
144
+      Contributor provides its Contributions) on an "AS IS" BASIS,
145
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
146
+      implied, including, without limitation, any warranties or conditions
147
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
148
+      PARTICULAR PURPOSE. You are solely responsible for determining the
149
+      appropriateness of using or redistributing the Work and assume any
150
+      risks associated with Your exercise of permissions under this License.
151
+
152
+   8. Limitation of Liability. In no event and under no legal theory,
153
+      whether in tort (including negligence), contract, or otherwise,
154
+      unless required by applicable law (such as deliberate and grossly
155
+      negligent acts) or agreed to in writing, shall any Contributor be
156
+      liable to You for damages, including any direct, indirect, special,
157
+      incidental, or consequential damages of any character arising as a
158
+      result of this License or out of the use or inability to use the
159
+      Work (including but not limited to damages for loss of goodwill,
160
+      work stoppage, computer failure or malfunction, or any and all
161
+      other commercial damages or losses), even if such Contributor
162
+      has been advised of the possibility of such damages.
163
+
164
+   9. Accepting Warranty or Additional Liability. While redistributing
165
+      the Work or Derivative Works thereof, You may choose to offer,
166
+      and charge a fee for, acceptance of support, warranty, indemnity,
167
+      or other liability obligations and/or rights consistent with this
168
+      License. However, in accepting such obligations, You may act only
169
+      on Your own behalf and on Your sole responsibility, not on behalf
170
+      of any other Contributor, and only if You agree to indemnify,
171
+      defend, and hold each Contributor harmless for any liability
172
+      incurred by, or claims asserted against, such Contributor by reason
173
+      of your accepting any such warranty or additional liability.
174
+
175
+   END OF TERMS AND CONDITIONS
176
+
177
+   APPENDIX: How to apply the Apache License to your work.
178
+
179
+      To apply the Apache License to your work, attach the following
180
+      boilerplate notice, with the fields enclosed by brackets "[]"
181
+      replaced with your own identifying information. (Don't include
182
+      the brackets!)  The text should be enclosed in the appropriate
183
+      comment syntax for the file format. We also recommend that a
184
+      file or class name and description of purpose be included on the
185
+      same "printed page" as the copyright notice for easier
186
+      identification within third-party archives.
187
+
188
+   Copyright [yyyy] [name of copyright owner]
189
+
190
+   Licensed under the Apache License, Version 2.0 (the "License");
191
+   you may not use this file except in compliance with the License.
192
+   You may obtain a copy of the License at
193
+
194
+       http://www.apache.org/licenses/LICENSE-2.0
195
+
196
+   Unless required by applicable law or agreed to in writing, software
197
+   distributed under the License is distributed on an "AS IS" BASIS,
198
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
199
+   See the License for the specific language governing permissions and
200
+   limitations under the License.
0 201
\ No newline at end of file
1 202
new file mode 100644
... ...
@@ -0,0 +1,133 @@
0
+/*
1
+   Copyright The Accelerated Container Image Authors
2
+
3
+   Licensed under the Apache License, Version 2.0 (the "License");
4
+   you may not use this file except in compliance with the License.
5
+   You may obtain a copy of the License at
6
+
7
+       http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+   Unless required by applicable law or agreed to in writing, software
10
+   distributed under the License is distributed on an "AS IS" BASIS,
11
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+   See the License for the specific language governing permissions and
13
+   limitations under the License.
14
+*/
15
+
16
+package label
17
+
18
+// support on-demand loading by the labels
19
+const (
20
+	// TargetSnapshotRef is the interface to know that Prepare
21
+	// action is to pull image, not for container Writable snapshot.
22
+	//
23
+	// NOTE: Only available in >= containerd 1.4.0 and containerd.Pull
24
+	// with Unpack option.
25
+	//
26
+	// FIXME(fuweid): With containerd design, we don't know that what purpose
27
+	// snapshotter.Prepare does for. For unpacked image, prepare is for
28
+	// container's rootfs. For pulling image, the prepare is for committed.
29
+	// With label "containerd.io/snapshot.ref" in preparing, snapshotter
30
+	// author will know it is for pulling image. It will be useful.
31
+	//
32
+	// The label is only propagated during pulling image. So, is it possible
33
+	// to propagate by image.Unpack()?
34
+	TargetSnapshotRef = "containerd.io/snapshot.ref"
35
+
36
+	// TargetImageRef is the label to mark where the snapshot comes from.
37
+	//
38
+	// TODO(fuweid): Is it possible to use it in upstream?
39
+	TargetImageRef = "containerd.io/snapshot/image-ref"
40
+
41
+	// OverlayBDBlobDigest is the annotation key in the manifest to
42
+	// describe the digest of blob in OverlayBD format.
43
+	//
44
+	// NOTE: The annotation is part of image layer blob's descriptor.
45
+	OverlayBDBlobDigest = "containerd.io/snapshot/overlaybd/blob-digest"
46
+
47
+	// OverlayBDBlobSize is the annotation key in the manifest to
48
+	// describe the size of blob in OverlayBD format.
49
+	//
50
+	// NOTE: The annotation is part of image layer blob's descriptor.
51
+	OverlayBDBlobSize = "containerd.io/snapshot/overlaybd/blob-size"
52
+
53
+	// OverlayBDBlobFsType is the annotation key in the manifest to
54
+	// describe the filesystem type to be mounted as of blob in OverlayBD format.
55
+	//
56
+	// NOTE: The annotation is part of image layer blob's descriptor.
57
+	OverlayBDBlobFsType = "containerd.io/snapshot/overlaybd/blob-fs-type"
58
+
59
+	// AccelerationLayer is the annotation key in the manifest to indicate
60
+	// whether a top layer is acceleration layer or not.
61
+	AccelerationLayer = "containerd.io/snapshot/overlaybd/acceleration-layer"
62
+
63
+	// RecordTrace tells snapshotter to record trace
64
+	RecordTrace = "containerd.io/snapshot/overlaybd/record-trace"
65
+
66
+	// RecordTracePath is the file path to record trace
67
+	RecordTracePath = "containerd.io/snapshot/overlaybd/record-trace-path"
68
+
69
+	// ZFileConfig is the config of ZFile
70
+	ZFileConfig = "containerd.io/snapshot/overlaybd/zfile-config"
71
+
72
+	// OverlayBD virtual block device size
73
+	OverlayBDVsize = "containerd.io/snapshot/overlaybd/vsize"
74
+
75
+	// CRIImageRef is the image-ref from cri
76
+	CRIImageRef = "containerd.io/snapshot/cri.image-ref"
77
+
78
+	// TurboOCIDigest is the index annotation key for image layer digest
79
+	FastOCIDigest  = "containerd.io/snapshot/overlaybd/fastoci/target-digest" // legacy
80
+	TurboOCIDigest = "containerd.io/snapshot/overlaybd/turbo-oci/target-digest"
81
+
82
+	// TurboOCIMediaType is the index annotation key for image layer media type
83
+	FastOCIMediaType  = "containerd.io/snapshot/overlaybd/fastoci/target-media-type" // legacy
84
+	TurboOCIMediaType = "containerd.io/snapshot/overlaybd/turbo-oci/target-media-type"
85
+
86
+	// DownloadRemoteBlob is a label for download remote blob
87
+	DownloadRemoteBlob = "containerd.io/snapshot/overlaybd/download-remote-blob"
88
+
89
+	RemoteLabel    = "containerd.io/snapshot/remote"
90
+	RemoteLabelVal = "remote snapshot"
91
+
92
+	// OverlayBDVersion is the version number of overlaybd blob
93
+	OverlayBDVersion = "containerd.io/snapshot/overlaybd/version"
94
+
95
+	// LayerToTurboOCI is used to convert local layer to turboOCI with tar index
96
+	LayerToTurboOCI = "containerd.io/snapshot/overlaybd/convert2turbo-oci"
97
+
98
+	SnapshotType = "containerd.io/snapshot/type"
99
+
100
+	// RootfsQuotaLabel sets container rootfs diskquota
101
+	RootfsQuotaLabel = "containerd.io/snapshot/disk_quota"
102
+)
103
+
104
+// used in filterAnnotationsForSave (https://github.com/moby/buildkit/blob/v0.11/cache/refs.go#L882)
105
+var OverlayBDAnnotations = []string{
106
+	LocalOverlayBDPath,
107
+	OverlayBDBlobDigest,
108
+	OverlayBDBlobSize,
109
+	OverlayBDBlobFsType,
110
+}
111
+
112
+// interface
113
+const (
114
+	// SupportReadWriteMode is used to support writable block device
115
+	// for active snapshotter.
116
+	//
117
+	// By default, multiple active snapshotters can share one block device
118
+	// from parent snapshotter(committed). Like image builder and
119
+	// sandboxed-like container runtime(KataContainer, Firecracker), those
120
+	// cases want to use the block device alone or as writable.
121
+	// There are two ways to provide writable devices:
122
+	//  - 'dir' mark the snapshotter
123
+	//    as wriable block device and mount it on rootfs.
124
+	//  - 'dev' mark the snapshotter
125
+	//    as wriable block device without mount.
126
+	SupportReadWriteMode = "containerd.io/snapshot/overlaybd.writable"
127
+
128
+	// LocalOverlayBDPath is used to export the commit file path.
129
+	//
130
+	// NOTE: Only used in image build.
131
+	LocalOverlayBDPath = "containerd.io/snapshot/overlaybd.localcommitpath"
132
+)
0 133
new file mode 100644
... ...
@@ -0,0 +1,45 @@
0
+/*
1
+   Copyright The Accelerated Container Image Authors
2
+
3
+   Licensed under the Apache License, Version 2.0 (the "License");
4
+   you may not use this file except in compliance with the License.
5
+   You may obtain a copy of the License at
6
+
7
+       http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+   Unless required by applicable law or agreed to in writing, software
10
+   distributed under the License is distributed on an "AS IS" BASIS,
11
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+   See the License for the specific language governing permissions and
13
+   limitations under the License.
14
+*/
15
+
16
+package types
17
+
18
+// OverlayBDBSConfig is the config of overlaybd target.
19
+type OverlayBDBSConfig struct {
20
+	RepoBlobURL       string                   `json:"repoBlobUrl"`
21
+	Lowers            []OverlayBDBSConfigLower `json:"lowers"`
22
+	Upper             OverlayBDBSConfigUpper   `json:"upper"`
23
+	ResultFile        string                   `json:"resultFile"`
24
+	AccelerationLayer bool                     `json:"accelerationLayer,omitempty"`
25
+	RecordTracePath   string                   `json:"recordTracePath,omitempty"`
26
+}
27
+
28
+// OverlayBDBSConfigLower
29
+type OverlayBDBSConfigLower struct {
30
+	GzipIndex    string `json:"gzipIndex,omitempty"`
31
+	File         string `json:"file,omitempty"`
32
+	Digest       string `json:"digest,omitempty"`
33
+	TargetFile   string `json:"targetFile,omitempty"`
34
+	TargetDigest string `json:"targetDigest,omitempty"`
35
+	Size         int64  `json:"size,omitempty"`
36
+	Dir          string `json:"dir,omitempty"`
37
+}
38
+
39
+type OverlayBDBSConfigUpper struct {
40
+	Index     string `json:"index,omitempty"`
41
+	Data      string `json:"data,omitempty"`
42
+	Target    string `json:"target,omitempty"`
43
+	GzipIndex string `json:"gzipIndex,omitempty"`
44
+}
0 45
new file mode 100644
... ...
@@ -0,0 +1,258 @@
0
+/*
1
+   Copyright The Accelerated Container Image Authors
2
+
3
+   Licensed under the Apache License, Version 2.0 (the "License");
4
+   you may not use this file except in compliance with the License.
5
+   You may obtain a copy of the License at
6
+
7
+       http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+   Unless required by applicable law or agreed to in writing, software
10
+   distributed under the License is distributed on an "AS IS" BASIS,
11
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+   See the License for the specific language governing permissions and
13
+   limitations under the License.
14
+*/
15
+
16
+package utils
17
+
18
+import (
19
+	"context"
20
+	"encoding/json"
21
+	"fmt"
22
+	"os"
23
+	"os/exec"
24
+	"path"
25
+	"path/filepath"
26
+	"strings"
27
+
28
+	sn "github.com/containerd/accelerated-container-image/pkg/types"
29
+	"github.com/containerd/log"
30
+	"github.com/pkg/errors"
31
+)
32
+
33
+const (
34
+	obdBinCreate        = "/opt/overlaybd/bin/overlaybd-create"
35
+	obdBinCommit        = "/opt/overlaybd/bin/overlaybd-commit"
36
+	obdBinApply         = "/opt/overlaybd/bin/overlaybd-apply"
37
+	obdBinTurboOCIApply = "/opt/overlaybd/bin/turboOCI-apply"
38
+
39
+	dataFile       = "writable_data"
40
+	idxFile        = "writable_index"
41
+	sealedFile     = "overlaybd.sealed"
42
+	commitTempFile = "overlaybd.commit.temp"
43
+	commitFile     = "overlaybd.commit"
44
+)
45
+
46
+type ConvertOption struct {
47
+	// src options
48
+	// (TODO) LayerPath   string // path of layer.tgz or layer.tar
49
+	TarMetaPath string // path of layer.tar.meta
50
+
51
+	Config  sn.OverlayBDBSConfig
52
+	Workdir string
53
+
54
+	// output options
55
+	Ext4FSMetaPath string
56
+	// (TODO) GzipIndexPath string
57
+}
58
+
59
+var defaultServiceTemplate = `
60
+{
61
+	"registryFsVersion": "v2",
62
+	"logPath": "",
63
+	"logLevel": 1,
64
+	"cacheConfig": {
65
+		"cacheType": "file",
66
+		"cacheDir": "%s",
67
+		"cacheSizeGB": 4
68
+	},
69
+	"gzipCacheConfig": {
70
+		"enable": false
71
+	},
72
+	"credentialConfig": {
73
+		"mode": "file",
74
+		"path": ""
75
+	},
76
+	"ioEngine": 0,
77
+	"download": {
78
+		"enable": false
79
+	},
80
+	"enableAudit": false
81
+}
82
+`
83
+
84
+func Create(ctx context.Context, dir string, opts ...string) error {
85
+	dataPath := path.Join(dir, dataFile)
86
+	indexPath := path.Join(dir, idxFile)
87
+	os.RemoveAll(dataPath)
88
+	os.RemoveAll(indexPath)
89
+	args := append([]string{dataPath, indexPath}, opts...)
90
+	log.G(ctx).Debugf("%s %s", obdBinCreate, strings.Join(args, " "))
91
+	out, err := exec.CommandContext(ctx, obdBinCreate, args...).CombinedOutput()
92
+	if err != nil {
93
+		return errors.Wrapf(err, "failed to overlaybd-create: %s", out)
94
+	}
95
+	return nil
96
+}
97
+
98
+func Seal(ctx context.Context, dir, toDir string, opts ...string) error {
99
+	args := append([]string{
100
+		"--seal",
101
+		path.Join(dir, dataFile),
102
+		path.Join(dir, idxFile),
103
+	}, opts...)
104
+	log.G(ctx).Debugf("%s %s", obdBinCommit, strings.Join(args, " "))
105
+	out, err := exec.CommandContext(ctx, obdBinCommit, args...).CombinedOutput()
106
+	if err != nil {
107
+		return errors.Wrapf(err, "failed to seal writable overlaybd: %s", out)
108
+	}
109
+	if err := os.Rename(path.Join(dir, dataFile), path.Join(toDir, sealedFile)); err != nil {
110
+		return errors.Wrapf(err, "failed to rename sealed overlaybd file")
111
+	}
112
+	os.RemoveAll(path.Join(dir, idxFile))
113
+	return nil
114
+}
115
+
116
+func Commit(ctx context.Context, dir, toDir string, sealed bool, opts ...string) error {
117
+	var args []string
118
+	if sealed {
119
+		args = append([]string{
120
+			"--commit_sealed",
121
+			path.Join(dir, sealedFile),
122
+			path.Join(toDir, commitTempFile),
123
+		}, opts...)
124
+	} else {
125
+		args = append([]string{
126
+			path.Join(dir, dataFile),
127
+			path.Join(dir, idxFile),
128
+			path.Join(toDir, commitFile),
129
+		}, opts...)
130
+	}
131
+	log.G(ctx).Debugf("%s %s", obdBinCommit, strings.Join(args, " "))
132
+	out, err := exec.CommandContext(ctx, obdBinCommit, args...).CombinedOutput()
133
+	if err != nil {
134
+		return errors.Wrapf(err, "failed to overlaybd-commit: %s", out)
135
+	}
136
+	if sealed {
137
+		return os.Rename(path.Join(toDir, commitTempFile), path.Join(toDir, commitFile))
138
+	}
139
+	return nil
140
+}
141
+
142
+func ApplyOverlaybd(ctx context.Context, dir string, opts ...string) error {
143
+
144
+	args := append([]string{
145
+		path.Join(dir, "layer.tar"),
146
+		path.Join(dir, "config.json")}, opts...)
147
+	log.G(ctx).Debugf("%s %s", obdBinApply, strings.Join(args, " "))
148
+	out, err := exec.CommandContext(ctx, obdBinApply, args...).CombinedOutput()
149
+	if err != nil {
150
+		return errors.Wrapf(err, "failed to overlaybd-apply[native]: %s", out)
151
+	}
152
+	return nil
153
+}
154
+
155
+func ApplyTurboOCI(ctx context.Context, dir, gzipMetaFile string, opts ...string) error {
156
+
157
+	args := append([]string{
158
+		path.Join(dir, "layer.tar"),
159
+		path.Join(dir, "config.json"),
160
+		"--gz_index_path", path.Join(dir, gzipMetaFile)}, opts...)
161
+	log.G(ctx).Debugf("%s %s", obdBinApply, strings.Join(args, " "))
162
+	out, err := exec.CommandContext(ctx, obdBinTurboOCIApply, args...).CombinedOutput()
163
+	if err != nil {
164
+		return errors.Wrapf(err, "failed to overlaybd-apply[turboOCI]: %s", out)
165
+	}
166
+	return nil
167
+}
168
+
169
+func GenerateTarMeta(ctx context.Context, srcTarFile string, dstTarMeta string) error {
170
+
171
+	if _, err := os.Stat(srcTarFile); os.IsNotExist(err) {
172
+		return nil
173
+	} else if err != nil {
174
+		return fmt.Errorf("error stating tar file: %w", err)
175
+	}
176
+	log.G(ctx).Infof("generate layer meta for %s", srcTarFile)
177
+	if err := exec.Command(obdBinTurboOCIApply, srcTarFile, dstTarMeta, "--export").Run(); err != nil {
178
+		return fmt.Errorf("failed to convert tar file to overlaybd device: %w", err)
179
+	}
180
+	return nil
181
+}
182
+
183
+// ConvertLayer produce a turbooci layer, target is path of ext4.fs.meta
184
+func ConvertLayer(ctx context.Context, opt *ConvertOption, fs_type string) error {
185
+	if opt.Workdir == "" {
186
+		opt.Workdir = "tmp_conv"
187
+	}
188
+
189
+	if err := os.MkdirAll(opt.Workdir, 0755); err != nil {
190
+		return fmt.Errorf("failed to create work dir: %w", err)
191
+	}
192
+
193
+	pathWritableData := filepath.Join(opt.Workdir, "writable_data")
194
+	pathWritableIndex := filepath.Join(opt.Workdir, "writable_index")
195
+	pathFakeTarget := filepath.Join(opt.Workdir, "fake_target")
196
+	pathService := filepath.Join(opt.Workdir, "service.json")
197
+	pathConfig := filepath.Join(opt.Workdir, "config.v1.json")
198
+
199
+	// overlaybd-create
200
+	args := []string{pathWritableData, pathWritableIndex, "256", "-s", "--turboOCI"}
201
+	if fs_type != "erofs" && len(opt.Config.Lowers) == 0 {
202
+		args = append(args, "--mkfs")
203
+	}
204
+	if out, err := exec.CommandContext(ctx, obdBinCreate, args...).CombinedOutput(); err != nil {
205
+		return fmt.Errorf("failed to overlaybd-create: %w, output: %s", err, out)
206
+	}
207
+	file, err := os.Create(pathFakeTarget)
208
+	if err != nil {
209
+		return fmt.Errorf("failed to create fake target: %w", err)
210
+	}
211
+	file.Close()
212
+	opt.Config.Upper = sn.OverlayBDBSConfigUpper{
213
+		Data:   pathWritableData,
214
+		Index:  pathWritableIndex,
215
+		Target: pathFakeTarget,
216
+	}
217
+
218
+	// turboOCI-apply
219
+	if err := os.WriteFile(pathService, []byte(fmt.Sprintf(defaultServiceTemplate,
220
+		filepath.Join(opt.Workdir, "cache"))), 0644,
221
+	); err != nil {
222
+		return fmt.Errorf("failed to write service.json: %w", err)
223
+	}
224
+	configBytes, err := json.Marshal(opt.Config)
225
+	if err != nil {
226
+		return fmt.Errorf("failed to marshal overlaybd config: %w", err)
227
+	}
228
+	if err := os.WriteFile(pathConfig, configBytes, 0644); err != nil {
229
+		return fmt.Errorf("failed to write overlaybd config: %w", err)
230
+	}
231
+	args = []string{
232
+		opt.TarMetaPath, pathConfig,
233
+		"--service_config_path", pathService,
234
+		"--fstype", fs_type,
235
+	}
236
+	if fs_type != "erofs" {
237
+		args = append(args, "--import")
238
+	}
239
+
240
+	log.G(ctx).Debugf("%s %s", obdBinTurboOCIApply, strings.Join(args, " "))
241
+	if out, err := exec.CommandContext(ctx, obdBinTurboOCIApply,
242
+		args...,
243
+	).CombinedOutput(); err != nil {
244
+		return fmt.Errorf("failed to turboOCI-apply: %w, output: %s", err, out)
245
+	}
246
+
247
+	// overlaybd-commit
248
+	if out, err := exec.CommandContext(ctx, obdBinCommit,
249
+		pathWritableData,
250
+		pathWritableIndex,
251
+		opt.Ext4FSMetaPath,
252
+		"-z", "--turboOCI",
253
+	).CombinedOutput(); err != nil {
254
+		return fmt.Errorf("failed to overlaybd-commit: %w, output: %s", err, out)
255
+	}
256
+	return nil
257
+}
0 258
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+//go:build !windows
1
+
2
+/*
3
+   Copyright The Accelerated Container Image Authors
4
+
5
+   Licensed under the Apache License, Version 2.0 (the "License");
6
+   you may not use this file except in compliance with the License.
7
+   You may obtain a copy of the License at
8
+
9
+       http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+   Unless required by applicable law or agreed to in writing, software
12
+   distributed under the License is distributed on an "AS IS" BASIS,
13
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+   See the License for the specific language governing permissions and
15
+   limitations under the License.
16
+*/
17
+
18
+// Based on https://github.com/containerd/continuity/blob/main/fs/du_unix.go
19
+// Used to calculate the usage of the block dir, excluding the block/mountpoint dir.
20
+
21
+package utils
22
+
23
+import (
24
+	"context"
25
+	"os"
26
+	"path/filepath"
27
+	"syscall"
28
+
29
+	"github.com/containerd/continuity/fs"
30
+)
31
+
32
+const blocksUnitSize = 512
33
+
34
+type inode struct {
35
+	dev, ino uint64
36
+}
37
+
38
+func newInode(stat *syscall.Stat_t) inode {
39
+	return inode{
40
+		dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
41
+		ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
42
+	}
43
+}
44
+
45
+func DiskUsageWithoutMountpoint(ctx context.Context, roots ...string) (fs.Usage, error) {
46
+	var (
47
+		size   int64
48
+		inodes = map[inode]struct{}{} // expensive!
49
+	)
50
+
51
+	for _, root := range roots {
52
+		if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
53
+			if fi.Name() == "mountpoint" {
54
+				return filepath.SkipDir
55
+			}
56
+			if err != nil {
57
+				return err
58
+			}
59
+
60
+			select {
61
+			case <-ctx.Done():
62
+				return ctx.Err()
63
+			default:
64
+			}
65
+			stat := fi.Sys().(*syscall.Stat_t)
66
+			inoKey := newInode(stat)
67
+			if _, ok := inodes[inoKey]; !ok {
68
+				inodes[inoKey] = struct{}{}
69
+				size += stat.Blocks * blocksUnitSize
70
+			}
71
+
72
+			return nil
73
+		}); err != nil {
74
+			return fs.Usage{}, err
75
+		}
76
+	}
77
+
78
+	return fs.Usage{
79
+		Inodes: int64(len(inodes)),
80
+		Size:   size,
81
+	}, nil
82
+}
... ...
@@ -1,13 +1,19 @@
1 1
 package cache
2 2
 
3 3
 import (
4
+	"bufio"
4 5
 	"context"
5 6
 	"fmt"
7
+	"io"
6 8
 	"maps"
7 9
 	"os"
10
+	"path"
8 11
 	"slices"
9 12
 	"strconv"
10 13
 
14
+	obdlabel "github.com/containerd/accelerated-container-image/pkg/label"
15
+	obdcmd "github.com/containerd/accelerated-container-image/pkg/utils"
16
+	"github.com/containerd/containerd/v2/core/content"
11 17
 	"github.com/containerd/containerd/v2/core/diff"
12 18
 	"github.com/containerd/containerd/v2/core/leases"
13 19
 	"github.com/containerd/containerd/v2/core/mount"
... ...
@@ -70,7 +76,6 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool
70 70
 	switch sr.kind() {
71 71
 	case Merge:
72 72
 		for _, parent := range sr.mergeParents {
73
-			parent := parent
74 73
 			eg.Go(func() error {
75 74
 				return computeBlobChain(ctx, parent, createIfNeeded, comp, s, filter)
76 75
 			})
... ...
@@ -109,7 +114,6 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool
109 109
 				}()
110 110
 
111 111
 				compressorFunc, finalize := comp.Type.Compress(ctx, comp)
112
-				mediaType := comp.Type.MediaType()
113 112
 
114 113
 				var lowerRef *immutableRef
115 114
 				switch sr.kind() {
... ...
@@ -181,6 +185,21 @@ func computeBlobChain(ctx context.Context, sr *immutableRef, createIfNeeded bool
181 181
 						enableOverlay = false
182 182
 					}
183 183
 				}
184
+
185
+				mediaType := comp.Type.MediaType()
186
+				if sr.cm.Snapshotter.Name() == "overlaybd" {
187
+					snStat, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID())
188
+					if err != nil {
189
+						return nil, errors.Wrapf(err, "failed to Stat overlaybd")
190
+					}
191
+					if bdPath := snStat.Labels[obdlabel.LocalOverlayBDPath]; bdPath != "" {
192
+						if err := commitOverlayBD(ctx, sr, &desc); err != nil {
193
+							return nil, err
194
+						}
195
+						mediaType = desc.MediaType
196
+						enableOverlay = false
197
+					}
198
+				}
184 199
 				if enableOverlay {
185 200
 					computed, ok, err := sr.tryComputeOverlayBlob(ctx, lower, upper, mediaType, sr.ID(), compressorFunc)
186 201
 					if !ok || err != nil {
... ...
@@ -351,7 +370,7 @@ func (sr *immutableRef) computeChainMetadata(ctx context.Context, filter map[str
351 351
 		}
352 352
 		diffID := sr.getDiffID()
353 353
 		chainID = diffID
354
-		blobChainID = imagespecidentity.ChainID([]digest.Digest{digest.Digest(sr.getBlob()), diffID})
354
+		blobChainID = imagespecidentity.ChainID([]digest.Digest{sr.getBlob(), diffID})
355 355
 	case Layer:
356 356
 		if _, ok := filter[sr.ID()]; !ok {
357 357
 			return nil
... ...
@@ -368,9 +387,9 @@ func (sr *immutableRef) computeChainMetadata(ctx context.Context, filter map[str
368 368
 				return errors.Errorf("failed to set blobchain for reference with non-addressable parent %q", sr.layerParent.GetDescription())
369 369
 			}
370 370
 		}
371
-		diffID := digest.Digest(sr.getDiffID())
371
+		diffID := sr.getDiffID()
372 372
 		chainID = imagespecidentity.ChainID([]digest.Digest{chainID, diffID})
373
-		blobID := imagespecidentity.ChainID([]digest.Digest{digest.Digest(sr.getBlob()), diffID})
373
+		blobID := imagespecidentity.ChainID([]digest.Digest{sr.getBlob(), diffID})
374 374
 		blobChainID = imagespecidentity.ChainID([]digest.Digest{blobChainID, blobID})
375 375
 	case Merge:
376 376
 		baseInput := sr.mergeParents[0]
... ...
@@ -386,9 +405,9 @@ func (sr *immutableRef) computeChainMetadata(ctx context.Context, filter map[str
386 386
 					// not enough information to compute chain at this time
387 387
 					return nil
388 388
 				}
389
-				diffID := digest.Digest(layer.getDiffID())
389
+				diffID := layer.getDiffID()
390 390
 				chainID = imagespecidentity.ChainID([]digest.Digest{chainID, diffID})
391
-				blobID := imagespecidentity.ChainID([]digest.Digest{digest.Digest(layer.getBlob()), diffID})
391
+				blobID := imagespecidentity.ChainID([]digest.Digest{layer.getBlob(), diffID})
392 392
 				blobChainID = imagespecidentity.ChainID([]digest.Digest{blobChainID, blobID})
393 393
 			}
394 394
 		}
... ...
@@ -397,7 +416,7 @@ func (sr *immutableRef) computeChainMetadata(ctx context.Context, filter map[str
397 397
 			// this diff is its own blob
398 398
 			diffID := sr.getDiffID()
399 399
 			chainID = diffID
400
-			blobChainID = imagespecidentity.ChainID([]digest.Digest{digest.Digest(sr.getBlob()), diffID})
400
+			blobChainID = imagespecidentity.ChainID([]digest.Digest{sr.getBlob(), diffID})
401 401
 		} else {
402 402
 			// re-using upper blob
403 403
 			chainID = sr.diffParents.upper.getChainID()
... ...
@@ -498,3 +517,50 @@ func ensureCompression(ctx context.Context, ref *immutableRef, comp compression.
498 498
 	}
499 499
 	return nil
500 500
 }
501
+
502
+func commitOverlayBD(ctx context.Context, sr *immutableRef, desc *ocispecs.Descriptor) error {
503
+	snStat, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID())
504
+	if err != nil {
505
+		return errors.Wrapf(err, "failed to Stat overlaybd")
506
+	}
507
+	bdPath := snStat.Labels[obdlabel.LocalOverlayBDPath]
508
+	if bdPath == "" {
509
+		return errors.New("missing overlaybd path label")
510
+	}
511
+	dir := path.Dir(bdPath)
512
+	commitPath := path.Join(dir, "overlaybd.commit")
513
+	err = obdcmd.Commit(ctx, dir, dir, true, "-t", "-z", "-f")
514
+	if err != nil {
515
+		return errors.Wrapf(err, "failed to overlaybd-commit")
516
+	}
517
+	cw, err := sr.cm.ContentStore.Writer(ctx, content.WithRef(sr.ID()))
518
+	if err != nil {
519
+		return errors.Wrapf(err, "failed to open writer")
520
+	}
521
+	fi, err := os.Open(commitPath)
522
+	if err != nil {
523
+		return errors.Wrapf(err, "failed to open overlaybd commit file")
524
+	}
525
+	sz, err := io.Copy(cw, bufio.NewReader(fi))
526
+	if err != nil {
527
+		return errors.Wrapf(err, "failed to do io.Copy()")
528
+	}
529
+	dgst := cw.Digest()
530
+	labels := map[string]string{
531
+		labels.LabelUncompressed:     dgst.String(),
532
+		obdlabel.OverlayBDBlobDigest: dgst.String(),
533
+		obdlabel.OverlayBDBlobSize:   fmt.Sprintf("%d", sz),
534
+	}
535
+	err = cw.Commit(ctx, sz, dgst, content.WithLabels(labels))
536
+	if err != nil {
537
+		return errors.Wrapf(err, "failed to do cw.Commit")
538
+	}
539
+	desc.Digest = dgst
540
+	desc.Size = sz
541
+	desc.MediaType = ocispecs.MediaTypeImageLayer
542
+	desc.Annotations = map[string]string{
543
+		obdlabel.OverlayBDBlobDigest: string(desc.Digest),
544
+		obdlabel.OverlayBDBlobSize:   fmt.Sprintf("%d", desc.Size),
545
+	}
546
+	return nil
547
+}
... ...
@@ -45,7 +45,7 @@ func (sr *immutableRef) tryComputeOverlayBlob(ctx context.Context, lower, upper
45 45
 
46 46
 	defer func() {
47 47
 		if cw != nil {
48
-			ctx = context.WithoutCancel(ctx)
48
+			ctx := context.WithoutCancel(ctx)
49 49
 			// after commit success cw will be set to nil, if cw isn't nil, error
50 50
 			// happened before commit, we should abort this ingest, and because the
51 51
 			// error may incured by ctx cancel, use a new context here. And since
... ...
@@ -399,7 +399,7 @@ func (cc *cacheContext) HandleChange(kind fsutil.ChangeKind, p string, fi os.Fil
399 399
 			for _, l := range links {
400 400
 				pp := convertKeyToPath(l)
401 401
 				cc.txn.Insert(l, cr)
402
-				d := path.Dir(string(pp))
402
+				d := path.Dir(pp)
403 403
 				if d == "/" {
404 404
 					d = ""
405 405
 				}
... ...
@@ -569,8 +569,8 @@ func (cc *cacheContext) includedPaths(ctx context.Context, m *mount, p string, o
569 569
 		//
570 570
 		// When wildcards are enabled, this translation applies to the
571 571
 		// portion of 'p' before any wildcards.
572
-		if strings.HasPrefix(fn, resolvedPrefix) {
573
-			fn = origPrefix + strings.TrimPrefix(fn, resolvedPrefix)
572
+		if after, ok := strings.CutPrefix(fn, resolvedPrefix); ok {
573
+			fn = origPrefix + after
574 574
 		}
575 575
 
576 576
 		for len(parentDirHeaders) != 0 {
... ...
@@ -774,11 +774,11 @@ func splitWildcards(p string) (d1, d2 string) {
774 774
 
775 775
 func containsWildcards(name string) bool {
776 776
 	for i := 0; i < len(name); i++ {
777
-		ch := name[i]
778
-		if ch == '\\' {
779
-			i++
780
-		} else if ch == '*' || ch == '?' || ch == '[' {
777
+		switch name[i] {
778
+		case '*', '?', '[':
781 779
 			return true
780
+		case '\\':
781
+			i++
782 782
 		}
783 783
 	}
784 784
 	return false
... ...
@@ -887,10 +887,7 @@ func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node[*CacheRe
887 887
 		iter.SeekLowerBound(append(slices.Clone(next), 0))
888 888
 		subk := next
889 889
 		ok := true
890
-		for {
891
-			if !ok || !bytes.HasPrefix(subk, next) {
892
-				break
893
-			}
890
+		for ok && bytes.HasPrefix(subk, next) {
894 891
 			h.Write(bytes.TrimPrefix(subk, k))
895 892
 
896 893
 			// We do not follow trailing links when checksumming a directory's
... ...
@@ -93,7 +93,7 @@ type statInfo struct {
93 93
 }
94 94
 
95 95
 func (s *statInfo) Name() string {
96
-	return filepath.Base(s.Stat.Path)
96
+	return filepath.Base(s.Path)
97 97
 }
98 98
 
99 99
 func (s *statInfo) Size() int64 {
... ...
@@ -3,7 +3,7 @@ package contenthash
3 3
 import (
4 4
 	"archive/tar"
5 5
 	"io"
6
-	"sort"
6
+	"slices"
7 7
 	"strconv"
8 8
 	"strings"
9 9
 )
... ...
@@ -49,14 +49,13 @@ func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
49 49
 	// Get extended attributes.
50 50
 	xAttrKeys := make([]string, 0, len(h.PAXRecords))
51 51
 	for k := range pax {
52
-		if strings.HasPrefix(k, "SCHILY.xattr.") {
53
-			k = strings.TrimPrefix(k, "SCHILY.xattr.")
52
+		if k, ok := strings.CutPrefix(k, "SCHILY.xattr."); ok {
54 53
 			if k == "security.capability" || !strings.HasPrefix(k, "security.") && !strings.HasPrefix(k, "system.") {
55 54
 				xAttrKeys = append(xAttrKeys, k)
56 55
 			}
57 56
 		}
58 57
 	}
59
-	sort.Strings(xAttrKeys)
58
+	slices.Sort(xAttrKeys)
60 59
 
61 60
 	// Make the slice with enough capacity to hold the 11 basic headers
62 61
 	// we want from the v0 selector plus however many xattrs we have.
... ...
@@ -7,7 +7,7 @@ import (
7 7
 	"fmt"
8 8
 	"io"
9 9
 	"path"
10
-	"sort"
10
+	"slices"
11 11
 
12 12
 	cdcompression "github.com/containerd/containerd/v2/pkg/archive/compression"
13 13
 	"github.com/moby/buildkit/session"
... ...
@@ -69,7 +69,7 @@ func (sr *immutableRef) FileList(ctx context.Context, s session.Group) ([]string
69 69
 			name := path.Clean(hdr.Name)
70 70
 			files = append(files, name)
71 71
 		}
72
-		sort.Strings(files)
72
+		slices.Sort(files)
73 73
 
74 74
 		dt, err = json.Marshal(files)
75 75
 		if err != nil {
... ...
@@ -1,17 +1,20 @@
1 1
 package cache
2 2
 
3 3
 import (
4
+	"cmp"
4 5
 	"context"
5 6
 	"fmt"
6 7
 	"maps"
7
-	"sort"
8
+	"slices"
8 9
 	"strings"
9 10
 	"sync"
10 11
 	"time"
11 12
 
13
+	obdlabel "github.com/containerd/accelerated-container-image/pkg/label"
12 14
 	"github.com/containerd/containerd/v2/core/content"
13 15
 	"github.com/containerd/containerd/v2/core/diff"
14 16
 	"github.com/containerd/containerd/v2/core/leases"
17
+	"github.com/containerd/containerd/v2/core/snapshots"
15 18
 	"github.com/containerd/containerd/v2/pkg/filters"
16 19
 	"github.com/containerd/containerd/v2/pkg/gc"
17 20
 	"github.com/containerd/containerd/v2/pkg/labels"
... ...
@@ -627,6 +630,10 @@ func (cm *cacheManager) New(ctx context.Context, s ImmutableRef, sess session.Gr
627 627
 		}); rerr != nil {
628 628
 			return nil, rerr
629 629
 		}
630
+	} else if cm.Snapshotter.Name() == "overlaybd" && parent != nil {
631
+		// Snapshotter will create a R/W block device directly as rootfs with this label
632
+		rwLabels := map[string]string{obdlabel.SupportReadWriteMode: "dev"}
633
+		err = cm.Snapshotter.Prepare(ctx, snapshotID, parentSnapshotID, snapshots.WithLabels(rwLabels))
630 634
 	} else {
631 635
 		err = cm.Snapshotter.Prepare(ctx, snapshotID, parentSnapshotID)
632 636
 	}
... ...
@@ -1426,10 +1433,7 @@ func (cm *cacheManager) DiskUsage(ctx context.Context, opt client.DiskUsageInfo)
1426 1426
 	}
1427 1427
 	cm.mu.Unlock()
1428 1428
 
1429
-	for {
1430
-		if len(rescan) == 0 {
1431
-			break
1432
-		}
1429
+	for len(rescan) != 0 {
1433 1430
 		for id := range rescan {
1434 1431
 			v := m[id]
1435 1432
 			if v.refs == 0 {
... ...
@@ -1667,23 +1671,23 @@ type deleteRecord struct {
1667 1667
 	*cacheRecord
1668 1668
 	lastUsedAt      *time.Time
1669 1669
 	usageCount      int
1670
-	lastUsedAtIndex int
1671
-	usageCountIndex int
1670
+	lastUsedAtIndex float64
1671
+	usageCountIndex float64
1672 1672
 	released        bool
1673 1673
 }
1674 1674
 
1675 1675
 func sortDeleteRecords(toDelete []*deleteRecord) {
1676
-	sort.Slice(toDelete, func(i, j int) bool {
1677
-		if toDelete[i].lastUsedAt == nil {
1678
-			return true
1676
+	slices.SortFunc(toDelete, func(a, b *deleteRecord) int {
1677
+		if a.lastUsedAt == nil {
1678
+			return -1
1679 1679
 		}
1680
-		if toDelete[j].lastUsedAt == nil {
1681
-			return false
1680
+		if b.lastUsedAt == nil {
1681
+			return 1
1682 1682
 		}
1683
-		return toDelete[i].lastUsedAt.Before(*toDelete[j].lastUsedAt)
1683
+		return a.lastUsedAt.Compare(*b.lastUsedAt)
1684 1684
 	})
1685 1685
 
1686
-	maxLastUsedIndex := 0
1686
+	maxLastUsedIndex := 1.0
1687 1687
 	var val time.Time
1688 1688
 	for _, v := range toDelete {
1689 1689
 		if v.lastUsedAt != nil && v.lastUsedAt.After(val) {
... ...
@@ -1693,11 +1697,11 @@ func sortDeleteRecords(toDelete []*deleteRecord) {
1693 1693
 		v.lastUsedAtIndex = maxLastUsedIndex
1694 1694
 	}
1695 1695
 
1696
-	sort.Slice(toDelete, func(i, j int) bool {
1697
-		return toDelete[i].usageCount < toDelete[j].usageCount
1696
+	slices.SortFunc(toDelete, func(a, b *deleteRecord) int {
1697
+		return a.usageCount - b.usageCount
1698 1698
 	})
1699 1699
 
1700
-	maxUsageCountIndex := 0
1700
+	maxUsageCountIndex := 1.0
1701 1701
 	var count int
1702 1702
 	for _, v := range toDelete {
1703 1703
 		if v.usageCount != count {
... ...
@@ -1707,11 +1711,11 @@ func sortDeleteRecords(toDelete []*deleteRecord) {
1707 1707
 		v.usageCountIndex = maxUsageCountIndex
1708 1708
 	}
1709 1709
 
1710
-	sort.Slice(toDelete, func(i, j int) bool {
1711
-		return float64(toDelete[i].lastUsedAtIndex)/float64(maxLastUsedIndex)+
1712
-			float64(toDelete[i].usageCountIndex)/float64(maxUsageCountIndex) <
1713
-			float64(toDelete[j].lastUsedAtIndex)/float64(maxLastUsedIndex)+
1714
-				float64(toDelete[j].usageCountIndex)/float64(maxUsageCountIndex)
1710
+	slices.SortFunc(toDelete, func(a, b *deleteRecord) int {
1711
+		return cmp.Compare(
1712
+			a.lastUsedAtIndex/maxLastUsedIndex+a.usageCountIndex/maxUsageCountIndex,
1713
+			b.lastUsedAtIndex/maxLastUsedIndex+b.usageCountIndex/maxUsageCountIndex,
1714
+		)
1715 1715
 	})
1716 1716
 }
1717 1717
 
... ...
@@ -406,11 +406,11 @@ func (md *cacheMetadata) getLastUsed() (int, *time.Time) {
406 406
 	if v == nil {
407 407
 		return usageCount, nil
408 408
 	}
409
-	var lastUsedTs int64
410
-	if err := v.Unmarshal(&lastUsedTs); err != nil || lastUsedTs == 0 {
409
+	var lastUsedTS int64
410
+	if err := v.Unmarshal(&lastUsedTS); err != nil || lastUsedTS == 0 {
411 411
 		return usageCount, nil
412 412
 	}
413
-	tm := time.Unix(lastUsedTs/1e9, lastUsedTs%1e9)
413
+	tm := time.Unix(lastUsedTS/1e9, lastUsedTS%1e9)
414 414
 	return usageCount, &tm
415 415
 }
416 416
 
... ...
@@ -223,7 +223,7 @@ func MigrateV2(ctx context.Context, from, to string, cs content.Store, s snapsho
223 223
 
224 224
 			if blob := md.getBlob(); blob != "" {
225 225
 				if _, err := cs.Update(ctx, content.Info{
226
-					Digest: digest.Digest(blob),
226
+					Digest: blob,
227 227
 				}, "labels.containerd.io/gc.root"); err != nil {
228 228
 					return err
229 229
 				}
... ...
@@ -30,7 +30,7 @@ func descHandlersOf(opts ...RefOption) DescHandlers {
30 30
 
31 31
 type DescHandlerKey digest.Digest
32 32
 
33
-type NeedsRemoteProviderError []digest.Digest //nolint:errname
33
+type NeedsRemoteProviderError []digest.Digest
34 34
 
35 35
 func (m NeedsRemoteProviderError) Error() string {
36 36
 	return fmt.Sprintf("missing descriptor handlers for lazy blobs %+v", []digest.Digest(m))
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"sync"
12 12
 	"time"
13 13
 
14
+	obdlabel "github.com/containerd/accelerated-container-image/pkg/label"
14 15
 	"github.com/containerd/containerd/v2/core/content"
15 16
 	"github.com/containerd/containerd/v2/core/images"
16 17
 	"github.com/containerd/containerd/v2/core/leases"
... ...
@@ -45,7 +46,7 @@ import (
45 45
 	"golang.org/x/sync/errgroup"
46 46
 )
47 47
 
48
-var additionalAnnotations = append(compression.EStargzAnnotations, labels.LabelUncompressed)
48
+var additionalAnnotations = append(append(compression.EStargzAnnotations, obdlabel.OverlayBDAnnotations...), labels.LabelUncompressed)
49 49
 
50 50
 // Ref is a reference to cacheable objects.
51 51
 type Ref interface {
... ...
@@ -188,7 +189,7 @@ func (p parentRefs) release(ctx context.Context) (rerr error) {
188 188
 	return rerr
189 189
 }
190 190
 
191
-func (p parentRefs) clone() parentRefs {
191
+func (p parentRefs) cloneParentRefs() parentRefs {
192 192
 	switch {
193 193
 	case p.layerParent != nil:
194 194
 		p.layerParent = p.layerParent.clone()
... ...
@@ -361,12 +362,12 @@ func (cr *cacheRecord) size(ctx context.Context) (int64, error) {
361 361
 		}
362 362
 		if dgst := cr.getBlob(); dgst != "" {
363 363
 			added := make(map[digest.Digest]struct{})
364
-			info, err := cr.cm.ContentStore.Info(ctx, digest.Digest(dgst))
364
+			info, err := cr.cm.ContentStore.Info(ctx, dgst)
365 365
 			if err == nil {
366 366
 				usage.Size += info.Size
367
-				added[digest.Digest(dgst)] = struct{}{}
367
+				added[dgst] = struct{}{}
368 368
 			}
369
-			walkBlobVariantsOnly(ctx, cr.cm.ContentStore, digest.Digest(dgst), func(desc ocispecs.Descriptor) bool {
369
+			walkBlobVariantsOnly(ctx, cr.cm.ContentStore, dgst, func(desc ocispecs.Descriptor) bool {
370 370
 				if _, ok := added[desc.Digest]; !ok {
371 371
 					if info, err := cr.cm.ContentStore.Info(ctx, desc.Digest); err == nil {
372 372
 						usage.Size += info.Size
... ...
@@ -423,7 +424,11 @@ func (cr *cacheRecord) mount(ctx context.Context) (_ snapshot.Mountable, rerr er
423 423
 		// Return the mount direct from View rather than setting it using the Mounts call below.
424 424
 		// The two are equivalent for containerd snapshotters but the moby snapshotter requires
425 425
 		// the use of the mountable returned by View in this case.
426
-		mnts, err := cr.cm.Snapshotter.View(ctx, mountSnapshotID, cr.getSnapshotID())
426
+		labels := make(map[string]string)
427
+		if cr.cm.Snapshotter.Name() == "overlaybd" {
428
+			labels["containerd.io/snapshot/overlaybd.writable"] = "dev"
429
+		}
430
+		mnts, err := cr.cm.Snapshotter.View(ctx, mountSnapshotID, cr.getSnapshotID(), snapshots.WithLabels(labels))
427 431
 		if err != nil && !cerrdefs.IsAlreadyExists(err) {
428 432
 			return nil, err
429 433
 		}
... ...
@@ -472,7 +477,7 @@ func (cr *cacheRecord) remove(ctx context.Context, removeSnapshot bool) (rerr er
472 472
 	if err := cr.cm.MetadataStore.Clear(cr.ID()); err != nil {
473 473
 		return errors.Wrapf(err, "failed to delete metadata of %s", cr.ID())
474 474
 	}
475
-	if err := cr.parentRefs.release(ctx); err != nil {
475
+	if err := cr.release(ctx); err != nil {
476 476
 		return errors.Wrapf(err, "failed to release parents of %s", cr.ID())
477 477
 	}
478 478
 	return nil
... ...
@@ -1018,6 +1023,10 @@ func (sr *immutableRef) Extract(ctx context.Context, s session.Group) (rerr erro
1018 1018
 			return err
1019 1019
 		}
1020 1020
 		return rerr
1021
+	} else if sr.cm.Snapshotter.Name() == "overlaybd" {
1022
+		if rerr = sr.prepareRemoteSnapshotsOverlaybdMode(ctx); rerr == nil {
1023
+			return sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true, false)
1024
+		}
1021 1025
 	}
1022 1026
 
1023 1027
 	return sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true, false)
... ...
@@ -1026,7 +1035,6 @@ func (sr *immutableRef) Extract(ctx context.Context, s session.Group) (rerr erro
1026 1026
 func (sr *immutableRef) withRemoteSnapshotLabelsStargzMode(ctx context.Context, s session.Group, f func()) error {
1027 1027
 	dhs := sr.descHandlers
1028 1028
 	for _, r := range sr.layerChain() {
1029
-		r := r
1030 1029
 		info, err := r.cm.Snapshotter.Stat(ctx, r.getSnapshotID())
1031 1030
 		if err != nil && !cerrdefs.IsNotFound(err) {
1032 1031
 			return err
... ...
@@ -1035,7 +1043,7 @@ func (sr *immutableRef) withRemoteSnapshotLabelsStargzMode(ctx context.Context,
1035 1035
 		} else if _, ok := info.Labels["containerd.io/snapshot/remote"]; !ok {
1036 1036
 			continue // This isn't a remote snapshot; skip
1037 1037
 		}
1038
-		dh := dhs[digest.Digest(r.getBlob())]
1038
+		dh := dhs[r.getBlob()]
1039 1039
 		if dh == nil {
1040 1040
 			continue // no info passed; skip
1041 1041
 		}
... ...
@@ -1069,13 +1077,12 @@ func (sr *immutableRef) prepareRemoteSnapshotsStargzMode(ctx context.Context, s
1069 1069
 	_, err := g.Do(ctx, sr.ID()+"-prepare-remote-snapshot", func(ctx context.Context) (_ *leaseutil.LeaseRef, rerr error) {
1070 1070
 		dhs := sr.descHandlers
1071 1071
 		for _, r := range sr.layerChain() {
1072
-			r := r
1073 1072
 			snapshotID := r.getSnapshotID()
1074 1073
 			if _, err := r.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
1075 1074
 				continue
1076 1075
 			}
1077 1076
 
1078
-			dh := dhs[digest.Digest(r.getBlob())]
1077
+			dh := dhs[r.getBlob()]
1079 1078
 			if dh == nil {
1080 1079
 				// We cannot prepare remote snapshots without descHandler.
1081 1080
 				return nil, nil
... ...
@@ -1145,6 +1152,54 @@ func (sr *immutableRef) prepareRemoteSnapshotsStargzMode(ctx context.Context, s
1145 1145
 	return err
1146 1146
 }
1147 1147
 
1148
+func (sr *immutableRef) prepareRemoteSnapshotsOverlaybdMode(ctx context.Context) error {
1149
+	_, err := g.Do(ctx, sr.ID()+"-prepare-remote-snapshot", func(ctx context.Context) (_ *leaseutil.LeaseRef, rerr error) {
1150
+		dhs := sr.descHandlers
1151
+		for _, r := range sr.layerChain() {
1152
+			snapshotID := r.getSnapshotID()
1153
+			if _, err := r.cm.Snapshotter.Stat(ctx, snapshotID); err == nil {
1154
+				continue
1155
+			}
1156
+			dh := dhs[digest.Digest(r.getBlob())]
1157
+			if dh == nil {
1158
+				// We cannot prepare remote snapshots without descHandler.
1159
+				return nil, nil
1160
+			}
1161
+			defaultLabels := snapshots.FilterInheritedLabels(dh.SnapshotLabels)
1162
+			if defaultLabels == nil {
1163
+				defaultLabels = make(map[string]string)
1164
+			}
1165
+			defaultLabels["containerd.io/snapshot.ref"] = snapshotID
1166
+			// Prepare remote snapshots
1167
+			var (
1168
+				key  = fmt.Sprintf("tmp-%s %s", identity.NewID(), r.getChainID())
1169
+				opts = []snapshots.Opt{
1170
+					snapshots.WithLabels(defaultLabels),
1171
+				}
1172
+			)
1173
+			parentID := ""
1174
+			if r.layerParent != nil {
1175
+				parentID = r.layerParent.getSnapshotID()
1176
+			}
1177
+			if err := r.cm.Snapshotter.Prepare(ctx, key, parentID, opts...); err != nil {
1178
+				if cerrdefs.IsAlreadyExists(err) {
1179
+					// Check if the targeting snapshot ID has been prepared as
1180
+					// a remote snapshot in the snapshotter.
1181
+					_, err := r.cm.Snapshotter.Stat(ctx, snapshotID)
1182
+					if err == nil { // usable as remote snapshot without unlazying.
1183
+						// Try the next layer as well.
1184
+						continue
1185
+					}
1186
+				}
1187
+			}
1188
+			// This layer and all upper layers cannot be prepared without unlazying.
1189
+			break
1190
+		}
1191
+		return nil, nil
1192
+	})
1193
+	return err
1194
+}
1195
+
1148 1196
 func makeTmpLabelsStargzMode(labels map[string]string, s session.Group) (fields []string, res map[string]string) {
1149 1197
 	res = make(map[string]string)
1150 1198
 	// Append unique ID to labels for avoiding collision of labels among calls
... ...
@@ -1319,7 +1374,12 @@ func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, pg pr
1319 1319
 
1320 1320
 	key := fmt.Sprintf("extract-%s %s", identity.NewID(), sr.getChainID())
1321 1321
 
1322
-	err = sr.cm.Snapshotter.Prepare(ctx, key, parentID)
1322
+	if sr.cm.Snapshotter.Name() == "overlaybd" {
1323
+		err = sr.cm.Snapshotter.Prepare(ctx, key, parentID,
1324
+			snapshots.WithLabels(map[string]string{"containerd.io/snapshot.ref": string(sr.getChainID())}))
1325
+	} else {
1326
+		err = sr.cm.Snapshotter.Prepare(ctx, key, parentID)
1327
+	}
1323 1328
 	if err != nil {
1324 1329
 		return err
1325 1330
 	}
... ...
@@ -1479,7 +1539,7 @@ func (sr *mutableRef) commit() (_ *immutableRef, rerr error) {
1479 1479
 	rec := &cacheRecord{
1480 1480
 		mu:            sr.mu,
1481 1481
 		cm:            sr.cm,
1482
-		parentRefs:    sr.parentRefs.clone(),
1482
+		parentRefs:    sr.cloneParentRefs(),
1483 1483
 		equalMutable:  sr,
1484 1484
 		refs:          make(map[ref]struct{}),
1485 1485
 		cacheMetadata: md,
... ...
@@ -1635,16 +1695,16 @@ func readonlyOverlay(opt []string) []string {
1635 1635
 	out := make([]string, 0, len(opt))
1636 1636
 	upper := ""
1637 1637
 	for _, o := range opt {
1638
-		if strings.HasPrefix(o, "upperdir=") {
1639
-			upper = strings.TrimPrefix(o, "upperdir=")
1638
+		if after, ok := strings.CutPrefix(o, "upperdir="); ok {
1639
+			upper = after
1640 1640
 		} else if !strings.HasPrefix(o, "workdir=") {
1641 1641
 			out = append(out, o)
1642 1642
 		}
1643 1643
 	}
1644 1644
 	if upper != "" {
1645 1645
 		for i, o := range out {
1646
-			if strings.HasPrefix(o, "lowerdir=") {
1647
-				out[i] = "lowerdir=" + upper + ":" + strings.TrimPrefix(o, "lowerdir=")
1646
+			if after, ok := strings.CutPrefix(o, "lowerdir="); ok {
1647
+				out[i] = "lowerdir=" + upper + ":" + after
1648 1648
 			}
1649 1649
 		}
1650 1650
 	}
... ...
@@ -121,7 +121,6 @@ func getAvailableBlobs(ctx context.Context, cs content.Store, chain *solver.Remo
121 121
 	}
122 122
 	var res []*solver.Remote
123 123
 	for _, desc := range descs {
124
-		desc := desc
125 124
 		if len(parents) == 0 { // bottommost ref
126 125
 			res = append(res, &solver.Remote{
127 126
 				Descriptors: []ocispecs.Descriptor{desc},
... ...
@@ -277,7 +276,6 @@ func (mp *lazyMultiProvider) Info(ctx context.Context, dgst digest.Digest) (cont
277 277
 func (mp *lazyMultiProvider) Unlazy(ctx context.Context) error {
278 278
 	eg, egctx := errgroup.WithContext(ctx)
279 279
 	for _, p := range mp.plist {
280
-		p := p
281 280
 		eg.Go(func() error {
282 281
 			return p.Unlazy(egctx)
283 282
 		})
... ...
@@ -83,7 +83,7 @@ func NewExportableCache(oci bool, imageManifest bool) (*ExportableCache, error)
83 83
 	if imageManifest {
84 84
 		mediaType = ocispecs.MediaTypeImageManifest
85 85
 		if !oci {
86
-			return nil, errors.Errorf("invalid configuration for remote cache")
86
+			return nil, errors.Errorf("invalid configuration for remote cache, OCI mediatypes are required for image-manifest cache format")
87 87
 		}
88 88
 	} else {
89 89
 		if oci {
... ...
@@ -137,7 +137,7 @@ func readBlob(ctx context.Context, provider content.Provider, desc ocispecs.Desc
137 137
 	if err != nil {
138 138
 		// NOTE: even if err == EOF, we might have got expected dt here.
139 139
 		// For instance, http.Response.Body is known to return non-zero bytes with EOF.
140
-		if err == io.EOF {
140
+		if errors.Is(err, io.EOF) {
141 141
 			if dtDigest := desc.Digest.Algorithm().FromBytes(dt); dtDigest != desc.Digest {
142 142
 				err = errors.Wrapf(err, "got EOF, expected %s (%d bytes), got %s (%d bytes)",
143 143
 					desc.Digest, desc.Size, dtDigest, len(dt))
... ...
@@ -58,7 +58,10 @@ func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExpor
58 58
 				return nil, errors.Wrapf(err, "failed to parse %s", attrImageManifest)
59 59
 			}
60 60
 			imageManifest = b
61
+		} else if !ociMediatypes {
62
+			imageManifest = false
61 63
 		}
64
+
62 65
 		csID := contentStoreIDPrefix + store
63 66
 		cs, err := getContentStore(ctx, sm, g, csID)
64 67
 		if err != nil {
... ...
@@ -106,7 +109,7 @@ func getContentStore(ctx context.Context, sm *session.Manager, g session.Group,
106 106
 		return nil, errors.New("local cache exporter/importer requires session")
107 107
 	}
108 108
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
109
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
109
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
110 110
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
111 111
 
112 112
 	caller, err := sm.Get(timeoutCtx, sessionID, false)
... ...
@@ -8,6 +8,8 @@ import (
8 8
 	"github.com/containerd/containerd/v2/core/content"
9 9
 	"github.com/containerd/containerd/v2/core/remotes/docker"
10 10
 	"github.com/containerd/containerd/v2/core/snapshots"
11
+	"github.com/containerd/containerd/v2/pkg/snapshotters"
12
+
11 13
 	"github.com/distribution/reference"
12 14
 	"github.com/moby/buildkit/cache/remotecache"
13 15
 	"github.com/moby/buildkit/session"
... ...
@@ -76,6 +78,8 @@ func ResolveCacheExporterFunc(sm *session.Manager, hosts docker.RegistryHosts) r
76 76
 				return nil, errors.Wrapf(err, "failed to parse %s", attrImageManifest)
77 77
 			}
78 78
 			imageManifest = b
79
+		} else if !ociMediatypes {
80
+			imageManifest = false
79 81
 		}
80 82
 		insecure := false
81 83
 		if v, ok := attrs[attrInsecure]; ok {
... ...
@@ -165,6 +169,7 @@ func (dsl *withDistributionSourceLabel) SnapshotLabels(descs []ocispecs.Descript
165 165
 		labels = make(map[string]string)
166 166
 	}
167 167
 	maps.Copy(labels, estargz.SnapshotLabels(dsl.ref, descs, index))
168
+	labels[snapshotters.TargetRefLabel] = dsl.ref
168 169
 	return labels
169 170
 }
170 171
 
... ...
@@ -226,10 +226,7 @@ func (c *item) LinkFrom(rec solver.CacheExporterRecord, index int, selector stri
226 226
 		return
227 227
 	}
228 228
 
229
-	for {
230
-		if index < len(c.links) {
231
-			break
232
-		}
229
+	for index >= len(c.links) {
233 230
 		c.links = append(c.links, map[link]struct{}{})
234 231
 	}
235 232
 
... ...
@@ -1,8 +1,10 @@
1 1
 package cacheimport
2 2
 
3 3
 import (
4
+	"cmp"
4 5
 	"context"
5 6
 	"fmt"
7
+	"slices"
6 8
 	"sort"
7 9
 
8 10
 	cerrdefs "github.com/containerd/errdefs"
... ...
@@ -28,13 +30,8 @@ func sortConfig(cc *CacheConfig) {
28 28
 		unsortedLayers[i] = il
29 29
 		sortedLayers[i] = il
30 30
 	}
31
-	sort.Slice(sortedLayers, func(i, j int) bool {
32
-		li := sortedLayers[i].l
33
-		lj := sortedLayers[j].l
34
-		if li.Blob == lj.Blob {
35
-			return li.ParentIndex < lj.ParentIndex
36
-		}
37
-		return li.Blob < lj.Blob
31
+	slices.SortFunc(sortedLayers, func(a, b *indexedLayer) int {
32
+		return cmp.Or(cmp.Compare(a.l.Blob, b.l.Blob), cmp.Compare(a.l.ParentIndex, b.l.ParentIndex))
38 33
 	})
39 34
 	for i, l := range sortedLayers {
40 35
 		l.newIndex = i
... ...
@@ -101,8 +98,8 @@ func sortConfig(cc *CacheConfig) {
101 101
 			for k := range inputs {
102 102
 				r.r.Inputs[j][k].LinkIndex = unsortedRecords[r.r.Inputs[j][k].LinkIndex].newIndex
103 103
 			}
104
-			sort.Slice(inputs, func(i, j int) bool {
105
-				return inputs[i].LinkIndex < inputs[j].LinkIndex
104
+			slices.SortFunc(inputs, func(a, b CacheInput) int {
105
+				return cmp.Compare(a.LinkIndex, b.LinkIndex)
106 106
 			})
107 107
 		}
108 108
 		records[i] = r.r
... ...
@@ -62,7 +62,8 @@ func ReadFile(ctx context.Context, mount snapshot.Mountable, req ReadRequest) ([
62 62
 			// The filename here is internal to the mount, so we can restore
63 63
 			// the request base path for error reporting.
64 64
 			// See os.DirFS.Open for details.
65
-			if pe, ok := err.(*os.PathError); ok {
65
+			pe := &os.PathError{}
66
+			if errors.As(err, &pe) {
66 67
 				pe.Path = req.Filename
67 68
 			}
68 69
 			return errors.WithStack(err)
... ...
@@ -151,7 +151,8 @@ func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error
151 151
 	gopts = append(gopts, grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor))
152 152
 	gopts = append(gopts, customDialOptions...)
153 153
 
154
-	//nolint:staticcheck // ignore SA1019 NewClient has different behavior and needs to be tested
154
+	// ignore SA1019 NewClient has different behavior and needs to be tested
155
+	//nolint:staticcheck
155 156
 	conn, err := grpc.DialContext(ctx, address, gopts...)
156 157
 	if err != nil {
157 158
 		return nil, errors.Wrapf(err, "failed to dial %q . make sure buildkitd is running", address)
... ...
@@ -1,8 +1,9 @@
1 1
 package client
2 2
 
3 3
 import (
4
+	"cmp"
4 5
 	"context"
5
-	"sort"
6
+	"slices"
6 7
 	"time"
7 8
 
8 9
 	controlapi "github.com/moby/buildkit/api/services/control"
... ...
@@ -60,13 +61,9 @@ func (c *Client) DiskUsage(ctx context.Context, opts ...DiskUsageOption) ([]*Usa
60 60
 		})
61 61
 	}
62 62
 
63
-	sort.Slice(du, func(i, j int) bool {
64
-		if du[i].Size == du[j].Size {
65
-			return du[i].ID > du[j].ID
66
-		}
67
-		return du[i].Size > du[j].Size
63
+	slices.SortFunc(du, func(a, b *UsageInfo) int {
64
+		return cmp.Or(cmp.Compare(a.Size, b.Size), cmp.Compare(a.ID, b.ID))
68 65
 	})
69
-
70 66
 	return du, nil
71 67
 }
72 68
 
... ...
@@ -240,7 +240,7 @@ func (d *DefinitionOp) Inputs() []Output {
240 240
 		d.mu.Unlock()
241 241
 
242 242
 		inputs = append(inputs, &output{vertex: vtx, platform: platform, getIndex: func() (pb.OutputIndex, error) {
243
-			return pb.OutputIndex(vtx.index), nil
243
+			return vtx.index, nil
244 244
 		}})
245 245
 	}
246 246
 
... ...
@@ -5,7 +5,7 @@ import (
5 5
 	_ "crypto/sha256" // for opencontainers/go-digest
6 6
 	"fmt"
7 7
 	"net"
8
-	"sort"
8
+	"slices"
9 9
 	"strings"
10 10
 
11 11
 	"github.com/moby/buildkit/solver/pb"
... ...
@@ -143,8 +143,8 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []
143 143
 		return "", nil, nil, nil, err
144 144
 	}
145 145
 	// make sure mounts are sorted
146
-	sort.Slice(e.mounts, func(i, j int) bool {
147
-		return e.mounts[i].target < e.mounts[j].target
146
+	slices.SortFunc(e.mounts, func(a, b *mount) int {
147
+		return strings.Compare(a.target, b.target)
148 148
 	})
149 149
 
150 150
 	env, err := getEnv(e.base)(ctx, c)
... ...
@@ -170,7 +170,10 @@ func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []
170 170
 			} else if e.constraints.Platform != nil {
171 171
 				os = e.constraints.Platform.OS
172 172
 			}
173
-			env = env.SetDefault("PATH", system.DefaultPathEnv(os))
173
+			// don't set PATH on Windows. #5445
174
+			if os != "windows" {
175
+				env = env.SetDefault("PATH", system.DefaultPathEnv(os))
176
+			}
174 177
 		} else {
175 178
 			addCap(&e.constraints, pb.CapExecMetaSetsDefaultPath)
176 179
 		}
... ...
@@ -477,10 +480,9 @@ func (e *ExecOp) Inputs() (inputs []Output) {
477 477
 	// make sure mounts are sorted
478 478
 	// the same sort occurs in (*ExecOp).Marshal, and this
479 479
 	// sort must be the same
480
-	sort.Slice(e.mounts, func(i int, j int) bool {
481
-		return e.mounts[i].target < e.mounts[j].target
480
+	slices.SortFunc(e.mounts, func(a, b *mount) int {
481
+		return strings.Compare(a.target, b.target)
482 482
 	})
483
-
484 483
 	seen := map[Output]struct{}{}
485 484
 	for _, m := range e.mounts {
486 485
 		if m.source != nil {
... ...
@@ -497,8 +499,8 @@ func (e *ExecOp) Inputs() (inputs []Output) {
497 497
 func (e *ExecOp) getMountIndexFn(m *mount) func() (pb.OutputIndex, error) {
498 498
 	return func() (pb.OutputIndex, error) {
499 499
 		// make sure mounts are sorted
500
-		sort.Slice(e.mounts, func(i, j int) bool {
501
-			return e.mounts[i].target < e.mounts[j].target
500
+		slices.SortFunc(e.mounts, func(a, b *mount) int {
501
+			return strings.Compare(a.target, b.target)
502 502
 		})
503 503
 
504 504
 		i := 0
... ...
@@ -136,7 +136,7 @@ func Image(ref string, opts ...ImageOption) State {
136 136
 	} else if info.metaResolver != nil {
137 137
 		if _, ok := r.(reference.Digested); ok || !info.resolveDigest {
138 138
 			return NewState(src.Output()).Async(func(ctx context.Context, st State, c *Constraints) (State, error) {
139
-				p := info.Constraints.Platform
139
+				p := info.Platform
140 140
 				if p == nil {
141 141
 					p = c.Platform
142 142
 				}
... ...
@@ -153,7 +153,7 @@ func Image(ref string, opts ...ImageOption) State {
153 153
 			})
154 154
 		}
155 155
 		return Scratch().Async(func(ctx context.Context, _ State, c *Constraints) (State, error) {
156
-			p := info.Constraints.Platform
156
+			p := info.Platform
157 157
 			if p == nil {
158 158
 				p = c.Platform
159 159
 			}
... ...
@@ -18,9 +18,9 @@ func (c *Client) Prune(ctx context.Context, ch chan UsageInfo, opts ...PruneOpti
18 18
 	req := &controlapi.PruneRequest{
19 19
 		Filter:        info.Filter,
20 20
 		KeepDuration:  int64(info.KeepDuration),
21
-		ReservedSpace: int64(info.ReservedSpace),
22
-		MaxUsedSpace:  int64(info.MaxUsedSpace),
23
-		MinFreeSpace:  int64(info.MinFreeSpace),
21
+		ReservedSpace: info.ReservedSpace,
22
+		MaxUsedSpace:  info.MaxUsedSpace,
23
+		MinFreeSpace:  info.MinFreeSpace,
24 24
 	}
25 25
 	if info.All {
26 26
 		req.All = true
... ...
@@ -33,7 +33,7 @@ func (c *Client) Prune(ctx context.Context, ch chan UsageInfo, opts ...PruneOpti
33 33
 	for {
34 34
 		d, err := cl.Recv()
35 35
 		if err != nil {
36
-			if err == io.EOF {
36
+			if errors.Is(err, io.EOF) {
37 37
 				return nil
38 38
 			}
39 39
 			return err
... ...
@@ -322,7 +322,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
322 322
 		for {
323 323
 			resp, err := stream.Recv()
324 324
 			if err != nil {
325
-				if err == io.EOF {
325
+				if errors.Is(err, io.EOF) {
326 326
 					return nil
327 327
 				}
328 328
 				return errors.Wrap(err, "failed to receive status")
... ...
@@ -356,7 +356,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
356 356
 			return nil, err
357 357
 		}
358 358
 		var manifestDesc ocispecs.Descriptor
359
-		if err = json.Unmarshal([]byte(manifestDescDt), &manifestDesc); err != nil {
359
+		if err = json.Unmarshal(manifestDescDt, &manifestDesc); err != nil {
360 360
 			return nil, err
361 361
 		}
362 362
 		for _, storePath := range storesToUpdate {
... ...
@@ -402,8 +402,7 @@ func prepareSyncedFiles(def *llb.Definition, localMounts map[string]fsutil.FS) (
402 402
 				return nil, errors.Wrap(err, "failed to parse llb proto op")
403 403
 			}
404 404
 			if src := op.GetSource(); src != nil {
405
-				if strings.HasPrefix(src.Identifier, "local://") {
406
-					name := strings.TrimPrefix(src.Identifier, "local://")
405
+				if name, ok := strings.CutPrefix(src.Identifier, "local://"); ok {
407 406
 					mount, ok := localMounts[name]
408 407
 					if !ok {
409 408
 						return nil, errors.Errorf("local directory %s not enabled", name)
... ...
@@ -104,7 +104,7 @@ type NetworkConfig struct {
104 104
 type OCIConfig struct {
105 105
 	Enabled          *bool             `toml:"enabled"`
106 106
 	Labels           map[string]string `toml:"labels"`
107
-	Platforms        []string          `toml:"platforms"`
107
+	Platforms        []string          `toml:"platforms,omitempty"`
108 108
 	Snapshotter      string            `toml:"snapshotter"`
109 109
 	Rootless         bool              `toml:"rootless"`
110 110
 	NoProcessSandbox bool              `toml:"noProcessSandbox"`
... ...
@@ -138,7 +138,7 @@ type ContainerdConfig struct {
138 138
 	Address   string            `toml:"address"`
139 139
 	Enabled   *bool             `toml:"enabled"`
140 140
 	Labels    map[string]string `toml:"labels"`
141
-	Platforms []string          `toml:"platforms"`
141
+	Platforms []string          `toml:"platforms,omitempty"`
142 142
 	Namespace string            `toml:"namespace"`
143 143
 	Runtime   ContainerdRuntime `toml:"runtime"`
144 144
 	GCConfig
... ...
@@ -60,7 +60,7 @@ func (gwf *GatewayForwarder) lookupForwarder(ctx context.Context) (gateway.LLBBr
60 60
 	}
61 61
 
62 62
 	ctx, cancel := context.WithCancelCause(ctx)
63
-	ctx, _ = context.WithTimeoutCause(ctx, 3*time.Second, errors.WithStack(context.DeadlineExceeded))
63
+	ctx, _ = context.WithTimeoutCause(ctx, 3*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
64 64
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
65 65
 
66 66
 	go func() {
... ...
@@ -5,13 +5,13 @@ import (
5 5
 	"syscall"
6 6
 )
7 7
 
8
-type internalErr struct {
8
+type internalError struct {
9 9
 	error
10 10
 }
11 11
 
12
-func (internalErr) System() {}
12
+func (internalError) System() {}
13 13
 
14
-func (err internalErr) Unwrap() error {
14
+func (err internalError) Unwrap() error {
15 15
 	return err.error
16 16
 }
17 17
 
... ...
@@ -19,13 +19,13 @@ type system interface {
19 19
 	System()
20 20
 }
21 21
 
22
-var _ system = internalErr{}
22
+var _ system = internalError{}
23 23
 
24 24
 func Internal(err error) error {
25 25
 	if err == nil {
26 26
 		return nil
27 27
 	}
28
-	return internalErr{err}
28
+	return internalError{err}
29 29
 }
30 30
 
31 31
 func IsInternal(err error) bool {
... ...
@@ -399,7 +399,7 @@ func (w *containerdExecutor) runProcess(ctx context.Context, p ctd.Process, resi
399 399
 			ctxDone = nil
400 400
 			var killCtx context.Context
401 401
 			killCtx, cancel = context.WithCancelCause(context.Background())
402
-			killCtx, _ = context.WithTimeoutCause(killCtx, 10*time.Second, errors.WithStack(context.DeadlineExceeded))
402
+			killCtx, _ = context.WithTimeoutCause(killCtx, 10*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
403 403
 			killCtxDone = killCtx.Done()
404 404
 			p.Kill(killCtx, syscall.SIGKILL)
405 405
 			io.Cancel()
... ...
@@ -122,7 +122,7 @@ func (s *Sampler[T]) run() {
122 122
 					ss.err = nil
123 123
 				}
124 124
 				dur := ss.last.Sub(ss.first)
125
-				if time.Duration(ss.interval)*time.Duration(s.maxSamples) <= dur {
125
+				if ss.interval*time.Duration(s.maxSamples) <= dur {
126 126
 					ss.interval *= 2
127 127
 				}
128 128
 			}
... ...
@@ -546,7 +546,7 @@ func (k procKiller) Kill(ctx context.Context) (err error) {
546 546
 	// this timeout is generally a no-op, the Kill ctx should already have a
547 547
 	// shorter timeout but here as a fail-safe for future refactoring.
548 548
 	ctx, cancel := context.WithCancelCause(ctx)
549
-	ctx, _ = context.WithTimeoutCause(ctx, 10*time.Second, errors.WithStack(context.DeadlineExceeded))
549
+	ctx, _ = context.WithTimeoutCause(ctx, 10*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
550 550
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
551 551
 
552 552
 	if k.pidfile == "" {
... ...
@@ -634,13 +634,13 @@ func runcProcessHandle(ctx context.Context, killer procKiller) (*procHandle, con
634 634
 		for {
635 635
 			select {
636 636
 			case <-ctx.Done():
637
-				killCtx, timeout := context.WithCancelCause(context.Background())
638
-				killCtx, _ = context.WithTimeoutCause(killCtx, 7*time.Second, errors.WithStack(context.DeadlineExceeded))
637
+				killCtx, timeout := context.WithCancelCause(context.Background())                                         //nolint:govet
638
+				killCtx, _ = context.WithTimeoutCause(killCtx, 7*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
639 639
 				if err := p.killer.Kill(killCtx); err != nil {
640 640
 					select {
641 641
 					case <-killCtx.Done():
642 642
 						cancel(errors.WithStack(context.Cause(ctx)))
643
-						return
643
+						return //nolint:govet
644 644
 					default:
645 645
 					}
646 646
 				}
... ...
@@ -693,7 +693,7 @@ func (p *procHandle) WaitForReady(ctx context.Context) error {
693 693
 // callback is non-nil it will be called after receiving the pid.
694 694
 func (p *procHandle) WaitForStart(ctx context.Context, startedCh <-chan int, started func()) error {
695 695
 	ctx, cancel := context.WithCancelCause(ctx)
696
-	ctx, _ = context.WithTimeoutCause(ctx, 10*time.Second, errors.WithStack(context.DeadlineExceeded))
696
+	ctx, _ = context.WithTimeoutCause(ctx, 10*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
697 697
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
698 698
 	select {
699 699
 	case <-ctx.Done():
... ...
@@ -129,7 +129,7 @@ func (w *runcExecutor) callWithIO(ctx context.Context, process executor.ProcessI
129 129
 			if errors.As(err, &ptmClosedError) {
130 130
 				if ptmClosedError.Op == "read" &&
131 131
 					ptmClosedError.Path == "/dev/ptmx" &&
132
-					ptmClosedError.Err == syscall.EIO {
132
+					errors.Is(ptmClosedError.Err, syscall.EIO) {
133 133
 					return nil
134 134
 				}
135 135
 			}
... ...
@@ -33,7 +33,7 @@ func MountStubsCleaner(ctx context.Context, dir string, mounts []Mount, recursiv
33 33
 
34 34
 		for {
35 35
 			_, err = os.Lstat(realPath)
36
-			if !(errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR)) {
36
+			if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, syscall.ENOTDIR) {
37 37
 				break
38 38
 			}
39 39
 			paths = append(paths, realPath)
... ...
@@ -20,7 +20,7 @@ import (
20 20
 func ReadAll(ctx context.Context, s session.Group, att exporter.Attestation) ([]byte, error) {
21 21
 	var content []byte
22 22
 	if att.ContentFunc != nil {
23
-		data, err := att.ContentFunc()
23
+		data, err := att.ContentFunc(ctx)
24 24
 		if err != nil {
25 25
 			return nil, err
26 26
 		}
... ...
@@ -166,7 +166,7 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
166 166
 			Kind:        gatewaypb.AttestationKind_InToto,
167 167
 			Metadata:    bundle.Metadata,
168 168
 			Path:        path.Join(bundle.Path, entry.Name()),
169
-			ContentFunc: func() ([]byte, error) { return predicate, nil },
169
+			ContentFunc: func(context.Context) ([]byte, error) { return predicate, nil },
170 170
 			InToto: result.InTotoAttestation{
171 171
 				PredicateType: stmt.PredicateType,
172 172
 				Subjects:      subjects,
... ...
@@ -118,7 +118,7 @@ func supplementSBOM(ctx context.Context, s session.Group, target cache.Immutable
118 118
 	return exporter.Attestation{
119 119
 		Kind:        att.Kind,
120 120
 		Path:        att.Path,
121
-		ContentFunc: func() ([]byte, error) { return content, nil },
121
+		ContentFunc: func(context.Context) ([]byte, error) { return content, nil },
122 122
 		InToto:      att.InToto,
123 123
 	}, nil
124 124
 }
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"github.com/containerd/containerd/v2/core/images"
15 15
 	"github.com/containerd/containerd/v2/core/leases"
16 16
 	"github.com/containerd/containerd/v2/core/remotes/docker"
17
+	remoteserrors "github.com/containerd/containerd/v2/core/remotes/errors"
17 18
 	"github.com/containerd/containerd/v2/pkg/epoch"
18 19
 	"github.com/containerd/containerd/v2/pkg/labels"
19 20
 	"github.com/containerd/containerd/v2/pkg/rootfs"
... ...
@@ -333,7 +334,6 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
333 333
 					}
334 334
 					eg, ctx := errgroup.WithContext(ctx)
335 335
 					for _, ref := range refs {
336
-						ref := ref
337 336
 						eg.Go(func() error {
338 337
 							remotes, err := ref.GetRemotes(ctx, false, e.opts.RefCfg, false, session.NewGroup(sessionID))
339 338
 							if err != nil {
... ...
@@ -356,6 +356,13 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
356 356
 			if e.push {
357 357
 				err = e.pushImage(ctx, src, sessionID, targetName, desc.Digest)
358 358
 				if err != nil {
359
+					var statusErr remoteserrors.ErrUnexpectedStatus
360
+					if errors.As(err, &statusErr) {
361
+						var dErr docker.Errors
362
+						if err1 := json.Unmarshal(statusErr.Body, &dErr); err1 == nil && len(dErr) > 0 {
363
+							err = &formattedDockerError{dErr: dErr}
364
+						}
365
+					}
359 366
 					return nil, nil, errors.Wrapf(err, "failed to push %v", targetName)
360 367
 				}
361 368
 			}
... ...
@@ -543,3 +550,36 @@ func (d *descriptorReference) Descriptor() ocispecs.Descriptor {
543 543
 func (d *descriptorReference) Release() error {
544 544
 	return d.release(context.TODO())
545 545
 }
546
+
547
+type formattedDockerError struct {
548
+	dErr docker.Errors
549
+}
550
+
551
+func (e *formattedDockerError) Error() string {
552
+	format := func(err error) string {
553
+		out := err.Error()
554
+		var dErr docker.Error
555
+		if errors.As(err, &dErr) {
556
+			if v, ok := dErr.Detail.(string); ok && v != "" {
557
+				out += " - " + v
558
+			}
559
+		}
560
+		return out
561
+	}
562
+	switch len(e.dErr) {
563
+	case 0:
564
+		return "<nil>"
565
+	case 1:
566
+		return format(e.dErr[0])
567
+	default:
568
+		msg := "errors:\n"
569
+		for _, err := range e.dErr {
570
+			msg += format(err) + "\n"
571
+		}
572
+		return msg
573
+	}
574
+}
575
+
576
+func (e *formattedDockerError) Unwrap() error {
577
+	return e.dErr
578
+}
... ...
@@ -683,7 +683,10 @@ func defaultImageConfig() ([]byte, error) {
683 683
 	img.Variant = pl.Variant
684 684
 	img.RootFS.Type = "layers"
685 685
 	img.Config.WorkingDir = "/"
686
-	img.Config.Env = []string{"PATH=" + system.DefaultPathEnv(pl.OS)}
686
+	// don't set default PATH on Windows. #5445
687
+	if pl.OS != "windows" {
688
+		img.Config.Env = []string{"PATH=" + system.DefaultPathEnv(pl.OS)}
689
+	}
687 690
 	dt, err := json.Marshal(img)
688 691
 	return dt, errors.Wrap(err, "failed to create empty image config")
689 692
 }
... ...
@@ -80,7 +80,7 @@ func (e *localExporter) Config() *exporter.Config {
80 80
 
81 81
 func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source, _ exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) {
82 82
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
83
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
83
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
84 84
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
85 85
 
86 86
 	if e.opts.Epoch == nil {
... ...
@@ -217,7 +217,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
217 217
 	}
218 218
 
219 219
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
220
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
220
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
221 221
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
222 222
 
223 223
 	caller, err := e.opt.SessionManager.Get(timeoutCtx, sessionID, false)
... ...
@@ -235,7 +235,6 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source
235 235
 	eg, egCtx := errgroup.WithContext(ctx)
236 236
 	mprovider := contentutil.NewMultiProvider(e.opt.ImageWriter.ContentStore())
237 237
 	for _, ref := range refs {
238
-		ref := ref
239 238
 		eg.Go(func() error {
240 239
 			remotes, err := ref.GetRemotes(egCtx, false, e.opts.RefCfg, false, session.NewGroup(sessionID))
241 240
 			if err != nil {
... ...
@@ -164,7 +164,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source
164 164
 	}
165 165
 
166 166
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
167
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
167
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
168 168
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
169 169
 
170 170
 	caller, err := e.opt.SessionManager.Get(timeoutCtx, sessionID, false)
... ...
@@ -32,8 +32,8 @@ func CaptureFrontendOpts[T comparable](m map[string]string, res *result.Result[T
32 32
 
33 33
 	req.Labels = map[string]string{}
34 34
 	for k, v := range m {
35
-		if strings.HasPrefix(k, labelsPrefix) {
36
-			req.Labels[strings.TrimPrefix(k, labelsPrefix)] = v
35
+		if after, ok := strings.CutPrefix(k, labelsPrefix); ok {
36
+			req.Labels[after] = v
37 37
 		}
38 38
 	}
39 39
 	req.Request = m[keyRequestID]
... ...
@@ -3,7 +3,7 @@ package verifier
3 3
 import (
4 4
 	"context"
5 5
 	"fmt"
6
-	"sort"
6
+	"slices"
7 7
 	"strings"
8 8
 
9 9
 	"github.com/containerd/platforms"
... ...
@@ -115,10 +115,10 @@ func CheckInvalidPlatforms[T comparable](ctx context.Context, res *result.Result
115 115
 }
116 116
 
117 117
 func platformsString(ps []exptypes.Platform) string {
118
-	var ss []string
119
-	for _, p := range ps {
120
-		ss = append(ss, platforms.FormatAll(platforms.Normalize(p.Platform)))
118
+	ss := make([]string, len(ps))
119
+	for i, p := range ps {
120
+		ss[i] = platforms.FormatAll(platforms.Normalize(p.Platform))
121 121
 	}
122
-	sort.Strings(ss)
122
+	slices.Sort(ss)
123 123
 	return strings.Join(ss, ",")
124 124
 }
... ...
@@ -43,12 +43,12 @@ func Validate(values map[string]map[string]string) (map[string]map[string]string
43 43
 func Parse(values map[string]string) (map[string]map[string]string, error) {
44 44
 	attests := make(map[string]string)
45 45
 	for k, v := range values {
46
-		if strings.HasPrefix(k, "attest:") {
47
-			attests[strings.ToLower(strings.TrimPrefix(k, "attest:"))] = v
46
+		if after, ok := strings.CutPrefix(k, "attest:"); ok {
47
+			attests[strings.ToLower(after)] = v
48 48
 			continue
49 49
 		}
50
-		if strings.HasPrefix(k, "build-arg:BUILDKIT_ATTEST_") {
51
-			attests[strings.ToLower(strings.TrimPrefix(k, "build-arg:BUILDKIT_ATTEST_"))] = v
50
+		if after, ok := strings.CutPrefix(k, "build-arg:BUILDKIT_ATTEST_"); ok {
51
+			attests[strings.ToLower(after)] = v
52 52
 			continue
53 53
 		}
54 54
 	}
... ...
@@ -103,7 +103,7 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
103 103
 	}
104 104
 
105 105
 	defer func() {
106
-		var el *parser.ErrorLocation
106
+		var el *parser.LocationError
107 107
 		if errors.As(err, &el) {
108 108
 			for _, l := range el.Locations {
109 109
 				err = wrapSource(err, src.SourceMap, l)
... ...
@@ -14,7 +14,6 @@ import (
14 14
 	"regexp"
15 15
 	"runtime"
16 16
 	"slices"
17
-	"sort"
18 17
 	"strconv"
19 18
 	"strings"
20 19
 	"sync"
... ...
@@ -138,7 +137,7 @@ func DockerfileLint(ctx context.Context, dt []byte, opt ConvertOpt) (*lint.LintR
138 138
 
139 139
 	_, err := toDispatchState(ctx, dt, opt)
140 140
 
141
-	var errLoc *parser.ErrorLocation
141
+	var errLoc *parser.LocationError
142 142
 	if err != nil {
143 143
 		buildErr := &lint.BuildError{
144 144
 			Message: err.Error(),
... ...
@@ -680,7 +679,10 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
680 680
 			if d.platform != nil {
681 681
 				osName = d.platform.OS
682 682
 			}
683
-			d.image.Config.Env = append(d.image.Config.Env, "PATH="+system.DefaultPathEnv(osName))
683
+			// except for Windows, leave that to the OS. #5445
684
+			if osName != "windows" {
685
+				d.image.Config.Env = append(d.image.Config.Env, "PATH="+system.DefaultPathEnv(osName))
686
+			}
684 687
 		}
685 688
 
686 689
 		// initialize base metadata from image conf
... ...
@@ -1219,7 +1221,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
1219 1219
 	// Run command can potentially access any file. Mark the full filesystem as used.
1220 1220
 	d.paths["/"] = struct{}{}
1221 1221
 
1222
-	var args []string = c.CmdLine
1222
+	var args = c.CmdLine
1223 1223
 	if len(c.Files) > 0 {
1224 1224
 		if len(args) != 1 || !c.PrependShell {
1225 1225
 			return errors.Errorf("parsing produced an invalid run command: %v", args)
... ...
@@ -1400,6 +1402,9 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
1400 1400
 			if user := d.image.Config.User; user != "" {
1401 1401
 				mkdirOpt = append(mkdirOpt, llb.WithUser(user))
1402 1402
 			}
1403
+			if d.epoch != nil {
1404
+				mkdirOpt = append(mkdirOpt, llb.WithCreatedTime(*d.epoch))
1405
+			}
1403 1406
 			platform := opt.targetPlatform
1404 1407
 			if d.platform != nil {
1405 1408
 				platform = *d.platform
... ...
@@ -1705,7 +1710,7 @@ func dispatchOnbuild(d *dispatchState, c *instructions.OnbuildCommand) error {
1705 1705
 func dispatchCmd(d *dispatchState, c *instructions.CmdCommand, lint *linter.Linter) error {
1706 1706
 	validateUsedOnce(c, &d.cmd, lint)
1707 1707
 
1708
-	var args []string = c.CmdLine
1708
+	var args = c.CmdLine
1709 1709
 	if c.PrependShell {
1710 1710
 		if len(d.image.Config.Shell) == 0 {
1711 1711
 			msg := linter.RuleJSONArgsRecommended.Format(c.Name())
... ...
@@ -1721,7 +1726,7 @@ func dispatchCmd(d *dispatchState, c *instructions.CmdCommand, lint *linter.Lint
1721 1721
 func dispatchEntrypoint(d *dispatchState, c *instructions.EntrypointCommand, lint *linter.Linter) error {
1722 1722
 	validateUsedOnce(c, &d.entrypoint, lint)
1723 1723
 
1724
-	var args []string = c.CmdLine
1724
+	var args = c.CmdLine
1725 1725
 	if c.PrependShell {
1726 1726
 		if len(d.image.Config.Shell) == 0 {
1727 1727
 			msg := linter.RuleJSONArgsRecommended.Format(c.Name())
... ...
@@ -2383,8 +2388,8 @@ func mergeLocations(locations ...[]parser.Range) []parser.Range {
2383 2383
 		return allRanges
2384 2384
 	}
2385 2385
 
2386
-	sort.Slice(allRanges, func(i, j int) bool {
2387
-		return allRanges[i].Start.Line < allRanges[j].Start.Line
2386
+	slices.SortFunc(allRanges, func(a, b parser.Range) int {
2387
+		return a.Start.Line - b.Start.Line
2388 2388
 	})
2389 2389
 
2390 2390
 	location := []parser.Range{}
... ...
@@ -37,6 +37,9 @@ func emptyImage(platform ocispecs.Platform) dockerspec.DockerOCIImage {
37 37
 	img.Variant = platform.Variant
38 38
 	img.RootFS.Type = "layers"
39 39
 	img.Config.WorkingDir = "/"
40
-	img.Config.Env = []string{"PATH=" + system.DefaultPathEnv(platform.OS)}
40
+	// don't set path for Windows, leave it to the OS. #5445
41
+	if platform.OS != "windows" {
42
+		img.Config.Env = []string{"PATH=" + system.DefaultPathEnv(platform.OS)}
43
+	}
41 44
 	return img
42 45
 }
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"fmt"
9 9
 	"regexp"
10 10
 	"slices"
11
-	"sort"
12 11
 	"strconv"
13 12
 	"strings"
14 13
 	"time"
... ...
@@ -688,7 +687,7 @@ func parseExpose(req parseRequest) (*ExposeCommand, error) {
688 688
 		return nil, err
689 689
 	}
690 690
 
691
-	sort.Strings(portsTab)
691
+	slices.Sort(portsTab)
692 692
 	return &ExposeCommand{
693 693
 		Ports:           portsTab,
694 694
 		withNameAndCode: newWithNameAndCode(req),
... ...
@@ -832,8 +831,8 @@ func getComment(comments []string, name string) string {
832 832
 		return ""
833 833
 	}
834 834
 	for _, line := range comments {
835
-		if strings.HasPrefix(line, name+" ") {
836
-			return strings.TrimPrefix(line, name+" ")
835
+		if after, ok := strings.CutPrefix(line, name+" "); ok {
836
+			return after
837 837
 		}
838 838
 	}
839 839
 	return ""
... ...
@@ -55,7 +55,7 @@ func (lc *Linter) Run(rule LinterRuleI, location []parser.Range, txt ...string)
55 55
 	rulename := rule.RuleName()
56 56
 	if rule.IsExperimental() {
57 57
 		_, experimentalOk := lc.ExperimentalRules[rulename]
58
-		if !(lc.ExperimentalAll || experimentalOk) {
58
+		if !lc.ExperimentalAll && !experimentalOk {
59 59
 			return
60 60
 		}
61 61
 	} else {
... ...
@@ -5,14 +5,14 @@ import (
5 5
 	"github.com/pkg/errors"
6 6
 )
7 7
 
8
-// ErrorLocation gives a location in source code that caused the error
9
-type ErrorLocation struct {
8
+// LocationError gives a location in source code that caused the error
9
+type LocationError struct {
10 10
 	Locations [][]Range
11 11
 	error
12 12
 }
13 13
 
14 14
 // Unwrap unwraps to the next error
15
-func (e *ErrorLocation) Unwrap() error {
15
+func (e *LocationError) Unwrap() error {
16 16
 	return e.error
17 17
 }
18 18
 
... ...
@@ -45,7 +45,7 @@ func setLocation(err error, location []Range, add bool) error {
45 45
 	if err == nil {
46 46
 		return nil
47 47
 	}
48
-	var el *ErrorLocation
48
+	var el *LocationError
49 49
 	if errors.As(err, &el) {
50 50
 		if add {
51 51
 			el.Locations = append(el.Locations, location)
... ...
@@ -54,7 +54,7 @@ func setLocation(err error, location []Range, add bool) error {
54 54
 		}
55 55
 		return err
56 56
 	}
57
-	return stack.Enable(&ErrorLocation{
57
+	return stack.Enable(&LocationError{
58 58
 		error:     err,
59 59
 		Locations: [][]Range{location},
60 60
 	})
... ...
@@ -318,7 +318,7 @@ func parseMaybeJSON(rest string, d *directives) (*Node, map[string]bool, error)
318 318
 	if err == nil {
319 319
 		return node, attrs, nil
320 320
 	}
321
-	if err == errDockerfileNotStringArray {
321
+	if errors.Is(err, errDockerfileNotStringArray) {
322 322
 		return nil, nil, err
323 323
 	}
324 324
 
... ...
@@ -336,7 +336,7 @@ func parseMaybeJSONToList(rest string, d *directives) (*Node, map[string]bool, e
336 336
 	if err == nil {
337 337
 		return node, attrs, nil
338 338
 	}
339
-	if err == errDockerfileNotStringArray {
339
+	if errors.Is(err, errDockerfileNotStringArray) {
340 340
 		return nil, nil, err
341 341
 	}
342 342
 
... ...
@@ -114,7 +114,7 @@ type Heredoc struct {
114 114
 var (
115 115
 	dispatch      map[string]func(string, *directives) (*Node, map[string]bool, error)
116 116
 	reWhitespace  = regexp.MustCompile(`[\t\v\f\r ]+`)
117
-	reHeredoc     = regexp.MustCompile(`^(\d*)<<(-?)([^<]*)$`)
117
+	reHeredoc     = regexp.MustCompile(`^(\d*)<<(-?)\s*([^<]*)$`)
118 118
 	reLeadingTabs = regexp.MustCompile(`(?m)^\t+`)
119 119
 )
120 120
 
... ...
@@ -556,8 +556,8 @@ func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
556 556
 }
557 557
 
558 558
 func handleScannerError(err error) error {
559
-	switch err {
560
-	case bufio.ErrTooLong:
559
+	switch {
560
+	case errors.Is(err, bufio.ErrTooLong):
561 561
 		return errors.Errorf("dockerfile line greater than max allowed size of %d", bufio.MaxScanTokenSize-1)
562 562
 	default:
563 563
 		return err
... ...
@@ -177,6 +177,7 @@ func (sw *shellWord) processStopOn(stopChar rune, rawEscapes bool) (string, []st
177 177
 	// no need to initialize all the time
178 178
 	var charFuncMapping = map[rune]func() (string, error){
179 179
 		'$': sw.processDollar,
180
+		'<': sw.processPossibleHeredoc,
180 181
 	}
181 182
 	if !sw.SkipProcessQuotes {
182 183
 		charFuncMapping['\''] = sw.processSingleQuote
... ...
@@ -512,6 +513,25 @@ func (sw *shellWord) processName() string {
512 512
 	return name.String()
513 513
 }
514 514
 
515
+func (sw *shellWord) processPossibleHeredoc() (string, error) {
516
+	sw.scanner.Next()
517
+	if sw.scanner.Peek() != '<' {
518
+		return "<", nil // not a heredoc
519
+	}
520
+	sw.scanner.Next()
521
+
522
+	// heredoc might have whitespace between << and word terminator
523
+	var space bytes.Buffer
524
+	nextCh := sw.scanner.Peek()
525
+	for isWhitespace(nextCh) {
526
+		space.WriteRune(nextCh)
527
+		sw.scanner.Next()
528
+		nextCh = sw.scanner.Peek()
529
+	}
530
+	result := "<<" + space.String()
531
+	return result, nil
532
+}
533
+
515 534
 // isSpecialParam checks if the provided character is a special parameters,
516 535
 // as defined in http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
517 536
 func isSpecialParam(char rune) bool {
... ...
@@ -677,3 +697,11 @@ func trimSuffix(pattern, word string, greedy bool) (string, error) {
677 677
 	}
678 678
 	return reverseString(str), nil
679 679
 }
680
+
681
+func isWhitespace(r rune) bool {
682
+	switch r {
683
+	case '\t', '\r', ' ':
684
+		return true
685
+	}
686
+	return false
687
+}
... ...
@@ -128,8 +128,8 @@ func parseSourceDateEpoch(v string) (*time.Time, error) {
128 128
 func parseLocalSessionIDs(opt map[string]string) map[string]string {
129 129
 	m := map[string]string{}
130 130
 	for k, v := range opt {
131
-		if strings.HasPrefix(k, localSessionIDPrefix) {
132
-			m[strings.TrimPrefix(k, localSessionIDPrefix)] = v
131
+		if after, ok := strings.CutPrefix(k, localSessionIDPrefix); ok {
132
+			m[after] = v
133 133
 		}
134 134
 	}
135 135
 	return m
... ...
@@ -138,8 +138,8 @@ func parseLocalSessionIDs(opt map[string]string) map[string]string {
138 138
 func filter(opt map[string]string, key string) map[string]string {
139 139
 	m := map[string]string{}
140 140
 	for k, v := range opt {
141
-		if strings.HasPrefix(k, key) {
142
-			m[strings.TrimPrefix(k, key)] = v
141
+		if after, ok := strings.CutPrefix(k, key); ok {
142
+			m[after] = v
143 143
 		}
144 144
 	}
145 145
 	return m
... ...
@@ -22,7 +22,6 @@ func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, erro
22 22
 
23 23
 	targets := make([]*ocispecs.Platform, 0, len(bc.TargetPlatforms))
24 24
 	for _, p := range bc.TargetPlatforms {
25
-		p := p
26 25
 		targets = append(targets, &p)
27 26
 	}
28 27
 	if len(targets) == 0 {
... ...
@@ -106,7 +105,6 @@ func (rb *ResultBuilder) Finalize() (*client.Result, error) {
106 106
 func (rb *ResultBuilder) EachPlatform(ctx context.Context, fn func(ctx context.Context, id string, p ocispecs.Platform) error) error {
107 107
 	eg, ctx := errgroup.WithContext(ctx)
108 108
 	for _, p := range rb.expPlatforms.Platforms {
109
-		p := p
110 109
 		eg.Go(func() error {
111 110
 			return fn(ctx, p.ID, p.Platform)
112 111
 		})
... ...
@@ -13,6 +13,12 @@ import (
13 13
 	digest "github.com/opencontainers/go-digest"
14 14
 )
15 15
 
16
+const (
17
+	// KeySource is the option key used by the gateway frontend to represent
18
+	// the source for the external frontend
19
+	KeySource = "source"
20
+)
21
+
16 22
 type Result = result.Result[solver.ResultProxy]
17 23
 
18 24
 type Attestation = result.Attestation[solver.ResultProxy]
... ...
@@ -1,11 +1,12 @@
1 1
 package container
2 2
 
3 3
 import (
4
+	"cmp"
4 5
 	"context"
5 6
 	"fmt"
6 7
 	"path/filepath"
7 8
 	"runtime"
8
-	"sort"
9
+	"slices"
9 10
 	"strings"
10 11
 	"sync"
11 12
 	"syscall"
... ...
@@ -20,10 +21,8 @@ import (
20 20
 	"github.com/moby/buildkit/session"
21 21
 	"github.com/moby/buildkit/snapshot"
22 22
 	"github.com/moby/buildkit/solver/llbsolver/mounts"
23
-	"github.com/moby/buildkit/solver/pb"
24 23
 	opspb "github.com/moby/buildkit/solver/pb"
25 24
 	"github.com/moby/buildkit/util/stack"
26
-	utilsystem "github.com/moby/buildkit/util/system"
27 25
 	"github.com/moby/buildkit/worker"
28 26
 	"github.com/pkg/errors"
29 27
 	"golang.org/x/sync/errgroup"
... ...
@@ -79,9 +78,9 @@ func NewContainer(ctx context.Context, cm cache.Manager, exec executor.Executor,
79 79
 		mnts = append(mnts, m.Mount)
80 80
 		if m.WorkerRef != nil {
81 81
 			refs = append(refs, m.WorkerRef)
82
-			m.Mount.Input = int64(len(refs) - 1)
82
+			m.Input = int64(len(refs) - 1)
83 83
 		} else {
84
-			m.Mount.Input = int64(opspb.Empty)
84
+			m.Input = int64(opspb.Empty)
85 85
 		}
86 86
 	}
87 87
 
... ...
@@ -106,13 +105,11 @@ func NewContainer(ctx context.Context, cm cache.Manager, exec executor.Executor,
106 106
 	ctr.mounts = p.Mounts
107 107
 
108 108
 	for _, o := range p.OutputRefs {
109
-		o := o
110 109
 		ctr.cleanup = append(ctr.cleanup, func() error {
111 110
 			return o.Ref.Release(context.TODO())
112 111
 		})
113 112
 	}
114 113
 	for _, active := range p.Actives {
115
-		active := active
116 114
 		ctr.cleanup = append(ctr.cleanup, func() error {
117 115
 			return active.Ref.Release(context.TODO())
118 116
 		})
... ...
@@ -276,8 +273,8 @@ func PrepareMounts(ctx context.Context, mm *mounts.MountManager, cm cache.Manage
276 276
 	}
277 277
 
278 278
 	// sort mounts so parents are mounted first
279
-	sort.Slice(p.Mounts, func(i, j int) bool {
280
-		return p.Mounts[i].Dest < p.Mounts[j].Dest
279
+	slices.SortFunc(p.Mounts, func(a, b executor.Mount) int {
280
+		return cmp.Compare(a.Dest, b.Dest)
281 281
 	})
282 282
 
283 283
 	return p, nil
... ...
@@ -327,7 +324,7 @@ func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartReques
327 327
 	if procInfo.Meta.Cwd == "" {
328 328
 		procInfo.Meta.Cwd = "/"
329 329
 	}
330
-	procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "PATH", utilsystem.DefaultPathEnv(gwCtr.platform.OS))
330
+	procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "PATH", system.DefaultPathEnv(gwCtr.platform.OS))
331 331
 	if req.Tty {
332 332
 		procInfo.Meta.Env = addDefaultEnvvar(procInfo.Meta.Env, "TERM", "xterm")
333 333
 	}
... ...
@@ -377,7 +374,7 @@ func (gwCtr *gatewayContainer) Start(ctx context.Context, req client.StartReques
377 377
 	return gwProc, nil
378 378
 }
379 379
 
380
-func (gwCtr *gatewayContainer) loadSecretEnv(ctx context.Context, secretEnv []*pb.SecretEnv) ([]string, error) {
380
+func (gwCtr *gatewayContainer) loadSecretEnv(ctx context.Context, secretEnv []*opspb.SecretEnv) ([]string, error) {
381 381
 	out := make([]string, 0, len(secretEnv))
382 382
 	for _, sopt := range secretEnv {
383 383
 		id := sopt.ID
... ...
@@ -393,7 +390,7 @@ func (gwCtr *gatewayContainer) loadSecretEnv(ctx context.Context, secretEnv []*p
393 393
 			}
394 394
 			return nil
395 395
 		})
396
-		if err != nil && !(errors.Is(err, secrets.ErrNotFound) && sopt.Optional) {
396
+		if err != nil && (!errors.Is(err, secrets.ErrNotFound) || !sopt.Optional) {
397 397
 			return nil, err
398 398
 		}
399 399
 		out = append(out, fmt.Sprintf("%s=%s", sopt.Name, string(dt)))
... ...
@@ -59,15 +59,7 @@ type BridgeClient struct {
59 59
 }
60 60
 
61 61
 func (c *BridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) {
62
-	res, err := c.FrontendLLBBridge.Solve(ctx, frontend.SolveRequest{
63
-		Evaluate:       req.Evaluate,
64
-		Definition:     req.Definition,
65
-		Frontend:       req.Frontend,
66
-		FrontendOpt:    req.FrontendOpt,
67
-		FrontendInputs: req.FrontendInputs,
68
-		CacheImports:   req.CacheImports,
69
-		SourcePolicies: req.SourcePolicies,
70
-	}, c.sid)
62
+	res, err := c.FrontendLLBBridge.Solve(ctx, req, c.sid)
71 63
 	if err != nil {
72 64
 		return nil, c.wrapSolveError(err)
73 65
 	}
... ...
@@ -63,8 +63,7 @@ import (
63 63
 )
64 64
 
65 65
 const (
66
-	keySource = "source"
67
-	keyDevel  = "gateway-devel"
66
+	keyDevel = "gateway-devel"
68 67
 )
69 68
 
70 69
 func NewGatewayFrontend(workers worker.Infos, allowedRepositories []string) (frontend.Frontend, error) {
... ...
@@ -92,8 +91,8 @@ type gatewayFrontend struct {
92 92
 func filterPrefix(opts map[string]string, pfx string) map[string]string {
93 93
 	m := map[string]string{}
94 94
 	for k, v := range opts {
95
-		if strings.HasPrefix(k, pfx) {
96
-			m[strings.TrimPrefix(k, pfx)] = v
95
+		if after, ok := strings.CutPrefix(k, pfx); ok {
96
+			m[after] = v
97 97
 		}
98 98
 	}
99 99
 	return m
... ...
@@ -122,7 +121,7 @@ func (gf *gatewayFrontend) checkSourceIsAllowed(source string) error {
122 122
 }
123 123
 
124 124
 func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, opts map[string]string, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*frontend.Result, error) {
125
-	source, ok := opts[keySource]
125
+	source, ok := opts[frontend.KeySource]
126 126
 	if !ok {
127 127
 		return nil, errors.Errorf("no source specified for gateway")
128 128
 	}
... ...
@@ -1573,7 +1572,7 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e
1573 1573
 						}()
1574 1574
 						dest := &outputWriter{
1575 1575
 							stream:    srv,
1576
-							fd:        uint32(fd),
1576
+							fd:        fd,
1577 1577
 							processID: pid,
1578 1578
 						}
1579 1579
 						_, err := io.Copy(dest, file)
... ...
@@ -1587,7 +1586,7 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e
1587 1587
 							ProcessID: pid,
1588 1588
 							Input: &pb.ExecMessage_File{
1589 1589
 								File: &pb.FdMessage{
1590
-									Fd:  uint32(fd),
1590
+									Fd:  fd,
1591 1591
 									EOF: true,
1592 1592
 								},
1593 1593
 							},
... ...
@@ -44,7 +44,7 @@ type GrpcClient interface {
44 44
 
45 45
 func New(ctx context.Context, opts map[string]string, session, product string, c pb.LLBBridgeClient, w []client.WorkerInfo) (GrpcClient, error) {
46 46
 	pingCtx, pingCancel := context.WithCancelCause(ctx)
47
-	pingCtx, _ = context.WithTimeoutCause(pingCtx, 15*time.Second, errors.WithStack(context.DeadlineExceeded))
47
+	pingCtx, _ = context.WithTimeoutCause(pingCtx, 15*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
48 48
 	defer pingCancel(errors.WithStack(context.Canceled))
49 49
 	resp, err := c.Ping(pingCtx, &pb.PingRequest{})
50 50
 	if err != nil {
... ...
@@ -1,4 +1,4 @@
1
-package moby_buildkit_v1_frontend //nolint:revive
1
+package moby_buildkit_v1_frontend //nolint:revive,staticcheck
2 2
 
3 3
 import "github.com/moby/buildkit/util/apicaps"
4 4
 
... ...
@@ -1,4 +1,4 @@
1
-package moby_buildkit_v1_frontend //nolint:revive
1
+package moby_buildkit_v1_frontend //nolint:revive,staticcheck
2 2
 
3 3
 import (
4 4
 	"fmt"
... ...
@@ -32,7 +32,7 @@ type bufferedWriteCloser struct {
32 32
 }
33 33
 
34 34
 func (bwc *bufferedWriteCloser) Close() error {
35
-	if err := bwc.Writer.Flush(); err != nil {
35
+	if err := bwc.Flush(); err != nil {
36 36
 		return errors.WithStack(err)
37 37
 	}
38 38
 	return bwc.Closer.Close()
... ...
@@ -59,10 +59,10 @@ func (wc *streamWriterCloser) Write(dt []byte) (int, error) {
59 59
 		return n1 + n2, nil
60 60
 	}
61 61
 
62
-	if err := wc.ClientStream.SendMsg(&BytesMessage{Data: dt}); err != nil {
62
+	if err := wc.SendMsg(&BytesMessage{Data: dt}); err != nil {
63 63
 		// SendMsg return EOF on remote errors
64 64
 		if errors.Is(err, io.EOF) {
65
-			if err := errors.WithStack(wc.ClientStream.RecvMsg(struct{}{})); err != nil {
65
+			if err := errors.WithStack(wc.RecvMsg(struct{}{})); err != nil {
66 66
 				return 0, err
67 67
 			}
68 68
 		}
... ...
@@ -72,12 +72,12 @@ func (wc *streamWriterCloser) Write(dt []byte) (int, error) {
72 72
 }
73 73
 
74 74
 func (wc *streamWriterCloser) Close() error {
75
-	if err := wc.ClientStream.CloseSend(); err != nil {
75
+	if err := wc.CloseSend(); err != nil {
76 76
 		return errors.WithStack(err)
77 77
 	}
78 78
 	// block until receiver is done
79 79
 	var bm BytesMessage
80
-	if err := wc.ClientStream.RecvMsg(&bm); err != io.EOF {
80
+	if err := wc.RecvMsg(&bm); !errors.Is(err, io.EOF) {
81 81
 		return errors.WithStack(err)
82 82
 	}
83 83
 	return nil
... ...
@@ -336,8 +336,8 @@ func (sp *SyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) {
336 336
 	opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object
337 337
 	md := map[string]string{}
338 338
 	for k, v := range opts {
339
-		if strings.HasPrefix(k, keyExporterMetaPrefix) {
340
-			md[strings.TrimPrefix(k, keyExporterMetaPrefix)] = strings.Join(v, ",")
339
+		if after, ok0 := strings.CutPrefix(k, keyExporterMetaPrefix); ok0 {
340
+			md[after] = strings.Join(v, ",")
341 341
 		}
342 342
 	}
343 343
 	wc, err := f(md)
... ...
@@ -73,7 +73,7 @@ func (sm *Manager) Any(ctx context.Context, g Group, f func(context.Context, str
73 73
 		}
74 74
 
75 75
 		timeoutCtx, cancel := context.WithCancelCause(ctx)
76
-		timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
76
+		timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
77 77
 		defer func() { cancel(errors.WithStack(context.Canceled)) }()
78 78
 		c, err := sm.Get(timeoutCtx, id, false)
79 79
 		if err != nil {
... ...
@@ -94,7 +94,7 @@ func monitorHealth(ctx context.Context, cc *grpc.ClientConn, cancelConn func(err
94 94
 			timeout := time.Duration(math.Max(float64(defaultHealthcheckDuration), float64(lastHealthcheckDuration)*1.5))
95 95
 
96 96
 			ctx, cancel := context.WithCancelCause(ctx)
97
-			ctx, _ = context.WithTimeoutCause(ctx, timeout, errors.WithStack(context.DeadlineExceeded))
97
+			ctx, _ = context.WithTimeoutCause(ctx, timeout, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
98 98
 			_, err := healthClient.Check(ctx, &grpc_health_v1.HealthCheckRequest{})
99 99
 			cancel(errors.WithStack(context.Canceled))
100 100
 
... ...
@@ -2,6 +2,7 @@ package grpchijack
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
5 6
 	"io"
6 7
 	"net"
7 8
 	"strings"
... ...
@@ -112,7 +113,7 @@ func (c *conn) Close() (err error) {
112 112
 			m.Data = c.buf
113 113
 			err = c.stream.RecvMsg(m)
114 114
 			if err != nil {
115
-				if err != io.EOF {
115
+				if !errors.Is(err, io.EOF) {
116 116
 					c.readMu.Unlock()
117 117
 					return
118 118
 				}
... ...
@@ -21,7 +21,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream Stream, closeStre
21 21
 		p := &BytesMessage{}
22 22
 		for {
23 23
 			if err := stream.RecvMsg(p); err != nil {
24
-				if err == io.EOF {
24
+				if errors.Is(err, io.EOF) {
25 25
 					// indicates client performed CloseSend, but they may still be
26 26
 					// reading data
27 27
 					if closeWriter, ok := conn.(interface {
... ...
@@ -55,7 +55,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream Stream, closeStre
55 55
 			buf := make([]byte, 32*1024)
56 56
 			n, err := conn.Read(buf)
57 57
 			switch {
58
-			case err == io.EOF:
58
+			case errors.Is(err, io.EOF):
59 59
 				if closeStream != nil {
60 60
 					closeStream()
61 61
 				}
... ...
@@ -40,7 +40,7 @@ func (s *server) run(ctx context.Context, l net.Listener, id string) error {
40 40
 
41 41
 			opts := make(map[string][]string)
42 42
 			opts[KeySSHID] = []string{id}
43
-			ctx = metadata.NewOutgoingContext(ctx, opts)
43
+			ctx := metadata.NewOutgoingContext(ctx, opts)
44 44
 
45 45
 			stream, err := client.ForwardAgent(ctx)
46 46
 			if err != nil {
... ...
@@ -42,7 +42,7 @@ func (u *Upload) WriteTo(w io.Writer) (int64, error) {
42 42
 	for {
43 43
 		var bm BytesMessage
44 44
 		if err := u.cc.RecvMsg(&bm); err != nil {
45
-			if err == io.EOF {
45
+			if errors.Is(err, io.EOF) {
46 46
 				return n, nil
47 47
 			}
48 48
 			return n, errors.WithStack(err)
... ...
@@ -166,10 +166,10 @@ func applierFor(dest Mountable, tryCrossSnapshotLink, userxattr bool) (_ *applie
166 166
 
167 167
 	if overlay.IsOverlayMountType(mnt) {
168 168
 		for _, opt := range mnt.Options {
169
-			if strings.HasPrefix(opt, "upperdir=") {
170
-				a.root = strings.TrimPrefix(opt, "upperdir=")
171
-			} else if strings.HasPrefix(opt, "lowerdir=") {
172
-				a.lowerdirs = strings.Split(strings.TrimPrefix(opt, "lowerdir="), ":")
169
+			if after, ok := strings.CutPrefix(opt, "upperdir="); ok {
170
+				a.root = after
171
+			} else if after, ok := strings.CutPrefix(opt, "lowerdir="); ok {
172
+				a.lowerdirs = strings.Split(after, ":")
173 173
 			}
174 174
 		}
175 175
 		if a.root == "" {
... ...
@@ -2,11 +2,11 @@ package snapshot
2 2
 
3 3
 import (
4 4
 	"os"
5
+	"slices"
5 6
 
6 7
 	"github.com/containerd/containerd/v2/core/mount"
7 8
 	"github.com/pkg/errors"
8 9
 	"golang.org/x/sys/unix"
9
-	"slices"
10 10
 )
11 11
 
12 12
 func (lm *localMounter) Mount() (string, error) {
... ...
@@ -307,8 +307,8 @@ func (s *Store) emptyBranchWithParents(tx *bolt.Tx, id []byte) error {
307 307
 	}
308 308
 
309 309
 	// intentionally ignoring errors
310
-	tx.Bucket([]byte(linksBucket)).DeleteBucket([]byte(id))
311
-	tx.Bucket([]byte(resultBucket)).DeleteBucket([]byte(id))
310
+	tx.Bucket([]byte(linksBucket)).DeleteBucket(id)
311
+	tx.Bucket([]byte(resultBucket)).DeleteBucket(id)
312 312
 
313 313
 	return nil
314 314
 }
... ...
@@ -360,7 +360,7 @@ func (s *Store) WalkLinks(id string, link solver.CacheInfoLink, fn func(id strin
360 360
 		}
361 361
 		index := bytes.Join([][]byte{dt, {}}, []byte("@"))
362 362
 		c := b.Cursor()
363
-		k, _ := c.Seek([]byte(index))
363
+		k, _ := c.Seek(index)
364 364
 		for {
365 365
 			if k != nil && bytes.HasPrefix(k, index) {
366 366
 				target := bytes.TrimPrefix(k, index)
... ...
@@ -388,7 +388,7 @@ func (c *cacheManager) ensurePersistentKey(k *CacheKey) error {
388 388
 		for _, ck := range deps {
389 389
 			l := CacheInfoLink{
390 390
 				Input:    Index(i),
391
-				Output:   Index(k.Output()),
391
+				Output:   k.Output(),
392 392
 				Digest:   k.Digest(),
393 393
 				Selector: ck.Selector,
394 394
 			}
... ...
@@ -415,7 +415,7 @@ func (c *cacheManager) getIDFromDeps(k *CacheKey) string {
415 415
 				m2 := make(map[string]struct{})
416 416
 				if err := c.backend.WalkLinks(c.getID(ck.CacheKey.CacheKey), CacheInfoLink{
417 417
 					Input:    Index(i),
418
-					Output:   Index(k.Output()),
418
+					Output:   k.Output(),
419 419
 					Digest:   k.Digest(),
420 420
 					Selector: ck.Selector,
421 421
 				}, func(id string) error {
... ...
@@ -131,7 +131,6 @@ func (cm *combinedCacheManager) Records(ctx context.Context, ck *CacheKey) ([]*C
131 131
 
132 132
 	eg, _ := errgroup.WithContext(context.TODO())
133 133
 	for _, c := range cms {
134
-		c := c
135 134
 		eg.Go(func() error {
136 135
 			recs, err := c.Records(ctx, ck)
137 136
 			if err != nil {
... ...
@@ -154,19 +154,20 @@ func debugSchedulerPreUnparkSlow(e *edge, inc []pipeSender, updates, allPipes []
154 154
 	}
155 155
 
156 156
 	for i, up := range updates {
157
-		if up == e.cacheMapReq {
157
+		switch up {
158
+		case e.cacheMapReq:
158 159
 			log.
159 160
 				WithField("update_index", i).
160 161
 				WithField("update_pointer", up).
161 162
 				WithField("update_complete", up.Status().Completed).
162 163
 				Debug("> update cacheMapReq")
163
-		} else if up == e.execReq {
164
+		case e.execReq:
164 165
 			log.
165 166
 				WithField("update_index", i).
166 167
 				WithField("update_pointer", up).
167 168
 				WithField("update_complete", up.Status().Completed).
168 169
 				Debug("> update execReq")
169
-		} else {
170
+		default:
170 171
 			st, ok := up.Status().Value.(*edgeState)
171 172
 			if ok {
172 173
 				index := -1
... ...
@@ -658,7 +658,7 @@ func (e *edge) processDepReq(dep *dep) (depChanged bool) {
658 658
 		newKeys := state.keys[len(dep.keys):]
659 659
 		if e.cacheMap != nil {
660 660
 			e.probeCache(dep, withSelector(newKeys, e.cacheMap.Deps[dep.index].Selector))
661
-			dep.edgeState.keys = state.keys
661
+			dep.keys = state.keys
662 662
 			if e.allDepsHaveKeys(false) {
663 663
 				e.keysDidChange = true
664 664
 			}
... ...
@@ -119,6 +119,59 @@ func (x *Source) GetRanges() []*pb.Range {
119 119
 	return nil
120 120
 }
121 121
 
122
+type Frontend struct {
123
+	state         protoimpl.MessageState
124
+	sizeCache     protoimpl.SizeCache
125
+	unknownFields protoimpl.UnknownFields
126
+
127
+	Name   string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`     // frontend name e.g. dockerfile.v0 or gateway.v0
128
+	Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` // used by the gateway frontend to identify the source, which corresponds to the image name
129
+}
130
+
131
+func (x *Frontend) Reset() {
132
+	*x = Frontend{}
133
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[2]
134
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
135
+	ms.StoreMessageInfo(mi)
136
+}
137
+
138
+func (x *Frontend) String() string {
139
+	return protoimpl.X.MessageStringOf(x)
140
+}
141
+
142
+func (*Frontend) ProtoMessage() {}
143
+
144
+func (x *Frontend) ProtoReflect() protoreflect.Message {
145
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[2]
146
+	if x != nil {
147
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
148
+		if ms.LoadMessageInfo() == nil {
149
+			ms.StoreMessageInfo(mi)
150
+		}
151
+		return ms
152
+	}
153
+	return mi.MessageOf(x)
154
+}
155
+
156
+// Deprecated: Use Frontend.ProtoReflect.Descriptor instead.
157
+func (*Frontend) Descriptor() ([]byte, []int) {
158
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{2}
159
+}
160
+
161
+func (x *Frontend) GetName() string {
162
+	if x != nil {
163
+		return x.Name
164
+	}
165
+	return ""
166
+}
167
+
168
+func (x *Frontend) GetSource() string {
169
+	if x != nil {
170
+		return x.Source
171
+	}
172
+	return ""
173
+}
174
+
122 175
 type FrontendCap struct {
123 176
 	state         protoimpl.MessageState
124 177
 	sizeCache     protoimpl.SizeCache
... ...
@@ -129,7 +182,7 @@ type FrontendCap struct {
129 129
 
130 130
 func (x *FrontendCap) Reset() {
131 131
 	*x = FrontendCap{}
132
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[2]
132
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[3]
133 133
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
134 134
 	ms.StoreMessageInfo(mi)
135 135
 }
... ...
@@ -141,7 +194,7 @@ func (x *FrontendCap) String() string {
141 141
 func (*FrontendCap) ProtoMessage() {}
142 142
 
143 143
 func (x *FrontendCap) ProtoReflect() protoreflect.Message {
144
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[2]
144
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[3]
145 145
 	if x != nil {
146 146
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
147 147
 		if ms.LoadMessageInfo() == nil {
... ...
@@ -154,7 +207,7 @@ func (x *FrontendCap) ProtoReflect() protoreflect.Message {
154 154
 
155 155
 // Deprecated: Use FrontendCap.ProtoReflect.Descriptor instead.
156 156
 func (*FrontendCap) Descriptor() ([]byte, []int) {
157
-	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{2}
157
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{3}
158 158
 }
159 159
 
160 160
 func (x *FrontendCap) GetName() string {
... ...
@@ -174,7 +227,7 @@ type Subrequest struct {
174 174
 
175 175
 func (x *Subrequest) Reset() {
176 176
 	*x = Subrequest{}
177
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[3]
177
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
178 178
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
179 179
 	ms.StoreMessageInfo(mi)
180 180
 }
... ...
@@ -186,7 +239,7 @@ func (x *Subrequest) String() string {
186 186
 func (*Subrequest) ProtoMessage() {}
187 187
 
188 188
 func (x *Subrequest) ProtoReflect() protoreflect.Message {
189
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[3]
189
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
190 190
 	if x != nil {
191 191
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
192 192
 		if ms.LoadMessageInfo() == nil {
... ...
@@ -199,7 +252,7 @@ func (x *Subrequest) ProtoReflect() protoreflect.Message {
199 199
 
200 200
 // Deprecated: Use Subrequest.ProtoReflect.Descriptor instead.
201 201
 func (*Subrequest) Descriptor() ([]byte, []int) {
202
-	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{3}
202
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{4}
203 203
 }
204 204
 
205 205
 func (x *Subrequest) GetName() string {
... ...
@@ -227,7 +280,7 @@ type Solve struct {
227 227
 
228 228
 func (x *Solve) Reset() {
229 229
 	*x = Solve{}
230
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
230
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
231 231
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
232 232
 	ms.StoreMessageInfo(mi)
233 233
 }
... ...
@@ -239,7 +292,7 @@ func (x *Solve) String() string {
239 239
 func (*Solve) ProtoMessage() {}
240 240
 
241 241
 func (x *Solve) ProtoReflect() protoreflect.Message {
242
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4]
242
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
243 243
 	if x != nil {
244 244
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
245 245
 		if ms.LoadMessageInfo() == nil {
... ...
@@ -252,7 +305,7 @@ func (x *Solve) ProtoReflect() protoreflect.Message {
252 252
 
253 253
 // Deprecated: Use Solve.ProtoReflect.Descriptor instead.
254 254
 func (*Solve) Descriptor() ([]byte, []int) {
255
-	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{4}
255
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{5}
256 256
 }
257 257
 
258 258
 func (x *Solve) GetInputIDs() []string {
... ...
@@ -331,7 +384,7 @@ type FileAction struct {
331 331
 
332 332
 func (x *FileAction) Reset() {
333 333
 	*x = FileAction{}
334
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
334
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
335 335
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
336 336
 	ms.StoreMessageInfo(mi)
337 337
 }
... ...
@@ -343,7 +396,7 @@ func (x *FileAction) String() string {
343 343
 func (*FileAction) ProtoMessage() {}
344 344
 
345 345
 func (x *FileAction) ProtoReflect() protoreflect.Message {
346
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5]
346
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
347 347
 	if x != nil {
348 348
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
349 349
 		if ms.LoadMessageInfo() == nil {
... ...
@@ -356,7 +409,7 @@ func (x *FileAction) ProtoReflect() protoreflect.Message {
356 356
 
357 357
 // Deprecated: Use FileAction.ProtoReflect.Descriptor instead.
358 358
 func (*FileAction) Descriptor() ([]byte, []int) {
359
-	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{5}
359
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{6}
360 360
 }
361 361
 
362 362
 func (x *FileAction) GetIndex() int64 {
... ...
@@ -377,7 +430,7 @@ type ContentCache struct {
377 377
 
378 378
 func (x *ContentCache) Reset() {
379 379
 	*x = ContentCache{}
380
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
380
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
381 381
 	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
382 382
 	ms.StoreMessageInfo(mi)
383 383
 }
... ...
@@ -389,7 +442,7 @@ func (x *ContentCache) String() string {
389 389
 func (*ContentCache) ProtoMessage() {}
390 390
 
391 391
 func (x *ContentCache) ProtoReflect() protoreflect.Message {
392
-	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[6]
392
+	mi := &file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[7]
393 393
 	if x != nil {
394 394
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
395 395
 		if ms.LoadMessageInfo() == nil {
... ...
@@ -402,7 +455,7 @@ func (x *ContentCache) ProtoReflect() protoreflect.Message {
402 402
 
403 403
 // Deprecated: Use ContentCache.ProtoReflect.Descriptor instead.
404 404
 func (*ContentCache) Descriptor() ([]byte, []int) {
405
-	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{6}
405
+	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP(), []int{7}
406 406
 }
407 407
 
408 408
 func (x *ContentCache) GetIndex() int64 {
... ...
@@ -429,39 +482,42 @@ var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDesc = []byte{
429 429
 	0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x21,
430 430
 	0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09,
431 431
 	0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65,
432
-	0x73, 0x22, 0x21, 0x0a, 0x0b, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x43, 0x61, 0x70,
433
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
434
-	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x0a, 0x0a, 0x53, 0x75, 0x62, 0x72, 0x65, 0x71, 0x75, 0x65,
435
-	0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
436
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xbf, 0x02, 0x0a, 0x05, 0x53, 0x6f, 0x6c, 0x76, 0x65,
437
-	0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03,
438
-	0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x73, 0x12, 0x1a, 0x0a, 0x08,
439
-	0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,
440
-	0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x03,
441
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x52, 0x02, 0x6f, 0x70,
442
-	0x12, 0x29, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
443
-	0x2e, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74,
444
-	0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x63,
445
-	0x61, 0x63, 0x68, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x65, 0x72, 0x72,
446
-	0x64, 0x65, 0x66, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68,
447
-	0x65, 0x48, 0x00, 0x52, 0x05, 0x63, 0x61, 0x63, 0x68, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x64, 0x65,
448
-	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32,
449
-	0x1f, 0x2e, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x2e,
450
-	0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79,
451
-	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3e, 0x0a,
452
-	0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72,
453
-	0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
454
-	0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
455
-	0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a,
456
-	0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x22, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65,
457
-	0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18,
458
-	0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x24, 0x0a, 0x0c,
459
-	0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x14, 0x0a, 0x05,
460
-	0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64,
461
-	0x65, 0x78, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
462
-	0x2f, 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x73,
463
-	0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2f, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x62, 0x06, 0x70,
464
-	0x72, 0x6f, 0x74, 0x6f, 0x33,
432
+	0x73, 0x22, 0x36, 0x0a, 0x08, 0x46, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a,
433
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
434
+	0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
435
+	0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x21, 0x0a, 0x0b, 0x46, 0x72, 0x6f,
436
+	0x6e, 0x74, 0x65, 0x6e, 0x64, 0x43, 0x61, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
437
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x20, 0x0a, 0x0a,
438
+	0x53, 0x75, 0x62, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
439
+	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xbf,
440
+	0x02, 0x0a, 0x05, 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x70, 0x75,
441
+	0x74, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x70, 0x75,
442
+	0x74, 0x49, 0x44, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x73,
443
+	0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x44, 0x73,
444
+	0x12, 0x16, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x70,
445
+	0x62, 0x2e, 0x4f, 0x70, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x29, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65,
446
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73,
447
+	0x2e, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x66,
448
+	0x69, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x05, 0x20, 0x01,
449
+	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
450
+	0x74, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x48, 0x00, 0x52, 0x05, 0x63, 0x61, 0x63,
451
+	0x68, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
452
+	0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x72, 0x72, 0x64, 0x65, 0x66,
453
+	0x73, 0x2e, 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
454
+	0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
455
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3e, 0x0a, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
456
+	0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
457
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
458
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
459
+	0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74,
460
+	0x22, 0x22, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14,
461
+	0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x69,
462
+	0x6e, 0x64, 0x65, 0x78, 0x22, 0x24, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x43,
463
+	0x61, 0x63, 0x68, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20,
464
+	0x01, 0x28, 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69,
465
+	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x62, 0x79, 0x2f, 0x62, 0x75,
466
+	0x69, 0x6c, 0x64, 0x6b, 0x69, 0x74, 0x2f, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2f, 0x65, 0x72,
467
+	0x72, 0x64, 0x65, 0x66, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
465 468
 }
466 469
 
467 470
 var (
... ...
@@ -476,27 +532,28 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescGZIP() []
476 476
 	return file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDescData
477 477
 }
478 478
 
479
-var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
479
+var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
480 480
 var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_goTypes = []any{
481 481
 	(*Vertex)(nil),        // 0: errdefs.Vertex
482 482
 	(*Source)(nil),        // 1: errdefs.Source
483
-	(*FrontendCap)(nil),   // 2: errdefs.FrontendCap
484
-	(*Subrequest)(nil),    // 3: errdefs.Subrequest
485
-	(*Solve)(nil),         // 4: errdefs.Solve
486
-	(*FileAction)(nil),    // 5: errdefs.FileAction
487
-	(*ContentCache)(nil),  // 6: errdefs.ContentCache
488
-	nil,                   // 7: errdefs.Solve.DescriptionEntry
489
-	(*pb.SourceInfo)(nil), // 8: pb.SourceInfo
490
-	(*pb.Range)(nil),      // 9: pb.Range
491
-	(*pb.Op)(nil),         // 10: pb.Op
483
+	(*Frontend)(nil),      // 2: errdefs.Frontend
484
+	(*FrontendCap)(nil),   // 3: errdefs.FrontendCap
485
+	(*Subrequest)(nil),    // 4: errdefs.Subrequest
486
+	(*Solve)(nil),         // 5: errdefs.Solve
487
+	(*FileAction)(nil),    // 6: errdefs.FileAction
488
+	(*ContentCache)(nil),  // 7: errdefs.ContentCache
489
+	nil,                   // 8: errdefs.Solve.DescriptionEntry
490
+	(*pb.SourceInfo)(nil), // 9: pb.SourceInfo
491
+	(*pb.Range)(nil),      // 10: pb.Range
492
+	(*pb.Op)(nil),         // 11: pb.Op
492 493
 }
493 494
 var file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_depIdxs = []int32{
494
-	8,  // 0: errdefs.Source.info:type_name -> pb.SourceInfo
495
-	9,  // 1: errdefs.Source.ranges:type_name -> pb.Range
496
-	10, // 2: errdefs.Solve.op:type_name -> pb.Op
497
-	5,  // 3: errdefs.Solve.file:type_name -> errdefs.FileAction
498
-	6,  // 4: errdefs.Solve.cache:type_name -> errdefs.ContentCache
499
-	7,  // 5: errdefs.Solve.description:type_name -> errdefs.Solve.DescriptionEntry
495
+	9,  // 0: errdefs.Source.info:type_name -> pb.SourceInfo
496
+	10, // 1: errdefs.Source.ranges:type_name -> pb.Range
497
+	11, // 2: errdefs.Solve.op:type_name -> pb.Op
498
+	6,  // 3: errdefs.Solve.file:type_name -> errdefs.FileAction
499
+	7,  // 4: errdefs.Solve.cache:type_name -> errdefs.ContentCache
500
+	8,  // 5: errdefs.Solve.description:type_name -> errdefs.Solve.DescriptionEntry
500 501
 	6,  // [6:6] is the sub-list for method output_type
501 502
 	6,  // [6:6] is the sub-list for method input_type
502 503
 	6,  // [6:6] is the sub-list for extension type_name
... ...
@@ -509,7 +566,7 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_init() {
509 509
 	if File_github_com_moby_buildkit_solver_errdefs_errdefs_proto != nil {
510 510
 		return
511 511
 	}
512
-	file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[4].OneofWrappers = []any{
512
+	file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_msgTypes[5].OneofWrappers = []any{
513 513
 		(*Solve_File)(nil),
514 514
 		(*Solve_Cache)(nil),
515 515
 	}
... ...
@@ -519,7 +576,7 @@ func file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_init() {
519 519
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
520 520
 			RawDescriptor: file_github_com_moby_buildkit_solver_errdefs_errdefs_proto_rawDesc,
521 521
 			NumEnums:      0,
522
-			NumMessages:   8,
522
+			NumMessages:   9,
523 523
 			NumExtensions: 0,
524 524
 			NumServices:   0,
525 525
 		},
... ...
@@ -15,6 +15,11 @@ message Source {
15 15
 	repeated pb.Range ranges = 2;
16 16
 }
17 17
 
18
+message Frontend {
19
+	string name = 1; // frontend name e.g. dockerfile.v0 or gateway.v0
20
+	string source = 2; // used by the gateway frontend to identify the source, which corresponds to the image name
21
+}
22
+
18 23
 message FrontendCap {
19 24
 	string name = 1;
20 25
 }
... ...
@@ -61,6 +61,24 @@ func (m *Source) CloneMessageVT() proto.Message {
61 61
 	return m.CloneVT()
62 62
 }
63 63
 
64
+func (m *Frontend) CloneVT() *Frontend {
65
+	if m == nil {
66
+		return (*Frontend)(nil)
67
+	}
68
+	r := new(Frontend)
69
+	r.Name = m.Name
70
+	r.Source = m.Source
71
+	if len(m.unknownFields) > 0 {
72
+		r.unknownFields = make([]byte, len(m.unknownFields))
73
+		copy(r.unknownFields, m.unknownFields)
74
+	}
75
+	return r
76
+}
77
+
78
+func (m *Frontend) CloneMessageVT() proto.Message {
79
+	return m.CloneVT()
80
+}
81
+
64 82
 func (m *FrontendCap) CloneVT() *FrontendCap {
65 83
 	if m == nil {
66 84
 		return (*FrontendCap)(nil)
... ...
@@ -239,6 +257,28 @@ func (this *Source) EqualMessageVT(thatMsg proto.Message) bool {
239 239
 	}
240 240
 	return this.EqualVT(that)
241 241
 }
242
+func (this *Frontend) EqualVT(that *Frontend) bool {
243
+	if this == that {
244
+		return true
245
+	} else if this == nil || that == nil {
246
+		return false
247
+	}
248
+	if this.Name != that.Name {
249
+		return false
250
+	}
251
+	if this.Source != that.Source {
252
+		return false
253
+	}
254
+	return string(this.unknownFields) == string(that.unknownFields)
255
+}
256
+
257
+func (this *Frontend) EqualMessageVT(thatMsg proto.Message) bool {
258
+	that, ok := thatMsg.(*Frontend)
259
+	if !ok {
260
+		return false
261
+	}
262
+	return this.EqualVT(that)
263
+}
242 264
 func (this *FrontendCap) EqualVT(that *FrontendCap) bool {
243 265
 	if this == that {
244 266
 		return true
... ...
@@ -519,6 +559,53 @@ func (m *Source) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
519 519
 	return len(dAtA) - i, nil
520 520
 }
521 521
 
522
+func (m *Frontend) MarshalVT() (dAtA []byte, err error) {
523
+	if m == nil {
524
+		return nil, nil
525
+	}
526
+	size := m.SizeVT()
527
+	dAtA = make([]byte, size)
528
+	n, err := m.MarshalToSizedBufferVT(dAtA[:size])
529
+	if err != nil {
530
+		return nil, err
531
+	}
532
+	return dAtA[:n], nil
533
+}
534
+
535
+func (m *Frontend) MarshalToVT(dAtA []byte) (int, error) {
536
+	size := m.SizeVT()
537
+	return m.MarshalToSizedBufferVT(dAtA[:size])
538
+}
539
+
540
+func (m *Frontend) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
541
+	if m == nil {
542
+		return 0, nil
543
+	}
544
+	i := len(dAtA)
545
+	_ = i
546
+	var l int
547
+	_ = l
548
+	if m.unknownFields != nil {
549
+		i -= len(m.unknownFields)
550
+		copy(dAtA[i:], m.unknownFields)
551
+	}
552
+	if len(m.Source) > 0 {
553
+		i -= len(m.Source)
554
+		copy(dAtA[i:], m.Source)
555
+		i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Source)))
556
+		i--
557
+		dAtA[i] = 0x12
558
+	}
559
+	if len(m.Name) > 0 {
560
+		i -= len(m.Name)
561
+		copy(dAtA[i:], m.Name)
562
+		i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name)))
563
+		i--
564
+		dAtA[i] = 0xa
565
+	}
566
+	return len(dAtA) - i, nil
567
+}
568
+
522 569
 func (m *FrontendCap) MarshalVT() (dAtA []byte, err error) {
523 570
 	if m == nil {
524 571
 		return nil, nil
... ...
@@ -844,6 +931,24 @@ func (m *Source) SizeVT() (n int) {
844 844
 	return n
845 845
 }
846 846
 
847
+func (m *Frontend) SizeVT() (n int) {
848
+	if m == nil {
849
+		return 0
850
+	}
851
+	var l int
852
+	_ = l
853
+	l = len(m.Name)
854
+	if l > 0 {
855
+		n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
856
+	}
857
+	l = len(m.Source)
858
+	if l > 0 {
859
+		n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
860
+	}
861
+	n += len(m.unknownFields)
862
+	return n
863
+}
864
+
847 865
 func (m *FrontendCap) SizeVT() (n int) {
848 866
 	if m == nil {
849 867
 		return 0
... ...
@@ -1167,6 +1272,121 @@ func (m *Source) UnmarshalVT(dAtA []byte) error {
1167 1167
 	}
1168 1168
 	return nil
1169 1169
 }
1170
+func (m *Frontend) UnmarshalVT(dAtA []byte) error {
1171
+	l := len(dAtA)
1172
+	iNdEx := 0
1173
+	for iNdEx < l {
1174
+		preIndex := iNdEx
1175
+		var wire uint64
1176
+		for shift := uint(0); ; shift += 7 {
1177
+			if shift >= 64 {
1178
+				return protohelpers.ErrIntOverflow
1179
+			}
1180
+			if iNdEx >= l {
1181
+				return io.ErrUnexpectedEOF
1182
+			}
1183
+			b := dAtA[iNdEx]
1184
+			iNdEx++
1185
+			wire |= uint64(b&0x7F) << shift
1186
+			if b < 0x80 {
1187
+				break
1188
+			}
1189
+		}
1190
+		fieldNum := int32(wire >> 3)
1191
+		wireType := int(wire & 0x7)
1192
+		if wireType == 4 {
1193
+			return fmt.Errorf("proto: Frontend: wiretype end group for non-group")
1194
+		}
1195
+		if fieldNum <= 0 {
1196
+			return fmt.Errorf("proto: Frontend: illegal tag %d (wire type %d)", fieldNum, wire)
1197
+		}
1198
+		switch fieldNum {
1199
+		case 1:
1200
+			if wireType != 2 {
1201
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
1202
+			}
1203
+			var stringLen uint64
1204
+			for shift := uint(0); ; shift += 7 {
1205
+				if shift >= 64 {
1206
+					return protohelpers.ErrIntOverflow
1207
+				}
1208
+				if iNdEx >= l {
1209
+					return io.ErrUnexpectedEOF
1210
+				}
1211
+				b := dAtA[iNdEx]
1212
+				iNdEx++
1213
+				stringLen |= uint64(b&0x7F) << shift
1214
+				if b < 0x80 {
1215
+					break
1216
+				}
1217
+			}
1218
+			intStringLen := int(stringLen)
1219
+			if intStringLen < 0 {
1220
+				return protohelpers.ErrInvalidLength
1221
+			}
1222
+			postIndex := iNdEx + intStringLen
1223
+			if postIndex < 0 {
1224
+				return protohelpers.ErrInvalidLength
1225
+			}
1226
+			if postIndex > l {
1227
+				return io.ErrUnexpectedEOF
1228
+			}
1229
+			m.Name = string(dAtA[iNdEx:postIndex])
1230
+			iNdEx = postIndex
1231
+		case 2:
1232
+			if wireType != 2 {
1233
+				return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType)
1234
+			}
1235
+			var stringLen uint64
1236
+			for shift := uint(0); ; shift += 7 {
1237
+				if shift >= 64 {
1238
+					return protohelpers.ErrIntOverflow
1239
+				}
1240
+				if iNdEx >= l {
1241
+					return io.ErrUnexpectedEOF
1242
+				}
1243
+				b := dAtA[iNdEx]
1244
+				iNdEx++
1245
+				stringLen |= uint64(b&0x7F) << shift
1246
+				if b < 0x80 {
1247
+					break
1248
+				}
1249
+			}
1250
+			intStringLen := int(stringLen)
1251
+			if intStringLen < 0 {
1252
+				return protohelpers.ErrInvalidLength
1253
+			}
1254
+			postIndex := iNdEx + intStringLen
1255
+			if postIndex < 0 {
1256
+				return protohelpers.ErrInvalidLength
1257
+			}
1258
+			if postIndex > l {
1259
+				return io.ErrUnexpectedEOF
1260
+			}
1261
+			m.Source = string(dAtA[iNdEx:postIndex])
1262
+			iNdEx = postIndex
1263
+		default:
1264
+			iNdEx = preIndex
1265
+			skippy, err := protohelpers.Skip(dAtA[iNdEx:])
1266
+			if err != nil {
1267
+				return err
1268
+			}
1269
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
1270
+				return protohelpers.ErrInvalidLength
1271
+			}
1272
+			if (iNdEx + skippy) > l {
1273
+				return io.ErrUnexpectedEOF
1274
+			}
1275
+			m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
1276
+			iNdEx += skippy
1277
+		}
1278
+	}
1279
+
1280
+	if iNdEx > l {
1281
+		return io.ErrUnexpectedEOF
1282
+	}
1283
+	return nil
1284
+}
1170 1285
 func (m *FrontendCap) UnmarshalVT(dAtA []byte) error {
1171 1286
 	l := len(dAtA)
1172 1287
 	iNdEx := 0
1173 1288
deleted file mode 100644
... ...
@@ -1,41 +0,0 @@
1
-package errdefs
2
-
3
-import (
4
-	fmt "fmt"
5
-
6
-	"github.com/containerd/typeurl/v2"
7
-	"github.com/moby/buildkit/util/grpcerrors"
8
-)
9
-
10
-func init() {
11
-	typeurl.Register((*FrontendCap)(nil), "github.com/moby/buildkit", "errdefs.FrontendCap+json")
12
-}
13
-
14
-type UnsupportedFrontendCapError struct {
15
-	*FrontendCap
16
-	error
17
-}
18
-
19
-func (e *UnsupportedFrontendCapError) Error() string {
20
-	msg := fmt.Sprintf("unsupported frontend capability %s", e.FrontendCap.Name)
21
-	if e.error != nil {
22
-		msg += ": " + e.error.Error()
23
-	}
24
-	return msg
25
-}
26
-
27
-func (e *UnsupportedFrontendCapError) Unwrap() error {
28
-	return e.error
29
-}
30
-
31
-func (e *UnsupportedFrontendCapError) ToProto() grpcerrors.TypedErrorProto {
32
-	return e.FrontendCap
33
-}
34
-
35
-func NewUnsupportedFrontendCapError(name string) error {
36
-	return &UnsupportedFrontendCapError{FrontendCap: &FrontendCap{Name: name}}
37
-}
38
-
39
-func (v *FrontendCap) WrapError(err error) error {
40
-	return &UnsupportedFrontendCapError{error: err, FrontendCap: v}
41
-}
42 1
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+package errdefs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+
6
+	"github.com/containerd/typeurl/v2"
7
+	"github.com/moby/buildkit/util/grpcerrors"
8
+)
9
+
10
+func init() {
11
+	typeurl.Register((*Frontend)(nil), "github.com/moby/buildkit", "errdefs.Frontend+json")
12
+}
13
+
14
+type FrontendError struct {
15
+	*Frontend
16
+	error
17
+}
18
+
19
+func (e *FrontendError) Error() string {
20
+	// These can be nested, so avoid adding any details to the error message
21
+	// if we already have an error. Otherwise the resulting error message
22
+	// can be very long and not very useful.
23
+	if e.error != nil {
24
+		return e.error.Error()
25
+	}
26
+	return fmt.Sprintf("frontend %s failed", e.Name)
27
+}
28
+
29
+func (e *FrontendError) Unwrap() error {
30
+	return e.error
31
+}
32
+
33
+func (e *FrontendError) ToProto() grpcerrors.TypedErrorProto {
34
+	return e.Frontend
35
+}
36
+
37
+func (v *Frontend) WrapError(err error) error {
38
+	return &FrontendError{error: err, Frontend: v}
39
+}
40
+
41
+func Frontends(err error) []*Frontend {
42
+	var out []*Frontend
43
+	var es *FrontendError
44
+	if errors.As(err, &es) {
45
+		out = Frontends(es.Unwrap())
46
+		out = append(out, es.CloneVT())
47
+	}
48
+	return out
49
+}
0 50
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+package errdefs
1
+
2
+import (
3
+	fmt "fmt"
4
+
5
+	"github.com/containerd/typeurl/v2"
6
+	"github.com/moby/buildkit/util/grpcerrors"
7
+)
8
+
9
+func init() {
10
+	typeurl.Register((*FrontendCap)(nil), "github.com/moby/buildkit", "errdefs.FrontendCap+json")
11
+}
12
+
13
+type UnsupportedFrontendCapError struct {
14
+	*FrontendCap
15
+	error
16
+}
17
+
18
+func (e *UnsupportedFrontendCapError) Error() string {
19
+	msg := fmt.Sprintf("unsupported frontend capability %s", e.Name)
20
+	if e.error != nil {
21
+		msg += ": " + e.error.Error()
22
+	}
23
+	return msg
24
+}
25
+
26
+func (e *UnsupportedFrontendCapError) Unwrap() error {
27
+	return e.error
28
+}
29
+
30
+func (e *UnsupportedFrontendCapError) ToProto() grpcerrors.TypedErrorProto {
31
+	return e.FrontendCap
32
+}
33
+
34
+func NewUnsupportedFrontendCapError(name string) error {
35
+	return &UnsupportedFrontendCapError{FrontendCap: &FrontendCap{Name: name}}
36
+}
37
+
38
+func (v *FrontendCap) WrapError(err error) error {
39
+	return &UnsupportedFrontendCapError{error: err, FrontendCap: v}
40
+}
... ...
@@ -14,7 +14,7 @@ func init() {
14 14
 	typeurl.Register((*Solve)(nil), "github.com/moby/buildkit", "errdefs.Solve+json")
15 15
 }
16 16
 
17
-//nolint:revive
17
+//nolint:revive,staticcheck
18 18
 type IsSolve_Subject isSolve_Subject
19 19
 
20 20
 // SolveError will be returned when an error is encountered during a solve that
... ...
@@ -14,34 +14,34 @@ func WithSource(err error, src *Source) error {
14 14
 	if err == nil {
15 15
 		return nil
16 16
 	}
17
-	return &ErrorSource{Source: src, error: err}
17
+	return &SourceError{Source: src, error: err}
18 18
 }
19 19
 
20
-type ErrorSource struct {
20
+type SourceError struct {
21 21
 	*Source
22 22
 	error
23 23
 }
24 24
 
25
-func (e *ErrorSource) Unwrap() error {
25
+func (e *SourceError) Unwrap() error {
26 26
 	return e.error
27 27
 }
28 28
 
29
-func (e *ErrorSource) ToProto() grpcerrors.TypedErrorProto {
29
+func (e *SourceError) ToProto() grpcerrors.TypedErrorProto {
30 30
 	return e.Source
31 31
 }
32 32
 
33 33
 func Sources(err error) []*Source {
34 34
 	var out []*Source
35
-	var es *ErrorSource
35
+	var es *SourceError
36 36
 	if errors.As(err, &es) {
37 37
 		out = Sources(es.Unwrap())
38
-		out = append(out, es.Source.CloneVT())
38
+		out = append(out, es.CloneVT())
39 39
 	}
40 40
 	return out
41 41
 }
42 42
 
43 43
 func (s *Source) WrapError(err error) error {
44
-	return &ErrorSource{error: err, Source: s}
44
+	return &SourceError{error: err, Source: s}
45 45
 }
46 46
 
47 47
 func (s *Source) Print(w io.Writer) error {
... ...
@@ -69,10 +69,7 @@ func (s *Source) Print(w io.Writer) error {
69 69
 	var p int
70 70
 
71 71
 	prepadStart := start
72
-	for {
73
-		if p >= pad {
74
-			break
75
-		}
72
+	for p < pad {
76 73
 		if start > 1 {
77 74
 			start--
78 75
 			p++
... ...
@@ -17,7 +17,7 @@ type UnsupportedSubrequestError struct {
17 17
 }
18 18
 
19 19
 func (e *UnsupportedSubrequestError) Error() string {
20
-	msg := fmt.Sprintf("unsupported request %s", e.Subrequest.Name)
20
+	msg := fmt.Sprintf("unsupported request %s", e.Name)
21 21
 	if e.error != nil {
22 22
 		msg += ": " + e.error.Error()
23 23
 	}
... ...
@@ -97,7 +97,7 @@ func (ei *edgeIndex) LoadOrStore(k *CacheKey, e *edge) *edge {
97 97
 		}
98 98
 	}
99 99
 
100
-	if old != nil && !(!isIgnoreCache(old) && isIgnoreCache(e)) {
100
+	if old != nil && (isIgnoreCache(old) || !isIgnoreCache(e)) {
101 101
 		ei.enforceLinked(oldID, k)
102 102
 		return old
103 103
 	}
... ...
@@ -190,7 +190,7 @@ func (ei *edgeIndex) getAllMatches(k *CacheKey) []string {
190 190
 		if i == 0 {
191 191
 			for _, d := range dd {
192 192
 				ll := CacheInfoLink{Input: Index(i), Digest: k.Digest(), Output: k.Output(), Selector: d.Selector}
193
-				for _, ckID := range d.CacheKey.CacheKey.indexIDs {
193
+				for _, ckID := range d.CacheKey.indexIDs {
194 194
 					item, ok := ei.items[ckID]
195 195
 					if ok {
196 196
 						for l := range item.links[ll] {
... ...
@@ -210,7 +210,7 @@ func (ei *edgeIndex) getAllMatches(k *CacheKey) []string {
210 210
 			found := false
211 211
 			for _, d := range dd {
212 212
 				ll := CacheInfoLink{Input: Index(i), Digest: k.Digest(), Output: k.Output(), Selector: d.Selector}
213
-				for _, ckID := range d.CacheKey.CacheKey.indexIDs {
213
+				for _, ckID := range d.CacheKey.indexIDs {
214 214
 					if item, ok := ei.items[ckID]; ok {
215 215
 						if l, ok := item.links[ll]; ok {
216 216
 							if _, ok := l[m]; ok {
... ...
@@ -625,7 +625,7 @@ func (jl *Solver) NewJob(id string) (*Job, error) {
625 625
 
626 626
 func (jl *Solver) Get(id string) (*Job, error) {
627 627
 	ctx, cancel := context.WithCancelCause(context.Background())
628
-	ctx, _ = context.WithTimeoutCause(ctx, 6*time.Second, errors.WithStack(context.DeadlineExceeded))
628
+	ctx, _ = context.WithTimeoutCause(ctx, 6*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
629 629
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
630 630
 
631 631
 	go func() {
... ...
@@ -265,7 +265,7 @@ func (rp *resultProxy) wrapError(err error) error {
265 265
 	var ve *errdefs.VertexError
266 266
 	if errors.As(err, &ve) {
267 267
 		if rp.req.Definition.Source != nil {
268
-			locs, ok := rp.req.Definition.Source.Locations[string(ve.Digest)]
268
+			locs, ok := rp.req.Definition.Source.Locations[ve.Digest]
269 269
 			if ok {
270 270
 				for _, loc := range locs.Locations {
271 271
 					err = errdefs.WithSource(err, &errdefs.Source{
... ...
@@ -157,49 +157,41 @@ func (m *Manager) parseDevice(dev *pb.CDIDevice, all []string) ([]string, error)
157 157
 
158 158
 	kind, name, _ := strings.Cut(dev.Name, "=")
159 159
 
160
-	// validate kind
161
-	if vendor, class := parser.ParseQualifier(kind); vendor == "" {
162
-		return nil, errors.Errorf("invalid device %q", dev.Name)
163
-	} else if err := parser.ValidateVendorName(vendor); err != nil {
164
-		return nil, errors.Wrapf(err, "invalid device %q", dev.Name)
165
-	} else if err := parser.ValidateClassName(class); err != nil {
166
-		return nil, errors.Wrapf(err, "invalid device %q", dev.Name)
167
-	}
168
-
169
-	switch name {
170
-	case "":
171
-		// first device of kind if no name is specified
172
-		for _, d := range all {
173
-			if strings.HasPrefix(d, kind+"=") {
174
-				out = append(out, d)
175
-				break
176
-			}
177
-		}
178
-	case "*":
179
-		// all devices of kind if the name is a wildcard
180
-		for _, d := range all {
181
-			if strings.HasPrefix(d, kind+"=") {
182
-				out = append(out, d)
160
+	vendor, _ := parser.ParseQualifier(kind)
161
+	if vendor != "" {
162
+		switch name {
163
+		case "":
164
+			// first device of kind if no name is specified
165
+			for _, d := range all {
166
+				if strings.HasPrefix(d, kind+"=") {
167
+					out = append(out, d)
168
+					break
169
+				}
183 170
 			}
184
-		}
185
-	default:
186
-		// the specified device
187
-		for _, d := range all {
188
-			if d == dev.Name {
189
-				out = append(out, d)
190
-				break
171
+		case "*":
172
+			// all devices of kind if the name is a wildcard
173
+			for _, d := range all {
174
+				if strings.HasPrefix(d, kind+"=") {
175
+					out = append(out, d)
176
+				}
191 177
 			}
192
-		}
193
-		if len(out) == 0 {
194
-			// check class annotation if name unknown
178
+		default:
179
+			// the specified device
195 180
 			for _, d := range all {
196
-				if !strings.HasPrefix(d, kind+"=") {
197
-					continue
181
+				if d == dev.Name {
182
+					out = append(out, d)
183
+					break
198 184
 				}
199
-				if a := deviceAnnotations(m.cache.GetDevice(d)); a != nil {
200
-					if class, ok := a[deviceAnnotationClass]; ok && class == name {
201
-						out = append(out, d)
202
-					}
185
+			}
186
+		}
187
+	}
188
+
189
+	// check class annotation if device qualifier invalid or no device found
190
+	if vendor == "" || len(out) == 0 {
191
+		for _, d := range all {
192
+			if a := deviceAnnotations(m.cache.GetDevice(d)); a != nil {
193
+				if class, ok := a[deviceAnnotationClass]; ok && class == dev.Name {
194
+					out = append(out, d)
203 195
 				}
204 196
 			}
205 197
 		}
... ...
@@ -271,7 +263,7 @@ func deviceAnnotations(dev *cdi.Device) map[string]string {
271 271
 	// spec annotations
272 272
 	maps.Copy(out, dev.GetSpec().Annotations)
273 273
 	// device annotations
274
-	maps.Copy(out, dev.Device.Annotations)
274
+	maps.Copy(out, dev.Annotations)
275 275
 	return out
276 276
 }
277 277
 
... ...
@@ -6,8 +6,9 @@ import (
6 6
 	"time"
7 7
 
8 8
 	"github.com/containerd/continuity/fs"
9
-	archive "github.com/moby/go-archive"
9
+	"github.com/moby/go-archive"
10 10
 	"github.com/moby/go-archive/chrootarchive"
11
+	"github.com/moby/go-archive/compression"
11 12
 	"github.com/moby/sys/user"
12 13
 	copy "github.com/tonistiigi/fsutil/copy"
13 14
 )
... ...
@@ -57,7 +58,7 @@ func isArchivePath(path string) bool {
57 57
 		return false
58 58
 	}
59 59
 	defer file.Close()
60
-	rdr, err := archive.DecompressStream(file)
60
+	rdr, err := compression.DecompressStream(file)
61 61
 	if err != nil {
62 62
 		return false
63 63
 	}
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"io"
9 9
 	"os"
10 10
 	"slices"
11
-	"sort"
12 11
 	"strconv"
13 12
 	"strings"
14 13
 	"sync"
... ...
@@ -336,8 +335,8 @@ func (h *HistoryQueue) gc() error {
336 336
 	}
337 337
 
338 338
 	// sort array by newest records first
339
-	sort.Slice(records, func(i, j int) bool {
340
-		return records[i].CompletedAt.AsTime().After(records[j].CompletedAt.AsTime())
339
+	slices.SortFunc(records, func(a, b *controlapi.BuildHistoryRecord) int {
340
+		return -a.CompletedAt.AsTime().Compare(b.CompletedAt.AsTime())
341 341
 	})
342 342
 
343 343
 	h.mu.Lock()
... ...
@@ -449,7 +448,7 @@ func (h *HistoryQueue) addResource(ctx context.Context, l leases.Lease, desc *co
449 449
 				return err
450 450
 			}
451 451
 			defer lr.Discard()
452
-			ok, err := h.migrateBlobV2(ctx, string(desc.Digest), detectSkipLayers)
452
+			ok, err := h.migrateBlobV2(ctx, desc.Digest, detectSkipLayers)
453 453
 			if err != nil {
454 454
 				return err
455 455
 			}
... ...
@@ -459,7 +458,7 @@ func (h *HistoryQueue) addResource(ctx context.Context, l leases.Lease, desc *co
459 459
 		}
460 460
 	}
461 461
 	return h.hLeaseManager.AddResource(ctx, l, leases.Resource{
462
-		ID:   string(desc.Digest),
462
+		ID:   desc.Digest,
463 463
 		Type: "content",
464 464
 	})
465 465
 }
... ...
@@ -500,7 +500,7 @@ func (r *cacheRefShare) release(ctx context.Context) error {
500 500
 	if r.main != nil {
501 501
 		delete(r.main.shares, r.key)
502 502
 	}
503
-	return r.MutableRef.Release(ctx)
503
+	return r.Release(ctx)
504 504
 }
505 505
 
506 506
 var (
... ...
@@ -462,7 +462,10 @@ func (e *ExecOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu
462 462
 	if e.platform != nil {
463 463
 		currentOS = e.platform.OS
464 464
 	}
465
-	meta.Env = addDefaultEnvvar(meta.Env, "PATH", utilsystem.DefaultPathEnv(currentOS))
465
+	// don't set PATH for Windows. #5445
466
+	if currentOS != "windows" {
467
+		meta.Env = addDefaultEnvvar(meta.Env, "PATH", utilsystem.DefaultPathEnv(currentOS))
468
+	}
466 469
 
467 470
 	secretEnv, err := e.loadSecretEnv(ctx, g)
468 471
 	if err != nil {
... ...
@@ -563,7 +566,7 @@ func (e *ExecOp) loadSecretEnv(ctx context.Context, g session.Group) ([]string,
563 563
 			}
564 564
 			return nil
565 565
 		})
566
-		if err != nil && !(errors.Is(err, secrets.ErrNotFound) && sopt.Optional) {
566
+		if err != nil && (!errors.Is(err, secrets.ErrNotFound) || !sopt.Optional) {
567 567
 			return nil, err
568 568
 		}
569 569
 		out = append(out, fmt.Sprintf("%s=%s", sopt.Name, string(dt)))
... ...
@@ -2,13 +2,13 @@ package ops
2 2
 
3 3
 import (
4 4
 	"bytes"
5
+	"cmp"
5 6
 	"context"
6 7
 	"encoding/json"
7 8
 	"fmt"
8 9
 	"path"
9 10
 	"runtime"
10 11
 	"slices"
11
-	"sort"
12 12
 	"sync"
13 13
 
14 14
 	"github.com/moby/buildkit/cache"
... ...
@@ -151,9 +151,8 @@ func (f *fileOp) CacheMap(ctx context.Context, g session.Group, index int) (*sol
151 151
 		for _, k := range m {
152 152
 			dgsts = append(dgsts, []byte(k.Path))
153 153
 		}
154
-		sort.Slice(dgsts, func(i, j int) bool {
155
-			return bytes.Compare(dgsts[i], dgsts[j]) > 0
156
-		})
154
+		slices.SortFunc(dgsts, bytes.Compare)
155
+		slices.Reverse(dgsts) // historical reasons
157 156
 		cm.Deps[idx].Selector = digest.FromBytes(bytes.Join(dgsts, []byte{0}))
158 157
 
159 158
 		cm.Deps[idx].ComputeDigestFunc = opsutils.NewContentHashFunc(dedupeSelectors(m))
... ...
@@ -261,10 +260,9 @@ func dedupeSelectors(m []opsutils.Selector) []opsutils.Selector {
261 261
 		}
262 262
 	}
263 263
 
264
-	sort.Slice(selectors, func(i, j int) bool {
265
-		return selectors[i].Path < selectors[j].Path
264
+	slices.SortFunc(selectors, func(i, j opsutils.Selector) int {
265
+		return cmp.Compare(i.Path, j.Path)
266 266
 	})
267
-
268 267
 	return selectors
269 268
 }
270 269
 
... ...
@@ -66,8 +66,8 @@ func ProvenanceProcessor(attrs map[string]string) llbsolver.Processor {
66 66
 					PredicateType: slsa02.PredicateSLSAProvenance,
67 67
 				},
68 68
 				Path: filename,
69
-				ContentFunc: func() ([]byte, error) {
70
-					pr, err := pc.Predicate()
69
+				ContentFunc: func(ctx context.Context) ([]byte, error) {
70
+					pr, err := pc.Predicate(ctx)
71 71
 					if err != nil {
72 72
 						return nil, err
73 73
 					}
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	"github.com/moby/buildkit/exporter/containerimage/exptypes"
18 18
 	"github.com/moby/buildkit/frontend"
19 19
 	"github.com/moby/buildkit/solver"
20
+	"github.com/moby/buildkit/solver/errdefs"
20 21
 	"github.com/moby/buildkit/solver/llbsolver/ops"
21 22
 	"github.com/moby/buildkit/solver/llbsolver/provenance"
22 23
 	provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
... ...
@@ -167,14 +168,18 @@ func (b *provenanceBridge) Solve(ctx context.Context, req frontend.SolveRequest,
167 167
 		b.builds = append(b.builds, resultWithBridge{res: res, bridge: b})
168 168
 		b.mu.Unlock()
169 169
 	} else if req.Frontend != "" {
170
-		f, ok := b.llbBridge.frontends[req.Frontend]
170
+		f, ok := b.frontends[req.Frontend]
171 171
 		if !ok {
172 172
 			return nil, errors.Errorf("invalid frontend: %s", req.Frontend)
173 173
 		}
174 174
 		wb := &provenanceBridge{llbBridge: b.llbBridge, req: &req}
175
-		res, err = f.Solve(ctx, wb, b.llbBridge, req.FrontendOpt, req.FrontendInputs, sid, b.llbBridge.sm)
175
+		res, err = f.Solve(ctx, wb, b.llbBridge, req.FrontendOpt, req.FrontendInputs, sid, b.sm)
176 176
 		if err != nil {
177
-			return nil, err
177
+			fe := errdefs.Frontend{
178
+				Name:   req.Frontend,
179
+				Source: req.FrontendOpt[frontend.KeySource],
180
+			}
181
+			return nil, fe.WrapError(err)
178 182
 		}
179 183
 		wb.builds = append(wb.builds, resultWithBridge{res: res, bridge: wb})
180 184
 		b.mu.Lock()
... ...
@@ -328,7 +333,7 @@ type ProvenanceCreator struct {
328 328
 	pr        *provenancetypes.ProvenancePredicate
329 329
 	j         *solver.Job
330 330
 	sampler   *resources.SysSampler
331
-	addLayers func() error
331
+	addLayers func(context.Context) error
332 332
 }
333 333
 
334 334
 func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solver.ResultProxy, attrs map[string]string, j *solver.Job, usage *resources.SysSampler) (*ProvenanceCreator, error) {
... ...
@@ -372,7 +377,7 @@ func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solve
372 372
 
373 373
 	pr.Builder.ID = attrs["builder-id"]
374 374
 
375
-	var addLayers func() error
375
+	var addLayers func(context.Context) error
376 376
 
377 377
 	switch mode {
378 378
 	case "min":
... ...
@@ -403,7 +408,7 @@ func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solve
403 403
 			return nil, errors.Errorf("invalid worker ref %T", r.Sys())
404 404
 		}
405 405
 
406
-		addLayers = func() error {
406
+		addLayers = func(ctx context.Context) error {
407 407
 			e := newCacheExporter()
408 408
 
409 409
 			if wref.ImmutableRef != nil {
... ...
@@ -455,12 +460,12 @@ func NewProvenanceCreator(ctx context.Context, cp *provenance.Capture, res solve
455 455
 	return pc, nil
456 456
 }
457 457
 
458
-func (p *ProvenanceCreator) Predicate() (*provenancetypes.ProvenancePredicate, error) {
458
+func (p *ProvenanceCreator) Predicate(ctx context.Context) (*provenancetypes.ProvenancePredicate, error) {
459 459
 	end := p.j.RegisterCompleteTime()
460 460
 	p.pr.Metadata.BuildFinishedOn = &end
461 461
 
462 462
 	if p.addLayers != nil {
463
-		if err := p.addLayers(); err != nil {
463
+		if err := p.addLayers(ctx); err != nil {
464 464
 			return nil, err
465 465
 		}
466 466
 	}
... ...
@@ -1,7 +1,8 @@
1 1
 package provenance
2 2
 
3 3
 import (
4
-	"sort"
4
+	"cmp"
5
+	"slices"
5 6
 
6 7
 	distreference "github.com/distribution/reference"
7 8
 	resourcestypes "github.com/moby/buildkit/executor/resources/types"
... ...
@@ -56,23 +57,23 @@ func (c *Capture) Merge(c2 *Capture) error {
56 56
 }
57 57
 
58 58
 func (c *Capture) Sort() {
59
-	sort.Slice(c.Sources.Images, func(i, j int) bool {
60
-		return c.Sources.Images[i].Ref < c.Sources.Images[j].Ref
59
+	slices.SortFunc(c.Sources.Images, func(a, b provenancetypes.ImageSource) int {
60
+		return cmp.Compare(a.Ref, b.Ref)
61 61
 	})
62
-	sort.Slice(c.Sources.Local, func(i, j int) bool {
63
-		return c.Sources.Local[i].Name < c.Sources.Local[j].Name
62
+	slices.SortFunc(c.Sources.Local, func(a, b provenancetypes.LocalSource) int {
63
+		return cmp.Compare(a.Name, b.Name)
64 64
 	})
65
-	sort.Slice(c.Sources.Git, func(i, j int) bool {
66
-		return c.Sources.Git[i].URL < c.Sources.Git[j].URL
65
+	slices.SortFunc(c.Sources.Git, func(a, b provenancetypes.GitSource) int {
66
+		return cmp.Compare(a.URL, b.URL)
67 67
 	})
68
-	sort.Slice(c.Sources.HTTP, func(i, j int) bool {
69
-		return c.Sources.HTTP[i].URL < c.Sources.HTTP[j].URL
68
+	slices.SortFunc(c.Sources.HTTP, func(a, b provenancetypes.HTTPSource) int {
69
+		return cmp.Compare(a.URL, b.URL)
70 70
 	})
71
-	sort.Slice(c.Secrets, func(i, j int) bool {
72
-		return c.Secrets[i].ID < c.Secrets[j].ID
71
+	slices.SortFunc(c.Secrets, func(a, b provenancetypes.Secret) int {
72
+		return cmp.Compare(a.ID, b.ID)
73 73
 	})
74
-	sort.Slice(c.SSH, func(i, j int) bool {
75
-		return c.SSH[i].ID < c.SSH[j].ID
74
+	slices.SortFunc(c.SSH, func(a, b provenancetypes.SSH) int {
75
+		return cmp.Compare(a.ID, b.ID)
76 76
 	})
77 77
 }
78 78
 
... ...
@@ -207,7 +207,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend
207 207
 		}
208 208
 
209 209
 		ctx, cancel := context.WithCancelCause(ctx)
210
-		ctx, _ = context.WithTimeoutCause(ctx, 300*time.Second, errors.WithStack(context.DeadlineExceeded))
210
+		ctx, _ = context.WithTimeoutCause(ctx, 300*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
211 211
 		defer func() { cancel(errors.WithStack(context.Canceled)) }()
212 212
 
213 213
 		var mu sync.Mutex
... ...
@@ -237,7 +237,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend
237 237
 			if err != nil {
238 238
 				return nil, nil, err
239 239
 			}
240
-			pr, err := prc.Predicate()
240
+			pr, err := prc.Predicate(ctx)
241 241
 			if err != nil {
242 242
 				return nil, nil, err
243 243
 			}
... ...
@@ -676,7 +676,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
676 676
 
677 677
 func (s *Solver) getSessionExporters(ctx context.Context, sessionID string, id int, inp *exporter.Source) ([]exporter.ExporterInstance, error) {
678 678
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
679
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
679
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
680 680
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
681 681
 
682 682
 	caller, err := s.sm.Get(timeoutCtx, sessionID, false)
... ...
@@ -753,10 +753,10 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j *
753 753
 		i, exp := i, exp
754 754
 		eg.Go(func() (err error) {
755 755
 			id := fmt.Sprint(j.SessionID, "-cache-", i)
756
-			err = inBuilderContext(ctx, j, exp.Exporter.Name(), id, func(ctx context.Context, _ session.Group) error {
756
+			err = inBuilderContext(ctx, j, exp.Name(), id, func(ctx context.Context, _ session.Group) error {
757 757
 				prepareDone := progress.OneOff(ctx, "preparing build cache for export")
758 758
 				if err := result.EachRef(cached, inp, func(res solver.CachedResult, ref cache.ImmutableRef) error {
759
-					ctx = withDescHandlerCacheOpts(ctx, ref)
759
+					ctx := withDescHandlerCacheOpts(ctx, ref)
760 760
 
761 761
 					// Configure compression
762 762
 					compressionConfig := exp.Config().Compression
... ...
@@ -2,8 +2,9 @@ package solver
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
5 6
 	"io"
6
-	"sort"
7
+	"slices"
7 8
 	"time"
8 9
 
9 10
 	"github.com/moby/buildkit/util/bklog"
... ...
@@ -26,7 +27,7 @@ func (j *Job) Status(ctx context.Context, ch chan *client.SolveStatus) error {
26 26
 	for {
27 27
 		p, err := pr.Read(ctx)
28 28
 		if err != nil {
29
-			if err == io.EOF {
29
+			if errors.Is(err, io.EOF) {
30 30
 				return nil
31 31
 			}
32 32
 			return err
... ...
@@ -77,20 +78,20 @@ func (j *Job) Status(ctx context.Context, ch chan *client.SolveStatus) error {
77 77
 				ss.Warnings = append(ss.Warnings, &v)
78 78
 			}
79 79
 		}
80
-		sort.Slice(ss.Vertexes, func(i, j int) bool {
81
-			if ss.Vertexes[i].Started == nil {
82
-				return true
80
+		slices.SortFunc(ss.Vertexes, func(a, b *client.Vertex) int {
81
+			if a.Started == nil {
82
+				return -1
83 83
 			}
84
-			if ss.Vertexes[j].Started == nil {
85
-				return false
84
+			if b.Started == nil {
85
+				return 1
86 86
 			}
87
-			return ss.Vertexes[i].Started.Before(*ss.Vertexes[j].Started)
87
+			return a.Started.Compare(*b.Started)
88 88
 		})
89
-		sort.Slice(ss.Statuses, func(i, j int) bool {
90
-			return ss.Statuses[i].Timestamp.Before(ss.Statuses[j].Timestamp)
89
+		slices.SortFunc(ss.Statuses, func(a, b *client.VertexStatus) int {
90
+			return a.Timestamp.Compare(b.Timestamp)
91 91
 		})
92
-		sort.Slice(ss.Logs, func(i, j int) bool {
93
-			return ss.Logs[i].Timestamp.Before(ss.Logs[j].Timestamp)
92
+		slices.SortFunc(ss.Logs, func(a, b *client.VertexLog) int {
93
+			return a.Timestamp.Compare(b.Timestamp)
94 94
 		})
95 95
 
96 96
 		select {
... ...
@@ -48,7 +48,7 @@ type splitResult struct {
48 48
 
49 49
 func (r *splitResult) Release(ctx context.Context) error {
50 50
 	if atomic.AddInt64(&r.released, 1) > 1 {
51
-		err := errors.Errorf("releasing already released reference %+v", r.Result.ID())
51
+		err := errors.Errorf("releasing already released reference %+v", r.ID())
52 52
 		bklog.G(ctx).Error(err)
53 53
 		return err
54 54
 	}
... ...
@@ -1,6 +1,8 @@
1 1
 package result
2 2
 
3 3
 import (
4
+	"context"
5
+
4 6
 	pb "github.com/moby/buildkit/frontend/gateway/pb"
5 7
 	digest "github.com/opencontainers/go-digest"
6 8
 )
... ...
@@ -23,7 +25,7 @@ type Attestation[T any] struct {
23 23
 
24 24
 	Ref         T
25 25
 	Path        string
26
-	ContentFunc func() ([]byte, error)
26
+	ContentFunc func(context.Context) ([]byte, error)
27 27
 
28 28
 	InToto InTotoAttestation
29 29
 }
... ...
@@ -125,7 +125,7 @@ func (r *ociLayoutResolver) info(ctx context.Context, ref reference.Spec) (conte
125 125
 func (r *ociLayoutResolver) withCaller(ctx context.Context, f func(context.Context, session.Caller) error) error {
126 126
 	if r.store.SessionID != "" {
127 127
 		timeoutCtx, cancel := context.WithCancelCause(ctx)
128
-		timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
128
+		timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
129 129
 		defer func() { cancel(errors.WithStack(context.Canceled)) }()
130 130
 
131 131
 		caller, err := r.sm.Get(timeoutCtx, r.store.SessionID, false)
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	"github.com/containerd/containerd/v2/core/remotes"
14 14
 	"github.com/containerd/containerd/v2/core/remotes/docker"
15 15
 	"github.com/containerd/containerd/v2/core/snapshots"
16
+	"github.com/containerd/containerd/v2/pkg/snapshotters"
16 17
 	cerrdefs "github.com/containerd/errdefs"
17 18
 	"github.com/moby/buildkit/cache"
18 19
 	"github.com/moby/buildkit/client"
... ...
@@ -88,11 +89,11 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach
88 88
 	switch p.ResolverType {
89 89
 	case ResolverTypeRegistry:
90 90
 		resolver := resolver.DefaultPool.GetResolver(p.RegistryHosts, p.Ref, "pull", p.SessionManager, g).WithImageStore(p.ImageStore, p.Mode)
91
-		p.Puller.Resolver = resolver
91
+		p.Resolver = resolver
92 92
 		getResolver = func(g session.Group) remotes.Resolver { return resolver.WithSession(g) }
93 93
 	case ResolverTypeOCILayout:
94 94
 		resolver := getOCILayoutResolver(p.store, p.SessionManager, g)
95
-		p.Puller.Resolver = resolver
95
+		p.Resolver = resolver
96 96
 		// OCILayout has no need for session
97 97
 		getResolver = func(g session.Group) remotes.Resolver { return resolver }
98 98
 	default:
... ...
@@ -152,6 +153,7 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach
152 152
 					labels = make(map[string]string)
153 153
 				}
154 154
 				maps.Copy(labels, estargz.SnapshotLabels(p.manifest.Ref, p.manifest.Descriptors, i))
155
+				labels[snapshotters.TargetRefLabel] = p.manifest.Ref
155 156
 
156 157
 				p.descHandlers[desc.Digest] = &cache.DescHandler{
157 158
 					Provider:       p.manifest.Provider,
... ...
@@ -203,11 +205,11 @@ func (p *puller) Snapshot(ctx context.Context, g session.Group) (ir cache.Immuta
203 203
 	switch p.ResolverType {
204 204
 	case ResolverTypeRegistry:
205 205
 		resolver := resolver.DefaultPool.GetResolver(p.RegistryHosts, p.Ref, "pull", p.SessionManager, g).WithImageStore(p.ImageStore, p.Mode)
206
-		p.Puller.Resolver = resolver
206
+		p.Resolver = resolver
207 207
 		getResolver = func(g session.Group) remotes.Resolver { return resolver.WithSession(g) }
208 208
 	case ResolverTypeOCILayout:
209 209
 		resolver := getOCILayoutResolver(p.store, p.SessionManager, g)
210
-		p.Puller.Resolver = resolver
210
+		p.Resolver = resolver
211 211
 		// OCILayout has no need for session
212 212
 		getResolver = func(g session.Group) remotes.Resolver { return resolver }
213 213
 	default:
... ...
@@ -4,6 +4,7 @@ package git
4 4
 
5 5
 import (
6 6
 	"context"
7
+	"errors"
7 8
 	"os"
8 9
 	"os/exec"
9 10
 	"os/signal"
... ...
@@ -59,7 +60,8 @@ func gitMain() {
59 59
 	err := cmd.Run()
60 60
 	close(done)
61 61
 	if err != nil {
62
-		if exiterr, ok := err.(*exec.ExitError); ok {
62
+		exiterr := &exec.ExitError{}
63
+		if errors.As(err, &exiterr) {
63 64
 			switch status := exiterr.Sys().(type) {
64 65
 			case unix.WaitStatus:
65 66
 				os.Exit(status.ExitStatus())
... ...
@@ -398,7 +398,7 @@ func (hs *httpSourceHandler) save(ctx context.Context, resp *http.Response, s se
398 398
 	uid := hs.src.UID
399 399
 	gid := hs.src.GID
400 400
 	if idmap := mount.IdentityMapping(); idmap != nil {
401
-		uid, gid, err = idmap.ToHost(int(uid), int(gid))
401
+		uid, gid, err = idmap.ToHost(uid, gid)
402 402
 		if err != nil {
403 403
 			return nil, "", err
404 404
 		}
... ...
@@ -496,7 +496,7 @@ func (hs *httpSourceHandler) Snapshot(ctx context.Context, g session.Group) (cac
496 496
 }
497 497
 
498 498
 func (hs *httpSourceHandler) newHTTPRequest(ctx context.Context, g session.Group) (*http.Request, error) {
499
-	req, err := http.NewRequest("GET", hs.src.URL, nil)
499
+	req, err := http.NewRequest(http.MethodGet, hs.src.URL, nil)
500 500
 	if err != nil {
501 501
 		return nil, err
502 502
 	}
... ...
@@ -25,7 +25,7 @@ func (h *sessionHandler) RoundTrip(req *http.Request) (*http.Response, error) {
25 25
 		return h.rt.RoundTrip(req)
26 26
 	}
27 27
 
28
-	if req.Method != "GET" {
28
+	if req.Method != http.MethodGet {
29 29
 		return nil, errors.Errorf("invalid request")
30 30
 	}
31 31
 
... ...
@@ -44,7 +44,7 @@ func (h *sessionHandler) RoundTrip(req *http.Request) (*http.Response, error) {
44 44
 
45 45
 		resp = &http.Response{
46 46
 			Status:        "200 OK",
47
-			StatusCode:    200,
47
+			StatusCode:    http.StatusOK,
48 48
 			Body:          pr,
49 49
 			ContentLength: -1,
50 50
 		}
... ...
@@ -164,7 +164,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context, g session.Group) (ca
164 164
 	}
165 165
 
166 166
 	timeoutCtx, cancel := context.WithCancelCause(ctx)
167
-	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
167
+	timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
168 168
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
169 169
 
170 170
 	caller, err := ls.sm.Get(timeoutCtx, sessionID, false)
... ...
@@ -74,6 +74,7 @@ func (e *Engine) Evaluate(ctx context.Context, op *pb.SourceOp) (bool, error) {
74 74
 			return mutated, errors.Wrapf(ErrTooManyOps, "too many mutations on a single source")
75 75
 		}
76 76
 
77
+		ctx := ctx
77 78
 		if i == 0 {
78 79
 			ctx = bklog.WithLogger(ctx, bklog.G(ctx).WithField("orig", op))
79 80
 		} else {
... ...
@@ -1,4 +1,4 @@
1
-package moby_buildkit_v1_sourcepolicy //nolint:revive
1
+package moby_buildkit_v1_sourcepolicy //nolint:revive,staticcheck
2 2
 
3 3
 import (
4 4
 	"github.com/moby/buildkit/util/gogo/proto"
... ...
@@ -1,8 +1,9 @@
1 1
 package apicaps
2 2
 
3 3
 import (
4
+	"cmp"
4 5
 	"fmt"
5
-	"sort"
6
+	"slices"
6 7
 	"strings"
7 8
 
8 9
 	pb "github.com/moby/buildkit/util/apicaps/pb"
... ...
@@ -76,8 +77,8 @@ func (l *CapList) All() []*pb.APICap {
76 76
 			DisabledAlternative: c.DisabledAlternative,
77 77
 		})
78 78
 	}
79
-	sort.Slice(out, func(i, j int) bool {
80
-		return out[i].ID < out[j].ID
79
+	slices.SortFunc(out, func(a, b *pb.APICap) int {
80
+		return cmp.Compare(a.ID, b.ID)
81 81
 	})
82 82
 	return out
83 83
 }
... ...
@@ -12,8 +12,9 @@ const (
12 12
 var (
13 13
 	Root                 = filepath.Join(os.Getenv("ProgramData"), "buildkitd", ".buildstate")
14 14
 	ConfigDir            = filepath.Join(os.Getenv("ProgramData"), "buildkitd")
15
-	DefaultCNIBinDir     = filepath.Join(ConfigDir, "bin")
16
-	DefaultCNIConfigPath = filepath.Join(ConfigDir, "cni.json")
15
+	defaultContainerdDir = filepath.Join(os.Getenv("ProgramFiles"), "containerd")
16
+	DefaultCNIBinDir     = filepath.Join(defaultContainerdDir, "cni", "bin")
17
+	DefaultCNIConfigPath = filepath.Join(defaultContainerdDir, "cni", "conf", "0-containerd-nat.conf")
17 18
 )
18 19
 
19 20
 var (
... ...
@@ -57,7 +57,8 @@ func check(arch, bin string) (string, error) {
57 57
 	if err == nil {
58 58
 		return "", errors.Errorf("invalid zero exit code")
59 59
 	}
60
-	if exitError, ok := err.(*exec.ExitError); ok {
60
+	exitError := &exec.ExitError{}
61
+	if errors.As(err, &exitError) {
61 62
 		switch exitError.ExitCode() {
62 63
 		case 65:
63 64
 			return "v1", nil
... ...
@@ -1,7 +1,7 @@
1 1
 package archutil
2 2
 
3 3
 import (
4
-	"sort"
4
+	"slices"
5 5
 	"strings"
6 6
 	"sync"
7 7
 	"time"
... ...
@@ -192,7 +192,7 @@ func amd64vector(v string) (out []string) {
192 192
 	case "v2":
193 193
 		out = append(out, "v2")
194 194
 	}
195
-	sort.Strings(out)
195
+	slices.Sort(out)
196 196
 	return
197 197
 }
198 198
 
... ...
@@ -191,7 +191,7 @@ func (c estargzType) Is(ctx context.Context, cs content.Store, dgst digest.Diges
191 191
 		if h.Name != estargz.TOCTarName {
192 192
 			return false
193 193
 		}
194
-		if _, err = tr.Next(); err != io.EOF { // must be EOF
194
+		if _, err = tr.Next(); !errors.Is(err, io.EOF) { // must be EOF
195 195
 			return false
196 196
 		}
197 197
 
... ...
@@ -27,7 +27,7 @@ type localFetcher struct {
27 27
 }
28 28
 
29 29
 func (f *localFetcher) Fetch(ctx context.Context, desc ocispecs.Descriptor) (io.ReadCloser, error) {
30
-	r, err := f.Provider.ReaderAt(ctx, desc)
30
+	r, err := f.ReaderAt(ctx, desc)
31 31
 	if err != nil {
32 32
 		return nil, err
33 33
 	}
... ...
@@ -42,7 +42,7 @@ type rc struct {
42 42
 func (r *rc) Read(b []byte) (int, error) {
43 43
 	n, err := r.ReadAt(b, r.offset)
44 44
 	r.offset += int64(n)
45
-	if n > 0 && err == io.EOF {
45
+	if n > 0 && errors.Is(err, io.EOF) {
46 46
 		err = nil
47 47
 	}
48 48
 	return n, err
... ...
@@ -54,8 +54,8 @@ func (r *readerAt) ReadAt(b []byte, off int64) (int, error) {
54 54
 
55 55
 	var totalN int
56 56
 	for len(b) > 0 {
57
-		n, err := r.Reader.Read(b)
58
-		if err == io.EOF && n == len(b) {
57
+		n, err := r.Read(b)
58
+		if errors.Is(err, io.EOF) && n == len(b) {
59 59
 			err = nil
60 60
 		}
61 61
 		r.offset += int64(n)
... ...
@@ -2,6 +2,7 @@ package tarconverter
2 2
 
3 3
 import (
4 4
 	"archive/tar"
5
+	"errors"
5 6
 	"io"
6 7
 )
7 8
 
... ...
@@ -19,7 +20,7 @@ func NewReader(srcContent io.Reader, headerConverter HeaderConverter) io.ReadClo
19 19
 
20 20
 		for {
21 21
 			hdr, err := srcTar.Next()
22
-			if err == io.EOF {
22
+			if errors.Is(err, io.EOF) {
23 23
 				// Signals end of archive.
24 24
 				rebasedTar.Close()
25 25
 				// drain the reader into io.Discard, until hitting EOF
... ...
@@ -17,6 +17,6 @@ func GetDiskStat(root string) (DiskStat, error) {
17 17
 	return DiskStat{
18 18
 		Total:     int64(st.Bsize) * int64(st.Blocks),
19 19
 		Free:      int64(st.Bsize) * int64(st.Bfree),
20
-		Available: int64(st.Bsize) * int64(st.Bavail),
20
+		Available: int64(st.Bsize) * st.Bavail,
21 21
 	}, nil
22 22
 }
... ...
@@ -120,7 +120,7 @@ func getFreeLoopID() (int, error) {
120 120
 	}
121 121
 	defer fd.Close()
122 122
 
123
-	const _LOOP_CTL_GET_FREE = 0x4C82 //nolint:revive
123
+	const _LOOP_CTL_GET_FREE = 0x4C82 //nolint:revive,staticcheck
124 124
 	r1, _, uerr := unix.Syscall(unix.SYS_IOCTL, fd.Fd(), _LOOP_CTL_GET_FREE, 0)
125 125
 	if uerr == 0 {
126 126
 		return int(r1), nil
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"io"
6 6
 	"math/rand"
7 7
 	"slices"
8
-	"sort"
9 8
 	"sync"
10 9
 	"time"
11 10
 
... ...
@@ -300,7 +299,7 @@ func (ps *progressState) run(pr progress.Reader) {
300 300
 	for {
301 301
 		p, err := pr.Read(context.TODO())
302 302
 		if err != nil {
303
-			if err == io.EOF {
303
+			if errors.Is(err, io.EOF) {
304 304
 				ps.mu.Lock()
305 305
 				ps.done = true
306 306
 				ps.mu.Unlock()
... ...
@@ -331,8 +330,8 @@ func (ps *progressState) add(pw progress.Writer) {
331 331
 	for _, p := range ps.items {
332 332
 		plist = append(plist, p)
333 333
 	}
334
-	sort.Slice(plist, func(i, j int) bool {
335
-		return plist[i].Timestamp.Before(plist[j].Timestamp)
334
+	slices.SortFunc(plist, func(a, b *progress.Progress) int {
335
+		return a.Timestamp.Compare(b.Timestamp)
336 336
 	})
337 337
 	for _, p := range plist {
338 338
 		rw.WriteRawProgress(p)
... ...
@@ -45,7 +45,7 @@ func ToGRPC(ctx context.Context, err error) error {
45 45
 
46 46
 	// If the original error was wrapped with more context than the GRPCStatus error,
47 47
 	// copy the original message to the GRPCStatus error
48
-	if err.Error() != st.Message() {
48
+	if errorHasMoreContext(err, st) {
49 49
 		pb := st.Proto()
50 50
 		pb.Message = err.Error()
51 51
 		st = status.FromProto(pb)
... ...
@@ -72,6 +72,21 @@ func ToGRPC(ctx context.Context, err error) error {
72 72
 	return st.Err()
73 73
 }
74 74
 
75
+// errorHasMoreContext checks if the original error provides more context by having
76
+// a different message or additional details than the Status.
77
+func errorHasMoreContext(err error, st *status.Status) bool {
78
+	if errMessage := err.Error(); len(errMessage) > len(st.Message()) {
79
+		// check if the longer message in errMessage is only due to
80
+		// prepending with the status code
81
+		var grpcStatusError *grpcStatusError
82
+		if errors.As(err, &grpcStatusError) {
83
+			return st.Code() != grpcStatusError.st.Code() || st.Message() != grpcStatusError.st.Message()
84
+		}
85
+		return true
86
+	}
87
+	return false
88
+}
89
+
75 90
 func withDetails(ctx context.Context, s *status.Status, details ...proto.Message) (*status.Status, error) {
76 91
 	if s.Code() == codes.OK {
77 92
 		return nil, errors.New("no error details for status with code OK")
... ...
@@ -124,7 +139,7 @@ func Code(err error) codes.Code {
124 124
 }
125 125
 
126 126
 func WrapCode(err error, code codes.Code) error {
127
-	return &withCode{error: err, code: code}
127
+	return &withCodeError{error: err, code: code}
128 128
 }
129 129
 
130 130
 func AsGRPCStatus(err error) (*status.Status, bool) {
... ...
@@ -172,6 +187,8 @@ func FromGRPC(err error) error {
172 172
 	for _, d := range pb.Details {
173 173
 		m, err := typeurl.UnmarshalAny(d)
174 174
 		if err != nil {
175
+			bklog.L.Debugf("failed to unmarshal error detail with type %q: %v", d.GetTypeUrl(), err)
176
+			n.Details = append(n.Details, d)
175 177
 			continue
176 178
 		}
177 179
 
... ...
@@ -181,6 +198,7 @@ func FromGRPC(err error) error {
181 181
 		case TypedErrorProto:
182 182
 			details = append(details, v)
183 183
 		default:
184
+			bklog.L.Debugf("unknown detail with type %T", v)
184 185
 			n.Details = append(n.Details, d)
185 186
 		}
186 187
 	}
... ...
@@ -219,16 +237,16 @@ func (e *grpcStatusError) GRPCStatus() *status.Status {
219 219
 	return e.st
220 220
 }
221 221
 
222
-type withCode struct {
222
+type withCodeError struct {
223 223
 	code codes.Code
224 224
 	error
225 225
 }
226 226
 
227
-func (e *withCode) Code() codes.Code {
227
+func (e *withCodeError) Code() codes.Code {
228 228
 	return e.code
229 229
 }
230 230
 
231
-func (e *withCode) Unwrap() error {
231
+func (e *withCodeError) Unwrap() error {
232 232
 	return e.error
233 233
 }
234 234
 
... ...
@@ -282,10 +282,10 @@ func (c *cniProvider) newNS(ctx context.Context, hostname string) (*cniNS, error
282 282
 
283 283
 	var cniRes *cni.Result
284 284
 	if ctx.Value(contextKeyDetachedNetNS) == nil {
285
-		cniRes, err = c.CNI.Setup(context.TODO(), id, nativeID, nsOpts...)
285
+		cniRes, err = c.Setup(context.TODO(), id, nativeID, nsOpts...)
286 286
 	} else {
287 287
 		// Parallel Setup cannot be used, apparently due to the goroutine issue with setns
288
-		cniRes, err = c.CNI.SetupSerially(context.TODO(), id, nativeID, nsOpts...)
288
+		cniRes, err = c.SetupSerially(context.TODO(), id, nativeID, nsOpts...)
289 289
 	}
290 290
 	if err != nil {
291 291
 		deleteNetNS(nativeID)
... ...
@@ -86,7 +86,7 @@ func withDetachedNetNSIfAny(ctx context.Context, fn func(context.Context) error)
86 86
 		detachedNetNS := filepath.Join(stateDir, "netns")
87 87
 		if _, err := os.Lstat(detachedNetNS); !errors.Is(err, os.ErrNotExist) {
88 88
 			return ns.WithNetNSPath(detachedNetNS, func(_ ns.NetNS) error {
89
-				ctx = context.WithValue(ctx, contextKeyDetachedNetNS, detachedNetNS)
89
+				ctx := context.WithValue(ctx, contextKeyDetachedNetNS, detachedNetNS)
90 90
 				bklog.G(ctx).Debugf("Entering RootlessKit's detached netns %q", detachedNetNS)
91 91
 				err2 := fn(ctx)
92 92
 				bklog.G(ctx).WithError(err2).Debugf("Leaving RootlessKit's detached netns %q", detachedNetNS)
... ...
@@ -94,7 +94,7 @@ func setNetNS(s *specs.Spec, nsPath string) error {
94 94
 
95 95
 func unmountNetNS(nsPath string) error {
96 96
 	if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil {
97
-		if err != syscall.EINVAL && err != syscall.ENOENT {
97
+		if !errors.Is(err, syscall.EINVAL) && !errors.Is(err, syscall.ENOENT) {
98 98
 			return errors.Wrap(err, "error unmounting network namespace")
99 99
 		}
100 100
 	}
... ...
@@ -86,10 +86,10 @@ func GetOverlayLayers(m mount.Mount) ([]string, error) {
86 86
 	var uFound bool
87 87
 	var l []string // l[0] = bottommost
88 88
 	for _, o := range m.Options {
89
-		if strings.HasPrefix(o, "upperdir=") {
90
-			u, uFound = strings.TrimPrefix(o, "upperdir="), true
91
-		} else if strings.HasPrefix(o, "lowerdir=") {
92
-			l = strings.Split(strings.TrimPrefix(o, "lowerdir="), ":")
89
+		if after, ok := strings.CutPrefix(o, "upperdir="); ok {
90
+			u, uFound = after, true
91
+		} else if after, ok := strings.CutPrefix(o, "lowerdir="); ok {
92
+			l = strings.Split(after, ":")
93 93
 			for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
94 94
 				l[i], l[j] = l[j], l[i] // make l[0] = bottommost
95 95
 			}
... ...
@@ -273,7 +273,7 @@ func checkOpaque(upperdir string, path string, base string, f os.FileInfo) (isOp
273 273
 	if f.IsDir() {
274 274
 		for _, oKey := range []string{"trusted.overlay.opaque", "user.overlay.opaque"} {
275 275
 			opaque, err := sysx.LGetxattr(filepath.Join(upperdir, path), oKey)
276
-			if err != nil && err != unix.ENODATA {
276
+			if err != nil && !errors.Is(err, unix.ENODATA) {
277 277
 				return false, errors.Wrapf(err, "failed to retrieve %s attr", oKey)
278 278
 			} else if len(opaque) == 1 && opaque[0] == 'y' {
279 279
 				// This is an opaque whiteout directory.
... ...
@@ -296,7 +296,7 @@ func checkRedirect(upperdir string, path string, f os.FileInfo) (bool, error) {
296 296
 	if f.IsDir() {
297 297
 		rKey := "trusted.overlay.redirect"
298 298
 		redirect, err := sysx.LGetxattr(filepath.Join(upperdir, path), rKey)
299
-		if err != nil && err != unix.ENODATA {
299
+		if err != nil && !errors.Is(err, unix.ENODATA) {
300 300
 			return false, errors.Wrapf(err, "failed to retrieve %s attr", rKey)
301 301
 		}
302 302
 		return len(redirect) > 0, nil
... ...
@@ -379,11 +379,11 @@ func compareSysStat(s1, s2 any) (bool, error) {
379 379
 // Copyright The containerd Authors.
380 380
 func compareCapabilities(p1, p2 string) (bool, error) {
381 381
 	c1, err := sysx.LGetxattr(p1, "security.capability")
382
-	if err != nil && err != sysx.ENODATA {
382
+	if err != nil && !errors.Is(err, sysx.ENODATA) {
383 383
 		return false, errors.Wrapf(err, "failed to get xattr for %s", p1)
384 384
 	}
385 385
 	c2, err := sysx.LGetxattr(p2, "security.capability")
386
-	if err != nil && err != sysx.ENODATA {
386
+	if err != nil && !errors.Is(err, sysx.ENODATA) {
387 387
 		return false, errors.Wrapf(err, "failed to get xattr for %s", p2)
388 388
 	}
389 389
 	return bytes.Equal(c1, c2), nil
... ...
@@ -443,7 +443,7 @@ func compareFileContent(p1, p2 string) (bool, error) {
443 443
 	defer bufPool.Put(b2)
444 444
 	for {
445 445
 		n1, err1 := io.ReadFull(f1, *b1)
446
-		if err1 == io.ErrUnexpectedEOF {
446
+		if errors.Is(err1, io.ErrUnexpectedEOF) {
447 447
 			// it's expected to get EOF when file size isn't a multiple of chunk size, consolidate these error types
448 448
 			err1 = io.EOF
449 449
 		}
... ...
@@ -451,7 +451,7 @@ func compareFileContent(p1, p2 string) (bool, error) {
451 451
 			return false, err1
452 452
 		}
453 453
 		n2, err2 := io.ReadFull(f2, *b2)
454
-		if err2 == io.ErrUnexpectedEOF {
454
+		if errors.Is(err2, io.ErrUnexpectedEOF) {
455 455
 			err2 = io.EOF
456 456
 		}
457 457
 		if err2 != nil && err2 != io.EOF {
... ...
@@ -173,7 +173,7 @@ func LoggerFromContext(ctx context.Context) func([]byte) {
173 173
 		defer pw.Close()
174 174
 		pw.Write(identity.NewID(), client.VertexLog{
175 175
 			Stream: stderr,
176
-			Data:   []byte(dt),
176
+			Data:   dt,
177 177
 		})
178 178
 	}
179 179
 }
... ...
@@ -2,6 +2,7 @@ package progress
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
5 6
 	"io"
6 7
 	"sync"
7 8
 )
... ...
@@ -110,7 +111,7 @@ func (mr *MultiReader) handle() error {
110 110
 	for {
111 111
 		p, err := mr.main.Read(context.TODO())
112 112
 		if err != nil {
113
-			if err == io.EOF {
113
+			if errors.Is(err, io.EOF) {
114 114
 				mr.mu.Lock()
115 115
 				cancelErr := context.Canceled
116 116
 				for w, c := range mr.writers {
... ...
@@ -2,7 +2,7 @@ package progress
2 2
 
3 3
 import (
4 4
 	"maps"
5
-	"sort"
5
+	"slices"
6 6
 	"sync"
7 7
 	"time"
8 8
 )
... ...
@@ -49,8 +49,8 @@ func (ps *MultiWriter) Add(pw Writer) {
49 49
 	ps.mu.Lock()
50 50
 	plist := make([]*Progress, 0, len(ps.items))
51 51
 	plist = append(plist, ps.items...)
52
-	sort.Slice(plist, func(i, j int) bool {
53
-		return plist[i].Timestamp.Before(plist[j].Timestamp)
52
+	slices.SortFunc(plist, func(a, b *Progress) int {
53
+		return a.Timestamp.Compare(b.Timestamp)
54 54
 	})
55 55
 	for _, p := range plist {
56 56
 		rw.WriteRawProgress(p)
... ...
@@ -4,7 +4,7 @@ import (
4 4
 	"context"
5 5
 	"io"
6 6
 	"maps"
7
-	"sort"
7
+	"slices"
8 8
 	"sync"
9 9
 	"time"
10 10
 
... ...
@@ -165,9 +165,8 @@ func (pr *progressReader) Read(ctx context.Context) ([]*Progress, error) {
165 165
 		for _, p := range dmap {
166 166
 			out = append(out, p)
167 167
 		}
168
-
169
-		sort.Slice(out, func(i, j int) bool {
170
-			return out[i].Timestamp.Before(out[j].Timestamp)
168
+		slices.SortFunc(out, func(a, b *Progress) int {
169
+			return a.Timestamp.Compare(b.Timestamp)
171 170
 		})
172 171
 
173 172
 		return out, nil
... ...
@@ -40,7 +40,7 @@ func setUserDefinedTermColors(colorsEnv string) {
40 40
 	for _, field := range fields {
41 41
 		k, v, ok := strings.Cut(field, "=")
42 42
 		if !ok || strings.Contains(v, "=") {
43
-			err := errors.New("A valid entry must have exactly two fields")
43
+			err := errors.New("valid entry must have exactly two fields")
44 44
 			bklog.L.WithError(err).Warnf("Could not parse BUILDKIT_COLORS component: %s", field)
45 45
 			continue
46 46
 		}
... ...
@@ -52,7 +52,7 @@ func setUserDefinedTermColors(colorsEnv string) {
52 52
 				parseKeys(k, c)
53 53
 			}
54 54
 		} else {
55
-			err := errors.New("Colors must be a name from the pre-defined list or a valid 3-part RGB value")
55
+			err := errors.New("colors must be a name from the pre-defined list or a valid 3-part RGB value")
56 56
 			bklog.L.WithError(err).Warnf("Unknown color value found in BUILDKIT_COLORS: %s=%s", k, v)
57 57
 		}
58 58
 	}
... ...
@@ -63,7 +63,7 @@ func readBuildkitColorsEnv(colorsEnv string) []string {
63 63
 	csvReader.Comma = ':'
64 64
 	fields, err := csvReader.Fields(colorsEnv, nil)
65 65
 	if err != nil {
66
-		bklog.L.WithError(err).Warnf("Could not parse BUILDKIT_COLORS. Falling back to defaults.")
66
+		bklog.L.WithError(err).Warnf("could not parse BUILDKIT_COLORS. Falling back to defaults.")
67 67
 		return nil
68 68
 	}
69 69
 	return fields
... ...
@@ -76,7 +76,7 @@ func readRGB(v string) aec.ANSI {
76 76
 		return nil
77 77
 	}
78 78
 	if len(fields) != 3 {
79
-		err = errors.New("A valid RGB color must have three fields")
79
+		err = errors.New("valid RGB color must have three fields")
80 80
 		bklog.L.WithError(err).Warnf("Could not parse value %s as valid RGB color. Ignoring.", v)
81 81
 		return nil
82 82
 	}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"fmt"
9 9
 	"io"
10 10
 	"os"
11
+	"slices"
11 12
 	"sort"
12 13
 	"strconv"
13 14
 	"strings"
... ...
@@ -518,8 +519,8 @@ func mergeIntervals(intervals []interval) []interval {
518 518
 	}
519 519
 
520 520
 	// sort intervals by start time
521
-	sort.Slice(intervals, func(i, j int) bool {
522
-		return intervals[i].start.Before(*intervals[j].start)
521
+	slices.SortFunc(intervals, func(a, b interval) int {
522
+		return a.start.Compare(*b.start)
523 523
 	})
524 524
 
525 525
 	var merged []interval
... ...
@@ -587,10 +588,8 @@ func (t *trace) triggerVertexEvent(v *client.Vertex) {
587 587
 		old = *v
588 588
 	}
589 589
 
590
-	changed := false
591
-	if v.Digest != old.Digest {
592
-		changed = true
593
-	}
590
+	changed := v.Digest != old.Digest
591
+
594 592
 	if v.Name != old.Name {
595 593
 		changed = true
596 594
 	}
... ...
@@ -641,7 +640,11 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) {
641 641
 					subVtxs: make(map[digest.Digest]client.Vertex),
642 642
 				}
643 643
 				if t.modeConsole {
644
-					group.term = vt100.NewVT100(termHeight, termWidth-termPad)
644
+					w := termWidth - termPad
645
+					if w <= 0 {
646
+						w = 1
647
+					}
648
+					group.term = vt100.NewVT100(termHeight, w)
645 649
 				}
646 650
 				t.groups[v.ProgressGroup.Id] = group
647 651
 				t.byDigest[group.Digest] = group.vertex
... ...
@@ -662,7 +665,11 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) {
662 662
 				intervals:     make(map[int64]interval),
663 663
 			}
664 664
 			if t.modeConsole {
665
-				t.byDigest[v.Digest].term = vt100.NewVT100(termHeight, termWidth-termPad)
665
+				w := termWidth - termPad
666
+				if w <= 0 {
667
+					w = 1
668
+				}
669
+				t.byDigest[v.Digest].term = vt100.NewVT100(termHeight, w)
666 670
 			}
667 671
 		}
668 672
 		t.triggerVertexEvent(v)
... ...
@@ -673,7 +680,7 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) {
673 673
 			t.vertexes = append(t.vertexes, t.byDigest[v.Digest])
674 674
 		}
675 675
 		// allow a duplicate initial vertex that shouldn't reset state
676
-		if !(prev != nil && prev.isStarted() && v.Started == nil) {
676
+		if prev == nil || !prev.isStarted() || v.Started != nil {
677 677
 			t.byDigest[v.Digest].Vertex = v
678 678
 		}
679 679
 		if v.Started != nil {
... ...
@@ -1000,6 +1007,9 @@ func (disp *ttyDisplay) print(d displayInfo, width, height int, all bool) {
1000 1000
 	} else {
1001 1001
 		out = align(out, "", width)
1002 1002
 	}
1003
+	if len(out) > width {
1004
+		out = out[:width]
1005
+	}
1003 1006
 	fmt.Fprintln(disp.c, out)
1004 1007
 	lineCount := 0
1005 1008
 	for _, j := range d.jobs {
... ...
@@ -147,7 +147,7 @@ func (p *textMux) printVtx(t *trace, dgst digest.Digest) {
147 147
 			l = l[v.logsOffset:]
148 148
 			fmt.Fprintf(p.w, "%s", l)
149 149
 		} else {
150
-			fmt.Fprintf(p.w, "#%d %s", v.index, []byte(l))
150
+			fmt.Fprintf(p.w, "#%d %s", v.index, l)
151 151
 		}
152 152
 
153 153
 		if i != len(v.logs)-1 || !v.logsPartial {
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"fmt"
7 7
 	"maps"
8 8
 	"net/http"
9
-	"sort"
9
+	"slices"
10 10
 	"strings"
11 11
 	"sync"
12 12
 	"time"
... ...
@@ -153,7 +153,8 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
153 153
 	handler := a.handlers.get(ctx, host, a.sm, a.session)
154 154
 
155 155
 	for _, c := range auth.ParseAuthHeader(last.Header) {
156
-		if c.Scheme == auth.BearerAuth {
156
+		switch c.Scheme {
157
+		case auth.BearerAuth:
157 158
 			var oldScopes []string
158 159
 			if err := invalidAuthorization(c, responses); err != nil {
159 160
 				a.handlers.delete(handler)
... ...
@@ -200,7 +201,7 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R
200 200
 			a.handlers.set(host, sessionID, newAuthHandler(host, a.client, c.Scheme, pubKey, common))
201 201
 
202 202
 			return nil
203
-		} else if c.Scheme == auth.BasicAuth {
203
+		case auth.BasicAuth:
204 204
 			sessionID, username, secret, err := a.getCredentials(host)
205 205
 			if err != nil {
206 206
 				return err
... ...
@@ -288,7 +289,7 @@ func (ah *authHandler) doBearerAuth(ctx context.Context, sm *session.Manager, g
288 288
 
289 289
 	to.Scopes = parseScopes(docker.GetTokenScopes(ctx, to.Scopes)).normalize()
290 290
 
291
-	// Docs: https://docs.docker.com/registry/spec/auth/scope
291
+	// Docs: https://distribution.github.io/distribution/spec/auth/scope
292 292
 	scoped := strings.Join(to.Scopes, " ")
293 293
 
294 294
 	res, err := ah.g.Do(ctx, scoped, func(ctx context.Context) (*authResult, error) {
... ...
@@ -377,7 +378,7 @@ func (ah *authHandler) fetchToken(ctx context.Context, sm *session.Manager, g se
377 377
 				// retry with POST request
378 378
 				// As of September 2017, GCR is known to return 404.
379 379
 				// As of February 2018, JFrog Artifactory is known to return 401.
380
-				if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 {
380
+				if (errStatus.StatusCode == http.StatusMethodNotAllowed && to.Username != "") || errStatus.StatusCode == http.StatusNotFound || errStatus.StatusCode == http.StatusUnauthorized {
381 381
 					resp, err := auth.FetchTokenWithOAuth(ctx, ah.client, hdr, "buildkit-client", to)
382 382
 					if err != nil {
383 383
 						return nil, err
... ...
@@ -444,7 +445,7 @@ func sameRequest(r1, r2 *http.Request) bool {
444 444
 type scopes map[string]map[string]struct{}
445 445
 
446 446
 func parseScopes(s []string) scopes {
447
-	// https://docs.docker.com/registry/spec/auth/scope/
447
+	// https://distribution.github.io/distribution/spec/auth/scope/
448 448
 	m := map[string]map[string]struct{}{}
449 449
 	for _, scopeStr := range s {
450 450
 		if scopeStr == "" {
... ...
@@ -481,7 +482,7 @@ func (s scopes) normalize() []string {
481 481
 	for n := range s {
482 482
 		names = append(names, n)
483 483
 	}
484
-	sort.Strings(names)
484
+	slices.Sort(names)
485 485
 
486 486
 	out := make([]string, 0, len(s))
487 487
 
... ...
@@ -490,7 +491,7 @@ func (s scopes) normalize() []string {
490 490
 		for a := range s[n] {
491 491
 			actions = append(actions, a)
492 492
 		}
493
-		sort.Strings(actions)
493
+		slices.Sort(actions)
494 494
 
495 495
 		out = append(out, n+":"+strings.Join(actions, ","))
496 496
 	}
... ...
@@ -25,7 +25,7 @@ loop0:
25 25
 			}
26 26
 			// full match, potentially skip all
27 27
 			if idx == len(st.Frames)-1 {
28
-				if st.Pid == prev.Pid && st.Version == prev.Version && slices.Compare(st.Cmdline, st.Cmdline) == 0 {
28
+				if st.Pid == prev.Pid && st.Version == prev.Version && slices.Equal(st.Cmdline, prev.Cmdline) {
29 29
 					continue loop0
30 30
 				}
31 31
 			}
... ...
@@ -50,7 +50,7 @@ func Traces(err error) []*Stack {
50 50
 func traces(err error) []*Stack {
51 51
 	var st []*Stack
52 52
 
53
-	switch e := err.(type) {
53
+	switch e := err.(type) { //nolint:errorlint
54 54
 	case interface{ Unwrap() error }:
55 55
 		st = Traces(e.Unwrap())
56 56
 	case interface{ Unwrap() []error }:
... ...
@@ -63,7 +63,7 @@ func traces(err error) []*Stack {
63 63
 		}
64 64
 	}
65 65
 
66
-	switch ste := err.(type) {
66
+	switch ste := err.(type) { //nolint:errorlint
67 67
 	case interface{ StackTrace() errors.StackTrace }:
68 68
 		st = append(st, convertStack(ste.StackTrace()))
69 69
 	case interface{ StackTrace() *Stack }:
... ...
@@ -85,7 +85,7 @@ func Enable(err error) error {
85 85
 }
86 86
 
87 87
 func Wrap(err error, s *Stack) error {
88
-	return &withStack{stack: s, error: err}
88
+	return &withStackError{stack: s, error: err}
89 89
 }
90 90
 
91 91
 func hasLocalStackTrace(err error) bool {
... ...
@@ -173,15 +173,15 @@ func convertStack(s errors.StackTrace) *Stack {
173 173
 	return &out
174 174
 }
175 175
 
176
-type withStack struct {
176
+type withStackError struct {
177 177
 	stack *Stack
178 178
 	error
179 179
 }
180 180
 
181
-func (e *withStack) Unwrap() error {
181
+func (e *withStackError) Unwrap() error {
182 182
 	return e.error
183 183
 }
184 184
 
185
-func (e *withStack) StackTrace() *Stack {
185
+func (e *withStackError) StackTrace() *Stack {
186 186
 	return e.stack
187 187
 }
... ...
@@ -69,10 +69,7 @@ func (mfs *MergeFS) Walk(ctx context.Context, target string, fn fs.WalkDirFunc)
69 69
 		next2, ok2 := <-ch2
70 70
 		key2 := next2.key()
71 71
 
72
-		for {
73
-			if !ok1 && !ok2 {
74
-				break
75
-			}
72
+		for ok1 || ok2 {
76 73
 			if !ok2 || ok1 && key1 < key2 {
77 74
 				if err := fn(next1.path, next1.entry, next1.err); err != nil {
78 75
 					return err
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"io"
7 7
 	"io/fs"
8 8
 	"os"
9
-	"sort"
9
+	"slices"
10 10
 	"strings"
11 11
 
12 12
 	"github.com/tonistiigi/fsutil"
... ...
@@ -52,7 +52,7 @@ func (fs *FS) Walk(ctx context.Context, target string, fn fs.WalkDirFunc) error
52 52
 		}
53 53
 		keys = append(keys, convertPathToKey(k))
54 54
 	}
55
-	sort.Strings(keys)
55
+	slices.Sort(keys)
56 56
 	for _, k := range keys {
57 57
 		p := convertKeyToPath(k)
58 58
 		st := fs.files[p].Stat
... ...
@@ -3,7 +3,7 @@ package detect
3 3
 import (
4 4
 	"context"
5 5
 	"os"
6
-	"sort"
6
+	"slices"
7 7
 	"strconv"
8 8
 
9 9
 	"github.com/pkg/errors"
... ...
@@ -66,8 +66,8 @@ func detectExporter[T any](envVar string, fn func(d ExporterDetector) (T, bool,
66 66
 	for _, d := range detectors {
67 67
 		arr = append(arr, d)
68 68
 	}
69
-	sort.Slice(arr, func(i, j int) bool {
70
-		return arr[i].priority < arr[j].priority
69
+	slices.SortFunc(arr, func(a, b detector) int {
70
+		return a.priority - b.priority
71 71
 	})
72 72
 
73 73
 	var ok bool
... ...
@@ -72,7 +72,7 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
72 72
 	ctx, cancel := c.connection.ContextWithStop(ctx)
73 73
 	defer func() { cancel(errors.WithStack(context.Canceled)) }()
74 74
 	ctx, tCancel := context.WithCancelCause(ctx)
75
-	ctx, _ = context.WithTimeoutCause(ctx, 30*time.Second, errors.WithStack(context.DeadlineExceeded))
75
+	ctx, _ = context.WithTimeoutCause(ctx, 30*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet
76 76
 	defer tCancel(errors.WithStack(context.Canceled))
77 77
 
78 78
 	ctx = c.connection.ContextWithMetadata(ctx)
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"fmt"
6 6
 	"net/http"
7 7
 	"net/http/httptrace"
8
-	"slices"
9 8
 
10 9
 	"github.com/moby/buildkit/util/bklog"
11 10
 	"github.com/moby/buildkit/util/stack"
... ...
@@ -34,19 +33,9 @@ func StartSpan(ctx context.Context, operationName string, opts ...trace.SpanStar
34 34
 }
35 35
 
36 36
 func hasStacktrace(err error) bool {
37
-	switch e := err.(type) {
38
-	case interface{ StackTrace() *stack.Stack }:
39
-		return true
40
-	case interface{ StackTrace() errors.StackTrace }:
41
-		return true
42
-	case interface{ Unwrap() error }:
43
-		return hasStacktrace(e.Unwrap())
44
-	case interface{ Unwrap() []error }:
45
-		if slices.ContainsFunc(e.Unwrap(), hasStacktrace) {
46
-			return true
47
-		}
48
-	}
49
-	return false
37
+	var stack interface{ StackTrace() *stack.Stack }
38
+	var pkgStack interface{ StackTrace() errors.StackTrace }
39
+	return errors.As(err, &stack) || errors.As(err, &pkgStack)
50 40
 }
51 41
 
52 42
 // FinishWithError finalizes the span and sets the error if one is passed
... ...
@@ -76,8 +76,8 @@ func (s *winApplier) Apply(ctx context.Context, desc ocispecs.Descriptor, mounts
76 76
 		}
77 77
 
78 78
 		rc2, discard := filter(rc, func(hdr *tar.Header) bool {
79
-			if strings.HasPrefix(hdr.Name, "Files/") {
80
-				hdr.Name = strings.TrimPrefix(hdr.Name, "Files/")
79
+			if after, ok := strings.CutPrefix(hdr.Name, "Files/"); ok {
80
+				hdr.Name = after
81 81
 				hdr.Linkname = strings.TrimPrefix(hdr.Linkname, "Files/")
82 82
 				// TODO: could convert the windows PAX headers to xattr here to reuse
83 83
 				// the original ones in diff for parent directories and file modifications
... ...
@@ -495,7 +495,6 @@ func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (ref cac
495 495
 	if len(remote.Descriptors) > 0 {
496 496
 		var eg errgroup.Group
497 497
 		for _, desc := range remote.Descriptors {
498
-			desc := desc
499 498
 			eg.Go(func() error {
500 499
 				if _, err := remote.Provider.Info(ctx, desc.Digest); err != nil {
501 500
 					return err
... ...
@@ -5,6 +5,7 @@ package fsutil
5 5
 
6 6
 import (
7 7
 	"os"
8
+	"strings"
8 9
 	"syscall"
9 10
 
10 11
 	"github.com/containerd/continuity/sysx"
... ...
@@ -12,6 +13,8 @@ import (
12 12
 	"github.com/tonistiigi/fsutil/types"
13 13
 )
14 14
 
15
+const xattrApplePrefix = "com.apple."
16
+
15 17
 func loadXattr(origpath string, stat *types.Stat) error {
16 18
 	xattrs, err := sysx.LListxattr(origpath)
17 19
 	if err != nil {
... ...
@@ -23,16 +26,29 @@ func loadXattr(origpath string, stat *types.Stat) error {
23 23
 	if len(xattrs) > 0 {
24 24
 		m := make(map[string][]byte)
25 25
 		for _, key := range xattrs {
26
-			v, err := sysx.LGetxattr(origpath, key)
27
-			if err == nil {
26
+			if skipXattr(key) {
27
+				continue
28
+			}
29
+
30
+			if v, err := sysx.LGetxattr(origpath, key); err == nil {
28 31
 				m[key] = v
29 32
 			}
30 33
 		}
31
-		stat.Xattrs = m
34
+
35
+		if len(m) > 0 {
36
+			stat.Xattrs = m
37
+		}
32 38
 	}
33 39
 	return nil
34 40
 }
35 41
 
42
+func skipXattr(key string) bool {
43
+	if strings.HasPrefix(key, xattrApplePrefix) {
44
+		return true
45
+	}
46
+	return false
47
+}
48
+
36 49
 func setUnixOpt(fi os.FileInfo, stat *types.Stat, path string, seenFiles map[uint64]string) {
37 50
 	s := fi.Sys().(*syscall.Stat_t)
38 51
 
... ...
@@ -293,6 +293,11 @@ github.com/cloudflare/cfssl/signer/local
293 293
 # github.com/container-storage-interface/spec v1.5.0
294 294
 ## explicit; go 1.16
295 295
 github.com/container-storage-interface/spec/lib/go/csi
296
+# github.com/containerd/accelerated-container-image v1.2.3
297
+## explicit; go 1.22.0
298
+github.com/containerd/accelerated-container-image/pkg/label
299
+github.com/containerd/accelerated-container-image/pkg/types
300
+github.com/containerd/accelerated-container-image/pkg/utils
296 301
 # github.com/containerd/cgroups/v3 v3.0.5
297 302
 ## explicit; go 1.22.0
298 303
 github.com/containerd/cgroups/v3
... ...
@@ -758,7 +763,7 @@ github.com/mitchellh/hashstructure/v2
758 758
 # github.com/mitchellh/reflectwalk v1.0.2
759 759
 ## explicit
760 760
 github.com/mitchellh/reflectwalk
761
-# github.com/moby/buildkit v0.21.1
761
+# github.com/moby/buildkit v0.22.0-rc1
762 762
 ## explicit; go 1.23.0
763 763
 github.com/moby/buildkit/api/services/control
764 764
 github.com/moby/buildkit/api/types
... ...
@@ -1183,7 +1188,7 @@ github.com/tinylib/msgp/msgp
1183 1183
 # github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323
1184 1184
 ## explicit; go 1.21
1185 1185
 github.com/tonistiigi/dchapes-mode
1186
-# github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583
1186
+# github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144
1187 1187
 ## explicit; go 1.21
1188 1188
 github.com/tonistiigi/fsutil
1189 1189
 github.com/tonistiigi/fsutil/copy