Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,22 +20,26 @@ 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
)

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
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
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
16 changes: 12 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand All @@ -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=
Expand Down Expand Up @@ -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=
Expand All @@ -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=
Expand Down
18 changes: 13 additions & 5 deletions mutate/mutate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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")
}
Expand Down Expand Up @@ -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")
}
Expand All @@ -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")
}
Expand Down Expand Up @@ -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)
}
Expand Down
5 changes: 3 additions & 2 deletions new.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")
}
Expand All @@ -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")
}
Expand Down
2 changes: 1 addition & 1 deletion oci/cas/cas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 9 additions & 6 deletions oci/cas/dir/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}

Expand All @@ -76,6 +77,7 @@ type dirEngine struct {
path string
temp string
tempFile *os.File
alg digest.Algorithm
}

func (e *dirEngine) ensureTempDir() error {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
}

Expand Down
4 changes: 2 additions & 2 deletions oci/casext/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
2 changes: 1 addition & 1 deletion oci/layer/unpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down