Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deb): content file modes to support gdebi #904

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 93 additions & 34 deletions deb/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -386,40 +387,49 @@
}

func createFilesInsideDataTar(info *nfpm.Info, tw *tar.Writer) (md5buf bytes.Buffer, instSize int64, err error) {
// create files and implicit directories
for _, file := range info.Contents {
var size int64 // declare early to avoid shadowing err
switch file.Type {
case files.TypeRPMGhost:
// skip ghost files in deb
continue
continue // skip ghost files in deb

Check warning on line 393 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L393

Added line #L393 was not covered by tests
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, err := tarHeader(file, info.MTime)
if err != nil {
return md5buf, 0, fmt.Errorf("build directory header for %q: %w",
file.Destination, err)
}

Check warning on line 399 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L397-L399

Added lines #L397 - L399 were not covered by tests

err = tw.WriteHeader(header)
if err != nil {
return md5buf, 0, fmt.Errorf("create directory %q in data tar: %w",
header.Name, err)
}

Check warning on line 405 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L403-L405

Added lines #L403 - L405 were not covered by tests
case files.TypeSymlink:
err = newItemInsideTar(tw, []byte{}, &tar.Header{
Name: files.AsExplicitRelativePath(file.Destination),
Linkname: file.Source,
Typeflag: tar.TypeSymlink,
ModTime: modtime.Get(info.MTime),
Format: tar.FormatGNU,
})
header, err := tarHeader(file, info.MTime)
if err != nil {
return md5buf, 0, fmt.Errorf("build symlink header for %q: %w",
file.Destination, err)
}

Check warning on line 411 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L409-L411

Added lines #L409 - L411 were not covered by tests

err = newItemInsideTar(tw, []byte{}, header)
if err != nil {
return md5buf, 0, fmt.Errorf("create symlink %q in data tar: %w",
header.Linkname, err)
}

Check warning on line 417 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L415-L417

Added lines #L415 - L417 were not covered by tests
case files.TypeDebChangelog:
size, err = createChangelogInsideDataTar(tw, &md5buf, info, file.Destination)
size, err := createChangelogInsideDataTar(tw, &md5buf, info, file.Destination)
if err != nil {
return md5buf, 0, fmt.Errorf("write changelog to data tar: %w", err)
}

Check warning on line 422 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L421-L422

Added lines #L421 - L422 were not covered by tests

instSize += size
default:
size, err = copyToTarAndDigest(file, tw, &md5buf)
}
if err != nil {
return md5buf, 0, err
size, err := copyToTarAndDigest(file, tw, &md5buf)
if err != nil {
return md5buf, 0, fmt.Errorf("write %q to data tar: %w", file.Destination, err)
}

Check warning on line 429 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L428-L429

Added lines #L428 - L429 were not covered by tests

instSize += size
}
instSize += size
}

return md5buf, instSize, nil
Expand All @@ -433,18 +443,11 @@
// don't care if it errs while closing...
defer tarFile.Close() // nolint: errcheck,gosec

header, err := tar.FileInfoHeader(file, file.Source)
header, err := tarHeader(file)
if err != nil {
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
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 +807,59 @@
})
return template.Must(tmpl.Parse(controlTemplate)).Execute(w, data)
}

func tarHeader(content *files.Content, preferredModTimes ...time.Time) (*tar.Header, error) {
const (
ISUID = 0o4000 // Set uid
ISGID = 0o2000 // Set gid
ISVTX = 0o1000 // Save text (sticky bit)
)

fm := content.Mode()

h := &tar.Header{
Name: content.Name(),
ModTime: modtime.Get(
append(preferredModTimes, content.ModTime())...),
Mode: int64(fm & 0o7777),
Uname: content.FileInfo.Owner,
Gname: content.FileInfo.Group,
Format: tar.FormatGNU,
}

switch {
case content.IsDir() || fm&fs.ModeDir != 0:
h.Typeflag = tar.TypeDir
h.Name = files.AsExplicitRelativePath(content.Destination)
case content.Type == files.TypeSymlink || fm&fs.ModeSymlink != 0:
h.Typeflag = tar.TypeSymlink
h.Name = files.AsExplicitRelativePath(content.Destination)
h.Linkname = content.Source
case fm&fs.ModeDevice != 0:
if fm&fs.ModeCharDevice != 0 {
h.Typeflag = tar.TypeChar
} else {
h.Typeflag = tar.TypeBlock
}
case fm&fs.ModeNamedPipe != 0:
h.Typeflag = tar.TypeFifo
case fm&fs.ModeSocket != 0:
return nil, fmt.Errorf("archive/tar: sockets not supported")

Check warning on line 847 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L838-L847

Added lines #L838 - L847 were not covered by tests
default:
h.Typeflag = tar.TypeReg
h.Name = files.AsExplicitRelativePath(content.Destination)
h.Size = content.Size()
}

if fm&fs.ModeSetuid != 0 {
h.Mode |= ISUID
}

Check warning on line 856 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L855-L856

Added lines #L855 - L856 were not covered by tests
if fm&fs.ModeSetgid != 0 {
h.Mode |= ISGID
}

Check warning on line 859 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L858-L859

Added lines #L858 - L859 were not covered by tests
if fm&fs.ModeSticky != 0 {
h.Mode |= ISVTX
}

Check warning on line 862 in deb/deb.go

View check run for this annotation

Codecov / codecov/patch

deb/deb.go#L861-L862

Added lines #L861 - L862 were not covered by tests

return h, nil
}
2 changes: 1 addition & 1 deletion files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ 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
}

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