use std::io;
use std::os::unix::io::RawFd;
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub(crate) fn copy_range(
in_fd: RawFd,
out_fd: RawFd,
mut offset: u64,
mut len: u64,
) -> io::Result<()> {
while len > 0 {
let mut off_in = offset as i64;
let n = unsafe {
libc::copy_file_range(
in_fd,
&mut off_in,
out_fd,
std::ptr::null_mut(),
len as usize,
0,
)
};
if n < 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::EXDEV) {
return copy_range_fallback(in_fd, out_fd, offset, len);
}
return Err(err);
}
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"copy_file_range returned 0",
));
}
let n = n.cast_unsigned() as u64;
offset += n;
len -= n;
}
Ok(())
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
fn copy_range_fallback(
in_fd: RawFd,
out_fd: RawFd,
mut offset: u64,
mut len: u64,
) -> io::Result<()> {
use std::io::Write;
use std::os::unix::io::FromRawFd;
let mut buf = vec![0u8; 256 * 1024];
let mut out = std::mem::ManuallyDrop::new(unsafe { std::fs::File::from_raw_fd(out_fd) });
while len > 0 {
let chunk = buf.len().min(len as usize);
let n = unsafe {
libc::pread(in_fd, buf.as_mut_ptr().cast(), chunk, offset as i64)
};
if n < 0 {
return Err(io::Error::last_os_error());
}
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"pread returned 0 during cross-device copy",
));
}
let n = n.cast_unsigned();
out.write_all(&buf[..n])?;
offset += n as u64;
len -= n as u64;
}
Ok(())
}