Workaround for copy_file_range spuriously returning EOPNOTSUPP when attemted on a NFS mount under RHEL/CentOS 7.

The syscall is supposed to return ENOSYS in most cases but when calling it on NFS it may leak through
EOPNOTSUPP even though that's supposed to be handled by the kernel and not returned to userspace.
Since it returns ENOSYS in some cases anyway this will trip the  HAS_COPY_FILE_RANGE
detection anyway, so treat EOPNOTSUPP as if it were a ENOSYS.

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/7.8_release_notes/deprecated_functionality#the_literal_copy_file_range_literal_call_has_been_disabled_on_local_file_systems_and_in_nfs
https://bugzilla.redhat.com/show_bug.cgi?id=1783554
This commit is contained in:
The8472 2020-08-12 01:15:08 +02:00
parent e5e33ebd2b
commit 1316c786a0

View file

@ -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) | Some(libc::EPERM) | Some(libc::EOPNOTSUPP) => {
HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
}
_ => {}
@ -1180,11 +1180,13 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
if os_err == libc::ENOSYS
|| os_err == libc::EXDEV
|| os_err == libc::EINVAL
|| os_err == libc::EPERM =>
|| os_err == libc::EPERM
|| os_err == 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);