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
This commit is contained in:
Nick Craig-Wood 2021-03-31 14:48:02 +01:00
parent d0f8b4f479
commit 20e15e52a9
2 changed files with 24 additions and 0 deletions

View File

@ -843,6 +843,23 @@ func (d *Dir) Open(flags int) (fd Handle, err error) {
// Create makes a new file node // Create makes a new file node
func (d *Dir) Create(name string, flags int) (*File, error) { func (d *Dir) Create(name string, flags int) (*File, error) {
// fs.Debugf(path, "Dir.Create") // 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 { if d.vfs.Opt.ReadOnly {
return nil, EROFS return nil, EROFS
} }

View File

@ -375,6 +375,13 @@ func TestDirCreate(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, int64(5), file2.Size()) 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 vfs.Opt.ReadOnly = true
_, err = dir.Create("sausage", os.O_WRONLY|os.O_CREATE) _, err = dir.Create("sausage", os.O_WRONLY|os.O_CREATE)
assert.Equal(t, EROFS, err) assert.Equal(t, EROFS, err)