Author: compnerd Date: Mon Jan 30 12:50:34 2017 New Revision: 293531 URL: http://llvm.org/viewvc/llvm-project?rev=293531&view=rev Log: experimental: port directory_iterator to Windows
This adds a basic first cut implementation for directory_iterator on Windows. It uses the FindFirstFile/FindNextFile which has the same restrictions as opendir/readdir where there exists a TOCTOU race condition. Modified: libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp Modified: libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp?rev=293531&r1=293530&r2=293531&view=diff ============================================================================== --- libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp (original) +++ libcxx/trunk/src/experimental/filesystem/directory_iterator.cpp Mon Jan 30 12:50:34 2017 @@ -8,17 +8,24 @@ //===----------------------------------------------------------------------===// #include "experimental/filesystem" +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#else #include <dirent.h> +#endif #include <errno.h> _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM namespace { namespace detail { +#if !defined(_WIN32) inline error_code capture_errno() { _LIBCPP_ASSERT(errno, "Expected errno to be non-zero"); return error_code{errno, std::generic_category()}; } +#endif template <class ...Args> inline bool set_or_throw(std::error_code& my_ec, @@ -33,6 +40,7 @@ inline bool set_or_throw(std::error_code return false; } +#if !defined(_WIN32) inline path::string_type posix_readdir(DIR *dir_stream, error_code& ec) { struct dirent* dir_entry_ptr = nullptr; errno = 0; // zero errno in order to detect errors @@ -44,11 +52,74 @@ inline path::string_type posix_readdir(D return dir_entry_ptr->d_name; } } +#endif }} // namespace detail using detail::set_or_throw; +#if defined(_WIN32) +class __dir_stream { +public: + __dir_stream() = delete; + __dir_stream& operator=(const __dir_stream&) = delete; + + __dir_stream(__dir_stream&& __ds) noexcept + : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), + __entry_(std::move(__ds.__entry_)) { + ds.__stream_ = INVALID_HANDLE_VALUE; + } + + __dir_stream(const path& root, directory_options opts, error_code& ec) + : __stream_(INVALID_HANDLE_VALUE), __root_(root) { + __stream_ = ::FindFirstFile(root.c_str(), &__data_); + if (__stream_ == INVALID_HANDLE_VALUE) { + ec = error_code(::GetLastError(), std::generic_category()); + const bool ignore_permission_denied = + bool(opts & directory_options::skip_permission_denied); + if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED) + ec.clear(); + return; + } + } + + ~__dir_stream() noexcept { + if (__stream_ == INVALID_HANDLE_VALUE) + return; + close(); + } + + bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; } + + bool advance(error_code& ec) { + while (::FindNextFile(__stream_, &__data_)) { + if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, "..")) + continue; + __entry_.assign(__root_ / __data_.cFileName); + return true; + } + ec = error_code(::GetLastError(), std::generic_category()); + close(); + return false; + } + +private: + std::error_code close() noexcept { + std::error_code ec; + if (!::FindClose(__stream_)) + ec = error_code(::GetLastError(), std::generic_category()); + __stream_ = INVALID_HANDLE_VALUE; + return ec; + } + + HANDLE __stream_{INVALID_HANDLE_VALUE}; + WIN32_FIND_DATA __data_; + +public: + path __root_; + directory_entry __entry_; +}; +#else class __dir_stream { public: __dir_stream() = delete; @@ -110,6 +181,7 @@ public: path __root_; directory_entry __entry_; }; +#endif // directory_iterator _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits