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
1 change: 1 addition & 0 deletions ociregistry/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type Reader interface {
// starting at offset0, up to but not including offset1.
// If offset1 is negative or exceeds the actual size of the blob, GetBlobRange will
// return all the data starting from offset0.
// If offset0 is negative, offset1 is the number of bytes to return from the end of the blob. (TODO https://github.com/cue-labs/oci/issues/47)
// The context also controls the lifetime of the returned BlobReader.
GetBlobRange(ctx context.Context, repo string, digest Digest, offset0, offset1 int64) (BlobReader, error)

Expand Down
5 changes: 4 additions & 1 deletion ociregistry/internal/ocirequest/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,10 @@ func Parse(method string, u *url.URL) (*Request, error) {
case ociref.IsValidTag(last):
rreq.Tag = last
default:
return nil, errNotFound
if strings.Contains(last, ":") {
return nil, errBadlyFormedDigest
}
return nil, errNotFound // TODO this probably shouldn't be 404 (more like 500 or just always errBadlyFormedDigest or maybe even "badly formed tag")
}
switch method {
case "GET":
Expand Down
6 changes: 2 additions & 4 deletions ociregistry/ocimem/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"fmt"
"sync"

"github.com/opencontainers/go-digest"

"cuelabs.dev/go/oci/ociregistry"
)

Expand Down Expand Up @@ -178,8 +176,8 @@ func (b *Buffer) checkCommit(dig ociregistry.Digest) (err error) {
b.commitErr = err
}
}()
if digest.FromBytes(b.buf) != dig {
return fmt.Errorf("digest mismatch (sha256(%q) != %s): %w", b.buf, dig, ociregistry.ErrDigestInvalid)
if err := CheckDescriptor(ociregistry.Descriptor{Digest: dig, Size: int64(len(b.buf)), MediaType: "application/octet-stream"}, b.buf); err != nil { // TODO is it sane to abuse CheckDescriptor as "validate digest algorithm + validate digest" like this? it feels good to have that logic centralized
return err
}
b.desc = ociregistry.Descriptor{
MediaType: "application/octet-stream",
Expand Down
4 changes: 3 additions & 1 deletion ociregistry/ocimem/lister.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ func (r *Registry) Referrers(_ context.Context, repoName string, digest ociregis
r.mu.Lock()
defer r.mu.Unlock()
repo, err := r.repo(repoName)
if err != nil {
if err == ociregistry.ErrNameUnknown {
return ociregistry.SliceSeq([]ociregistry.Descriptor{}) // "Assuming a repository is found, this request MUST return a 200 OK response code. If the registry supports the referrers API, the registry MUST NOT return a 404 Not Found to a referrers API requests."
} else if err != nil {
return ociregistry.ErrorSeq[ociregistry.Descriptor](err)
}
var referrers []ociregistry.Descriptor
Expand Down
6 changes: 5 additions & 1 deletion ociregistry/ocimem/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ func (r *Registry) GetBlobRange(ctx context.Context, repoName string, dig ocireg
if o1 < 0 || o1 > int64(len(b.data)) {
o1 = int64(len(b.data))
}
if o0 < 0 { // TODO https://github.com/cue-labs/oci/issues/47
o0 = max(int64(len(b.data))-o1, 0)
o1 = int64(len(b.data))
}
if o0 < 0 || o0 > o1 {
return nil, fmt.Errorf("invalid range [%d, %d]; have [%d, %d]", o0, o1, 0, len(b.data))
return nil, fmt.Errorf("%w: [%d, %d]; have [%d, %d]", ociregistry.ErrRangeInvalid, o0, o1, 0, len(b.data))
}
return NewBytesReader(b.data[o0:o1], b.descriptor()), nil
}
Expand Down
15 changes: 6 additions & 9 deletions ociregistry/ocimem/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type repository struct {

type blob struct {
mediaType string
digest ociregistry.Digest
data []byte
info manifestInfo
}
Expand All @@ -51,7 +52,7 @@ func (b *blob) descriptor() ociregistry.Descriptor {
return ociregistry.Descriptor{
MediaType: b.mediaType,
Size: int64(len(b.data)),
Digest: digest.FromBytes(b.data),
Digest: b.digest,
ArtifactType: b.info.artifactType,
Annotations: b.info.annotations,
}
Expand Down Expand Up @@ -158,16 +159,12 @@ func CheckDescriptor(desc ociregistry.Descriptor, data []byte) error {
if err := desc.Digest.Validate(); err != nil {
return fmt.Errorf("invalid digest: %v", err)
}
if data != nil {
if digest.FromBytes(data) != desc.Digest {
return fmt.Errorf("digest mismatch")
}
if data != nil || desc.Size == 0 {
if desc.Size != int64(len(data)) {
return fmt.Errorf("size mismatch")
return ociregistry.ErrSizeInvalid // TODO include both sizes in the error?
}
} else {
if desc.Size == 0 && desc.Digest != emptyHash {
return fmt.Errorf("zero sized content with mismatching digest")
if desc.Digest.Algorithm().FromBytes(data) != desc.Digest {
return ociregistry.ErrDigestInvalid // TODO include both digests in the error?
}
}
if desc.MediaType == "" {
Expand Down
11 changes: 6 additions & 5 deletions ociregistry/ocimem/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (r *Registry) PushBlob(ctx context.Context, repoName string, desc ociregist
return ociregistry.Descriptor{}, fmt.Errorf("cannot read content: %v", err)
}
if err := CheckDescriptor(desc, data); err != nil {
return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err)
return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %w", err)
}

r.mu.Lock()
Expand All @@ -42,7 +42,7 @@ func (r *Registry) PushBlob(ctx context.Context, repoName string, desc ociregist
if err != nil {
return ociregistry.Descriptor{}, err
}
repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, data: data}
repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, digest: desc.Digest, data: data}
return desc, nil
}

Expand All @@ -69,7 +69,7 @@ func (r *Registry) PushBlobChunkedResume(ctx context.Context, repoName, id strin
r.mu.Lock()
defer r.mu.Unlock()
desc, data, _ := b.GetBlob()
repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, data: data}
repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, digest: desc.Digest, data: data}
return nil
}, id)
repo.uploads[b.ID()] = b
Expand Down Expand Up @@ -129,7 +129,7 @@ func (r *Registry) PushManifest(ctx context.Context, repoName string, tag string
// make a copy of the data to avoid potential corruption.
data = slices.Clone(data)
if err := CheckDescriptor(desc, data); err != nil {
return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err)
return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %w", err)
}
info, err := r.checkManifestReferences(repoName, desc.MediaType, data)
if err != nil {
Expand All @@ -138,6 +138,7 @@ func (r *Registry) PushManifest(ctx context.Context, repoName string, tag string

repo.manifests[dig] = &blob{
mediaType: mediaType,
digest: dig,
data: data,
info: info,
}
Expand Down Expand Up @@ -166,7 +167,7 @@ func (r *Registry) checkManifestReferences(repoName string, mediaType string, da
}
switch info.kind {
case kindBlob:
if repo.blobs[info.desc.Digest] == nil {
if repo.blobs[info.desc.Digest] == nil && len(info.desc.URLs) == 0 { // TODO should the "urls" half of this check be optional?
return manifestInfo{}, fmt.Errorf("blob for %s not found", info.name)
}
case kindManifest:
Expand Down
9 changes: 8 additions & 1 deletion ociregistry/ociserver/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

// httpRange specifies a byte range as requested by a client.
// If end is negative, it represents the end of the file.
// If start is negative, end represents the number of bytes to return from the end of the file. (TODO https://github.com/cue-labs/oci/issues/47)
type httpRange struct {
start, end int64
}
Expand Down Expand Up @@ -63,7 +64,13 @@ func parseRange(s string) ([]httpRange, error) {
if end == "" || end[0] == '-' {
return nil, errors.New("invalid range")
}
return nil, errors.New("end-relative range not supported")
// TODO https://github.com/cue-labs/oci/issues/47
r.start = -1
i, err := strconv.ParseInt(end, 10, 64)
if err != nil {
return nil, errors.New("invalid range")
}
r.end = i
} else {
i, err := strconv.ParseInt(start, 10, 64)
if err != nil || i < 0 {
Expand Down
4 changes: 4 additions & 0 deletions ociregistry/ociserver/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ func (r *registry) handleBlobGet(ctx context.Context, resp http.ResponseWriter,
if rng.end == -1 || rng.end > desc.Size {
rng.end = desc.Size
}
if rng.start == -1 { // TODO https://github.com/cue-labs/oci/issues/47
rng.start = desc.Size - rng.end
rng.end = desc.Size
}
if rng.start > desc.Size {
return withHTTPCode(http.StatusRequestedRangeNotSatisfiable, fmt.Errorf("range starts after end of blob"))
}
Expand Down
4 changes: 2 additions & 2 deletions ociregistry/ociserver/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (r *registry) handleBlobCompleteUpload(ctx context.Context, resp http.Respo
defer w.Close()

if _, err := io.Copy(w, req.Body); err != nil {
return fmt.Errorf("failed to copy data to %T: %v", w, err)
return fmt.Errorf("failed to copy data to %T: %w", w, err)
}
desc, err := w.Commit(ociregistry.Digest(rreq.Digest))
if err != nil {
Expand All @@ -154,7 +154,7 @@ func (r *registry) handleBlobCompleteUpload(ctx context.Context, resp http.Respo
func (r *registry) handleBlobMount(ctx context.Context, resp http.ResponseWriter, req *http.Request, rreq *ocirequest.Request) error {
desc, err := r.backend.MountBlob(ctx, rreq.FromRepo, rreq.Repo, ociregistry.Digest(rreq.Digest))
if err != nil {
return err
return r.handleBlobStartUpload(ctx, resp, req, rreq) // TODO debug log? only do this on 404?
}
if err := r.setLocationHeader(resp, true, desc, "/v2/"+rreq.Repo+"/blobs/"+rreq.Digest); err != nil {
return err
Expand Down