xref: /llvm-project/libcxx/src/filesystem/directory_iterator.cpp (revision 2b26ee6e790574e05c3c9a562bc37897daf0f384)
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