324 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <__assert>10#include <__config>11#include <__memory/shared_ptr.h>12#include <errno.h>13#include <filesystem>14#include <stack>15#include <utility>16 17#include "error.h"18#include "file_descriptor.h"19 20#if defined(_LIBCPP_WIN32API)21# define WIN32_LEAN_AND_MEAN22# define NOMINMAX23# include <windows.h>24#else25# include <dirent.h> // for DIR & friends26#endif27 28_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM29 30using detail::ErrorHandler;31 32#if defined(_LIBCPP_WIN32API)33class __dir_stream {34public:35 __dir_stream() = delete;36 __dir_stream& operator=(const __dir_stream&) = delete;37 38 __dir_stream(__dir_stream&& __ds) noexcept39 : __stream_(__ds.__stream_), __root_(std::move(__ds.__root_)), __entry_(std::move(__ds.__entry_)) {40 __ds.__stream_ = INVALID_HANDLE_VALUE;41 }42 43 __dir_stream(const path& root, directory_options opts, error_code& ec)44 : __stream_(INVALID_HANDLE_VALUE), __root_(root) {45 if (root.native().empty()) {46 ec = make_error_code(errc::no_such_file_or_directory);47 return;48 }49 __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);50 if (__stream_ == INVALID_HANDLE_VALUE) {51 ec = detail::get_last_error();52 const bool ignore_permission_denied = bool(opts & directory_options::skip_permission_denied);53 if (ignore_permission_denied && ec == errc::permission_denied)54 ec.clear();55 return;56 }57 if (!assign())58 advance(ec);59 }60 61 ~__dir_stream() noexcept {62 if (__stream_ == INVALID_HANDLE_VALUE)63 return;64 close();65 }66 67 bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }68 69 bool advance(error_code& ec) {70 while (::FindNextFileW(__stream_, &__data_)) {71 if (assign())72 return true;73 }74 close();75 return false;76 }77 78 bool assign() {79 if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))80 return false;81 __entry_.__assign_iter_entry(82 __root_ / __data_.cFileName,83 directory_entry::__create_iter_cached_result(84 detail::get_file_type(__data_),85 detail::get_file_size(__data_),86 detail::get_file_perm(__data_),87 detail::get_write_time(__data_)));88 return true;89 }90 91private:92 error_code close() noexcept {93 error_code ec;94 if (!::FindClose(__stream_))95 ec = detail::get_last_error();96 __stream_ = INVALID_HANDLE_VALUE;97 return ec;98 }99 100 HANDLE __stream_{INVALID_HANDLE_VALUE};101 WIN32_FIND_DATAW __data_;102 103public:104 path __root_;105 directory_entry __entry_;106};107#else108class __dir_stream {109public:110 __dir_stream() = delete;111 __dir_stream& operator=(const __dir_stream&) = delete;112 113 __dir_stream(__dir_stream&& other) noexcept114 : __stream_(other.__stream_), __root_(std::move(other.__root_)), __entry_(std::move(other.__entry_)) {115 other.__stream_ = nullptr;116 }117 118 __dir_stream(const path& root, directory_options opts, error_code& ec) : __stream_(nullptr), __root_(root) {119 if ((__stream_ = ::opendir(root.c_str())) == nullptr) {120 ec = detail::capture_errno();121 const bool allow_eacces = bool(opts & directory_options::skip_permission_denied);122 if (allow_eacces && ec == errc::permission_denied)123 ec.clear();124 return;125 }126 advance(ec);127 }128 129 ~__dir_stream() noexcept {130 if (__stream_)131 close();132 }133 134 bool good() const noexcept { return __stream_ != nullptr; }135 136 bool advance(error_code& ec) {137 while (true) {138 auto str_type_pair = detail::posix_readdir(__stream_, ec);139 auto& str = str_type_pair.first;140 if (str == "." || str == "..") {141 continue;142 } else if (ec || str.empty()) {143 close();144 return false;145 } else {146 __entry_.__assign_iter_entry(__root_ / str, directory_entry::__create_iter_result(str_type_pair.second));147 return true;148 }149 }150 }151 152private:153 error_code close() noexcept {154 error_code m_ec;155 if (::closedir(__stream_) == -1)156 m_ec = detail::capture_errno();157 __stream_ = nullptr;158 return m_ec;159 }160 161 DIR* __stream_{nullptr};162 163public:164 path __root_;165 directory_entry __entry_;166};167#endif168 169// directory_iterator170 171directory_iterator::directory_iterator(const path& p, error_code* ec, directory_options opts) {172 ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);173 174 error_code m_ec;175 __imp_ = make_shared<__dir_stream>(p, opts, m_ec);176 if (ec)177 *ec = m_ec;178 if (!__imp_->good()) {179 __imp_.reset();180 if (m_ec)181 err.report(m_ec);182 }183}184 185directory_iterator& directory_iterator::__increment(error_code* ec) {186 _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to increment an invalid iterator");187 ErrorHandler<void> err("directory_iterator::operator++()", ec);188 189 error_code m_ec;190 if (!__imp_->advance(m_ec)) {191 path root = std::move(__imp_->__root_);192 __imp_.reset();193 if (m_ec)194 err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());195 }196 return *this;197}198 199directory_entry const& directory_iterator::__dereference() const {200 _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Attempting to dereference an invalid iterator");201 return __imp_->__entry_;202}203 204// recursive_directory_iterator205 206struct recursive_directory_iterator::__shared_imp {207 stack<__dir_stream> __stack_;208 directory_options __options_;209};210 211recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options opt, error_code* ec)212 : __imp_(nullptr), __rec_(true) {213 ErrorHandler<void> err("recursive_directory_iterator", ec, &p);214 215 error_code m_ec;216 __dir_stream new_s(p, opt, m_ec);217 if (m_ec)218 err.report(m_ec);219 if (m_ec || !new_s.good())220 return;221 222 __imp_ = make_shared<__shared_imp>();223 __imp_->__options_ = opt;224 __imp_->__stack_.push(std::move(new_s));225}226 227void recursive_directory_iterator::__pop(error_code* ec) {228 _LIBCPP_ASSERT_NON_NULL(__imp_ != nullptr, "Popping the end iterator");229 if (ec)230 ec->clear();231 __imp_->__stack_.pop();232 if (__imp_->__stack_.size() == 0)233 __imp_.reset();234 else235 __advance(ec);236}237 238directory_options recursive_directory_iterator::options() const { return __imp_->__options_; }239 240int recursive_directory_iterator::depth() const { return __imp_->__stack_.size() - 1; }241 242const directory_entry& recursive_directory_iterator::__dereference() const { return __imp_->__stack_.top().__entry_; }243 244recursive_directory_iterator& recursive_directory_iterator::__increment(error_code* ec) {245 if (ec)246 ec->clear();247 if (recursion_pending()) {248 if (__try_recursion(ec) || (ec && *ec))249 return *this;250 }251 __rec_ = true;252 __advance(ec);253 return *this;254}255 256void recursive_directory_iterator::__advance(error_code* ec) {257 ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);258 259 const directory_iterator end_it;260 auto& stack = __imp_->__stack_;261 error_code m_ec;262 while (stack.size() > 0) {263 if (stack.top().advance(m_ec))264 return;265 if (m_ec)266 break;267 stack.pop();268 }269 270 if (m_ec) {271 path root = std::move(stack.top().__root_);272 __imp_.reset();273 err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());274 } else {275 __imp_.reset();276 }277}278 279bool recursive_directory_iterator::__try_recursion(error_code* ec) {280 ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);281 282 bool rec_sym = bool(options() & directory_options::follow_directory_symlink);283 284 auto& curr_it = __imp_->__stack_.top();285 286 bool skip_rec = false;287 error_code m_ec;288 if (!rec_sym) {289 file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));290 if (m_ec && status_known(st))291 m_ec.clear();292 if (m_ec || is_symlink(st) || !is_directory(st))293 skip_rec = true;294 } else {295 file_status st(curr_it.__entry_.__get_ft(&m_ec));296 if (m_ec && status_known(st))297 m_ec.clear();298 if (m_ec || !is_directory(st))299 skip_rec = true;300 }301 302 if (!skip_rec) {303 __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);304 if (new_it.good()) {305 __imp_->__stack_.push(std::move(new_it));306 return true;307 }308 }309 if (m_ec) {310 const bool allow_eacess = bool(__imp_->__options_ & directory_options::skip_permission_denied);311 if (m_ec == errc::permission_denied && allow_eacess) {312 if (ec)313 ec->clear();314 } else {315 path at_ent = std::move(curr_it.__entry_.__p_);316 __imp_.reset();317 err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT, at_ent.c_str());318 }319 }320 return false;321}322 323_LIBCPP_END_NAMESPACE_FILESYSTEM324