1eb8650a7SLouis Dionne //===----------------------------------------------------------------------===// 2998a5c88SEric Fiselier // 357b08b09SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 457b08b09SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 557b08b09SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6998a5c88SEric Fiselier // 7998a5c88SEric Fiselier //===----------------------------------------------------------------------===// 8998a5c88SEric Fiselier 9f87aa19bSLouis Dionne #include <__assert> 10bbb0f2c7SArthur O'Dwyer #include <__config> 11998a5c88SEric Fiselier #include <errno.h> 12bbb0f2c7SArthur O'Dwyer #include <filesystem> 13bbb0f2c7SArthur O'Dwyer #include <stack> 14c7d3c844SLouis Dionne #include <utility> 15998a5c88SEric Fiselier 16c7d3c844SLouis Dionne #include "error.h" 17c7d3c844SLouis Dionne #include "file_descriptor.h" 18c7d3c844SLouis Dionne 19c7d3c844SLouis Dionne #if defined(_LIBCPP_WIN32API) 20c7d3c844SLouis Dionne # define WIN32_LEAN_AND_MEAN 21c7d3c844SLouis Dionne # define NOMINMAX 22c7d3c844SLouis Dionne # include <windows.h> 23c7d3c844SLouis Dionne #else 24c7d3c844SLouis Dionne # include <dirent.h> // for DIR & friends 25c7d3c844SLouis Dionne #endif 26998a5c88SEric Fiselier 27998a5c88SEric Fiselier _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM 28998a5c88SEric Fiselier 29998a5c88SEric Fiselier using detail::ErrorHandler; 30998a5c88SEric Fiselier 31998a5c88SEric Fiselier #if defined(_LIBCPP_WIN32API) 32998a5c88SEric Fiselier class __dir_stream { 33998a5c88SEric Fiselier public: 34998a5c88SEric Fiselier __dir_stream() = delete; 35998a5c88SEric Fiselier __dir_stream& operator=(const __dir_stream&) = delete; 36998a5c88SEric Fiselier 379783f28cSLouis Dionne __dir_stream(__dir_stream&& __ds) noexcept 389783f28cSLouis Dionne : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), __entry_(std::move(__ds.__entry_)) { 39998a5c88SEric Fiselier __ds.__stream_ = INVALID_HANDLE_VALUE; 40998a5c88SEric Fiselier } 41998a5c88SEric Fiselier 42998a5c88SEric Fiselier __dir_stream(const path& root, directory_options opts, error_code& ec) 43998a5c88SEric Fiselier : __stream_(INVALID_HANDLE_VALUE), __root_(root) { 4415618072SMartin Storsjö if (root.native().empty()) { 4515618072SMartin Storsjö ec = make_error_code(errc::no_such_file_or_directory); 4615618072SMartin Storsjö return; 4715618072SMartin Storsjö } 4815618072SMartin Storsjö __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_); 49998a5c88SEric Fiselier if (__stream_ == INVALID_HANDLE_VALUE) { 50*2b26ee6eSJames Y Knight ec = detail::get_last_error(); 519783f28cSLouis Dionne const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied); 52*2b26ee6eSJames Y Knight if (ignore_permission_denied && ec == errc::permission_denied) 53998a5c88SEric Fiselier ec.clear(); 54998a5c88SEric Fiselier return; 55998a5c88SEric Fiselier } 5615618072SMartin Storsjö if (!assign()) 5715618072SMartin Storsjö advance(ec); 58998a5c88SEric Fiselier } 59998a5c88SEric Fiselier 60998a5c88SEric Fiselier ~__dir_stream() noexcept { 61998a5c88SEric Fiselier if (__stream_ == INVALID_HANDLE_VALUE) 62998a5c88SEric Fiselier return; 63998a5c88SEric Fiselier close(); 64998a5c88SEric Fiselier } 65998a5c88SEric Fiselier 66998a5c88SEric Fiselier bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; } 67998a5c88SEric Fiselier 68998a5c88SEric Fiselier bool advance(error_code& ec) { 6915618072SMartin Storsjö while (::FindNextFileW(__stream_, &__data_)) { 7015618072SMartin Storsjö if (assign()) 7115618072SMartin Storsjö return true; 7215618072SMartin Storsjö } 7315618072SMartin Storsjö close(); 7415618072SMartin Storsjö return false; 7515618072SMartin Storsjö } 7615618072SMartin Storsjö 7715618072SMartin Storsjö bool assign() { 7815618072SMartin Storsjö if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L"..")) 7915618072SMartin Storsjö return false; 80998a5c88SEric Fiselier __entry_.__assign_iter_entry( 81b1b9b7b8SEduard Satdarov __root_ / __data_.cFileName, 82b1b9b7b8SEduard Satdarov directory_entry::__create_iter_cached_result( 83b1b9b7b8SEduard Satdarov detail::get_file_type(__data_), 84b1b9b7b8SEduard Satdarov detail::get_file_size(__data_), 85b1b9b7b8SEduard Satdarov detail::get_file_perm(__data_), 86b1b9b7b8SEduard Satdarov detail::get_write_time(__data_))); 87998a5c88SEric Fiselier return true; 88998a5c88SEric Fiselier } 89998a5c88SEric Fiselier 90998a5c88SEric Fiselier private: 91998a5c88SEric Fiselier error_code close() noexcept { 92998a5c88SEric Fiselier error_code ec; 93998a5c88SEric Fiselier if (!::FindClose(__stream_)) 94*2b26ee6eSJames Y Knight ec = detail::get_last_error(); 95998a5c88SEric Fiselier __stream_ = INVALID_HANDLE_VALUE; 96998a5c88SEric Fiselier return ec; 97998a5c88SEric Fiselier } 98998a5c88SEric Fiselier 99998a5c88SEric Fiselier HANDLE __stream_{INVALID_HANDLE_VALUE}; 10015618072SMartin Storsjö WIN32_FIND_DATAW __data_; 101998a5c88SEric Fiselier 102998a5c88SEric Fiselier public: 103998a5c88SEric Fiselier path __root_; 104998a5c88SEric Fiselier directory_entry __entry_; 105998a5c88SEric Fiselier }; 106998a5c88SEric Fiselier #else 107998a5c88SEric Fiselier class __dir_stream { 108998a5c88SEric Fiselier public: 109998a5c88SEric Fiselier __dir_stream() = delete; 110998a5c88SEric Fiselier __dir_stream& operator=(const __dir_stream&) = delete; 111998a5c88SEric Fiselier 1129783f28cSLouis Dionne __dir_stream(__dir_stream&& other) noexcept 1139783f28cSLouis Dionne : __stream_(other.__stream_), __root_(std::move(other.__root_)), __entry_(std::move(other.__entry_)) { 114998a5c88SEric Fiselier other.__stream_ = nullptr; 115998a5c88SEric Fiselier } 116998a5c88SEric Fiselier 1179783f28cSLouis Dionne __dir_stream(const path& root, directory_options opts, error_code& ec) : __stream_(nullptr), __root_(root) { 118998a5c88SEric Fiselier if ((__stream_ = ::opendir(root.c_str())) == nullptr) { 119998a5c88SEric Fiselier ec = detail::capture_errno(); 1209783f28cSLouis Dionne const bool allow_eacces = bool(opts & directory_options::skip_permission_denied); 121*2b26ee6eSJames Y Knight if (allow_eacces && ec == errc::permission_denied) 122998a5c88SEric Fiselier ec.clear(); 123998a5c88SEric Fiselier return; 124998a5c88SEric Fiselier } 125998a5c88SEric Fiselier advance(ec); 126998a5c88SEric Fiselier } 127998a5c88SEric Fiselier 128998a5c88SEric Fiselier ~__dir_stream() noexcept { 129998a5c88SEric Fiselier if (__stream_) 130998a5c88SEric Fiselier close(); 131998a5c88SEric Fiselier } 132998a5c88SEric Fiselier 133998a5c88SEric Fiselier bool good() const noexcept { return __stream_ != nullptr; } 134998a5c88SEric Fiselier 135998a5c88SEric Fiselier bool advance(error_code& ec) { 136998a5c88SEric Fiselier while (true) { 137998a5c88SEric Fiselier auto str_type_pair = detail::posix_readdir(__stream_, ec); 138998a5c88SEric Fiselier auto& str = str_type_pair.first; 139998a5c88SEric Fiselier if (str == "." || str == "..") { 140998a5c88SEric Fiselier continue; 141998a5c88SEric Fiselier } else if (ec || str.empty()) { 142998a5c88SEric Fiselier close(); 143998a5c88SEric Fiselier return false; 144998a5c88SEric Fiselier } else { 1459783f28cSLouis Dionne __entry_.__assign_iter_entry(__root_ / str, directory_entry::__create_iter_result(str_type_pair.second)); 146998a5c88SEric Fiselier return true; 147998a5c88SEric Fiselier } 148998a5c88SEric Fiselier } 149998a5c88SEric Fiselier } 150998a5c88SEric Fiselier 151998a5c88SEric Fiselier private: 152998a5c88SEric Fiselier error_code close() noexcept { 153998a5c88SEric Fiselier error_code m_ec; 154998a5c88SEric Fiselier if (::closedir(__stream_) == -1) 155998a5c88SEric Fiselier m_ec = detail::capture_errno(); 156998a5c88SEric Fiselier __stream_ = nullptr; 157998a5c88SEric Fiselier return m_ec; 158998a5c88SEric Fiselier } 159998a5c88SEric Fiselier 160998a5c88SEric Fiselier DIR* __stream_{nullptr}; 161998a5c88SEric Fiselier 162998a5c88SEric Fiselier public: 163998a5c88SEric Fiselier path __root_; 164998a5c88SEric Fiselier directory_entry __entry_; 165998a5c88SEric Fiselier }; 166998a5c88SEric Fiselier #endif 167998a5c88SEric Fiselier 168998a5c88SEric Fiselier // directory_iterator 169998a5c88SEric Fiselier 1709783f28cSLouis Dionne directory_iterator::directory_iterator(const path& p, error_code* ec, directory_options opts) { 171998a5c88SEric Fiselier ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p); 172998a5c88SEric Fiselier 173998a5c88SEric Fiselier error_code m_ec; 174998a5c88SEric Fiselier __imp_ = make_shared<__dir_stream>(p, opts, m_ec); 175998a5c88SEric Fiselier if (ec) 176998a5c88SEric Fiselier *ec = m_ec; 177998a5c88SEric Fiselier if (!__imp_->good()) { 178998a5c88SEric Fiselier __imp_.reset(); 179998a5c88SEric Fiselier if (m_ec) 180998a5c88SEric Fiselier err.report(m_ec); 181998a5c88SEric Fiselier } 182998a5c88SEric Fiselier } 183998a5c88SEric Fiselier 184998a5c88SEric Fiselier directory_iterator& directory_iterator::__increment(error_code* ec) { 185b85fdc4fSKonstantin Varlamov _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to increment an invalid iterator"); 186998a5c88SEric Fiselier ErrorHandler<void> err("directory_iterator::operator++()", ec); 187998a5c88SEric Fiselier 188998a5c88SEric Fiselier error_code m_ec; 189998a5c88SEric Fiselier if (!__imp_->advance(m_ec)) { 19048f8a7c4SCorentin Jabot path root = std::move(__imp_->__root_); 191998a5c88SEric Fiselier __imp_.reset(); 192998a5c88SEric Fiselier if (m_ec) 1930aa637b2SArthur O'Dwyer err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); 194998a5c88SEric Fiselier } 195998a5c88SEric Fiselier return *this; 196998a5c88SEric Fiselier } 197998a5c88SEric Fiselier 198998a5c88SEric Fiselier directory_entry const& directory_iterator::__dereference() const { 199b85fdc4fSKonstantin Varlamov _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to dereference an invalid iterator"); 200998a5c88SEric Fiselier return __imp_->__entry_; 201998a5c88SEric Fiselier } 202998a5c88SEric Fiselier 203998a5c88SEric Fiselier // recursive_directory_iterator 204998a5c88SEric Fiselier 205998a5c88SEric Fiselier struct recursive_directory_iterator::__shared_imp { 206998a5c88SEric Fiselier stack<__dir_stream> __stack_; 207998a5c88SEric Fiselier directory_options __options_; 208998a5c88SEric Fiselier }; 209998a5c88SEric Fiselier 2109783f28cSLouis Dionne recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code* ec) 211998a5c88SEric Fiselier : __imp_(nullptr), __rec_(true) { 212998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator", ec, &p); 213998a5c88SEric Fiselier 214998a5c88SEric Fiselier error_code m_ec; 215998a5c88SEric Fiselier __dir_stream new_s(p, opt, m_ec); 216998a5c88SEric Fiselier if (m_ec) 217998a5c88SEric Fiselier err.report(m_ec); 218998a5c88SEric Fiselier if (m_ec || !new_s.good()) 219998a5c88SEric Fiselier return; 220998a5c88SEric Fiselier 221998a5c88SEric Fiselier __imp_ = make_shared<__shared_imp>(); 222998a5c88SEric Fiselier __imp_->__options_ = opt; 22348f8a7c4SCorentin Jabot __imp_->__stack_.push(std::move(new_s)); 224998a5c88SEric Fiselier } 225998a5c88SEric Fiselier 226998a5c88SEric Fiselier void recursive_directory_iterator::__pop(error_code* ec) { 227b85fdc4fSKonstantin Varlamov _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Popping the end iterator"); 228998a5c88SEric Fiselier if (ec) 229998a5c88SEric Fiselier ec->clear(); 230998a5c88SEric Fiselier __imp_->__stack_.pop(); 231998a5c88SEric Fiselier if (__imp_->__stack_.size() == 0) 232998a5c88SEric Fiselier __imp_.reset(); 233998a5c88SEric Fiselier else 234998a5c88SEric Fiselier __advance(ec); 235998a5c88SEric Fiselier } 236998a5c88SEric Fiselier 2379783f28cSLouis Dionne directory_options recursive_directory_iterator::options() const { return __imp_->__options_; } 238998a5c88SEric Fiselier 2399783f28cSLouis Dionne int recursive_directory_iterator::depth() const { return __imp_->__stack_.size() - 1; } 240998a5c88SEric Fiselier 2419783f28cSLouis Dionne const directory_entry& recursive_directory_iterator::__dereference() const { return __imp_->__stack_.top().__entry_; } 242998a5c88SEric Fiselier 2439783f28cSLouis Dionne recursive_directory_iterator& recursive_directory_iterator::__increment(error_code* ec) { 244998a5c88SEric Fiselier if (ec) 245998a5c88SEric Fiselier ec->clear(); 246998a5c88SEric Fiselier if (recursion_pending()) { 247998a5c88SEric Fiselier if (__try_recursion(ec) || (ec && *ec)) 248998a5c88SEric Fiselier return *this; 249998a5c88SEric Fiselier } 250998a5c88SEric Fiselier __rec_ = true; 251998a5c88SEric Fiselier __advance(ec); 252998a5c88SEric Fiselier return *this; 253998a5c88SEric Fiselier } 254998a5c88SEric Fiselier 255998a5c88SEric Fiselier void recursive_directory_iterator::__advance(error_code* ec) { 256998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); 257998a5c88SEric Fiselier 258998a5c88SEric Fiselier const directory_iterator end_it; 259998a5c88SEric Fiselier auto& stack = __imp_->__stack_; 260998a5c88SEric Fiselier error_code m_ec; 261998a5c88SEric Fiselier while (stack.size() > 0) { 262998a5c88SEric Fiselier if (stack.top().advance(m_ec)) 263998a5c88SEric Fiselier return; 264998a5c88SEric Fiselier if (m_ec) 265998a5c88SEric Fiselier break; 266998a5c88SEric Fiselier stack.pop(); 267998a5c88SEric Fiselier } 268998a5c88SEric Fiselier 269998a5c88SEric Fiselier if (m_ec) { 27048f8a7c4SCorentin Jabot path root = std::move(stack.top().__root_); 271998a5c88SEric Fiselier __imp_.reset(); 2720aa637b2SArthur O'Dwyer err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str()); 273998a5c88SEric Fiselier } else { 274998a5c88SEric Fiselier __imp_.reset(); 275998a5c88SEric Fiselier } 276998a5c88SEric Fiselier } 277998a5c88SEric Fiselier 278998a5c88SEric Fiselier bool recursive_directory_iterator::__try_recursion(error_code* ec) { 279998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); 280998a5c88SEric Fiselier 281998a5c88SEric Fiselier bool rec_sym = bool(options() & directory_options::follow_directory_symlink); 282998a5c88SEric Fiselier 283998a5c88SEric Fiselier auto& curr_it = __imp_->__stack_.top(); 284998a5c88SEric Fiselier 285998a5c88SEric Fiselier bool skip_rec = false; 286998a5c88SEric Fiselier error_code m_ec; 287998a5c88SEric Fiselier if (!rec_sym) { 288998a5c88SEric Fiselier file_status st(curr_it.__entry_.__get_sym_ft(&m_ec)); 289998a5c88SEric Fiselier if (m_ec && status_known(st)) 290998a5c88SEric Fiselier m_ec.clear(); 291998a5c88SEric Fiselier if (m_ec || is_symlink(st) || !is_directory(st)) 292998a5c88SEric Fiselier skip_rec = true; 293998a5c88SEric Fiselier } else { 294998a5c88SEric Fiselier file_status st(curr_it.__entry_.__get_ft(&m_ec)); 295998a5c88SEric Fiselier if (m_ec && status_known(st)) 296998a5c88SEric Fiselier m_ec.clear(); 297998a5c88SEric Fiselier if (m_ec || !is_directory(st)) 298998a5c88SEric Fiselier skip_rec = true; 299998a5c88SEric Fiselier } 300998a5c88SEric Fiselier 301998a5c88SEric Fiselier if (!skip_rec) { 302998a5c88SEric Fiselier __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec); 303998a5c88SEric Fiselier if (new_it.good()) { 30448f8a7c4SCorentin Jabot __imp_->__stack_.push(std::move(new_it)); 305998a5c88SEric Fiselier return true; 306998a5c88SEric Fiselier } 307998a5c88SEric Fiselier } 308998a5c88SEric Fiselier if (m_ec) { 3099783f28cSLouis Dionne const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied); 310*2b26ee6eSJames Y Knight if (m_ec == errc::permission_denied && allow_eacess) { 311998a5c88SEric Fiselier if (ec) 312998a5c88SEric Fiselier ec->clear(); 313998a5c88SEric Fiselier } else { 31448f8a7c4SCorentin Jabot path at_ent = std::move(curr_it.__entry_.__p_); 315998a5c88SEric Fiselier __imp_.reset(); 3169783f28cSLouis Dionne err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, at_ent.c_str()); 317998a5c88SEric Fiselier } 318998a5c88SEric Fiselier } 319998a5c88SEric Fiselier return false; 320998a5c88SEric Fiselier } 321998a5c88SEric Fiselier 322998a5c88SEric Fiselier _LIBCPP_END_NAMESPACE_FILESYSTEM 323