storage: set `0o744` for files with exec mode set
Cherry-picked from 2736b748e6
This commit is contained in:
parent
82dc24c667
commit
af854cfcf9
|
@ -50,10 +50,12 @@ import (
|
|||
const GarbageCountLimit = 1000
|
||||
|
||||
const (
|
||||
// defaultFileMode is the permission mode applied to all files inside of an artifact archive.
|
||||
// defaultFileMode is the permission mode applied to files inside of an artifact archive.
|
||||
defaultFileMode int64 = 0o644
|
||||
// defaultDirMode is the permission mode applied to all directories inside of an artifact archive.
|
||||
defaultDirMode int64 = 0o755
|
||||
// defaultExeFileMode is the permission mode applied to executable files inside an artifact archive.
|
||||
defaultExeFileMode int64 = 0o744
|
||||
)
|
||||
|
||||
// Storage manages artifacts
|
||||
|
@ -395,6 +397,7 @@ func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter Archiv
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The name needs to be modified to maintain directory structure
|
||||
// as tar.FileInfoHeader only has access to the base name of the file.
|
||||
// Ref: https://golang.org/src/archive/tar/common.go?#L626
|
||||
|
@ -405,21 +408,7 @@ func (s *Storage) Archive(artifact *sourcev1.Artifact, dir string, filter Archiv
|
|||
return err
|
||||
}
|
||||
}
|
||||
header.Name = relFilePath
|
||||
|
||||
// We want to remove any environment specific data as well, this
|
||||
// ensures the checksum is purely content based.
|
||||
header.Gid = 0
|
||||
header.Uid = 0
|
||||
header.Uname = ""
|
||||
header.Gname = ""
|
||||
header.ModTime = time.Time{}
|
||||
header.AccessTime = time.Time{}
|
||||
header.ChangeTime = time.Time{}
|
||||
header.Mode = defaultFileMode
|
||||
if fi.Mode().IsDir() {
|
||||
header.Mode = defaultDirMode
|
||||
}
|
||||
sanitizeHeader(relFilePath, header)
|
||||
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
return err
|
||||
|
@ -682,3 +671,42 @@ func (wc *writeCounter) Write(p []byte) (int, error) {
|
|||
wc.written += int64(n)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// sanitizeHeader modifies the tar.Header to be relative to the root of the
|
||||
// archive and removes any environment specific data.
|
||||
func sanitizeHeader(relP string, h *tar.Header) {
|
||||
// Modify the name to be relative to the root of the archive,
|
||||
// this ensures we maintain the same structure when extracting.
|
||||
h.Name = relP
|
||||
|
||||
// We want to remove any environment specific data as well, this
|
||||
// ensures the checksum is purely content based.
|
||||
h.Gid = 0
|
||||
h.Uid = 0
|
||||
h.Uname = ""
|
||||
h.Gname = ""
|
||||
h.ModTime = time.Time{}
|
||||
h.AccessTime = time.Time{}
|
||||
h.ChangeTime = time.Time{}
|
||||
|
||||
// Override the mode to be the default for the type of file.
|
||||
setDefaultMode(h)
|
||||
}
|
||||
|
||||
// setDefaultMode sets the default mode for the given header.
|
||||
func setDefaultMode(h *tar.Header) {
|
||||
if h.FileInfo().IsDir() {
|
||||
h.Mode = defaultDirMode
|
||||
return
|
||||
}
|
||||
|
||||
if h.FileInfo().Mode().IsRegular() {
|
||||
mode := h.FileInfo().Mode()
|
||||
if mode&os.ModeType == 0 && mode&0o111 != 0 {
|
||||
h.Mode = defaultExeFileMode
|
||||
return
|
||||
}
|
||||
h.Mode = defaultFileMode
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,9 +107,14 @@ func TestStorage_Archive(t *testing.T) {
|
|||
t.Fatalf("error while bootstrapping storage: %v", err)
|
||||
}
|
||||
|
||||
createFiles := func(files map[string][]byte) (dir string, err error) {
|
||||
type dummyFile struct {
|
||||
content []byte
|
||||
mode int64
|
||||
}
|
||||
|
||||
createFiles := func(files map[string]dummyFile) (dir string, err error) {
|
||||
dir = t.TempDir()
|
||||
for name, b := range files {
|
||||
for name, df := range files {
|
||||
absPath := filepath.Join(dir, name)
|
||||
if err = os.MkdirAll(filepath.Dir(absPath), 0o750); err != nil {
|
||||
return
|
||||
|
@ -118,18 +123,24 @@ func TestStorage_Archive(t *testing.T) {
|
|||
if err != nil {
|
||||
return "", fmt.Errorf("could not create file %q: %w", absPath, err)
|
||||
}
|
||||
if n, err := f.Write(b); err != nil {
|
||||
if n, err := f.Write(df.content); err != nil {
|
||||
f.Close()
|
||||
return "", fmt.Errorf("could not write %d bytes to file %q: %w", n, f.Name(), err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if df.mode != 0 {
|
||||
if err = os.Chmod(absPath, os.FileMode(df.mode)); err != nil {
|
||||
return "", fmt.Errorf("could not chmod file %q: %w", absPath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
matchFiles := func(t *testing.T, storage *Storage, artifact sourcev1.Artifact, files map[string][]byte, dirs []string) {
|
||||
matchFiles := func(t *testing.T, storage *Storage, artifact sourcev1.Artifact, files map[string]dummyFile, dirs []string) {
|
||||
t.Helper()
|
||||
for name, b := range files {
|
||||
for name, df := range files {
|
||||
mustExist := !(name[0:1] == "!")
|
||||
if !mustExist {
|
||||
name = name[1:]
|
||||
|
@ -138,7 +149,7 @@ func TestStorage_Archive(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed reading tarball: %v", err)
|
||||
}
|
||||
if bs := int64(len(b)); s != bs {
|
||||
if bs := int64(len(df.content)); s != bs {
|
||||
t.Fatalf("%q size %v != %v", name, s, bs)
|
||||
}
|
||||
if exist != mustExist {
|
||||
|
@ -148,8 +159,12 @@ func TestStorage_Archive(t *testing.T) {
|
|||
t.Errorf("tarball contained excluded file %q", name)
|
||||
}
|
||||
}
|
||||
if exist && m != defaultFileMode {
|
||||
t.Fatalf("%q mode %v != %v", name, m, defaultFileMode)
|
||||
expectMode := df.mode
|
||||
if expectMode == 0 {
|
||||
expectMode = defaultFileMode
|
||||
}
|
||||
if exist && m != expectMode {
|
||||
t.Fatalf("%q mode %v != %v", name, m, expectMode)
|
||||
}
|
||||
}
|
||||
for _, name := range dirs {
|
||||
|
@ -177,62 +192,62 @@ func TestStorage_Archive(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
name string
|
||||
files map[string][]byte
|
||||
files map[string]dummyFile
|
||||
filter ArchiveFileFilter
|
||||
want map[string][]byte
|
||||
want map[string]dummyFile
|
||||
wantDirs []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no filter",
|
||||
files: map[string][]byte{
|
||||
".git/config": nil,
|
||||
"file.jpg": []byte(`contents`),
|
||||
"manifest.yaml": nil,
|
||||
files: map[string]dummyFile{
|
||||
".git/config": {},
|
||||
"file.jpg": {content: []byte(`contents`)},
|
||||
"manifest.yaml": {},
|
||||
},
|
||||
filter: nil,
|
||||
want: map[string][]byte{
|
||||
".git/config": nil,
|
||||
"file.jpg": []byte(`contents`),
|
||||
"manifest.yaml": nil,
|
||||
want: map[string]dummyFile{
|
||||
".git/config": {},
|
||||
"file.jpg": {content: []byte(`contents`)},
|
||||
"manifest.yaml": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exclude VCS",
|
||||
files: map[string][]byte{
|
||||
".git/config": nil,
|
||||
"manifest.yaml": nil,
|
||||
files: map[string]dummyFile{
|
||||
".git/config": {},
|
||||
"manifest.yaml": {},
|
||||
},
|
||||
wantDirs: []string{
|
||||
"!.git",
|
||||
},
|
||||
filter: SourceIgnoreFilter(nil, nil),
|
||||
want: map[string][]byte{
|
||||
"!.git/config": nil,
|
||||
"manifest.yaml": nil,
|
||||
want: map[string]dummyFile{
|
||||
"!.git/config": {},
|
||||
"manifest.yaml": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom",
|
||||
files: map[string][]byte{
|
||||
".git/config": nil,
|
||||
"custom": nil,
|
||||
"horse.jpg": nil,
|
||||
files: map[string]dummyFile{
|
||||
".git/config": {},
|
||||
"custom": {},
|
||||
"horse.jpg": {},
|
||||
},
|
||||
filter: SourceIgnoreFilter([]gitignore.Pattern{
|
||||
gitignore.ParsePattern("custom", nil),
|
||||
}, nil),
|
||||
want: map[string][]byte{
|
||||
"!git/config": nil,
|
||||
"!custom": nil,
|
||||
"horse.jpg": nil,
|
||||
want: map[string]dummyFile{
|
||||
"!git/config": {},
|
||||
"!custom": {},
|
||||
"horse.jpg": {},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "including directories",
|
||||
files: map[string][]byte{
|
||||
"test/.gitkeep": nil,
|
||||
files: map[string]dummyFile{
|
||||
"test/.gitkeep": {},
|
||||
},
|
||||
filter: SourceIgnoreFilter([]gitignore.Pattern{
|
||||
gitignore.ParsePattern("custom", nil),
|
||||
|
@ -242,6 +257,26 @@ func TestStorage_Archive(t *testing.T) {
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "sets default file modes",
|
||||
files: map[string]dummyFile{
|
||||
"test/file": {
|
||||
mode: 0o666,
|
||||
},
|
||||
"test/executable": {
|
||||
mode: 0o777,
|
||||
},
|
||||
},
|
||||
want: map[string]dummyFile{
|
||||
"test/file": {
|
||||
mode: defaultFileMode,
|
||||
},
|
||||
"test/executable": {
|
||||
mode: defaultExeFileMode,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue