diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 8f8aebb82d5..2e1ace57374 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -401,6 +401,12 @@ func pullImage(cmd *cobra.Command, imageName string, cliVals *entities.Container pullOptions.RetryDelay = val } + pullOptions.UserNS = cliVals.UserNS + pullOptions.UIDMap = cliVals.UIDMap + pullOptions.GIDMap = cliVals.GIDMap + pullOptions.SubUIDName = cliVals.SubUIDName + pullOptions.SubGIDName = cliVals.SubGIDName + if cliVals.Creds != "" { creds, err := util.ParseRegistryCreds(cliVals.Creds) if err != nil { diff --git a/go.mod b/go.mod index 64b9a3e0e20..eed865eee71 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/vishvananda/netlink v1.3.1 go.podman.io/common v0.67.1-0.20260323194312-7e1f14c29cb4 go.podman.io/image/v5 v5.39.2-0.20260323194312-7e1f14c29cb4 - go.podman.io/storage v1.62.1-0.20260323194312-7e1f14c29cb4 + go.podman.io/storage v1.62.1-0.20260323201758-04a109851e66 golang.org/x/crypto v0.49.0 golang.org/x/net v0.52.0 golang.org/x/sync v0.20.0 @@ -95,7 +95,7 @@ require ( github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v1.0.0-rc.2 // indirect + github.com/containerd/platforms v1.0.0-rc.3 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/containernetworking/plugins v1.9.0 // indirect @@ -129,7 +129,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.18.4 // indirect + github.com/klauspost/compress v1.18.5 // indirect github.com/kr/fs v0.1.0 // indirect github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect github.com/manifoldco/promptui v0.9.0 // indirect @@ -188,3 +188,9 @@ require ( gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect tags.cncf.io/container-device-interface/specs-go v1.1.0 // indirect ) + +replace ( + go.podman.io/common => github.com/giuseppe/container-libs/common v0.0.0-20260327134027-2f2ff1752ec0 + go.podman.io/image/v5 => github.com/giuseppe/container-libs/image/v5 v5.0.0-20260327134027-2f2ff1752ec0 + go.podman.io/storage => github.com/giuseppe/container-libs/storage v0.0.0-20260327134027-2f2ff1752ec0 +) diff --git a/go.sum b/go.sum index 92e67a00861..7bf549539a2 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= -github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v1.0.0-rc.3 h1:YdvwaHtrN6wHcGJ2mYRYP3Nso8OcysuqFe9Hxm1X/tI= +github.com/containerd/platforms v1.0.0-rc.3/go.mod h1:gw0R+alP3nFQPh1L4K9bv13fRWeeyokLGLu2fKuqI10= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= @@ -100,8 +100,8 @@ github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWh github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk= -github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.3.1+incompatible h1:M04FDj2TRehDacrosh7Vlkgc7AuQoWloQkf1PA5hmoI= +github.com/docker/cli v29.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= @@ -125,6 +125,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsouza/go-dockerclient v1.13.0 h1:i6VWBKrFprrqDDK6rEYw0Zp5KsN+cVlqZ/ODaIX2erM= github.com/fsouza/go-dockerclient v1.13.0/go.mod h1:slZeNd4OpH+YfOrTc4+fkjtq8fwKymehRReLvUVFfpI= +github.com/giuseppe/container-libs/common v0.0.0-20260327134027-2f2ff1752ec0 h1:jLcovThLw1Z8B4HY09s+g9RBaSC5+/0dBBDdJ5cCMqY= +github.com/giuseppe/container-libs/common v0.0.0-20260327134027-2f2ff1752ec0/go.mod h1:BeZEvRCqnBigHU1pXTwl9zOa9vnlWsN8xu8qJ3aAax0= +github.com/giuseppe/container-libs/image/v5 v5.0.0-20260327134027-2f2ff1752ec0 h1:kUKuHWV5gRgh0LB1g59rzWNP2oW4ZxFKn2Nq2aVxg7s= +github.com/giuseppe/container-libs/image/v5 v5.0.0-20260327134027-2f2ff1752ec0/go.mod h1:d2xLDZUX546mC7wGaRNC/FxDKTrcopsUWAIUnmRfNxY= +github.com/giuseppe/container-libs/storage v0.0.0-20260327134027-2f2ff1752ec0 h1:rK7dkpT0hN0wzhFOXo9G6uIDRTa3xjvA54nVu90qEO4= +github.com/giuseppe/container-libs/storage v0.0.0-20260327134027-2f2ff1752ec0/go.mod h1:FpDiADa6tAspPQXdefPDFvP4fSE9OXUFiVip+q0GjyE= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -209,8 +215,8 @@ github.com/kevinburke/ssh_config v1.5.0 h1:3cPZmE54xb5j3G5xQCjSvokqNwU2uW+3ry1+P github.com/kevinburke/ssh_config v1.5.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= -github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= @@ -431,12 +437,6 @@ go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4A go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= -go.podman.io/common v0.67.1-0.20260323194312-7e1f14c29cb4 h1:pIoOJdFbpFp8ivkS8cPl/+WxRJhG5dszRrk8M5eu4sg= -go.podman.io/common v0.67.1-0.20260323194312-7e1f14c29cb4/go.mod h1:uCTWM5sPmjiUM5hC4KVE8JrLud3mUfV7RES4Fg69YpE= -go.podman.io/image/v5 v5.39.2-0.20260323194312-7e1f14c29cb4 h1:Qh59zBJSqj2Afq51k9Q8nJq4n1GW1rj/DF8/wbYakzk= -go.podman.io/image/v5 v5.39.2-0.20260323194312-7e1f14c29cb4/go.mod h1:3OGrMYP3EKDQq9jDRxXqFoC9rT2/oCA+4kX6dZjkQK0= -go.podman.io/storage v1.62.1-0.20260323194312-7e1f14c29cb4 h1:TU2iLuSSPNqzX4VcsL1uVngGdWsOhg6CPrrfO1RuvvU= -go.podman.io/storage v1.62.1-0.20260323194312-7e1f14c29cb4/go.mod h1:k8lWBDknm4IJEfY9Gy3P/wRFT8aWvtk+rPtD8FKxIBE= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index b59197ac471..2d968ff6421 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -79,6 +79,17 @@ type ImagePullOptions struct { // OciDecryptConfig contains the config that can be used to decrypt an image if it is // encrypted if non-nil. If nil, it does not attempt to decrypt an image. OciDecryptConfig *encconfig.DecryptConfig + // UserNS is the user namespace mode (e.g., "keep-id", "nomap", "host"). + // Mappings are resolved server-side from this mode. + UserNS string + // UIDMap and GIDMap are the raw user-provided UID/GID mappings + // (e.g., from --uidmap/--gidmap flags). + UIDMap []string + GIDMap []string + // SubUIDName and SubGIDName are the user/group names for subordinate + // UID/GID ranges. + SubUIDName string + SubGIDName string } // ImagePullStatus contains the status of the image pull diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 55c84458b13..c60b12618b9 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -23,6 +23,8 @@ import ( "github.com/containers/buildah/pkg/volumes" "github.com/containers/podman/v6/libpod/define" "github.com/containers/podman/v6/pkg/domain/entities" + "github.com/containers/podman/v6/pkg/namespaces" + "github.com/containers/podman/v6/pkg/util" "github.com/containers/podman/v6/pkg/domain/entities/reports" domainUtils "github.com/containers/podman/v6/pkg/domain/utils" "github.com/containers/podman/v6/pkg/errorhandling" @@ -294,6 +296,36 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify pullOptions.Writer = options.Writer pullOptions.OciDecryptConfig = options.OciDecryptConfig + if options.UserNS != "" { + usernsMode := namespaces.UsernsMode(options.UserNS) + switch { + case usernsMode.IsKeepID(): + keepIDOpts, err := usernsMode.GetKeepIDOptions() + if err != nil { + return nil, err + } + mapping, _, _, err := util.GetKeepIDMapping(keepIDOpts) + if err != nil { + return nil, err + } + pullOptions.UIDMap = mapping.UIDMap + pullOptions.GIDMap = mapping.GIDMap + case usernsMode.IsNoMap(): + mapping, _, _, err := util.GetNoMapMapping() + if err != nil { + return nil, err + } + pullOptions.UIDMap = mapping.UIDMap + pullOptions.GIDMap = mapping.GIDMap + default: + mapping, err := util.ParseIDMapping(usernsMode, options.UIDMap, options.GIDMap, options.SubUIDName, options.SubGIDName) + if err != nil { + return nil, err + } + pullOptions.UIDMap = mapping.UIDMap + pullOptions.GIDMap = mapping.GIDMap + } + } pullOptions.MaxRetries = options.Retry if options.RetryDelay != "" { diff --git a/test/system/170-run-userns.bats b/test/system/170-run-userns.bats index 267358fd149..6634f5afffa 100644 --- a/test/system/170-run-userns.bats +++ b/test/system/170-run-userns.bats @@ -204,3 +204,35 @@ EOF run_podman rm -f $cname } + +# CANNOT BE PARALLELIZED because other tests may use $NONLOCAL_IMAGE +@test "podman run --userns=keep-id does not create mapped layers" { + skip_if_not_rootless "keep-id mapped layers only relevant for rootless" + skip_if_remote "reads local storage files" + + NONLOCAL_IMAGE="$PODMAN_NONLOCAL_IMAGE_FQN" + + run_podman '?' rmi -f $NONLOCAL_IMAGE + run_podman 1 image exists $NONLOCAL_IMAGE + + run_podman run --rm --userns=keep-id $NONLOCAL_IMAGE true + + run_podman image inspect --format '{{.Id}}' $NONLOCAL_IMAGE + local image_id="${output#sha256:}" + + run_podman info --format '{{.Store.GraphRoot}}' + local graphroot="$output" + run_podman info --format '{{.Store.GraphDriverName}}' + local graphdriver="$output" + local images_json="$graphroot/${graphdriver}-images/images.json" + + # Verify no mapped-layers were created for this image + local mapped_count + mapped_count=$(jq --arg id "$image_id" \ + '[.[] | select(.id == $id) | (."mapped-layers" // []) | length] | .[0] // 0' \ + "$images_json") + assert "$mapped_count" == "0" \ + "Image should have no mapped-layers (got $mapped_count)" + + run_podman rmi $NONLOCAL_IMAGE +} diff --git a/vendor/github.com/containerd/platforms/.golangci.yml b/vendor/github.com/containerd/platforms/.golangci.yml index d574fe11d79..9320503c7ba 100644 --- a/vendor/github.com/containerd/platforms/.golangci.yml +++ b/vendor/github.com/containerd/platforms/.golangci.yml @@ -1,32 +1,25 @@ +version: "2" linters: enable: - copyloopvar - - gofmt - - goimports + - dupword - gosec - - ineffassign - misspell - nolintlint - revive - - staticcheck - - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17 - unconvert - - unused - - govet - - dupword # Checks for duplicate words in the source code disable: - errcheck - -run: - timeout: 5m - -issues: - exclude-dirs: - - api - - cluster - - design - - docs - - docs/man - - releases - - reports - - test # e2e scripts + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax diff --git a/vendor/github.com/containerd/platforms/compare.go b/vendor/github.com/containerd/platforms/compare.go index 24403f3b3d4..802b860452d 100644 --- a/vendor/github.com/containerd/platforms/compare.go +++ b/vendor/github.com/containerd/platforms/compare.go @@ -213,9 +213,20 @@ func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool return true } if p1m || p2m { + if p1m && p2m { + // Prefer one with most matching features + if len(p1.OSFeatures) != len(p2.OSFeatures) { + return len(p1.OSFeatures) > len(p2.OSFeatures) + } + } return false } } + if len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0 { + p1.OSFeatures = nil + p2.OSFeatures = nil + return c.Less(p1, p2) + } return false } @@ -242,9 +253,20 @@ func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool { p2m = true } if p1m && p2m { - return false + if len(p1.OSFeatures) != len(p2.OSFeatures) { + return len(p1.OSFeatures) > len(p2.OSFeatures) + } + break } } + + // If neither match and has features, strip features and compare + if !p1m && !p2m && (len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0) { + p1.OSFeatures = nil + p2.OSFeatures = nil + return c.Less(p1, p2) + } + // If one matches, and the other does, sort match first return p1m && !p2m } diff --git a/vendor/github.com/containerd/platforms/cpuinfo_other.go b/vendor/github.com/containerd/platforms/cpuinfo_other.go index 97a1fe8a3e5..5bbfef70425 100644 --- a/vendor/github.com/containerd/platforms/cpuinfo_other.go +++ b/vendor/github.com/containerd/platforms/cpuinfo_other.go @@ -27,7 +27,8 @@ func getCPUVariant() (string, error) { var variant string - if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + switch runtime.GOOS { + case "windows", "darwin": // Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use // runtime.GOARCH to determine the variants switch runtime.GOARCH { @@ -38,7 +39,7 @@ func getCPUVariant() (string, error) { default: variant = "unknown" } - } else if runtime.GOOS == "freebsd" { + case "freebsd": // FreeBSD supports ARMv6 and ARMv7 as well as ARMv4 and ARMv5 (though deprecated) // detecting those variants is currently unimplemented switch runtime.GOARCH { @@ -47,7 +48,7 @@ func getCPUVariant() (string, error) { default: variant = "unknown" } - } else { + default: return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented) } diff --git a/vendor/github.com/containerd/platforms/platforms.go b/vendor/github.com/containerd/platforms/platforms.go index 14d65abd4f8..2d9b3c204c3 100644 --- a/vendor/github.com/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/platforms/platforms.go @@ -111,9 +111,11 @@ package platforms import ( "fmt" + "net/url" "path" "regexp" "runtime" + "slices" "strconv" "strings" @@ -121,12 +123,10 @@ import ( ) var ( - specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`) - osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`) + specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`) + osRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.%-]*)((?:\+[A-Za-z0-9_.%-]+)*)\))?$`) ) -const osAndVersionFormat = "%s(%s)" - // Platform is a type alias for convenience, so there is no need to import image-spec package everywhere. type Platform = specs.Platform @@ -143,6 +143,10 @@ type Matcher interface { // functionality. // // Applications should opt to use `Match` over directly parsing specifiers. +// +// For OSFeatures, this matcher will match if the platform to match has +// OSFeatures which are a subset of the OSFeatures of the platform +// provided to NewMatcher. func NewMatcher(platform specs.Platform) Matcher { m := &matcher{ Platform: Normalize(platform), @@ -178,10 +182,39 @@ type matcher struct { func (m *matcher) Match(platform specs.Platform) bool { normalized := Normalize(platform) - return m.OS == normalized.OS && + if m.OS == normalized.OS && m.Architecture == normalized.Architecture && m.Variant == normalized.Variant && - m.matchOSVersion(platform) + m.matchOSVersion(platform) { + if len(normalized.OSFeatures) == 0 { + return true + } + if len(m.OSFeatures) >= len(normalized.OSFeatures) { + // Ensure that normalized.OSFeatures is a subset of + // m.OSFeatures + j := 0 + for _, feature := range normalized.OSFeatures { + found := false + for ; j < len(m.OSFeatures); j++ { + if feature == m.OSFeatures[j] { + found = true + j++ + break + } + // Since both lists are ordered, if the feature is less + // than what is seen, it is not in the list + if feature < m.OSFeatures[j] { + return false + } + } + if !found { + return false + } + } + return true + } + } + return false } func (m *matcher) matchOSVersion(platform specs.Platform) bool { @@ -210,11 +243,14 @@ func ParseAll(specifiers []string) ([]specs.Platform, error) { // Parse parses the platform specifier syntax into a platform declaration. // -// Platform specifiers are in the format `[()]||[()]/[/]`. +// Platform specifiers are in the format `[()]||[()]/[/]`. // The minimum required information for a platform specifier is the operating -// system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)` -// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value, -// and an empty string otherwise. +// system or architecture. The "os options" may be OSVersion which can be part of the OS +// like `windows(10.0.17763)`. When an OSVersion is specified, then specs.Platform.OSVersion is +// populated with that value, and an empty string otherwise. The "os options" may also include an +// array of OSFeatures, each feature prefixed with '+', without any other separator, and provided +// after the OSVersion when the OSVersion is specified. An "os options" with version and features +// is like `windows(10.0.17763+win32k)`. // If there is only a single string (no slashes), the // value will be matched against the known set of operating systems, then fall // back to the known set of architectures. The missing component will be @@ -231,14 +267,28 @@ func Parse(specifier string) (specs.Platform, error) { var p specs.Platform for i, part := range parts { if i == 0 { - // First element is [()] - osVer := osAndVersionRe.FindStringSubmatch(part) - if osVer == nil { - return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument) + // First element is [([+]*)] + osOptions := osRe.FindStringSubmatch(part) + if osOptions == nil { + return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osRe.String(), errInvalidArgument) } - p.OS = normalizeOS(osVer[1]) - p.OSVersion = osVer[2] + p.OS = normalizeOS(osOptions[1]) + osVersion, err := decodeOSOption(osOptions[2]) + if err != nil { + return specs.Platform{}, fmt.Errorf("%q has an invalid OS version %q: %w", specifier, osOptions[2], err) + } + p.OSVersion = osVersion + if osOptions[3] != "" { + rawFeatures := strings.Split(osOptions[3][1:], "+") + p.OSFeatures = make([]string, len(rawFeatures)) + for i, f := range rawFeatures { + p.OSFeatures[i], err = decodeOSOption(f) + if err != nil { + return specs.Platform{}, fmt.Errorf("%q has an invalid OS feature %q: %w", specifier, f, err) + } + } + } } else { if !specifierRe.MatchString(part) { return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument) @@ -322,13 +372,44 @@ func FormatAll(platform specs.Platform) string { return "unknown" } - if platform.OSVersion != "" { - OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion) + osOptions := encodeOSOption(platform.OSVersion) + features := platform.OSFeatures + if !slices.IsSorted(features) { + features = slices.Clone(features) + slices.Sort(features) + } + for _, f := range features { + osOptions += "+" + encodeOSOption(f) + } + if osOptions != "" { + OSAndVersion := fmt.Sprintf("%s(%s)", platform.OS, osOptions) return path.Join(OSAndVersion, platform.Architecture, platform.Variant) } return path.Join(platform.OS, platform.Architecture, platform.Variant) } +// osOptionReplacer encodes characters in OS option values (version and +// features) that are ambiguous with the format syntax. The percent sign +// must be replaced first to avoid double-encoding. +var osOptionReplacer = strings.NewReplacer( + "%", "%25", + "+", "%2B", + "(", "%28", + ")", "%29", + "/", "%2F", +) + +func encodeOSOption(v string) string { + return osOptionReplacer.Replace(v) +} + +func decodeOSOption(v string) (string, error) { + if strings.Contains(v, "%") { + return url.PathUnescape(v) + } + return v, nil +} + // Normalize validates and translate the platform to the canonical value. // // For example, if "Aarch64" is encountered, we change it to "arm64" or if @@ -336,6 +417,11 @@ func FormatAll(platform specs.Platform) string { func Normalize(platform specs.Platform) specs.Platform { platform.OS = normalizeOS(platform.OS) platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant) + if len(platform.OSFeatures) > 0 { + platform.OSFeatures = slices.Clone(platform.OSFeatures) + slices.Sort(platform.OSFeatures) + platform.OSFeatures = slices.Compact(platform.OSFeatures) + } return platform } diff --git a/vendor/github.com/klauspost/compress/.goreleaser.yml b/vendor/github.com/klauspost/compress/.goreleaser.yml index 4528059ca68..804a2018167 100644 --- a/vendor/github.com/klauspost/compress/.goreleaser.yml +++ b/vendor/github.com/klauspost/compress/.goreleaser.yml @@ -31,6 +31,9 @@ builds: - mips64le goarm: - 7 + ignore: + - goos: windows + goarch: arm - id: "s2d" binary: s2d @@ -57,6 +60,9 @@ builds: - mips64le goarm: - 7 + ignore: + - goos: windows + goarch: arm - id: "s2sx" binary: s2sx @@ -84,6 +90,9 @@ builds: - mips64le goarm: - 7 + ignore: + - goos: windows + goarch: arm archives: - @@ -91,7 +100,7 @@ archives: name_template: "s2-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" format_overrides: - goos: windows - format: zip + formats: ['zip'] files: - unpack/* - s2/LICENSE diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index 5125c1f267e..e839fe9c60c 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -26,6 +26,12 @@ This package will support the current Go version and 2 versions back. Use the links above for more information on each. # changelog + +* Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) + * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 + * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 + * gzhttp: preserve qvalue when extra parameters follow in Accept-Encoding by @analytically in https://github.com/klauspost/compress/pull/1116 + * Jan 16th, 2026 [1.18.3](https://github.com/klauspost/compress/releases/tag/v1.18.3) * Downstream CVE-2025-61728. See [golang/go#77102](https://github.com/golang/go/issues/77102). @@ -691,3 +697,4 @@ This code is licensed under the same conditions as the original Go code. See LIC + diff --git a/vendor/github.com/klauspost/compress/flate/huffman_code.go b/vendor/github.com/klauspost/compress/flate/huffman_code.go index 5f901bd0fe8..4b312dea3e5 100644 --- a/vendor/github.com/klauspost/compress/flate/huffman_code.go +++ b/vendor/github.com/klauspost/compress/flate/huffman_code.go @@ -407,8 +407,8 @@ func histogramSplit(b []byte, h []uint16) { for i, t := range x { v0 := &h[t] v1 := &h[y[i]] - v3 := &h[w[i]] v2 := &h[z[i]] + v3 := &h[w[i]] *v0++ *v1++ *v2++ diff --git a/vendor/github.com/klauspost/compress/flate/regmask_other.go b/vendor/github.com/klauspost/compress/flate/regmask_other.go index 1b7a2cbd793..e62caf711e4 100644 --- a/vendor/github.com/klauspost/compress/flate/regmask_other.go +++ b/vendor/github.com/klauspost/compress/flate/regmask_other.go @@ -1,5 +1,4 @@ //go:build !amd64 -// +build !amd64 package flate diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go index 99ddd4af97c..2d6ef64be15 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress_amd64.go @@ -1,5 +1,4 @@ //go:build amd64 && !appengine && !noasm && gc -// +build amd64,!appengine,!noasm,gc // This file contains the specialisation of Decoder.Decompress4X // and Decoder.Decompress1X that use an asm implementation of thir main loops. diff --git a/vendor/github.com/klauspost/compress/huff0/decompress_generic.go b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go index 908c17de63f..61039232224 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress_generic.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress_generic.go @@ -1,5 +1,4 @@ //go:build !amd64 || appengine || !gc || noasm -// +build !amd64 appengine !gc noasm // This file contains a generic implementation of Decoder.Decompress4X. package huff0 diff --git a/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go index e802579c4f9..b97f9056f4c 100644 --- a/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go +++ b/vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go @@ -1,5 +1,4 @@ //go:build amd64 && !appengine && !noasm && gc -// +build amd64,!appengine,!noasm,gc package cpuinfo diff --git a/vendor/github.com/klauspost/compress/zstd/blockenc.go b/vendor/github.com/klauspost/compress/zstd/blockenc.go index fd35ea1480a..0e33aea4422 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockenc.go +++ b/vendor/github.com/klauspost/compress/zstd/blockenc.go @@ -78,6 +78,7 @@ func (b *blockEnc) initNewEncode() { b.recentOffsets = [3]uint32{1, 4, 8} b.litEnc.Reuse = huff0.ReusePolicyNone b.coders.setPrev(nil, nil, nil) + b.dictLitEnc = nil } // reset will reset the block for a new encode, but in the same stream, diff --git a/vendor/github.com/klauspost/compress/zstd/enc_base.go b/vendor/github.com/klauspost/compress/zstd/enc_base.go index c1192ec38f4..c4de134a7a4 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_base.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_base.go @@ -21,7 +21,7 @@ type fastBase struct { crc *xxhash.Digest tmp [8]byte blk *blockEnc - lastDictID uint32 + lastDict *dict lowMem bool } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go index c1581cfcb8b..851799322bd 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_best.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -479,10 +479,13 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { if d == nil { return } + dictChanged := d != e.lastDict // Init or copy dict table - if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { + if len(e.dictTable) != len(e.table) || dictChanged { if len(e.dictTable) != len(e.table) { e.dictTable = make([]prevEntry, len(e.table)) + } else { + clear(e.dictTable) } end := int32(len(d.content)) - 8 + e.maxMatchOff for i := e.maxMatchOff; i < end; i += 4 { @@ -510,13 +513,14 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { offset: i + 3, } } - e.lastDictID = d.id } - // Init or copy dict table - if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { + // Init or copy dict long table + if len(e.dictLongTable) != len(e.longTable) || dictChanged { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]prevEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) } if len(d.content) >= 8 { cv := load6432(d.content, 0) @@ -538,8 +542,8 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { off++ } } - e.lastDictID = d.id } + e.lastDict = d // Reset table to initial state copy(e.longTable[:], e.dictLongTable) diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go index 85dcd28c32e..3305f09248c 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -1102,10 +1102,13 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { if d == nil { return } + dictChanged := d != e.lastDict // Init or copy dict table - if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { + if len(e.dictTable) != len(e.table) || dictChanged { if len(e.dictTable) != len(e.table) { e.dictTable = make([]tableEntry, len(e.table)) + } else { + clear(e.dictTable) } end := int32(len(d.content)) - 8 + e.maxMatchOff for i := e.maxMatchOff; i < end; i += 4 { @@ -1133,14 +1136,15 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { offset: i + 3, } } - e.lastDictID = d.id e.allDirty = true } - // Init or copy dict table - if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { + // Init or copy dict long table + if len(e.dictLongTable) != len(e.longTable) || dictChanged { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]prevEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) } if len(d.content) >= 8 { cv := load6432(d.content, 0) @@ -1162,9 +1166,9 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { off++ } } - e.lastDictID = d.id e.allDirty = true } + e.lastDict = d // Reset table to initial state { diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index cf8cad00dcf..2fb6da112bc 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -1040,15 +1040,18 @@ func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { allDirty := e.allDirty + dictChanged := d != e.lastDict e.fastEncoderDict.Reset(d, singleBlock) if d == nil { return } // Init or copy dict table - if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { + if len(e.dictLongTable) != len(e.longTable) || dictChanged { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]tableEntry, len(e.longTable)) + } else { + clear(e.dictLongTable) } if len(d.content) >= 8 { cv := load6432(d.content, 0) @@ -1065,7 +1068,6 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { } } } - e.lastDictID = d.id allDirty = true } // Reset table to initial state diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 9180a3a5820..5e104f1a482 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -805,9 +805,11 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { } // Init or copy dict table - if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { + if len(e.dictTable) != len(e.table) || d != e.lastDict { if len(e.dictTable) != len(e.table) { e.dictTable = make([]tableEntry, len(e.table)) + } else { + clear(e.dictTable) } if true { end := e.maxMatchOff + int32(len(d.content)) - 8 @@ -827,7 +829,7 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { } } } - e.lastDictID = d.id + e.lastDict = d e.allDirty = true } diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index 19e730acc26..0f2a00a0033 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -138,11 +138,18 @@ func (e *Encoder) Reset(w io.Writer) { func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error { e.o.resetOpt = true defer func() { e.o.resetOpt = false }() + hadDict := e.o.dict != nil for _, o := range opts { if err := o(&e.o); err != nil { return err } } + hasDict := e.o.dict != nil + if hadDict != hasDict { + // Dict presence changed — encoder type must be recreated. + e.state.encoder = nil + e.init = sync.Once{} + } e.Reset(w) return nil } @@ -448,6 +455,12 @@ func (e *Encoder) Close() error { if s.encoder == nil { return nil } + if s.w == nil { + if len(s.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { + return nil + } + return errors.New("zstd: encoder has no writer") + } err := e.nextBlock(true) if err != nil { if errors.Is(s.err, ErrEncoderClosed) { diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index 8e0f5cac71b..e217be0a17a 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -42,6 +42,7 @@ func (o *encoderOptions) setDefault() { level: SpeedDefault, allLitEntropy: false, lowMem: false, + fullZero: true, } } diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go index d04a829b0a0..b8c8607b5df 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go @@ -1,5 +1,4 @@ //go:build amd64 && !appengine && !noasm && gc -// +build amd64,!appengine,!noasm,gc package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go index 8adfebb0297..2138f8091a9 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go @@ -1,5 +1,4 @@ //go:build !amd64 || appengine || !gc || noasm -// +build !amd64 appengine !gc noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go index 0be16cefc7f..9576426e686 100644 --- a/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go +++ b/vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go @@ -1,5 +1,4 @@ //go:build (!amd64 && !arm64) || appengine || !gc || purego || noasm -// +build !amd64,!arm64 appengine !gc purego noasm package xxhash diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go index f41932b7a4f..1ed18927f95 100644 --- a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go @@ -1,5 +1,4 @@ //go:build amd64 && !appengine && !noasm && gc -// +build amd64,!appengine,!noasm,gc // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go index bea1779e973..379746c96ca 100644 --- a/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go @@ -1,5 +1,4 @@ //go:build !amd64 || appengine || !gc || noasm -// +build !amd64 appengine !gc noasm // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 1f8c3cec28c..18c3703ddc9 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -1,5 +1,4 @@ //go:build amd64 && !appengine && !noasm && gc -// +build amd64,!appengine,!noasm,gc package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go index 7cec2197cd9..516cd9b0701 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go @@ -1,5 +1,4 @@ //go:build !amd64 || appengine || !gc || noasm -// +build !amd64 appengine !gc noasm package zstd diff --git a/vendor/go.podman.io/common/libimage/copier.go b/vendor/go.podman.io/common/libimage/copier.go index 89711b337e4..cd4a6df63f9 100644 --- a/vendor/go.podman.io/common/libimage/copier.go +++ b/vendor/go.podman.io/common/libimage/copier.go @@ -27,6 +27,7 @@ import ( "go.podman.io/image/v5/transports" "go.podman.io/image/v5/types" "go.podman.io/storage" + "go.podman.io/storage/pkg/idtools" ) const ( @@ -150,6 +151,13 @@ type CopyOptions struct { // an access token for the registry. IdentityToken string `json:"identitytoken,omitempty"` + // ----- ID Mapping --------------------------------------------------- + + // UIDMap and GIDMap are used for setting up image layers with the + // correct UID/GID mappings when pulling into storage. + UIDMap []idtools.IDMap + GIDMap []idtools.IDMap + // ----- internal ----------------------------------------------------- // Additional tags when creating or copying a docker-archive. @@ -280,6 +288,9 @@ func NewCopier(options *CopyOptions, sc *types.SystemContext) (*Copier, error) { c.systemContext.CompressionLevel = options.CompressionLevel } + c.systemContext.UIDMap = options.UIDMap + c.systemContext.GIDMap = options.GIDMap + // NOTE: for the sake of consistency it's called Oci* in the CopyOptions. c.systemContext.OCIAcceptUncompressedLayers = options.OciAcceptUncompressedLayers diff --git a/vendor/go.podman.io/image/v5/docker/docker_client.go b/vendor/go.podman.io/image/v5/docker/docker_client.go index e28e59a2298..4b5f41a4f25 100644 --- a/vendor/go.podman.io/image/v5/docker/docker_client.go +++ b/vendor/go.podman.io/image/v5/docker/docker_client.go @@ -1030,7 +1030,7 @@ func (c *dockerClient) fetchManifest(ctx context.Context, ref dockerReference, t } res, err := c.makeRequest(ctx, http.MethodGet, path, headers, nil, v2Auth, nil) if err != nil { - return nil, "", err + return nil, "", fmt.Errorf("fetching manifest %s in %s: %w", tagOrDigest, ref.ref.Name(), err) } logrus.Debugf("Content-Type from manifest GET is %q", res.Header.Get("Content-Type")) defer res.Body.Close() @@ -1040,7 +1040,7 @@ func (c *dockerClient) fetchManifest(ctx context.Context, ref dockerReference, t manblob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxManifestBodySize) if err != nil { - return nil, "", err + return nil, "", fmt.Errorf("reading manifest body %s in %s: %w", tagOrDigest, ref.ref.Name(), err) } return manblob, simplifyContentType(res.Header.Get("Content-Type")), nil } diff --git a/vendor/go.podman.io/image/v5/docker/docker_image.go b/vendor/go.podman.io/image/v5/docker/docker_image.go index 1e5de65a749..58f4fc97221 100644 --- a/vendor/go.podman.io/image/v5/docker/docker_image.go +++ b/vendor/go.podman.io/image/v5/docker/docker_image.go @@ -76,7 +76,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. for { res, err := client.makeRequest(ctx, http.MethodGet, path, nil, nil, v2Auth, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("getting repository tags: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { @@ -87,7 +87,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. Tags []string } if err = json.NewDecoder(res.Body).Decode(&tagsHolder); err != nil { - return nil, err + return nil, fmt.Errorf("decoding tag list response: %w", err) } for _, tag := range tagsHolder.Tags { if _, err := reference.WithTag(dr.ref, tag); err != nil { // Ensure the tag does not contain unexpected values @@ -169,7 +169,7 @@ func GetDigest(ctx context.Context, sys *types.SystemContext, ref types.ImageRef res, err := client.makeRequest(ctx, http.MethodHead, path, headers, nil, v2Auth, nil) if err != nil { - return "", err + return "", fmt.Errorf("getting digest %s in %s: %w", tagOrDigest, dr.ref.Name(), err) } defer res.Body.Close() diff --git a/vendor/go.podman.io/image/v5/storage/storage_dest.go b/vendor/go.podman.io/image/v5/storage/storage_dest.go index 318dcc0c3d0..9aaf3b36123 100644 --- a/vendor/go.podman.io/image/v5/storage/storage_dest.go +++ b/vendor/go.podman.io/image/v5/storage/storage_dest.go @@ -37,7 +37,9 @@ import ( "go.podman.io/storage/pkg/archive" "go.podman.io/storage/pkg/chunked" "go.podman.io/storage/pkg/chunked/toc" + "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/ioutils" + storagetypes "go.podman.io/storage/types" ) var ( @@ -58,6 +60,8 @@ type storageImageDestination struct { stubs.AlwaysSupportsSignatures imageRef storageReference + uidMap []idtools.IDMap + gidMap []idtools.IDMap directory string // Temporary directory where we store blobs until Commit() time nextTempFileID atomic.Int32 // A counter that we use for computing filenames to assign to blobs manifest []byte // (Per-instance) manifest contents, or nil if not yet known. @@ -184,10 +188,31 @@ func newImageDestination(sys *types.SystemContext, imageRef storageReference) (* fileSizes: make(map[digest.Digest]int64), }, } + if sys != nil { + dest.uidMap = sys.UIDMap + dest.gidMap = sys.GIDMap + } dest.Compat = impl.AddCompat(dest) return dest, nil } +// idMappingOptions returns the IDMappingOptions for layer creation. +// When custom UID/GID maps are set, they are used directly. +// Otherwise, HostUIDMapping/HostGIDMapping are set to true so that +// populateLayerOptions does not silently inherit mappings from a parent layer. +func (s *storageImageDestination) idMappingOptions() storagetypes.IDMappingOptions { + if len(s.uidMap) > 0 || len(s.gidMap) > 0 { + return storagetypes.IDMappingOptions{ + UIDMap: s.uidMap, + GIDMap: s.gidMap, + } + } + return storagetypes.IDMappingOptions{ + HostUIDMapping: true, + HostGIDMapping: true, + } +} + // Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent, // e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects. func (s *storageImageDestination) Reference() types.ImageReference { @@ -1134,6 +1159,9 @@ func (s *storageImageDestination) createNewLayer(index int, trusted trustedLayer args := storage.ApplyStagedLayerOptions{ ID: newLayerID, ParentLayer: parentLayer, + LayerOptions: &storage.LayerOptions{ + IDMappingOptions: s.idMappingOptions(), + }, DiffOutput: diffOutput, DiffOptions: &graphdriver.ApplyDiffWithDifferOpts{ @@ -1274,12 +1302,14 @@ func (s *storageImageDestination) createNewLayer(index int, trusted trustedLayer defer file.Close() // Build the new layer using the diff, regardless of where it came from. // TODO: This can take quite some time, and should ideally be cancellable using ctx.Done(). - layer, _, err := s.imageRef.transport.store.PutLayer(newLayerID, parentLayer, nil, "", false, &storage.LayerOptions{ - OriginalDigest: trustedOriginalDigest, - OriginalSize: trustedOriginalSize, // nil in many cases + layerOptions := &storage.LayerOptions{ + IDMappingOptions: s.idMappingOptions(), + OriginalDigest: trustedOriginalDigest, + OriginalSize: trustedOriginalSize, // nil in many cases // This might be "" if trusted.layerIdentifiedByTOC; in that case PutLayer will compute the value from the stream. UncompressedDigest: trusted.diffID, - }, file) + } + layer, _, err := s.imageRef.transport.store.PutLayer(newLayerID, parentLayer, nil, "", false, layerOptions, file) if err != nil && !errors.Is(err, storage.ErrDuplicateID) { return nil, fmt.Errorf("adding layer with blob %s: %w", trusted.logString(), err) } diff --git a/vendor/go.podman.io/image/v5/types/types.go b/vendor/go.podman.io/image/v5/types/types.go index 1c0007e6e42..e8f33e8165f 100644 --- a/vendor/go.podman.io/image/v5/types/types.go +++ b/vendor/go.podman.io/image/v5/types/types.go @@ -11,6 +11,7 @@ import ( v1 "github.com/opencontainers/image-spec/specs-go/v1" "go.podman.io/image/v5/docker/reference" compression "go.podman.io/image/v5/pkg/compression/types" + "go.podman.io/storage/pkg/idtools" ) // ImageTransport is a top-level namespace for ways to store/load an image. @@ -700,6 +701,12 @@ type SystemContext struct { CompressionFormat *compression.Algorithm // CompressionLevel specifies what compression level is used CompressionLevel *int + + // === storage.Transport overrides === + // UIDMap specifies the UID mappings to apply when creating layers for the storage transport. + UIDMap []idtools.IDMap + // GIDMap specifies the GID mappings to apply when creating layers for the storage transport. + GIDMap []idtools.IDMap } // ProgressEvent is the type of events a progress reader can produce diff --git a/vendor/modules.txt b/vendor/modules.txt index 3a15c108231..fe920d806b0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -65,8 +65,8 @@ github.com/containerd/errdefs/pkg/internal/cause # github.com/containerd/log v0.1.0 ## explicit; go 1.20 github.com/containerd/log -# github.com/containerd/platforms v1.0.0-rc.2 -## explicit; go 1.20 +# github.com/containerd/platforms v1.0.0-rc.3 +## explicit; go 1.21 github.com/containerd/platforms # github.com/containerd/stargz-snapshotter/estargz v0.18.2 ## explicit; go 1.24.0 @@ -350,8 +350,8 @@ github.com/json-iterator/go # github.com/kevinburke/ssh_config v1.5.0 ## explicit; go 1.18 github.com/kevinburke/ssh_config -# github.com/klauspost/compress v1.18.4 -## explicit; go 1.23 +# github.com/klauspost/compress v1.18.5 +## explicit; go 1.24 github.com/klauspost/compress github.com/klauspost/compress/flate github.com/klauspost/compress/fse @@ -734,7 +734,7 @@ go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/internal/telemetry go.opentelemetry.io/otel/trace/noop -# go.podman.io/common v0.67.1-0.20260323194312-7e1f14c29cb4 +# go.podman.io/common v0.67.1-0.20260323194312-7e1f14c29cb4 => github.com/giuseppe/container-libs/common v0.0.0-20260327134027-2f2ff1752ec0 ## explicit; go 1.25.6 go.podman.io/common/internal go.podman.io/common/libimage @@ -800,7 +800,7 @@ go.podman.io/common/pkg/umask go.podman.io/common/pkg/util go.podman.io/common/pkg/version go.podman.io/common/version -# go.podman.io/image/v5 v5.39.2-0.20260323194312-7e1f14c29cb4 +# go.podman.io/image/v5 v5.39.2-0.20260323194312-7e1f14c29cb4 => github.com/giuseppe/container-libs/image/v5 v5.0.0-20260327134027-2f2ff1752ec0 ## explicit; go 1.25.6 go.podman.io/image/v5/copy go.podman.io/image/v5/directory @@ -877,7 +877,7 @@ go.podman.io/image/v5/transports go.podman.io/image/v5/transports/alltransports go.podman.io/image/v5/types go.podman.io/image/v5/version -# go.podman.io/storage v1.62.1-0.20260323194312-7e1f14c29cb4 +# go.podman.io/storage v1.62.1-0.20260323201758-04a109851e66 => github.com/giuseppe/container-libs/storage v0.0.0-20260327134027-2f2ff1752ec0 ## explicit; go 1.25.0 go.podman.io/storage go.podman.io/storage/drivers @@ -1180,3 +1180,6 @@ tags.cncf.io/container-device-interface/pkg/parser # tags.cncf.io/container-device-interface/specs-go v1.1.0 ## explicit; go 1.19 tags.cncf.io/container-device-interface/specs-go +# go.podman.io/common => github.com/giuseppe/container-libs/common v0.0.0-20260327134027-2f2ff1752ec0 +# go.podman.io/image/v5 => github.com/giuseppe/container-libs/image/v5 v5.0.0-20260327134027-2f2ff1752ec0 +# go.podman.io/storage => github.com/giuseppe/container-libs/storage v0.0.0-20260327134027-2f2ff1752ec0