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: parent directory permissions #128

Merged
merged 34 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5a9e828
remove deb package blob used for testing
letFunny Jan 8, 2024
e283b54
remove base-files and create test data instead
letFunny Jan 10, 2024
5b4a669
rename base-files
letFunny Jan 10, 2024
0b971ec
minor tweaks
letFunny Jan 11, 2024
c05849e
restore base-files package
letFunny Jan 11, 2024
8843df7
change folder names in tests to be meaningful
letFunny Jan 30, 2024
f0f3373
more tests
letFunny Jan 30, 2024
c66ee38
Slicer tests passing
letFunny Jan 31, 2024
5dcb35d
remove deb blob
letFunny Jan 31, 2024
2454adf
final touches
letFunny Jan 31, 2024
8fdb140
naming tests nitpicks
letFunny Jan 31, 2024
02cc088
change underscore to dash in file names
letFunny Feb 26, 2024
e654732
fix test case not matching previous behavior
letFunny Feb 26, 2024
b245f35
comments
letFunny Feb 26, 2024
5e9eb2f
Merge remote-tracking branch 'base/main' into remove-deb-blob
letFunny Mar 1, 2024
3d84be3
Merge branch 'remove-deb-blob' into chisel-db-report-filter
letFunny Mar 5, 2024
622e346
feat: filter created entries for report
letFunny Mar 5, 2024
b2d22d9
use IsDir() in favour of bit mask
letFunny Mar 6, 2024
bb9dc02
simplify and remove auxiliary function
letFunny Mar 7, 2024
f7887d2
comment nit
letFunny Mar 7, 2024
a9b8c02
Merge branch 'main' into chisel-db-report-filter
letFunny Mar 7, 2024
a2805d0
bugfix: parent directory permissions
letFunny Mar 8, 2024
23e5e15
address review comments
letFunny Mar 11, 2024
21eea9e
Merge branch 'main' into parent-dir-permissions
letFunny Mar 11, 2024
9a55a36
Merge branch 'chisel-db-report-filter' into parent-dir-permissions
letFunny Mar 11, 2024
2f12765
tarball uniformly
letFunny Mar 12, 2024
ccc7ff5
remove slices dependency
letFunny Mar 12, 2024
241cedf
renaming per PR review
letFunny Apr 3, 2024
acfe93e
more efficient parentDirs implementation
letFunny Apr 3, 2024
847ecb3
avoid redundant calls to os.Stat
letFunny Apr 3, 2024
75b82b5
Merge branch 'main' into parent-dir-permissions
letFunny Apr 3, 2024
387477a
revert back unnecessary change to utf-8 string handling
letFunny Apr 8, 2024
5849477
add OverrideMode flag to directory creation and refactor
letFunny Apr 9, 2024
eb21499
remove override mode for dirs
letFunny Apr 12, 2024
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
75 changes: 56 additions & 19 deletions internal/deb/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"compress/gzip"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
Expand Down Expand Up @@ -159,6 +160,13 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
}
}

// When creating a file we will iterate through its parent directories and
// create them with the permissions defined in the tarball.
//
// The assumption is that the tar entries of the parent directories appear
// before the entry for the file itself. This is the case for .deb files but
// not for all tarballs.
tarDirMode := make(map[string]fs.FileMode)
letFunny marked this conversation as resolved.
Show resolved Hide resolved
tarReader := tar.NewReader(dataReader)
for {
tarHeader, err := tarReader.Next()
Expand All @@ -180,6 +188,9 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
}

sourceIsDir := sourcePath[len(sourcePath)-1] == '/'
if sourceIsDir {
tarDirMode[sourcePath] = tarHeader.FileInfo().Mode()
}

//debugf("Extracting header: %#v", tarHeader)

