@@ -1140,14 +1140,14 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1140
1140
}
1141
1141
1142
1142
let ( mut reader, reader_metadata) = open_from ( from) ?;
1143
- let len = reader_metadata . len ( ) ;
1143
+ let max_len = u64 :: MAX ;
1144
1144
let ( mut writer, _) = open_to_and_set_permissions ( to, reader_metadata) ?;
1145
1145
1146
1146
let has_copy_file_range = HAS_COPY_FILE_RANGE . load ( Ordering :: Relaxed ) ;
1147
1147
let mut written = 0u64 ;
1148
- while written < len {
1148
+ while written < max_len {
1149
1149
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 ;
1151
1151
let copy_result = unsafe {
1152
1152
// We actually don't have to adjust the offsets,
1153
1153
// because copy_file_range adjusts the file offset automatically
@@ -1162,7 +1162,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1162
1162
} ;
1163
1163
if let Err ( ref copy_err) = copy_result {
1164
1164
match copy_err. raw_os_error ( ) {
1165
- Some ( libc:: ENOSYS ) | Some ( libc:: EPERM ) => {
1165
+ Some ( libc:: ENOSYS | libc:: EPERM | libc :: EOPNOTSUPP ) => {
1166
1166
HAS_COPY_FILE_RANGE . store ( false , Ordering :: Relaxed ) ;
1167
1167
}
1168
1168
_ => { }
@@ -1173,18 +1173,25 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1173
1173
Err ( io:: Error :: from_raw_os_error ( libc:: ENOSYS ) )
1174
1174
} ;
1175
1175
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
1176
1185
Ok ( ret) => written += ret as u64 ,
1177
1186
Err ( err) => {
1178
1187
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
+ ) => {
1185
1191
// Try fallback io::copy if either:
1186
1192
// - Kernel version is < 4.5 (ENOSYS)
1187
1193
// - Files are mounted on different fs (EXDEV)
1194
+ // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
1188
1195
// - copy_file_range is disallowed, for example by seccomp (EPERM)
1189
1196
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
1190
1197
assert_eq ! ( written, 0 ) ;
0 commit comments