From 5117e662ba6a7a728d84dd04ca8a83a7dee3fed2 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 14 Jul 2022 12:52:42 +0100 Subject: [PATCH] Add patch to fix filesystem::copy_file EXDEV handling Resolves: #2106878 --- boost-1.76.0-filesystem-copy_file-exdev.patch | 152 ++++++++++++++++++ boost.spec | 6 + 2 files changed, 158 insertions(+) create mode 100644 boost-1.76.0-filesystem-copy_file-exdev.patch diff --git a/boost-1.76.0-filesystem-copy_file-exdev.patch b/boost-1.76.0-filesystem-copy_file-exdev.patch new file mode 100644 index 0000000..9d1b630 --- /dev/null +++ b/boost-1.76.0-filesystem-copy_file-exdev.patch @@ -0,0 +1,152 @@ +From 4b9052f1e0b2acf625e8247582f44acdcc78a4ce Mon Sep 17 00:00:00 2001 +From: Andrey Semashev +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 ++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 = ©_file_data_read_write; ++atomic_ns::atomic< copy_file_data_t* > copy_file_data(©_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(©_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(©_file_data_sendfile, atomic_ns::memory_order_relaxed); ++ goto fallback_to_sendfile; ++#else ++ copy_file_data.store(©_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 = ©_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 + diff --git a/boost.spec b/boost.spec index 3e0edde..6e931ea 100644 --- a/boost.spec +++ b/boost.spec @@ -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 - 1.76.0-4 - Third attempt at making the long double c99 and tr1 math libs conditional