From 20e15e52a9d79c5ceb3b9008ca7bb16bc13c0853 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 31 Mar 2021 14:48:02 +0100 Subject: [PATCH] vfs: fix Create causing windows explorer to truncate files on CTRL-C CTRL-V Before this fix, doing CTRL-C and CTRL-V on a file in Windows explorer caused the **source** and the the destination to be truncated to 0. This is because Windows opens the source file with Create with flags `O_RDWR|O_CREATE|O_EXCL` but doesn't write to it - it only reads from it. Rclone was taking the call to Create as a signal to always make a new file, but this is incorrect. This fix reads an existing file from the directory if it exists when Create is called rather than always creating a new one. This fixes the problem. Fixes #5181 --- vfs/dir.go | 17 +++++++++++++++++ vfs/dir_test.go | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/vfs/dir.go b/vfs/dir.go index 79a8d6537..755fde807 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -843,6 +843,23 @@ func (d *Dir) Open(flags int) (fd Handle, err error) { // Create makes a new file node func (d *Dir) Create(name string, flags int) (*File, error) { // fs.Debugf(path, "Dir.Create") + // Return existing node if one exists + node, err := d.stat(name) + switch err { + case ENOENT: + // not found, carry on + case nil: + // found so check what it is + if node.IsFile() { + return node.(*File), err + } + return nil, EEXIST // EISDIR would be better but we don't have that + default: + // a different error - report + fs.Errorf(d, "Dir.Create stat failed: %v", err) + return nil, err + } + // node doesn't exist so create it if d.vfs.Opt.ReadOnly { return nil, EROFS } diff --git a/vfs/dir_test.go b/vfs/dir_test.go index 06c23cdc2..93c35c00d 100644 --- a/vfs/dir_test.go +++ b/vfs/dir_test.go @@ -375,6 +375,13 @@ func TestDirCreate(t *testing.T) { require.NoError(t, err) assert.Equal(t, int64(5), file2.Size()) + // Try creating the file again - make sure we get the same file node + file3, err := dir.Create("potato", os.O_RDWR|os.O_CREATE) + require.NoError(t, err) + assert.Equal(t, int64(5), file3.Size()) + assert.Equal(t, fmt.Sprintf("%p", file), fmt.Sprintf("%p", file3), "didn't return same node") + + // Test read only fs creating new vfs.Opt.ReadOnly = true _, err = dir.Create("sausage", os.O_WRONLY|os.O_CREATE) assert.Equal(t, EROFS, err)