mirror of
https://github.com/boostorg/filesystem.git
synced 2025-05-12 13:41:47 +00:00
Refactored copy_file, added copy_options, deprecated copy_option.
The copy_file operation implementation has been inlined into the detail::copy_file function. The part that copies the file body has been extracted to a separate function, so that addition of specialized copy implementations later is possible. Added copy_options enum, which reflects the enum from C++20. Currently, only overwrite_existing option is supported. Other options will be added later. The old enum copy_option is deprecated in favor of copy_options. Updated docs to reflect recent changes to copy_file behavior.
This commit is contained in:
parent
4f3d762a31
commit
f199152b7d
@ -748,7 +748,7 @@ nothing else."</p>
|
||||
{
|
||||
status_error, file_not_found, regular_file, directory_file,
|
||||
symlink_file, block_file, character_file, fifo_file, socket_file,
|
||||
type_unknown
|
||||
reparse_file, type_unknown
|
||||
};
|
||||
|
||||
enum <a href="#Enum-perms">perms</a>
|
||||
@ -771,16 +771,23 @@ nothing else."</p>
|
||||
uintmax_t available; // free space available to non-privileged process
|
||||
};
|
||||
|
||||
enum class <a name="copy_options">copy_options</a>
|
||||
{
|
||||
none,
|
||||
overwrite_existing
|
||||
};
|
||||
|
||||
// Deprecated, use <a href="#copy_options">copy_options</a> instead
|
||||
enum class <a name="copy_option">copy_option</a>
|
||||
{
|
||||
none
|
||||
none = copy_options::none,
|
||||
fail_if_exists = none,
|
||||
overwrite_if_exists
|
||||
overwrite_if_exists = copy_options::overwrite_existing
|
||||
};
|
||||
|
||||
enum class <a name="directory_options">directory_options</a>
|
||||
{
|
||||
none
|
||||
none,
|
||||
skip_permission_denied,
|
||||
follow_directory_symlink,
|
||||
pop_on_error
|
||||
@ -789,7 +796,7 @@ nothing else."</p>
|
||||
// Deprecated, use <a href="#directory_options">directory_options</a> instead
|
||||
enum class <a name="symlink_option">symlink_option</a>
|
||||
{
|
||||
none = directory_options::none
|
||||
none = directory_options::none,
|
||||
no_recurse = none,
|
||||
recurse = directory_options::follow_directory_symlink
|
||||
};
|
||||
@ -815,9 +822,14 @@ nothing else."</p>
|
||||
void <a href="#copy_file">copy_file</a>(const path& from, const path& to,
|
||||
system::error_code& ec);
|
||||
void <a href="#copy_file">copy_file</a>(const path& from, const path& to,
|
||||
<a href="#copy_option">copy_option</a> option);
|
||||
<a href="#copy_options">copy_options</a> options);
|
||||
void <a href="#copy_file">copy_file</a>(const path& from, const path& to,
|
||||
<a href="#copy_option">copy_option</a> option, system::error_code& ec);
|
||||
<a href="#copy_options">copy_options</a> options, system::error_code& ec);
|
||||
// Deprecated, use overloads taking <a href="#copy_options">copy_options</a> instead
|
||||
void <a href="#copy_file">copy_file</a>(const path& from, const path& to,
|
||||
<a href="#copy_option">copy_option</a> options);
|
||||
void <a href="#copy_file">copy_file</a>(const path& from, const path& to,
|
||||
<a href="#copy_option">copy_option</a> options, system::error_code& ec);
|
||||
|
||||
void <a href="#copy_symlink">copy_symlink</a>(const path& existing_symlink,
|
||||
const path& new_symlink);
|
||||
@ -3078,7 +3090,7 @@ if(is_symlink(s))
|
||||
else if(is_directory(s))
|
||||
copy_directory(from, to<i>[</i><code>, ec</code><i>]</i>);
|
||||
else if(is_regular_file(s))
|
||||
copy_file(from, to, copy_option::fail_if_exists<i>[</i><code>, ec</code><i>]</i>);
|
||||
copy_file(from, to, copy_options::none<i>[</i><code>, ec</code><i>]</i>);
|
||||
else
|
||||
<i> Report error as specified in <a href="#Error-reporting">Error reporting</a>.</i></pre>
|
||||
</blockquote>
|
||||
@ -3123,16 +3135,26 @@ void copy_directory(const path& from, const path& to, system::error_code
|
||||
<pre>void copy_file(const path& from, const path& to);
|
||||
void copy_file(const path& from, const path& to, system::error_code& ec);</pre>
|
||||
<blockquote>
|
||||
<p><i>Effects: </i><code>copy_file(from, to, copy_option::fail_if_exists</code><i>[</i><code>, ec</code><i>]</i><code>)</code>.</p>
|
||||
<p><i>Effects: </i><code>copy_file(from, to, copy_options::none</code><i>[</i><code>, ec</code><i>]</i><code>)</code>.</p>
|
||||
|
||||
<p><i>Throws:</i> As specified in <a href="#Error-reporting">Error reporting</a>.</p>
|
||||
|
||||
</blockquote>
|
||||
<pre>void <a name="copy_file">copy_file</a>(const path& from, const path& to, <a href="#copy_option">copy_option</a> option);
|
||||
void <a name="copy_file2">copy_file</a>(const path& from, const path& to, <a href="#copy_option">copy_option</a> option, system::error_code& ec);</pre>
|
||||
<pre>void <a name="copy_file">copy_file</a>(const path& from, const path& to, <a href="#copy_options">copy_options</a> options);
|
||||
void <a name="copy_file2">copy_file</a>(const path& from, const path& to, <a href="#copy_options">copy_options</a> options, system::error_code& ec);
|
||||
void <a name="copy_file3">copy_file</a>(const path& from, const path& to, <a href="#copy_option">copy_option</a> options);
|
||||
void <a name="copy_file4">copy_file</a>(const path& from, const path& to, <a href="#copy_option">copy_option</a> options, system::error_code& ec);</pre>
|
||||
<blockquote>
|
||||
<p><i>Effects:</i> If <code>option == copy_option::</code><code>fail_if_exists && exists(to)</code>, an error is reported. Otherwise, the contents and attributes of the file <code>from</code> resolves to are copied to the file <code>to</code> resolves to.</p>
|
||||
<p><i>Effects:</i> Report an error if:
|
||||
<ul>
|
||||
<li><code>!is_regular_file(from)</code>, or</li>
|
||||
<li><code>exists(to) && !is_regular_file(to)</code>, or</li>
|
||||
<li><code>exists(to) && equivalent(from, to)</code>, or</li>
|
||||
<li><code>exists(to) && (options & copy_options::overwrite_existing) == copy_options::none</code>.</li>
|
||||
</ul>
|
||||
Otherwise, the contents and attributes of the file <code>from</code> resolves to are copied to the file <code>to</code> resolves to.</p>
|
||||
<p><i>Throws:</i> As specified in <a href="#Error-reporting">Error reporting</a>.</p>
|
||||
<p>[<i>Note:</i> The overloads taking <a href="#copy_option"><code>copy_option</code></a> are deprecated. Their effect is equivalent to the corresponding overloads taking <a href="#copy_options"><code>copy_options</code></a> after casting the <code>options</code> argument to <a href="#copy_options"><code>copy_options</code></a>.]</p>
|
||||
</blockquote>
|
||||
<pre>void <a name="copy_symlink">copy_symlink</a>(const path& existing_symlink, const path& new_symlink);
|
||||
void copy_symlink(const path& existing_symlink, const path& new_symlink, system::error_code& ec);</pre>
|
||||
|
@ -45,6 +45,7 @@
|
||||
<li>Fixed that <code>read_symlink</code> on Windows could potentially fail or cause failures elsewhere with a sharing violation error, if the same symlink was opened concurrently. (<a href="https://github.com/boostorg/filesystem/issues/138">#138</a>)</li>
|
||||
<li><code>copy_file</code> implementation has been updated to perform checks on the source and target files, as required by C++20 ([fs.op.copy.file]/4.1). In particular, the operation will fail if the source or target file is not a regular file or the source and target paths identify the same file.</li>
|
||||
<li><code>copy_file</code> on POSIX systems will now also copy the source file permissions to the target file, if the target file is overwritten.</li>
|
||||
<li><b>Deprecated:</b> The <code>copy_option</code> enumeration that is used with the <code>copy_file</code> operation is deprecated. As a replacement, the new enum <code>copy_options</code> (note the trailing 's') has been added. The new enum contains values similar to the <code>copy_options</code> enum from C++20. The old enum values are mapped onto the new enum. The old enum will be removed in a future release.</li>
|
||||
<li><code>equivalent</code> on POSIX systems now returns the actual error code from the OS if one of the paths does not resolve to a file. Previously the function would return an error code of 1. (<a href="https://github.com/boostorg/filesystem/issues/141">#141</a>)</li>
|
||||
<li><code>equivalent</code> no longer considers file size and last modification time in order to test whether the two paths refer to the same file. These checks could result in a false negative if the file was modified during the <code>equivalent</code> call.</li>
|
||||
<li><code>space</code> now initializes the <code>space_info</code> structure members to -1 values on error, as required by C++20 ([fs.op.space]/1).</li>
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <boost/filesystem/directory.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/detail/bitmask.hpp>
|
||||
#include <boost/core/scoped_enum.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
@ -52,9 +53,24 @@ struct space_info
|
||||
boost::uintmax_t available; // <= free
|
||||
};
|
||||
|
||||
BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(copy_options, unsigned int)
|
||||
{
|
||||
none = 0u, // Default, error if the target file exists
|
||||
overwrite_existing = 1u, // Overwrite existing file
|
||||
}
|
||||
BOOST_SCOPED_ENUM_DECLARE_END(copy_options)
|
||||
|
||||
BOOST_BITMASK(BOOST_SCOPED_ENUM_NATIVE(copy_options))
|
||||
|
||||
#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED)
|
||||
BOOST_SCOPED_ENUM_DECLARE_BEGIN(copy_option)
|
||||
{none=0, fail_if_exists = none, overwrite_if_exists}
|
||||
{
|
||||
none = static_cast< unsigned int >(copy_options::none),
|
||||
fail_if_exists = none,
|
||||
overwrite_if_exists = static_cast< unsigned int >(copy_options::overwrite_existing)
|
||||
}
|
||||
BOOST_SCOPED_ENUM_DECLARE_END(copy_option)
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
// implementation details //
|
||||
@ -62,11 +78,6 @@ BOOST_SCOPED_ENUM_DECLARE_END(copy_option)
|
||||
|
||||
namespace detail {
|
||||
|
||||
// We cannot pass a BOOST_SCOPED_ENUM to a compled function because it will result
|
||||
// in an undefined reference if the library is compled with -std=c++0x but the use
|
||||
// is compiled in C++03 mode, or vice versa. See tickets 6124, 6779, 10038.
|
||||
enum copy_option {none=0, fail_if_exists = none, overwrite_if_exists};
|
||||
|
||||
BOOST_FILESYSTEM_DECL
|
||||
file_status status(const path&p, system::error_code* ec=0);
|
||||
BOOST_FILESYSTEM_DECL
|
||||
@ -83,7 +94,7 @@ BOOST_FILESYSTEM_DECL
|
||||
void copy_directory(const path& from, const path& to, system::error_code* ec=0);
|
||||
BOOST_FILESYSTEM_DECL
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
detail::copy_option option, system::error_code* ec=0);
|
||||
unsigned int options, system::error_code* ec=0); // see copy_options for options
|
||||
BOOST_FILESYSTEM_DECL
|
||||
void copy_symlink(const path& existing_symlink, const path& new_symlink, system::error_code* ec=0);
|
||||
BOOST_FILESYSTEM_DECL
|
||||
@ -246,28 +257,42 @@ inline
|
||||
void copy_directory(const path& from, const path& to, system::error_code& ec) BOOST_NOEXCEPT
|
||||
{detail::copy_directory(from, to, &ec);}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_option) option)
|
||||
{
|
||||
detail::copy_file(from, to, static_cast<detail::copy_option>(option));
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to)
|
||||
{
|
||||
detail::copy_file(from, to, detail::fail_if_exists);
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_option) option, system::error_code& ec) BOOST_NOEXCEPT
|
||||
{
|
||||
detail::copy_file(from, to, static_cast<detail::copy_option>(option), &ec);
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(copy_options::none));
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, system::error_code& ec) BOOST_NOEXCEPT
|
||||
{
|
||||
detail::copy_file(from, to, detail::fail_if_exists, &ec);
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(copy_options::none), &ec);
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_options) options)
|
||||
{
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(options));
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_options) options, system::error_code& ec) BOOST_NOEXCEPT
|
||||
{
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(options), &ec);
|
||||
}
|
||||
#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED)
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_option) options)
|
||||
{
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(options));
|
||||
}
|
||||
inline
|
||||
void copy_file(const path& from, const path& to, // See ticket #2925
|
||||
BOOST_SCOPED_ENUM_NATIVE(copy_option) options, system::error_code& ec) BOOST_NOEXCEPT
|
||||
{
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(options), &ec);
|
||||
}
|
||||
#endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED)
|
||||
inline
|
||||
void copy_symlink(const path& existing_symlink,
|
||||
const path& new_symlink) {detail::copy_symlink(existing_symlink, new_symlink);}
|
||||
|
||||
|
@ -254,7 +254,6 @@ union reparse_data_buffer
|
||||
# define BOOST_DELETE_FILE(P)(::unlink(P)== 0)
|
||||
# define BOOST_COPY_DIRECTORY(F,T)(!(::stat(from.c_str(), &from_stat)!= 0\
|
||||
|| ::mkdir(to.c_str(),from_stat.st_mode)!= 0))
|
||||
# define BOOST_COPY_FILE(F,T,FailIfExistsBool)copy_file_api(F, T, FailIfExistsBool)
|
||||
# define BOOST_MOVE_FILE(OLD,NEW)(::rename(OLD, NEW)== 0)
|
||||
# define BOOST_RESIZE_FILE(P,SZ)(::truncate(P, SZ)== 0)
|
||||
|
||||
@ -267,7 +266,6 @@ union reparse_data_buffer
|
||||
# define BOOST_REMOVE_DIRECTORY(P)(::RemoveDirectoryW(P)!= 0)
|
||||
# define BOOST_DELETE_FILE(P)(::DeleteFileW(P)!= 0)
|
||||
# define BOOST_COPY_DIRECTORY(F,T)(::CreateDirectoryExW(F, T, 0)!= 0)
|
||||
# define BOOST_COPY_FILE(F,T,FailIfExistsBool)(::CopyFileW(F, T, FailIfExistsBool)!= 0)
|
||||
# define BOOST_MOVE_FILE(OLD,NEW)(::MoveFileExW(OLD, NEW, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)!= 0)
|
||||
# define BOOST_RESIZE_FILE(P,SZ)(resize_file_api(P, SZ)!= 0)
|
||||
# define BOOST_READ_SYMLINK(P,T)
|
||||
@ -397,6 +395,21 @@ boost::uintmax_t remove_all_aux(const path& p, fs::file_type type,
|
||||
|
||||
BOOST_CONSTEXPR_OR_CONST char dot = '.';
|
||||
|
||||
struct fd_wrapper
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd_wrapper() BOOST_NOEXCEPT : fd(-1) {}
|
||||
explicit fd_wrapper(int fd) BOOST_NOEXCEPT : fd(fd) {}
|
||||
~fd_wrapper() BOOST_NOEXCEPT
|
||||
{
|
||||
if (fd >= 0)
|
||||
::close(fd);
|
||||
}
|
||||
BOOST_DELETED_FUNCTION(fd_wrapper(fd_wrapper const&))
|
||||
BOOST_DELETED_FUNCTION(fd_wrapper& operator= (fd_wrapper const&))
|
||||
};
|
||||
|
||||
inline bool not_found_error(int errval) BOOST_NOEXCEPT
|
||||
{
|
||||
return errval == ENOENT || errval == ENOTDIR;
|
||||
@ -410,78 +423,15 @@ inline bool equivalent_stat(struct ::stat const& s1, struct ::stat const& s2) BO
|
||||
return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino;
|
||||
}
|
||||
|
||||
bool // true if ok
|
||||
copy_file_api(const std::string& from_p,
|
||||
const std::string& to_p, bool fail_if_exists)
|
||||
typedef int (copy_file_data_t)(int infile, struct ::stat const& from_stat, int outfile, struct ::stat const& to_stat);
|
||||
|
||||
//! copy_file implementation that uses read/write loop
|
||||
int copy_file_data_read_write(int infile, struct ::stat const& from_stat, int outfile, struct ::stat const& to_stat)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
int infile = ::open(from_p.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (BOOST_UNLIKELY(infile < 0))
|
||||
return false;
|
||||
|
||||
struct ::stat from_stat = {};
|
||||
if (BOOST_UNLIKELY(::fstat(infile, &from_stat) != 0))
|
||||
{
|
||||
err = errno;
|
||||
|
||||
fail1:
|
||||
::close(infile);
|
||||
errno = err;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BOOST_UNLIKELY(!S_ISREG(from_stat.st_mode)))
|
||||
{
|
||||
err = ENOSYS;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
// 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.
|
||||
mode_t to_mode = from_stat.st_mode | S_IWUSR;
|
||||
|
||||
int oflag = O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC;
|
||||
if (fail_if_exists)
|
||||
oflag |= O_EXCL;
|
||||
int outfile = ::open(to_p.c_str(), oflag, to_mode);
|
||||
if (BOOST_UNLIKELY(outfile < 0))
|
||||
{
|
||||
err = errno;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
struct ::stat to_stat = {};
|
||||
if (BOOST_UNLIKELY(::fstat(outfile, &to_stat) != 0))
|
||||
{
|
||||
err = errno;
|
||||
|
||||
fail2:
|
||||
::close(outfile);
|
||||
::close(infile);
|
||||
errno = err;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BOOST_UNLIKELY(!S_ISREG(to_stat.st_mode)))
|
||||
{
|
||||
err = ENOSYS;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (BOOST_UNLIKELY(equivalent_stat(from_stat, to_stat)))
|
||||
{
|
||||
err = EEXIST;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR_OR_CONST std::size_t buf_sz = 65536u;
|
||||
boost::scoped_array<char> buf(new (std::nothrow) char [buf_sz]);
|
||||
boost::scoped_array<char> buf(new (std::nothrow) char[buf_sz]);
|
||||
if (BOOST_UNLIKELY(!buf.get()))
|
||||
{
|
||||
err = ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
return ENOMEM;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -490,10 +440,10 @@ copy_file_api(const std::string& from_p,
|
||||
break;
|
||||
if (BOOST_UNLIKELY(sz_read < 0))
|
||||
{
|
||||
err = errno;
|
||||
int err = errno;
|
||||
if (err == EINTR)
|
||||
continue;
|
||||
goto fail2;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
|
||||
@ -503,50 +453,22 @@ copy_file_api(const std::string& from_p,
|
||||
ssize_t sz = ::write(outfile, buf.get() + sz_wrote, static_cast< std::size_t >(sz_read - sz_wrote));
|
||||
if (BOOST_UNLIKELY(sz < 0))
|
||||
{
|
||||
err = errno;
|
||||
int err = errno;
|
||||
if (err == EINTR)
|
||||
continue;
|
||||
goto fail2;
|
||||
return err;
|
||||
}
|
||||
|
||||
sz_wrote += sz;
|
||||
}
|
||||
}
|
||||
|
||||
// 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_stat.st_mode != from_stat.st_mode)
|
||||
{
|
||||
if (BOOST_UNLIKELY(::fchmod(outfile, from_stat.st_mode) != 0))
|
||||
{
|
||||
err = errno;
|
||||
goto fail2;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR.
|
||||
// Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the
|
||||
// file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the
|
||||
// underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync
|
||||
// ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really
|
||||
// care at that point.
|
||||
#if defined(BOOST_FILESYSTEM_HAS_FDATASYNC)
|
||||
err = ::fdatasync(outfile);
|
||||
#else
|
||||
err = ::fsync(outfile);
|
||||
#endif
|
||||
if (BOOST_UNLIKELY(err != 0))
|
||||
{
|
||||
err = errno;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
::close(outfile);
|
||||
::close(infile);
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Pointer to the actual implementation of the copy_file_data implementation
|
||||
copy_file_data_t* copy_file_data = ©_file_data_read_write;
|
||||
|
||||
inline fs::file_type query_file_type(const path& p, error_code* ec)
|
||||
{
|
||||
return fs::detail::symlink_status(p, ec).type();
|
||||
@ -560,8 +482,6 @@ inline fs::file_type query_file_type(const path& p, error_code* ec)
|
||||
// //
|
||||
//--------------------------------------------------------------------------------------//
|
||||
|
||||
BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128;
|
||||
|
||||
BOOST_CONSTEXPR_OR_CONST wchar_t dot = L'.';
|
||||
|
||||
// Windows CE has no environment variables
|
||||
@ -627,13 +547,16 @@ void to_FILETIME(std::time_t t, FILETIME & ft)
|
||||
struct handle_wrapper
|
||||
{
|
||||
HANDLE handle;
|
||||
handle_wrapper(HANDLE h)
|
||||
: handle(h){}
|
||||
~handle_wrapper()
|
||||
|
||||
handle_wrapper() BOOST_NOEXCEPT : handle(INVALID_HANDLE_VALUE) {}
|
||||
explicit handle_wrapper(HANDLE h) BOOST_NOEXCEPT : handle(h) {}
|
||||
~handle_wrapper() BOOST_NOEXCEPT
|
||||
{
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
BOOST_DELETED_FUNCTION(handle_wrapper(handle_wrapper const&))
|
||||
BOOST_DELETED_FUNCTION(handle_wrapper& operator= (handle_wrapper const&))
|
||||
};
|
||||
|
||||
HANDLE create_file_handle(const path& p, DWORD dwDesiredAccess,
|
||||
@ -950,7 +873,7 @@ void copy(const path& from, const path& to, system::error_code* ec)
|
||||
}
|
||||
else if (is_regular_file(s))
|
||||
{
|
||||
detail::copy_file(from, to, detail::fail_if_exists, ec);
|
||||
detail::copy_file(from, to, static_cast< unsigned int >(copy_options::none), ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -972,11 +895,96 @@ void copy_directory(const path& from, const path& to, system::error_code* ec)
|
||||
}
|
||||
|
||||
BOOST_FILESYSTEM_DECL
|
||||
void copy_file(const path& from, const path& to, copy_option option, error_code* ec)
|
||||
void copy_file(const path& from, const path& to, unsigned int options, error_code* ec)
|
||||
{
|
||||
error(!BOOST_COPY_FILE(from.c_str(), to.c_str(),
|
||||
option == fail_if_exists) ? BOOST_ERRNO : 0,
|
||||
from, to, ec, "boost::filesystem::copy_file");
|
||||
if (ec)
|
||||
ec->clear();
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
|
||||
int err = 0;
|
||||
|
||||
fd_wrapper infile(::open(from.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (BOOST_UNLIKELY(infile.fd < 0))
|
||||
{
|
||||
fail_errno:
|
||||
err = errno;
|
||||
fail:
|
||||
error(err, from, to, ec, "boost::filesystem::copy_file");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ::stat from_stat = {};
|
||||
if (BOOST_UNLIKELY(::fstat(infile.fd, &from_stat) != 0))
|
||||
goto fail_errno;
|
||||
|
||||
if (BOOST_UNLIKELY(!S_ISREG(from_stat.st_mode)))
|
||||
{
|
||||
err = ENOSYS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// 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.
|
||||
mode_t to_mode = from_stat.st_mode | S_IWUSR;
|
||||
|
||||
int oflag = O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC;
|
||||
if ((options & static_cast< unsigned int >(copy_options::overwrite_existing)) == 0u)
|
||||
oflag |= O_EXCL;
|
||||
fd_wrapper outfile(::open(to.c_str(), oflag, to_mode));
|
||||
if (BOOST_UNLIKELY(outfile.fd < 0))
|
||||
goto fail_errno;
|
||||
|
||||
struct ::stat to_stat = {};
|
||||
if (BOOST_UNLIKELY(::fstat(outfile.fd, &to_stat) != 0))
|
||||
goto fail_errno;
|
||||
|
||||
if (BOOST_UNLIKELY(!S_ISREG(to_stat.st_mode)))
|
||||
{
|
||||
err = ENOSYS;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (BOOST_UNLIKELY(detail::equivalent_stat(from_stat, to_stat)))
|
||||
{
|
||||
err = EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = detail::copy_file_data(infile.fd, from_stat, outfile.fd, to_stat);
|
||||
if (BOOST_UNLIKELY(err != 0))
|
||||
goto fail; // err already contains the error code
|
||||
|
||||
// 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_stat.st_mode != from_stat.st_mode)
|
||||
{
|
||||
if (BOOST_UNLIKELY(::fchmod(outfile.fd, from_stat.st_mode) != 0))
|
||||
goto fail_errno;
|
||||
}
|
||||
|
||||
// Note: Use fsync/fdatasync followed by close to avoid dealing with the possibility of close failing with EINTR.
|
||||
// Even if close fails, including with EINTR, most operating systems (presumably, except HP-UX) will close the
|
||||
// file descriptor upon its return. This means that if an error happens later, when the OS flushes data to the
|
||||
// underlying media, this error will go unnoticed and we have no way to receive it from close. Calling fsync/fdatasync
|
||||
// ensures that all data have been written, and even if close fails for some unfathomable reason, we don't really
|
||||
// care at that point.
|
||||
#if defined(BOOST_FILESYSTEM_HAS_FDATASYNC)
|
||||
err = ::fdatasync(outfile.fd);
|
||||
#else
|
||||
err = ::fsync(outfile.fd);
|
||||
#endif
|
||||
if (BOOST_UNLIKELY(err != 0))
|
||||
goto fail_errno;
|
||||
|
||||
#else // defined(BOOST_POSIX_API)
|
||||
|
||||
const bool fail_if_exists = (options & static_cast< unsigned int >(copy_options::overwrite_existing)) == 0u;
|
||||
BOOL res = ::CopyFileW(from.c_str(), to.c_str(), fail_if_exists);
|
||||
if (BOOST_UNLIKELY(!res))
|
||||
error(BOOST_ERRNO, from, to, ec, "boost::filesystem::copy_file");
|
||||
|
||||
#endif // defined(BOOST_POSIX_API)
|
||||
}
|
||||
|
||||
BOOST_FILESYSTEM_DECL
|
||||
@ -2100,6 +2108,8 @@ path system_complete(const path& p, system::error_code* ec)
|
||||
if (ec != 0) ec->clear();
|
||||
return p;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = 128;
|
||||
wchar_t buf[buf_size];
|
||||
wchar_t* pfn;
|
||||
std::size_t len = get_full_path_name(p, buf_size, buf, &pfn);
|
||||
|
@ -7,8 +7,8 @@ void myFunc()
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
copy_option opt(copy_option::overwrite_if_exists);
|
||||
copy_options opt(copy_options::overwrite_existing);
|
||||
|
||||
copy_file(path("p1"),path("p2"),copy_option::overwrite_if_exists);
|
||||
copy_file(path("p1"),path("p2"),copy_options::overwrite_existing);
|
||||
// copy_file(path("p1"),path("p2"),opt);
|
||||
}
|
@ -1604,14 +1604,14 @@ namespace
|
||||
BOOST_TEST(copy_ex_ok);
|
||||
|
||||
copy_ex_ok = false;
|
||||
try { fs::copy_file(f1x, d1x / "f2", fs::copy_option::fail_if_exists); }
|
||||
try { fs::copy_file(f1x, d1x / "f2", fs::copy_options::none); }
|
||||
catch (const fs::filesystem_error &) { copy_ex_ok = true; }
|
||||
BOOST_TEST(copy_ex_ok);
|
||||
|
||||
create_file(d1x / "f2", "1234567890");
|
||||
BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 10U);
|
||||
copy_ex_ok = true;
|
||||
try { fs::copy_file(f1x, d1x / "f2", fs::copy_option::overwrite_if_exists); }
|
||||
try { fs::copy_file(f1x, d1x / "f2", fs::copy_options::overwrite_existing); }
|
||||
catch (const fs::filesystem_error &) { copy_ex_ok = false; }
|
||||
BOOST_TEST(copy_ex_ok);
|
||||
BOOST_TEST_EQ(fs::file_size(d1x / "f2"), 7U);
|
||||
|
Loading…
x
Reference in New Issue
Block a user