about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-05 19:09:22 +0000
committerbors <bors@rust-lang.org>2020-09-05 19:09:22 +0000
commitde921ab3c3aa25d65b1476d77285da1ca99af397 (patch)
tree802cb52c77d098a57d90067999d68f1ec266a44f
parent04f44fb9232dc960d213d0df4a203c387215a5ff (diff)
parent4ddedd521418d67e845ecb43dc02c09b0af53022 (diff)
downloadrust-de921ab3c3aa25d65b1476d77285da1ca99af397.tar.gz
rust-de921ab3c3aa25d65b1476d77285da1ca99af397.zip
Auto merge of #75428 - the8472:fix-copy-eopnotsupp, r=joshtriplett
Workarounds for copy_file_range issues

fixes #75387
fixes #75446
-rw-r--r--library/std/src/sys/unix/fs.rs27
1 files changed, 17 insertions, 10 deletions
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index acb18e6d064..566ac0920dc 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1140,14 +1140,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
     }
 
     let (mut reader, reader_metadata) = open_from(from)?;
-    let len = reader_metadata.len();
+    let max_len = u64::MAX;
     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
 
     let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
     let mut written = 0u64;
-    while written < len {
+    while written < max_len {
         let copy_result = if has_copy_file_range {
-            let bytes_to_copy = cmp::min(len - written, usize::MAX as u64) as usize;
+            let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64) as usize;
             let copy_result = unsafe {
                 // We actually don't have to adjust the offsets,
                 // because copy_file_range adjusts the file offset automatically
@@ -1162,7 +1162,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
             };
             if let Err(ref copy_err) = copy_result {
                 match copy_err.raw_os_error() {
-                    Some(libc::ENOSYS) | Some(libc::EPERM) => {
+                    Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => {
                         HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
                     }
                     _ => {}
@@ -1173,18 +1173,25 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
             Err(io::Error::from_raw_os_error(libc::ENOSYS))
         };
         match copy_result {
+            Ok(0) if written == 0 => {
+                // fallback to work around several kernel bugs where copy_file_range will fail to
+                // copy any bytes and return 0 instead of an error if
+                // - reading virtual files from the proc filesystem which appear to have 0 size
+                //   but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
+                // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
+                return io::copy(&mut reader, &mut writer);
+            }
+            Ok(0) => return Ok(written), // reached EOF
             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
-                            || os_err == libc::EINVAL
-                            || os_err == libc::EPERM =>
-                    {
+                    Some(
+                        libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
+                    ) => {
                         // Try fallback io::copy if either:
                         // - Kernel version is < 4.5 (ENOSYS)
                         // - Files are mounted on different fs (EXDEV)
+                        // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
                         // - copy_file_range is disallowed, for example by seccomp (EPERM)
                         // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
                         assert_eq!(written, 0);