Path: blob/main/contrib/llvm-project/libcxx/src/filesystem/directory_iterator.cpp
35231 views
//===----------------------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <__assert>9#include <__config>10#include <errno.h>11#include <filesystem>12#include <stack>13#include <utility>1415#include "error.h"16#include "file_descriptor.h"1718#if defined(_LIBCPP_WIN32API)19# define WIN32_LEAN_AND_MEAN20# define NOMINMAX21# include <windows.h>22#else23# include <dirent.h> // for DIR & friends24#endif2526_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM2728using detail::ErrorHandler;2930#if defined(_LIBCPP_WIN32API)31class __dir_stream {32public:33__dir_stream() = delete;34__dir_stream& operator=(const __dir_stream&) = delete;3536__dir_stream(__dir_stream&& __ds) noexcept37: __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), __entry_(std::move(__ds.__entry_)) {38__ds.__stream_ = INVALID_HANDLE_VALUE;39}4041__dir_stream(const path& root, directory_options opts, error_code& ec)42: __stream_(INVALID_HANDLE_VALUE), __root_(root) {43if (root.native().empty()) {44ec = make_error_code(errc::no_such_file_or_directory);45return;46}47__stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);48if (__stream_ == INVALID_HANDLE_VALUE) {49ec = detail::make_windows_error(GetLastError());50const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied);51if (ignore_permission_denied && ec.value() == static_cast<int>(errc::permission_denied))52ec.clear();53return;54}55if (!assign())56advance(ec);57}5859~__dir_stream() noexcept {60if (__stream_ == INVALID_HANDLE_VALUE)61return;62close();63}6465bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }6667bool advance(error_code& ec) {68while (::FindNextFileW(__stream_, &__data_)) {69if (assign())70return true;71}72close();73return false;74}7576bool assign() {77if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))78return false;79// FIXME: Cache more of this80// directory_entry::__cached_data cdata;81// cdata.__type_ = get_file_type(__data_);82// cdata.__size_ = get_file_size(__data_);83// cdata.__write_time_ = get_write_time(__data_);84__entry_.__assign_iter_entry(85__root_ / __data_.cFileName, directory_entry::__create_iter_result(detail::get_file_type(__data_)));86return true;87}8889private:90error_code close() noexcept {91error_code ec;92if (!::FindClose(__stream_))93ec = detail::make_windows_error(GetLastError());94__stream_ = INVALID_HANDLE_VALUE;95return ec;96}9798HANDLE __stream_{INVALID_HANDLE_VALUE};99WIN32_FIND_DATAW __data_;100101public:102path __root_;103directory_entry __entry_;104};105#else106class __dir_stream {107public:108__dir_stream() = delete;109__dir_stream& operator=(const __dir_stream&) = delete;110111__dir_stream(__dir_stream&& other) noexcept112: __stream_(other.__stream_), __root_(std::move(other.__root_)), __entry_(std::move(other.__entry_)) {113other.__stream_ = nullptr;114}115116__dir_stream(const path& root, directory_options opts, error_code& ec) : __stream_(nullptr), __root_(root) {117if ((__stream_ = ::opendir(root.c_str())) == nullptr) {118ec = detail::capture_errno();119const bool allow_eacces = bool(opts & directory_options::skip_permission_denied);120if (allow_eacces && ec.value() == EACCES)121ec.clear();122return;123}124advance(ec);125}126127~__dir_stream() noexcept {128if (__stream_)129close();130}131132bool good() const noexcept { return __stream_ != nullptr; }133134bool advance(error_code& ec) {135while (true) {136auto str_type_pair = detail::posix_readdir(__stream_, ec);137auto& str = str_type_pair.first;138if (str == "." || str == "..") {139continue;140} else if (ec || str.empty()) {141close();142return false;143} else {144__entry_.__assign_iter_entry(__root_ / str, directory_entry::__create_iter_result(str_type_pair.second));145return true;146}147}148}149150private:151error_code close() noexcept {152error_code m_ec;153if (::closedir(__stream_) == -1)154m_ec = detail::capture_errno();155__stream_ = nullptr;156return m_ec;157}158159DIR* __stream_{nullptr};160161public:162path __root_;163directory_entry __entry_;164};165#endif166167// directory_iterator168169directory_iterator::directory_iterator(const path& p, error_code* ec, directory_options opts) {170ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);171172error_code m_ec;173__imp_ = make_shared<__dir_stream>(p, opts, m_ec);174if (ec)175*ec = m_ec;176if (!__imp_->good()) {177__imp_.reset();178if (m_ec)179err.report(m_ec);180}181}182183directory_iterator& directory_iterator::__increment(error_code* ec) {184_LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to increment an invalid iterator");185ErrorHandler<void> err("directory_iterator::operator++()", ec);186187error_code m_ec;188if (!__imp_->advance(m_ec)) {189path root = std::move(__imp_->__root_);190__imp_.reset();191if (m_ec)192err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());193}194return *this;195}196197directory_entry const& directory_iterator::__dereference() const {198_LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to dereference an invalid iterator");199return __imp_->__entry_;200}201202// recursive_directory_iterator203204struct recursive_directory_iterator::__shared_imp {205stack<__dir_stream> __stack_;206directory_options __options_;207};208209recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code* ec)210: __imp_(nullptr), __rec_(true) {211ErrorHandler<void> err("recursive_directory_iterator", ec, &p);212213error_code m_ec;214__dir_stream new_s(p, opt, m_ec);215if (m_ec)216err.report(m_ec);217if (m_ec || !new_s.good())218return;219220__imp_ = make_shared<__shared_imp>();221__imp_->__options_ = opt;222__imp_->__stack_.push(std::move(new_s));223}224225void recursive_directory_iterator::__pop(error_code* ec) {226_LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Popping the end iterator");227if (ec)228ec->clear();229__imp_->__stack_.pop();230if (__imp_->__stack_.size() == 0)231__imp_.reset();232else233__advance(ec);234}235236directory_options recursive_directory_iterator::options() const { return __imp_->__options_; }237238int recursive_directory_iterator::depth() const { return __imp_->__stack_.size() - 1; }239240const directory_entry& recursive_directory_iterator::__dereference() const { return __imp_->__stack_.top().__entry_; }241242recursive_directory_iterator& recursive_directory_iterator::__increment(error_code* ec) {243if (ec)244ec->clear();245if (recursion_pending()) {246if (__try_recursion(ec) || (ec && *ec))247return *this;248}249__rec_ = true;250__advance(ec);251return *this;252}253254void recursive_directory_iterator::__advance(error_code* ec) {255ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);256257const directory_iterator end_it;258auto& stack = __imp_->__stack_;259error_code m_ec;260while (stack.size() > 0) {261if (stack.top().advance(m_ec))262return;263if (m_ec)264break;265stack.pop();266}267268if (m_ec) {269path root = std::move(stack.top().__root_);270__imp_.reset();271err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());272} else {273__imp_.reset();274}275}276277bool recursive_directory_iterator::__try_recursion(error_code* ec) {278ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);279280bool rec_sym = bool(options() & directory_options::follow_directory_symlink);281282auto& curr_it = __imp_->__stack_.top();283284bool skip_rec = false;285error_code m_ec;286if (!rec_sym) {287file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));288if (m_ec && status_known(st))289m_ec.clear();290if (m_ec || is_symlink(st) || !is_directory(st))291skip_rec = true;292} else {293file_status st(curr_it.__entry_.__get_ft(&m_ec));294if (m_ec && status_known(st))295m_ec.clear();296if (m_ec || !is_directory(st))297skip_rec = true;298}299300if (!skip_rec) {301__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);302if (new_it.good()) {303__imp_->__stack_.push(std::move(new_it));304return true;305}306}307if (m_ec) {308const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied);309if (m_ec.value() == EACCES && allow_eacess) {310if (ec)311ec->clear();312} else {313path at_ent = std::move(curr_it.__entry_.__p_);314__imp_.reset();315err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, at_ent.c_str());316}317}318return false;319}320321_LIBCPP_END_NAMESPACE_FILESYSTEM322323324