mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
87
pkg/build/fsutil/copy_test.go
Normal file
87
pkg/build/fsutil/copy_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package fsutil_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
src, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
if err := os.RemoveAll(src.Name()); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = os.WriteFile(src.Name(), []byte("Contents"), 0600)
|
||||
require.NoError(t, err)
|
||||
|
||||
dst, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
if err := os.RemoveAll(dst.Name()); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = fsutil.CopyFile(src.Name(), dst.Name())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCopyFile_Permissions(t *testing.T) {
|
||||
perms := os.FileMode(0700)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows doesn't have file Unix style file permissions
|
||||
// It seems you have either 0444 for read-only or 0666 for read-write
|
||||
perms = os.FileMode(0666)
|
||||
}
|
||||
|
||||
src, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
if err := os.RemoveAll(src.Name()); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = os.WriteFile(src.Name(), []byte("Contents"), perms)
|
||||
require.NoError(t, err)
|
||||
err = os.Chmod(src.Name(), perms)
|
||||
require.NoError(t, err)
|
||||
|
||||
dst, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
if err := os.RemoveAll(dst.Name()); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = fsutil.CopyFile(src.Name(), dst.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
fi, err := os.Stat(dst.Name())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, perms, fi.Mode()&os.ModePerm)
|
||||
}
|
||||
|
||||
// Test case where destination directory doesn't exist.
|
||||
func TestCopyFile_NonExistentDestDir(t *testing.T) {
|
||||
src, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
if err := os.RemoveAll(src.Name()); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = fsutil.CopyFile(src.Name(), "non-existent/dest")
|
||||
require.EqualError(t, err, "destination directory doesn't exist: \"non-existent\"")
|
||||
}
|
||||
107
pkg/build/fsutil/copyfile.go
Normal file
107
pkg/build/fsutil/copyfile.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package fsutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CopyFile copies a file from src to dst.
|
||||
//
|
||||
// If src and dst files exist, and are the same, then return success. Otherwise, attempt to create a hard link
|
||||
// between the two files. If that fails, copy the file contents from src to dst.
|
||||
func CopyFile(src, dst string) (err error) {
|
||||
absSrc, err := filepath.Abs(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get absolute path of source file %q: %w", src, err)
|
||||
}
|
||||
sfi, err := os.Stat(src)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("couldn't stat source file %q: %w", absSrc, err)
|
||||
return
|
||||
}
|
||||
if !sfi.Mode().IsRegular() {
|
||||
// Cannot copy non-regular files (e.g., directories, symlinks, devices, etc.)
|
||||
return fmt.Errorf("non-regular source file %s (%q)", absSrc, sfi.Mode().String())
|
||||
}
|
||||
dpath := filepath.Dir(dst)
|
||||
exists, err := Exists(dpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
err = fmt.Errorf("destination directory doesn't exist: %q", dpath)
|
||||
return
|
||||
}
|
||||
|
||||
var dfi os.FileInfo
|
||||
dfi, err = os.Stat(dst)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !(dfi.Mode().IsRegular()) {
|
||||
return fmt.Errorf("non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
|
||||
}
|
||||
if os.SameFile(sfi, dfi) {
|
||||
return copyPermissions(sfi.Name(), dfi.Name())
|
||||
}
|
||||
}
|
||||
|
||||
err = copyFileContents(src, dst)
|
||||
return err
|
||||
}
|
||||
|
||||
// copyFileContents copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFileContents(src, dst string) (err error) {
|
||||
//nolint:gosec
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := in.Close(); err != nil {
|
||||
log.Println("error closing file", err)
|
||||
}
|
||||
}()
|
||||
|
||||
//nolint:gosec
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if cerr := out.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := out.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyPermissions(src, dst)
|
||||
}
|
||||
|
||||
func copyPermissions(src, dst string) error {
|
||||
sfi, err := os.Lstat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(dst, sfi.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
15
pkg/build/fsutil/exists_test.go
Normal file
15
pkg/build/fsutil/exists_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package fsutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExists_NonExistent(t *testing.T) {
|
||||
exists, err := fsutil.Exists("non-existent")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.False(t, exists)
|
||||
}
|
||||
16
pkg/build/fsutil/exsits.go
Normal file
16
pkg/build/fsutil/exsits.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package fsutil
|
||||
|
||||
import "os"
|
||||
|
||||
// Exists determines whether a file/directory exists or not.
|
||||
func Exists(fpath string) (bool, error) {
|
||||
_, err := os.Stat(fpath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user