Skip to content

Commit

Permalink
Merge pull request #256 from hashicorp/f-umask-master-may-2020
Browse files Browse the repository at this point in the history
umask final version against master (again)
  • Loading branch information
Mahmood Ali authored Sep 16, 2020
2 parents 437601e + 142d79c commit 7ac233b
Show file tree
Hide file tree
Showing 22 changed files with 228 additions and 159 deletions.
24 changes: 21 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type Client struct {
// for documentation.
Mode ClientMode

// Umask is used to mask file permissions when storing local files or decompressing
// an archive
Umask os.FileMode

// Detectors is the list of detectors that are tried on the source.
// If this is nil, then the default Detectors will be used.
Detectors []Detector
Expand Down Expand Up @@ -66,6 +70,20 @@ type Client struct {
Options []ClientOption
}

// umask returns the effective umask for the Client, defaulting to the process umask
func (c *Client) umask() os.FileMode {
if c == nil {
return 0
}
return c.Umask
}

// mode returns file mode umasked by the Client umask
func (c *Client) mode(mode os.FileMode) os.FileMode {
m := mode & ^c.umask()
return m
}

// Get downloads the configured source to the destination.
func (c *Client) Get() error {
if err := c.Configure(c.Options...); err != nil {
Expand Down Expand Up @@ -233,7 +251,7 @@ func (c *Client) Get() error {
if decompressor != nil {
// We have a decompressor, so decompress the current destination
// into the final destination with the proper mode.
err := decompressor.Decompress(decompressDst, dst, decompressDir)
err := decompressor.Decompress(decompressDst, dst, decompressDir, c.umask())
if err != nil {
return err
}
Expand Down Expand Up @@ -281,7 +299,7 @@ func (c *Client) Get() error {
if err := os.RemoveAll(realDst); err != nil {
return err
}
if err := os.MkdirAll(realDst, 0755); err != nil {
if err := os.MkdirAll(realDst, c.mode(0755)); err != nil {
return err
}

Expand All @@ -291,7 +309,7 @@ func (c *Client) Get() error {
return err
}

return copyDir(c.Ctx, realDst, subDir, false)
return copyDir(c.Ctx, realDst, subDir, false, c.umask())
}

return nil
Expand Down
29 changes: 9 additions & 20 deletions copy_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import (
"strings"
)

// mode returns the file mode masked by the umask
func mode(mode, umask os.FileMode) os.FileMode {
return mode & ^umask
}

// copyDir copies the src directory contents into dst. Both directories
// should already exist.
//
// If ignoreDot is set to true, then dot-prefixed files/folders are ignored.
func copyDir(ctx context.Context, dst string, src string, ignoreDot bool) error {
func copyDir(ctx context.Context, dst string, src string, ignoreDot bool, umask os.FileMode) error {
src, err := filepath.EvalSymlinks(src)
if err != nil {
return err
Expand Down Expand Up @@ -46,32 +51,16 @@ func copyDir(ctx context.Context, dst string, src string, ignoreDot bool) error
return nil
}

if err := os.MkdirAll(dstPath, 0755); err != nil {
if err := os.MkdirAll(dstPath, mode(0755, umask)); err != nil {
return err
}

return nil
}

// If we have a file, copy the contents.
srcF, err := os.Open(path)
if err != nil {
return err
}
defer srcF.Close()

dstF, err := os.Create(dstPath)
if err != nil {
return err
}
defer dstF.Close()

if _, err := Copy(ctx, dstF, srcF); err != nil {
return err
}

// Chmod it
return os.Chmod(dstPath, info.Mode())
_, err = copyFile(ctx, dstPath, path, info.Mode(), umask)
return err
}

return filepath.Walk(src, walkFn)
Expand Down
3 changes: 2 additions & 1 deletion decompress.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package getter

import (
"os"
"strings"
)

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

// Decompressors is the mapping of extension to the Decompressor implementation
Expand Down
14 changes: 3 additions & 11 deletions decompress_bzip2.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package getter
import (
"compress/bzip2"
"fmt"
"io"
"os"
"path/filepath"
)
Expand All @@ -12,14 +11,14 @@ import (
// decompress bz2 files.
type Bzip2Decompressor struct{}

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

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

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

// Copy it out
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()

_, err = io.Copy(dstF, bzipR)
return err
return copyReader(dst, bzipR, 0622, umask)
}
14 changes: 3 additions & 11 deletions decompress_gzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package getter
import (
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
)
Expand All @@ -12,14 +11,14 @@ import (
// decompress gzip files.
type GzipDecompressor struct{}

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

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

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

// Copy it out
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()

_, err = io.Copy(dstF, gzipR)
return err
return copyReader(dst, gzipR, 0622, umask)
}
26 changes: 8 additions & 18 deletions decompress_tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

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

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

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

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

// Open the file for writing
dstF, err := os.Create(path)
err = copyReader(path, tarR, hdr.FileInfo().Mode(), umask)
if err != nil {
return err
}
_, err = io.Copy(dstF, tarR)
dstF.Close()
if err != nil {
return err
}

// Chmod the file
if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil {
return err
}

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

func (d *tarDecompressor) Decompress(dst, src string, dir bool) error {
func (d *tarDecompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
// If we're going into a directory we should make that first
mkdir := dst
if !dir {
mkdir = filepath.Dir(dst)
}
if err := os.MkdirAll(mkdir, 0755); err != nil {
if err := os.MkdirAll(mkdir, mode(0755, umask)); err != nil {
return err
}

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

return untar(f, dst, src, dir)
return untar(f, dst, src, dir, umask)
}
64 changes: 64 additions & 0 deletions decompress_tar_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package getter

import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"
"time"
)
Expand Down Expand Up @@ -41,3 +44,64 @@ func TestTar(t *testing.T) {

TestDecompressor(t, new(tarDecompressor), cases)
}

// testDecompressPermissions decompresses a directory and checks the permissions of the expanded files
func testDecompressorPermissions(t *testing.T, d Decompressor, input string, expected map[string]int, umask os.FileMode) {
td, err := ioutil.TempDir("", "getter")
if err != nil {
t.Fatalf("err: %s", err)
}

// Destination is always joining result so that we have a new path
dst := filepath.Join(td, "subdir", "result")

err = d.Decompress(dst, input, true, umask)
if err != nil {
t.Fatalf("err: %s", err)
}

defer os.RemoveAll(dst)

for name, mode := range expected {
fi, err := os.Stat(filepath.Join(dst, name))
if err != nil {
t.Fatalf("err: %s", err)
}

real := fi.Mode()
if real != os.FileMode(mode) {
t.Fatalf("err: %s expected mode %o got %o", name, mode, real)
}
}
}

func TestDecompressTarPermissions(t *testing.T) {
d := new(tarDecompressor)
input := "./test-fixtures/decompress-tar/permissions.tar"

var expected map[string]int
var masked int

if runtime.GOOS == "windows" {
expected = map[string]int{
"directory/public": 0666,
"directory/private": 0666,
"directory/exec": 0666,
"directory/setuid": 0666,
}
masked = 0666
} else {
expected = map[string]int{
"directory/public": 0666,
"directory/private": 0600,
"directory/exec": 0755,
"directory/setuid": 040000755,
}
masked = 0755
}

testDecompressorPermissions(t, d, input, expected, os.FileMode(0))

expected["directory/setuid"] = masked
testDecompressorPermissions(t, d, input, expected, os.FileMode(060000000))
}
6 changes: 3 additions & 3 deletions decompress_tbz2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import (
// decompress tar.bz2 files.
type TarBzip2Decompressor struct{}

func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool) error {
func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool, umask os.FileMode) error {
// If we're going into a directory we should make that first
mkdir := dst
if !dir {
mkdir = filepath.Dir(dst)
}
if err := os.MkdirAll(mkdir, 0755); err != nil {
if err := os.MkdirAll(mkdir, mode(0755, umask)); err != nil {
return err
}

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

// Bzip2 compression is second
bzipR := bzip2.NewReader(f)
return untar(bzipR, dst, src, dir)
return untar(bzipR, dst, src, dir, umask)
}
2 changes: 1 addition & 1 deletion decompress_testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
defer os.RemoveAll(td)

// Decompress
err := d.Decompress(dst, tc.Input, tc.Dir)
err := d.Decompress(dst, tc.Input, tc.Dir, 0022)
if (err != nil) != tc.Err {
t.Fatalf("err %s: %s", tc.Input, err)
}
Expand Down
Loading

0 comments on commit 7ac233b

Please sign in to comment.