fs: use copy_file_range on linux
This commit is contained in:
@@ -761,6 +761,7 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
||||
Ok(PathBuf::from(OsString::from_vec(buf)))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
use fs::{File, set_permissions};
|
||||
if !from.is_file() {
|
||||
@@ -776,3 +777,69 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
set_permissions(to, perm)?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
use fs::{File, set_permissions};
|
||||
|
||||
unsafe fn copy_file_range(
|
||||
fd_in: libc::c_int,
|
||||
off_in: *mut libc::loff_t,
|
||||
fd_out: libc::c_int,
|
||||
off_out: *mut libc::loff_t,
|
||||
len: libc::size_t,
|
||||
flags: libc::c_uint,
|
||||
) -> libc::c_long {
|
||||
libc::syscall(
|
||||
libc::SYS_copy_file_range,
|
||||
fd_in,
|
||||
off_in,
|
||||
fd_out,
|
||||
off_out,
|
||||
len,
|
||||
flags,
|
||||
)
|
||||
}
|
||||
|
||||
if !from.is_file() {
|
||||
return Err(Error::new(ErrorKind::InvalidInput,
|
||||
"the source path is not an existing regular file"))
|
||||
}
|
||||
|
||||
let mut reader = File::open(from)?;
|
||||
let mut writer = File::create(to)?;
|
||||
let (perm, len) = {
|
||||
let metadata = reader.metadata()?;
|
||||
(metadata.permissions(), metadata.size())
|
||||
};
|
||||
|
||||
let mut written = 0u64;
|
||||
while written < len {
|
||||
let copy_result = unsafe {
|
||||
cvt(copy_file_range(reader.as_raw_fd(),
|
||||
ptr::null_mut(),
|
||||
writer.as_raw_fd(),
|
||||
ptr::null_mut(),
|
||||
len as usize,
|
||||
0)
|
||||
)
|
||||
};
|
||||
match copy_result {
|
||||
Ok(ret) => written += ret as u64,
|
||||
Err(err) => {
|
||||
match err.raw_os_error() {
|
||||
Some(os_err) if os_err == libc::ENOSYS || os_err == libc::EXDEV => {
|
||||
// Either kernel is too old or the files are not mounted on the same fs.
|
||||
// Try again with fallback method
|
||||
let ret = io::copy(&mut reader, &mut writer)?;
|
||||
set_permissions(to, perm)?;
|
||||
return Ok(ret)
|
||||
},
|
||||
_ => return Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
set_permissions(to, perm)?;
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user