@@ -12,6 +12,7 @@ import (
1212 "errors"
1313 "fmt"
1414 "io"
15+ "io/fs"
1516 "os"
1617 "path/filepath"
1718 "strings"
@@ -386,40 +387,49 @@ func fillDataTar(info *nfpm.Info, w io.Writer) (md5sums []byte, instSize int64,
386387}
387388
388389func createFilesInsideDataTar (info * nfpm.Info , tw * tar.Writer ) (md5buf bytes.Buffer , instSize int64 , err error ) {
389- // create files and implicit directories
390390 for _ , file := range info .Contents {
391- var size int64 // declare early to avoid shadowing err
392391 switch file .Type {
393392 case files .TypeRPMGhost :
394- // skip ghost files in deb
395- continue
393+ continue // skip ghost files in deb
396394 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+ }
406406 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+ }
414418 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
416425 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
421432 }
422- instSize += size
423433 }
424434
425435 return md5buf , instSize , nil
@@ -433,18 +443,11 @@ func copyToTarAndDigest(file *files.Content, tw *tar.Writer, md5w io.Writer) (in
433443 // don't care if it errs while closing...
434444 defer tarFile .Close () // nolint: errcheck,gosec
435445
436- header , err := tar . FileInfoHeader (file , file . Source )
446+ header , err := tarHeader (file )
437447 if err != nil {
438448 return 0 , err
439449 }
440450
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
448451 if err := tw .WriteHeader (header ); err != nil {
449452 return 0 , fmt .Errorf ("cannot write header of %s to data.tar.gz: %w" , file .Source , err )
450453 }
@@ -804,3 +807,59 @@ func writeControl(w io.Writer, data controlData) error {
804807 })
805808 return template .Must (tmpl .Parse (controlTemplate )).Execute (w , data )
806809}
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