Skip to content

Commit de921ab

Browse files
committed
Auto merge of #75428 - the8472:fix-copy-eopnotsupp, r=joshtriplett
Workarounds for copy_file_range issues fixes #75387 fixes #75446
2 parents 04f44fb + 4ddedd5 commit de921ab

File tree

1 file changed

+17
-10
lines changed
  • library/std/src/sys/unix

1 file changed

+17
-10
lines changed

library/std/src/sys/unix/fs.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)