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)))
|
Ok(PathBuf::from(OsString::from_vec(buf)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||||
use fs::{File, set_permissions};
|
use fs::{File, set_permissions};
|
||||||
if !from.is_file() {
|
if !from.is_file() {
|
||||||
@@ -776,3 +777,69 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
|||||||
set_permissions(to, perm)?;
|
set_permissions(to, perm)?;
|
||||||
Ok(ret)
|
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