lib/file: retry preallocate on EINTR

Before this change, sometimes preallocate failed with EINTR which
rclone ignored.

Retrying the syscall is the correct thing to do and seems to make
preallocate 100% reliable.
This commit is contained in:
Nick Craig-Wood 2021-02-17 18:11:27 +00:00
parent 4b4e531846
commit 5e038a5e1e
1 changed files with 27 additions and 18 deletions

View File

@ -6,6 +6,7 @@ import (
"os"
"sync"
"sync/atomic"
"syscall"
"github.com/rclone/rclone/fs"
"golang.org/x/sys/unix"
@ -25,30 +26,38 @@ var (
const PreallocateImplemented = true
// PreAllocate the file for performance reasons
func PreAllocate(size int64, out *os.File) error {
func PreAllocate(size int64, out *os.File) (err error) {
if size <= 0 {
return nil
}
preAllocateMu.Lock()
defer preAllocateMu.Unlock()
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
}
// Wrap important errors
if err == unix.ENOSPC {
return ErrDiskFull
for {
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
}
// Wrap important errors
if err == unix.ENOSPC {
return ErrDiskFull
}
if err != syscall.EINTR {
break
}
}
return err
}