lib/versioner: Restore for all versioners, cross-device support (#5514)
* lib/versioner: Restore for all versioners, cross-device support Fixes #4631 Fixes #4586 Fixes #1634 Fixes #5338 Fixes #5419
This commit is contained in:
committed by
GitHub
parent
2984d40641
commit
0ca1f26ff8
@@ -22,37 +22,62 @@ import (
|
||||
// often enough that there is any contention on this lock.
|
||||
var renameLock = sync.NewMutex()
|
||||
|
||||
// TryRename renames a file, leaving source file intact in case of failure.
|
||||
// RenameOrCopy renames a file, leaving source file intact in case of failure.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func TryRename(filesystem fs.Filesystem, from, to string) error {
|
||||
func RenameOrCopy(src, dst fs.Filesystem, from, to string) error {
|
||||
renameLock.Lock()
|
||||
defer renameLock.Unlock()
|
||||
|
||||
return withPreparedTarget(filesystem, from, to, func() error {
|
||||
return filesystem.Rename(from, to)
|
||||
})
|
||||
}
|
||||
return withPreparedTarget(dst, from, to, func() error {
|
||||
// Optimisation 1
|
||||
if src.Type() == dst.Type() && src.URI() == dst.URI() {
|
||||
return src.Rename(from, to)
|
||||
}
|
||||
|
||||
// Rename moves a temporary file to its final place.
|
||||
// Will make sure to delete the from file if the operation fails, so use only
|
||||
// for situations like committing a temp file to its final location.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func Rename(filesystem fs.Filesystem, from, to string) error {
|
||||
// Don't leave a dangling temp file in case of rename error
|
||||
if !(runtime.GOOS == "windows" && strings.EqualFold(from, to)) {
|
||||
defer filesystem.Remove(from)
|
||||
}
|
||||
return TryRename(filesystem, from, to)
|
||||
// "Optimisation" 2
|
||||
// Try to find a common prefix between the two filesystems, use that as the base for the new one
|
||||
// and try a rename.
|
||||
if src.Type() == dst.Type() {
|
||||
commonPrefix := fs.CommonPrefix(src.URI(), dst.URI())
|
||||
if len(commonPrefix) > 0 {
|
||||
commonFs := fs.NewFilesystem(src.Type(), commonPrefix)
|
||||
err := commonFs.Rename(
|
||||
filepath.Join(strings.TrimPrefix(src.URI(), commonPrefix), from),
|
||||
filepath.Join(strings.TrimPrefix(dst.URI(), commonPrefix), to),
|
||||
)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is sad, do a copy and delete.
|
||||
if _, err := dst.Stat(to); !fs.IsNotExist(err) {
|
||||
err := dst.Remove(to)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := copyFileContents(src, dst, from, to)
|
||||
if err != nil {
|
||||
_ = dst.Remove(to)
|
||||
return err
|
||||
}
|
||||
|
||||
return withPreparedTarget(src, from, from, func() error {
|
||||
return src.Remove(from)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Copy copies the file content from source to destination.
|
||||
// Tries hard to succeed on various systems by temporarily tweaking directory
|
||||
// permissions and removing the destination file when necessary.
|
||||
func Copy(filesystem fs.Filesystem, from, to string) (err error) {
|
||||
return withPreparedTarget(filesystem, from, to, func() error {
|
||||
return copyFileContents(filesystem, from, to)
|
||||
func Copy(src, dst fs.Filesystem, from, to string) (err error) {
|
||||
return withPreparedTarget(dst, from, to, func() error {
|
||||
return copyFileContents(src, dst, from, to)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -115,13 +140,13 @@ func withPreparedTarget(filesystem fs.Filesystem, from, to string, f func() erro
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all its contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFileContents(filesystem fs.Filesystem, src, dst string) (err error) {
|
||||
in, err := filesystem.Open(src)
|
||||
func copyFileContents(srcFs, dstFs fs.Filesystem, src, dst string) (err error) {
|
||||
in, err := srcFs.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := filesystem.Create(dst)
|
||||
out, err := dstFs.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user