@@ -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"
@@ -394,23 +395,19 @@ func createFilesInsideDataTar(info *nfpm.Info, tw *tar.Writer) (md5buf bytes.Buf
394
395
// skip ghost files in deb
395
396
continue
396
397
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
- })
398
+ header , headerErr := tarHeader (file ) // headerErr to avoid shadowing err
399
+ if err != nil {
400
+ return md5buf , 0 , fmt .Errorf ("build directory header: %w" , headerErr )
401
+ }
402
+
403
+ err = tw .WriteHeader (header )
406
404
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
- })
405
+ header , headerErr := tarHeader (file ) // headerErr to avoid shadowing err
406
+ if err != nil {
407
+ return md5buf , 0 , fmt .Errorf ("build symlink header: %w" , headerErr )
408
+ }
409
+
410
+ err = newItemInsideTar (tw , []byte {}, header )
414
411
case files .TypeDebChangelog :
415
412
size , err = createChangelogInsideDataTar (tw , & md5buf , info , file .Destination )
416
413
default :
@@ -433,18 +430,11 @@ func copyToTarAndDigest(file *files.Content, tw *tar.Writer, md5w io.Writer) (in
433
430
// don't care if it errs while closing...
434
431
defer tarFile .Close () // nolint: errcheck,gosec
435
432
436
- header , err := tar . FileInfoHeader (file , file . Source )
433
+ header , err := tarHeader (file )
437
434
if err != nil {
438
435
return 0 , err
439
436
}
440
437
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
438
if err := tw .WriteHeader (header ); err != nil {
449
439
return 0 , fmt .Errorf ("cannot write header of %s to data.tar.gz: %w" , file .Source , err )
450
440
}
@@ -804,3 +794,58 @@ func writeControl(w io.Writer, data controlData) error {
804
794
})
805
795
return template .Must (tmpl .Parse (controlTemplate )).Execute (w , data )
806
796
}
797
+
798
+ func tarHeader (content * files.Content ) (* tar.Header , error ) {
799
+ const (
800
+ ISUID = 0o4000 // Set uid
801
+ ISGID = 0o2000 // Set gid
802
+ ISVTX = 0o1000 // Save text (sticky bit)
803
+ )
804
+
805
+ fm := content .Mode ()
806
+
807
+ h := & tar.Header {
808
+ Name : content .Name (),
809
+ ModTime : content .ModTime (),
810
+ Mode : int64 (fm & 0o7777 ),
811
+ Uname : content .FileInfo .Owner ,
812
+ Gname : content .FileInfo .Group ,
813
+ Format : tar .FormatGNU ,
814
+ }
815
+
816
+ switch {
817
+ case content .IsDir ():
818
+ h .Typeflag = tar .TypeDir
819
+ h .Name = files .AsExplicitRelativePath (content .Destination )
820
+ case content .Type == files .TypeSymlink || fm & fs .ModeSymlink != 0 :
821
+ h .Typeflag = tar .TypeSymlink
822
+ h .Name = files .AsExplicitRelativePath (content .Destination )
823
+ h .Linkname = content .Source
824
+ case fm & fs .ModeDevice != 0 :
825
+ if fm & fs .ModeCharDevice != 0 {
826
+ h .Typeflag = tar .TypeChar
827
+ } else {
828
+ h .Typeflag = tar .TypeBlock
829
+ }
830
+ case fm & fs .ModeNamedPipe != 0 :
831
+ h .Typeflag = tar .TypeFifo
832
+ case fm & fs .ModeSocket != 0 :
833
+ return nil , fmt .Errorf ("archive/tar: sockets not supported" )
834
+ default :
835
+ h .Typeflag = tar .TypeReg
836
+ h .Name = files .AsExplicitRelativePath (content .Destination )
837
+ h .Size = content .Size ()
838
+ }
839
+
840
+ if fm & fs .ModeSetuid != 0 {
841
+ h .Mode |= ISUID
842
+ }
843
+ if fm & fs .ModeSetgid != 0 {
844
+ h .Mode |= ISGID
845
+ }
846
+ if fm & fs .ModeSticky != 0 {
847
+ h .Mode |= ISVTX
848
+ }
849
+
850
+ return h , nil
851
+ }
0 commit comments