Skip to content

Commit

Permalink
Allow moving file to a destination without write permissions (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi authored Jul 18, 2024
1 parent 2cc911a commit d7b53f2
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 2 deletions.
24 changes: 22 additions & 2 deletions io/fileutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"strconv"
"strings"
"time"

"github.com/jfrog/gofrog/log"
)

const (
Expand Down Expand Up @@ -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
}
Expand All @@ -353,6 +354,25 @@ 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) (*os.File, error) {
// Try to create the destination file
outputFile, err := os.Create(destPath)
if err == nil {
return outputFile, nil
}

log.Debug(fmt.Sprintf("Couldn't to open the destination file: '%s' due to %s. Attempting to set the file permissions to read-write.", destPath, err.Error()))
if chmodErr := os.Chmod(destPath, 0600); chmodErr != nil {
return nil, errors.Join(err, chmodErr)
}

// Try to open the destination file again
return os.Create(destPath)
}

// Return the list of files and directories in the specified path
func ListFiles(path string, includeDirs bool) ([]string, error) {
sep := GetFileSeparator()
Expand Down
67 changes: 67 additions & 0 deletions io/fileutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"), 0600)
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"), 0600)
assert.NoError(t, err)

// Remove all permissions from destination file
assert.NoError(t, os.Chmod(destPath, 0000))
_, err = os.Create(destPath)
assert.Error(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 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"), 0600)
assert.NoError(t, err)
return
}

0 comments on commit d7b53f2

Please sign in to comment.