@@ -12,6 +12,7 @@ import (
12
12
"errors"
13
13
"fmt"
14
14
"io"
15
+ "io/fs"
15
16
"os"
16
17
"path/filepath"
17
18
"strings"
@@ -386,40 +387,49 @@ func fillDataTar(info *nfpm.Info, w io.Writer) (md5sums []byte, instSize int64,
386
387
}
387
388
388
389
func createFilesInsideDataTar (info * nfpm.Info , tw * tar.Writer ) (md5buf bytes.Buffer , instSize int64 , err error ) {
389
- // create files and implicit directories
390
390
for _ , file := range info .Contents {
391
- var size int64 // declare early to avoid shadowing err
392
391
switch file .Type {
393
392
case files .TypeRPMGhost :
394
- // skip ghost files in deb
395
- continue
393
+ continue // skip ghost files in deb
396
394
case files .TypeDir , files .TypeImplicitDir :
397
- err = tw .WriteHeader (& tar.Header {
398
- Name : files .AsExplicitRelativePath (file .Destination ),
399
- Mode : int64 (file .FileInfo .Mode ),
400
- Typeflag : tar .TypeDir ,
401
- Format : tar .FormatGNU ,
402
- Uname : file .FileInfo .Owner ,
403
- Gname : file .FileInfo .Group ,
404
- ModTime : modtime .Get (info .MTime ),
405
- })
395
+ header , err := tarHeader (file , info .MTime )
396
+ if err != nil {
397
+ return md5buf , 0 , fmt .Errorf ("build directory header for %q: %w" ,
398
+ file .Destination , err )
399
+ }
400
+
401
+ err = tw .WriteHeader (header )
402
+ if err != nil {
403
+ return md5buf , 0 , fmt .Errorf ("create directory %q in data tar: %w" ,
404
+ header .Name , err )
405
+ }
406
406
case files .TypeSymlink :
407
- err = newItemInsideTar (tw , []byte {}, & tar.Header {
408
- Name : files .AsExplicitRelativePath (file .Destination ),
409
- Linkname : file .Source ,
410
- Typeflag : tar .TypeSymlink ,
411
- ModTime : modtime .Get (info .MTime ),
412
- Format : tar .FormatGNU ,
413
- })
407
+ header , err := tarHeader (file , info .MTime )
408
+ if err != nil {
409
+ return md5buf , 0 , fmt .Errorf ("build symlink header for %q: %w" ,
410
+ file .Destination , err )
411
+ }
412
+
413
+ err = newItemInsideTar (tw , []byte {}, header )
414
+ if err != nil {
415
+ return md5buf , 0 , fmt .Errorf ("create symlink %q in data tar: %w" ,
416
+ header .Linkname , err )
417
+ }
414
418
case files .TypeDebChangelog :
415
- size , err = createChangelogInsideDataTar (tw , & md5buf , info , file .Destination )
419
+ size , err := createChangelogInsideDataTar (tw , & md5buf , info , file .Destination )
420
+ if err != nil {
421
+ return md5buf , 0 , fmt .Errorf ("write changelog to data tar: %w" , err )
422
+ }
423
+
424
+ instSize += size
416
425
default :
417
- size , err = copyToTarAndDigest (file , tw , & md5buf )
418
- }
419
- if err != nil {
420
- return md5buf , 0 , err
426
+ size , err := copyToTarAndDigest (file , tw , & md5buf )
427
+ if err != nil {
428
+ return md5buf , 0 , fmt .Errorf ("write %q to data tar: %w" , file .Destination , err )
429
+ }
430
+
431
+ instSize += size
421
432
}
422
- instSize += size
423
433
}
424
434
425
435
return md5buf , instSize , nil
@@ -433,18 +443,11 @@ func copyToTarAndDigest(file *files.Content, tw *tar.Writer, md5w io.Writer) (in
433
443
// don't care if it errs while closing...
434
444
defer tarFile .Close () // nolint: errcheck,gosec
435
445
436
- header , err := tar . FileInfoHeader (file , file . Source )
446
+ header , err := tarHeader (file )
437
447
if err != nil {
438
448
return 0 , err
439
449
}
440
450
441
- // tar.FileInfoHeader only uses file.Mode().Perm() which masks the mode with
442
- // 0o777 which we don't want because we want to be able to set the suid bit.
443
- header .Mode = int64 (file .Mode ())
444
- header .Format = tar .FormatGNU
445
- header .Name = files .AsExplicitRelativePath (file .Destination )
446
- header .Uname = file .FileInfo .Owner
447
- header .Gname = file .FileInfo .Group
448
451
if err := tw .WriteHeader (header ); err != nil {
449
452
return 0 , fmt .Errorf ("cannot write header of %s to data.tar.gz: %w" , file .Source , err )
450
453
}
@@ -804,3 +807,59 @@ func writeControl(w io.Writer, data controlData) error {
804
807
})
805
808
return template .Must (tmpl .Parse (controlTemplate )).Execute (w , data )
806
809
}
810
+
811
+ func tarHeader (content * files.Content , preferredModTimes ... time.Time ) (* tar.Header , error ) {
812
+ const (
813
+ ISUID = 0o4000 // Set uid
814
+ ISGID = 0o2000 // Set gid
815
+ ISVTX = 0o1000 // Save text (sticky bit)
816
+ )
817
+
818
+ fm := content .Mode ()
819
+
820
+ h := & tar.Header {
821
+ Name : content .Name (),
822
+ ModTime : modtime .Get (
823
+ append (preferredModTimes , content .ModTime ())... ),
824
+ Mode : int64 (fm & 0o7777 ),
825
+ Uname : content .FileInfo .Owner ,
826
+ Gname : content .FileInfo .Group ,
827
+ Format : tar .FormatGNU ,
828
+ }
829
+
830
+ switch {
831
+ case content .IsDir () || fm & fs .ModeDir != 0 :
832
+ h .Typeflag = tar .TypeDir
833
+ h .Name = files .AsExplicitRelativePath (content .Destination )
834
+ case content .Type == files .TypeSymlink || fm & fs .ModeSymlink != 0 :
835
+ h .Typeflag = tar .TypeSymlink
836
+ h .Name = files .AsExplicitRelativePath (content .Destination )
837
+ h .Linkname = content .Source
838
+ case fm & fs .ModeDevice != 0 :
839
+ if fm & fs .ModeCharDevice != 0 {
840
+ h .Typeflag = tar .TypeChar
841
+ } else {
842
+ h .Typeflag = tar .TypeBlock
843
+ }
844
+ case fm & fs .ModeNamedPipe != 0 :
845
+ h .Typeflag = tar .TypeFifo
846
+ case fm & fs .ModeSocket != 0 :
847
+ return nil , fmt .Errorf ("archive/tar: sockets not supported" )
848
+ default :
849
+ h .Typeflag = tar .TypeReg
850
+ h .Name = files .AsExplicitRelativePath (content .Destination )
851
+ h .Size = content .Size ()
852
+ }
853
+
854
+ if fm & fs .ModeSetuid != 0 {
855
+ h .Mode |= ISUID
856
+ }
857
+ if fm & fs .ModeSetgid != 0 {
858
+ h .Mode |= ISGID
859
+ }
860
+ if fm & fs .ModeSticky != 0 {
861
+ h .Mode |= ISVTX
862
+ }
863
+
864
+ return h , nil
865
+ }
0 commit comments