pkg/fileutil: fix preallocate under OS X kernel
ftruncate changes st_blocks, and following fallocate
syscalls would return EINVAL when allocated block size
is already greater than requested block size
(e.g. st_blocks==8, requested blocks are 2).
@@ -30,6 +30,8 @@ func preallocExtend(f *os.File, sizeInBytes int64) error {
}
func preallocFixed(f *os.File, sizeInBytes int64) error {
+ // allocate all requested space or no space at all
+ // TODO: allocate contiguous space on disk with F_ALLOCATECONTIG flag
fstore := &syscall.Fstore_t{
Flags: syscall.F_ALLOCATEALL,
Posmode: syscall.F_PEOFPOSMODE,
@@ -39,5 +41,25 @@ func preallocFixed(f *os.File, sizeInBytes int64) error {
if errno == 0 || errno == syscall.ENOTSUP {
return nil
}
+
+ // wrong argument to fallocate syscall
+ if errno == syscall.EINVAL {
+ // filesystem "st_blocks" are allocated in the units of
+ // "Allocation Block Size" (run "diskutil info /" command)
+ var stat syscall.Stat_t
+ syscall.Fstat(int(f.Fd()), &stat)
+
+ // syscall.Statfs_t.Bsize is "optimal transfer block size"
+ // and contains matching 4096 value when latest OS X kernel
+ // supports 4,096 KB filesystem block size
+ var statfs syscall.Statfs_t
+ syscall.Fstatfs(int(f.Fd()), &statfs)
+ blockSize := int64(statfs.Bsize)
+
+ if stat.Blocks*blockSize >= sizeInBytes {
+ // enough blocks are already allocated
+ return nil
+ }
+ }
return errno
}
