@@ -1140,14 +1140,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
11401140 }
11411141
11421142 let ( mut reader, reader_metadata) = open_from ( from) ?;
1143- let len = reader_metadata . len ( ) ;
1143+ let max_len = u64 :: MAX ;
11441144 let ( mut writer, _) = open_to_and_set_permissions ( to, reader_metadata) ?;
11451145
11461146 let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
11471147 let mut written = 0u64 ;
1148- while written < len {
1148+ while written < max_len {
11491149 let copy_result = if has_copy_file_range {
1150- let bytes_to_copy = cmp:: min ( len - written, usize:: MAX as u64 ) as usize ;
1150+ let bytes_to_copy = cmp:: min ( max_len - written, usize:: MAX as u64 ) as usize ;
11511151 let copy_result = unsafe {
11521152 // We actually don't have to adjust the offsets,
11531153 // because copy_file_range adjusts the file offset automatically
@@ -1162,7 +1162,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
11621162 } ;
11631163 if let Err ( ref copy_err) = copy_result {
11641164 match copy_err. raw_os_error ( ) {
1165- Some ( libc:: ENOSYS ) | Some ( libc:: EPERM ) => {
1165+ Some ( libc:: ENOSYS | libc:: EPERM | libc :: EOPNOTSUPP ) => {
11661166 HAS_COPY_FILE_RANGE . store ( false , Ordering :: Relaxed ) ;
11671167 }
11681168 _ => { }
@@ -1173,18 +1173,25 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
11731173 Err ( io:: Error :: from_raw_os_error ( libc:: ENOSYS ) )
11741174 } ;
11751175 match copy_result {
1176+ Ok ( 0 ) if written == 0 => {
1177+ // fallback to work around several kernel bugs where copy_file_range will fail to
1178+ // copy any bytes and return 0 instead of an error if
1179+ // - reading virtual files from the proc filesystem which appear to have 0 size
1180+ // but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
1181+ // - copying from an overlay filesystem in docker. reported to occur on fedora 32.
1182+ return io:: copy ( & mut reader, & mut writer) ;
1183+ }
1184+ Ok ( 0 ) => return Ok ( written) , // reached EOF
11761185 Ok ( ret) => written += ret as u64 ,
11771186 Err ( err) => {
11781187 match err. raw_os_error ( ) {
1179- Some ( os_err)
1180- if os_err == libc:: ENOSYS
1181- || os_err == libc:: EXDEV
1182- || os_err == libc:: EINVAL
1183- || os_err == libc:: EPERM =>
1184- {
1188+ Some (
1189+ libc:: ENOSYS | libc:: EXDEV | libc:: EINVAL | libc:: EPERM | libc:: EOPNOTSUPP ,
1190+ ) => {
11851191 // Try fallback io::copy if either:
11861192 // - Kernel version is < 4.5 (ENOSYS)
11871193 // - Files are mounted on different fs (EXDEV)
1194+ // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
11881195 // - copy_file_range is disallowed, for example by seccomp (EPERM)
11891196 // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
11901197 assert_eq ! ( written, 0 ) ;
0 commit comments