From 5e38aadc536518dfe3de789c20024a9aba48f96b Mon Sep 17 00:00:00 2001 From: Mikhail Dmitrichenko Date: Fri, 19 Jun 2026 14:16:45 +0300 Subject: [PATCH 1/5] snapshots/devmapper: avoid nil status deref after mkfs failure dmsetup.Status() returns a nil DeviceStatus when it fails. The mkfs failure path records that error, but still unconditionally dereferences status when logging the pool status. Avoid the panic by logging a fallback status string when dmsetup.Status() does not return a status. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Mikhail Dmitrichenko --- plugins/snapshots/devmapper/snapshotter.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/snapshots/devmapper/snapshotter.go b/plugins/snapshots/devmapper/snapshotter.go index 5ec9baed8dbf5..538ef123578a1 100644 --- a/plugins/snapshots/devmapper/snapshotter.go +++ b/plugins/snapshots/devmapper/snapshotter.go @@ -428,8 +428,13 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k errs = append(errs, sErr) } + poolStatus := "unavailable" + if status != nil { + poolStatus = status.RawOutput + } + // Rollback thin device creation if mkfs failed - log.G(ctx).WithError(errors.Join(errs...)).Errorf("failed to initialize thin device %q for snapshot %s pool status %s", deviceName, snap.ID, status.RawOutput) + log.G(ctx).WithError(errors.Join(errs...)).Errorf("failed to initialize thin device %q for snapshot %s pool status %s", deviceName, snap.ID, poolStatus) return nil, errors.Join(append(errs, s.pool.RemoveDevice(ctx, deviceName))...) } } else { From 1755053a786fd7f027e52ead3734e3966d502cb4 Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Thu, 16 Apr 2026 17:31:43 -0700 Subject: [PATCH 2/5] cri: exclude cached layer bytes from image_pulling_throughput The image_pulling_throughput histogram divided the full image size (all layers plus config) by wall-clock pull duration. Layers already present in the content store were counted in the numerator, so the reported MB/s came out way higher than what was actually fetched. Fully-cached pulls were the worst case: they "pulled" in milliseconds but reported the full image size, showing up as huge outliers in the histogram. Both pull paths (local client.Pull and the transfer service) already maintain a totalBytesRead counter in pullRequestReporter for progress and timeout checks. Cached blobs never trigger a fetch, so the counter naturally excludes them. Plumb that value out of both helpers and use it in place of image.Size(ctx). Fully-cached pulls (bytesPulled == 0) are not observed, so they don't produce near-infinite samples. Also updates the metric's Help text to say the denominator is the end-to-end pull duration including layer extraction and snapshotter unpack, not just network transfer. Addresses #13244 Signed-off-by: Ahmet Alp Balkan --- internal/cri/server/images/image_pull.go | 41 +++++++----- internal/cri/server/images/image_pull_test.go | 63 +++++++++++++++++++ internal/cri/server/images/metrics.go | 24 ++++++- 3 files changed, 111 insertions(+), 17 deletions(-) diff --git a/internal/cri/server/images/image_pull.go b/internal/cri/server/images/image_pull.go index 0d927437ddb89..916cec2c0cb54 100644 --- a/internal/cri/server/images/image_pull.go +++ b/internal/cri/server/images/image_pull.go @@ -180,11 +180,14 @@ func (c *CRIImageService) PullImage(ctx context.Context, name string, credential // // Transfer service does not currently support all the CRI image config options. // TODO: Add support for DisableSnapshotAnnotations, DiscardUnpackedLayers, ImagePullWithSyncFs and unpackDuplicationSuppressor - var image containerd.Image + var ( + image containerd.Image + bytesPulled uint64 + ) if c.config.UseLocalImagePull { - image, err = c.pullImageWithLocalPull(ctx, ref, credentials, snapshotter, labels, imagePullProgressTimeout) + image, bytesPulled, err = c.pullImageWithLocalPull(ctx, ref, credentials, snapshotter, labels, imagePullProgressTimeout) } else { - image, err = c.pullImageWithTransferService(ctx, ref, credentials, snapshotter, labels, imagePullProgressTimeout) + image, bytesPulled, err = c.pullImageWithTransferService(ctx, ref, credentials, snapshotter, labels, imagePullProgressTimeout) } if err != nil { @@ -216,11 +219,9 @@ func (c *CRIImageService) PullImage(ctx context.Context, name string, credential } } - const mbToByte = 1024 * 1024 - size, _ := image.Size(ctx) - imagePullingSpeed := float64(size) / mbToByte / time.Since(startTime).Seconds() - imagePullThroughput.Observe(imagePullingSpeed) + recordImagePullThroughput(imagePullThroughput, bytesPulled, time.Since(startTime)) + size, _ := image.Size(ctx) log.G(ctx).Infof("Pulled image %q with image id %q, repo tag %q, repo digest %q, size %q in %s", name, imageID, repoTag, repoDigest, strconv.FormatInt(size, 10), time.Since(startTime)) // NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain @@ -232,6 +233,10 @@ func (c *CRIImageService) PullImage(ctx context.Context, name string, credential } // pullImageWithLocalPull handles image pulling using the local client. +// +// The returned bytesPulled is the number of bytes actually fetched from the +// registry — cached layers are not counted because they never trigger an +// HTTP request. func (c *CRIImageService) pullImageWithLocalPull( ctx context.Context, ref string, @@ -239,7 +244,7 @@ func (c *CRIImageService) pullImageWithLocalPull( snapshotter string, labels map[string]string, imagePullProgressTimeout time.Duration, -) (containerd.Image, error) { +) (containerd.Image, uint64, error) { pctx, pcancel := context.WithCancel(ctx) defer pcancel() pullReporter := newPullProgressReporter(ref, pcancel, imagePullProgressTimeout) @@ -279,12 +284,17 @@ func (c *CRIImageService) pullImageWithLocalPull( image, err := c.client.Pull(pctx, ref, pullOpts...) pcancel() if err != nil { - return nil, fmt.Errorf("failed to pull and unpack image %q: %w", ref, err) + return nil, 0, fmt.Errorf("failed to pull and unpack image %q: %w", ref, err) } - return image, nil + _, bytesPulled := pullReporter.reqReporter.status() + return image, bytesPulled, nil } // pullImageWithTransferService handles image pulling using the transfer service. +// +// The returned bytesPulled is the number of bytes actually fetched from the +// registry, accumulated from transfer progress events; cached layers emit an +// "already exists" event that does not increment the counter. func (c *CRIImageService) pullImageWithTransferService( ctx context.Context, ref string, @@ -292,7 +302,7 @@ func (c *CRIImageService) pullImageWithTransferService( snapshotter string, labels map[string]string, imagePullProgressTimeout time.Duration, -) (containerd.Image, error) { +) (containerd.Image, uint64, error) { log.G(ctx).Debugf("PullImage %q with snapshotter %s using transfer service", ref, snapshotter) rctx, rcancel := context.WithCancel(ctx) defer rcancel() @@ -318,7 +328,7 @@ func (c *CRIImageService) pullImageWithTransferService( reg, err := registry.NewOCIRegistry(ctx, ref, opts...) if err != nil { - return nil, fmt.Errorf("failed to create OCI registry: %w", err) + return nil, 0, fmt.Errorf("failed to create OCI registry: %w", err) } transferProgressReporter.start(rctx) @@ -326,15 +336,16 @@ func (c *CRIImageService) pullImageWithTransferService( err = c.transferrer.Transfer(rctx, reg, is, transfer.WithProgress(transferProgressReporter.createProgressFunc(rctx))) rcancel() if err != nil { - return nil, fmt.Errorf("failed to pull and unpack image %q: %w", ref, err) + return nil, 0, fmt.Errorf("failed to pull and unpack image %q: %w", ref, err) } // Image should be pulled, unpacked and present in containerd image store at this moment image, err := c.client.GetImage(ctx, ref) if err != nil { - return nil, fmt.Errorf("failed to get image %q from containerd image store: %w", ref, err) + return nil, 0, fmt.Errorf("failed to get image %q from containerd image store: %w", ref, err) } - return image, nil + _, bytesPulled := transferProgressReporter.reqReporter.status() + return image, bytesPulled, nil } // ParseAuth parses AuthConfig and returns username and password/secret required by containerd. diff --git a/internal/cri/server/images/image_pull_test.go b/internal/cri/server/images/image_pull_test.go index e2f1388ae6651..3605b594b269e 100644 --- a/internal/cri/server/images/image_pull_test.go +++ b/internal/cri/server/images/image_pull_test.go @@ -834,3 +834,66 @@ func TestPullProgressReporter(t *testing.T) { } }) } + +// fakeObserver records every Observe call so tests can assert both the +// presence and the value of observations without touching the real histogram. +type fakeObserver struct { + samples []float64 +} + +func (f *fakeObserver) Observe(v float64) { + f.samples = append(f.samples, v) +} + +func TestRecordImagePullThroughput(t *testing.T) { + for _, tc := range []struct { + name string + bytesPulled uint64 + duration time.Duration + wantSamples int + wantValue float64 // only checked when wantSamples == 1 + }{ + { + name: "fully cached pull is not observed", + bytesPulled: 0, + duration: 2 * time.Second, + wantSamples: 0, + }, + { + name: "zero duration is not observed", + bytesPulled: 10 * mbToByte, + duration: 0, + wantSamples: 0, + }, + { + name: "cold pull observes MB/s from fetched bytes", + bytesPulled: 10 * mbToByte, + duration: 2 * time.Second, + wantSamples: 1, + wantValue: 5.0, + }, + { + name: "partial cache hit observes only fetched bytes", + // 200 MB image, 150 MB cached, 50 MB actually fetched over 1s. + bytesPulled: 50 * mbToByte, + duration: 1 * time.Second, + wantSamples: 1, + wantValue: 50.0, + }, + { + name: "sub-second pull observes correctly", + bytesPulled: 25 * mbToByte, + duration: 500 * time.Millisecond, + wantSamples: 1, + wantValue: 50.0, + }, + } { + t.Run(tc.name, func(t *testing.T) { + obs := &fakeObserver{} + recordImagePullThroughput(obs, tc.bytesPulled, tc.duration) + if assert.Len(t, obs.samples, tc.wantSamples) && tc.wantSamples == 1 { + assert.InDelta(t, tc.wantValue, obs.samples[0], 0.001) + } + }) + } +} diff --git a/internal/cri/server/images/metrics.go b/internal/cri/server/images/metrics.go index a5b07fbd192d9..e138c312141e1 100644 --- a/internal/cri/server/images/metrics.go +++ b/internal/cri/server/images/metrics.go @@ -17,14 +17,20 @@ package images import ( + "time" + "github.com/docker/go-metrics" prom "github.com/prometheus/client_golang/prometheus" ) +const mbToByte = 1024 * 1024 + var ( imagePulls metrics.LabeledCounter inProgressImagePulls metrics.Gauge - // image size in MB / image pull duration in seconds + // imagePullThroughput observes one MB/s sample per image pull (not per + // layer): fetched bytes over end-to-end pull duration (includes + // extraction). Fully-cached pulls are skipped. imagePullThroughput prom.Histogram ) @@ -44,10 +50,24 @@ func init() { Namespace: namespace, Subsystem: subsystem, Name: "image_pulling_throughput", - Help: "image pull throughput", + Help: "image pull throughput in MB/s (fetched bytes / pull duration; cached layers excluded)", Buckets: prom.DefBuckets, }, ) ns.Add(imagePullThroughput) metrics.Register(ns) } + +// recordImagePullThroughput observes a single pull throughput sample in MB/s. +// bytesPulled is the bytes actually fetched from the registry — cached layers +// never trigger a fetch, so they're naturally excluded. duration is the +// end-to-end pull duration (includes extraction, not just network transfer). +// +// Pulls that fetched nothing (fully cached) are skipped rather than recorded +// as zero or near-infinite samples, which would pollute the histogram. +func recordImagePullThroughput(obs prom.Observer, bytesPulled uint64, duration time.Duration) { + if bytesPulled == 0 || duration <= 0 { + return + } + obs.Observe(float64(bytesPulled) / mbToByte / duration.Seconds()) +} From 16ff70b8614592cb379dccfb0376c54cbe0e147d Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Mon, 4 May 2026 16:04:53 -0700 Subject: [PATCH 3/5] cri: add image_pulling_throughput_mibps and deprecate image_pulling_throughput Address review feedback on the previous commit: - Use MiB/s (binary, 1024^2) consistently. The divisor was already 1024*1024; the constant name (mbToByte) and the Help/comment text mislabeled the unit. - Keep image_pulling_throughput for backwards compatibility but mark it Deprecated. Its prom.DefBuckets top out at 10 MiB/s, which saturates almost immediately on modern hardware. - Add image_pulling_throughput_mibps with buckets covering 0.1 MiB/s through 4000 MiB/s (~31 Gbps), enough for 10G+ NICs. Above that, disk write throughput becomes the bottleneck rather than the network, so finer buckets aren't worth the cardinality. - Both metrics observe the same value (fetched bytes / pull duration, cached layers excluded; fully-cached pulls skipped). Only the buckets and deprecation status differ. Signed-off-by: Ahmet Alp Balkan --- internal/cri/server/images/image_pull.go | 6 ++- internal/cri/server/images/image_pull_test.go | 14 +++--- internal/cri/server/images/metrics.go | 44 ++++++++++++++----- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/internal/cri/server/images/image_pull.go b/internal/cri/server/images/image_pull.go index 916cec2c0cb54..23dd60f050c2d 100644 --- a/internal/cri/server/images/image_pull.go +++ b/internal/cri/server/images/image_pull.go @@ -219,11 +219,13 @@ func (c *CRIImageService) PullImage(ctx context.Context, name string, credential } } - recordImagePullThroughput(imagePullThroughput, bytesPulled, time.Since(startTime)) + elapsed := time.Since(startTime) + recordImagePullThroughput(imagePullThroughput, bytesPulled, elapsed) + recordImagePullThroughput(imagePullThroughputMiBps, bytesPulled, elapsed) size, _ := image.Size(ctx) log.G(ctx).Infof("Pulled image %q with image id %q, repo tag %q, repo digest %q, size %q in %s", name, imageID, - repoTag, repoDigest, strconv.FormatInt(size, 10), time.Since(startTime)) + repoTag, repoDigest, strconv.FormatInt(size, 10), elapsed) // NOTE(random-liu): the actual state in containerd is the source of truth, even we maintain // in-memory image store, it's only for in-memory indexing. The image could be removed // by someone else anytime, before/during/after we create the metadata. We should always diff --git a/internal/cri/server/images/image_pull_test.go b/internal/cri/server/images/image_pull_test.go index 3605b594b269e..5db59bc1b4ba2 100644 --- a/internal/cri/server/images/image_pull_test.go +++ b/internal/cri/server/images/image_pull_test.go @@ -861,28 +861,28 @@ func TestRecordImagePullThroughput(t *testing.T) { }, { name: "zero duration is not observed", - bytesPulled: 10 * mbToByte, + bytesPulled: 10 * mibToByte, duration: 0, wantSamples: 0, }, { - name: "cold pull observes MB/s from fetched bytes", - bytesPulled: 10 * mbToByte, + name: "cold pull observes MiB/s from fetched bytes", + bytesPulled: 10 * mibToByte, duration: 2 * time.Second, wantSamples: 1, wantValue: 5.0, }, { - name: "partial cache hit observes only fetched bytes", - // 200 MB image, 150 MB cached, 50 MB actually fetched over 1s. - bytesPulled: 50 * mbToByte, + name: "partial cache hit observes only fetched bytes", + // 200 MiB image, 150 MiB cached, 50 MiB actually fetched over 1s. + bytesPulled: 50 * mibToByte, duration: 1 * time.Second, wantSamples: 1, wantValue: 50.0, }, { name: "sub-second pull observes correctly", - bytesPulled: 25 * mbToByte, + bytesPulled: 25 * mibToByte, duration: 500 * time.Millisecond, wantSamples: 1, wantValue: 50.0, diff --git a/internal/cri/server/images/metrics.go b/internal/cri/server/images/metrics.go index e138c312141e1..5b2b47256702f 100644 --- a/internal/cri/server/images/metrics.go +++ b/internal/cri/server/images/metrics.go @@ -23,15 +23,23 @@ import ( prom "github.com/prometheus/client_golang/prometheus" ) -const mbToByte = 1024 * 1024 +const mibToByte = 1024 * 1024 var ( imagePulls metrics.LabeledCounter inProgressImagePulls metrics.Gauge - // imagePullThroughput observes one MB/s sample per image pull (not per - // layer): fetched bytes over end-to-end pull duration (includes - // extraction). Fully-cached pulls are skipped. + // imagePullThroughput observes one MiB/s sample per image pull: bytes + // actually fetched from the registry over end-to-end pull duration. + // Fully-cached pulls are skipped. + // + // Deprecated: use imagePullThroughputMiBps instead. The new metric has + // the same semantics but more useful buckets — DefBuckets tops out at + // 10 MiB/s and saturates almost immediately on real hardware. imagePullThroughput prom.Histogram + // imagePullThroughputMiBps observes one MiB/s sample per image pull (not + // per layer): bytes actually fetched from the registry over end-to-end + // pull duration (includes extraction). Fully-cached pulls are skipped. + imagePullThroughputMiBps prom.Histogram ) func init() { @@ -50,18 +58,34 @@ func init() { Namespace: namespace, Subsystem: subsystem, Name: "image_pulling_throughput", - Help: "image pull throughput in MB/s (fetched bytes / pull duration; cached layers excluded)", + Help: "Deprecated: use image_pulling_throughput_mibps instead. Image pull throughput in MiB/s (fetched bytes / pull duration; cached layers excluded).", Buckets: prom.DefBuckets, }, ) + imagePullThroughputMiBps = prom.NewHistogram( + prom.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "image_pulling_throughput_mibps", + Help: "image pull throughput in MiB/s (fetched bytes / pull duration; cached layers excluded)", + // Buckets cover everything from slow registry pulls (0.1 MiB/s) + // to 4000 MiB/s (~31 Gbps), enough for modern 10G+ NICs. The + // top end is intentionally generous: past ~4 GiB/s, disk write + // throughput becomes the bottleneck rather than the network, so + // we don't need finer buckets above that. + Buckets: []float64{0.1, 1, 10, 25, 75, 150, 300, 500, 1000, 2000, 4000}, + }, + ) ns.Add(imagePullThroughput) + ns.Add(imagePullThroughputMiBps) metrics.Register(ns) } -// recordImagePullThroughput observes a single pull throughput sample in MB/s. -// bytesPulled is the bytes actually fetched from the registry — cached layers -// never trigger a fetch, so they're naturally excluded. duration is the -// end-to-end pull duration (includes extraction, not just network transfer). +// recordImagePullThroughput observes one MiB/s sample on obs (intended for +// imagePullThroughputMiBps). bytesPulled is bytes actually fetched from the +// registry — cached layers never trigger a fetch, so they are excluded. +// duration is the end-to-end pull duration (includes extraction, not just +// network transfer). // // Pulls that fetched nothing (fully cached) are skipped rather than recorded // as zero or near-infinite samples, which would pollute the histogram. @@ -69,5 +93,5 @@ func recordImagePullThroughput(obs prom.Observer, bytesPulled uint64, duration t if bytesPulled == 0 || duration <= 0 { return } - obs.Observe(float64(bytesPulled) / mbToByte / duration.Seconds()) + obs.Observe(float64(bytesPulled) / mibToByte / duration.Seconds()) } From 7a7aebfcbf6d3ce24d7b50eb2d7f9676b26c91d5 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 26 Jun 2026 09:40:21 +0200 Subject: [PATCH 4/5] pkg/oci: update TestOpenUserFileCapsReads to use newlined data This test verifies the maximum file-size constraints that were added in [containerd7b05ec4]. However, github.com/moby/sys@v0.4.1 adds similar constraints, including a constraint on line-length (1M): [moby/sys@2c56c3d] that may hit before the file-size limit is reached if the data does not contain newlines. This patch updates the test to use data that includes newlines to make sure it's testing the file-size constraints, not line-limit constraints. [containerd7b05ec4]: https://github.com/containerd/containerd/commit/7b05ec421d0a07b33964c74145b6bf5dff58f476 [moby/sys@2c56c3d]: https://github.com/moby/sys/commit/2c56c3d5d089bfa66d01b6c0bb69428e9d5d18c5 Signed-off-by: Sebastiaan van Stijn --- pkg/oci/spec_opts_user_bounds_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/oci/spec_opts_user_bounds_test.go b/pkg/oci/spec_opts_user_bounds_test.go index 54384f79a1672..30315c6871ba3 100644 --- a/pkg/oci/spec_opts_user_bounds_test.go +++ b/pkg/oci/spec_opts_user_bounds_test.go @@ -30,6 +30,8 @@ import ( // TestOpenUserFileCapsReads asserts the boundary behavior of the read cap: // well below, ending exactly at, and past maxUserFileBytes. +// +// Regression test for CVE-2026-47262 / GHSA-jpcc-p29g-p8mq func TestOpenUserFileCapsReads(t *testing.T) { t.Parallel() @@ -60,7 +62,13 @@ func TestOpenUserFileCapsReads(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - data := append(bytes.Repeat([]byte{0}, tc.padBytes), beyond...) + pattern := []byte("# padding\n") + pad := bytes.Repeat(pattern, (tc.padBytes+len(pattern)-1)/len(pattern))[:tc.padBytes] + if len(pad) > 0 { + pad[len(pad)-1] = '\n' + } + + data := append(pad, beyond...) fsys := fstest.MapFS{ "etc/group": &fstest.MapFile{Data: data, Mode: 0o644}, } From 35f753cc4431b2c69f84a30ac62988810f37ff0a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 26 Jun 2026 10:14:53 +0200 Subject: [PATCH 5/5] pkg/archive: remove redundant github.com/moby/sys/sequential dependency Go 1.26 adds support for passing Windows file flags via os.OpenFile, eliminating the need to call windows.CreateFile directly. github.com/moby/sys/sequential v0.7.0 uses this functionality when compiled with go1.26, but provides fallbacks for older Go versions. Given that containerd has go1.26 as a minimum requirement, we can remove github.com/moby/sys/sequential as an intermediate, and implement the code locally. ref: - https://github.com/moby/sys/commit/9d2fc630f5932bed2dc29585a5ef6b025f5d58bf - https://go-review.googlesource.com/c/go/+/699415 - https://go-review.googlesource.com/c/go/+/724621 Signed-off-by: Sebastiaan van Stijn --- go.mod | 1 - go.sum | 2 - pkg/archive/tar_windows.go | 14 +- vendor/github.com/moby/sys/sequential/LICENSE | 202 ------------------ vendor/github.com/moby/sys/sequential/doc.go | 15 -- .../moby/sys/sequential/sequential_unix.go | 25 --- .../moby/sys/sequential/sequential_windows.go | 91 -------- .../sequential/sequential_windows_go126.go | 13 -- .../sequential/sequential_windows_pre126.go | 76 ------- vendor/modules.txt | 3 - 10 files changed, 11 insertions(+), 431 deletions(-) delete mode 100644 vendor/github.com/moby/sys/sequential/LICENSE delete mode 100644 vendor/github.com/moby/sys/sequential/doc.go delete mode 100644 vendor/github.com/moby/sys/sequential/sequential_unix.go delete mode 100644 vendor/github.com/moby/sys/sequential/sequential_windows.go delete mode 100644 vendor/github.com/moby/sys/sequential/sequential_windows_go126.go delete mode 100644 vendor/github.com/moby/sys/sequential/sequential_windows_pre126.go diff --git a/go.mod b/go.mod index 41e194ca0f4ca..5c54cfb64c381 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,6 @@ require ( github.com/mdlayher/vsock v1.3.0 github.com/moby/locker v1.0.1 github.com/moby/sys/mountinfo v0.7.2 - github.com/moby/sys/sequential v0.7.0 github.com/moby/sys/signal v0.7.1 github.com/moby/sys/symlink v0.3.0 github.com/moby/sys/user v0.4.0 diff --git a/go.sum b/go.sum index dd5f2b1db5930..c8fcff0d0b700 100644 --- a/go.sum +++ b/go.sum @@ -238,8 +238,6 @@ github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCnd github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/sequential v0.7.0 h1:ASQNGNROJSuOO6LL6bPHbKvuZu6NU8P4ldPWk31zj/8= -github.com/moby/sys/sequential v0.7.0/go.mod h1:NfSTAp6V3fw4tmkD62PEcOKeZKquXT8VKCkf7aVR79o= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU= diff --git a/pkg/archive/tar_windows.go b/pkg/archive/tar_windows.go index dc8f64ee41248..51e8e462f85fb 100644 --- a/pkg/archive/tar_windows.go +++ b/pkg/archive/tar_windows.go @@ -23,7 +23,7 @@ import ( "os" "strings" - "github.com/moby/sys/sequential" + "golang.org/x/sys/windows" ) // chmodTarEntry is used to adjust the file permissions used in tar header based @@ -44,13 +44,21 @@ func setHeaderForSpecialDevice(*tar.Header, string, os.FileInfo) error { func open(p string) (*os.File, error) { // We use sequential file access to avoid depleting the standby list on // Windows. - return sequential.Open(p) + // + // Refer to the [Win32 API documentation] for details on sequential file access. + // + // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN + return os.OpenFile(p, os.O_RDONLY|windows.O_FILE_FLAG_SEQUENTIAL_SCAN, 0) } func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { // Source is regular file. We use sequential file access to avoid depleting // the standby list on Windows. - return sequential.OpenFile(name, flag, perm) + // + // Refer to the [Win32 API documentation] for details on sequential file access. + // + // [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN + return os.OpenFile(name, flag|windows.O_FILE_FLAG_SEQUENTIAL_SCAN, perm) } func mkdir(path string, perm os.FileMode) error { diff --git a/vendor/github.com/moby/sys/sequential/LICENSE b/vendor/github.com/moby/sys/sequential/LICENSE deleted file mode 100644 index d645695673349..0000000000000 --- a/vendor/github.com/moby/sys/sequential/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/moby/sys/sequential/doc.go b/vendor/github.com/moby/sys/sequential/doc.go deleted file mode 100644 index af2817504b7a0..0000000000000 --- a/vendor/github.com/moby/sys/sequential/doc.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package sequential provides a set of functions for managing sequential -// files on Windows. -// -// The origin of these functions are the golang OS and windows packages, -// slightly modified to only cope with files, not directories due to the -// specific use case. -// -// The alteration is to allow a file on Windows to be opened with -// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating -// the standby list, particularly when accessing large files such as layer.tar. -// -// For non-Windows platforms, the package provides wrappers for the equivalents -// in the os packages. They are passthrough on Unix platforms, and only relevant -// on Windows. -package sequential diff --git a/vendor/github.com/moby/sys/sequential/sequential_unix.go b/vendor/github.com/moby/sys/sequential/sequential_unix.go deleted file mode 100644 index 24c7c4be4e18a..0000000000000 --- a/vendor/github.com/moby/sys/sequential/sequential_unix.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build !windows - -package sequential - -import "os" - -// Create is an alias for [os.Create] on non-Windows platforms. -func Create(name string) (*os.File, error) { - return os.Create(name) -} - -// Open is an alias for [os.Open] on non-Windows platforms. -func Open(name string) (*os.File, error) { - return os.Open(name) -} - -// OpenFile is an alias for [os.OpenFile] on non-Windows platforms. -func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { - return os.OpenFile(name, flag, perm) -} - -// CreateTemp is an alias for [os.CreateTemp] on non-Windows platforms. -func CreateTemp(dir, prefix string) (f *os.File, err error) { - return os.CreateTemp(dir, prefix) -} diff --git a/vendor/github.com/moby/sys/sequential/sequential_windows.go b/vendor/github.com/moby/sys/sequential/sequential_windows.go deleted file mode 100644 index a8bfc3265d3a0..0000000000000 --- a/vendor/github.com/moby/sys/sequential/sequential_windows.go +++ /dev/null @@ -1,91 +0,0 @@ -//go:build windows - -package sequential - -import ( - "os" - "path/filepath" - "strconv" - "sync" - "time" -) - -// Create is a copy of [os.Create], modified to use sequential file access. -// -// It uses the Windows sequential scan file flag. Refer to the [Win32 API -// documentation] for details on sequential file access. -// -// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN -func Create(name string) (*os.File, error) { - return openFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) -} - -// Open is a copy of [os.Open], modified to use sequential file access. -// -// It uses the Windows sequential scan file flag. Refer to the [Win32 API -// documentation] for details on sequential file access. -// -// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN -func Open(name string) (*os.File, error) { - return openFileSequential(name, os.O_RDONLY, 0) -} - -// OpenFile is a copy of [os.OpenFile], modified to use sequential file access. -// -// It uses the Windows sequential scan file flag. Refer to the [Win32 API -// documentation] for details on sequential file access. -// -// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN -func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { - return openFileSequential(name, flag, perm) -} - -// Helpers for CreateTemp -var ( - rand uint32 - randmu sync.Mutex -) - -func reseed() uint32 { - return uint32(time.Now().UnixNano() + int64(os.Getpid())) -} - -func nextSuffix() string { - randmu.Lock() - r := rand - if r == 0 { - r = reseed() - } - r = r*1664525 + 1013904223 // constants from Numerical Recipes - rand = r - randmu.Unlock() - return strconv.Itoa(int(1e9 + r%1e9))[1:] -} - -// CreateTemp is a copy of [os.CreateTemp], modified to use sequential file access. -// -// It uses the Windows sequential scan file flag. Refer to the [Win32 API -// documentation] for details on sequential file access. -// -// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN -func CreateTemp(dir, prefix string) (f *os.File, err error) { - if dir == "" { - dir = os.TempDir() - } - - nconflict := 0 - for range 10000 { - name := filepath.Join(dir, prefix+nextSuffix()) - f, err = openFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) - if os.IsExist(err) { - if nconflict++; nconflict > 10 { - randmu.Lock() - rand = reseed() - randmu.Unlock() - } - continue - } - break - } - return -} diff --git a/vendor/github.com/moby/sys/sequential/sequential_windows_go126.go b/vendor/github.com/moby/sys/sequential/sequential_windows_go126.go deleted file mode 100644 index 926c4201d3c77..0000000000000 --- a/vendor/github.com/moby/sys/sequential/sequential_windows_go126.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build windows && go1.26 - -package sequential - -import ( - "os" - - "golang.org/x/sys/windows" -) - -func openFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) { - return os.OpenFile(name, flag|windows.O_FILE_FLAG_SEQUENTIAL_SCAN, perm) -} diff --git a/vendor/github.com/moby/sys/sequential/sequential_windows_pre126.go b/vendor/github.com/moby/sys/sequential/sequential_windows_pre126.go deleted file mode 100644 index fa8129cf2688b..0000000000000 --- a/vendor/github.com/moby/sys/sequential/sequential_windows_pre126.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build windows && !go1.26 - -package sequential - -import ( - "os" - "unsafe" - - "golang.org/x/sys/windows" -) - -func openFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { - if name == "" { - return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND} - } - r, e := openSequential(name, flag|windows.O_CLOEXEC) - if e != nil { - return nil, &os.PathError{Op: "open", Path: name, Err: e} - } - return os.NewFile(uintptr(r), name), nil -} - -func makeInheritSa() *windows.SecurityAttributes { - var sa windows.SecurityAttributes - sa.Length = uint32(unsafe.Sizeof(sa)) - sa.InheritHandle = 1 - return &sa -} - -func openSequential(path string, mode int) (fd windows.Handle, err error) { - if len(path) == 0 { - return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND - } - pathp, err := windows.UTF16PtrFromString(path) - if err != nil { - return windows.InvalidHandle, err - } - var access uint32 - switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { - case windows.O_RDONLY: - access = windows.GENERIC_READ - case windows.O_WRONLY: - access = windows.GENERIC_WRITE - case windows.O_RDWR: - access = windows.GENERIC_READ | windows.GENERIC_WRITE - } - if mode&windows.O_CREAT != 0 { - access |= windows.GENERIC_WRITE - } - if mode&windows.O_APPEND != 0 { - access &^= windows.GENERIC_WRITE - access |= windows.FILE_APPEND_DATA - } - sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) - var sa *windows.SecurityAttributes - if mode&windows.O_CLOEXEC == 0 { - sa = makeInheritSa() - } - var createmode uint32 - switch { - case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): - createmode = windows.CREATE_NEW - case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): - createmode = windows.CREATE_ALWAYS - case mode&windows.O_CREAT == windows.O_CREAT: - createmode = windows.OPEN_ALWAYS - case mode&windows.O_TRUNC == windows.O_TRUNC: - createmode = windows.TRUNCATE_EXISTING - default: - createmode = windows.OPEN_EXISTING - } - // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. - // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN - h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0) - return h, e -} diff --git a/vendor/modules.txt b/vendor/modules.txt index b27624c35e2c8..5e999a43af4ea 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -417,9 +417,6 @@ github.com/moby/sys/capability # github.com/moby/sys/mountinfo v0.7.2 ## explicit; go 1.17 github.com/moby/sys/mountinfo -# github.com/moby/sys/sequential v0.7.0 -## explicit; go 1.24.0 -github.com/moby/sys/sequential # github.com/moby/sys/signal v0.7.1 ## explicit; go 1.17 github.com/moby/sys/signal