Skip to content

Commit 834ef9f

Browse files
committed
fs: use copy_file_range on linux
1 parent 76027ed commit 834ef9f

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

src/libstd/sys/unix/fs.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,7 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
761761
Ok(PathBuf::from(OsString::from_vec(buf)))
762762
}
763763

764+
#[cfg(not(target_os = "linux"))]
764765
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
765766
use fs::{File, set_permissions};
766767
if !from.is_file() {
@@ -776,3 +777,69 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
776777
set_permissions(to, perm)?;
777778
Ok(ret)
778779
}
780+
781+
#[cfg(target_os = "linux")]
782+
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
783+
use fs::{File, set_permissions};
784+
785+
unsafe fn copy_file_range(
786+
fd_in: libc::c_int,
787+
off_in: *mut libc::loff_t,
788+
fd_out: libc::c_int,
789+
off_out: *mut libc::loff_t,
790+
len: libc::size_t,
791+
flags: libc::c_uint,
792+
) -> libc::c_long {
793+
libc::syscall(
794+
libc::SYS_copy_file_range,
795+
fd_in,
796+
off_in,
797+
fd_out,
798+
off_out,
799+
len,
800+
flags,
801+
)
802+
}
803+
804+
if !from.is_file() {
805+
return Err(Error::new(ErrorKind::InvalidInput,
806+
"the source path is not an existing regular file"))
807+
}
808+
809+
let mut reader = File::open(from)?;
810+
let mut writer = File::create(to)?;
811+
let (perm, len) = {
812+
let metadata = reader.metadata()?;
813+
(metadata.permissions(), metadata.size())
814+
};
815+
816+
let mut written = 0u64;
817+
while written < len {
818+
let copy_result = unsafe {
819+
cvt(copy_file_range(reader.as_raw_fd(),
820+
ptr::null_mut(),
821+
writer.as_raw_fd(),
822+
ptr::null_mut(),
823+
len as usize,
824+
0)
825+
)
826+
};
827+
match copy_result {
828+
Ok(ret) => written += ret as u64,
829+
Err(err) => {
830+
match err.raw_os_error() {
831+
Some(os_err) if os_err == libc::ENOSYS || os_err == libc::EXDEV => {
832+
// Either kernel is too old or the files are not mounted on the same fs.
833+
// Try again with fallback method
834+
let ret = io::copy(&mut reader, &mut writer)?;
835+
set_permissions(to, perm)?;
836+
return Ok(ret)
837+
},
838+
_ => return Err(err),
839+
}
840+
}
841+
}
842+
}
843+
set_permissions(to, perm)?;
844+
Ok(written)
845+
}

0 commit comments

Comments
 (0)