diff options
author | Eric Fiselier <eric@efcs.ca> | 2018-07-23 02:00:52 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2018-07-23 02:00:52 +0000 |
commit | 0ddb77a467e90256a57d06af8bf1900ec8890eaa (patch) | |
tree | 4e589af25d17d060f749bc939c3f6f59f2e44c9f /src | |
parent | e14f03815bb3485bb5ea795adb2c895853b3d1b8 (diff) | |
download | libcxx-0ddb77a467e90256a57d06af8bf1900ec8890eaa.tar.gz |
Implement filesystem_error::what() and improve reporting.
This patch implements the `what()` for filesystem errors. The message
includes the 'what_arg', any paths that were specified, and the
error code message.
Additionally this patch refactors how errors are created, making it easier
to report them correctly.
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@337664 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'src')
-rw-r--r-- | src/experimental/filesystem/directory_iterator.cpp | 130 | ||||
-rw-r--r-- | src/experimental/filesystem/filesystem_common.h | 150 | ||||
-rw-r--r-- | src/experimental/filesystem/operations.cpp | 587 |
3 files changed, 494 insertions, 373 deletions
diff --git a/src/experimental/filesystem/directory_iterator.cpp b/src/experimental/filesystem/directory_iterator.cpp index b0b31450e..60f234cb1 100644 --- a/src/experimental/filesystem/directory_iterator.cpp +++ b/src/experimental/filesystem/directory_iterator.cpp @@ -88,7 +88,7 @@ static file_time_type get_write_time(const WIN32_FIND_DATA& data) { } // namespace } // namespace detail -using detail::set_or_throw; +using detail::ErrorHandler; #if defined(_LIBCPP_WIN32API) class __dir_stream { @@ -231,27 +231,30 @@ public: directory_iterator::directory_iterator(const path& p, error_code *ec, directory_options opts) { - std::error_code m_ec; - __imp_ = make_shared<__dir_stream>(p, opts, m_ec); - if (ec) *ec = m_ec; - if (!__imp_->good()) { - __imp_.reset(); - if (m_ec) - set_or_throw(m_ec, ec, - "directory_iterator::directory_iterator(...)", p); - } + ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p); + + std::error_code m_ec; + __imp_ = make_shared<__dir_stream>(p, opts, m_ec); + if (ec) + *ec = m_ec; + if (!__imp_->good()) { + __imp_.reset(); + if (m_ec) + err.report(m_ec); + } } directory_iterator& directory_iterator::__increment(error_code *ec) { _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator"); + ErrorHandler<void> err("directory_iterator::operator++()", ec); + std::error_code m_ec; if (!__imp_->advance(m_ec)) { - __imp_.reset(); - if (m_ec) - set_or_throw(m_ec, ec, "directory_iterator::operator++()"); - } else { - if (ec) ec->clear(); + path root = std::move(__imp_->__root_); + __imp_.reset(); + if (m_ec) + err.report(m_ec, "at root \"%s\"", root); } return *this; @@ -273,15 +276,18 @@ recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code *ec) : __imp_(nullptr), __rec_(true) { - if (ec) ec->clear(); - std::error_code m_ec; - __dir_stream new_s(p, opt, m_ec); - if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p); - if (m_ec || !new_s.good()) return; - - __imp_ = _VSTD::make_shared<__shared_imp>(); - __imp_->__options_ = opt; - __imp_->__stack_.push(_VSTD::move(new_s)); + ErrorHandler<void> err("recursive_directory_iterator", ec, &p); + + std::error_code m_ec; + __dir_stream new_s(p, opt, m_ec); + if (m_ec) + err.report(m_ec); + if (m_ec || !new_s.good()) + return; + + __imp_ = _VSTD::make_shared<__shared_imp>(); + __imp_->__options_ = opt; + __imp_->__stack_.push(_VSTD::move(new_s)); } void recursive_directory_iterator::__pop(error_code* ec) @@ -321,42 +327,50 @@ recursive_directory_iterator::__increment(error_code *ec) } void recursive_directory_iterator::__advance(error_code* ec) { - // REQUIRES: ec must be cleared before calling this function. - const directory_iterator end_it; - auto& stack = __imp_->__stack_; - std::error_code m_ec; - while (stack.size() > 0) { - if (stack.top().advance(m_ec)) - return; - if (m_ec) break; - stack.pop(); - } - __imp_.reset(); - if (m_ec) - set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()"); -} + ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); -bool recursive_directory_iterator::__try_recursion(error_code *ec) { - bool rec_sym = - bool(options() & directory_options::follow_directory_symlink); - - auto& curr_it = __imp_->__stack_.top(); + const directory_iterator end_it; + auto& stack = __imp_->__stack_; + std::error_code m_ec; + while (stack.size() > 0) { + if (stack.top().advance(m_ec)) + return; + if (m_ec) + break; + stack.pop(); + } - bool skip_rec = false; - std::error_code m_ec; - if (!rec_sym) { - file_status st = curr_it.__entry_.symlink_status(m_ec); - if (m_ec && status_known(st)) - m_ec.clear(); - if (m_ec || is_symlink(st) || !is_directory(st)) - skip_rec = true; + if (m_ec) { + path root = std::move(stack.top().__root_); + __imp_.reset(); + err.report(m_ec, "at root \"%s\"", root); } else { - file_status st = curr_it.__entry_.status(m_ec); - if (m_ec && status_known(st)) - m_ec.clear(); - if (m_ec || !is_directory(st)) - skip_rec = true; + __imp_.reset(); } +} + +bool recursive_directory_iterator::__try_recursion(error_code *ec) { + ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); + + bool rec_sym = bool(options() & directory_options::follow_directory_symlink); + + auto& curr_it = __imp_->__stack_.top(); + + bool skip_rec = false; + std::error_code m_ec; + if (!rec_sym) { + file_status st = curr_it.__entry_.symlink_status(m_ec); + if (m_ec && status_known(st)) + m_ec.clear(); + if (m_ec || is_symlink(st) || !is_directory(st)) + skip_rec = true; + } else { + file_status st = curr_it.__entry_.status(m_ec); + if (m_ec && status_known(st)) + m_ec.clear(); + if (m_ec || !is_directory(st)) + skip_rec = true; + } if (!skip_rec) { __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec); @@ -371,9 +385,9 @@ bool recursive_directory_iterator::__try_recursion(error_code *ec) { if (m_ec.value() == EACCES && allow_eacess) { if (ec) ec->clear(); } else { + path at_ent = std::move(curr_it.__entry_.__p_); __imp_.reset(); - set_or_throw(m_ec, ec, - "recursive_directory_iterator::operator++()"); + err.report(m_ec, "attempting recursion into \"%s\"", at_ent); } } return false; diff --git a/src/experimental/filesystem/filesystem_common.h b/src/experimental/filesystem/filesystem_common.h index 187428309..5b1f4482a 100644 --- a/src/experimental/filesystem/filesystem_common.h +++ b/src/experimental/filesystem/filesystem_common.h @@ -11,6 +11,7 @@ #define FILESYSTEM_COMMON_H #include "experimental/__config" +#include "array" #include "chrono" #include "cstdlib" #include "climits" @@ -66,26 +67,149 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM namespace detail { namespace { +static std::string format_string_imp(const char* msg, ...) { + // we might need a second shot at this, so pre-emptivly make a copy + struct GuardVAList { + va_list& target; + bool active = true; + void clear() { + if (active) + va_end(target); + active = false; + } + ~GuardVAList() { + if (active) + va_end(target); + } + }; + va_list args; + va_start(args, msg); + GuardVAList args_guard = {args}; + + va_list args_cp; + va_copy(args_cp, args); + GuardVAList args_copy_guard = {args_cp}; + + std::array<char, 256> local_buff; + std::size_t size = local_buff.size(); + auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp); + + args_copy_guard.clear(); + + // handle empty expansion + if (ret == 0) + return std::string{}; + if (static_cast<std::size_t>(ret) < size) + return std::string(local_buff.data()); + + // we did not provide a long enough buffer on our first attempt. + // add 1 to size to account for null-byte in size cast to prevent overflow + size = static_cast<std::size_t>(ret) + 1; + auto buff_ptr = std::unique_ptr<char[]>(new char[size]); + ret = ::vsnprintf(buff_ptr.get(), size, msg, args); + return std::string(buff_ptr.get()); +} + +const char* unwrap(string const& s) { return s.c_str(); } +const char* unwrap(path const& p) { return p.native().c_str(); } +template <class Arg> +Arg const& unwrap(Arg const& a) { + static_assert(!is_class<Arg>::value, "cannot pass class here"); + return a; +} + +template <class... Args> +std::string format_string(const char* fmt, Args const&... args) { + return format_string_imp(fmt, unwrap(args)...); +} + std::error_code capture_errno() { _LIBCPP_ASSERT(errno, "Expected errno to be non-zero"); return std::error_code(errno, std::generic_category()); } -void set_or_throw(std::error_code const& m_ec, std::error_code* ec, - const char* msg, path const& p = {}, path const& p2 = {}) { - if (ec) { - *ec = m_ec; - } else { - string msg_s("std::experimental::filesystem::"); - msg_s += msg; - __throw_filesystem_error(msg_s, p, p2, m_ec); - } +template <class T> +T error_value(); +template <> +constexpr void error_value<void>() {} +template <> +constexpr bool error_value<bool>() { + return false; } - -void set_or_throw(std::error_code* ec, const char* msg, path const& p = {}, - path const& p2 = {}) { - return set_or_throw(capture_errno(), ec, msg, p, p2); +template <> +constexpr uintmax_t error_value<uintmax_t>() { + return uintmax_t(-1); } +template <> +constexpr file_time_type error_value<file_time_type>() { + return file_time_type::min(); +} +template <> +path error_value<path>() { + return {}; +} + +template <class T> +struct ErrorHandler { + const char* func_name; + error_code* ec = nullptr; + const path* p1 = nullptr; + const path* p2 = nullptr; + + ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr, + const path* p2 = nullptr) + : func_name(fname), ec(ec), p1(p1), p2(p2) { + if (ec) + ec->clear(); + } + + T report(const error_code& m_ec) const { + if (ec) { + *ec = m_ec; + return error_value<T>(); + } + string what = string("in ") + func_name; + switch (bool(p1) + bool(p2)) { + case 0: + __throw_filesystem_error(what, m_ec); + case 1: + __throw_filesystem_error(what, *p1, m_ec); + case 2: + __throw_filesystem_error(what, *p1, *p2, m_ec); + } + _LIBCPP_UNREACHABLE(); + } + + template <class... Args> + T report(const error_code& m_ec, const char* msg, Args const&... args) const { + if (ec) { + *ec = m_ec; + return error_value<T>(); + } + string what = + string("in ") + func_name + ": " + format_string(msg, args...); + switch (bool(p1) + bool(p2)) { + case 0: + __throw_filesystem_error(what, m_ec); + case 1: + __throw_filesystem_error(what, *p1, m_ec); + case 2: + __throw_filesystem_error(what, *p1, *p2, m_ec); + } + _LIBCPP_UNREACHABLE(); + } + + T report(errc const& err) const { return report(make_error_code(err)); } + + template <class... Args> + T report(errc const& err, const char* msg, Args const&... args) const { + return report(make_error_code(err), msg, args...); + } + +private: + ErrorHandler(ErrorHandler const&) = delete; + ErrorHandler& operator=(ErrorHandler const&) = delete; +}; namespace time_util { diff --git a/src/experimental/filesystem/operations.cpp b/src/experimental/filesystem/operations.cpp index cc744fb9b..a1e88b394 100644 --- a/src/experimental/filesystem/operations.cpp +++ b/src/experimental/filesystem/operations.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "experimental/filesystem" +#include "array" #include "iterator" #include "fstream" #include "random" /* for unique_path */ @@ -364,7 +365,8 @@ file_status create_file_status(std::error_code& m_ec, path const& p, if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) { return file_status(file_type::not_found); } else if (m_ec) { - set_or_throw(m_ec, ec, "posix_stat", p); + ErrorHandler<void> err("posix_stat", ec, &p); + err.report(m_ec, "failed to determine attributes for the specified path"); return file_status(file_type::none); } // else @@ -453,10 +455,30 @@ file_status FileDescriptor::refresh_status(std::error_code& ec) { } }} // end namespace detail -using detail::set_or_throw; -using parser::string_view_t; -using parser::PathParser; +using detail::capture_errno; +using detail::ErrorHandler; +using detail::StatT; using parser::createView; +using parser::PathParser; +using parser::string_view_t; + +void filesystem_error::__create_what(int __num_paths) { + const char* derived_what = system_error::what(); + __storage_->__what_ = [&]() -> string { + const char* p1 = path1().native().empty() ? "\"\"" : path1().c_str(); + const char* p2 = path2().native().empty() ? "\"\"" : path2().c_str(); + switch (__num_paths) { + default: + return detail::format_string("filesystem error: %s", derived_what); + case 1: + return detail::format_string("filesystem error: %s [%s]", derived_what, + p1); + case 2: + return detail::format_string("filesystem error: %s [%s] [%s]", + derived_what, p1, p2); + } + }(); +} static path __do_absolute(const path& p, path *cwd, std::error_code *ec) { if (ec) ec->clear(); @@ -476,48 +498,46 @@ path __absolute(const path& p, std::error_code *ec) { path __canonical(path const & orig_p, std::error_code *ec) { path cwd; + ErrorHandler<path> err("canonical", ec, &orig_p, &cwd); + path p = __do_absolute(orig_p, &cwd, ec); char buff[PATH_MAX + 1]; char *ret; - if ((ret = ::realpath(p.c_str(), buff)) == nullptr) { - set_or_throw(ec, "canonical", orig_p, cwd); - return {}; - } - if (ec) ec->clear(); + if ((ret = ::realpath(p.c_str(), buff)) == nullptr) + return err.report(capture_errno()); return {ret}; } void __copy(const path& from, const path& to, copy_options options, std::error_code *ec) { - const bool sym_status = bool(options & - (copy_options::create_symlinks | copy_options::skip_symlinks)); - - const bool sym_status2 = bool(options & - copy_options::copy_symlinks); - - std::error_code m_ec1; - struct ::stat f_st = {}; - const file_status f = sym_status || sym_status2 - ? detail::posix_lstat(from, f_st, &m_ec1) - : detail::posix_stat(from, f_st, &m_ec1); - if (m_ec1) - return set_or_throw(m_ec1, ec, "copy", from, to); - - struct ::stat t_st = {}; - const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1) - : detail::posix_stat(to, t_st, &m_ec1); - - if (not status_known(t)) - return set_or_throw(m_ec1, ec, "copy", from, to); - - if (!exists(f) || is_other(f) || is_other(t) - || (is_directory(f) && is_regular_file(t)) - || detail::stat_equivalent(f_st, t_st)) - { - return set_or_throw(make_error_code(errc::function_not_supported), - ec, "copy", from, to); - } + ErrorHandler<void> err("copy", ec, &from, &to); + + const bool sym_status = bool( + options & (copy_options::create_symlinks | copy_options::skip_symlinks)); + + const bool sym_status2 = bool(options & copy_options::copy_symlinks); + + std::error_code m_ec1; + struct ::stat f_st = {}; + const file_status f = sym_status || sym_status2 + ? detail::posix_lstat(from, f_st, &m_ec1) + : detail::posix_stat(from, f_st, &m_ec1); + if (m_ec1) + return err.report(m_ec1); + + struct ::stat t_st = {}; + const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1) + : detail::posix_stat(to, t_st, &m_ec1); + + if (not status_known(t)) + return err.report(m_ec1); + + if (!exists(f) || is_other(f) || is_other(t) || + (is_directory(f) && is_regular_file(t)) || + detail::stat_equivalent(f_st, t_st)) { + return err.report(errc::function_not_supported); + } if (ec) ec->clear(); @@ -527,8 +547,7 @@ void __copy(const path& from, const path& to, copy_options options, } else if (not exists(t)) { __copy_symlink(from, to, ec); } else { - set_or_throw(make_error_code(errc::file_exists), - ec, "copy", from, to); + return err.report(errc::file_exists); } return; } @@ -550,7 +569,7 @@ void __copy(const path& from, const path& to, copy_options options, return; } else if (is_directory(f) && bool(copy_options::create_symlinks & options)) { - return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy"); + return err.report(errc::is_a_directory); } else if (is_directory(f) && (bool(copy_options::recursive & options) || copy_options::none == options)) { @@ -565,7 +584,9 @@ void __copy(const path& from, const path& to, copy_options options, if (ec && *ec) { return; } std::error_code m_ec2; for (; it != directory_iterator(); it.increment(m_ec2)) { - if (m_ec2) return set_or_throw(m_ec2, ec, "copy", from, to); + if (m_ec2) { + return err.report(m_ec2); + } __copy(it->path(), to / it->path().filename(), options | copy_options::__in_recursive_copy, ec); if (ec && *ec) { return; } @@ -672,28 +693,20 @@ bool __copy_file(const path& from, const path& to, copy_options options, std::error_code *ec) { using detail::FileDescriptor; - using detail::StatT; - - if (ec) - ec->clear(); - - auto Error = [&](const error_code& error_ec) { - set_or_throw(error_ec, ec, "copy_file", from, to); - return false; - }; + ErrorHandler<bool> err("copy_file", ec, &to, &from); std::error_code m_ec; FileDescriptor from_fd = FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK); if (m_ec) - return Error(m_ec); + return err.report(m_ec); auto from_st = from_fd.get_status(); StatT const& from_stat = from_fd.get_stat(); if (!is_regular_file(from_st)) { if (not m_ec) m_ec = make_error_code(errc::not_supported); - return Error(m_ec); + return err.report(m_ec); } const bool skip_existing = bool(copy_options::skip_existing & options); @@ -704,14 +717,14 @@ bool __copy_file(const path& from, const path& to, copy_options options, StatT to_stat_path; file_status to_st = detail::posix_stat(to, to_stat_path, &m_ec); if (!status_known(to_st)) - return Error(m_ec); + return err.report(m_ec); const bool to_exists = exists(to_st); if (to_exists && !is_regular_file(to_st)) - return Error(make_error_code(errc::not_supported)); + return err.report(make_error_code(errc::not_supported)); if (to_exists && detail::stat_equivalent(from_stat, to_stat_path)) - return Error(make_error_code(errc::file_exists)); + return err.report(make_error_code(errc::file_exists)); if (to_exists && skip_existing) return false; @@ -729,7 +742,7 @@ bool __copy_file(const path& from, const path& to, copy_options options, } if (!to_exists || overwrite_existing) return true; - return Error(make_error_code(errc::file_exists)); + return err.report(errc::file_exists); }(); if (!ShouldCopy) return false; @@ -742,25 +755,25 @@ bool __copy_file(const path& from, const path& to, copy_options options, FileDescriptor to_fd = FileDescriptor::create_with_status( &to, m_ec, to_open_flags, from_stat.st_mode); if (m_ec) - return Error(m_ec); + return err.report(m_ec); if (to_exists) { // Check that the file we initially stat'ed is equivalent to the one // we opened. // FIXME: report this better. if (!detail::stat_equivalent(to_stat_path, to_fd.get_stat())) - return Error(make_error_code(errc::bad_file_descriptor)); + return err.report(errc::bad_file_descriptor); // Set the permissions and truncate the file we opened. if (!detail::posix_fchmod(to_fd, from_stat, m_ec)) - return Error(m_ec); + return err.report(m_ec); if (!detail::posix_ftruncate(to_fd, 0, m_ec)) - return Error(m_ec); + return err.report(m_ec); } if (!copy_file_impl(from_fd, to_fd, m_ec)) { // FIXME: Remove the dest file if we failed, and it didn't exist previously. - return Error(m_ec); + return err.report(m_ec); } return true; @@ -779,213 +792,192 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink, bool __create_directories(const path& p, std::error_code *ec) { - std::error_code m_ec; - auto const st = detail::posix_stat(p, &m_ec); - if (!status_known(st)) { - set_or_throw(m_ec, ec, "create_directories", p); - return false; - } - else if (is_directory(st)) { - if (ec) ec->clear(); - return false; - } - else if (exists(st)) { - set_or_throw(make_error_code(errc::file_exists), - ec, "create_directories", p); - return false; - } + ErrorHandler<bool> err("create_directories", ec, &p); - const path parent = p.parent_path(); - if (!parent.empty()) { - const file_status parent_st = status(parent, m_ec); - if (not status_known(parent_st)) { - set_or_throw(m_ec, ec, "create_directories", p); - return false; - } - if (not exists(parent_st)) { - __create_directories(parent, ec); - if (ec && *ec) { return false; } - } + std::error_code m_ec; + auto const st = detail::posix_stat(p, &m_ec); + if (!status_known(st)) + return err.report(m_ec); + else if (is_directory(st)) + return false; + else if (exists(st)) + return err.report(errc::file_exists); + + const path parent = p.parent_path(); + if (!parent.empty()) { + const file_status parent_st = status(parent, m_ec); + if (not status_known(parent_st)) + return err.report(m_ec); + if (not exists(parent_st)) { + __create_directories(parent, ec); + if (ec && *ec) { + return false; + } } + } return __create_directory(p, ec); } bool __create_directory(const path& p, std::error_code *ec) { - if (ec) ec->clear(); - if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0) - return true; - if (errno != EEXIST || !is_directory(p)) - set_or_throw(ec, "create_directory", p); - return false; + ErrorHandler<bool> err("create_directory", ec, &p); + + if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0) + return true; + if (errno != EEXIST || !is_directory(p)) + err.report(capture_errno()); + return false; } bool __create_directory(path const & p, path const & attributes, std::error_code *ec) { - struct ::stat attr_stat; - std::error_code mec; - auto st = detail::posix_stat(attributes, attr_stat, &mec); - if (!status_known(st)) { - set_or_throw(mec, ec, "create_directory", p, attributes); - return false; - } - if (ec) ec->clear(); - if (::mkdir(p.c_str(), attr_stat.st_mode) == 0) - return true; - if (errno != EEXIST || !is_directory(p)) - set_or_throw(ec, "create_directory", p, attributes); - return false; + ErrorHandler<bool> err("create_directory", ec, &p, &attributes); + + StatT attr_stat; + std::error_code mec; + auto st = detail::posix_stat(attributes, attr_stat, &mec); + if (!status_known(st)) + return err.report(mec); + + if (::mkdir(p.c_str(), attr_stat.st_mode) == 0) + return true; + if (errno != EEXIST || !is_directory(p)) + err.report(capture_errno()); + return false; } -void __create_directory_symlink(path const & from, path const & to, - std::error_code *ec){ - if (::symlink(from.c_str(), to.c_str()) != 0) - set_or_throw(ec, "create_directory_symlink", from, to); - else if (ec) - ec->clear(); +void __create_directory_symlink(path const& from, path const& to, + std::error_code* ec) { + ErrorHandler<void> err("create_directory_symlink", ec, &from, &to); + if (::symlink(from.c_str(), to.c_str()) != 0) + return err.report(capture_errno()); } void __create_hard_link(const path& from, const path& to, std::error_code *ec){ - if (::link(from.c_str(), to.c_str()) == -1) - set_or_throw(ec, "create_hard_link", from, to); - else if (ec) - ec->clear(); + ErrorHandler<void> err("create_hard_link", ec, &from, &to); + if (::link(from.c_str(), to.c_str()) == -1) + return err.report(capture_errno()); } void __create_symlink(path const & from, path const & to, std::error_code *ec) { - - if (::symlink(from.c_str(), to.c_str()) == -1) - set_or_throw(ec, "create_symlink", from, to); - else if (ec) - ec->clear(); + ErrorHandler<void> err("create_symlink", ec, &from, &to); + if (::symlink(from.c_str(), to.c_str()) == -1) + return err.report(capture_errno()); } path __current_path(std::error_code *ec) { - auto size = ::pathconf(".", _PC_PATH_MAX); - _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size"); + ErrorHandler<path> err("current_path", ec); - auto buff = std::unique_ptr<char[]>(new char[size + 1]); - char* ret; - if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) { - set_or_throw(ec, "current_path"); - return {}; - } - if (ec) ec->clear(); - return {buff.get()}; + auto size = ::pathconf(".", _PC_PATH_MAX); + _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size"); + + auto buff = std::unique_ptr<char[]>(new char[size + 1]); + char* ret; + if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) + return err.report(capture_errno(), "call to getcwd failed"); + + return {buff.get()}; } void __current_path(const path& p, std::error_code *ec) { - if (::chdir(p.c_str()) == -1) - set_or_throw(ec, "current_path", p); - else if (ec) - ec->clear(); + ErrorHandler<void> err("current_path", ec, &p); + if (::chdir(p.c_str()) == -1) + err.report(capture_errno()); } bool __equivalent(const path& p1, const path& p2, std::error_code *ec) { - auto make_unsupported_error = [&]() { - set_or_throw(make_error_code(errc::not_supported), ec, - "equivalent", p1, p2); - return false; - }; - std::error_code ec1, ec2; - struct ::stat st1 = {}; - struct ::stat st2 = {}; - auto s1 = detail::posix_stat(p1.native(), st1, &ec1); - if (!exists(s1)) - return make_unsupported_error(); - auto s2 = detail::posix_stat(p2.native(), st2, &ec2); - if (!exists(s2)) - return make_unsupported_error(); - if (ec) ec->clear(); - return detail::stat_equivalent(st1, st2); + ErrorHandler<bool> err("equivalent", ec, &p1, &p2); + + std::error_code ec1, ec2; + StatT st1 = {}, st2 = {}; + auto s1 = detail::posix_stat(p1.native(), st1, &ec1); + if (!exists(s1)) + return err.report(errc::not_supported); + auto s2 = detail::posix_stat(p2.native(), st2, &ec2); + if (!exists(s2)) + return err.report(errc::not_supported); + + return detail::stat_equivalent(st1, st2); } std::uintmax_t __file_size(const path& p, std::error_code *ec) { - std::error_code m_ec; - struct ::stat st; - file_status fst = detail::posix_stat(p, st, &m_ec); - if (!exists(fst) || !is_regular_file(fst)) { - errc error_kind = - is_directory(fst) ? errc::is_a_directory : errc::not_supported; - if (!m_ec) - m_ec = make_error_code(error_kind); - set_or_throw(m_ec, ec, "file_size", p); - return static_cast<uintmax_t>(-1); - } + ErrorHandler<uintmax_t> err("file_size", ec, &p); + + std::error_code m_ec; + struct ::stat st; + file_status fst = detail::posix_stat(p, st, &m_ec); + if (!exists(fst) || !is_regular_file(fst)) { + errc error_kind = + is_directory(fst) ? errc::is_a_directory : errc::not_supported; + if (!m_ec) + m_ec = make_error_code(error_kind); + return err.report(m_ec); + } // is_regular_file(p) == true - if (ec) ec->clear(); return static_cast<std::uintmax_t>(st.st_size); } std::uintmax_t __hard_link_count(const path& p, std::error_code *ec) { - std::error_code m_ec; - struct ::stat st; - detail::posix_stat(p, st, &m_ec); - if (m_ec) { - set_or_throw(m_ec, ec, "hard_link_count", p); - return static_cast<std::uintmax_t>(-1); - } - if (ec) ec->clear(); - return static_cast<std::uintmax_t>(st.st_nlink); + ErrorHandler<uintmax_t> err("hard_link_count", ec, &p); + + std::error_code m_ec; + StatT st; + detail::posix_stat(p, st, &m_ec); + if (m_ec) + return err.report(m_ec); + return static_cast<std::uintmax_t>(st.st_nlink); } bool __fs_is_empty(const path& p, std::error_code *ec) { - if (ec) ec->clear(); - std::error_code m_ec; - struct ::stat pst; - auto st = detail::posix_stat(p, pst, &m_ec); - if (m_ec) { - set_or_throw(m_ec, ec, "is_empty", p); - return false; - } - else if (!is_directory(st) && !is_regular_file(st)) { - m_ec = make_error_code(errc::not_supported); - set_or_throw(m_ec, ec, "is_empty"); - return false; - } - else if (is_directory(st)) { - auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p); - if (ec && *ec) - return false; - return it == directory_iterator{}; - } - else if (is_regular_file(st)) - return static_cast<std::uintmax_t>(pst.st_size) == 0; + ErrorHandler<bool> err("is_empty", ec, &p); - _LIBCPP_UNREACHABLE(); + std::error_code m_ec; + StatT pst; + auto st = detail::posix_stat(p, pst, &m_ec); + if (m_ec) + return err.report(m_ec); + else if (!is_directory(st) && !is_regular_file(st)) + return err.report(errc::not_supported); + else if (is_directory(st)) { + auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p); + if (ec && *ec) + return false; + return it == directory_iterator{}; + } else if (is_regular_file(st)) + return static_cast<std::uintmax_t>(pst.st_size) == 0; + + _LIBCPP_UNREACHABLE(); } -static file_time_type __extract_last_write_time(const path& p, - const struct ::stat& st, +static file_time_type __extract_last_write_time(const path& p, const StatT& st, error_code* ec) { using detail::FSTime; + ErrorHandler<file_time_type> err("last_write_time", ec, &p); + auto ts = detail::extract_mtime(st); - if (!FSTime::is_representable(ts)) { - set_or_throw(make_error_code(errc::value_too_large), ec, "last_write_time", - p); - return file_time_type::min(); - } + if (!FSTime::is_representable(ts)) + return err.report(errc::value_too_large); + return FSTime::convert_timespec(ts); } file_time_type __last_write_time(const path& p, std::error_code *ec) { using namespace ::std::chrono; + ErrorHandler<file_time_type> err("last_write_time", ec, &p); + std::error_code m_ec; - struct ::stat st; + StatT st; detail::posix_stat(p, st, &m_ec); - if (m_ec) { - set_or_throw(m_ec, ec, "last_write_time", p); - return file_time_type::min(); - } - if (ec) ec->clear(); + if (m_ec) + return err.report(m_ec); return __extract_last_write_time(p, st, ec); } @@ -995,6 +987,8 @@ void __last_write_time(const path& p, file_time_type new_time, using namespace std::chrono; using namespace detail; + ErrorHandler<void> err("last_write_time", ec, &p); + std::error_code m_ec; TimeStructArray tbuf; #if !defined(_LIBCXX_USE_UTIMENSAT) @@ -1003,99 +997,92 @@ void __last_write_time(const path& p, file_time_type new_time, // ::utimes struct ::stat st; file_status fst = detail::posix_stat(p, st, &m_ec); - if (m_ec && !status_known(fst)) { - set_or_throw(m_ec, ec, "last_write_time", p); - return; - } + if (m_ec && !status_known(fst)) + return err.report(m_ec); SetTimeStructTo(tbuf[0], detail::extract_atime(st)); #else tbuf[0].tv_sec = 0; tbuf[0].tv_nsec = UTIME_OMIT; #endif - if (SetTimeStructTo(tbuf[1], new_time)) { - set_or_throw(make_error_code(errc::invalid_argument), ec, - "last_write_time", p); - return; - } + if (SetTimeStructTo(tbuf[1], new_time)) + return err.report(errc::invalid_argument); SetFileTimes(p, tbuf, m_ec); if (m_ec) - set_or_throw(m_ec, ec, "last_write_time", p); - else if (ec) - ec->clear(); + return err.report(m_ec); } void __permissions(const path& p, perms prms, perm_options opts, std::error_code *ec) { - auto has_opt = [&](perm_options o) { return bool(o & opts); }; - const bool resolve_symlinks = !has_opt(perm_options::nofollow); - const bool add_perms = has_opt(perm_options::add); - const bool remove_perms = has_opt(perm_options::remove); - _LIBCPP_ASSERT( - (add_perms + remove_perms + has_opt(perm_options::replace)) == 1, - "One and only one of the perm_options constants replace, add, or remove " - "is present in opts"); - - bool set_sym_perms = false; - prms &= perms::mask; - if (!resolve_symlinks || (add_perms || remove_perms)) { - std::error_code m_ec; - file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec) - : detail::posix_lstat(p, &m_ec); - set_sym_perms = is_symlink(st); - if (m_ec) return set_or_throw(m_ec, ec, "permissions", p); - _LIBCPP_ASSERT(st.permissions() != perms::unknown, - "Permissions unexpectedly unknown"); - if (add_perms) - prms |= st.permissions(); - else if (remove_perms) - prms = st.permissions() & ~prms; - } + ErrorHandler<void> err("permissions", ec, &p); + + auto has_opt = [&](perm_options o) { return bool(o & opts); }; + const bool resolve_symlinks = !has_opt(perm_options::nofollow); + const bool add_perms = has_opt(perm_options::add); + const bool remove_perms = has_opt(perm_options::remove); + _LIBCPP_ASSERT( + (add_perms + remove_perms + has_opt(perm_options::replace)) == 1, + "One and only one of the perm_options constants replace, add, or remove " + "is present in opts"); + + bool set_sym_perms = false; + prms &= perms::mask; + if (!resolve_symlinks || (add_perms || remove_perms)) { + std::error_code m_ec; + file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec) + : detail::posix_lstat(p, &m_ec); + set_sym_perms = is_symlink(st); + if (m_ec) + return err.report(m_ec); + _LIBCPP_ASSERT(st.permissions() != perms::unknown, + "Permissions unexpectedly unknown"); + if (add_perms) + prms |= st.permissions(); + else if (remove_perms) + prms = st.permissions() & ~prms; + } const auto real_perms = detail::posix_convert_perms(prms); # if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD) const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0; if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) { - return set_or_throw(ec, "permissions", p); + return err.report(capture_errno()); } # else if (set_sym_perms) - return set_or_throw(make_error_code(errc::operation_not_supported), - ec, "permissions", p); + return err.report(errc::operation_not_supported); if (::chmod(p.c_str(), real_perms) == -1) { - return set_or_throw(ec, "permissions", p); + return err.report(capture_errno()); } # endif - if (ec) ec->clear(); } path __read_symlink(const path& p, std::error_code *ec) { - char buff[PATH_MAX + 1]; - std::error_code m_ec; - ::ssize_t ret; - if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) { - set_or_throw(ec, "read_symlink", p); - return {}; - } + ErrorHandler<path> err("read_symlink", ec, &p); + + char buff[PATH_MAX + 1]; + std::error_code m_ec; + ::ssize_t ret; + if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) { + return err.report(capture_errno()); + } _LIBCPP_ASSERT(ret <= PATH_MAX, "TODO"); _LIBCPP_ASSERT(ret > 0, "TODO"); - if (ec) ec->clear(); buff[ret] = 0; return {buff}; } bool __remove(const path& p, std::error_code *ec) { - if (ec) ec->clear(); - - if (::remove(p.c_str()) == -1) { - if (errno != ENOENT) - set_or_throw(ec, "remove", p); - return false; - } + ErrorHandler<bool> err("remove", ec, &p); + if (::remove(p.c_str()) == -1) { + if (errno != ENOENT) + err.report(capture_errno()); + return false; + } return true; } @@ -1123,45 +1110,39 @@ std::uintmax_t remove_all_impl(path const & p, std::error_code& ec) } // end namespace std::uintmax_t __remove_all(const path& p, std::error_code *ec) { - if (ec) ec->clear(); - - std::error_code mec; - auto count = remove_all_impl(p, mec); - if (mec) { - if (mec == errc::no_such_file_or_directory) { - return 0; - } else { - set_or_throw(mec, ec, "remove_all", p); - return static_cast<std::uintmax_t>(-1); - } - } + ErrorHandler<uintmax_t> err("remove_all", ec, &p); + + std::error_code mec; + auto count = remove_all_impl(p, mec); + if (mec) { + if (mec == errc::no_such_file_or_directory) + return 0; + return err.report(mec); + } return count; } void __rename(const path& from, const path& to, std::error_code *ec) { - if (::rename(from.c_str(), to.c_str()) == -1) - set_or_throw(ec, "rename", from, to); - else if (ec) - ec->clear(); + ErrorHandler<void> err("rename", ec, &from, &to); + if (::rename(from.c_str(), to.c_str()) == -1) + err.report(capture_errno()); } void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) { - if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1) - set_or_throw(ec, "resize_file", p); - else if (ec) - ec->clear(); + ErrorHandler<void> err("resize_file", ec, &p); + if (::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1) + return err.report(capture_errno()); } space_info __space(const path& p, std::error_code *ec) { - space_info si; - struct statvfs m_svfs = {}; - if (::statvfs(p.c_str(), &m_svfs) == -1) { - set_or_throw(ec, "space", p); - si.capacity = si.free = si.available = - static_cast<std::uintmax_t>(-1); - return si; - } - if (ec) ec->clear(); + ErrorHandler<void> err("space", ec, &p); + space_info si; + struct statvfs m_svfs = {}; + if (::statvfs(p.c_str(), &m_svfs) == -1) { + err.report(capture_errno()); + si.capacity = si.free = si.available = static_cast<std::uintmax_t>(-1); + return si; + } // Multiply with overflow checking. auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) { out = other * m_svfs.f_frsize; @@ -1183,6 +1164,8 @@ file_status __symlink_status(const path& p, std::error_code *ec) { } path __temp_directory_path(std::error_code* ec) { + ErrorHandler<path> err("temp_directory_path", ec); + const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; const char* ret = nullptr; @@ -1194,20 +1177,21 @@ path __temp_directory_path(std::error_code* ec) { path p(ret); std::error_code m_ec; - if (!exists(p, m_ec) || !is_directory(p, m_ec)) { - if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory)) - m_ec = make_error_code(errc::not_a_directory); - set_or_throw(m_ec, ec, "temp_directory_path"); - return {}; - } + file_status st = detail::posix_stat(p, &m_ec); + if (!status_known(st)) + return err.report(m_ec, "cannot access path \"%s\"", p); + + if (!exists(st) || !is_directory(st)) + return err.report(errc::not_a_directory, "path \"%s\" is not a directory", + p); - if (ec) - ec->clear(); return p; } path __weakly_canonical(const path& p, std::error_code *ec) { + ErrorHandler<path> err("weakly_canonical", ec, &p); + if (p.empty()) return __canonical("", ec); @@ -1223,8 +1207,7 @@ path __weakly_canonical(const path& p, std::error_code *ec) { std::error_code m_ec; file_status st = __status(tmp, &m_ec); if (!status_known(st)) { - set_or_throw(m_ec, ec, "weakly_canonical", p); - return {}; + return err.report(m_ec); } else if (exists(st)) { result = __canonical(tmp, ec); break; |