Expand All @@ -189,23 +200,10 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
delete(pendingPaths, globPath)
} else {
extractInfos, ok = options.Extract[sourcePath]
if ok {
delete(pendingPaths, sourcePath)
} else {
// Base directory for extracted content. Relevant mainly to preserve
// the metadata, since the extracted content itself will also create
// any missing directories unaccounted for in the options.
createOptions := &fsutil.CreateOptions{
Path: filepath.Join(options.TargetDir, sourcePath),
Mode: tarHeader.FileInfo().Mode(),
MakeParents: true,
}
err := options.Create(nil, createOptions)
if err != nil {
return err
}
if !ok {
continue
letFunny marked this conversation as resolved.
Show resolved Hide resolved
}
delete(pendingPaths, sourcePath)
}

var contentCache []byte
Expand All @@ -228,17 +226,43 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {
if contentIsCached {
pathReader = bytes.NewReader(contentCache)
}
var targetPath string
var relPath string
if globPath == "" {
targetPath = filepath.Join(options.TargetDir, extractInfo.Path)
relPath = extractInfo.Path
} else {
targetPath = filepath.Join(options.TargetDir, sourcePath)
relPath = sourcePath
}
if extractInfo.Mode != 0 {
tarHeader.Mode = int64(extractInfo.Mode)
}
// Create the parent directories using the permissions from the tarball.
parents := parentDirs(relPath)
for _, path := range parents {
if path == "/" {
continue
}
mode, ok := tarDirMode[path]
if !ok {
continue
}
delete(tarDirMode, path)

createOptions := &fsutil.CreateOptions{
Path: filepath.Join(options.TargetDir, path),
Mode: mode,
MakeParents: true,
// We dont want this implicit parent directory to overwrite
// an existing directory.
OverrideMode: false,
}
err := options.Create(nil, createOptions)
if err != nil {
return err
}
}
// Create the entry itself.
createOptions := &fsutil.CreateOptions{
letFunny marked this conversation as resolved.
Show resolved Hide resolved
Path: targetPath,
Path: filepath.Join(options.TargetDir, relPath),
Mode: tarHeader.FileInfo().Mode(),
Data: pathReader,
Link: tarHeader.Linkname,
Expand Down Expand Up @@ -269,3 +293,16 @@ func extractData(dataReader io.Reader, options *ExtractOptions) error {

return nil
}

func parentDirs(path string) []string {
path = filepath.Clean(path)
parents := make([]string, strings.Count(path, "/"))
count := 0
for i, c := range path {
if c == '/' {
parents[count] = path[:i+1]
count++
}
}
return parents
}
22 changes: 20 additions & 2 deletions internal/deb/extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,7 @@ var extractTests = []extractTest{{
},
},
result: map[string]string{
"/dir/": "dir 0755",
"/other-dir/": "dir 0755",
"/dir/": "dir 0755",
},
notCreated: []string{},
}, {
Expand All @@ -319,6 +318,25 @@ var extractTests = []extractTest{{
},
},
error: `cannot extract from package "test-package": no content at /dir/missing-file`,
}, {
summary: "Extract non-ASCII path and preserve parent directories permissions",
pkgdata: testutil.MustMakeDeb([]testutil.TarEntry{
testutil.Dir(0755, "./"),
testutil.Dir(0766, "./日本/"),
testutil.Reg(0644, "./日本/語", "whatever"),
}),
options: deb.ExtractOptions{
Extract: map[string][]deb.ExtractInfo{
"/日本/語": []deb.ExtractInfo{{
Path: "/日本/語",
}},
},
},
result: map[string]string{
"/日本/": "dir 0766",
"/日本/語": "file 0644 85738f8f",
},
notCreated: []string{},
}}

func (s *S) TestExtract(c *C) {
Expand Down
5 changes: 5 additions & 0 deletions internal/fsutil/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type CreateOptions struct {
Mode fs.FileMode
Data io.Reader
Link string
// If OverrideMode is true and a directory exists, its mode will be modified.
OverrideMode bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not looking right, for two reasons:

  1. We've added a flag to control behavior that was standard until this commit, meaning every single call site for the Create function was the equivalent of OverrideMode: true. Yet, we're not using this flag anywhere apparently, which means it's false 100% of the time and the behavior has been disabled. That implies to me that the old behavior has turned irrelevant after the last couple of changes, so we can just drop the behavior altogether.

  2. This flag doesn't seem to sit well in the API as a whole. It doesn't make sense for this flag to work only on directories, for example.

GIven (1), what's the situation? Can we just delete the behavior altogether?

Copy link
Collaborator Author

@letFunny letFunny Apr 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with both. I was keeping it for future compatibility but when we need it we can just add it back to the API. Deleting unused code is the best code.

// If MakeParents is true, missing parent directories of Path are
// created with permissions 0755.
MakeParents bool
Expand Down Expand Up @@ -74,6 +76,9 @@ func createDir(o *CreateOptions) error {
debugf("Creating directory: %s (mode %#o)", o.Path, o.Mode)
err := os.Mkdir(o.Path, o.Mode)
if os.IsExist(err) {
if !o.OverrideMode {
return nil
}
err = os.Chmod(o.Path, o.Mode)
}
return err
Expand Down
31 changes: 31 additions & 0 deletions internal/fsutil/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"syscall"
Expand All @@ -16,6 +17,7 @@ import (

type createTest struct {
options fsutil.CreateOptions
hackdir func(c *C, dir string)
result map[string]string
error string
}
Expand Down Expand Up @@ -66,6 +68,32 @@ var createTests = []createTest{{
Mode: fs.ModeDir | 0775,
},
error: `.*: no such file or directory`,
}, {
options: fsutil.CreateOptions{
Path: "foo",
Mode: fs.ModeDir | 0775,
OverrideMode: false,
},
hackdir: func(c *C, dir string) {
c.Assert(os.Mkdir(filepath.Join(dir, "foo/"), fs.ModeDir|0765), IsNil)
},
result: map[string]string{
// mode is not updated.
"/foo/": "dir 0765",
},
}, {
options: fsutil.CreateOptions{
Path: "foo",
Mode: fs.ModeDir | 0775,
OverrideMode: true,
},
hackdir: func(c *C, dir string) {
c.Assert(os.Mkdir(filepath.Join(dir, "foo/"), fs.ModeDir|0765), IsNil)
},
result: map[string]string{
// mode is updated.
"/foo/": "dir 0775",
},
}}

func (s *S) TestCreate(c *C) {
Expand All @@ -81,6 +109,9 @@ func (s *S) TestCreate(c *C) {
}
c.Logf("Options: %v", test.options)
dir := c.MkDir()
if test.hackdir != nil {
test.hackdir(c, dir)
}
options := test.options
options.Path = filepath.Join(dir, options.Path)
entry, err := fsutil.Create(&options)
Expand Down
33 changes: 28 additions & 5 deletions internal/slicer/slicer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ type slicerTest struct {
// TODO:
// The results of the report do not conform to the planned implementation
// yet. Namely:
// * Parent directories of {text} files are reported even though they should not.
// * We do not track removed directories or changes done in Starlark.
report map[string]string
error string
Expand Down Expand Up @@ -191,6 +190,32 @@ var slicerTests = []slicerTest{{
report: map[string]string{
"/parent/new/": "dir 0755 {test-package_myslice}",
},
}, {
summary: "Create new file using glob and preserve parent directory permissions",
slices: []setup.SliceKey{{"test-package", "myslice"}},
pkgs: map[string][]byte{
"test-package": testutil.PackageData["test-package"],
},
release: map[string]string{
"slices/mydir/test-package.yaml": `
package: test-package
slices:
myslice:
contents:
# Note the missing /parent/ and /parent/permissions/ here.
/parent/**:
`,
},
filesystem: map[string]string{
"/parent/": "dir 01777", // This is the magic.
"/parent/permissions/": "dir 0764", // This is the magic.
"/parent/permissions/file": "file 0755 722c14b3",
},
report: map[string]string{
"/parent/": "dir 01777 {test-package_myslice}",
"/parent/permissions/": "dir 0764 {test-package_myslice}",
"/parent/permissions/file": "file 0755 722c14b3 {test-package_myslice}",
},
}, {
summary: "Conditional architecture",
arch: "amd64",
Expand Down Expand Up @@ -323,8 +348,7 @@ var slicerTests = []slicerTest{{
`,
},
filesystem: map[string]string{
// TODO this is the wrong mode for the directory, it should be 01777.
"/dir/": "dir 0755",
"/dir/": "dir 01777",
"/dir/file": "file 0644 a441b15f",
},
report: map[string]string{
Expand Down Expand Up @@ -362,8 +386,7 @@ var slicerTests = []slicerTest{{
`,
},
filesystem: map[string]string{
// TODO this is the wrong mode for the directory, it should be 01777.
"/dir/": "dir 0755",
"/dir/": "dir 01777",
"/dir/file": "file 0644 a441b15f",
},
report: map[string]string{
Expand Down
Loading