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" "os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall"
"github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -25,30 +26,38 @@ var (
const PreallocateImplemented = true const PreallocateImplemented = true
// PreAllocate the file for performance reasons // 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 { if size <= 0 {
return nil return nil
} }
preAllocateMu.Lock() preAllocateMu.Lock()
defer preAllocateMu.Unlock() 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
} for {
// Wrap important errors
if err == unix.ENOSPC { index := atomic.LoadInt32(&fallocFlagsIndex)
return ErrDiskFull 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 return err
} }