From 1d170a11c0965836f39fb2a99f5c6917d80c5bca Mon Sep 17 00:00:00 2001 From: yahavi Date: Thu, 18 Jul 2024 18:14:25 +0300 Subject: [PATCH] Allow moving file to a destination without write permissions --- io/fileutils.go | 25 +++++++++++++++-- io/fileutils_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/io/fileutils.go b/io/fileutils.go index 59c467e..8538646 100644 --- a/io/fileutils.go +++ b/io/fileutils.go @@ -17,6 +17,8 @@ import ( "strconv" "strings" "time" + + "github.com/jfrog/gofrog/log" ) const ( @@ -325,8 +327,7 @@ func MoveFile(sourcePath, destPath string) (err error) { return } - var outputFile *os.File - outputFile, err = os.Create(destPath) + outputFile, err := createFileForWriting(destPath) if err != nil { return } @@ -353,6 +354,26 @@ func MoveFile(sourcePath, destPath string) (err error) { return } +// Create file for writing. If file already exists, it will be truncated. +// If the file exists and is read-only, the function will try to change the file permissions to read-write. +// The caller should update the permissions and close the file when done. +func createFileForWriting(destPath string) (outputFile *os.File, err error) { + // Try to create the destination file + outputFile, err = os.Create(destPath) + if err == nil { + return + } + + log.Debug("Couldn't open the destination file: '" + destPath + "'. Trying to set the file permissions to read-write.") + if err = os.Chmod(destPath, 0600); err != nil { + return + } + + // Try to open the destination file again + outputFile, err = os.Create(destPath) + return +} + // Return the list of files and directories in the specified path func ListFiles(path string, includeDirs bool) ([]string, error) { sep := GetFileSeparator() diff --git a/io/fileutils_test.go b/io/fileutils_test.go index 3e356b7..192f618 100644 --- a/io/fileutils_test.go +++ b/io/fileutils_test.go @@ -93,3 +93,70 @@ func TestCreateTempDir(t *testing.T) { assert.NoError(t, os.RemoveAll(tempDir)) }() } + +func TestMoveFile_New(t *testing.T) { + // Init test + sourcePath, destPath := initMoveTest(t) + + // Move file + assert.NoError(t, MoveFile(sourcePath, destPath)) + + // Assert expected file paths + assert.FileExists(t, destPath) + assert.NoFileExists(t, sourcePath) +} + +func TestMoveFile_Override(t *testing.T) { + // Init test + sourcePath, destPath := initMoveTest(t) + err := os.WriteFile(destPath, []byte("dst"), os.ModePerm) + assert.NoError(t, err) + + // Move file + assert.NoError(t, MoveFile(sourcePath, destPath)) + + // Assert file overidden + assert.FileExists(t, destPath) + destFileContent, err := os.ReadFile(destPath) + assert.NoError(t, err) + assert.Equal(t, "src", string(destFileContent)) + + // Assert source file removed + assert.NoFileExists(t, sourcePath) +} + +func TestMoveFile_NoPerm(t *testing.T) { + // Init test + sourcePath, destPath := initMoveTest(t) + err := os.WriteFile(destPath, []byte("dst"), os.ModePerm) + assert.NoError(t, err) + + // Remove all permissions from destination file + assert.NoError(t, os.Chmod(destPath, 0000)) + _, err = os.Create(destPath) + assert.ErrorContains(t, err, "permission") + + // Move file + assert.NoError(t, MoveFile(sourcePath, destPath)) + + // Assert file overidden + assert.FileExists(t, destPath) + destFileContent, err := os.ReadFile(destPath) + assert.NoError(t, err) + assert.Equal(t, "src", string(destFileContent)) + + // Assert source file removed + assert.NoFileExists(t, sourcePath) +} + +func initMoveTest(t *testing.T) (sourcePath, destPath string) { + // Create source and destination paths + tmpDir := t.TempDir() + sourcePath = filepath.Join(tmpDir, "src") + destPath = filepath.Join(tmpDir, "dst") + + // Write content to source file + err := os.WriteFile(sourcePath, []byte("src"), os.ModePerm) + assert.NoError(t, err) + return +}