Opened 5 years ago

Last modified 5 years ago

#13189 new Bugs

copy throws exception from a function defined as noexcept

Reported by: kukkerman@… 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 charan <harisaicharan111.challa@…>, 5 years ago

i want to work on this bug

comment:2 by charan <harisaicharan111.challa@…>, 5 years ago

can i get more information regarding this bug?

comment:3 by kukkerman@…, 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 Gábor Szuromi <kukkerman@…>, 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());
    }
  }
Note: See TracTickets for help on using tickets.