1349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 981ad6265SDimitry Andric #include <__assert> 1081ad6265SDimitry Andric #include <__config> 110b57cec5SDimitry Andric #include <errno.h> 1281ad6265SDimitry Andric #include <filesystem> 1381ad6265SDimitry Andric #include <stack> 1406c3fb27SDimitry Andric #include <utility> 150b57cec5SDimitry Andric 1606c3fb27SDimitry Andric #include "error.h" 1706c3fb27SDimitry Andric #include "file_descriptor.h" 1806c3fb27SDimitry Andric 1906c3fb27SDimitry Andric #if defined(_LIBCPP_WIN32API) 2006c3fb27SDimitry Andric # define WIN32_LEAN_AND_MEAN 2106c3fb27SDimitry Andric # define NOMINMAX 2206c3fb27SDimitry Andric # include <windows.h> 2306c3fb27SDimitry Andric #else 2406c3fb27SDimitry Andric # include <dirent.h> // for DIR & friends 2506c3fb27SDimitry Andric #endif 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric using detail::ErrorHandler; 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric #if defined(_LIBCPP_WIN32API) 320b57cec5SDimitry Andric class __dir_stream { 330b57cec5SDimitry Andric public: 340b57cec5SDimitry Andric __dir_stream() = delete; 350b57cec5SDimitry Andric __dir_stream& operator=(const __dir_stream&) = delete; 360b57cec5SDimitry Andric 37*cb14a3feSDimitry Andric __dir_stream(__dir_stream&& __ds) noexcept 38*cb14a3feSDimitry Andric : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), __entry_(std::move(__ds.__entry_)) { 390b57cec5SDimitry Andric __ds.__stream_ = INVALID_HANDLE_VALUE; 400b57cec5SDimitry Andric } 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric __dir_stream(const path& root, directory_options opts, error_code& ec) 430b57cec5SDimitry Andric : __stream_(INVALID_HANDLE_VALUE), __root_(root) { 44e8d8bef9SDimitry Andric if (root.native().empty()) { 45e8d8bef9SDimitry Andric ec = make_error_code(errc::no_such_file_or_directory); 46e8d8bef9SDimitry Andric return; 47e8d8bef9SDimitry Andric } 48e8d8bef9SDimitry Andric __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_); 490b57cec5SDimitry Andric if (__stream_ == INVALID_HANDLE_VALUE) { 50e8d8bef9SDimitry Andric ec = detail::make_windows_error(GetLastError()); 51*cb14a3feSDimitry Andric const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied); 52*cb14a3feSDimitry Andric if (ignore_permission_denied && ec.value() == static_cast<int>(errc::permission_denied)) 530b57cec5SDimitry Andric ec.clear(); 540b57cec5SDimitry Andric return; 550b57cec5SDimitry Andric } 56e8d8bef9SDimitry Andric if (!assign()) 57e8d8bef9SDimitry Andric advance(ec); 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric ~__dir_stream() noexcept { 610b57cec5SDimitry Andric if (__stream_ == INVALID_HANDLE_VALUE) 620b57cec5SDimitry Andric return; 630b57cec5SDimitry Andric close(); 640b57cec5SDimitry Andric } 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; } 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric bool advance(error_code& ec) { 69e8d8bef9SDimitry Andric while (::FindNextFileW(__stream_, &__data_)) { 70e8d8bef9SDimitry Andric if (assign()) 71e8d8bef9SDimitry Andric return true; 72e8d8bef9SDimitry Andric } 73e8d8bef9SDimitry Andric close(); 74e8d8bef9SDimitry Andric return false; 75e8d8bef9SDimitry Andric } 76e8d8bef9SDimitry Andric 77e8d8bef9SDimitry Andric bool assign() { 78e8d8bef9SDimitry Andric if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L"..")) 79e8d8bef9SDimitry Andric return false; 800b57cec5SDimitry Andric // FIXME: Cache more of this 810b57cec5SDimitry Andric // directory_entry::__cached_data cdata; 820b57cec5SDimitry Andric // cdata.__type_ = get_file_type(__data_); 830b57cec5SDimitry Andric // cdata.__size_ = get_file_size(__data_); 840b57cec5SDimitry Andric // cdata.__write_time_ = get_write_time(__data_); 850b57cec5SDimitry Andric __entry_.__assign_iter_entry( 86*cb14a3feSDimitry Andric __root_ / __data_.cFileName, directory_entry::__create_iter_result(detail::get_file_type(__data_))); 870b57cec5SDimitry Andric return true; 880b57cec5SDimitry Andric } 890b57cec5SDimitry Andric 900b57cec5SDimitry Andric private: 910b57cec5SDimitry Andric error_code close() noexcept { 920b57cec5SDimitry Andric error_code ec; 930b57cec5SDimitry Andric if (!::FindClose(__stream_)) 94e8d8bef9SDimitry Andric ec = detail::make_windows_error(GetLastError()); 950b57cec5SDimitry Andric __stream_ = INVALID_HANDLE_VALUE; 960b57cec5SDimitry Andric return ec; 970b57cec5SDimitry Andric } 980b57cec5SDimitry Andric 990b57cec5SDimitry Andric HANDLE __stream_{INVALID_HANDLE_VALUE}; 100e8d8bef9SDimitry Andric WIN32_FIND_DATAW __data_; 1010b57cec5SDimitry Andric 1020b57cec5SDimitry Andric public: 1030b57cec5SDimitry Andric path __root_; 1040b57cec5SDimitry Andric directory_entry __entry_; 1050b57cec5SDimitry Andric }; 1060b57cec5SDimitry Andric #else 1070b57cec5SDimitry Andric class __dir_stream { 1080b57cec5SDimitry Andric public: 1090b57cec5SDimitry Andric __dir_stream() = delete; 1100b57cec5SDimitry Andric __dir_stream& operator=(const __dir_stream&) = delete; 1110b57cec5SDimitry Andric 112*cb14a3feSDimitry Andric __dir_stream(__dir_stream&& other) noexcept 113*cb14a3feSDimitry Andric : __stream_(other.__stream_), __root_(std::move(other.__root_)), __entry_(std::move(other.__entry_)) { 1140b57cec5SDimitry Andric other.__stream_ = nullptr; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 117*cb14a3feSDimitry Andric __dir_stream(const path& root, directory_options opts, error_code& ec) : __stream_(nullptr), __root_(root) { 1180b57cec5SDimitry Andric if ((__stream_ = ::opendir(root.c_str())) == nullptr) { 1190b57cec5SDimitry Andric ec = detail::capture_errno(); 120*cb14a3feSDimitry Andric const bool allow_eacces = bool(opts & directory_options::skip_permission_denied); 12104eeddc0SDimitry Andric if (allow_eacces && ec.value() == EACCES) 1220b57cec5SDimitry Andric ec.clear(); 1230b57cec5SDimitry Andric return; 1240b57cec5SDimitry Andric } 1250b57cec5SDimitry Andric advance(ec); 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric 1280b57cec5SDimitry Andric ~__dir_stream() noexcept { 1290b57cec5SDimitry Andric if (__stream_) 1300b57cec5SDimitry Andric close(); 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric bool good() const noexcept { return __stream_ != nullptr; } 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric bool advance(error_code& ec) { 1360b57cec5SDimitry Andric while (true) { 1370b57cec5SDimitry Andric auto str_type_pair = detail::posix_readdir(__stream_, ec); 1380b57cec5SDimitry Andric auto& str = str_type_pair.first; 1390b57cec5SDimitry Andric if (str == "." || str == "..") { 1400b57cec5SDimitry Andric continue; 1410b57cec5SDimitry Andric } else if (ec || str.empty()) { 1420b57cec5SDimitry Andric close(); 1430b57cec5SDimitry Andric return false; 1440b57cec5SDimitry Andric } else { 145*cb14a3feSDimitry Andric __entry_.__assign_iter_entry(__root_ / str, directory_entry::__create_iter_result(str_type_pair.second)); 1460b57cec5SDimitry Andric return true; 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric } 1490b57cec5SDimitry Andric } 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric private: 1520b57cec5SDimitry Andric error_code close() noexcept { 1530b57cec5SDimitry Andric error_code m_ec; 1540b57cec5SDimitry Andric if (::closedir(__stream_) == -1) 1550b57cec5SDimitry Andric m_ec = detail::capture_errno(); 1560b57cec5SDimitry Andric __stream_ = nullptr; 1570b57cec5SDimitry Andric return m_ec; 1580b57cec5SDimitry Andric } 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric DIR* __stream_{nullptr}; 1610b57cec5SDimitry Andric 1620b57cec5SDimitry Andric public: 1630b57cec5SDimitry Andric path __root_; 1640b57cec5SDimitry Andric directory_entry __entry_; 1650b57cec5SDimitry Andric }; 1660b57cec5SDimitry Andric #endif 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric // directory_iterator 1690b57cec5SDimitry Andric 170*cb14a3feSDimitry Andric directory_iterator::directory_iterator(const path& p, error_code* ec, directory_options opts) { 1710b57cec5SDimitry Andric ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p); 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric error_code m_ec; 1740b57cec5SDimitry Andric __imp_ = make_shared<__dir_stream>(p, opts, m_ec); 1750b57cec5SDimitry Andric if (ec) 1760b57cec5SDimitry Andric *ec = m_ec; 1770b57cec5SDimitry Andric if (!__imp_->good()) { 1780b57cec5SDimitry Andric __imp_.reset(); 1790b57cec5SDimitry Andric if (m_ec) 1800b57cec5SDimitry Andric err.report(m_ec); 1810b57cec5SDimitry Andric } 1820b57cec5SDimitry Andric } 1830b57cec5SDimitry Andric 1840b57cec5SDimitry Andric directory_iterator& directory_iterator::__increment(error_code* ec) { 1855f757f3fSDimitry Andric _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to increment an invalid iterator"); 1860b57cec5SDimitry Andric ErrorHandler<void> err("directory_iterator::operator++()", ec); 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric error_code m_ec; 1890b57cec5SDimitry Andric if (!__imp_->advance(m_ec)) { 19081ad6265SDimitry Andric path root = std::move(__imp_->__root_); 1910b57cec5SDimitry Andric __imp_.reset(); 1920b57cec5SDimitry Andric if (m_ec) 193fe6060f1SDimitry Andric err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric return *this; 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric 1980b57cec5SDimitry Andric directory_entry const& directory_iterator::__dereference() const { 1995f757f3fSDimitry Andric _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to dereference an invalid iterator"); 2000b57cec5SDimitry Andric return __imp_->__entry_; 2010b57cec5SDimitry Andric } 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric // recursive_directory_iterator 2040b57cec5SDimitry Andric 2050b57cec5SDimitry Andric struct recursive_directory_iterator::__shared_imp { 2060b57cec5SDimitry Andric stack<__dir_stream> __stack_; 2070b57cec5SDimitry Andric directory_options __options_; 2080b57cec5SDimitry Andric }; 2090b57cec5SDimitry Andric 210*cb14a3feSDimitry Andric recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code* ec) 2110b57cec5SDimitry Andric : __imp_(nullptr), __rec_(true) { 2120b57cec5SDimitry Andric ErrorHandler<void> err("recursive_directory_iterator", ec, &p); 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric error_code m_ec; 2150b57cec5SDimitry Andric __dir_stream new_s(p, opt, m_ec); 2160b57cec5SDimitry Andric if (m_ec) 2170b57cec5SDimitry Andric err.report(m_ec); 2180b57cec5SDimitry Andric if (m_ec || !new_s.good()) 2190b57cec5SDimitry Andric return; 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric __imp_ = make_shared<__shared_imp>(); 2220b57cec5SDimitry Andric __imp_->__options_ = opt; 22381ad6265SDimitry Andric __imp_->__stack_.push(std::move(new_s)); 2240b57cec5SDimitry Andric } 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric void recursive_directory_iterator::__pop(error_code* ec) { 2275f757f3fSDimitry Andric _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Popping the end iterator"); 2280b57cec5SDimitry Andric if (ec) 2290b57cec5SDimitry Andric ec->clear(); 2300b57cec5SDimitry Andric __imp_->__stack_.pop(); 2310b57cec5SDimitry Andric if (__imp_->__stack_.size() == 0) 2320b57cec5SDimitry Andric __imp_.reset(); 2330b57cec5SDimitry Andric else 2340b57cec5SDimitry Andric __advance(ec); 2350b57cec5SDimitry Andric } 2360b57cec5SDimitry Andric 237*cb14a3feSDimitry Andric directory_options recursive_directory_iterator::options() const { return __imp_->__options_; } 2380b57cec5SDimitry Andric 239*cb14a3feSDimitry Andric int recursive_directory_iterator::depth() const { return __imp_->__stack_.size() - 1; } 2400b57cec5SDimitry Andric 241*cb14a3feSDimitry Andric const directory_entry& recursive_directory_iterator::__dereference() const { return __imp_->__stack_.top().__entry_; } 2420b57cec5SDimitry Andric 243*cb14a3feSDimitry Andric recursive_directory_iterator& recursive_directory_iterator::__increment(error_code* ec) { 2440b57cec5SDimitry Andric if (ec) 2450b57cec5SDimitry Andric ec->clear(); 2460b57cec5SDimitry Andric if (recursion_pending()) { 2470b57cec5SDimitry Andric if (__try_recursion(ec) || (ec && *ec)) 2480b57cec5SDimitry Andric return *this; 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric __rec_ = true; 2510b57cec5SDimitry Andric __advance(ec); 2520b57cec5SDimitry Andric return *this; 2530b57cec5SDimitry Andric } 2540b57cec5SDimitry Andric 2550b57cec5SDimitry Andric void recursive_directory_iterator::__advance(error_code* ec) { 2560b57cec5SDimitry Andric ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric const directory_iterator end_it; 2590b57cec5SDimitry Andric auto& stack = __imp_->__stack_; 2600b57cec5SDimitry Andric error_code m_ec; 2610b57cec5SDimitry Andric while (stack.size() > 0) { 2620b57cec5SDimitry Andric if (stack.top().advance(m_ec)) 2630b57cec5SDimitry Andric return; 2640b57cec5SDimitry Andric if (m_ec) 2650b57cec5SDimitry Andric break; 2660b57cec5SDimitry Andric stack.pop(); 2670b57cec5SDimitry Andric } 2680b57cec5SDimitry Andric 2690b57cec5SDimitry Andric if (m_ec) { 27081ad6265SDimitry Andric path root = std::move(stack.top().__root_); 2710b57cec5SDimitry Andric __imp_.reset(); 272fe6060f1SDimitry Andric err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); 2730b57cec5SDimitry Andric } else { 2740b57cec5SDimitry Andric __imp_.reset(); 2750b57cec5SDimitry Andric } 2760b57cec5SDimitry Andric } 2770b57cec5SDimitry Andric 2780b57cec5SDimitry Andric bool recursive_directory_iterator::__try_recursion(error_code* ec) { 2790b57cec5SDimitry Andric ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); 2800b57cec5SDimitry Andric 2810b57cec5SDimitry Andric bool rec_sym = bool(options() & directory_options::follow_directory_symlink); 2820b57cec5SDimitry Andric 2830b57cec5SDimitry Andric auto& curr_it = __imp_->__stack_.top(); 2840b57cec5SDimitry Andric 2850b57cec5SDimitry Andric bool skip_rec = false; 2860b57cec5SDimitry Andric error_code m_ec; 2870b57cec5SDimitry Andric if (!rec_sym) { 2880b57cec5SDimitry Andric file_status st(curr_it.__entry_.__get_sym_ft(&m_ec)); 2890b57cec5SDimitry Andric if (m_ec && status_known(st)) 2900b57cec5SDimitry Andric m_ec.clear(); 2910b57cec5SDimitry Andric if (m_ec || is_symlink(st) || !is_directory(st)) 2920b57cec5SDimitry Andric skip_rec = true; 2930b57cec5SDimitry Andric } else { 2940b57cec5SDimitry Andric file_status st(curr_it.__entry_.__get_ft(&m_ec)); 2950b57cec5SDimitry Andric if (m_ec && status_known(st)) 2960b57cec5SDimitry Andric m_ec.clear(); 2970b57cec5SDimitry Andric if (m_ec || !is_directory(st)) 2980b57cec5SDimitry Andric skip_rec = true; 2990b57cec5SDimitry Andric } 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric if (!skip_rec) { 3020b57cec5SDimitry Andric __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec); 3030b57cec5SDimitry Andric if (new_it.good()) { 30481ad6265SDimitry Andric __imp_->__stack_.push(std::move(new_it)); 3050b57cec5SDimitry Andric return true; 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric } 3080b57cec5SDimitry Andric if (m_ec) { 309*cb14a3feSDimitry Andric const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied); 3100b57cec5SDimitry Andric if (m_ec.value() == EACCES && allow_eacess) { 3110b57cec5SDimitry Andric if (ec) 3120b57cec5SDimitry Andric ec->clear(); 3130b57cec5SDimitry Andric } else { 31481ad6265SDimitry Andric path at_ent = std::move(curr_it.__entry_.__p_); 3150b57cec5SDimitry Andric __imp_.reset(); 316*cb14a3feSDimitry Andric err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, at_ent.c_str()); 3170b57cec5SDimitry Andric } 3180b57cec5SDimitry Andric } 3190b57cec5SDimitry Andric return false; 3200b57cec5SDimitry Andric } 3210b57cec5SDimitry Andric 3220b57cec5SDimitry Andric _LIBCPP_END_NAMESPACE_FILESYSTEM 323