From fc243122b9ece2f5f35ac637ed0465c5bfcc3821 Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Sat, 13 Jan 2024 17:22:16 +0300 Subject: [PATCH] Added a copy_options::ignore_attribute_errors option for copy_file/copy. The new option allows to ignore errors while copying file attributes (but not file contents). Closes https://github.com/boostorg/filesystem/issues/179. --- doc/reference.html | 10 +++++++--- doc/release_history.html | 1 + include/boost/filesystem/operations.hpp | 25 +++++++++++++------------ src/operations.cpp | 9 ++++++--- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/doc/reference.html b/doc/reference.html index b6a32e699..9d7edf8f8 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -708,6 +708,7 @@

Header <boost/filesystem.hpp&g update_existing, synchronize_data, synchronize, + ignore_attribute_errors, // copy options recursive, copy_symlinks, @@ -3198,6 +3199,7 @@

Operational functions [fs.op.funcs]

  • copy_options::skip_existing, copy_options::overwrite_existing or copy_options::update_existing;
  • copy_options::synchronize_data or copy_options::synchronize;
  • +
  • copy_options::ignore_attribute_errors;
  • copy_options::recursive;
  • copy_options::copy_symlinks or copy_options::skip_symlinks;
  • copy_options::directories_only, copy_options::create_symlinks or copy_options::create_hard_links.
  • @@ -3290,7 +3292,8 @@

    Operational functions [fs.op.funcs]

    Precondition: options must contain at most one option from each of the following groups:

    • copy_options::skip_existing, copy_options::overwrite_existing or copy_options::update_existing;
    • -
    • copy_options::synchronize_data or copy_options::synchronize.
    • +
    • copy_options::synchronize_data or copy_options::synchronize;
    • +
    • copy_options::ignore_attribute_errors.

    Effects: Report an error if: @@ -3303,11 +3306,11 @@

    Operational functions [fs.op.funcs]

    Otherwise, return successfully with no effect if:
    • exists(to) && (options & copy_options::skip_existing) != copy_options::none, or
    • -
    • exists(to) && (options & copy_options::update_existing) != copy_options::none and last write time of from is more recent than that of to.
    • +
    • exists(to) && (options & copy_options::update_existing) != copy_options::none and last write time of to is more recent than that of from.
    Otherwise:
      -
    • The contents and attributes of the file from resolves to are copied to the file to resolves to; then
    • +
    • The contents and attributes of the file from resolves to are copied to the file to resolves to. If copying file attributes (but not contents) fails with an error and (options & copy_options::ignore_attribute_errors) != copy_options::none then that error is ignored. After that,
    • If (options & copy_options::synchronize) != copy_options::none, the written data and attributes are synchronized with the permanent storage; otherwise
    • If (options & copy_options::synchronize_data) != copy_options::none, the written data is synchronized with the permanent storage.
    @@ -3317,6 +3320,7 @@

    Operational functions [fs.op.funcs]

    [Note: The overloads taking copy_option are deprecated. Their effect is equivalent to the corresponding overloads taking copy_options after casting the options argument to copy_options.]

    [Note: When copy_options::update_existing is specified, checking the write times of from and to may not be atomic with the copy operation. Another process may create or modify the file identified by to after the file modification times have been checked but before copying starts. In this case the target file will be overwritten.]

    [Note: The copy_options::synchronize_data and copy_options::synchronize options may have a significant performance impact. The copy_options::synchronize_data option may be less expensive than copy_options::synchronize. However, without these options, upon returning from copy_file it is not guaranteed that the copied file is completely written and preserved in case of a system failure. Any delayed write operations may fail after the function returns, at the point of physically writing the data to the underlying media, and this error will not be reported to the caller.]

    +

    [Note: The copy_options::ignore_attribute_errors option can be used when the caller does not require file attributes to be copied. The implementation is permitted to make an attempt to copy the file attributes, but still succeed the file copying operation if that attempt fails. This option may be useful with file systems that do not fully support operations of file attributes.]

    void copy_symlink(const path& existing_symlink, const path& new_symlink);
     void copy_symlink(const path& existing_symlink, const path& new_symlink, system::error_code& ec);
    diff --git a/doc/release_history.html b/doc/release_history.html index 598066480..47d0cf6e7 100644 --- a/doc/release_history.html +++ b/doc/release_history.html @@ -47,6 +47,7 @@

    1.85.0

  • v4: absolute now returns a path with a trailing directory separator when the input path has an empty relative_path(). (#301)
  • Added a unique_path overload taking a single error_code& ec argument. The overload generates a unique path using the default path model.
  • weakly_canonical now produces an absolute path if the input path is relative and contains no elements that exist in the filesystem. (#300)
  • +
  • Added a new copy_options::ignore_attribute_errors option for copy_file and copy operations. The new option allows to ignore possible errors while copying file attributes. (#179)
  • On Linux, copy_file backends based on sendfile and copy_file_range system calls will attempt to preallocate storage for the target file. This may reduce filesystem fragmentation and provide early error indication if there is not enough free space. Not all filesystems support this feature; file copying proceeds if storage preallocation is not supported.
diff --git a/include/boost/filesystem/operations.hpp b/include/boost/filesystem/operations.hpp index ab2103a22..b556e668c 100644 --- a/include/boost/filesystem/operations.hpp +++ b/include/boost/filesystem/operations.hpp @@ -54,20 +54,21 @@ BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(copy_options, unsigned int) none = 0u, // Default. For copy_file: error if the target file exists. For copy: do not recurse, follow symlinks, copy file contents. // copy_file options: - skip_existing = 1u, // Don't overwrite the existing target file, don't report an error - overwrite_existing = 1u << 1, // Overwrite existing file - update_existing = 1u << 2, // Overwrite existing file if its last write time is older than the replacement file - synchronize_data = 1u << 3, // Flush all buffered data written to the target file to permanent storage - synchronize = 1u << 4, // Flush all buffered data and attributes written to the target file to permanent storage + skip_existing = 1u, // Don't overwrite the existing target file, don't report an error + overwrite_existing = 1u << 1u, // Overwrite existing file + update_existing = 1u << 2u, // Overwrite existing file if its last write time is older than the replacement file + synchronize_data = 1u << 3u, // Flush all buffered data written to the target file to permanent storage + synchronize = 1u << 4u, // Flush all buffered data and attributes written to the target file to permanent storage + ignore_attribute_errors = 1u << 5u, // Ignore errors of copying file attributes // copy options: - recursive = 1u << 8, // Recurse into sub-directories - copy_symlinks = 1u << 9, // Copy symlinks as symlinks instead of copying the referenced file - skip_symlinks = 1u << 10, // Don't copy symlinks - directories_only = 1u << 11, // Only copy directory structure, do not copy non-directory files - create_symlinks = 1u << 12, // Create symlinks instead of copying files - create_hard_links = 1u << 13, // Create hard links instead of copying files - _detail_recursing = 1u << 14 // Internal use only, do not use + recursive = 1u << 8u, // Recurse into sub-directories + copy_symlinks = 1u << 9u, // Copy symlinks as symlinks instead of copying the referenced file + skip_symlinks = 1u << 10u, // Don't copy symlinks + directories_only = 1u << 11u, // Only copy directory structure, do not copy non-directory files + create_symlinks = 1u << 12u, // Create symlinks instead of copying files + create_hard_links = 1u << 13u, // Create hard links instead of copying files + _detail_recursing = 1u << 14u // Internal use only, do not use } BOOST_SCOPED_ENUM_DECLARE_END(copy_options) diff --git a/src/operations.cpp b/src/operations.cpp index 3ffa49980..8e3af57aa 100644 --- a/src/operations.cpp +++ b/src/operations.cpp @@ -3061,7 +3061,7 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod goto fail; } - mode_t to_mode = from_mode; + mode_t to_mode = from_mode & fs::perms_mask; #if !defined(BOOST_FILESYSTEM_USE_WASI) // Enable writing for the newly created files. Having write permission set is important e.g. for NFS, // which checks the file permission on the server, even if the client's file descriptor supports writing. @@ -3184,10 +3184,13 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod #if !defined(BOOST_FILESYSTEM_USE_WASI) // If we created a new file with an explicitly added S_IWUSR permission, // we may need to update its mode bits to match the source file. - if (to_mode != from_mode) + if ((to_mode & fs::perms_mask) != (from_mode & fs::perms_mask)) { - if (BOOST_UNLIKELY(::fchmod(outfile.fd, from_mode) != 0)) + if (BOOST_UNLIKELY(::fchmod(outfile.fd, (from_mode & fs::perms_mask)) != 0 && + (options & static_cast< unsigned int >(copy_options::ignore_attribute_errors)) == 0u)) + { goto fail_errno; + } } #endif