@@ -2209,7 +2209,7 @@ impl FileSystem for PassthroughFs {
22092209 _ctx : Context ,
22102210 inode : Inode ,
22112211 handle : Handle ,
2212- _mode : u32 ,
2212+ mode : u32 ,
22132213 offset : u64 ,
22142214 length : u64 ,
22152215 ) -> io:: Result < ( ) > {
@@ -2224,36 +2224,79 @@ impl FileSystem for PassthroughFs {
22242224
22252225 let fd = data. file . write ( ) . unwrap ( ) . as_raw_fd ( ) ;
22262226
2227- let proposed_length = ( offset + length) as i64 ;
2228- let mut fs = libc:: fstore_t {
2229- fst_flags : libc:: F_ALLOCATECONTIG ,
2230- fst_posmode : libc:: F_PEOFPOSMODE ,
2231- fst_offset : 0 ,
2232- fst_length : proposed_length,
2233- fst_bytesalloc : 0 ,
2234- } ;
2235-
2236- let res = unsafe { libc:: fcntl ( fd, libc:: F_PREALLOCATE , & mut fs as * mut _ ) } ;
2237- if res < 0 {
2238- fs. fst_flags = libc:: F_ALLOCATEALL ;
2239- let res = unsafe { libc:: fcntl ( fd, libc:: F_PREALLOCATE , & mut fs as & mut _ ) } ;
2240- if res < 0 {
2241- return Err ( linux_error ( io:: Error :: last_os_error ( ) ) ) ;
2227+ const SUPPORTED_FLAGS : i32 = bindings:: LINUX_FALLOC_FL_ALLOCATE_RANGE
2228+ | bindings:: LINUX_FALLOC_FL_KEEP_SIZE
2229+ | bindings:: LINUX_FALLOC_FL_PUNCH_HOLE ;
2230+
2231+ if mode as i32 & !SUPPORTED_FLAGS != 0 {
2232+ return Err ( linux_error ( io:: Error :: from_raw_os_error ( libc:: EOPNOTSUPP ) ) ) ;
2233+ }
2234+
2235+ let keep_size = mode & bindings:: LINUX_FALLOC_FL_KEEP_SIZE as u32 != 0 ;
2236+ let mode = mode & !bindings:: LINUX_FALLOC_FL_KEEP_SIZE as u32 ;
2237+
2238+ match mode as i32 {
2239+ bindings:: LINUX_FALLOC_FL_ALLOCATE_RANGE => {
2240+ // The closest thing we have on macOS to posix_fallocate is F_PREALLOCATE,
2241+ // but this one doesn't allow us to allocate arbitrary ranges, only allocate
2242+ // blocks to the file's end.
2243+ //
2244+ // The best thing we can do here is extend the file to (offset + length).
2245+ // This doesn't adhere to the same semantics, but should work fine (albeit
2246+ // less performant) for most guest applications.
2247+ let st = fstat ( fd, true ) ?;
2248+ let new_length = ( offset + length) as i64 ;
2249+
2250+ if keep_size {
2251+ // Check the number of allocated blocks instead of the file size.
2252+ let disk_size = st. st_blocks * 512_i64 ;
2253+ if disk_size >= new_length {
2254+ return Ok ( ( ) ) ;
2255+ }
2256+ let mut fs = libc:: fstore_t {
2257+ fst_flags : libc:: F_ALLOCATEALL ,
2258+ fst_posmode : libc:: F_PEOFPOSMODE ,
2259+ fst_offset : 0 ,
2260+ fst_length : new_length - disk_size,
2261+ fst_bytesalloc : 0 ,
2262+ } ;
2263+
2264+ let res = unsafe { libc:: fcntl ( fd, libc:: F_PREALLOCATE , & mut fs as * mut _ ) } ;
2265+ if res < 0 {
2266+ return Err ( linux_error ( io:: Error :: last_os_error ( ) ) ) ;
2267+ }
2268+ } else {
2269+ if st. st_size >= new_length {
2270+ return Ok ( ( ) ) ;
2271+ }
2272+ let res = unsafe { libc:: ftruncate ( fd, new_length) } ;
2273+ if res < 0 {
2274+ return Err ( linux_error ( io:: Error :: last_os_error ( ) ) ) ;
2275+ }
2276+ }
22422277 }
2243- }
2278+ bindings:: LINUX_FALLOC_FL_PUNCH_HOLE => {
2279+ if !keep_size {
2280+ // Linux forbids the use of PUNCH_HOLE without KEEP_SIZE.
2281+ return Err ( linux_error ( io:: Error :: from_raw_os_error ( libc:: EINVAL ) ) ) ;
2282+ }
22442283
2245- let st = fstat ( fd , true ) ? ;
2246- if st . st_size >= proposed_length {
2247- // fallocate should not shrink the file. The file is already larger than needed.
2248- return Ok ( ( ) ) ;
2249- }
2250- let res = unsafe { libc :: ftruncate ( fd , proposed_length ) } ;
2284+ let mut hole = libc :: fpunchhole_t {
2285+ fp_offset : offset as i64 ,
2286+ fp_flags : 0 ,
2287+ reserved : 0 ,
2288+ fp_length : length as i64 ,
2289+ } ;
22512290
2252- if res == 0 {
2253- Ok ( ( ) )
2254- } else {
2255- Err ( linux_error ( io:: Error :: last_os_error ( ) ) )
2291+ let res = unsafe { libc:: fcntl ( fd, libc:: F_PUNCHHOLE , & mut hole as * mut _ ) } ;
2292+ if res < 0 {
2293+ return Err ( linux_error ( io:: Error :: last_os_error ( ) ) ) ;
2294+ }
2295+ }
2296+ _ => unreachable ! ( ) ,
22562297 }
2298+
2299+ Ok ( ( ) )
22572300 }
22582301
22592302 fn lseek (
0 commit comments