use std::{fs::File, io, os::fd::AsRawFd};
#[cfg(target_os = "linux")]
pub(super) fn reserve(file: &File, length: usize) -> io::Result<()> {
if unsafe {
libc::fallocate(
file.as_raw_fd(),
libc::FALLOC_FL_KEEP_SIZE,
0,
length as libc::off_t,
)
} == 0
{
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(target_os = "macos")]
pub(super) fn reserve(file: &File, length: usize) -> io::Result<()> {
use std::os::unix::fs::FileExt;
let original = seek(file, 0, libc::SEEK_CUR)?;
let result = (|| {
let mut start = 0;
let zeros = [0_u8; 16 * 1024];
while start < length as u64 {
let hole = seek(file, start, libc::SEEK_HOLE)?;
if hole >= length as u64 {
break;
}
let end = match seek(file, hole, libc::SEEK_DATA) {
Ok(data) => data.min(length as u64),
Err(error) if error.raw_os_error() == Some(libc::ENXIO) => length as u64,
Err(error) => return Err(error),
};
if end <= hole {
return Err(io::Error::other("sparse file extent did not advance"));
}
let mut at = hole;
while at < end {
let count = zeros.len().min((end - at) as usize);
file.write_all_at(&zeros[..count], at)?;
at += count as u64;
}
start = end;
}
Ok(())
})();
let restored = seek(file, original, libc::SEEK_SET);
result.and(restored.map(|_| ()))
}
#[cfg(target_os = "macos")]
fn seek(file: &File, offset: u64, mode: libc::c_int) -> io::Result<u64> {
let result = unsafe { libc::lseek(file.as_raw_fd(), offset as libc::off_t, mode) };
if result < 0 {
Err(io::Error::last_os_error())
} else {
Ok(result as u64)
}
}