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")