diff options
| author | Nicolas Koch <nioko1337@gmail.com> | 2018-05-15 15:25:09 +0200 |
|---|---|---|
| committer | Nicolas Koch <nioko1337@gmail.com> | 2018-05-15 15:25:09 +0200 |
| commit | 834ef9f08ae3429a05dead80237bb4bd04769895 (patch) | |
| tree | 8df8fc109050c67cda769de3cdfc03ce761b1b4b /src/libstd/sys | |
| parent | 76027ed2ad16cbb57919fe9c4dd3849469650570 (diff) | |
| download | rust-834ef9f08ae3429a05dead80237bb4bd04769895.tar.gz rust-834ef9f08ae3429a05dead80237bb4bd04769895.zip | |
fs: use copy_file_range on linux
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/fs.rs | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index a1ca839dc18..b9f0f39bbe2 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -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) +} |
