1*4bdff4beSrobert //===----------------------------------------------------------------------===//
246035553Spatrick //
346035553Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
446035553Spatrick // See https://llvm.org/LICENSE.txt for license information.
546035553Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
646035553Spatrick //
746035553Spatrick //===----------------------------------------------------------------------===//
846035553Spatrick
9*4bdff4beSrobert #include <__assert>
10*4bdff4beSrobert #include <__config>
1146035553Spatrick #include <errno.h>
12*4bdff4beSrobert #include <filesystem>
13*4bdff4beSrobert #include <stack>
1446035553Spatrick
1546035553Spatrick #include "filesystem_common.h"
1646035553Spatrick
1746035553Spatrick _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
1846035553Spatrick
1946035553Spatrick using detail::ErrorHandler;
2046035553Spatrick
2146035553Spatrick #if defined(_LIBCPP_WIN32API)
2246035553Spatrick class __dir_stream {
2346035553Spatrick public:
2446035553Spatrick __dir_stream() = delete;
2546035553Spatrick __dir_stream& operator=(const __dir_stream&) = delete;
2646035553Spatrick
__dir_stream(__dir_stream && __ds)2746035553Spatrick __dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
28*4bdff4beSrobert __root_(std::move(__ds.__root_)),
29*4bdff4beSrobert __entry_(std::move(__ds.__entry_)) {
3046035553Spatrick __ds.__stream_ = INVALID_HANDLE_VALUE;
3146035553Spatrick }
3246035553Spatrick
__dir_stream(const path & root,directory_options opts,error_code & ec)3346035553Spatrick __dir_stream(const path& root, directory_options opts, error_code& ec)
3446035553Spatrick : __stream_(INVALID_HANDLE_VALUE), __root_(root) {
3576d0caaeSpatrick if (root.native().empty()) {
3676d0caaeSpatrick ec = make_error_code(errc::no_such_file_or_directory);
3776d0caaeSpatrick return;
3876d0caaeSpatrick }
3976d0caaeSpatrick __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
4046035553Spatrick if (__stream_ == INVALID_HANDLE_VALUE) {
4176d0caaeSpatrick ec = detail::make_windows_error(GetLastError());
4246035553Spatrick const bool ignore_permission_denied =
4346035553Spatrick bool(opts & directory_options::skip_permission_denied);
4476d0caaeSpatrick if (ignore_permission_denied &&
4576d0caaeSpatrick ec.value() == static_cast<int>(errc::permission_denied))
4646035553Spatrick ec.clear();
4746035553Spatrick return;
4846035553Spatrick }
4976d0caaeSpatrick if (!assign())
5076d0caaeSpatrick advance(ec);
5146035553Spatrick }
5246035553Spatrick
~__dir_stream()5346035553Spatrick ~__dir_stream() noexcept {
5446035553Spatrick if (__stream_ == INVALID_HANDLE_VALUE)
5546035553Spatrick return;
5646035553Spatrick close();
5746035553Spatrick }
5846035553Spatrick
good() const5946035553Spatrick bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
6046035553Spatrick
advance(error_code & ec)6146035553Spatrick bool advance(error_code& ec) {
6276d0caaeSpatrick while (::FindNextFileW(__stream_, &__data_)) {
6376d0caaeSpatrick if (assign())
6476d0caaeSpatrick return true;
6576d0caaeSpatrick }
6676d0caaeSpatrick close();
6776d0caaeSpatrick return false;
6876d0caaeSpatrick }
6976d0caaeSpatrick
assign()7076d0caaeSpatrick bool assign() {
7176d0caaeSpatrick if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
7276d0caaeSpatrick return false;
7346035553Spatrick // FIXME: Cache more of this
7446035553Spatrick //directory_entry::__cached_data cdata;
7546035553Spatrick //cdata.__type_ = get_file_type(__data_);
7646035553Spatrick //cdata.__size_ = get_file_size(__data_);
7746035553Spatrick //cdata.__write_time_ = get_write_time(__data_);
7846035553Spatrick __entry_.__assign_iter_entry(
7946035553Spatrick __root_ / __data_.cFileName,
8076d0caaeSpatrick directory_entry::__create_iter_result(detail::get_file_type(__data_)));
8146035553Spatrick return true;
8246035553Spatrick }
8346035553Spatrick
8446035553Spatrick private:
close()8546035553Spatrick error_code close() noexcept {
8646035553Spatrick error_code ec;
8746035553Spatrick if (!::FindClose(__stream_))
8876d0caaeSpatrick ec = detail::make_windows_error(GetLastError());
8946035553Spatrick __stream_ = INVALID_HANDLE_VALUE;
9046035553Spatrick return ec;
9146035553Spatrick }
9246035553Spatrick
9346035553Spatrick HANDLE __stream_{INVALID_HANDLE_VALUE};
9476d0caaeSpatrick WIN32_FIND_DATAW __data_;
9546035553Spatrick
9646035553Spatrick public:
9746035553Spatrick path __root_;
9846035553Spatrick directory_entry __entry_;
9946035553Spatrick };
10046035553Spatrick #else
10146035553Spatrick class __dir_stream {
10246035553Spatrick public:
10346035553Spatrick __dir_stream() = delete;
10446035553Spatrick __dir_stream& operator=(const __dir_stream&) = delete;
10546035553Spatrick
__dir_stream(__dir_stream && other)10646035553Spatrick __dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
107*4bdff4beSrobert __root_(std::move(other.__root_)),
108*4bdff4beSrobert __entry_(std::move(other.__entry_)) {
10946035553Spatrick other.__stream_ = nullptr;
11046035553Spatrick }
11146035553Spatrick
__dir_stream(const path & root,directory_options opts,error_code & ec)11246035553Spatrick __dir_stream(const path& root, directory_options opts, error_code& ec)
11346035553Spatrick : __stream_(nullptr), __root_(root) {
11446035553Spatrick if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
11546035553Spatrick ec = detail::capture_errno();
116*4bdff4beSrobert const bool allow_eacces =
11746035553Spatrick bool(opts & directory_options::skip_permission_denied);
118*4bdff4beSrobert if (allow_eacces && ec.value() == EACCES)
11946035553Spatrick ec.clear();
12046035553Spatrick return;
12146035553Spatrick }
12246035553Spatrick advance(ec);
12346035553Spatrick }
12446035553Spatrick
~__dir_stream()12546035553Spatrick ~__dir_stream() noexcept {
12646035553Spatrick if (__stream_)
12746035553Spatrick close();
12846035553Spatrick }
12946035553Spatrick
good() const13046035553Spatrick bool good() const noexcept { return __stream_ != nullptr; }
13146035553Spatrick
advance(error_code & ec)13246035553Spatrick bool advance(error_code& ec) {
13346035553Spatrick while (true) {
13446035553Spatrick auto str_type_pair = detail::posix_readdir(__stream_, ec);
13546035553Spatrick auto& str = str_type_pair.first;
13646035553Spatrick if (str == "." || str == "..") {
13746035553Spatrick continue;
13846035553Spatrick } else if (ec || str.empty()) {
13946035553Spatrick close();
14046035553Spatrick return false;
14146035553Spatrick } else {
14246035553Spatrick __entry_.__assign_iter_entry(
14346035553Spatrick __root_ / str,
14446035553Spatrick directory_entry::__create_iter_result(str_type_pair.second));
14546035553Spatrick return true;
14646035553Spatrick }
14746035553Spatrick }
14846035553Spatrick }
14946035553Spatrick
15046035553Spatrick private:
close()15146035553Spatrick error_code close() noexcept {
15246035553Spatrick error_code m_ec;
15346035553Spatrick if (::closedir(__stream_) == -1)
15446035553Spatrick m_ec = detail::capture_errno();
15546035553Spatrick __stream_ = nullptr;
15646035553Spatrick return m_ec;
15746035553Spatrick }
15846035553Spatrick
15946035553Spatrick DIR* __stream_{nullptr};
16046035553Spatrick
16146035553Spatrick public:
16246035553Spatrick path __root_;
16346035553Spatrick directory_entry __entry_;
16446035553Spatrick };
16546035553Spatrick #endif
16646035553Spatrick
16746035553Spatrick // directory_iterator
16846035553Spatrick
directory_iterator(const path & p,error_code * ec,directory_options opts)16946035553Spatrick directory_iterator::directory_iterator(const path& p, error_code* ec,
17046035553Spatrick directory_options opts) {
17146035553Spatrick ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
17246035553Spatrick
17346035553Spatrick error_code m_ec;
17446035553Spatrick __imp_ = make_shared<__dir_stream>(p, opts, m_ec);
17546035553Spatrick if (ec)
17646035553Spatrick *ec = m_ec;
17746035553Spatrick if (!__imp_->good()) {
17846035553Spatrick __imp_.reset();
17946035553Spatrick if (m_ec)
18046035553Spatrick err.report(m_ec);
18146035553Spatrick }
18246035553Spatrick }
18346035553Spatrick
__increment(error_code * ec)18446035553Spatrick directory_iterator& directory_iterator::__increment(error_code* ec) {
18546035553Spatrick _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
18646035553Spatrick ErrorHandler<void> err("directory_iterator::operator++()", ec);
18746035553Spatrick
18846035553Spatrick error_code m_ec;
18946035553Spatrick if (!__imp_->advance(m_ec)) {
190*4bdff4beSrobert path root = std::move(__imp_->__root_);
19146035553Spatrick __imp_.reset();
19246035553Spatrick if (m_ec)
19376d0caaeSpatrick err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
19446035553Spatrick }
19546035553Spatrick return *this;
19646035553Spatrick }
19746035553Spatrick
__dereference() const19846035553Spatrick directory_entry const& directory_iterator::__dereference() const {
19946035553Spatrick _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
20046035553Spatrick return __imp_->__entry_;
20146035553Spatrick }
20246035553Spatrick
20346035553Spatrick // recursive_directory_iterator
20446035553Spatrick
20546035553Spatrick struct recursive_directory_iterator::__shared_imp {
20646035553Spatrick stack<__dir_stream> __stack_;
20746035553Spatrick directory_options __options_;
20846035553Spatrick };
20946035553Spatrick
recursive_directory_iterator(const path & p,directory_options opt,error_code * ec)21046035553Spatrick recursive_directory_iterator::recursive_directory_iterator(
21146035553Spatrick const path& p, directory_options opt, error_code* ec)
21246035553Spatrick : __imp_(nullptr), __rec_(true) {
21346035553Spatrick ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
21446035553Spatrick
21546035553Spatrick error_code m_ec;
21646035553Spatrick __dir_stream new_s(p, opt, m_ec);
21746035553Spatrick if (m_ec)
21846035553Spatrick err.report(m_ec);
21946035553Spatrick if (m_ec || !new_s.good())
22046035553Spatrick return;
22146035553Spatrick
22246035553Spatrick __imp_ = make_shared<__shared_imp>();
22346035553Spatrick __imp_->__options_ = opt;
224*4bdff4beSrobert __imp_->__stack_.push(std::move(new_s));
22546035553Spatrick }
22646035553Spatrick
__pop(error_code * ec)22746035553Spatrick void recursive_directory_iterator::__pop(error_code* ec) {
22846035553Spatrick _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
22946035553Spatrick if (ec)
23046035553Spatrick ec->clear();
23146035553Spatrick __imp_->__stack_.pop();
23246035553Spatrick if (__imp_->__stack_.size() == 0)
23346035553Spatrick __imp_.reset();
23446035553Spatrick else
23546035553Spatrick __advance(ec);
23646035553Spatrick }
23746035553Spatrick
options() const23846035553Spatrick directory_options recursive_directory_iterator::options() const {
23946035553Spatrick return __imp_->__options_;
24046035553Spatrick }
24146035553Spatrick
depth() const24246035553Spatrick int recursive_directory_iterator::depth() const {
24346035553Spatrick return __imp_->__stack_.size() - 1;
24446035553Spatrick }
24546035553Spatrick
__dereference() const24646035553Spatrick const directory_entry& recursive_directory_iterator::__dereference() const {
24746035553Spatrick return __imp_->__stack_.top().__entry_;
24846035553Spatrick }
24946035553Spatrick
25046035553Spatrick recursive_directory_iterator&
__increment(error_code * ec)25146035553Spatrick recursive_directory_iterator::__increment(error_code* ec) {
25246035553Spatrick if (ec)
25346035553Spatrick ec->clear();
25446035553Spatrick if (recursion_pending()) {
25546035553Spatrick if (__try_recursion(ec) || (ec && *ec))
25646035553Spatrick return *this;
25746035553Spatrick }
25846035553Spatrick __rec_ = true;
25946035553Spatrick __advance(ec);
26046035553Spatrick return *this;
26146035553Spatrick }
26246035553Spatrick
__advance(error_code * ec)26346035553Spatrick void recursive_directory_iterator::__advance(error_code* ec) {
26446035553Spatrick ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
26546035553Spatrick
26646035553Spatrick const directory_iterator end_it;
26746035553Spatrick auto& stack = __imp_->__stack_;
26846035553Spatrick error_code m_ec;
26946035553Spatrick while (stack.size() > 0) {
27046035553Spatrick if (stack.top().advance(m_ec))
27146035553Spatrick return;
27246035553Spatrick if (m_ec)
27346035553Spatrick break;
27446035553Spatrick stack.pop();
27546035553Spatrick }
27646035553Spatrick
27746035553Spatrick if (m_ec) {
278*4bdff4beSrobert path root = std::move(stack.top().__root_);
27946035553Spatrick __imp_.reset();
28076d0caaeSpatrick err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
28146035553Spatrick } else {
28246035553Spatrick __imp_.reset();
28346035553Spatrick }
28446035553Spatrick }
28546035553Spatrick
__try_recursion(error_code * ec)28646035553Spatrick bool recursive_directory_iterator::__try_recursion(error_code* ec) {
28746035553Spatrick ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
28846035553Spatrick
28946035553Spatrick bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
29046035553Spatrick
29146035553Spatrick auto& curr_it = __imp_->__stack_.top();
29246035553Spatrick
29346035553Spatrick bool skip_rec = false;
29446035553Spatrick error_code m_ec;
29546035553Spatrick if (!rec_sym) {
29646035553Spatrick file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
29746035553Spatrick if (m_ec && status_known(st))
29846035553Spatrick m_ec.clear();
29946035553Spatrick if (m_ec || is_symlink(st) || !is_directory(st))
30046035553Spatrick skip_rec = true;
30146035553Spatrick } else {
30246035553Spatrick file_status st(curr_it.__entry_.__get_ft(&m_ec));
30346035553Spatrick if (m_ec && status_known(st))
30446035553Spatrick m_ec.clear();
30546035553Spatrick if (m_ec || !is_directory(st))
30646035553Spatrick skip_rec = true;
30746035553Spatrick }
30846035553Spatrick
30946035553Spatrick if (!skip_rec) {
31046035553Spatrick __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
31146035553Spatrick if (new_it.good()) {
312*4bdff4beSrobert __imp_->__stack_.push(std::move(new_it));
31346035553Spatrick return true;
31446035553Spatrick }
31546035553Spatrick }
31646035553Spatrick if (m_ec) {
31746035553Spatrick const bool allow_eacess =
31846035553Spatrick bool(__imp_->__options_ & directory_options::skip_permission_denied);
31946035553Spatrick if (m_ec.value() == EACCES && allow_eacess) {
32046035553Spatrick if (ec)
32146035553Spatrick ec->clear();
32246035553Spatrick } else {
323*4bdff4beSrobert path at_ent = std::move(curr_it.__entry_.__p_);
32446035553Spatrick __imp_.reset();
32576d0caaeSpatrick err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT,
32676d0caaeSpatrick at_ent.c_str());
32746035553Spatrick }
32846035553Spatrick }
32946035553Spatrick return false;
33046035553Spatrick }
33146035553Spatrick
33246035553Spatrick _LIBCPP_END_NAMESPACE_FILESYSTEM
333