Skip to content

Commit fcc6ec3

Browse files
authored
Merge pull request #1094 from rashedkvm/executable-permission
storage: set `0o744` for files with exec mode set
2 parents 8d9b0f4 + 2736b74 commit fcc6ec3

File tree

2 files changed

+113
-50
lines changed

2 files changed

+113
-50
lines changed

internal/controller/storage.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ import (
4848
const GarbageCountLimit = 1000
4949

5050
const (
51-
// defaultFileMode is the permission mode applied to all files inside an artifact archive.
51+
// defaultFileMode is the permission mode applied to files inside an artifact archive.
5252
defaultFileMode int64 = 0o644
5353
// defaultDirMode is the permission mode applied to all directories inside an artifact archive.
5454
defaultDirMode int64 = 0o755
55+
// defaultExeFileMode is the permission mode applied to executable files inside an artifact archive.
56+
defaultExeFileMode int64 = 0o744
5557
)
5658

5759
// Storage manages artifacts
@@ -424,6 +426,7 @@ func (s Storage) Archive(artifact *v1.Artifact, dir string, filter ArchiveFileFi
424426
if err != nil {
425427
return err
426428
}
429+
427430
// The name needs to be modified to maintain directory structure
428431
// as tar.FileInfoHeader only has access to the base name of the file.
429432
// Ref: https://golang.org/src/archive/tar/common.go?#L626
@@ -434,21 +437,7 @@ func (s Storage) Archive(artifact *v1.Artifact, dir string, filter ArchiveFileFi
434437
return err
435438
}
436439
}
437-
header.Name = relFilePath
438-
439-
// We want to remove any environment specific data as well, this
440-
// ensures the checksum is purely content based.
441-
header.Gid = 0
442-
header.Uid = 0
443-
header.Uname = ""
444-
header.Gname = ""
445-
header.ModTime = time.Time{}
446-
header.AccessTime = time.Time{}
447-
header.ChangeTime = time.Time{}
448-
header.Mode = defaultFileMode
449-
if fi.Mode().IsDir() {
450-
header.Mode = defaultDirMode
451-
}
440+
sanitizeHeader(relFilePath, header)
452441

453442
if err := tw.WriteHeader(header); err != nil {
454443
return err
@@ -689,3 +678,42 @@ func (wc *writeCounter) Write(p []byte) (int, error) {
689678
wc.written += int64(n)
690679
return n, nil
691680
}
681+
682+
// sanitizeHeader modifies the tar.Header to be relative to the root of the
683+
// archive and removes any environment specific data.
684+
func sanitizeHeader(relP string, h *tar.Header) {
685+
// Modify the name to be relative to the root of the archive,
686+
// this ensures we maintain the same structure when extracting.
687+
h.Name = relP
688+
689+
// We want to remove any environment specific data as well, this
690+
// ensures the checksum is purely content based.
691+
h.Gid = 0
692+
h.Uid = 0
693+
h.Uname = ""
694+
h.Gname = ""
695+
h.ModTime = time.Time{}
696+
h.AccessTime = time.Time{}
697+
h.ChangeTime = time.Time{}
698+
699+
// Override the mode to be the default for the type of file.
700+
setDefaultMode(h)
701+
}
702+
703+
// setDefaultMode sets the default mode for the given header.
704+
func setDefaultMode(h *tar.Header) {
705+
if h.FileInfo().IsDir() {
706+
h.Mode = defaultDirMode
707+
return
708+
}
709+
710+
if h.FileInfo().Mode().IsRegular() {
711+
mode := h.FileInfo().Mode()
712+
if mode&os.ModeType == 0 && mode&0o111 != 0 {
713+
h.Mode = defaultExeFileMode
714+
return
715+
}
716+
h.Mode = defaultFileMode
717+
return
718+
}
719+
}

internal/controller/storage_test.go

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,14 @@ func TestStorage_Archive(t *testing.T) {
109109
t.Fatalf("error while bootstrapping storage: %v", err)
110110
}
111111

112-
createFiles := func(files map[string][]byte) (dir string, err error) {
112+
type dummyFile struct {
113+
content []byte
114+
mode int64
115+
}
116+
117+
createFiles := func(files map[string]dummyFile) (dir string, err error) {
113118
dir = t.TempDir()
114-
for name, b := range files {
119+
for name, df := range files {
115120
absPath := filepath.Join(dir, name)
116121
if err = os.MkdirAll(filepath.Dir(absPath), 0o750); err != nil {
117122
return
@@ -120,18 +125,24 @@ func TestStorage_Archive(t *testing.T) {
120125
if err != nil {
121126
return "", fmt.Errorf("could not create file %q: %w", absPath, err)
122127
}
123-
if n, err := f.Write(b); err != nil {
128+
if n, err := f.Write(df.content); err != nil {
124129
f.Close()
125130
return "", fmt.Errorf("could not write %d bytes to file %q: %w", n, f.Name(), err)
126131
}
127132
f.Close()
133+
134+
if df.mode != 0 {
135+
if err = os.Chmod(absPath, os.FileMode(df.mode)); err != nil {
136+
return "", fmt.Errorf("could not chmod file %q: %w", absPath, err)
137+
}
138+
}
128139
}
129140
return
130141
}
131142

132-
matchFiles := func(t *testing.T, storage *Storage, artifact sourcev1.Artifact, files map[string][]byte, dirs []string) {
143+
matchFiles := func(t *testing.T, storage *Storage, artifact sourcev1.Artifact, files map[string]dummyFile, dirs []string) {
133144
t.Helper()
134-
for name, b := range files {
145+
for name, df := range files {
135146
mustExist := !(name[0:1] == "!")
136147
if !mustExist {
137148
name = name[1:]
@@ -140,7 +151,7 @@ func TestStorage_Archive(t *testing.T) {
140151
if err != nil {
141152
t.Fatalf("failed reading tarball: %v", err)
142153
}
143-
if bs := int64(len(b)); s != bs {
154+
if bs := int64(len(df.content)); s != bs {
144155
t.Fatalf("%q size %v != %v", name, s, bs)
145156
}
146157
if exist != mustExist {
@@ -150,8 +161,12 @@ func TestStorage_Archive(t *testing.T) {
150161
t.Errorf("tarball contained excluded file %q", name)
151162
}
152163
}
153-
if exist && m != defaultFileMode {
154-
t.Fatalf("%q mode %v != %v", name, m, defaultFileMode)
164+
expectMode := df.mode
165+
if expectMode == 0 {
166+
expectMode = defaultFileMode
167+
}
168+
if exist && m != expectMode {
169+
t.Fatalf("%q mode %v != %v", name, m, expectMode)
155170
}
156171
}
157172
for _, name := range dirs {
@@ -179,62 +194,62 @@ func TestStorage_Archive(t *testing.T) {
179194

180195
tests := []struct {
181196
name string
182-
files map[string][]byte
197+
files map[string]dummyFile
183198
filter ArchiveFileFilter
184-
want map[string][]byte
199+
want map[string]dummyFile
185200
wantDirs []string
186201
wantErr bool
187202
}{
188203
{
189204
name: "no filter",
190-
files: map[string][]byte{
191-
".git/config": nil,
192-
"file.jpg": []byte(`contents`),
193-
"manifest.yaml": nil,
205+
files: map[string]dummyFile{
206+
".git/config": {},
207+
"file.jpg": {content: []byte(`contents`)},
208+
"manifest.yaml": {},
194209
},
195210
filter: nil,
196-
want: map[string][]byte{
197-
".git/config": nil,
198-
"file.jpg": []byte(`contents`),
199-
"manifest.yaml": nil,
211+
want: map[string]dummyFile{
212+
".git/config": {},
213+
"file.jpg": {content: []byte(`contents`)},
214+
"manifest.yaml": {},
200215
},
201216
},
202217
{
203218
name: "exclude VCS",
204-
files: map[string][]byte{
205-
".git/config": nil,
206-
"manifest.yaml": nil,
219+
files: map[string]dummyFile{
220+
".git/config": {},
221+
"manifest.yaml": {},
207222
},
208223
wantDirs: []string{
209224
"!.git",
210225
},
211226
filter: SourceIgnoreFilter(nil, nil),
212-
want: map[string][]byte{
213-
"!.git/config": nil,
214-
"manifest.yaml": nil,
227+
want: map[string]dummyFile{
228+
"!.git/config": {},
229+
"manifest.yaml": {},
215230
},
216231
},
217232
{
218233
name: "custom",
219-
files: map[string][]byte{
220-
".git/config": nil,
221-
"custom": nil,
222-
"horse.jpg": nil,
234+
files: map[string]dummyFile{
235+
".git/config": {},
236+
"custom": {},
237+
"horse.jpg": {},
223238
},
224239
filter: SourceIgnoreFilter([]gitignore.Pattern{
225240
gitignore.ParsePattern("custom", nil),
226241
}, nil),
227-
want: map[string][]byte{
228-
"!git/config": nil,
229-
"!custom": nil,
230-
"horse.jpg": nil,
242+
want: map[string]dummyFile{
243+
"!git/config": {},
244+
"!custom": {},
245+
"horse.jpg": {},
231246
},
232247
wantErr: false,
233248
},
234249
{
235250
name: "including directories",
236-
files: map[string][]byte{
237-
"test/.gitkeep": nil,
251+
files: map[string]dummyFile{
252+
"test/.gitkeep": {},
238253
},
239254
filter: SourceIgnoreFilter([]gitignore.Pattern{
240255
gitignore.ParsePattern("custom", nil),
@@ -244,6 +259,26 @@ func TestStorage_Archive(t *testing.T) {
244259
},
245260
wantErr: false,
246261
},
262+
{
263+
name: "sets default file modes",
264+
files: map[string]dummyFile{
265+
"test/file": {
266+
mode: 0o666,
267+
},
268+
"test/executable": {
269+
mode: 0o777,
270+
},
271+
},
272+
want: map[string]dummyFile{
273+
"test/file": {
274+
mode: defaultFileMode,
275+
},
276+
"test/executable": {
277+
mode: defaultExeFileMode,
278+
},
279+
},
280+
wantErr: false,
281+
},
247282
}
248283
for _, tt := range tests {
249284
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)