@@ -150,22 +150,32 @@ func readEstargzChunkedManifest(blobStream ImageSourceSeekable, blobSize int64,
150150// readZstdChunkedManifest reads the zstd:chunked manifest from the seekable stream blobStream. The blob total size must
151151// be specified.
152152// This function uses the io.github.containers.zstd-chunked. annotations when specified.
153- func readZstdChunkedManifest (ctx context.Context , blobStream ImageSourceSeekable , blobSize int64 , annotations map [string ]string ) ([]byte , int64 , error ) {
153+ func readZstdChunkedManifest (ctx context.Context , blobStream ImageSourceSeekable , blobSize int64 , annotations map [string ]string ) ([]byte , [] byte , int64 , error ) {
154154 footerSize := int64 (internal .FooterSizeSupported )
155155 if blobSize <= footerSize {
156- return nil , 0 , errors .New ("blob too small" )
156+ return nil , nil , 0 , errors .New ("blob too small" )
157157 }
158158
159159 manifestChecksumAnnotation := annotations [internal .ManifestChecksumKey ]
160160 if manifestChecksumAnnotation == "" {
161- return nil , 0 , fmt .Errorf ("manifest checksum annotation %q not found" , internal .ManifestChecksumKey )
161+ return nil , nil , 0 , fmt .Errorf ("manifest checksum annotation %q not found" , internal .ManifestChecksumKey )
162162 }
163163
164164 var offset , length , lengthUncompressed , manifestType uint64
165165
166+ var offsetTarSplit , lengthTarSplit , lengthUncompressedTarSplit uint64
167+ tarSplitChecksumAnnotation := ""
168+
166169 if offsetMetadata := annotations [internal .ManifestInfoKey ]; offsetMetadata != "" {
167170 if _ , err := fmt .Sscanf (offsetMetadata , "%d:%d:%d:%d" , & offset , & length , & lengthUncompressed , & manifestType ); err != nil {
168- return nil , 0 , err
171+ return nil , nil , 0 , err
172+ }
173+
174+ if tarSplitInfoKeyAnnotation , found := annotations [internal .TarSplitInfoKey ]; found {
175+ if _ , err := fmt .Sscanf (tarSplitInfoKeyAnnotation , "%d:%d:%d" , & offsetTarSplit , & lengthTarSplit , & lengthUncompressedTarSplit ); err != nil {
176+ return nil , nil , 0 , err
177+ }
178+ tarSplitChecksumAnnotation = annotations [internal .TarSplitChecksumKey ]
169179 }
170180 } else {
171181 chunk := ImageSourceChunk {
@@ -174,87 +184,129 @@ func readZstdChunkedManifest(ctx context.Context, blobStream ImageSourceSeekable
174184 }
175185 parts , errs , err := blobStream .GetBlobAt ([]ImageSourceChunk {chunk })
176186 if err != nil {
177- return nil , 0 , err
187+ return nil , nil , 0 , err
178188 }
179189 var reader io.ReadCloser
180190 select {
181191 case r := <- parts :
182192 reader = r
183193 case err := <- errs :
184- return nil , 0 , err
194+ return nil , nil , 0 , err
185195 }
186196 footer := make ([]byte , footerSize )
187197 if _ , err := io .ReadFull (reader , footer ); err != nil {
188- return nil , 0 , err
198+ return nil , nil , 0 , err
189199 }
190200
191201 offset = binary .LittleEndian .Uint64 (footer [0 :8 ])
192202 length = binary .LittleEndian .Uint64 (footer [8 :16 ])
193203 lengthUncompressed = binary .LittleEndian .Uint64 (footer [16 :24 ])
194204 manifestType = binary .LittleEndian .Uint64 (footer [24 :32 ])
195- if ! isZstdChunkedFrameMagic (footer [32 : 40 ]) {
196- return nil , 0 , errors .New ("invalid magic number" )
205+ if ! isZstdChunkedFrameMagic (footer [48 : 56 ]) {
206+ return nil , nil , 0 , errors .New ("invalid magic number" )
197207 }
198208 }
199209
200210 if manifestType != internal .ManifestTypeCRFS {
201- return nil , 0 , errors .New ("invalid manifest type" )
211+ return nil , nil , 0 , errors .New ("invalid manifest type" )
202212 }
203213
204214 // set a reasonable limit
205215 if length > (1 << 20 )* 50 {
206- return nil , 0 , errors .New ("manifest too big" )
216+ return nil , nil , 0 , errors .New ("manifest too big" )
207217 }
208218 if lengthUncompressed > (1 << 20 )* 50 {
209- return nil , 0 , errors .New ("manifest too big" )
219+ return nil , nil , 0 , errors .New ("manifest too big" )
210220 }
211221
212222 chunk := ImageSourceChunk {
213223 Offset : offset ,
214224 Length : length ,
215225 }
216226
217- parts , errs , err := blobStream .GetBlobAt ([]ImageSourceChunk {chunk })
227+ chunks := []ImageSourceChunk {chunk }
228+
229+ if offsetTarSplit > 0 {
230+ chunkTarSplit := ImageSourceChunk {
231+ Offset : offsetTarSplit ,
232+ Length : lengthTarSplit ,
233+ }
234+ chunks = append (chunks , chunkTarSplit )
235+ }
236+
237+ parts , errs , err := blobStream .GetBlobAt (chunks )
218238 if err != nil {
219- return nil , 0 , err
239+ return nil , nil , 0 , err
220240 }
221- var reader io.ReadCloser
222- select {
223- case r := <- parts :
224- reader = r
225- case err := <- errs :
226- return nil , 0 , err
241+
242+ readBlob := func (len uint64 ) ([]byte , error ) {
243+ var reader io.ReadCloser
244+ select {
245+ case r := <- parts :
246+ reader = r
247+ case err := <- errs :
248+ return nil , err
249+ }
250+
251+ blob := make ([]byte , len )
252+ if _ , err := io .ReadFull (reader , blob ); err != nil {
253+ reader .Close ()
254+ return nil , err
255+ }
256+ if err := reader .Close (); err != nil {
257+ return nil , err
258+ }
259+ return blob , nil
227260 }
228261
229- manifest := make ([] byte , length )
230- if _ , err := io . ReadFull ( reader , manifest ); err != nil {
231- return nil , 0 , err
262+ manifest , err := readBlob ( length )
263+ if err != nil {
264+ return nil , nil , 0 , err
232265 }
233266
234- manifestDigester := digest .Canonical .Digester ()
235- manifestChecksum := manifestDigester .Hash ()
236- if _ , err := manifestChecksum .Write (manifest ); err != nil {
237- return nil , 0 , err
267+ decodedBlob , err := decodeAndValidateBlob (manifest , lengthUncompressed , manifestChecksumAnnotation )
268+ if err != nil {
269+ return nil , nil , 0 , err
270+ }
271+ decodedTarSplit := []byte {}
272+ if offsetTarSplit > 0 {
273+ tarSplit , err := readBlob (lengthTarSplit )
274+ if err != nil {
275+ return nil , nil , 0 , err
276+ }
277+
278+ decodedTarSplit , err = decodeAndValidateBlob (tarSplit , lengthUncompressedTarSplit , tarSplitChecksumAnnotation )
279+ if err != nil {
280+ return nil , nil , 0 , err
281+ }
238282 }
283+ return decodedBlob , decodedTarSplit , int64 (offset ), err
284+ }
239285
240- d , err := digest .Parse (manifestChecksumAnnotation )
286+ func decodeAndValidateBlob (blob []byte , lengthUncompressed uint64 , expectedUncompressedChecksum string ) ([]byte , error ) {
287+ d , err := digest .Parse (expectedUncompressedChecksum )
241288 if err != nil {
242- return nil , 0 , err
289+ return nil , err
243290 }
244- if manifestDigester .Digest () != d {
245- return nil , 0 , errors .New ("invalid manifest checksum" )
291+
292+ blobDigester := d .Algorithm ().Digester ()
293+ blobChecksum := blobDigester .Hash ()
294+ if _ , err := blobChecksum .Write (blob ); err != nil {
295+ return nil , err
296+ }
297+ if blobDigester .Digest () != d {
298+ return nil , fmt .Errorf ("invalid blob checksum, expected checksum %s, got %s" , d , blobDigester .Digest ())
246299 }
247300
248301 decoder , err := zstd .NewReader (nil ) //nolint:contextcheck
249302 if err != nil {
250- return nil , 0 , err
303+ return nil , err
251304 }
252305 defer decoder .Close ()
253306
254307 b := make ([]byte , 0 , lengthUncompressed )
255- if decoded , err := decoder .DecodeAll (manifest , b ); err == nil {
256- return decoded , int64 ( offset ), nil
308+ if decoded , err := decoder .DecodeAll (blob , b ); err == nil {
309+ return decoded , nil
257310 }
258-
259- return manifest , int64 (offset ), nil
311+ return blob , nil
260312}
0 commit comments