1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ocimem
16
17 import (
18 "context"
19 "fmt"
20 "io"
21
22 "cuelabs.dev/go/oci/ociregistry"
23 "github.com/opencontainers/go-digest"
24 )
25
26
27
28 func (r *Registry) PushBlob(ctx context.Context, repoName string, desc ociregistry.Descriptor, content io.Reader) (ociregistry.Descriptor, error) {
29 data, err := io.ReadAll(content)
30 if err != nil {
31 return ociregistry.Descriptor{}, fmt.Errorf("cannot read content: %v", err)
32 }
33 if err := CheckDescriptor(desc, data); err != nil {
34 return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err)
35 }
36
37 r.mu.Lock()
38 defer r.mu.Unlock()
39 repo, err := r.makeRepo(repoName)
40 if err != nil {
41 return ociregistry.Descriptor{}, err
42 }
43 repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, data: data}
44 return desc, nil
45 }
46
47 func (r *Registry) PushBlobChunked(ctx context.Context, repoName string, chunkSize int) (ociregistry.BlobWriter, error) {
48
49
50
51
52
53
54 return r.PushBlobChunkedResume(ctx, repoName, "", 0, chunkSize)
55 }
56
57 func (r *Registry) PushBlobChunkedResume(ctx context.Context, repoName, id string, offset int64, chunkSize int) (ociregistry.BlobWriter, error) {
58 r.mu.Lock()
59 defer r.mu.Unlock()
60 repo, err := r.makeRepo(repoName)
61 if err != nil {
62 return nil, err
63 }
64 b := repo.uploads[id]
65 if b == nil {
66 b = NewBuffer(func(b *Buffer) error {
67 r.mu.Lock()
68 defer r.mu.Unlock()
69 desc, data, _ := b.GetBlob()
70 repo.blobs[desc.Digest] = &blob{mediaType: desc.MediaType, data: data}
71 return nil
72 }, id)
73 repo.uploads[b.ID()] = b
74 }
75 b.checkStartOffset = offset
76 return b, nil
77 }
78
79 func (r *Registry) MountBlob(ctx context.Context, fromRepo, toRepo string, dig ociregistry.Digest) (ociregistry.Descriptor, error) {
80 r.mu.Lock()
81 defer r.mu.Unlock()
82 rto, err := r.makeRepo(toRepo)
83 if err != nil {
84 return ociregistry.Descriptor{}, err
85 }
86 b, err := r.blobForDigest(fromRepo, dig)
87 if err != nil {
88 return ociregistry.Descriptor{}, err
89 }
90 rto.blobs[dig] = b
91 return b.descriptor(), nil
92 }
93
94 var errCannotOverwriteTag = fmt.Errorf("%w: cannot overwrite tag", ociregistry.ErrDenied)
95
96 func (r *Registry) PushManifest(ctx context.Context, repoName string, tag string, data []byte, mediaType string) (ociregistry.Descriptor, error) {
97 r.mu.Lock()
98 defer r.mu.Unlock()
99 repo, err := r.makeRepo(repoName)
100 if err != nil {
101 return ociregistry.Descriptor{}, err
102 }
103 dig := digest.FromBytes(data)
104 desc := ociregistry.Descriptor{
105 Digest: dig,
106 MediaType: mediaType,
107 Size: int64(len(data)),
108 }
109 if tag != "" {
110 if !ociregistry.IsValidTag(tag) {
111 return ociregistry.Descriptor{}, fmt.Errorf("invalid tag")
112 }
113 if r.cfg.ImmutableTags {
114 if currDesc, ok := repo.tags[tag]; ok {
115 if dig == currDesc.Digest {
116 if currDesc.MediaType != mediaType {
117
118 return ociregistry.Descriptor{}, fmt.Errorf("%w: mismatched media type", ociregistry.ErrDenied)
119 }
120
121 return currDesc, nil
122 }
123 return ociregistry.Descriptor{}, errCannotOverwriteTag
124 }
125 }
126 }
127
128 data = append([]byte(nil), data...)
129 if err := CheckDescriptor(desc, data); err != nil {
130 return ociregistry.Descriptor{}, fmt.Errorf("invalid descriptor: %v", err)
131 }
132 subject, err := r.checkManifest(repoName, desc.MediaType, data)
133 if err != nil {
134 return ociregistry.Descriptor{}, fmt.Errorf("invalid manifest: %v", err)
135 }
136
137 repo.manifests[dig] = &blob{
138 mediaType: mediaType,
139 data: data,
140 subject: subject,
141 }
142 if tag != "" {
143 repo.tags[tag] = desc
144 }
145 return desc, nil
146 }
147
148 func (r *Registry) checkManifest(repoName string, mediaType string, data []byte) (subject ociregistry.Digest, retErr error) {
149 repo, err := r.repo(repoName)
150 if err != nil {
151 return "", err
152 }
153 iter, err := manifestReferences(mediaType, data)
154 if err != nil {
155
156 return "", err
157 }
158 iter(func(info descInfo) bool {
159 if err := CheckDescriptor(info.desc, nil); err != nil {
160 retErr = fmt.Errorf("bad descriptor in %s: %v", info.name, err)
161 return false
162 }
163 switch info.kind {
164 case kindBlob:
165 if repo.blobs[info.desc.Digest] == nil {
166 retErr = fmt.Errorf("blob for %s not found", info.name)
167 return false
168 }
169 case kindManifest:
170 if repo.manifests[info.desc.Digest] == nil {
171 retErr = fmt.Errorf("manifest for %s not found", info.name)
172 return false
173 }
174 case kindSubjectManifest:
175 subject = info.desc.Digest
176
177
178 }
179 return true
180 })
181 return subject, retErr
182 }
183
184
185
186
187
188 func refersTo(repo *repository, iter descIter, digest ociregistry.Digest) (found bool, retErr error) {
189 iter(func(info descInfo) bool {
190 if info.desc.Digest == digest {
191 found = true
192 return false
193 }
194 switch info.kind {
195 case kindManifest, kindSubjectManifest:
196 b := repo.manifests[info.desc.Digest]
197 if b == nil {
198 break
199 }
200 miter, err := manifestReferences(info.desc.MediaType, b.data)
201 if err != nil {
202 retErr = err
203 return false
204 }
205 found, retErr = refersTo(repo, miter, digest)
206 if found || retErr != nil {
207 return false
208 }
209 }
210 return true
211 })
212 return found, retErr
213 }
214
View as plain text