Skip to content

Commit 9619751

Browse files
committed
deb: Fix content file modes to support gdebi, etc.
1 parent 14dcdfe commit 9619751

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

deb/deb.go

+69-24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"errors"
1313
"fmt"
1414
"io"
15+
"io/fs"
1516
"os"
1617
"path/filepath"
1718
"strings"
@@ -394,23 +395,19 @@ func createFilesInsideDataTar(info *nfpm.Info, tw *tar.Writer) (md5buf bytes.Buf
394395
// skip ghost files in deb
395396
continue
396397
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)
406404
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)
414411
case files.TypeDebChangelog:
415412
size, err = createChangelogInsideDataTar(tw, &md5buf, info, file.Destination)
416413
default:
@@ -433,18 +430,11 @@ func copyToTarAndDigest(file *files.Content, tw *tar.Writer, md5w io.Writer) (in
433430
// don't care if it errs while closing...
434431
defer tarFile.Close() // nolint: errcheck,gosec
435432

436-
header, err := tar.FileInfoHeader(file, file.Source)
433+
header, err := tarHeader(file)
437434
if err != nil {
438435
return 0, err
439436
}
440437

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
448438
if err := tw.WriteHeader(header); err != nil {
449439
return 0, fmt.Errorf("cannot write header of %s to data.tar.gz: %w", file.Source, err)
450440
}
@@ -804,3 +794,58 @@ func writeControl(w io.Writer, data controlData) error {
804794
})
805795
return template.Must(tmpl.Parse(controlTemplate)).Execute(w, data)
806796
}
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+
}

files/files.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (c *Content) ModTime() time.Time {
183183

184184
// IsDir to part of the os.FileInfo interface
185185
func (c *Content) IsDir() bool {
186-
return false
186+
return c.Type == TypeDir || c.Type == TypeImplicitDir
187187
}
188188

189189
// Sys to part of the os.FileInfo interface

0 commit comments

Comments
 (0)