xref: /openbsd-src/gnu/llvm/libcxx/src/filesystem/directory_iterator.cpp (revision 4bdff4bed0e3d54e55670334c7d0077db4170f86)
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