From a492c0fb0e2c84bbdb8d7612ba3fa1bfce4dab8f Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 6 Mar 2020 12:41:48 +0000 Subject: [PATCH] local: speed up multi thread downloads by using sparse files on Windows Before this change rclone didn't use sparse files on Windows. This means that when you downloaded a file with multithread download it wrote the entire file with zeros first on the first write not at the start of the file. This change makes the file be sparse on Windows. Linux/macOS files were already sparse. --- backend/local/local.go | 6 ++++++ backend/local/preallocate_other.go | 5 +++++ backend/local/preallocate_unix.go | 5 +++++ backend/local/preallocate_windows.go | 13 +++++++++++++ 4 files changed, 29 insertions(+) diff --git a/backend/local/local.go b/backend/local/local.go index 7721d1edf..5e5369e74 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -1068,6 +1068,12 @@ func (f *Fs) OpenWriterAt(ctx context.Context, remote string, size int64) (fs.Wr if err != nil { fs.Debugf(o, "Failed to pre-allocate: %v", err) } + // Set the file to be a sparse file (important on Windows) + err = setSparse(out) + if err != nil { + fs.Debugf(o, "Failed to set sparse: %v", err) + } + return out, nil } diff --git a/backend/local/preallocate_other.go b/backend/local/preallocate_other.go index fb71cad59..5738b485c 100644 --- a/backend/local/preallocate_other.go +++ b/backend/local/preallocate_other.go @@ -8,3 +8,8 @@ import "os" func preAllocate(size int64, out *os.File) error { return nil } + +// setSparse makes the file be a sparse file +func setSparse(out *os.File) error { + return nil +} diff --git a/backend/local/preallocate_unix.go b/backend/local/preallocate_unix.go index 5caf6c1e4..e7472f6a7 100644 --- a/backend/local/preallocate_unix.go +++ b/backend/local/preallocate_unix.go @@ -44,3 +44,8 @@ again: // } return err } + +// setSparse makes the file be a sparse file +func setSparse(out *os.File) error { + return nil +} diff --git a/backend/local/preallocate_windows.go b/backend/local/preallocate_windows.go index dfd88369d..a434272f7 100644 --- a/backend/local/preallocate_windows.go +++ b/backend/local/preallocate_windows.go @@ -77,3 +77,16 @@ func preAllocate(size int64, out *os.File) error { return nil } + +const ( + FSCTL_SET_SPARSE = 0x000900c4 +) + +// setSparse makes the file be a sparse file +func setSparse(out *os.File) error { + err := syscall.DeviceIoControl(syscall.Handle(out.Fd()), FSCTL_SET_SPARSE, nil, 0, nil, 0, nil, nil) + if err != nil { + return errors.Wrap(err, "DeviceIoControl FSCTL_SET_SPARSE") + } + return nil +}