summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2018-07-20 01:22:32 +0000
committerEric Fiselier <eric@efcs.ca>2018-07-20 01:22:32 +0000
commite274f439c68f228846e0b78b42d10c06bc4d0142 (patch)
tree2cbac787444cd20aa7526ce7cabe5225c9e17450 /src
parentc69e1a06154ae84c564588ecf0a2d7c1018f4483 (diff)
downloadlibcxx-e274f439c68f228846e0b78b42d10c06bc4d0142.tar.gz
[libc++] Implement Directory Entry Caching -- Sort of.
Summary: This patch implements directory_entry caching *almost* as specified in P0317r1. However, I explicitly chose to deviate from the standard as I'll explain below. The approach I decided to take is a fully caching one. When `refresh()` is called, the cache is populated by calls to `stat` and `lstat` as needed. During directory iteration the cache is only populated with the `file_type` as reported by `readdir`. The cache can be in the following states: * `_Empty`: There is nothing in the cache (likely due to an error) * `_IterSymlink`: Created by directory iteration when we walk onto a symlink only the symlink file type is known. * `_IterNonSymlink`: Created by directory iteration when we walk onto a non-symlink. Both the regular file type and symlink file type are known. * `_RefreshSymlink` and `_RefreshNonSymlink`: A full cache created by `refresh()`. This case includes dead symlinks. * `_RefreshSymlinkUnresolved`: A partial cache created by refresh when we fail to resolve the file pointed to by a symlink (likely due to permissions). Symlink attributes are cached, but attributes about the linked entity are not. As mentioned, this implementation purposefully deviates from the standard. According to some readings of the specification, and the Windows filesystem implementation, the constructors and modifiers which don't pass an `error_code` must throw when the `directory_entry` points to a entity which doesn't exist. or when attribute resolution fails for another reason. @BillyONeal has proposed a more reasonable set of requirements, where modifiers other than refresh ignore errors. This is the behavior libc++ currently implements, with the expectation some form of the new language will be accepted into the standard. Some additional semantics which differ from the Windows implementation: 1. `refresh` will not throw when the entry doesn't exist. In this case we can still meet the functions specification, so we don't treat it as an error. 2. We don't clear the path name when a constructor fails via refresh (this will hopefully be changed in the standard as well). It should be noted that libstdc++'s current implementation has the same behavior as libc++, except for point (2). If the changes to the specification don't get accepted, we'll be able to make the changes later. [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0317r1.html Reviewers: mclow.lists, gromer, ldionne, aaron.ballman Subscribers: BillyONeal, christof, cfe-commits Differential Revision: https://reviews.llvm.org/D49530 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@337516 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'src')
-rw-r--r--src/experimental/filesystem/directory_iterator.cpp93
-rw-r--r--src/experimental/filesystem/filesystem_common.h (renamed from src/experimental/filesystem/filesystem_time_helper.h)145
-rw-r--r--src/experimental/filesystem/operations.cpp357
3 files changed, 393 insertions, 202 deletions
diff --git a/src/experimental/filesystem/directory_iterator.cpp b/src/experimental/filesystem/directory_iterator.cpp
index a552fdc44..b0b31450e 100644
--- a/src/experimental/filesystem/directory_iterator.cpp
+++ b/src/experimental/filesystem/directory_iterator.cpp
@@ -17,32 +17,43 @@
#endif
#include <errno.h>
+#include "filesystem_common.h"
+
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
-namespace { namespace detail {
+namespace detail {
+namespace {
#if !defined(_LIBCPP_WIN32API)
-inline error_code capture_errno() {
- _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
- return error_code{errno, std::generic_category()};
+template <class DirEntT, class = decltype(DirEntT::d_type)>
+static file_type get_file_type(DirEntT *ent, int) {
+ switch (ent->d_type) {
+ case DT_BLK:
+ return file_type::block;
+ case DT_CHR:
+ return file_type::character;
+ case DT_DIR:
+ return file_type::directory;
+ case DT_FIFO:
+ return file_type::fifo;
+ case DT_LNK:
+ return file_type::symlink;
+ case DT_REG:
+ return file_type::regular;
+ case DT_SOCK:
+ return file_type::socket;
+ case DT_UNKNOWN:
+ return file_type::unknown;
+ }
+ return file_type::none;
}
-#endif
-
-template <class ...Args>
-inline bool set_or_throw(std::error_code& my_ec,
- std::error_code* user_ec,
- const char* msg, Args&&... args)
-{
- if (user_ec) {
- *user_ec = my_ec;
- return true;
- }
- __throw_filesystem_error(msg, std::forward<Args>(args)..., my_ec);
- return false;
+template <class DirEntT>
+static file_type get_file_type(DirEntT *ent, long) {
+ return file_type::unknown;
}
-#if !defined(_LIBCPP_WIN32API)
-inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
+static pair<string_view, file_type>
+posix_readdir(DIR *dir_stream, error_code& ec) {
struct dirent* dir_entry_ptr = nullptr;
errno = 0; // zero errno in order to detect errors
ec.clear();
@@ -51,18 +62,38 @@ inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) {
ec = capture_errno();
return {};
} else {
- return dir_entry_ptr->d_name;
+ return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
}
}
+#else
+
+static file_type get_file_type(const WIN32_FIND_DATA& data) {
+ //auto attrs = data.dwFileAttributes;
+ // FIXME(EricWF)
+ return file_type::unknown;
+}
+static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
+ return (data.nFileSizeHight * (MAXDWORD+1)) + data.nFileSizeLow;
+}
+static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
+ ULARGE_INTEGER tmp;
+ FILETIME& time = data.ftLastWriteTime;
+ tmp.u.LowPart = time.dwLowDateTime;
+ tmp.u.HighPart = time.dwHighDateTime;
+ return file_time_type(file_time_type::duration(time.QuadPart));
+}
+
#endif
-}} // namespace detail
+} // namespace
+} // namespace detail
using detail::set_or_throw;
#if defined(_LIBCPP_WIN32API)
class __dir_stream {
public:
+
__dir_stream() = delete;
__dir_stream& operator=(const __dir_stream&) = delete;
@@ -74,7 +105,7 @@ public:
__dir_stream(const path& root, directory_options opts, error_code& ec)
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
- __stream_ = ::FindFirstFile(root.c_str(), &__data_);
+ __stream_ = ::FindFirstFileEx(root.c_str(), &__data_);
if (__stream_ == INVALID_HANDLE_VALUE) {
ec = error_code(::GetLastError(), std::generic_category());
const bool ignore_permission_denied =
@@ -97,7 +128,14 @@ public:
while (::FindNextFile(__stream_, &__data_)) {
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
continue;
- __entry_.assign(__root_ / __data_.cFileName);
+ // FIXME: Cache more of this
+ //directory_entry::__cached_data cdata;
+ //cdata.__type_ = get_file_type(__data_);
+ //cdata.__size_ = get_file_size(__data_);
+ //cdata.__write_time_ = get_write_time(__data_);
+ __entry_.__assign_iter_entry(
+ __root_ / __data_.cFileName,
+ directory_entry::__create_iter_result(get_file_type(__data)));
return true;
}
ec = error_code(::GetLastError(), std::generic_category());
@@ -157,15 +195,18 @@ public:
bool advance(error_code &ec) {
while (true) {
- auto str = detail::posix_readdir(__stream_, ec);
+ auto str_type_pair = detail::posix_readdir(__stream_, ec);
+ auto& str = str_type_pair.first;
if (str == "." || str == "..") {
continue;
} else if (ec || str.empty()) {
close();
return false;
} else {
- __entry_.assign(__root_ / str);
- return true;
+ __entry_.__assign_iter_entry(
+ __root_ / str,
+ directory_entry::__create_iter_result(str_type_pair.second));
+ return true;
}
}
}
diff --git a/src/experimental/filesystem/filesystem_time_helper.h b/src/experimental/filesystem/filesystem_common.h
index a60fdef5f..ab7ecbd01 100644
--- a/src/experimental/filesystem/filesystem_time_helper.h
+++ b/src/experimental/filesystem/filesystem_common.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===////
-#ifndef FILESYSTEM_TIME_HELPER_H
-#define FILESYSTEM_TIME_HELPER_H
+#ifndef FILESYSTEM_COMMON_H
+#define FILESYSTEM_COMMON_H
#include "experimental/__config"
#include "chrono"
@@ -17,13 +17,77 @@
#include <unistd.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h> /* values for fchmodat */
+
+#include <experimental/filesystem>
+
+#if (__APPLE__)
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
+#define _LIBCXX_USE_UTIMENSAT
+#endif
+#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
+#define _LIBCXX_USE_UTIMENSAT
+#endif
+#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
+#define _LIBCXX_USE_UTIMENSAT
+#endif
+#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
+#define _LIBCXX_USE_UTIMENSAT
+#endif
+#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
+#else
+// We can use the presence of UTIME_OMIT to detect platforms that provide
+// utimensat.
+#if defined(UTIME_OMIT)
+#define _LIBCXX_USE_UTIMENSAT
+#endif
+#endif // __APPLE__
+
+#if !defined(_LIBCXX_USE_UTIMENSAT)
+#include <sys/time.h> // for ::utimes as used in __last_write_time
+#endif
+
#if !defined(UTIME_OMIT)
#include <sys/time.h> // for ::utimes as used in __last_write_time
#endif
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
-namespace time_detail { namespace {
+namespace detail {
+namespace {
+
+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);
+ }
+}
+
+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);
+}
+
+namespace time_util {
using namespace chrono;
@@ -78,9 +142,8 @@ const long long fs_time_util_base<FileTimeT, true>::min_seconds =
template <class FileTimeT>
const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
- duration_cast<nanoseconds>((FileTimeT::duration::min() -
- seconds(min_seconds)) +
- seconds(1))
+ duration_cast<nanoseconds>(
+ (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
.count();
template <class FileTimeT, class TimeT, class TimeSpecT>
@@ -145,7 +208,6 @@ public:
template <class SubSecDurT, class SubSecT>
static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
FileTimeT tp) {
- using namespace chrono;
auto dur = tp.time_since_epoch();
auto sec_dur = duration_cast<seconds>(dur);
auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
@@ -163,11 +225,72 @@ public:
}
};
-} // end namespace
-} // end namespace time_detail
+} // namespace time_util
-using time_detail::fs_time_util;
+
+using TimeSpec = struct timespec;
+using StatT = struct stat;
+
+using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
+
+#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; }
+TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
+#endif
+
+#if !defined(_LIBCXX_USE_UTIMENSAT)
+using TimeStruct = struct ::timeval;
+using TimeStructArray = TimeStruct[2];
+#else
+using TimeStruct = struct ::timespec;
+using TimeStructArray = TimeStruct[2];
+#endif
+
+bool SetFileTimes(const path& p, TimeStructArray const& TS,
+ std::error_code& ec) {
+#if !defined(_LIBCXX_USE_UTIMENSAT)
+ if (::utimes(p.c_str(), TS) == -1)
+#else
+ if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
+#endif
+ {
+ ec = capture_errno();
+ return true;
+ }
+ return false;
+}
+
+void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
+ using namespace chrono;
+ TS.tv_sec = ToTS.tv_sec;
+#if !defined(_LIBCXX_USE_UTIMENSAT)
+ TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
+#else
+ TS.tv_nsec = ToTS.tv_nsec;
+#endif
+}
+
+bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
+ using namespace chrono;
+#if !defined(_LIBCXX_USE_UTIMENSAT)
+ return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
+ NewTime);
+#else
+ return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
+ NewTime);
+#endif
+}
+
+} // namespace
+} // end namespace detail
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM
-#endif // FILESYSTEM_TIME_HELPER_H
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+#endif // FILESYSTEM_COMMON_H
diff --git a/src/experimental/filesystem/operations.cpp b/src/experimental/filesystem/operations.cpp
index 70284ab65..449b60975 100644
--- a/src/experimental/filesystem/operations.cpp
+++ b/src/experimental/filesystem/operations.cpp
@@ -17,42 +17,18 @@
#include "cstdlib"
#include "climits"
-#include "filesystem_time_helper.h"
+#include "filesystem_common.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <fcntl.h> /* values for fchmodat */
+#include <experimental/filesystem>
-#if (__APPLE__)
-#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
-#else
-// We can use the presence of UTIME_OMIT to detect platforms that provide
-// utimensat.
-#if defined(UTIME_OMIT)
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#endif // __APPLE__
-
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-#include <sys/time.h> // for ::utimes as used in __last_write_time
+#ifdef NDEBUG
+#undef NDEBUG
#endif
+#include <cassert>
_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
@@ -310,96 +286,78 @@ namespace detail { namespace {
using value_type = path::value_type;
using string_type = path::string_type;
-inline 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);
- }
-}
-
-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);
-}
-
-perms posix_get_perms(const struct ::stat & st) noexcept {
- return static_cast<perms>(st.st_mode) & perms::mask;
+perms posix_get_perms(const struct ::stat& st) noexcept {
+ return static_cast<perms>(st.st_mode) & perms::mask;
}
::mode_t posix_convert_perms(perms prms) {
- return static_cast< ::mode_t>(prms & perms::mask);
+ return static_cast< ::mode_t>(prms & perms::mask);
}
file_status create_file_status(std::error_code& m_ec, path const& p,
- struct ::stat& path_stat,
- std::error_code* ec)
-{
- if (ec) *ec = m_ec;
- 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);
- return file_status(file_type::none);
- }
- // else
-
- file_status fs_tmp;
- auto const mode = path_stat.st_mode;
- if (S_ISLNK(mode)) fs_tmp.type(file_type::symlink);
- else if (S_ISREG(mode)) fs_tmp.type(file_type::regular);
- else if (S_ISDIR(mode)) fs_tmp.type(file_type::directory);
- else if (S_ISBLK(mode)) fs_tmp.type(file_type::block);
- else if (S_ISCHR(mode)) fs_tmp.type(file_type::character);
- else if (S_ISFIFO(mode)) fs_tmp.type(file_type::fifo);
- else if (S_ISSOCK(mode)) fs_tmp.type(file_type::socket);
- else fs_tmp.type(file_type::unknown);
-
- fs_tmp.permissions(detail::posix_get_perms(path_stat));
- return fs_tmp;
-}
-
-file_status posix_stat(path const & p, struct ::stat& path_stat,
- std::error_code* ec)
-{
- std::error_code m_ec;
- if (::stat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
- return create_file_status(m_ec, p, path_stat, ec);
+ struct ::stat& path_stat, std::error_code* ec) {
+ if (ec)
+ *ec = m_ec;
+ // assert(m_ec.value() != ENOTDIR);
+ 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);
+ return file_status(file_type::none);
+ }
+ // else
+
+ file_status fs_tmp;
+ auto const mode = path_stat.st_mode;
+ if (S_ISLNK(mode))
+ fs_tmp.type(file_type::symlink);
+ else if (S_ISREG(mode))
+ fs_tmp.type(file_type::regular);
+ else if (S_ISDIR(mode))
+ fs_tmp.type(file_type::directory);
+ else if (S_ISBLK(mode))
+ fs_tmp.type(file_type::block);
+ else if (S_ISCHR(mode))
+ fs_tmp.type(file_type::character);
+ else if (S_ISFIFO(mode))
+ fs_tmp.type(file_type::fifo);
+ else if (S_ISSOCK(mode))
+ fs_tmp.type(file_type::socket);
+ else
+ fs_tmp.type(file_type::unknown);
+
+ fs_tmp.permissions(detail::posix_get_perms(path_stat));
+ return fs_tmp;
+}
+
+file_status posix_stat(path const& p, struct ::stat& path_stat,
+ std::error_code* ec) {
+ std::error_code m_ec;
+ if (::stat(p.c_str(), &path_stat) == -1)
+ m_ec = detail::capture_errno();
+ return create_file_status(m_ec, p, path_stat, ec);
}
-file_status posix_stat(path const & p, std::error_code* ec) {
- struct ::stat path_stat;
- return posix_stat(p, path_stat, ec);
+file_status posix_stat(path const& p, std::error_code* ec) {
+ struct ::stat path_stat;
+ return posix_stat(p, path_stat, ec);
}
-file_status posix_lstat(path const & p, struct ::stat & path_stat,
- std::error_code* ec)
-{
- std::error_code m_ec;
- if (::lstat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
- return create_file_status(m_ec, p, path_stat, ec);
+file_status posix_lstat(path const& p, struct ::stat& path_stat,
+ std::error_code* ec) {
+ std::error_code m_ec;
+ if (::lstat(p.c_str(), &path_stat) == -1)
+ m_ec = detail::capture_errno();
+ return create_file_status(m_ec, p, path_stat, ec);
}
-file_status posix_lstat(path const & p, std::error_code* ec) {
- struct ::stat path_stat;
- return posix_lstat(p, path_stat, ec);
+file_status posix_lstat(path const& p, std::error_code* ec) {
+ struct ::stat path_stat;
+ return posix_lstat(p, path_stat, ec);
}
bool stat_equivalent(struct ::stat& st1, struct ::stat& st2) {
- return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
+ return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
}
// DETAIL::MISC
@@ -622,7 +580,6 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink,
__create_symlink(real_path, new_symlink, ec);
}
-
bool __create_directories(const path& p, std::error_code *ec)
{
std::error_code m_ec;
@@ -755,10 +712,12 @@ std::uintmax_t __file_size(const path& p, std::error_code *ec)
struct ::stat st;
file_status fst = detail::posix_stat(p, st, &m_ec);
if (!exists(fst) || !is_regular_file(fst)) {
- if (!m_ec)
- m_ec = make_error_code(errc::not_supported);
- set_or_throw(m_ec, ec, "file_size", p);
- return static_cast<uintmax_t>(-1);
+ 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);
}
// is_regular_file(p) == true
if (ec) ec->clear();
@@ -806,25 +765,18 @@ bool __fs_is_empty(const path& p, std::error_code *ec)
_LIBCPP_UNREACHABLE();
}
-
-namespace detail { namespace {
-
-using TimeSpec = struct timespec;
-using StatT = struct stat;
-
-#if defined(__APPLE__)
-TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
-__attribute__((unused)) // Suppress warning
-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
-
-}} // end namespace detail
-
-using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
+static file_time_type __extract_last_write_time(path const& p,
+ const struct ::stat& st,
+ error_code *ec) {
+ using detail::FSTime;
+ 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();
+ }
+ return FSTime::convert_timespec(ts);
+}
file_time_type __last_write_time(const path& p, std::error_code *ec)
{
@@ -837,21 +789,17 @@ file_time_type __last_write_time(const path& p, std::error_code *ec)
return file_time_type::min();
}
if (ec) ec->clear();
- auto ts = detail::extract_mtime(st);
- if (!FSTime::is_representable(ts)) {
- set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
- "last_write_time", p);
- return file_time_type::min();
- }
- return FSTime::convert_timespec(ts);
+ return __extract_last_write_time(p, st, ec);
}
void __last_write_time(const path& p, file_time_type new_time,
std::error_code *ec)
{
using namespace std::chrono;
- std::error_code m_ec;
+ using namespace detail;
+ std::error_code m_ec;
+ TimeStructArray tbuf;
#if !defined(_LIBCXX_USE_UTIMENSAT)
// This implementation has a race condition between determining the
// last access time and attempting to set it to the same value using
@@ -862,37 +810,18 @@ 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 = atime.tv_sec;
- tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
- const bool overflowed = !FSTime::set_times_checked<microseconds>(
- &tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
-
- if (overflowed) {
- set_or_throw(make_error_code(errc::invalid_argument), ec,
- "last_write_time", p);
- return;
- }
- if (::utimes(p.c_str(), tbuf) == -1) {
- m_ec = detail::capture_errno();
- }
+ SetTimeStructTo(tbuf[0], detail::extract_atime(st));
#else
- struct ::timespec tbuf[2];
tbuf[0].tv_sec = 0;
tbuf[0].tv_nsec = UTIME_OMIT;
-
- const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
- &tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
- if (overflowed) {
- set_or_throw(make_error_code(errc::invalid_argument),
- ec, "last_write_time", p);
- return;
- }
- if (::utimensat(AT_FDCWD, p.c_str(), tbuf, 0) == -1) {
- m_ec = detail::capture_errno();
- }
#endif
+ if (SetTimeStructTo(tbuf[1], new_time)) {
+ set_or_throw(make_error_code(errc::invalid_argument), ec,
+ "last_write_time", p);
+ return;
+ }
+
+ SetFileTimes(p, tbuf, m_ec);
if (m_ec)
set_or_throw(m_ec, ec, "last_write_time", p);
else if (ec)
@@ -1116,8 +1045,6 @@ path __weakly_canonical(const path& p, std::error_code *ec) {
return result.lexically_normal();
}
-
-
///////////////////////////////////////////////////////////////////////////////
// path definitions
///////////////////////////////////////////////////////////////////////////////
@@ -1468,5 +1395,105 @@ path::iterator& path::iterator::__decrement() {
return *this;
}
+///////////////////////////////////////////////////////////////////////////////
+// directory entry definitions
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _LIBCPP_WIN32API
+error_code directory_entry::__do_refresh() noexcept {
+ __data_.__reset();
+ error_code failure_ec;
+
+ struct ::stat full_st;
+ file_status st = detail::posix_lstat(__p_, full_st, &failure_ec);
+ if (!status_known(st)) {
+ __data_.__reset();
+ return failure_ec;
+ }
+
+ if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+ } else { // we have a symlink
+ __data_.__sym_perms_ = st.permissions();
+ // Get the information about the linked entity.
+ // Ignore errors from stat, since we don't want errors regarding symlink
+ // resolution to be reported to the user.
+ error_code ignored_ec;
+ st = detail::posix_stat(__p_, full_st, &ignored_ec);
+
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+
+ // If we failed to resolve the link, then only partially populate the
+ // cache.
+ if (!status_known(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
+ return error_code{};
+ }
+ // Otherwise, we resolved the link as not existing. That's OK.
+ __data_.__cache_type_ = directory_entry::_RefreshSymlink;
+ }
+
+ if (_VSTD_FS::is_regular_file(st))
+ __data_.__size_ = static_cast<uintmax_t>(full_st.st_size);
+
+ if (_VSTD_FS::exists(st)) {
+ __data_.__nlink_ = static_cast<uintmax_t>(full_st.st_nlink);
+
+ // Attempt to extract the mtime, and fail if it's not representable using
+ // file_time_type. For now we ignore the error, as we'll report it when
+ // the value is actually used.
+ error_code ignored_ec;
+ __data_.__write_time_ =
+ __extract_last_write_time(__p_, full_st, &ignored_ec);
+ }
+
+ return failure_ec;
+}
+#else
+error_code directory_entry::__do_refresh() noexcept {
+ __data_.__reset();
+ error_code failure_ec;
+
+ file_status st = _VSTD_FS::symlink_status(__p_, failure_ec);
+ if (!status_known(st)) {
+ __data_.__reset();
+ return failure_ec;
+ }
+
+ if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+ } else { // we have a symlink
+ __data_.__sym_perms_ = st.permissions();
+ // Get the information about the linked entity.
+ // Ignore errors from stat, since we don't want errors regarding symlink
+ // resolution to be reported to the user.
+ error_code ignored_ec;
+ st = _VSTD_FS::status(__p_, ignored_ec);
+
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+
+ // If we failed to resolve the link, then only partially populate the
+ // cache.
+ if (!status_known(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
+ return error_code{};
+ }
+ // Otherwise, we resolved the link as not existing. That's OK.
+ __data_.__cache_type_ = directory_entry::_RefreshSymlink;
+ }
+
+ // FIXME: This is currently broken, and the implementation only a placeholder.
+ // We need to cache last_write_time, file_size, and hard_link_count here before
+ // the implementation actually works.
+
+ return failure_ec;
+}
+#endif
_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM