@@ -528,38 +528,56 @@ func canonicalTarName(name string, isDir bool) (string, error) {
528
528
return name , nil
529
529
}
530
530
531
- // addFile adds a file from `path` as `name` to the tar archive.
532
- func (ta * tarWriter ) addFile (path , name string ) error {
531
+ type addFileData struct {
532
+ // The path from which to read contents.
533
+ path string
534
+
535
+ // os.Stat for the above.
536
+ fi os.FileInfo
537
+
538
+ // The file header of the above.
539
+ hdr * tar.Header
540
+
541
+ // if present, an extra whiteout entry to write after the header.
542
+ extraWhiteout * tar.Header
543
+ }
544
+
545
+ // prepareAddFile generates the tar file header(s) for adding a file
546
+ // from path as name to the tar archive, without writing to the
547
+ // tar stream. Thus, any error may be ignored without corrupting the
548
+ // tar file. A (nil, nil) return means that the file should be
549
+ // ignored for non-error reasons.
550
+ func (ta * tarWriter ) prepareAddFile (path , name string ) (* addFileData , error ) {
533
551
fi , err := os .Lstat (path )
534
552
if err != nil {
535
- return err
553
+ return nil , err
536
554
}
537
555
538
556
var link string
539
557
if fi .Mode ()& os .ModeSymlink != 0 {
540
558
var err error
541
559
link , err = os .Readlink (path )
542
560
if err != nil {
543
- return err
561
+ return nil , err
544
562
}
545
563
}
546
564
if fi .Mode ()& os .ModeSocket != 0 {
547
565
logrus .Infof ("archive: skipping %q since it is a socket" , path )
548
- return nil
566
+ return nil , nil
549
567
}
550
568
551
569
hdr , err := FileInfoHeader (name , fi , link )
552
570
if err != nil {
553
- return err
571
+ return nil , err
554
572
}
555
573
if err := readSecurityXattrToTarHeader (path , hdr ); err != nil {
556
- return err
574
+ return nil , err
557
575
}
558
576
if err := readUserXattrToTarHeader (path , hdr ); err != nil {
559
- return err
577
+ return nil , err
560
578
}
561
579
if err := ReadFileFlagsToTarHeader (path , hdr ); err != nil {
562
- return err
580
+ return nil , err
563
581
}
564
582
if ta .CopyPass {
565
583
copyPassHeader (hdr )
@@ -584,11 +602,11 @@ func (ta *tarWriter) addFile(path, name string) error {
584
602
if ! strings .HasPrefix (filepath .Base (hdr .Name ), WhiteoutPrefix ) && ! ta .IDMappings .Empty () {
585
603
fileIDPair , err := getFileUIDGID (fi .Sys ())
586
604
if err != nil {
587
- return err
605
+ return nil , err
588
606
}
589
607
hdr .Uid , hdr .Gid , err = ta .IDMappings .ToContainer (fileIDPair )
590
608
if err != nil {
591
- return err
609
+ return nil , err
592
610
}
593
611
}
594
612
@@ -611,6 +629,11 @@ func (ta *tarWriter) addFile(path, name string) error {
611
629
612
630
maybeTruncateHeaderModTime (hdr )
613
631
632
+ result := & addFileData {
633
+ path : path ,
634
+ hdr : hdr ,
635
+ fi : fi ,
636
+ }
614
637
if ta .WhiteoutConverter != nil {
615
638
// The WhiteoutConverter suggests a generic mechanism,
616
639
// but this code is only used to convert between
@@ -621,36 +644,41 @@ func (ta *tarWriter) addFile(path, name string) error {
621
644
//
622
645
// For AUFS, a directory with all its contents deleted
623
646
// should be represented as a directory containing a
624
- // magic whiteout regular file, hence the extra wo header
625
- // returned here.
626
- //
627
- // We assume that whiteout entries are empty;
628
- // otherwise, if we write hdr with hdr.Size > 0, we
629
- // have to write the body before we can write the `wo`
630
- // header.
631
- wo , err := ta .WhiteoutConverter .ConvertWrite (hdr , path , fi )
647
+ // magic whiteout empty regular file, hence the
648
+ // extraWhiteout header returned here.
649
+ result .extraWhiteout , err = ta .WhiteoutConverter .ConvertWrite (hdr , path , fi )
632
650
if err != nil {
633
- return err
651
+ return nil , err
634
652
}
653
+ }
635
654
636
- if wo != nil {
637
- if hdr .Typeflag != tar .TypeReg || hdr .Size == 0 {
638
- if err := ta .TarWriter .WriteHeader (hdr ); err != nil {
639
- return err
640
- }
641
- hdr = wo
642
- } else {
643
- logrus .Infof ("tar: cannot use whiteout for non-empty file %s" , hdr .Name )
644
- }
655
+ return result , nil
656
+ }
657
+
658
+ // addFile performs the write. An error here corrupts the tar file.
659
+ func (ta * tarWriter ) addFile (headers * addFileData ) error {
660
+ hdr := headers .hdr
661
+ if headers .extraWhiteout != nil {
662
+ if hdr .Typeflag == tar .TypeReg && hdr .Size > 0 {
663
+ // If we write hdr with hdr.Size > 0, we have
664
+ // to write the body before we can write the
665
+ // extraWhiteout header. This can only happen
666
+ // if the contract for WhiteoutConverter is
667
+ // not honored, so bail out.
668
+ return fmt .Errorf ("tar: cannot use extra whiteout with non-empty file %s" , hdr .Name )
669
+ }
670
+ if err := ta .TarWriter .WriteHeader (hdr ); err != nil {
671
+ return err
645
672
}
673
+ hdr = headers .extraWhiteout
646
674
}
647
675
648
676
if err := ta .TarWriter .WriteHeader (hdr ); err != nil {
649
677
return err
650
678
}
651
679
652
680
if hdr .Typeflag == tar .TypeReg && hdr .Size > 0 {
653
- file , err := os .Open (path )
681
+ file , err := os .Open (headers . path )
654
682
if err != nil {
655
683
return err
656
684
}
@@ -668,8 +696,8 @@ func (ta *tarWriter) addFile(path, name string) error {
668
696
}
669
697
}
670
698
671
- if ! fi .IsDir () && hasHardlinks (fi ) {
672
- ta .SeenFiles [getInodeFromStat (fi .Sys ())] = hdr .Name
699
+ if ! headers . fi .IsDir () && hasHardlinks (headers . fi ) {
700
+ ta .SeenFiles [getInodeFromStat (headers . fi .Sys ())] = headers . hdr .Name
673
701
}
674
702
675
703
return nil
@@ -1024,10 +1052,11 @@ func tarWithOptionsTo(dest io.WriteCloser, srcPath string, options *TarOptions)
1024
1052
relFilePath = strings .Replace (relFilePath , include , replacement , 1 )
1025
1053
}
1026
1054
1027
- if err := ta .addFile (filePath , relFilePath ); err != nil {
1028
- logrus .Errorf ("Can't add file %s to tar: %s" , filePath , err )
1029
- // if pipe is broken, stop writing tar stream to it
1030
- if err == io .ErrClosedPipe {
1055
+ headers , err := ta .prepareAddFile (filePath , relFilePath )
1056
+ if err != nil {
1057
+ logrus .Errorf ("Can't add file %s to tar: %s; skipping" , filePath , err )
1058
+ } else if headers != nil {
1059
+ if err := ta .addFile (headers ); err != nil {
1031
1060
return err
1032
1061
}
1033
1062
}
0 commit comments