From e1bf46c6a53acee31df8088ce0d94541a3e629fb Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Thu, 27 May 2021 20:14:12 +0200 Subject: [PATCH] local fs rename: if it fails with a cross device error try a copy I don't want to add a new setting for this, at least until we get the first complain for a slow rename :) Fixes #440 --- vfs/fileinfo_windows.go | 7 ------- vfs/osfs.go | 11 +++++++++-- vfs/{fileinfo_unix.go => sys_unix.go} | 7 +++++++ vfs/sys_windows.go | 16 ++++++++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) delete mode 100644 vfs/fileinfo_windows.go rename vfs/{fileinfo_unix.go => sys_unix.go} (77%) create mode 100644 vfs/sys_windows.go diff --git a/vfs/fileinfo_windows.go b/vfs/fileinfo_windows.go deleted file mode 100644 index 9947677c..00000000 --- a/vfs/fileinfo_windows.go +++ /dev/null @@ -1,7 +0,0 @@ -package vfs - -import "syscall" - -func (fi FileInfo) getFileInfoSys() interface{} { - return syscall.Win32FileAttributeData{} -} diff --git a/vfs/osfs.go b/vfs/osfs.go index d749a9f2..c2c913e2 100644 --- a/vfs/osfs.go +++ b/vfs/osfs.go @@ -11,6 +11,7 @@ import ( "time" "github.com/eikenb/pipeat" + fscopy "github.com/otiai10/copy" "github.com/pkg/sftp" "github.com/rs/xid" @@ -98,8 +99,14 @@ func (*OsFs) Create(name string, flag int) (File, *PipeWriter, func(), error) { } // Rename renames (moves) source to target -func (*OsFs) Rename(source, target string) error { - return os.Rename(source, target) +func (fs *OsFs) Rename(source, target string) error { + err := os.Rename(source, target) + if err != nil && isCrossDeviceError(err) { + fsLog(fs, logger.LevelWarn, "cross device error detected while renaming %#v -> %#v. Trying a copy, this could take a long time", + source, target) + return fscopy.Copy(source, target) + } + return err } // Remove removes the named file or (empty) directory. diff --git a/vfs/fileinfo_unix.go b/vfs/sys_unix.go similarity index 77% rename from vfs/fileinfo_unix.go rename to vfs/sys_unix.go index a28ed3ea..6c98f0f0 100644 --- a/vfs/fileinfo_unix.go +++ b/vfs/sys_unix.go @@ -3,8 +3,11 @@ package vfs import ( + "errors" "os" "syscall" + + "golang.org/x/sys/unix" ) var ( @@ -27,3 +30,7 @@ func (fi FileInfo) getFileInfoSys() interface{} { Uid: uint32(defaultUID), Gid: uint32(defaultGID)} } + +func isCrossDeviceError(err error) bool { + return errors.Is(err, unix.EXDEV) +} diff --git a/vfs/sys_windows.go b/vfs/sys_windows.go new file mode 100644 index 00000000..0521a2e8 --- /dev/null +++ b/vfs/sys_windows.go @@ -0,0 +1,16 @@ +package vfs + +import ( + "errors" + "syscall" + + "golang.org/x/sys/windows" +) + +func (fi FileInfo) getFileInfoSys() interface{} { + return syscall.Win32FileAttributeData{} +} + +func isCrossDeviceError(err error) bool { + return errors.Is(err, windows.ERROR_NOT_SAME_DEVICE) +}