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

Signed-off-by: Gyu-Ho Lee <gyuhox@gmail.com>
release-3.3
Gyu-Ho Lee 2017-11-27 06:23:31 -08:00 committed by Gyuho Lee
parent b48cf77abb
commit 962976f2df
1 changed files with 22 additions and 0 deletions

View File

@ -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
}