diff options
Diffstat (limited to 'contrib/libc++/src/experimental/filesystem/operations.cpp')
-rw-r--r-- | contrib/libc++/src/experimental/filesystem/operations.cpp | 184 |
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); |