pub trait CopyOnWrite: AsRawFd + Seek + FileExt
{
#[inline(always)]
fn atomic_clone_to<COW: CopyOnWrite>(&self, to: &COW) -> Result<(), &'static str>
{
let result = unsafe { ioctl(to.as_raw_fd(), FICLONE, self.as_raw_fd()) };
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
match errno().0
{
EBADF => Err("The filesystem which src_fd resides on does not support reflink OR src_fd is not open for reading; dest_fd is not open for writing or is open for append-only writes"),
EOPNOTSUPP => Err("This can appear if the filesystem does not support reflinking either file descriptor, or if either file descriptor refers to special inodes"),
EXDEV => Err("dest_fd and src_fd are not on the same mounted filesystem"),
EPERM => panic!("dest_fd is immutable"),
ETXTBSY => panic!("One of the files is a swap file. Swap files cannot share storage"),
EINVAL => unreachable_code(format_args!("The filesystem does not support reflinking the ranges of the given files. This error can also appear if either file descriptor represents a device, FIFO, or socket. Disk filesystems generally require the offset and length arguments to be aligned to the fundamental block size. XFS and Btrfs do not support overlapping reflink ranges in the same file.")),
EISDIR => unreachable_code(format_args!("One of the files is a directory and the filesystem does not support shared regions in directories.")),
_ => Err("other error from ioctl()")
}
}
else
{
unreachable_code(format_args!("Unexpected result {} from ioctl()", result))
}
}
#[inline(always)]
fn atomic_clone_range_to<COW: CopyOnWrite>(&self, from_logical_range_in_bytes: RangeInclusive<u64>, to: &COW, to_logical_offset: u64) -> Result<(), &'static str>
{
let (start, end) = from_logical_range_in_bytes.into_inner();
let length = end - start + 1;
let input = file_clone_range
{
src_fd: self.as_raw_fd() as i64,
src_offset: start,
src_length: length,
dest_offset: to_logical_offset,
};
let result = unsafe { ioctl(to.as_raw_fd(), FICLONERANGE, &input) };
if likely!(result == 0)
{
Ok(())
}
else if likely!(result == -1)
{
match errno().0
{
EBADF => Err("The filesystem which src_fd resides on does not support reflink OR src_fd is not open for reading; dest_fd is not open for writing or is open for append-only writes"),
EINVAL => Err("The filesystem does not support reflinking the ranges of the given files. This error can also appear if either file descriptor represents a device, FIFO, or socket. Disk filesystems generally require the offset and length arguments to be aligned to the fundamental block size. XFS and Btrfs do not support overlapping reflink ranges in the same file."),
EOPNOTSUPP => Err("This can appear if the filesystem does not support reflinking either file descriptor, or if either file descriptor refers to special inodes"),
EXDEV => Err("dest_fd and src_fd are not on the same mounted filesystem"),
EPERM => panic!("dest_fd is immutable"),
ETXTBSY => panic!("One of the files is a swap file. Swap files cannot share storage"),
EISDIR => unreachable_code(format_args!("One of the files is a directory and the filesystem does not support shared regions in directories.")),
_ => Err("other error from ioctl()")
}
}
else
{
unreachable_code(format_args!("Unexpected result {} from ioctl()", result))
}
}
#[inline(always)]
fn deduplicate<COW: CopyOnWrite>(&self, from_logical_range_in_bytes: RangeInclusive<u64>, to_files_and_their_logical_offsets: &[(&COW, u64)]) -> Result<Deduplicate, &'static str>
{
let mut deduplicate = Deduplicate::new(from_logical_range_in_bytes, to_files_and_their_logical_offsets);
let result = unsafe { ioctl(self.as_raw_fd(), FIDEDUPERANGE, deduplicate.data_pointer()) };
if likely!(result == 0)
{
Ok(deduplicate)
}
else if likely!(result == -1)
{
match errno().0
{
EBADF => Err("The filesystem which src_fd resides on does not support deduplication OR src_fd is not open for reading; dest_fd is not open for writing or is open for append-only writes."),
EINVAL => Err("The filesystem does not support deduplicating the ranges of the given files. This error can also appear if either file descriptor represents a device, FIFO, or socket. Diskfilesystems generally require the offset and length argumentsto be aligned to the fundamental block size. Neither Btrfs nor XFS support overlapping deduplication ranges in the same file"),
ENOMEM => Err("The kernel was unable to allocate sufficient memory to perform the operation or dest_count is so large that the input argument description spans more than a single page of memory"),
EOPNOTSUPP => Err("TThis can appear if the filesystem does not support deduplicating either file descriptor, or if either file descriptor refers to special inodes."),
EXDEV => Err("dest_fd and src_fd are not on the same mounted filesystem"),
EPERM => panic!("dest_fd is immutable"),
ETXTBSY => panic!("One of the files is a swap file. Swap files cannot share storage"),
EISDIR => unreachable_code(format_args!("One of the files is a directory and the filesystem does not support shared regions in directories.")),
_ => Err("other error from ioctl()")
}
}
else
{
unreachable_code(format_args!("Unexpected result {} from ioctl()", result))
}
}
}