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

umask final version against master #198

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ before_script:
branches:
only:
- master
- /^release-.*/
26 changes: 22 additions & 4 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
// Using a client directly allows more fine-grained control over how downloading
// is done, as well as customizing the protocols supported.
type Client struct {
// Ctx for cancellation
// Ctx for cancellation
Ctx context.Context

// Src is the source URL to get.
Expand All @@ -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))
}
Loading