#[cfg(feature = "async-std")]
use async_std::fs;
use random_access_storage::RandomAccessError;
#[cfg(feature = "tokio")]
use tokio::fs;
pub async fn get_length_and_block_size(
file: &fs::File,
) -> Result<(u64, u64), RandomAccessError> {
use std::os::unix::fs::MetadataExt;
let meta = file.metadata().await?;
let block_size = meta.blksize();
Ok((meta.len(), block_size))
}
pub async fn set_sparse(_file: &mut fs::File) -> Result<(), RandomAccessError> {
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
pub async fn trim(
file: &mut fs::File,
offset: u64,
length: u64,
_block_size: u64,
) -> Result<(), RandomAccessError> {
use libc::{fallocate, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE};
use std::os::unix::io::AsRawFd;
let fd = file.as_raw_fd();
unsafe {
let ret = fallocate(
fd,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
offset as libc::off_t,
length as libc::off_t,
);
if ret < 0 {
return Err(RandomAccessError::IO {
context: Some("Failed to punch hole to file on linux".to_string()),
return_code: Some(ret),
source: std::io::Error::last_os_error(),
});
}
}
Ok(())
}
#[cfg(target_os = "macos")]
pub async fn trim(
file: &mut fs::File,
offset: u64,
length: u64,
block_size: u64,
) -> Result<(), RandomAccessError> {
#[cfg(feature = "async-std")]
use async_std::io::{
prelude::{SeekExt, WriteExt},
SeekFrom,
};
#[cfg(feature = "tokio")]
use std::io::SeekFrom;
#[cfg(feature = "tokio")]
use tokio::io::{AsyncSeekExt, AsyncWriteExt};
if length == 0 {
return Ok(());
}
let next_block_distance: u64 = if offset % block_size == 0 {
0
} else {
block_size - offset % block_size
};
let end = offset + length;
let last_block_offset = end - (end % block_size);
let initial_zero_length = if offset + next_block_distance >= last_block_offset
{
length
} else {
next_block_distance
};
if initial_zero_length > 0 {
let data = vec![0_u8; initial_zero_length as usize];
file.seek(SeekFrom::Start(offset)).await?;
file.write_all(&data).await?;
if initial_zero_length == length {
return Ok(());
}
}
let punch_hole_offset = offset + next_block_distance;
if punch_hole_offset < last_block_offset {
punch_hole(
file,
punch_hole_offset,
last_block_offset - punch_hole_offset,
)?;
}
if last_block_offset < end {
let data = vec![0_u8; (end - last_block_offset) as usize];
file.seek(SeekFrom::Start(last_block_offset)).await?;
file.write_all(&data).await?;
}
Ok(())
}
#[cfg(target_os = "macos")]
fn punch_hole(
file: &fs::File,
offset: u64,
length: u64,
) -> Result<(), RandomAccessError> {
use libc::c_int;
use std::os::unix::io::AsRawFd;
let fd = file.as_raw_fd();
#[repr(C)]
struct fpunchhole_t {
fp_flags: c_int,
reserved: c_int,
fp_offset: u64,
fp_length: u64,
}
const F_PUNCHHOLE: c_int = 99;
let hole = fpunchhole_t {
fp_flags: 0,
reserved: 0,
fp_offset: offset,
fp_length: length,
};
unsafe {
let ret = libc::fcntl(fd, F_PUNCHHOLE, &hole);
if ret < 0 {
return Err(RandomAccessError::IO {
context: Some("Failed to punch hole to file on macos".to_string()),
return_code: Some(ret),
source: std::io::Error::last_os_error(),
});
}
}
Ok(())
}