summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libstd/sys/unix/fs.rs88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index 77968ffded..889d21cad6 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -794,6 +794,7 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
794 Ok(PathBuf::from(OsString::from_vec(buf))) 794 Ok(PathBuf::from(OsString::from_vec(buf)))
795} 795}
796 796
797#[cfg(not(any(target_os = "linux", target_os = "android")))]
797pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { 798pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
798 use fs::{File, set_permissions}; 799 use fs::{File, set_permissions};
799 if !from.is_file() { 800 if !from.is_file() {
@@ -809,3 +810,90 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
809 set_permissions(to, perm)?; 810 set_permissions(to, perm)?;
810 Ok(ret) 811 Ok(ret)
811} 812}
813
814#[cfg(any(target_os = "linux", target_os = "android"))]
815pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
816 use cmp;
817 use fs::{File, set_permissions};
818 use sync::atomic::{AtomicBool, Ordering};
819
820 // Kernel prior to 4.5 don't have copy_file_range
821 // We store the availability in a global to avoid unneccessary syscalls
822 static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
823
824 unsafe fn copy_file_range(
825 fd_in: libc::c_int,
826 off_in: *mut libc::loff_t,
827 fd_out: libc::c_int,
828 off_out: *mut libc::loff_t,
829 len: libc::size_t,
830 flags: libc::c_uint,
831 ) -> libc::c_long {
832 libc::syscall(
833 libc::SYS_copy_file_range,
834 fd_in,
835 off_in,
836 fd_out,
837 off_out,
838 len,
839 flags,
840 )
841 }
842
843 if !from.is_file() {
844 return Err(Error::new(ErrorKind::InvalidInput,
845 "the source path is not an existing regular file"))
846 }
847
848 let mut reader = File::open(from)?;
849 let mut writer = File::create(to)?;
850 let (perm, len) = {
851 let metadata = reader.metadata()?;
852 (metadata.permissions(), metadata.size())
853 };
854
855 let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
856 let mut written = 0u64;
857 while written < len {
858 let copy_result = if has_copy_file_range {
859 let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize;
860 let copy_result = unsafe {
861 // We actually don't have to adjust the offsets,
862 // because copy_file_range adjusts the file offset automatically
863 cvt(copy_file_range(reader.as_raw_fd(),
864 ptr::null_mut(),
865 writer.as_raw_fd(),
866 ptr::null_mut(),
867 bytes_to_copy,
868 0)
869 )
870 };
871 if let Err(ref copy_err) = copy_result {
872 if let Some(libc::ENOSYS) = copy_err.raw_os_error() {
873 HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
874 }
875 }
876 copy_result
877 } else {
878 Err(io::Error::from_raw_os_error(libc::ENOSYS))
879 };
880 match copy_result {
881 Ok(ret) => written += ret as u64,
882 Err(err) => {
883 match err.raw_os_error() {
884 Some(os_err) if os_err == libc::ENOSYS || os_err == libc::EXDEV => {
885 // Either kernel is too old or the files are not mounted on the same fs.
886 // Try again with fallback method
887 assert_eq!(written, 0);
888 let ret = io::copy(&mut reader, &mut writer)?;
889 set_permissions(to, perm)?;
890 return Ok(ret)
891 },
892 _ => return Err(err),
893 }
894 }
895 }
896 }
897 set_permissions(to, perm)?;
898 Ok(written)
899}