6
6
"errors"
7
7
"fmt"
8
8
"io"
9
+ "io/fs"
9
10
"os"
10
11
"path"
11
12
"path/filepath"
@@ -296,11 +297,17 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st
296
297
return wrapTerminal (fmt .Errorf ("catalog image is missing the required label %q" , ConfigDirLabel ), specIsCanonical )
297
298
}
298
299
299
- if err := os .MkdirAll (unpackPath , 0700 ); err != nil {
300
- return fmt .Errorf ("error creating unpack directory: %w" , err )
301
- }
302
300
l := log .FromContext (ctx )
303
- l .Info ("unpacking image" , "path" , unpackPath )
301
+ tempUnpackPath , err := os .MkdirTemp ("" , "unpack-*" )
302
+ if err != nil {
303
+ return fmt .Errorf ("error creating temporary unpack directory: %w" , err )
304
+ }
305
+ defer func () {
306
+ if err := os .RemoveAll (tempUnpackPath ); err != nil {
307
+ l .Error (err , "error removing temporary unpack directory" )
308
+ }
309
+ }()
310
+ l .Info ("unpacking image" , "path" , unpackPath , "temp path" , tempUnpackPath )
304
311
for i , layerInfo := range img .LayerInfos () {
305
312
if err := func () error {
306
313
layerReader , _ , err := layoutSrc .GetBlob (ctx , layerInfo , none .NoCache )
@@ -309,21 +316,66 @@ func (i *ContainersImageRegistry) unpackImage(ctx context.Context, unpackPath st
309
316
}
310
317
defer layerReader .Close ()
311
318
312
- if err := applyLayer (ctx , unpackPath , dirToUnpack , layerReader ); err != nil {
319
+ if err := applyLayer (ctx , tempUnpackPath , dirToUnpack , layerReader ); err != nil {
313
320
return fmt .Errorf ("error applying layer[%d]: %w" , i , err )
314
321
}
315
322
l .Info ("applied layer" , "layer" , i )
316
323
return nil
317
324
}(); err != nil {
318
- return errors .Join (err , deleteRecursive (unpackPath ))
325
+ return errors .Join (err , deleteRecursive (tempUnpackPath ))
319
326
}
320
327
}
328
+
329
+ // ensure unpack path is empty
330
+ if err := os .RemoveAll (unpackPath ); err != nil {
331
+ return fmt .Errorf ("error removing unpack path: %w" , err )
332
+ }
333
+ if err := copyRecursively (tempUnpackPath , unpackPath , 0700 ); err != nil {
334
+ return fmt .Errorf ("error moving temporary unpack directory to final unpack directory: %w" , err )
335
+ }
321
336
if err := setReadOnlyRecursive (unpackPath ); err != nil {
322
337
return fmt .Errorf ("error making unpack directory read-only: %w" , err )
323
338
}
324
339
return nil
325
340
}
326
341
342
+ func copyRecursively (srcPath string , destPath string , perm os.FileMode ) error {
343
+ // ensure destination path not exist
344
+ if stat , err := os .Stat (destPath ); stat != nil && ! os .IsNotExist (err ) {
345
+ return fmt .Errorf ("destination path %q already exists: %w" , destPath , err )
346
+ }
347
+ return filepath .WalkDir (srcPath , func (path string , d fs.DirEntry , err error ) error {
348
+ if err != nil {
349
+ return err
350
+ }
351
+ relPath , err := filepath .Rel (srcPath , path )
352
+ if err != nil {
353
+ return err
354
+ }
355
+ if d .IsDir () {
356
+ return os .MkdirAll (filepath .Join (destPath , relPath ), perm )
357
+ }
358
+ return copyFile (path , filepath .Join (destPath , relPath ))
359
+ })
360
+ }
361
+
362
+ func copyFile (src , dest string ) error {
363
+ in , err := os .Open (src )
364
+ if err != nil {
365
+ return err
366
+ }
367
+ defer in .Close ()
368
+
369
+ out , err := os .Create (dest )
370
+ if err != nil {
371
+ return err
372
+ }
373
+ defer out .Close ()
374
+
375
+ _ , err = io .Copy (out , in )
376
+ return err
377
+ }
378
+
327
379
func applyLayer (ctx context.Context , destPath string , srcPath string , layer io.ReadCloser ) error {
328
380
decompressed , _ , err := compression .AutoDecompress (layer )
329
381
if err != nil {
0 commit comments