summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2018-07-23 02:00:52 +0000
committerEric Fiselier <eric@efcs.ca>2018-07-23 02:00:52 +0000
commit0ddb77a467e90256a57d06af8bf1900ec8890eaa (patch)
tree4e589af25d17d060f749bc939c3f6f59f2e44c9f /src
parente14f03815bb3485bb5ea795adb2c895853b3d1b8 (diff)
downloadlibcxx-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.cpp130
-rw-r--r--src/experimental/filesystem/filesystem_common.h150
-rw-r--r--src/experimental/filesystem/operations.cpp587
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;