From 79726b5d8e6ebc13d61e083a4f598d9356328e5e Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 13 Nov 2017 14:46:29 -0500 Subject: Replace os.Rename with directory copy util in plugin extraction (#7825) --- utils/file.go | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ utils/file_test.go | 54 ++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) (limited to 'utils') diff --git a/utils/file.go b/utils/file.go index c7e092a20..6472770a0 100644 --- a/utils/file.go +++ b/utils/file.go @@ -5,6 +5,8 @@ package utils import ( "bytes" + "fmt" + "io" "io/ioutil" "net/http" "os" @@ -367,3 +369,102 @@ func CopyMetadata(encrypt bool) map[string]string { metaData["x-amz-server-side-encryption"] = "AES256" return metaData } + +// CopyFile will copy a file from src path to dst path. +// Overwrites any existing files at dst. +// Permissions are copied from file at src to the new file at dst. +func CopyFile(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + if e := out.Close(); e != nil { + err = e + } + }() + + _, err = io.Copy(out, in) + if err != nil { + return + } + + err = out.Sync() + if err != nil { + return + } + + stat, err := os.Stat(src) + if err != nil { + return + } + err = os.Chmod(dst, stat.Mode()) + if err != nil { + return + } + + return +} + +// CopyDir will copy a directory and all contained files and directories. +// src must exist and dst must not exist. +// Permissions are preserved when possible. Symlinks are skipped. +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + stat, err := os.Stat(src) + if err != nil { + return + } + if !stat.IsDir() { + return fmt.Errorf("source must be a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = os.MkdirAll(dst, stat.Mode()) + if err != nil { + return + } + + items, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, item := range items { + srcPath := filepath.Join(src, item.Name()) + dstPath := filepath.Join(dst, item.Name()) + + if item.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + if item.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +} diff --git a/utils/file_test.go b/utils/file_test.go index 5c7162450..91e78f24e 100644 --- a/utils/file_test.go +++ b/utils/file_test.go @@ -5,9 +5,13 @@ package utils import ( "fmt" + "io/ioutil" "os" + "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/mattermost/mattermost-server/model" @@ -180,3 +184,53 @@ func (s *FileTestSuite) TestRemoveDirectory() { _, err = ReadFile("tests2/asdf") s.Error(err) } + +func TestCopyDir(t *testing.T) { + srcDir, err := ioutil.TempDir("", "src") + require.NoError(t, err) + defer os.RemoveAll(srcDir) + + dstParentDir, err := ioutil.TempDir("", "dstparent") + require.NoError(t, err) + defer os.RemoveAll(dstParentDir) + + dstDir := filepath.Join(dstParentDir, "dst") + + tempFile := "temp.txt" + err = ioutil.WriteFile(filepath.Join(srcDir, tempFile), []byte("test file"), 0655) + require.NoError(t, err) + + childDir := "child" + err = os.Mkdir(filepath.Join(srcDir, childDir), 0777) + require.NoError(t, err) + + childTempFile := "childtemp.txt" + err = ioutil.WriteFile(filepath.Join(srcDir, childDir, childTempFile), []byte("test file"), 0755) + require.NoError(t, err) + + err = CopyDir(srcDir, dstDir) + assert.NoError(t, err) + + stat, err := os.Stat(filepath.Join(dstDir, tempFile)) + assert.NoError(t, err) + assert.Equal(t, uint32(0655), uint32(stat.Mode())) + assert.False(t, stat.IsDir()) + data, err := ioutil.ReadFile(filepath.Join(dstDir, tempFile)) + assert.NoError(t, err) + assert.Equal(t, "test file", string(data)) + + stat, err = os.Stat(filepath.Join(dstDir, childDir)) + assert.NoError(t, err) + assert.True(t, stat.IsDir()) + + stat, err = os.Stat(filepath.Join(dstDir, childDir, childTempFile)) + assert.NoError(t, err) + assert.Equal(t, uint32(0755), uint32(stat.Mode())) + assert.False(t, stat.IsDir()) + data, err = ioutil.ReadFile(filepath.Join(dstDir, childDir, childTempFile)) + assert.NoError(t, err) + assert.Equal(t, "test file", string(data)) + + err = CopyDir(srcDir, dstDir) + assert.Error(t, err) +} -- cgit v1.2.3-1-g7c22