From a323580cb68dfe38c947eeaaef910b51d6a95154 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Thu, 5 Dec 2024 17:42:04 +0400 Subject: [PATCH] fs/linux: Clone(): add implementation using `copy_file_range()` --- internal/fs/file_linux.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/fs/file_linux.go b/internal/fs/file_linux.go index 641951c0a..fec3e4b43 100644 --- a/internal/fs/file_linux.go +++ b/internal/fs/file_linux.go @@ -4,12 +4,14 @@ package fs import ( + "github.com/restic/restic/internal/errors" "golang.org/x/sys/unix" "os" ) func init() { registerCloneMethod(doCloneIoctl) + registerCloneMethod(doCloneCopyFileRange) } func withFileDescriptors(file1, file2 *os.File, fn func(fd1, fd2 int) (int, error)) (int, error) { @@ -43,3 +45,22 @@ func doCloneIoctl(src, dest *os.File) (cloned bool, err error) { }) return true, err } + +func doCloneCopyFileRange(src, dest *os.File) (cloned bool, err error) { + srcInfo, err := src.Stat() + if err != nil { + return false, err + } + srcLength := int(srcInfo.Size()) + + n, err := withFileDescriptors(src, dest, func(srcFd, destFd int) (int, error) { + var srcOff, destOff int64 = 0, 0 + return unix.CopyFileRange(srcFd, &srcOff, destFd, &destOff, srcLength, 0) + }) + + if n > 0 && n != srcLength { + return false, errors.Wrapf(err, "copy_file_range() wrote %d of %d bytes", n, srcLength) + } + // we cannot guarantee that copy_file_range() performs block cloning + return false, err +}