Skip to content

Commit 7ac233b

Browse files
author
Mahmood Ali
authored
Merge pull request #256 from hashicorp/f-umask-master-may-2020
umask final version against master (again)
2 parents 437601e + 142d79c commit 7ac233b

22 files changed

+228
-159
lines changed

client.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ type Client struct {
3939
// for documentation.
4040
Mode ClientMode
4141

42+
// Umask is used to mask file permissions when storing local files or decompressing
43+
// an archive
44+
Umask os.FileMode
45+
4246
// Detectors is the list of detectors that are tried on the source.
4347
// If this is nil, then the default Detectors will be used.
4448
Detectors []Detector
@@ -66,6 +70,20 @@ type Client struct {
6670
Options []ClientOption
6771
}
6872

73+
// umask returns the effective umask for the Client, defaulting to the process umask
74+
func (c *Client) umask() os.FileMode {
75+
if c == nil {
76+
return 0
77+
}
78+
return c.Umask
79+
}
80+
81+
// mode returns file mode umasked by the Client umask
82+
func (c *Client) mode(mode os.FileMode) os.FileMode {
83+
m := mode & ^c.umask()
84+
return m
85+
}
86+
6987
// Get downloads the configured source to the destination.
7088
func (c *Client) Get() error {
7189
if err := c.Configure(c.Options...); err != nil {
@@ -233,7 +251,7 @@ func (c *Client) Get() error {
233251
if decompressor != nil {
234252
// We have a decompressor, so decompress the current destination
235253
// into the final destination with the proper mode.
236-
err := decompressor.Decompress(decompressDst, dst, decompressDir)
254+
err := decompressor.Decompress(decompressDst, dst, decompressDir, c.umask())
237255
if err != nil {
238256
return err
239257
}
@@ -281,7 +299,7 @@ func (c *Client) Get() error {
281299
if err := os.RemoveAll(realDst); err != nil {
282300
return err
283301
}
284-
if err := os.MkdirAll(realDst, 0755); err != nil {
302+
if err := os.MkdirAll(realDst, c.mode(0755)); err != nil {
285303
return err
286304
}
287305

@@ -291,7 +309,7 @@ func (c *Client) Get() error {
291309
return err
292310
}
293311

294-
return copyDir(c.Ctx, realDst, subDir, false)
312+
return copyDir(c.Ctx, realDst, subDir, false, c.umask())
295313
}
296314

297315
return nil

copy_dir.go

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ import (
77
"strings"
88
)
99

10+
// mode returns the file mode masked by the umask
11+
func mode(mode, umask os.FileMode) os.FileMode {
12+
return mode & ^umask
13+
}
14+
1015
// copyDir copies the src directory contents into dst. Both directories
1116
// should already exist.
1217
//
1318
// If ignoreDot is set to true, then dot-prefixed files/folders are ignored.
14-
func copyDir(ctx context.Context, dst string, src string, ignoreDot bool) error {
19+
func copyDir(ctx context.Context, dst string, src string, ignoreDot bool, umask os.FileMode) error {
1520
src, err := filepath.EvalSymlinks(src)
1621
if err != nil {
1722
return err
@@ -46,32 +51,16 @@ func copyDir(ctx context.Context, dst string, src string, ignoreDot bool) error
4651
return nil
4752
}
4853

49-
if err := os.MkdirAll(dstPath, 0755); err != nil {
54+
if err := os.MkdirAll(dstPath, mode(0755, umask)); err != nil {
5055
return err
5156
}
5257

5358
return nil
5459
}
5560

5661
// If we have a file, copy the contents.
57-
srcF, err := os.Open(path)
58-
if err != nil {
59-
return err
60-
}
61-
defer srcF.Close()
62-
63-
dstF, err := os.Create(dstPath)
64-
if err != nil {
65-
return err
66-
}
67-
defer dstF.Close()
68-
69-
if _, err := Copy(ctx, dstF, srcF); err != nil {
70-
return err
71-
}
72-
73-
// Chmod it
74-
return os.Chmod(dstPath, info.Mode())
62+
_, err = copyFile(ctx, dstPath, path, info.Mode(), umask)
63+
return err
7564
}
7665

7766
return filepath.Walk(src, walkFn)

decompress.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package getter
22

33
import (
4+
"os"
45
"strings"
56
)
67

@@ -14,7 +15,7 @@ type Decompressor interface {
1415
// Decompress should decompress src to dst. dir specifies whether dst
1516
// is a directory or single file. src is guaranteed to be a single file
1617
// that exists. dst is not guaranteed to exist already.
17-
Decompress(dst, src string, dir bool) error
18+
Decompress(dst, src string, dir bool, umask os.FileMode) error
1819
}
1920

2021
// Decompressors is the mapping of extension to the Decompressor implementation

decompress_bzip2.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package getter
33
import (
44
"compress/bzip2"
55
"fmt"
6-
"io"
76
"os"
87
"path/filepath"
98
)
@@ -12,14 +11,14 @@ import (
1211
// decompress bz2 files.
1312
type Bzip2Decompressor struct{}
1413

15-
func (d *Bzip2Decompressor) Decompress(dst, src string, dir bool) error {
14+
func (d *Bzip2Decompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
1615
// Directory isn't supported at all
1716
if dir {
1817
return fmt.Errorf("bzip2-compressed files can only unarchive to a single file")
1918
}
2019

2120
// If we're going into a directory we should make that first
22-
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
21+
if err := os.MkdirAll(filepath.Dir(dst), mode(0755, umask)); err != nil {
2322
return err
2423
}
2524

@@ -34,12 +33,5 @@ func (d *Bzip2Decompressor) Decompress(dst, src string, dir bool) error {
3433
bzipR := bzip2.NewReader(f)
3534

3635
// Copy it out
37-
dstF, err := os.Create(dst)
38-
if err != nil {
39-
return err
40-
}
41-
defer dstF.Close()
42-
43-
_, err = io.Copy(dstF, bzipR)
44-
return err
36+
return copyReader(dst, bzipR, 0622, umask)
4537
}

decompress_gzip.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package getter
33
import (
44
"compress/gzip"
55
"fmt"
6-
"io"
76
"os"
87
"path/filepath"
98
)
@@ -12,14 +11,14 @@ import (
1211
// decompress gzip files.
1312
type GzipDecompressor struct{}
1413

15-
func (d *GzipDecompressor) Decompress(dst, src string, dir bool) error {
14+
func (d *GzipDecompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
1615
// Directory isn't supported at all
1716
if dir {
1817
return fmt.Errorf("gzip-compressed files can only unarchive to a single file")
1918
}
2019

2120
// If we're going into a directory we should make that first
22-
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
21+
if err := os.MkdirAll(filepath.Dir(dst), mode(0755, umask)); err != nil {
2322
return err
2423
}
2524

@@ -38,12 +37,5 @@ func (d *GzipDecompressor) Decompress(dst, src string, dir bool) error {
3837
defer gzipR.Close()
3938

4039
// Copy it out
41-
dstF, err := os.Create(dst)
42-
if err != nil {
43-
return err
44-
}
45-
defer dstF.Close()
46-
47-
_, err = io.Copy(dstF, gzipR)
48-
return err
40+
return copyReader(dst, gzipR, 0622, umask)
4941
}

decompress_tar.go

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
// untar is a shared helper for untarring an archive. The reader should provide
1313
// an uncompressed view of the tar archive.
14-
func untar(input io.Reader, dst, src string, dir bool) error {
14+
func untar(input io.Reader, dst, src string, dir bool, umask os.FileMode) error {
1515
tarR := tar.NewReader(input)
1616
done := false
1717
dirHdrs := []*tar.Header{}
@@ -51,7 +51,7 @@ func untar(input io.Reader, dst, src string, dir bool) error {
5151
}
5252

5353
// A directory, just make the directory and continue unarchiving...
54-
if err := os.MkdirAll(path, 0755); err != nil {
54+
if err := os.MkdirAll(path, mode(0755, umask)); err != nil {
5555
return err
5656
}
5757

@@ -67,7 +67,7 @@ func untar(input io.Reader, dst, src string, dir bool) error {
6767

6868
// Check that the directory exists, otherwise create it
6969
if _, err := os.Stat(dstPath); os.IsNotExist(err) {
70-
if err := os.MkdirAll(dstPath, 0755); err != nil {
70+
if err := os.MkdirAll(dstPath, mode(0755, umask)); err != nil {
7171
return err
7272
}
7373
}
@@ -82,20 +82,10 @@ func untar(input io.Reader, dst, src string, dir bool) error {
8282
done = true
8383

8484
// Open the file for writing
85-
dstF, err := os.Create(path)
85+
err = copyReader(path, tarR, hdr.FileInfo().Mode(), umask)
8686
if err != nil {
8787
return err
8888
}
89-
_, err = io.Copy(dstF, tarR)
90-
dstF.Close()
91-
if err != nil {
92-
return err
93-
}
94-
95-
// Chmod the file
96-
if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
97-
return err
98-
}
9989

10090
// Set the access and modification time if valid, otherwise default to current time
10191
aTime := now
@@ -115,7 +105,7 @@ func untar(input io.Reader, dst, src string, dir bool) error {
115105
for _, dirHdr := range dirHdrs {
116106
path := filepath.Join(dst, dirHdr.Name)
117107
// Chmod the directory since they might be created before we know the mode flags
118-
if err := os.Chmod(path, dirHdr.FileInfo().Mode()); err != nil {
108+
if err := os.Chmod(path, mode(dirHdr.FileInfo().Mode(), umask)); err != nil {
119109
return err
120110
}
121111
// Set the mtime/atime attributes since they would have been changed during extraction
@@ -139,13 +129,13 @@ func untar(input io.Reader, dst, src string, dir bool) error {
139129
// unpack tar files.
140130
type tarDecompressor struct{}
141131

142-
func (d *tarDecompressor) Decompress(dst, src string, dir bool) error {
132+
func (d *tarDecompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
143133
// If we're going into a directory we should make that first
144134
mkdir := dst
145135
if !dir {
146136
mkdir = filepath.Dir(dst)
147137
}
148-
if err := os.MkdirAll(mkdir, 0755); err != nil {
138+
if err := os.MkdirAll(mkdir, mode(0755, umask)); err != nil {
149139
return err
150140
}
151141

@@ -156,5 +146,5 @@ func (d *tarDecompressor) Decompress(dst, src string, dir bool) error {
156146
}
157147
defer f.Close()
158148

159-
return untar(f, dst, src, dir)
149+
return untar(f, dst, src, dir, umask)
160150
}

decompress_tar_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package getter
22

33
import (
4+
"io/ioutil"
5+
"os"
46
"path/filepath"
7+
"runtime"
58
"testing"
69
"time"
710
)
@@ -41,3 +44,64 @@ func TestTar(t *testing.T) {
4144

4245
TestDecompressor(t, new(tarDecompressor), cases)
4346
}
47+
48+
// testDecompressPermissions decompresses a directory and checks the permissions of the expanded files
49+
func testDecompressorPermissions(t *testing.T, d Decompressor, input string, expected map[string]int, umask os.FileMode) {
50+
td, err := ioutil.TempDir("", "getter")
51+
if err != nil {
52+
t.Fatalf("err: %s", err)
53+
}
54+
55+
// Destination is always joining result so that we have a new path
56+
dst := filepath.Join(td, "subdir", "result")
57+
58+
err = d.Decompress(dst, input, true, umask)
59+
if err != nil {
60+
t.Fatalf("err: %s", err)
61+
}
62+
63+
defer os.RemoveAll(dst)
64+
65+
for name, mode := range expected {
66+
fi, err := os.Stat(filepath.Join(dst, name))
67+
if err != nil {
68+
t.Fatalf("err: %s", err)
69+
}
70+
71+
real := fi.Mode()
72+
if real != os.FileMode(mode) {
73+
t.Fatalf("err: %s expected mode %o got %o", name, mode, real)
74+
}
75+
}
76+
}
77+
78+
func TestDecompressTarPermissions(t *testing.T) {
79+
d := new(tarDecompressor)
80+
input := "./test-fixtures/decompress-tar/permissions.tar"
81+
82+
var expected map[string]int
83+
var masked int
84+
85+
if runtime.GOOS == "windows" {
86+
expected = map[string]int{
87+
"directory/public": 0666,
88+
"directory/private": 0666,
89+
"directory/exec": 0666,
90+
"directory/setuid": 0666,
91+
}
92+
masked = 0666
93+
} else {
94+
expected = map[string]int{
95+
"directory/public": 0666,
96+
"directory/private": 0600,
97+
"directory/exec": 0755,
98+
"directory/setuid": 040000755,
99+
}
100+
masked = 0755
101+
}
102+
103+
testDecompressorPermissions(t, d, input, expected, os.FileMode(0))
104+
105+
expected["directory/setuid"] = masked
106+
testDecompressorPermissions(t, d, input, expected, os.FileMode(060000000))
107+
}

decompress_tbz2.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import (
1010
// decompress tar.bz2 files.
1111
type TarBzip2Decompressor struct{}
1212

13-
func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool) error {
13+
func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
1414
// If we're going into a directory we should make that first
1515
mkdir := dst
1616
if !dir {
1717
mkdir = filepath.Dir(dst)
1818
}
19-
if err := os.MkdirAll(mkdir, 0755); err != nil {
19+
if err := os.MkdirAll(mkdir, mode(0755, umask)); err != nil {
2020
return err
2121
}
2222

@@ -29,5 +29,5 @@ func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool) error {
2929

3030
// Bzip2 compression is second
3131
bzipR := bzip2.NewReader(f)
32-
return untar(bzipR, dst, src, dir)
32+
return untar(bzipR, dst, src, dir, umask)
3333
}

decompress_testing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
4747
defer os.RemoveAll(td)
4848

4949
// Decompress
50-
err := d.Decompress(dst, tc.Input, tc.Dir)
50+
err := d.Decompress(dst, tc.Input, tc.Dir, 0022)
5151
if (err != nil) != tc.Err {
5252
t.Fatalf("err %s: %s", tc.Input, err)
5353
}

0 commit comments

Comments
 (0)