diff --git a/go.mod b/go.mod index db6b7cd4c..8bde69b98 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/containerd/containerd v1.7.12 github.com/containernetworking/cni v1.1.2 github.com/containernetworking/plugins v1.4.0 - github.com/containers/image/v5 v5.29.1-0.20231221164234-1b221d4a9c28 + github.com/containers/image/v5 v5.29.1 github.com/containers/ocicrypt v1.1.9 github.com/containers/storage v1.51.1-0.20231221151421-1020ab61b4e5 github.com/coreos/go-systemd/v22 v22.5.0 diff --git a/go.sum b/go.sum index 1b6e2eb89..b41845aa8 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,8 @@ github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl3 github.com/containernetworking/cni v1.1.2/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= github.com/containernetworking/plugins v1.4.0 h1:+w22VPYgk7nQHw7KT92lsRmuToHvb7wwSv9iTbXzzic= github.com/containernetworking/plugins v1.4.0/go.mod h1:UYhcOyjefnrQvKvmmyEKsUA+M9Nfn7tqULPpH0Pkcj0= -github.com/containers/image/v5 v5.29.1-0.20231221164234-1b221d4a9c28 h1:dI4/9x4Oh8SWEKIP8KcwoCFUWDO8jHbbfLhaFr20R/Y= -github.com/containers/image/v5 v5.29.1-0.20231221164234-1b221d4a9c28/go.mod h1:LC9m+8ED9+Vuw2WSd/mgvrHbi/44WJj/XBDNdiZC0AY= +github.com/containers/image/v5 v5.29.1 h1:9COTXQpl3FgrW/jw/roLAWlW4TN9ly7/bCAKY76wYl8= +github.com/containers/image/v5 v5.29.1/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.1.9 h1:2Csfba4jse85Raxk5HIyEk8OwZNjRvfkhEGijOjIdEM= diff --git a/vendor/github.com/containers/image/v5/copy/manifest.go b/vendor/github.com/containers/image/v5/copy/manifest.go index 8844ac8e7..6f01cf5cc 100644 --- a/vendor/github.com/containers/image/v5/copy/manifest.go +++ b/vendor/github.com/containers/image/v5/copy/manifest.go @@ -6,10 +6,8 @@ import ( "fmt" "strings" - internalManifest "github.com/containers/image/v5/internal/manifest" "github.com/containers/image/v5/internal/set" "github.com/containers/image/v5/manifest" - compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/image/v5/types" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" @@ -21,8 +19,8 @@ import ( // Include v2s1 signed but not v2s1 unsigned, because docker/distribution requires a signature even if the unsigned MIME type is used. var preferredManifestMIMETypes = []string{manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1SignedMediaType} -// allManifestMIMETypes lists all possible manifest MIME types. -var allManifestMIMETypes = []string{v1.MediaTypeImageManifest, manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1SignedMediaType, manifest.DockerV2Schema1MediaType} +// ociEncryptionMIMETypes lists manifest MIME types that are known to support OCI encryption. +var ociEncryptionMIMETypes = []string{v1.MediaTypeImageManifest} // orderedSet is a list of strings (MIME types or platform descriptors in our case), with each string appearing at most once. type orderedSet struct { @@ -53,10 +51,9 @@ type determineManifestConversionInputs struct { destSupportedManifestMIMETypes []string // MIME types supported by the destination, per types.ImageDestination.SupportedManifestMIMETypes() - forceManifestMIMEType string // User’s choice of forced manifest MIME type - requestedCompressionFormat *compressiontypes.Algorithm // Compression algorithm to use, if the user _explictily_ requested one. - requiresOCIEncryption bool // Restrict to manifest formats that can support OCI encryption - cannotModifyManifestReason string // The reason the manifest cannot be modified, or an empty string if it can + forceManifestMIMEType string // User’s choice of forced manifest MIME type + requiresOCIEncryption bool // Restrict to manifest formats that can support OCI encryption + cannotModifyManifestReason string // The reason the manifest cannot be modified, or an empty string if it can } // manifestConversionPlan contains the decisions made by determineManifestConversion. @@ -83,74 +80,41 @@ func determineManifestConversion(in determineManifestConversionInputs) (manifest destSupportedManifestMIMETypes = []string{in.forceManifestMIMEType} } - restrictiveCompressionRequired := in.requestedCompressionFormat != nil && !internalManifest.CompressionAlgorithmIsUniversallySupported(*in.requestedCompressionFormat) if len(destSupportedManifestMIMETypes) == 0 { - if (!in.requiresOCIEncryption || manifest.MIMETypeSupportsEncryption(srcType)) && - (!restrictiveCompressionRequired || internalManifest.MIMETypeSupportsCompressionAlgorithm(srcType, *in.requestedCompressionFormat)) { + if !in.requiresOCIEncryption || manifest.MIMETypeSupportsEncryption(srcType) { return manifestConversionPlan{ // Anything goes; just use the original as is, do not try any conversions. preferredMIMEType: srcType, otherMIMETypeCandidates: []string{}, }, nil } - destSupportedManifestMIMETypes = allManifestMIMETypes + destSupportedManifestMIMETypes = ociEncryptionMIMETypes } supportedByDest := set.New[string]() for _, t := range destSupportedManifestMIMETypes { - if in.requiresOCIEncryption && !manifest.MIMETypeSupportsEncryption(t) { - continue + if !in.requiresOCIEncryption || manifest.MIMETypeSupportsEncryption(t) { + supportedByDest.Add(t) } - if restrictiveCompressionRequired && !internalManifest.MIMETypeSupportsCompressionAlgorithm(t, *in.requestedCompressionFormat) { - continue - } - supportedByDest.Add(t) } if supportedByDest.Empty() { - if len(destSupportedManifestMIMETypes) == 0 { // Coverage: This should never happen, empty values were replaced by allManifestMIMETypes + if len(destSupportedManifestMIMETypes) == 0 { // Coverage: This should never happen, empty values were replaced by ociEncryptionMIMETypes return manifestConversionPlan{}, errors.New("internal error: destSupportedManifestMIMETypes is empty") } - // We know, and have verified, that destSupportedManifestMIMETypes is not empty, so some filtering of supported MIME types must have been involved. - + // We know, and have verified, that destSupportedManifestMIMETypes is not empty, so encryption must have been involved. + if !in.requiresOCIEncryption { // Coverage: This should never happen, destSupportedManifestMIMETypes was not empty, so we should have filtered for encryption. + return manifestConversionPlan{}, errors.New("internal error: supportedByDest is empty but destSupportedManifestMIMETypes is not, and not encrypting") + } // destSupportedManifestMIMETypes has three possible origins: if in.forceManifestMIMEType != "" { // 1. forceManifestType specified - switch { - case in.requiresOCIEncryption && restrictiveCompressionRequired: - return manifestConversionPlan{}, fmt.Errorf("compression using %s, and encryption, required together with format %s, which does not support both", - in.requestedCompressionFormat.Name(), in.forceManifestMIMEType) - case in.requiresOCIEncryption: - return manifestConversionPlan{}, fmt.Errorf("encryption required together with format %s, which does not support encryption", - in.forceManifestMIMEType) - case restrictiveCompressionRequired: - return manifestConversionPlan{}, fmt.Errorf("compression using %s required together with format %s, which does not support it", - in.requestedCompressionFormat.Name(), in.forceManifestMIMEType) - default: - return manifestConversionPlan{}, errors.New("internal error: forceManifestMIMEType was rejected for an unknown reason") - } - } - if len(in.destSupportedManifestMIMETypes) == 0 { // 2. destination accepts anything and we have chosen allManifestTypes - if !restrictiveCompressionRequired { - // Coverage: This should never happen. - // If we have not rejected for encryption reasons, we must have rejected due to encryption, but - // allManifestTypes includes OCI, which supports encryption. - return manifestConversionPlan{}, errors.New("internal error: in.destSupportedManifestMIMETypes is empty but supportedByDest is empty as well") - } - // This can legitimately happen when the user asks for completely unsupported formats like Bzip2 or Xz. - return manifestConversionPlan{}, fmt.Errorf("compression using %s required, but none of the known manifest formats support it", in.requestedCompressionFormat.Name()) + return manifestConversionPlan{}, fmt.Errorf("encryption required together with format %s, which does not support encryption", + in.forceManifestMIMEType) } - // 3. destination accepts a restricted list of mime types - destMIMEList := strings.Join(destSupportedManifestMIMETypes, ", ") - switch { - case in.requiresOCIEncryption && restrictiveCompressionRequired: - return manifestConversionPlan{}, fmt.Errorf("compression using %s, and encryption, required but the destination only supports MIME types [%s], none of which support both", - in.requestedCompressionFormat.Name(), destMIMEList) - case in.requiresOCIEncryption: - return manifestConversionPlan{}, fmt.Errorf("encryption required but the destination only supports MIME types [%s], none of which support encryption", - destMIMEList) - case restrictiveCompressionRequired: - return manifestConversionPlan{}, fmt.Errorf("compression using %s required but the destination only supports MIME types [%s], none of which support it", - in.requestedCompressionFormat.Name(), destMIMEList) - default: // Coverage: This should never happen, we only filter for in.requiresOCIEncryption || restrictiveCompressionRequired - return manifestConversionPlan{}, errors.New("internal error: supportedByDest is empty but destSupportedManifestMIMETypes is not, and we are neither encrypting nor requiring a restrictive compression algorithm") + if len(in.destSupportedManifestMIMETypes) == 0 { // 2. destination accepts anything and we have chosen ociEncryptionMIMETypes + // Coverage: This should never happen, ociEncryptionMIMETypes all support encryption + return manifestConversionPlan{}, errors.New("internal error: in.destSupportedManifestMIMETypes is empty but supportedByDest is empty as well") } + // 3. destination does not support encryption. + return manifestConversionPlan{}, fmt.Errorf("encryption required but the destination only supports MIME types [%s], none of which support encryption", + strings.Join(destSupportedManifestMIMETypes, ", ")) } // destSupportedManifestMIMETypes is a static guess; a particular registry may still only support a subset of the types. @@ -192,7 +156,7 @@ func determineManifestConversion(in determineManifestConversionInputs) (manifest } logrus.Debugf("Manifest has MIME type %s, ordered candidate list [%s]", srcType, strings.Join(prioritizedTypes.list, ", ")) - if len(prioritizedTypes.list) == 0 { // Coverage: destSupportedManifestMIMETypes and supportedByDest, which is a subset, is not empty (or we would have exited above), so this should never happen. + if len(prioritizedTypes.list) == 0 { // Coverage: destSupportedManifestMIMETypes and supportedByDest, which is a subset, is not empty (or we would have exited above), so this should never happen. return manifestConversionPlan{}, errors.New("Internal error: no candidate MIME types") } res := manifestConversionPlan{ diff --git a/vendor/github.com/containers/image/v5/copy/single.go b/vendor/github.com/containers/image/v5/copy/single.go index 9003965c9..67ca43f7b 100644 --- a/vendor/github.com/containers/image/v5/copy/single.go +++ b/vendor/github.com/containers/image/v5/copy/single.go @@ -20,7 +20,6 @@ import ( compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/image/v5/transports" "github.com/containers/image/v5/types" - chunkedToc "github.com/containers/storage/pkg/chunked/toc" digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" @@ -168,7 +167,6 @@ func (c *copier) copySingleImage(ctx context.Context, unparsedImage *image.Unpar srcMIMEType: ic.src.ManifestMIMEType, destSupportedManifestMIMETypes: ic.c.dest.SupportedManifestMIMETypes(), forceManifestMIMEType: c.options.ForceManifestMIMEType, - requestedCompressionFormat: ic.compressionFormat, requiresOCIEncryption: destRequiresOciEncryption, cannotModifyManifestReason: ic.cannotModifyManifestReason, }) @@ -695,13 +693,6 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to requiredCompression = ic.compressionFormat originalCompression = srcInfo.CompressionAlgorithm } - - // Check if we have a chunked layer in storage that's based on that blob. These layers are stored by their TOC digest. - tocDigest, err := chunkedToc.GetTOCDigest(srcInfo.Annotations) - if err != nil { - return types.BlobInfo{}, "", err - } - reused, reusedBlob, err := ic.c.dest.TryReusingBlobWithOptions(ctx, srcInfo, private.TryReusingBlobOptions{ Cache: ic.c.blobInfoCache, CanSubstitute: canSubstitute, @@ -710,7 +701,6 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to SrcRef: srcRef, RequiredCompression: requiredCompression, OriginalCompression: originalCompression, - TOCDigest: tocDigest, }) if err != nil { return types.BlobInfo{}, "", fmt.Errorf("trying to reuse blob %s at destination: %w", srcInfo.Digest, err) diff --git a/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go b/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go index 4d3858ab8..429d68263 100644 --- a/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go +++ b/vendor/github.com/containers/image/v5/internal/blobinfocache/types.go @@ -36,7 +36,7 @@ type BlobInfoCache2 interface { // that could possibly be reused within the specified (transport scope) (if they still // exist, which is not guaranteed). // - // If !canSubstitute, the returned candidates will match the submitted digest exactly; if + // If !canSubstitute, the returned cadidates will match the submitted digest exactly; if // canSubstitute, data from previous RecordDigestUncompressedPair calls is used to also look // up variants of the blob which have the same uncompressed digest. // diff --git a/vendor/github.com/containers/image/v5/internal/imagedestination/wrapper.go b/vendor/github.com/containers/image/v5/internal/imagedestination/wrapper.go index cdd3c5e5d..17e1870c1 100644 --- a/vendor/github.com/containers/image/v5/internal/imagedestination/wrapper.go +++ b/vendor/github.com/containers/image/v5/internal/imagedestination/wrapper.go @@ -28,7 +28,7 @@ type wrapped struct { // // NOTE: The returned API MUST NOT be a public interface (it can be either just a struct // with public methods, or perhaps a private interface), so that we can add methods -// without breaking any external implementers of a public interface. +// without breaking any external implementors of a public interface. func FromPublic(dest types.ImageDestination) private.ImageDestination { if dest2, ok := dest.(private.ImageDestination); ok { return dest2 diff --git a/vendor/github.com/containers/image/v5/internal/imagesource/wrapper.go b/vendor/github.com/containers/image/v5/internal/imagesource/wrapper.go index f0d1d042b..886b4e833 100644 --- a/vendor/github.com/containers/image/v5/internal/imagesource/wrapper.go +++ b/vendor/github.com/containers/image/v5/internal/imagesource/wrapper.go @@ -27,7 +27,7 @@ type wrapped struct { // // NOTE: The returned API MUST NOT be a public interface (it can be either just a struct // with public methods, or perhaps a private interface), so that we can add methods -// without breaking any external implementers of a public interface. +// without breaking any external implementors of a public interface. func FromPublic(src types.ImageSource) private.ImageSource { if src2, ok := src.(private.ImageSource); ok { return src2 diff --git a/vendor/github.com/containers/image/v5/internal/manifest/manifest.go b/vendor/github.com/containers/image/v5/internal/manifest/manifest.go index 6f7bc8bbe..1dbcc1418 100644 --- a/vendor/github.com/containers/image/v5/internal/manifest/manifest.go +++ b/vendor/github.com/containers/image/v5/internal/manifest/manifest.go @@ -3,7 +3,6 @@ package manifest import ( "encoding/json" - compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/libtrust" digest "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -15,7 +14,7 @@ import ( const ( // DockerV2Schema1MediaType MIME type represents Docker manifest schema 1 DockerV2Schema1MediaType = "application/vnd.docker.distribution.manifest.v1+json" - // DockerV2Schema1SignedMediaType MIME type represents Docker manifest schema 1 with a JWS signature + // DockerV2Schema1MediaType MIME type represents Docker manifest schema 1 with a JWS signature DockerV2Schema1SignedMediaType = "application/vnd.docker.distribution.manifest.v1+prettyjws" // DockerV2Schema2MediaType MIME type represents Docker manifest schema 2 DockerV2Schema2MediaType = "application/vnd.docker.distribution.manifest.v2+json" @@ -166,26 +165,3 @@ func NormalizedMIMEType(input string) string { return DockerV2Schema1SignedMediaType } } - -// CompressionAlgorithmIsUniversallySupported returns true if MIMETypeSupportsCompressionAlgorithm(mimeType, algo) returns true for all mimeType values. -func CompressionAlgorithmIsUniversallySupported(algo compressiontypes.Algorithm) bool { - switch algo.Name() { // Should this use InternalUnstableUndocumentedMIMEQuestionMark() ? - case compressiontypes.GzipAlgorithmName: - return true - default: - return false - } -} - -// MIMETypeSupportsCompressionAlgorithm returns true if mimeType can represent algo. -func MIMETypeSupportsCompressionAlgorithm(mimeType string, algo compressiontypes.Algorithm) bool { - if CompressionAlgorithmIsUniversallySupported(algo) { - return true - } - switch algo.Name() { // Should this use InternalUnstableUndocumentedMIMEQuestionMark() ? - case compressiontypes.ZstdAlgorithmName, compressiontypes.ZstdChunkedAlgorithmName: - return mimeType == imgspecv1.MediaTypeImageManifest - default: // Includes Bzip2AlgorithmName and XzAlgorithmName, which are defined names but are not supported anywhere - return false - } -} diff --git a/vendor/github.com/containers/image/v5/internal/private/private.go b/vendor/github.com/containers/image/v5/internal/private/private.go index 72b574a5b..95d561fcd 100644 --- a/vendor/github.com/containers/image/v5/internal/private/private.go +++ b/vendor/github.com/containers/image/v5/internal/private/private.go @@ -117,7 +117,6 @@ type TryReusingBlobOptions struct { EmptyLayer bool // True if the blob is an "empty"/"throwaway" layer, and may not necessarily be physically represented. LayerIndex *int // If the blob is a layer, a zero-based index of the layer within the image; nil otherwise. SrcRef reference.Named // A reference to the source image that contains the input blob. - TOCDigest *digest.Digest // If specified, the blob can be looked up in the destination also by its TOC digest. } // ReusedBlob is information about a blob reused in a destination. diff --git a/vendor/github.com/containers/image/v5/manifest/docker_schema1.go b/vendor/github.com/containers/image/v5/manifest/docker_schema1.go index 762815570..a80af701a 100644 --- a/vendor/github.com/containers/image/v5/manifest/docker_schema1.go +++ b/vendor/github.com/containers/image/v5/manifest/docker_schema1.go @@ -10,7 +10,6 @@ import ( "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/internal/manifest" "github.com/containers/image/v5/internal/set" - compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/regexp" "github.com/docker/docker/api/types/versions" @@ -143,15 +142,6 @@ func (m *Schema1) LayerInfos() []LayerInfo { return layers } -const fakeSchema1MIMEType = DockerV2Schema2LayerMediaType // Used only in schema1CompressionMIMETypeSets -var schema1CompressionMIMETypeSets = []compressionMIMETypeSet{ - { - mtsUncompressed: fakeSchema1MIMEType, - compressiontypes.GzipAlgorithmName: fakeSchema1MIMEType, - compressiontypes.ZstdAlgorithmName: mtsUnsupportedMIMEType, - }, -} - // UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers) func (m *Schema1) UpdateLayerInfos(layerInfos []types.BlobInfo) error { // Our LayerInfos includes empty layers (where m.ExtractedV1Compatibility[].ThrowAway), so expect them to be included here as well. @@ -160,11 +150,6 @@ func (m *Schema1) UpdateLayerInfos(layerInfos []types.BlobInfo) error { } m.FSLayers = make([]Schema1FSLayers, len(layerInfos)) for i, info := range layerInfos { - // There are no MIME types in schema1, but we do a “conversion” here to reject unsupported compression algorithms, - // in a way that is consistent with the other schema implementations. - if _, err := updatedMIMEType(schema1CompressionMIMETypeSets, fakeSchema1MIMEType, info); err != nil { - return fmt.Errorf("preparing updated manifest, layer %q: %w", info.Digest, err) - } // (docker push) sets up m.ExtractedV1Compatibility[].{Id,Parent} based on values of info.Digest, // but (docker pull) ignores them in favor of computing DiffIDs from uncompressed data, except verifying the child->parent links and uniqueness. // So, we don't bother recomputing the IDs in m.History.V1Compatibility. diff --git a/vendor/github.com/containers/image/v5/manifest/manifest.go b/vendor/github.com/containers/image/v5/manifest/manifest.go index 828b8da0b..959aac935 100644 --- a/vendor/github.com/containers/image/v5/manifest/manifest.go +++ b/vendor/github.com/containers/image/v5/manifest/manifest.go @@ -16,7 +16,7 @@ import ( const ( // DockerV2Schema1MediaType MIME type represents Docker manifest schema 1 DockerV2Schema1MediaType = manifest.DockerV2Schema1MediaType - // DockerV2Schema1SignedMediaType MIME type represents Docker manifest schema 1 with a JWS signature + // DockerV2Schema1MediaType MIME type represents Docker manifest schema 1 with a JWS signature DockerV2Schema1SignedMediaType = manifest.DockerV2Schema1SignedMediaType // DockerV2Schema2MediaType MIME type represents Docker manifest schema 2 DockerV2Schema2MediaType = manifest.DockerV2Schema2MediaType diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go index 6d5acb45d..a85641c36 100644 --- a/vendor/github.com/containers/image/v5/manifest/oci.go +++ b/vendor/github.com/containers/image/v5/manifest/oci.go @@ -9,7 +9,6 @@ import ( compressiontypes "github.com/containers/image/v5/pkg/compression/types" "github.com/containers/image/v5/types" ociencspec "github.com/containers/ocicrypt/spec" - chunkedToc "github.com/containers/storage/pkg/chunked/toc" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -236,7 +235,7 @@ func (m *OCI1) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*type } // ImageID computes an ID which can uniquely identify this image by its contents. -func (m *OCI1) ImageID(diffIDs []digest.Digest) (string, error) { +func (m *OCI1) ImageID([]digest.Digest) (string, error) { // The way m.Config.Digest “uniquely identifies” an image is // by containing RootFS.DiffIDs, which identify the layers of the image. // For non-image artifacts, the we can’t expect the config to change @@ -260,44 +259,9 @@ func (m *OCI1) ImageID(diffIDs []digest.Digest) (string, error) { if err := m.Config.Digest.Validate(); err != nil { return "", err } - - // If there is any layer that is using partial content, we calculate the image ID - // in a different way since the diffID cannot be validated as for regular pulled images. - for _, layer := range m.Layers { - toc, err := chunkedToc.GetTOCDigest(layer.Annotations) - if err != nil { - return "", fmt.Errorf("error looking up annotation for layer %q: %w", layer.Digest, err) - } - if toc != nil { - return m.calculateImageIDForPartialImage(diffIDs) - } - } - return m.Config.Digest.Hex(), nil } -func (m *OCI1) calculateImageIDForPartialImage(diffIDs []digest.Digest) (string, error) { - newID := digest.Canonical.Digester() - for i, layer := range m.Layers { - diffID := diffIDs[i] - _, err := newID.Hash().Write([]byte(diffID.Hex())) - if err != nil { - return "", fmt.Errorf("error writing diffID %q: %w", diffID, err) - } - toc, err := chunkedToc.GetTOCDigest(layer.Annotations) - if err != nil { - return "", fmt.Errorf("error looking up annotation for layer %q: %w", layer.Digest, err) - } - if toc != nil { - _, err = newID.Hash().Write([]byte(toc.Hex())) - if err != nil { - return "", fmt.Errorf("error writing TOC %q: %w", toc, err) - } - } - } - return newID.Digest().Hex(), nil -} - // CanChangeLayerCompression returns true if we can compress/decompress layers with mimeType in the current image // (and the code can handle that). // NOTE: Even if this returns true, the relevant format might not accept all compression algorithms; the set of accepted diff --git a/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go b/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go index 6ca618e35..8386c47a3 100644 --- a/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go +++ b/vendor/github.com/containers/image/v5/oci/archive/oci_dest.go @@ -13,7 +13,6 @@ import ( "github.com/containers/image/v5/internal/signature" "github.com/containers/image/v5/types" "github.com/containers/storage/pkg/archive" - "github.com/containers/storage/pkg/idtools" digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" ) @@ -170,15 +169,10 @@ func (d *ociArchiveImageDestination) Commit(ctx context.Context, unparsedTopleve // tar converts the directory at src and saves it to dst func tarDirectory(src, dst string) error { // input is a stream of bytes from the archive of the directory at path - input, err := archive.TarWithOptions(src, &archive.TarOptions{ - Compression: archive.Uncompressed, - // Don’t include the data about the user account this code is running under. - ChownOpts: &idtools.IDPair{UID: 0, GID: 0}, - }) + input, err := archive.Tar(src, archive.Uncompressed) if err != nil { return fmt.Errorf("retrieving stream of bytes from %q: %w", src, err) } - defer input.Close() // creates the tar file outFile, err := os.Create(dst) diff --git a/vendor/github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize/prioritize.go b/vendor/github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize/prioritize.go index 470fca0c1..97562687c 100644 --- a/vendor/github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize/prioritize.go +++ b/vendor/github.com/containers/image/v5/pkg/blobinfocache/internal/prioritize/prioritize.go @@ -91,11 +91,11 @@ func min(a, b int) int { // destructivelyPrioritizeReplacementCandidatesWithMax is destructivelyPrioritizeReplacementCandidates with parameters for the // number of entries to limit for known and unknown location separately, only to make testing simpler. -// TODO: following function is not destructive any more in the nature instead prioritized result is actually copies of the original +// TODO: following function is not destructive any more in the nature instead priortized result is actually copies of the original // candidate set, so In future we might wanna re-name this public API and remove the destructive prefix. func destructivelyPrioritizeReplacementCandidatesWithMax(cs []CandidateWithTime, primaryDigest, uncompressedDigest digest.Digest, totalLimit int, noLocationLimit int) []blobinfocache.BICReplacementCandidate2 { // split unknown candidates and known candidates - // and limit them separately. + // and limit them seperately. var knownLocationCandidates []CandidateWithTime var unknownLocationCandidates []CandidateWithTime // We don't need to use sort.Stable() because nanosecond timestamps are (presumably?) unique, so no two elements should diff --git a/vendor/github.com/containers/image/v5/pkg/blobinfocache/memory/memory.go b/vendor/github.com/containers/image/v5/pkg/blobinfocache/memory/memory.go index 16193db95..cfad16b2e 100644 --- a/vendor/github.com/containers/image/v5/pkg/blobinfocache/memory/memory.go +++ b/vendor/github.com/containers/image/v5/pkg/blobinfocache/memory/memory.go @@ -184,7 +184,7 @@ func (mem *cache) CandidateLocations(transport types.ImageTransport, scope types // CandidateLocations2 returns a prioritized, limited, number of blobs and their locations (if known) that could possibly be reused // within the specified (transport scope) (if they still exist, which is not guaranteed). // -// If !canSubstitute, the returned candidates will match the submitted digest exactly; if canSubstitute, +// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if canSubstitute, // data from previous RecordDigestUncompressedPair calls is used to also look up variants of the blob which have the same // uncompressed digest. func (mem *cache) CandidateLocations2(transport types.ImageTransport, scope types.BICTransportScope, primaryDigest digest.Digest, canSubstitute bool) []blobinfocache.BICReplacementCandidate2 { diff --git a/vendor/github.com/containers/image/v5/pkg/blobinfocache/sqlite/sqlite.go b/vendor/github.com/containers/image/v5/pkg/blobinfocache/sqlite/sqlite.go index d8bde2fa0..2b446a61c 100644 --- a/vendor/github.com/containers/image/v5/pkg/blobinfocache/sqlite/sqlite.go +++ b/vendor/github.com/containers/image/v5/pkg/blobinfocache/sqlite/sqlite.go @@ -171,7 +171,7 @@ func transaction[T any](sqc *cache, fn func(tx *sql.Tx) (T, error)) (T, error) { // dbTransaction calls fn within a read-write transaction in db. func dbTransaction[T any](db *sql.DB, fn func(tx *sql.Tx) (T, error)) (T, error) { - // Ideally we should be able to distinguish between read-only and read-write transactions, see the _txlock=exclusive discussion. + // Ideally we should be able to distinguish between read-only and read-write transactions, see the _txlock=exclusive dicussion. var zeroRes T // A zero value of T @@ -496,7 +496,7 @@ func (sqc *cache) appendReplacementCandidates(candidates []prioritize.CandidateW // that could possibly be reused within the specified (transport scope) (if they still // exist, which is not guaranteed). // -// If !canSubstitute, the returned candidates will match the submitted digest exactly; if +// If !canSubstitute, the returned cadidates will match the submitted digest exactly; if // canSubstitute, data from previous RecordDigestUncompressedPair calls is used to also look // up variants of the blob which have the same uncompressed digest. // diff --git a/vendor/github.com/containers/image/v5/signature/fulcio_cert.go b/vendor/github.com/containers/image/v5/signature/fulcio_cert.go index c11fa46a9..ef5d3df6f 100644 --- a/vendor/github.com/containers/image/v5/signature/fulcio_cert.go +++ b/vendor/github.com/containers/image/v5/signature/fulcio_cert.go @@ -1,6 +1,3 @@ -//go:build !containers_image_fulcio_stub -// +build !containers_image_fulcio_stub - package signature import ( diff --git a/vendor/github.com/containers/image/v5/signature/fulcio_cert_stub.go b/vendor/github.com/containers/image/v5/signature/fulcio_cert_stub.go deleted file mode 100644 index c0b48dafa..000000000 --- a/vendor/github.com/containers/image/v5/signature/fulcio_cert_stub.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build containers_image_fulcio_stub -// +build containers_image_fulcio_stub - -package signature - -import ( - "crypto" - "crypto/ecdsa" - "crypto/x509" - "errors" -) - -type fulcioTrustRoot struct { - caCertificates *x509.CertPool - oidcIssuer string - subjectEmail string -} - -func (f *fulcioTrustRoot) validate() error { - return errors.New("fulcio disabled at compile-time") -} - -func verifyRekorFulcio(rekorPublicKey *ecdsa.PublicKey, fulcioTrustRoot *fulcioTrustRoot, untrustedRekorSET []byte, - untrustedCertificateBytes []byte, untrustedIntermediateChainBytes []byte, untrustedBase64Signature string, - untrustedPayloadBytes []byte) (crypto.PublicKey, error) { - return nil, errors.New("fulcio disabled at compile-time") - -} diff --git a/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go b/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go index d86e98a45..d439b5f7a 100644 --- a/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go +++ b/vendor/github.com/containers/image/v5/signature/internal/rekor_set.go @@ -1,6 +1,3 @@ -//go:build !containers_image_rekor_stub -// +build !containers_image_rekor_stub - package internal import ( diff --git a/vendor/github.com/containers/image/v5/signature/internal/rekor_set_stub.go b/vendor/github.com/containers/image/v5/signature/internal/rekor_set_stub.go deleted file mode 100644 index 7c121cc2e..000000000 --- a/vendor/github.com/containers/image/v5/signature/internal/rekor_set_stub.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build containers_image_rekor_stub -// +build containers_image_rekor_stub - -package internal - -import ( - "crypto/ecdsa" - "time" -) - -// VerifyRekorSET verifies that unverifiedRekorSET is correctly signed by publicKey and matches the rest of the data. -// Returns bundle upload time on success. -func VerifyRekorSET(publicKey *ecdsa.PublicKey, unverifiedRekorSET []byte, unverifiedKeyOrCertBytes []byte, unverifiedBase64Signature string, unverifiedPayloadBytes []byte) (time.Time, error) { - return time.Time{}, NewInvalidSignatureError("rekor disabled at compile-time") -} diff --git a/vendor/github.com/containers/image/v5/storage/storage_dest.go b/vendor/github.com/containers/image/v5/storage/storage_dest.go index bbbff6cf9..07e1d5e1f 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_dest.go +++ b/vendor/github.com/containers/image/v5/storage/storage_dest.go @@ -77,13 +77,13 @@ type storageImageDestination struct { indexToStorageID map[int]*string // All accesses to below data are protected by `lock` which is made // *explicit* in the code. - uncompressedOrTocDigest map[digest.Digest]digest.Digest // Mapping from layer blobsums to their corresponding DiffIDs or TOC IDs. - fileSizes map[digest.Digest]int64 // Mapping from layer blobsums to their sizes - filenames map[digest.Digest]string // Mapping from layer blobsums to names of files we used to hold them - currentIndex int // The index of the layer to be committed (i.e., lower indices have already been committed) - indexToAddedLayerInfo map[int]addedLayerInfo // Mapping from layer (by index) to blob to add to the image - blobAdditionalLayer map[digest.Digest]storage.AdditionalLayer // Mapping from layer blobsums to their corresponding additional layer - diffOutputs map[digest.Digest]*graphdriver.DriverWithDifferOutput // Mapping from digest to differ output + blobDiffIDs map[digest.Digest]digest.Digest // Mapping from layer blobsums to their corresponding DiffIDs + fileSizes map[digest.Digest]int64 // Mapping from layer blobsums to their sizes + filenames map[digest.Digest]string // Mapping from layer blobsums to names of files we used to hold them + currentIndex int // The index of the layer to be committed (i.e., lower indices have already been committed) + indexToAddedLayerInfo map[int]addedLayerInfo // Mapping from layer (by index) to blob to add to the image + blobAdditionalLayer map[digest.Digest]storage.AdditionalLayer // Mapping from layer blobsums to their corresponding additional layer + diffOutputs map[digest.Digest]*graphdriver.DriverWithDifferOutput // Mapping from digest to differ output } // addedLayerInfo records data about a layer to use in this image. @@ -117,18 +117,18 @@ func newImageDestination(sys *types.SystemContext, imageRef storageReference) (* HasThreadSafePutBlob: true, }), - imageRef: imageRef, - directory: directory, - signatureses: make(map[digest.Digest][]byte), - uncompressedOrTocDigest: make(map[digest.Digest]digest.Digest), - blobAdditionalLayer: make(map[digest.Digest]storage.AdditionalLayer), - fileSizes: make(map[digest.Digest]int64), - filenames: make(map[digest.Digest]string), - SignatureSizes: []int{}, - SignaturesSizes: make(map[digest.Digest][]int), - indexToStorageID: make(map[int]*string), - indexToAddedLayerInfo: make(map[int]addedLayerInfo), - diffOutputs: make(map[digest.Digest]*graphdriver.DriverWithDifferOutput), + imageRef: imageRef, + directory: directory, + signatureses: make(map[digest.Digest][]byte), + blobDiffIDs: make(map[digest.Digest]digest.Digest), + blobAdditionalLayer: make(map[digest.Digest]storage.AdditionalLayer), + fileSizes: make(map[digest.Digest]int64), + filenames: make(map[digest.Digest]string), + SignatureSizes: []int{}, + SignaturesSizes: make(map[digest.Digest][]int), + indexToStorageID: make(map[int]*string), + indexToAddedLayerInfo: make(map[int]addedLayerInfo), + diffOutputs: make(map[digest.Digest]*graphdriver.DriverWithDifferOutput), } dest.Compat = impl.AddCompat(dest) return dest, nil @@ -227,7 +227,7 @@ func (s *storageImageDestination) putBlobToPendingFile(stream io.Reader, blobinf // Record information about the blob. s.lock.Lock() - s.uncompressedOrTocDigest[blobDigest] = diffID.Digest() + s.blobDiffIDs[blobDigest] = diffID.Digest() s.fileSizes[blobDigest] = counter.Count s.filenames[blobDigest] = filename s.lock.Unlock() @@ -289,7 +289,7 @@ func (s *storageImageDestination) PutBlobPartial(ctx context.Context, chunkAcces blobDigest := srcInfo.Digest s.lock.Lock() - s.uncompressedOrTocDigest[blobDigest] = blobDigest + s.blobDiffIDs[blobDigest] = blobDigest s.fileSizes[blobDigest] = 0 s.filenames[blobDigest] = "" s.diffOutputs[blobDigest] = out @@ -321,7 +321,7 @@ func (s *storageImageDestination) TryReusingBlobWithOptions(ctx context.Context, }) } -// tryReusingBlobAsPending implements TryReusingBlobWithOptions for (digest, size or -1), filling s.uncompressedOrTocDigest and other metadata. +// tryReusingBlobAsPending implements TryReusingBlobWithOptions for (digest, size or -1), filling s.blobDiffIDs and other metadata. // The caller must arrange the blob to be eventually committed using s.commitLayer(). func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, size int64, options *private.TryReusingBlobOptions) (bool, private.ReusedBlob, error) { // lock the entire method as it executes fairly quickly @@ -335,7 +335,7 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, return false, private.ReusedBlob{}, fmt.Errorf(`looking for compressed layers with digest %q and labels: %w`, digest, err) } else if err == nil { // Record the uncompressed value so that we can use it to calculate layer IDs. - s.uncompressedOrTocDigest[digest] = aLayer.UncompressedDigest() + s.blobDiffIDs[digest] = aLayer.UncompressedDigest() s.blobAdditionalLayer[digest] = aLayer return true, private.ReusedBlob{ Digest: digest, @@ -366,7 +366,7 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, } if len(layers) > 0 { // Save this for completeness. - s.uncompressedOrTocDigest[digest] = layers[0].UncompressedDigest + s.blobDiffIDs[digest] = layers[0].UncompressedDigest return true, private.ReusedBlob{ Digest: digest, Size: layers[0].UncompressedSize, @@ -380,7 +380,7 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, } if len(layers) > 0 { // Record the uncompressed value so that we can use it to calculate layer IDs. - s.uncompressedOrTocDigest[digest] = layers[0].UncompressedDigest + s.blobDiffIDs[digest] = layers[0].UncompressedDigest return true, private.ReusedBlob{ Digest: digest, Size: layers[0].CompressedSize, @@ -398,7 +398,7 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, } if len(layers) > 0 { if size != -1 { - s.uncompressedOrTocDigest[digest] = layers[0].UncompressedDigest + s.blobDiffIDs[digest] = layers[0].UncompressedDigest return true, private.ReusedBlob{ Digest: digest, Size: size, @@ -407,7 +407,7 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, if !options.CanSubstitute { return false, private.ReusedBlob{}, fmt.Errorf("Internal error: options.CanSubstitute was expected to be true for blob with digest %s", digest) } - s.uncompressedOrTocDigest[uncompressedDigest] = layers[0].UncompressedDigest + s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest return true, private.ReusedBlob{ Digest: uncompressedDigest, Size: layers[0].UncompressedSize, @@ -416,25 +416,6 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, } } - tocDigest := digest - if options.TOCDigest != nil { - tocDigest = *options.TOCDigest - } - - // Check if we have a chunked layer in storage with the same TOC digest. - layers, err = s.imageRef.transport.store.LayersByTOCDigest(tocDigest) - if err != nil && !errors.Is(err, storage.ErrLayerUnknown) { - return false, private.ReusedBlob{}, fmt.Errorf(`looking for layers with TOC digest %q: %w`, tocDigest, err) - } - if len(layers) > 0 { - // Save this for completeness. - s.uncompressedOrTocDigest[digest] = layers[0].TOCDigest - return true, private.ReusedBlob{ - Digest: layers[0].TOCDigest, - Size: layers[0].UncompressedSize, - }, nil - } - // Nope, we don't have it. return false, private.ReusedBlob{}, nil } @@ -457,20 +438,16 @@ func (s *storageImageDestination) computeID(m manifest.Manifest) string { continue } blobSum := m.FSLayers[i].BlobSum - diffID, ok := s.uncompressedOrTocDigest[blobSum] + diffID, ok := s.blobDiffIDs[blobSum] if !ok { logrus.Infof("error looking up diffID for layer %q", blobSum.String()) return "" } diffIDs = append([]digest.Digest{diffID}, diffIDs...) } - case *manifest.Schema2: - // We know the ID calculation doesn't actually use the diffIDs, so we don't need to populate - // the diffID list. - case *manifest.OCI1: - for _, l := range m.Layers { - diffIDs = append(diffIDs, l.Digest) - } + case *manifest.Schema2, *manifest.OCI1: + // We know the ID calculation for these formats doesn't actually use the diffIDs, + // so we don't need to populate the diffID list. default: return "" } @@ -541,7 +518,7 @@ func (s *storageImageDestination) queueOrCommit(index int, info addedLayerInfo) } s.lock.Unlock() // Note: commitLayer locks on-demand. - if stopQueue, err := s.commitLayer(index, info, -1); stopQueue || err != nil { + if err := s.commitLayer(index, info, -1); err != nil { return err } s.lock.Lock() @@ -555,32 +532,18 @@ func (s *storageImageDestination) queueOrCommit(index int, info addedLayerInfo) return nil } -// getDiffIDOrTOCDigest returns the diffID for the specified digest or the digest for the TOC, if known. -func (s *storageImageDestination) getDiffIDOrTOCDigest(uncompressedDigest digest.Digest) (digest.Digest, bool) { - s.lock.Lock() - defer s.lock.Unlock() - - if d, found := s.diffOutputs[uncompressedDigest]; found { - return d.TOCDigest, found - } - d, found := s.uncompressedOrTocDigest[uncompressedDigest] - return d, found -} - // commitLayer commits the specified layer with the given index to the storage. -// size can usually be -1; it can be provided if the layer is not known to be already present in uncompressedOrTocDigest. -// -// If the layer cannot be committed yet, the function returns (true, nil). +// size can usually be -1; it can be provided if the layer is not known to be already present in blobDiffIDs. // // Note that the previous layer is expected to already be committed. // // Caution: this function must be called without holding `s.lock`. Callers // must guarantee that, at any given time, at most one goroutine may execute // `commitLayer()`. -func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, size int64) (bool, error) { +func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, size int64) error { // Already committed? Return early. if _, alreadyCommitted := s.indexToStorageID[index]; alreadyCommitted { - return false, nil + return nil } // Start with an empty string or the previous layer ID. Note that @@ -594,96 +557,68 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si // Carry over the previous ID for empty non-base layers. if info.emptyLayer { s.indexToStorageID[index] = &lastLayer - return false, nil + return nil } // Check if there's already a layer with the ID that we'd give to the result of applying // this layer blob to its parent, if it has one, or the blob's hex value otherwise. - // The diffIDOrTOCDigest refers either to the DiffID or the digest of the TOC. - diffIDOrTOCDigest, haveDiffIDOrTOCDigest := s.getDiffIDOrTOCDigest(info.digest) - if !haveDiffIDOrTOCDigest { + s.lock.Lock() + diffID, haveDiffID := s.blobDiffIDs[info.digest] + s.lock.Unlock() + if !haveDiffID { // Check if it's elsewhere and the caller just forgot to pass it to us in a PutBlob(), // or to even check if we had it. // Use none.NoCache to avoid a repeated DiffID lookup in the BlobInfoCache; a caller // that relies on using a blob digest that has never been seen by the store had better call // TryReusingBlob; not calling PutBlob already violates the documented API, so there’s only // so far we are going to accommodate that (if we should be doing that at all). - logrus.Debugf("looking for diffID or TOC digest for blob %+v", info.digest) + logrus.Debugf("looking for diffID for blob %+v", info.digest) // Use tryReusingBlobAsPending, not the top-level TryReusingBlobWithOptions, to prevent recursion via queueOrCommit. has, _, err := s.tryReusingBlobAsPending(info.digest, size, &private.TryReusingBlobOptions{ Cache: none.NoCache, CanSubstitute: false, }) if err != nil { - return false, fmt.Errorf("checking for a layer based on blob %q: %w", info.digest.String(), err) + return fmt.Errorf("checking for a layer based on blob %q: %w", info.digest.String(), err) } if !has { - return false, fmt.Errorf("error determining uncompressed digest or TOC digest for blob %q", info.digest.String()) + return fmt.Errorf("error determining uncompressed digest for blob %q", info.digest.String()) } - diffIDOrTOCDigest, haveDiffIDOrTOCDigest = s.getDiffIDOrTOCDigest(info.digest) - if !haveDiffIDOrTOCDigest { - return false, fmt.Errorf("we have blob %q, but don't know its uncompressed or TOC digest", info.digest.String()) + diffID, haveDiffID = s.blobDiffIDs[info.digest] + if !haveDiffID { + return fmt.Errorf("we have blob %q, but don't know its uncompressed digest", info.digest.String()) } } - id := diffIDOrTOCDigest.Hex() + id := diffID.Hex() if lastLayer != "" { - id = digest.Canonical.FromBytes([]byte(lastLayer + "+" + diffIDOrTOCDigest.Hex())).Hex() + id = digest.Canonical.FromBytes([]byte(lastLayer + "+" + diffID.Hex())).Hex() } if layer, err2 := s.imageRef.transport.store.Layer(id); layer != nil && err2 == nil { // There's already a layer that should have the right contents, just reuse it. lastLayer = layer.ID s.indexToStorageID[index] = &lastLayer - return false, nil + return nil } s.lock.Lock() diffOutput, ok := s.diffOutputs[info.digest] s.lock.Unlock() if ok { - if s.manifest == nil { - logrus.Debugf("Skipping commit for TOC=%q, manifest not yet available", id) - return true, nil - } - - man, err := manifest.FromBlob(s.manifest, manifest.GuessMIMEType(s.manifest)) - if err != nil { - return false, fmt.Errorf("parsing manifest: %w", err) - } - - cb, err := s.getConfigBlob(man.ConfigInfo()) - if err != nil { - return false, err - } - - // retrieve the expected uncompressed digest from the config blob. - configOCI := &imgspecv1.Image{} - if err := json.Unmarshal(cb, configOCI); err != nil { - return false, err - } - if index >= len(configOCI.RootFS.DiffIDs) { - return false, fmt.Errorf("index %d out of range for configOCI.RootFS.DiffIDs", index) - } - layer, err := s.imageRef.transport.store.CreateLayer(id, lastLayer, nil, "", false, nil) if err != nil { - return false, err + return err } - // let the storage layer know what was the original uncompressed layer. - flags := make(map[string]interface{}) - flags[expectedLayerDiffIDFlag] = configOCI.RootFS.DiffIDs[index] - logrus.Debugf("Setting uncompressed digest to %q for layer %q", configOCI.RootFS.DiffIDs[index], id) - options := &graphdriver.ApplyDiffWithDifferOpts{ - Flags: flags, - } + // FIXME: what to do with the uncompressed digest? + diffOutput.UncompressedDigest = info.digest - if err := s.imageRef.transport.store.ApplyDiffFromStagingDirectory(layer.ID, diffOutput.Target, diffOutput, options); err != nil { + if err := s.imageRef.transport.store.ApplyDiffFromStagingDirectory(layer.ID, diffOutput.Target, diffOutput, nil); err != nil { _ = s.imageRef.transport.store.Delete(layer.ID) - return false, err + return err } s.indexToStorageID[index] = &layer.ID - return false, nil + return nil } s.lock.Lock() @@ -692,11 +627,11 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si if ok { layer, err := al.PutAs(id, lastLayer, nil) if err != nil && !errors.Is(err, storage.ErrDuplicateID) { - return false, fmt.Errorf("failed to put layer from digest and labels: %w", err) + return fmt.Errorf("failed to put layer from digest and labels: %w", err) } lastLayer = layer.ID s.indexToStorageID[index] = &lastLayer - return false, nil + return nil } // Check if we previously cached a file with that blob's contents. If we didn't, @@ -707,7 +642,7 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si if !ok { // Try to find the layer with contents matching that blobsum. layer := "" - layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(diffIDOrTOCDigest) + layers, err2 := s.imageRef.transport.store.LayersByUncompressedDigest(diffID) if err2 == nil && len(layers) > 0 { layer = layers[0].ID } else { @@ -717,7 +652,7 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si } } if layer == "" { - return false, fmt.Errorf("locating layer for blob %q: %w", info.digest, err2) + return fmt.Errorf("locating layer for blob %q: %w", info.digest, err2) } // Read the layer's contents. noCompression := archive.Uncompressed @@ -726,17 +661,17 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si } diff, err2 := s.imageRef.transport.store.Diff("", layer, diffOptions) if err2 != nil { - return false, fmt.Errorf("reading layer %q for blob %q: %w", layer, info.digest, err2) + return fmt.Errorf("reading layer %q for blob %q: %w", layer, info.digest, err2) } // Copy the layer diff to a file. Diff() takes a lock that it holds // until the ReadCloser that it returns is closed, and PutLayer() wants // the same lock, so the diff can't just be directly streamed from one // to the other. filename = s.computeNextBlobCacheFile() - file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, 0o600) + file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, 0600) if err != nil { diff.Close() - return false, fmt.Errorf("creating temporary file %q: %w", filename, err) + return fmt.Errorf("creating temporary file %q: %w", filename, err) } // Copy the data to the file. // TODO: This can take quite some time, and should ideally be cancellable using @@ -745,7 +680,7 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si diff.Close() file.Close() if err != nil { - return false, fmt.Errorf("storing blob to file %q: %w", filename, err) + return fmt.Errorf("storing blob to file %q: %w", filename, err) } // Make sure that we can find this file later, should we need the layer's // contents again. @@ -756,21 +691,21 @@ func (s *storageImageDestination) commitLayer(index int, info addedLayerInfo, si // Read the cached blob and use it as a diff. file, err := os.Open(filename) if err != nil { - return false, fmt.Errorf("opening file %q: %w", filename, err) + return fmt.Errorf("opening file %q: %w", filename, err) } 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(id, lastLayer, nil, "", false, &storage.LayerOptions{ OriginalDigest: info.digest, - UncompressedDigest: diffIDOrTOCDigest, + UncompressedDigest: diffID, }, file) if err != nil && !errors.Is(err, storage.ErrDuplicateID) { - return false, fmt.Errorf("adding layer with blob %q: %w", info.digest, err) + return fmt.Errorf("adding layer with blob %q: %w", info.digest, err) } s.indexToStorageID[index] = &layer.ID - return false, nil + return nil } // Commit marks the process of storing the image as successful and asks for the image to be persisted. @@ -817,13 +752,11 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t // Extract, commit, or find the layers. for i, blob := range layerBlobs { - if stopQueue, err := s.commitLayer(i, addedLayerInfo{ + if err := s.commitLayer(i, addedLayerInfo{ digest: blob.Digest, emptyLayer: blob.EmptyLayer, }, blob.Size); err != nil { return err - } else if stopQueue { - return fmt.Errorf("Internal error: storageImageDestination.Commit(): commitLayer() not ready to commit for layer %q", blob.Digest) } } var lastLayer string diff --git a/vendor/github.com/containers/image/v5/storage/storage_src.go b/vendor/github.com/containers/image/v5/storage/storage_src.go index 7022d322e..f1ce0861e 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_src.go +++ b/vendor/github.com/containers/image/v5/storage/storage_src.go @@ -29,33 +29,21 @@ import ( "github.com/sirupsen/logrus" ) -// getBlobMutexProtected is a struct to hold the state of the getBlobMutex mutex. -type getBlobMutexProtected struct { - // digestToLayerID is a lookup map from the layer digest (either the uncompressed digest or the TOC digest) to the - // layer ID in the store. - digestToLayerID map[digest.Digest]string - - // layerPosition stores where we are in reading a blob's layers - layerPosition map[digest.Digest]int -} - type storageImageSource struct { impl.Compat impl.PropertyMethodsInitialize stubs.NoGetBlobAtInitialize - imageRef storageReference - image *storage.Image - systemContext *types.SystemContext // SystemContext used in GetBlob() to create temporary files - cachedManifest []byte // A cached copy of the manifest, if already known, or nil - getBlobMutex sync.Mutex // Mutex to sync state for parallel GetBlob executions (it guards layerPosition and digestToLayerID) - getBlobMutexProtected getBlobMutexProtected - SignatureSizes []int `json:"signature-sizes,omitempty"` // List of sizes of each signature slice - SignaturesSizes map[digest.Digest][]int `json:"signatures-sizes,omitempty"` // List of sizes of each signature slice + imageRef storageReference + image *storage.Image + systemContext *types.SystemContext // SystemContext used in GetBlob() to create temporary files + layerPosition map[digest.Digest]int // Where we are in reading a blob's layers + cachedManifest []byte // A cached copy of the manifest, if already known, or nil + getBlobMutex sync.Mutex // Mutex to sync state for parallel GetBlob executions + SignatureSizes []int `json:"signature-sizes,omitempty"` // List of sizes of each signature slice + SignaturesSizes map[digest.Digest][]int `json:"signatures-sizes,omitempty"` // List of sizes of each signature slice } -const expectedLayerDiffIDFlag = "expected-layer-diffid" - // newImageSource sets up an image for reading. func newImageSource(sys *types.SystemContext, imageRef storageReference) (*storageImageSource, error) { // First, locate the image. @@ -74,12 +62,9 @@ func newImageSource(sys *types.SystemContext, imageRef storageReference) (*stora imageRef: imageRef, systemContext: sys, image: img, + layerPosition: make(map[digest.Digest]int), SignatureSizes: []int{}, SignaturesSizes: make(map[digest.Digest][]int), - getBlobMutexProtected: getBlobMutexProtected{ - digestToLayerID: make(map[digest.Digest]string), - layerPosition: make(map[digest.Digest]int), - }, } image.Compat = impl.AddCompat(image) if img.Metadata != "" { @@ -106,7 +91,6 @@ func (s *storageImageSource) Close() error { func (s *storageImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (rc io.ReadCloser, n int64, err error) { // We need a valid digest value. digest := info.Digest - err = digest.Validate() if err != nil { return nil, 0, err @@ -116,24 +100,10 @@ func (s *storageImageSource) GetBlob(ctx context.Context, info types.BlobInfo, c return io.NopCloser(bytes.NewReader(image.GzippedEmptyLayer)), int64(len(image.GzippedEmptyLayer)), nil } - var layers []storage.Layer - - // If the digest was overridden by LayerInfosForCopy, then we need to use the TOC digest - // to retrieve it from the storage. - s.getBlobMutex.Lock() - layerID, found := s.getBlobMutexProtected.digestToLayerID[digest] - s.getBlobMutex.Unlock() - - if found { - if layer, err := s.imageRef.transport.store.Layer(layerID); err == nil { - layers = []storage.Layer{*layer} - } - } else { - // Check if the blob corresponds to a diff that was used to initialize any layers. Our - // callers should try to retrieve layers using their uncompressed digests, so no need to - // check if they're using one of the compressed digests, which we can't reproduce anyway. - layers, _ = s.imageRef.transport.store.LayersByUncompressedDigest(digest) - } + // Check if the blob corresponds to a diff that was used to initialize any layers. Our + // callers should try to retrieve layers using their uncompressed digests, so no need to + // check if they're using one of the compressed digests, which we can't reproduce anyway. + layers, _ := s.imageRef.transport.store.LayersByUncompressedDigest(digest) // If it's not a layer, then it must be a data item. if len(layers) == 0 { @@ -204,8 +174,8 @@ func (s *storageImageSource) getBlobAndLayerID(digest digest.Digest, layers []st // which claim to have the same contents, that we actually do have multiple layers, otherwise we could // just go ahead and use the first one every time. s.getBlobMutex.Lock() - i := s.getBlobMutexProtected.layerPosition[digest] - s.getBlobMutexProtected.layerPosition[digest] = i + 1 + i := s.layerPosition[digest] + s.layerPosition[digest] = i + 1 s.getBlobMutex.Unlock() if len(layers) > 0 { layer = layers[i%len(layers)] @@ -297,35 +267,14 @@ func (s *storageImageSource) LayerInfosForCopy(ctx context.Context, instanceDige if err != nil { return nil, fmt.Errorf("reading layer %q in image %q: %w", layerID, s.image.ID, err) } - if layer.UncompressedDigest == "" && layer.TOCDigest == "" { - return nil, fmt.Errorf("uncompressed digest and TOC digest for layer %q is unknown", layerID) + if layer.UncompressedDigest == "" { + return nil, fmt.Errorf("uncompressed digest for layer %q is unknown", layerID) } if layer.UncompressedSize < 0 { return nil, fmt.Errorf("uncompressed size for layer %q is unknown", layerID) } - - blobDigest := layer.UncompressedDigest - - if layer.TOCDigest != "" { - if layer.Flags == nil || layer.Flags[expectedLayerDiffIDFlag] == nil { - return nil, fmt.Errorf("TOC digest %q for layer %q is present but %q flag is not set", layer.TOCDigest, layerID, expectedLayerDiffIDFlag) - } - if expectedDigest, ok := layer.Flags[expectedLayerDiffIDFlag].(string); ok { - // if the layer is stored by its TOC, report the expected diffID as the layer Digest - // but store the TOC digest so we can later retrieve it from the storage. - blobDigest, err = digest.Parse(expectedDigest) - if err != nil { - return nil, fmt.Errorf("parsing expected diffID %q for layer %q: %w", expectedDigest, layerID, err) - } - } else { - return nil, fmt.Errorf("TOC digest %q for layer %q is present but %q flag is not a string", layer.TOCDigest, layerID, expectedLayerDiffIDFlag) - } - } - s.getBlobMutex.Lock() - s.getBlobMutexProtected.digestToLayerID[blobDigest] = layer.ID - s.getBlobMutex.Unlock() blobInfo := types.BlobInfo{ - Digest: blobDigest, + Digest: layer.UncompressedDigest, Size: layer.UncompressedSize, MediaType: uncompressedLayerType, } @@ -435,7 +384,7 @@ func (s *storageImageSource) getSize() (int64, error) { if err != nil { return -1, err } - if (layer.TOCDigest == "" && layer.UncompressedDigest == "") || layer.UncompressedSize < 0 { + if layer.UncompressedDigest == "" || layer.UncompressedSize < 0 { return -1, fmt.Errorf("size for layer %q is unknown, failing getSize()", layerID) } sum += layer.UncompressedSize diff --git a/vendor/github.com/containers/image/v5/storage/storage_transport.go b/vendor/github.com/containers/image/v5/storage/storage_transport.go index b981953ad..deb500b4d 100644 --- a/vendor/github.com/containers/image/v5/storage/storage_transport.go +++ b/vendor/github.com/containers/image/v5/storage/storage_transport.go @@ -213,7 +213,7 @@ func (s *storageTransport) GetStore() (storage.Store, error) { // Return the transport's previously-set store. If we don't have one // of those, initialize one now. if s.store == nil { - options, err := storage.DefaultStoreOptions() + options, err := storage.DefaultStoreOptionsAutoDetectUID() if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 0a057ddf0..f5ee5a7fe 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -11,7 +11,7 @@ const ( VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-dev" + VersionDev = "" ) // Version is the specification version that the package types support. diff --git a/vendor/github.com/containers/storage/pkg/chunked/toc/toc.go b/vendor/github.com/containers/storage/pkg/chunked/toc/toc.go deleted file mode 100644 index 9cfd97d8e..000000000 --- a/vendor/github.com/containers/storage/pkg/chunked/toc/toc.go +++ /dev/null @@ -1,34 +0,0 @@ -package toc - -import ( - "github.com/containers/storage/pkg/chunked/internal" - digest "github.com/opencontainers/go-digest" -) - -// tocJSONDigestAnnotation is the annotation key for the digest of the estargz -// TOC JSON. -// It is defined in github.com/containerd/stargz-snapshotter/estargz as TOCJSONDigestAnnotation -// Duplicate it here to avoid a dependency on the package. -const tocJSONDigestAnnotation = "containerd.io/snapshot/stargz/toc.digest" - -// GetTOCDigest returns the digest of the TOC as recorded in the annotations. -// This function retrieves a digest that represents the content of a -// table of contents (TOC) from the image's annotations. -// This is an experimental feature and may be changed/removed in the future. -func GetTOCDigest(annotations map[string]string) (*digest.Digest, error) { - if contentDigest, ok := annotations[tocJSONDigestAnnotation]; ok { - d, err := digest.Parse(contentDigest) - if err != nil { - return nil, err - } - return &d, nil - } - if contentDigest, ok := annotations[internal.ManifestChecksumKey]; ok { - d, err := digest.Parse(contentDigest) - if err != nil { - return nil, err - } - return &d, nil - } - return nil, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index d84830d3f..f3c7cbd5c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -94,7 +94,7 @@ github.com/containernetworking/cni/pkg/version # github.com/containernetworking/plugins v1.4.0 ## explicit; go 1.20 github.com/containernetworking/plugins/pkg/ns -# github.com/containers/image/v5 v5.29.1-0.20231221164234-1b221d4a9c28 +# github.com/containers/image/v5 v5.29.1 ## explicit; go 1.19 github.com/containers/image/v5/copy github.com/containers/image/v5/directory @@ -200,7 +200,6 @@ github.com/containers/storage/pkg/chunked github.com/containers/storage/pkg/chunked/compressor github.com/containers/storage/pkg/chunked/dump github.com/containers/storage/pkg/chunked/internal -github.com/containers/storage/pkg/chunked/toc github.com/containers/storage/pkg/config github.com/containers/storage/pkg/devicemapper github.com/containers/storage/pkg/directory