diff --git a/go.mod b/go.mod index e6f756cac..29d5b8b69 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/opencontainers/umoci -go 1.18 +go 1.22 require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 @@ -20,7 +20,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.12 github.com/vbatts/go-mtree v0.5.3 - golang.org/x/sys v0.13.0 + golang.org/x/sys v0.32.0 google.golang.org/protobuf v1.31.0 ) @@ -28,6 +28,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect @@ -35,7 +36,10 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect golang.org/x/crypto v0.8.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/opencontainers/go-digest => github.com/project-zot/go-digest v0.0.0-20250501003621-612e7142c60b diff --git a/go.sum b/go.sum index 5f4278b7a..98879a58a 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -61,8 +63,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v1.1.8 h1:zICRlc+C1XzivLc3nzE+cbJV4LIi8tib6YG0MqC6OqA= @@ -74,6 +74,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/project-zot/go-digest v0.0.0-20250501003621-612e7142c60b h1:+hNMoRN8I3CvY9T/HJbm4eguU1l4rWRlL+U9n3GAsqE= +github.com/project-zot/go-digest v0.0.0-20250501003621-612e7142c60b/go.mod h1:fD+VSSgkVpG1shrWHaX1XcWJmU0gfR+5qLbw0/UvE3k= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rootless-containers/proto/go-proto v0.0.0-20230421021042-4cd87ebadd67 h1:58jvc5cZ+hGKidQ4Z37/+rj9eQxRRjOOsqNEwPSZXR4= github.com/rootless-containers/proto/go-proto v0.0.0-20230421021042-4cd87ebadd67/go.mod h1:LLjEAc6zmycfeN7/1fxIphWQPjHpTt7ElqT7eVf8e4A= @@ -107,6 +109,12 @@ github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w= github.com/vbatts/go-mtree v0.5.3/go.mod h1:eXsdoPMdL2jcJx6HweWi9lYQxBsTp4lNhqqAjgkZUg8= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= @@ -124,8 +132,8 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/mutate/mutate.go b/mutate/mutate.go index f0c8f5f05..ace423c0b 100644 --- a/mutate/mutate.go +++ b/mutate/mutate.go @@ -66,6 +66,8 @@ type Mutator struct { // Cached values of the configuration and manifest. manifest *ispec.Manifest config *ispec.Image + + alg digest.Algorithm } // Meta is a wrapper around the "safe" fields in ispec.Image, which can be @@ -142,9 +144,15 @@ func New(engine cas.Engine, src casext.DescriptorPath) (*Mutator, error) { return &Mutator{ engine: casext.NewEngine(engine), source: src, + alg: digest.SHA256, }, nil } +func (m *Mutator) WithAlgorithm(alg digest.Algorithm) *Mutator { + m.alg = alg + return m +} + // Config returns the current (cached) image configuration, which should be // used as the source for any modifications of the configuration using // Set. @@ -257,7 +265,7 @@ func (m *Mutator) add(ctx context.Context, reader io.Reader, history *ispec.Hist return "", -1, errors.Wrap(err, "getting cache failed") } - diffidDigester := cas.BlobAlgorithm.Digester() + diffidDigester := m.alg.Digester() hashReader := io.TeeReader(reader, diffidDigester.Hash()) compressed, err := compressor.Compress(hashReader) @@ -266,7 +274,7 @@ func (m *Mutator) add(ctx context.Context, reader io.Reader, history *ispec.Hist } defer compressed.Close() - layerDigest, layerSize, err := m.engine.PutBlob(ctx, compressed) + layerDigest, layerSize, err := m.engine.PutBlob(ctx, compressed, m.alg) if err != nil { return "", -1, errors.Wrap(err, "put layer blob") } @@ -339,7 +347,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { } // We first have to commit the configuration blob. - configDigest, configSize, err := m.engine.PutBlobJSON(ctx, m.config) + configDigest, configSize, err := m.engine.PutBlobJSON(ctx, m.config, m.alg) if err != nil { return casext.DescriptorPath{}, errors.Wrap(err, "commit mutated config blob") } @@ -351,7 +359,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { } // Now commit the manifest. - manifestDigest, manifestSize, err := m.engine.PutBlobJSON(ctx, m.manifest) + manifestDigest, manifestSize, err := m.engine.PutBlobJSON(ctx, m.manifest, m.alg) if err != nil { return casext.DescriptorPath{}, errors.Wrap(err, "commit mutated manifest blob") } @@ -395,7 +403,7 @@ func (m *Mutator) Commit(ctx context.Context) (casext.DescriptorPath, error) { // Re-commit the blob. // TODO: This won't handle foreign blobs correctly, we need to make it // possible to write a modified blob through the blob API. - blobDigest, blobSize, err := m.engine.PutBlobJSON(ctx, parentBlob.Data) + blobDigest, blobSize, err := m.engine.PutBlobJSON(ctx, parentBlob.Data, m.alg) if err != nil { return casext.DescriptorPath{}, errors.Wrapf(err, "put json parent-%d blob", idx) } diff --git a/new.go b/new.go index d9b10a2d7..67e740166 100644 --- a/new.go +++ b/new.go @@ -23,6 +23,7 @@ import ( "time" "github.com/apex/log" + "github.com/opencontainers/go-digest" imeta "github.com/opencontainers/image-spec/specs-go" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/casext" @@ -53,7 +54,7 @@ func NewImage(engineExt casext.Engine, tagName string) error { // Update config and create a new blob for it. config := g.Image() - configDigest, configSize, err := engineExt.PutBlobJSON(context.Background(), config) + configDigest, configSize, err := engineExt.PutBlobJSON(context.Background(), config, digest.Blake3) if err != nil { return errors.Wrap(err, "put config blob") } @@ -78,7 +79,7 @@ func NewImage(engineExt casext.Engine, tagName string) error { Layers: []ispec.Descriptor{}, } - manifestDigest, manifestSize, err := engineExt.PutBlobJSON(context.Background(), manifest) + manifestDigest, manifestSize, err := engineExt.PutBlobJSON(context.Background(), manifest, digest.Blake3) if err != nil { return errors.Wrap(err, "put manifest blob") } diff --git a/oci/cas/cas.go b/oci/cas/cas.go index 5fa51daf2..9cbe21059 100644 --- a/oci/cas/cas.go +++ b/oci/cas/cas.go @@ -66,7 +66,7 @@ type Engine interface { // PutBlob adds a new blob to the image. This is idempotent; a nil error // means that "the content is stored at DIGEST" without implying "because // of this PutBlob() call". - PutBlob(ctx context.Context, reader io.Reader) (digest digest.Digest, size int64, err error) + PutBlob(ctx context.Context, reader io.Reader, alg digest.Algorithm) (digest digest.Digest, size int64, err error) // GetBlob returns a reader for retrieving a blob from the image, which the // caller must Close(). Returns ErrNotExist if the digest is not found. diff --git a/oci/cas/dir/dir.go b/oci/cas/dir/dir.go index 3fc7f87ea..0051dedf1 100644 --- a/oci/cas/dir/dir.go +++ b/oci/cas/dir/dir.go @@ -27,6 +27,7 @@ import ( "github.com/apex/log" "github.com/opencontainers/go-digest" + godigest "github.com/opencontainers/go-digest" imeta "github.com/opencontainers/image-spec/specs-go" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/umoci/oci/cas" @@ -65,7 +66,7 @@ func blobPath(digest digest.Digest) (string, error) { algo := digest.Algorithm() hash := digest.Hex() - if algo != cas.BlobAlgorithm { + if algo != cas.BlobAlgorithm && algo != godigest.Blake3 { return "", errors.Errorf("unsupported algorithm: %q", algo) } @@ -76,6 +77,7 @@ type dirEngine struct { path string temp string tempFile *os.File + alg digest.Algorithm } func (e *dirEngine) ensureTempDir() error { @@ -151,12 +153,12 @@ func (e *dirEngine) validate() error { // PutBlob adds a new blob to the image. This is idempotent; a nil error // means that "the content is stored at DIGEST" without implying "because // of this PutBlob() call". -func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader) (digest.Digest, int64, error) { +func (e *dirEngine) PutBlob(ctx context.Context, reader io.Reader, alg digest.Algorithm) (digest.Digest, int64, error) { if err := e.ensureTempDir(); err != nil { return "", -1, errors.Wrap(err, "ensure tempdir") } - digester := cas.BlobAlgorithm.Digester() + digester := alg.Digester() // We copy this into a temporary file because we need to get the blob hash, // but also to avoid half-writing an invalid blob. @@ -317,7 +319,7 @@ func (e *dirEngine) DeleteBlob(ctx context.Context, digest digest.Digest) error // ListBlobs returns the set of blob digests stored in the image. func (e *dirEngine) ListBlobs(ctx context.Context) ([]digest.Digest, error) { digests := []digest.Digest{} - blobDir := filepath.Join(e.path, blobDirectory, cas.BlobAlgorithm.String()) + blobDir := filepath.Join(e.path, blobDirectory, e.alg.String()) if err := filepath.Walk(blobDir, func(path string, _ os.FileInfo, _ error) error { // Skip the actual directory. @@ -326,7 +328,7 @@ func (e *dirEngine) ListBlobs(ctx context.Context) ([]digest.Digest, error) { } // XXX: Do we need to handle multiple-directory-deep cases? - digest := digest.NewDigestFromHex(cas.BlobAlgorithm.String(), filepath.Base(path)) + digest := digest.NewDigestFromHex(e.alg.String(), filepath.Base(path)) digests = append(digests, digest) return nil }); err != nil { @@ -403,6 +405,7 @@ func Open(path string) (cas.Engine, error) { engine := &dirEngine{ path: path, temp: "", + alg: digest.Blake3, } if err := engine.validate(); err != nil { @@ -432,7 +435,7 @@ func Create(path string) error { if err := os.Mkdir(filepath.Join(path, blobDirectory), 0755); err != nil { return errors.Wrap(err, "mkdir blobdir") } - if err := os.Mkdir(filepath.Join(path, blobDirectory, cas.BlobAlgorithm.String()), 0755); err != nil { + if err := os.Mkdir(filepath.Join(path, blobDirectory, "blake3"), 0755); err != nil { return errors.Wrap(err, "mkdir algorithm") } diff --git a/oci/casext/json.go b/oci/casext/json.go index e7f562f11..9acbb728a 100644 --- a/oci/casext/json.go +++ b/oci/casext/json.go @@ -37,10 +37,10 @@ import ( // consistent output. Go's JSON library doesn't even attempt to sort // map[...]... objects (which have their iteration order randomised in // Go). -func (e Engine) PutBlobJSON(ctx context.Context, data interface{}) (digest.Digest, int64, error) { +func (e Engine) PutBlobJSON(ctx context.Context, data interface{}, alg digest.Algorithm) (digest.Digest, int64, error) { var buffer bytes.Buffer if err := json.NewEncoder(&buffer).Encode(data); err != nil { return "", -1, errors.Wrap(err, "encode JSON") } - return e.PutBlob(ctx, &buffer) + return e.PutBlob(ctx, &buffer, alg) } diff --git a/oci/layer/unpack.go b/oci/layer/unpack.go index 323870371..422ff3c11 100644 --- a/oci/layer/unpack.go +++ b/oci/layer/unpack.go @@ -261,7 +261,7 @@ func UnpackRootfs(ctx context.Context, engine cas.Engine, rootfsPath string, man } } - layerDigester := digest.SHA256.Digester() + layerDigester := digest.Blake3.Digester() layer := io.TeeReader(layerRaw, layerDigester.Hash()) if err := UnpackLayer(rootfsPath, layer, opt); err != nil {