summaryrefslogtreecommitdiffstats
path: root/contrib/libc++/src/experimental/filesystem/operations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libc++/src/experimental/filesystem/operations.cpp')
-rw-r--r--contrib/libc++/src/experimental/filesystem/operations.cpp184
1 files changed, 164 insertions, 20 deletions
diff --git a/contrib/libc++/src/experimental/filesystem/operations.cpp b/contrib/libc++/src/experimental/filesystem/operations.cpp
index 369996f..6c7e4cf 100644
--- a/contrib/libc++/src/experimental/filesystem/operations.cpp
+++ b/contrib/libc++/src/experimental/filesystem/operations.cpp
@@ -51,7 +51,7 @@ void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
} else {
string msg_s("std::experimental::filesystem::");
msg_s += msg;
- __libcpp_throw(filesystem_error(msg_s, p, p2, m_ec));
+ __throw_filesystem_error(msg_s, p, p2, m_ec);
}
}
@@ -236,12 +236,11 @@ void __copy(const path& from, const path& to, copy_options options,
}
return;
}
- else if (is_directory(f)) {
- if (not bool(copy_options::recursive & options) &&
- bool(copy_options::__in_recursive_copy & 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");
+ }
+ else if (is_directory(f) && (bool(copy_options::recursive & options) ||
+ copy_options::none == options)) {
if (!exists(t)) {
// create directory to with attributes from 'from'.
@@ -283,6 +282,10 @@ bool __copy_file(const path& from, const path& to, copy_options options,
}
const bool to_exists = exists(to_st);
+ if (to_exists && !is_regular_file(to_st)) {
+ set_or_throw(make_error_code(errc::not_supported), ec, "copy_file", from, to);
+ return false;
+ }
if (to_exists && bool(copy_options::skip_existing & options)) {
return false;
}
@@ -303,6 +306,8 @@ bool __copy_file(const path& from, const path& to, copy_options options,
set_or_throw(make_error_code(errc::file_exists), ec, "copy", from, to);
return false;
}
+
+ _LIBCPP_UNREACHABLE();
}
void __copy_symlink(const path& existing_symlink, const path& new_symlink,
@@ -476,18 +481,32 @@ bool __fs_is_empty(const path& p, std::error_code *ec)
std::error_code m_ec;
struct ::stat pst;
auto st = detail::posix_stat(p, pst, &m_ec);
- if (is_directory(st))
- return directory_iterator(p) == directory_iterator{};
+ 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;
- // else
- set_or_throw(m_ec, ec, "is_empty", p);
- return false;
+
+ _LIBCPP_UNREACHABLE();
}
namespace detail { namespace {
+using namespace std::chrono;
+
template <class CType, class ChronoType>
bool checked_set(CType* out, ChronoType time) {
using Lim = numeric_limits<CType>;
@@ -497,8 +516,127 @@ bool checked_set(CType* out, ChronoType time) {
return true;
}
-constexpr long long min_seconds = file_time_type::duration::min().count()
- / file_time_type::period::den;
+using TimeSpec = struct ::timespec;
+using StatT = struct ::stat;
+
+#if defined(__APPLE__)
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
+TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
+#else
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
+__attribute__((unused)) // Suppress warning
+TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
+#endif
+
+constexpr auto max_seconds = duration_cast<seconds>(
+ file_time_type::duration::max()).count();
+
+constexpr auto max_nsec = duration_cast<nanoseconds>(
+ file_time_type::duration::max() - seconds(max_seconds)).count();
+
+constexpr auto min_seconds = duration_cast<seconds>(
+ file_time_type::duration::min()).count();
+
+constexpr auto min_nsec_timespec = duration_cast<nanoseconds>(
+ (file_time_type::duration::min() - seconds(min_seconds)) + seconds(1)).count();
+
+// Static assert that these values properly round trip.
+static_assert((seconds(min_seconds) + duration_cast<microseconds>(nanoseconds(min_nsec_timespec)))
+ - duration_cast<microseconds>(seconds(1))
+ == file_time_type::duration::min(), "");
+
+constexpr auto max_time_t = numeric_limits<time_t>::max();
+constexpr auto min_time_t = numeric_limits<time_t>::min();
+
+#if !defined(__LP64__) && defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
+#endif
+
+_LIBCPP_CONSTEXPR_AFTER_CXX11
+bool is_representable(TimeSpec const& tm) {
+ if (tm.tv_sec >= 0) {
+ return (tm.tv_sec < max_seconds) ||
+ (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
+ } else if (tm.tv_sec == (min_seconds - 1)) {
+ return tm.tv_nsec >= min_nsec_timespec;
+ } else {
+ return (tm.tv_sec >= min_seconds);
+ }
+}
+#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
+#if defined(__LP64__)
+static_assert(is_representable({max_seconds, max_nsec}), "");
+static_assert(!is_representable({max_seconds + 1, 0}), "");
+static_assert(!is_representable({max_seconds, max_nsec + 1}), "");
+static_assert(!is_representable({max_time_t, 0}), "");
+static_assert(is_representable({min_seconds, 0}), "");
+static_assert(is_representable({min_seconds - 1, min_nsec_timespec}), "");
+static_assert(is_representable({min_seconds - 1, min_nsec_timespec + 1}), "");
+static_assert(!is_representable({min_seconds - 1, min_nsec_timespec - 1}), "");
+static_assert(!is_representable({min_time_t, 999999999}), "");
+#else
+static_assert(is_representable({max_time_t, 999999999}), "");
+static_assert(is_representable({max_time_t, 1000000000}), "");
+static_assert(is_representable({min_time_t, 0}), "");
+#endif
+#endif
+
+_LIBCPP_CONSTEXPR_AFTER_CXX11
+bool is_representable(file_time_type const& tm) {
+ auto secs = duration_cast<seconds>(tm.time_since_epoch());
+ auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
+ if (nsecs.count() < 0) {
+ secs = secs + seconds(1);
+ nsecs = nsecs + seconds(1);
+ }
+ using TLim = numeric_limits<time_t>;
+ if (secs.count() >= 0)
+ return secs.count() <= TLim::max();
+ return secs.count() >= TLim::min();
+}
+#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
+#if defined(__LP64__)
+static_assert(is_representable(file_time_type::max()), "");
+static_assert(is_representable(file_time_type::min()), "");
+#else
+static_assert(!is_representable(file_time_type::max()), "");
+static_assert(!is_representable(file_time_type::min()), "");
+static_assert(is_representable(file_time_type(seconds(max_time_t))), "");
+static_assert(is_representable(file_time_type(seconds(min_time_t))), "");
+#endif
+#endif
+
+_LIBCPP_CONSTEXPR_AFTER_CXX11
+file_time_type convert_timespec(TimeSpec const& tm) {
+ auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
+ if (tm.tv_sec >= 0) {
+ auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
+ return file_time_type(Dur);
+ } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() == 0) {
+ return file_time_type(seconds(tm.tv_sec));
+ } else { // tm.tv_sec < 0
+ auto adj_subsec = duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
+ auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
+ return file_time_type(Dur);
+ }
+}
+#ifndef _LIBCPP_HAS_NO_CXX14_CONSTEXPR
+#if defined(__LP64__)
+static_assert(convert_timespec({max_seconds, max_nsec}) == file_time_type::max(), "");
+static_assert(convert_timespec({max_seconds, max_nsec - 1}) < file_time_type::max(), "");
+static_assert(convert_timespec({max_seconds - 1, 999999999}) < file_time_type::max(), "");
+static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec}) == file_time_type::min(), "");
+static_assert(convert_timespec({min_seconds - 1, min_nsec_timespec + 1}) > file_time_type::min(), "");
+static_assert(convert_timespec({min_seconds , 0}) > file_time_type::min(), "");
+#else
+// FIXME add tests for 32 bit builds
+#endif
+#endif
+
+#if !defined(__LP64__) && defined(__clang__)
+#pragma clang diagnostic pop
+#endif
template <class SubSecDurT, class SubSecT>
bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp) {
@@ -509,7 +647,6 @@ bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp)
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
if (subsec_dur.count() < 0) {
if (sec_dur.count() > min_seconds) {
-
sec_dur -= seconds(1);
subsec_dur += seconds(1);
} else {
@@ -522,9 +659,9 @@ bool set_times_checked(time_t* sec_out, SubSecT* subsec_out, file_time_type tp)
}} // end namespace detail
-
file_time_type __last_write_time(const path& p, std::error_code *ec)
{
+ using namespace ::std::chrono;
std::error_code m_ec;
struct ::stat st;
detail::posix_stat(p, st, &m_ec);
@@ -533,7 +670,13 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
return file_time_type::min();
}
if (ec) ec->clear();
- return file_time_type::clock::from_time_t(st.st_mtime);
+ auto ts = detail::extract_mtime(st);
+ if (!detail::is_representable(ts)) {
+ set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
+ "last_write_time", p);
+ return file_time_type::min();
+ }
+ return detail::convert_timespec(ts);
}
void __last_write_time(const path& p, file_time_type new_time,
@@ -554,9 +697,10 @@ void __last_write_time(const path& p, file_time_type new_time,
set_or_throw(m_ec, ec, "last_write_time", p);
return;
}
+ auto atime = detail::extract_atime(st);
struct ::timeval tbuf[2];
- tbuf[0].tv_sec = st.st_atime;
- tbuf[0].tv_usec = 0;
+ tbuf[0].tv_sec = atime.tv_sec;
+ tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
const bool overflowed = !detail::set_times_checked<microseconds>(
&tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
@@ -720,7 +864,7 @@ space_info __space(const path& p, std::error_code *ec) {
// Multiply with overflow checking.
auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
out = other * m_svfs.f_frsize;
- if (out / other != m_svfs.f_frsize || other == 0)
+ if (other == 0 || out / other != m_svfs.f_frsize)
out = static_cast<std::uintmax_t>(-1);
};
do_mult(si.capacity, m_svfs.f_blocks);
OpenPOWER on IntegriCloud