Browse Source

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>
Gyu-Ho Lee 8 years ago
parent
commit
962976f2df
1 changed files with 22 additions and 0 deletions
  1. 22 0
      pkg/fileutil/preallocate_darwin.go

+ 22 - 0
pkg/fileutil/preallocate_darwin.go

@@ -30,6 +30,8 @@ func preallocExtend(f *os.File, sizeInBytes int64) error {
 }
 }
 
 
 func preallocFixed(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{
 	fstore := &syscall.Fstore_t{
 		Flags:   syscall.F_ALLOCATEALL,
 		Flags:   syscall.F_ALLOCATEALL,
 		Posmode: syscall.F_PEOFPOSMODE,
 		Posmode: syscall.F_PEOFPOSMODE,
@@ -39,5 +41,25 @@ func preallocFixed(f *os.File, sizeInBytes int64) error {
 	if errno == 0 || errno == syscall.ENOTSUP {
 	if errno == 0 || errno == syscall.ENOTSUP {
 		return nil
 		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
 	return errno
 }
 }