From 5a941cdcdca1f76dd7d2f5cca1a827db64d421bd Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 25 Mar 2019 15:22:40 +0000 Subject: [PATCH] local: fix preallocate warning on Linux with ZFS Under Linux, rclone attempts to preallocate files for efficiency. Before this change, pre-allocation would fail on ZFS with the error Failed to pre-allocate: operation not supported After this change rclone tries a different flag combination for ZFS then disables pre-allocate if that doesn't work. Fixes #3066 --- backend/local/preallocate_unix.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/backend/local/preallocate_unix.go b/backend/local/preallocate_unix.go index f7208ab0b..215b19066 100644 --- a/backend/local/preallocate_unix.go +++ b/backend/local/preallocate_unix.go @@ -4,16 +4,40 @@ package local import ( "os" + "sync/atomic" + "github.com/ncw/rclone/fs" "golang.org/x/sys/unix" ) +var ( + fallocFlags = [...]uint32{ + unix.FALLOC_FL_KEEP_SIZE, // Default + unix.FALLOC_FL_KEEP_SIZE | unix.FALLOC_FL_PUNCH_HOLE, // for ZFS #3066 + } + fallocFlagsIndex int32 +) + // preAllocate the file for performance reasons func preAllocate(size int64, out *os.File) error { if size <= 0 { return nil } - err := unix.Fallocate(int(out.Fd()), unix.FALLOC_FL_KEEP_SIZE, 0, size) + index := atomic.LoadInt32(&fallocFlagsIndex) +again: + if index >= int32(len(fallocFlags)) { + return nil // Fallocate is disabled + } + flags := fallocFlags[index] + err := unix.Fallocate(int(out.Fd()), flags, 0, size) + if err == unix.ENOTSUP { + // Try the next flags combination + index++ + atomic.StoreInt32(&fallocFlagsIndex, index) + fs.Debugf(nil, "preAllocate: got error on fallocate, trying combination %d/%d: %v", index, len(fallocFlags), err) + goto again + + } // FIXME could be doing something here // if err == unix.ENOSPC { // log.Printf("No space")