Opened 5 years ago
Last modified 5 years ago
#13189 new Bugs
copy throws exception from a function defined as noexcept
| Reported by: | Owned by: | Beman Dawes | |
|---|---|---|---|
| Milestone: | To Be Determined | Component: | filesystem |
| Version: | Boost 1.63.0 | Severity: | Problem |
| Keywords: | Cc: |
Description
When copy fails because access to the file specified by from is denied, the corresponding exception reaches a function defined as noexcept. Stack trace:
bool <anon. namespace>::error(err_t error_num, const path& p1,
const path& p2, error_code* ec,
const char* message)
void detail::copy_file(const path& from, const path& to,
copy_option option, error_code* ec)
void copy_file(const path& from, const path& to,
BOOST_SCOPED_ENUM(copy_option) option,
system::error_code& ec) BOOST_NOEXCEPT
void detail::copy(const path& from, const path& to,
system::error_code* ec)
void copy(const path& from, const path& to)
The exception is thrown from error, and isn't caught anywhere along the calling path. The problem is that copy_file is defined as noexcept (BOOST_NOEXCEPT to be precise, but it is resolved as noexcept under a C++11 compliant compiler), and doesn't handle the exception either, which causes program termination by implicitly calling std::terminate.
Change History (4)
comment:1 by , 5 years ago
comment:3 by , 5 years ago
The root cause of the problem is that copy(from, to) calls detail::copy(from, to) but the declaration of detail::copy is:
void copy(const path& from, const path& to, system::error_code* ec=0);
That way ec will be a null pointer. The next call in the chain in case of a regular file will be copy_file(from, to, fs::copy_option::fail_if_exists, *ec) Looking at the declaration of copy_file
void copy_file(const path& from, const path& to, // See ticket #2925
BOOST_SCOPED_ENUM(copy_option) option, system::error_code& ec) BOOST_NOEXCEPT
we can see that ec will be a null reference. The next call will be detail::copy_file(from, to, static_cast<detail::copy_option>(option), &ec) which finally calls the function error:
bool error(err_t error_num, error_code* ec, const char* message)
{
if (!error_num)
{
if (ec != 0) ec->clear();
}
else
{ // error
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error(message,
error_code(error_num, system_category())));
else
ec->assign(error_num, system_category());
}
return error_num != 0;
}
which will throw an exception because ec is a null pointer. This exception will propagate up to
void copy_file(const path& from, const path& to, // See ticket #2925
BOOST_SCOPED_ENUM(copy_option) option, system::error_code& ec) BOOST_NOEXCEPT
which is a noexcept function.
comment:4 by , 5 years ago
After running a few tests not just filesystem::copy_file but filesystem::copy_symlink and filesystem::copy_directory functions are affected depending on what kind of file from points to. All of these errors can easily be fixed by rewriting filesystem::detail::copy to call functions defined in filesystem::detail namespace instead of filesystem namespace. Because I was unable to attach a patch file to the ticket I'm pasting the patched filesystem::detail::copy function here:
BOOST_FILESYSTEM_DECL
void copy(const path& from, const path& to, system::error_code* ec)
{
file_status s(symlink_status(from, *ec));
if (ec != 0 && *ec) return;
if(is_symlink(s))
{
copy_symlink(from, to, ec);
}
else if(is_directory(s))
{
copy_directory(from, to, ec);
}
else if(is_regular_file(s))
{
copy_file(from, to, fs::detail::copy_option::fail_if_exists, ec);
}
else
{
if (ec == 0)
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::copy",
from, to, error_code(BOOST_ERROR_NOT_SUPPORTED, system_category())));
ec->assign(BOOST_ERROR_NOT_SUPPORTED, system_category());
}
}

i want to work on this bug