Skip to content

Commit

Permalink
deb: Fix content file modes to support gdebi, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgeiser committed Feb 4, 2025
1 parent 14dcdfe commit 3328717
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
52 changes: 38 additions & 14 deletions deb/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -394,15 +395,21 @@ func createFilesInsideDataTar(info *nfpm.Info, tw *tar.Writer) (md5buf bytes.Buf
// skip ghost files in deb
continue
case files.TypeDir, files.TypeImplicitDir:
err = tw.WriteHeader(&tar.Header{
Name: files.AsExplicitRelativePath(file.Destination),
Mode: int64(file.FileInfo.Mode),
Typeflag: tar.TypeDir,
Format: tar.FormatGNU,
Uname: file.FileInfo.Owner,
Gname: file.FileInfo.Group,
ModTime: modtime.Get(info.MTime),
})
header, headerErr := tar.FileInfoHeader(file, "") // headerErr to avoid shadowing err
if err != nil {
return md5buf, 0, fmt.Errorf("build directory header: %w", headerErr)
}

Check warning on line 401 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L400-L401

Added lines #L400 - L401 were not covered by tests

// we need to change the type from file to dir because
// tar.FileInfoHEader thinks it is a file because the file mode does
// not contain type flags and in this case, it ignores file.IsDir().
header.Typeflag = tar.TypeDir
header.Name = files.AsExplicitRelativePath(file.Destination)
header.Format = tar.FormatGNU
header.ModTime = modtime.Get(info.MTime)
header.Mode = addSpecialBits(header.Mode, file.Mode())

err = tw.WriteHeader(header)
case files.TypeSymlink:
err = newItemInsideTar(tw, []byte{}, &tar.Header{
Name: files.AsExplicitRelativePath(file.Destination),
Expand Down Expand Up @@ -438,13 +445,10 @@ func copyToTarAndDigest(file *files.Content, tw *tar.Writer, md5w io.Writer) (in
return 0, err
}

// tar.FileInfoHeader only uses file.Mode().Perm() which masks the mode with
// 0o777 which we don't want because we want to be able to set the suid bit.
header.Mode = int64(file.Mode())
header.Format = tar.FormatGNU
header.Name = files.AsExplicitRelativePath(file.Destination)
header.Uname = file.FileInfo.Owner
header.Gname = file.FileInfo.Group
header.Mode = addSpecialBits(header.Mode, file.Mode())

if err := tw.WriteHeader(header); err != nil {
return 0, fmt.Errorf("cannot write header of %s to data.tar.gz: %w", file.Source, err)
}
Expand Down Expand Up @@ -804,3 +808,23 @@ func writeControl(w io.Writer, data controlData) error {
})
return template.Must(tmpl.Parse(controlTemplate)).Execute(w, data)
}

func addSpecialBits(originalMode int64, specialBits fs.FileMode) int64 {
const (
ISUID = 0o4000 // Set uid
ISGID = 0o2000 // Set gid
ISVTX = 0o1000 // Save text (sticky bit)
)

if specialBits&fs.ModeSetuid != 0 {
originalMode |= ISUID
}

Check warning on line 821 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L820-L821

Added lines #L820 - L821 were not covered by tests
if specialBits&fs.ModeSetgid != 0 {
originalMode |= ISGID
}

Check warning on line 824 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L823-L824

Added lines #L823 - L824 were not covered by tests
if specialBits&fs.ModeSticky != 0 {
originalMode |= ISVTX
}

Check warning on line 827 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L826-L827

Added lines #L826 - L827 were not covered by tests

return originalMode
}
19 changes: 18 additions & 1 deletion files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ type Content struct {
Expand bool `yaml:"expand,omitempty" json:"expand,omitempty"`
}

// FileInfoNames is used to make sure *Content satisfies tar.FileInfoNames.
type FileInfoNames interface {
fs.FileInfo
Uname() (string, error)
Gname() (string, error)
}

var _ FileInfoNames = &Content{}

type ContentFileInfo struct {
Owner string `yaml:"owner,omitempty" json:"owner,omitempty"`
Group string `yaml:"group,omitempty" json:"group,omitempty"`
Expand Down Expand Up @@ -183,7 +192,15 @@ func (c *Content) ModTime() time.Time {

// IsDir to part of the os.FileInfo interface
func (c *Content) IsDir() bool {
return false
return c.Type == TypeDir || c.Type == TypeImplicitDir

Check warning on line 195 in files/files.go

View check run for this annotation

Codecov / codecov/patch

files/files.go#L195

Added line #L195 was not covered by tests
}

func (c *Content) Uname() (string, error) {
return c.FileInfo.Owner, nil
}

func (c *Content) Gname() (string, error) {
return c.FileInfo.Group, nil
}

// Sys to part of the os.FileInfo interface
Expand Down

0 comments on commit 3328717

Please sign in to comment.