Add patch to fix filesystem::copy_file EXDEV handling

Resolves: #2106878
This commit is contained in:
Jonathan Wakely 2022-07-14 12:52:42 +01:00
parent 2844380e87
commit 5117e662ba
2 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,152 @@
From 4b9052f1e0b2acf625e8247582f44acdcc78a4ce Mon Sep 17 00:00:00 2001
From: Andrey Semashev <andrey.semashev@gmail.com>
Date: Tue, 18 May 2021 22:53:40 +0300
Subject: [PATCH] Fallback to read/write loop if sendfile/copy_file_range fail.
Since sendfile and copy_file_range can fail for some filesystems
(e.g. eCryptFS), we have to fallback to the read/write loop in copy_file
implementation. Additionally, since we implement the fallback now,
fallback to sendfile if copy_file_range fails with EXDEV and use
copy_file_range on older kernels that don't implement it for
cross-filesystem copying. This may be beneficial if copy_file_range
is used within a filesystem, and is performed on a remote server NFS or CIFS).
Also, it was discovered that copy_file_range can also fail with EOPNOTSUPP
when it is performed on an NFSv4 filesystem and the remote server does
not support COPY operation. This happens on some patched kernels in RHEL/CentOS.
Lastly, to make sure the copy_file_data pointer is accessed atomically,
it is now declared as an atomic value. If std::atomic is unavailable,
Boost.Atomic is used.
Fixes https://github.com/boostorg/filesystem/issues/184.
---
diff --git a/src/operations.cpp b/src/operations.cpp
index abc7e4f6e..8f1130f00 100644
--- a/libs/filesystem/src/operations.cpp
+++ b/libs/filesystem/src/operations.cpp
@@ -135,6 +135,8 @@ using std::time_t;
# endif // BOOST_WINDOWS_API
#include "error_handling.hpp"
+#include <atomic>
+namespace atomic_ns = std;
namespace fs = boost::filesystem;
using boost::filesystem::path;
@@ -521,6 +522,9 @@ int copy_file_data_read_write(int infile, int outfile, uintmax_t size)
if (BOOST_UNLIKELY(!buf.get()))
return ENOMEM;
+ // Don't use file size to limit the amount of data to copy since some filesystems, like procfs or sysfs,
+ // provide files with generated content and indicate that their size is zero or 4096. Just copy as much data
+ // as we can read from the input file.
while (true)
{
ssize_t sz_read = ::read(infile, buf.get(), buf_sz);
@@ -555,7 +559,7 @@ int copy_file_data_read_write(int infile, int outfile, uintmax_t size)
}
//! Pointer to the actual implementation of the copy_file_data implementation
-copy_file_data_t* copy_file_data = &copy_file_data_read_write;
+atomic_ns::atomic< copy_file_data_t* > copy_file_data(&copy_file_data_read_write);
#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
@@ -577,6 +581,23 @@ int copy_file_data_sendfile(int infile, int outfile, uintmax_t size)
int err = errno;
if (err == EINTR)
continue;
+
+ if (offset == 0u)
+ {
+ // sendfile may fail with EINVAL if the underlying filesystem does not support it
+ if (err == EINVAL)
+ {
+ fallback_to_read_write:
+ return copy_file_data_read_write(infile, outfile, size);
+ }
+
+ if (err == ENOSYS)
+ {
+ copy_file_data.store(&copy_file_data_read_write, atomic_ns::memory_order_relaxed);
+ goto fallback_to_read_write;
+ }
+ }
+
return err;
}
@@ -611,6 +632,44 @@ int copy_file_data_copy_file_range(int infile, int outfile, uintmax_t size)
int err = errno;
if (err == EINTR)
continue;
+
+ if (offset == 0u)
+ {
+ // copy_file_range may fail with EINVAL if the underlying filesystem does not support it.
+ // In some RHEL/CentOS 7.7-7.8 kernel versions, copy_file_range on NFSv4 is also known to return EOPNOTSUPP
+ // if the remote server does not support COPY, despite that it is not a documented error code.
+ // See https://patchwork.kernel.org/project/linux-nfs/patch/20190411183418.4510-1-olga.kornievskaia@gmail.com/
+ // and https://bugzilla.redhat.com/show_bug.cgi?id=1783554.
+ if (err == EINVAL || err == EOPNOTSUPP)
+ {
+#if !defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ fallback_to_read_write:
+#endif
+ return copy_file_data_read_write(infile, outfile, size);
+ }
+
+ if (err == EXDEV)
+ {
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ fallback_to_sendfile:
+ return copy_file_data_sendfile(infile, outfile, size);
+#else
+ goto fallback_to_read_write;
+#endif
+ }
+
+ if (err == ENOSYS)
+ {
+#if defined(BOOST_FILESYSTEM_USE_SENDFILE)
+ copy_file_data.store(&copy_file_data_sendfile, atomic_ns::memory_order_relaxed);
+ goto fallback_to_sendfile;
+#else
+ copy_file_data.store(&copy_file_data_read_write, atomic_ns::memory_order_relaxed);
+ goto fallback_to_read_write;
+#endif
+ }
+ }
+
return err;
}
@@ -646,13 +705,14 @@ struct copy_file_data_initializer
#endif
#if defined(BOOST_FILESYSTEM_USE_COPY_FILE_RANGE)
- // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3
- if (major > 5u || (major == 5u && minor >= 3u))
+ // Although copy_file_range appeared in Linux 4.5, it did not support cross-filesystem copying until 5.3.
+ // copy_file_data_copy_file_range will fallback to copy_file_data_sendfile if copy_file_range returns EXDEV.
+ if (major > 4u || (major == 4u && minor >= 5u))
cfd = &copy_file_data_copy_file_range;
#endif
- copy_file_data = cfd;
+ copy_file_data.store(cfd, atomic_ns::memory_order_relaxed);
}
}
const copy_file_data_init;
@@ -1412,7 +1472,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod
goto fail_errno;
}
- err = detail::copy_file_data(infile.fd, outfile.fd, get_size(from_stat));
+ err = detail::copy_file_data.load(atomic_ns::memory_order_relaxed)(infile.fd, outfile.fd, get_size(from_stat));
if (BOOST_UNLIKELY(err != 0))
goto fail; // err already contains the error code

View File

@ -168,6 +168,10 @@ Patch105: boost-1.76.0-ptr_cont-xml.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2106441
Patch106: boost-1.76.0-asio-header.patch
# https://bugzilla.redhat.com/show_bug.cgi?id=2106878
# https://github.com/boostorg/filesystem/issues/184
Patch107: boost-1.76.0-filesystem-copy_file-exdev.patch
%bcond_with tests
%bcond_with docs_generated
@ -694,6 +698,7 @@ find ./boost -name '*.hpp' -perm /111 | xargs chmod a-x
%patch102 -p1
%patch105 -p1
%patch106 -p2
%patch107 -p1
%build
%set_build_flags
@ -1311,6 +1316,7 @@ fi
- Add patch to fix XML validation errors in ptr_container docs
- Add BuildRequires: libzstd-devel (#2042336)
- Add patch to fix Asio includes (#2106441)
- Add patch to fix filesystem::copy_file EXDEV handling (#2106878)
* Thu Aug 05 2021 Thomas Rodgers <trodgers@redhat.com> - 1.76.0-4
- Third attempt at making the long double c99 and tr1 math libs conditional