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>
14998a5c88SEric Fiselier
15998a5c88SEric Fiselier #include "filesystem_common.h"
16998a5c88SEric Fiselier
17998a5c88SEric Fiselier _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
18998a5c88SEric Fiselier
19998a5c88SEric Fiselier using detail::ErrorHandler;
20998a5c88SEric Fiselier
21998a5c88SEric Fiselier #if defined(_LIBCPP_WIN32API)
22998a5c88SEric Fiselier class __dir_stream {
23998a5c88SEric Fiselier public:
24998a5c88SEric Fiselier __dir_stream() = delete;
25998a5c88SEric Fiselier __dir_stream& operator=(const __dir_stream&) = delete;
26998a5c88SEric Fiselier
__dir_stream(__dir_stream && __ds)27998a5c88SEric Fiselier __dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
28*48f8a7c4SCorentin Jabot __root_(std::move(__ds.__root_)),
29*48f8a7c4SCorentin Jabot __entry_(std::move(__ds.__entry_)) {
30998a5c88SEric Fiselier __ds.__stream_ = INVALID_HANDLE_VALUE;
31998a5c88SEric Fiselier }
32998a5c88SEric Fiselier
__dir_stream(const path & root,directory_options opts,error_code & ec)33998a5c88SEric Fiselier __dir_stream(const path& root, directory_options opts, error_code& ec)
34998a5c88SEric Fiselier : __stream_(INVALID_HANDLE_VALUE), __root_(root) {
3515618072SMartin Storsjö if (root.native().empty()) {
3615618072SMartin Storsjö ec = make_error_code(errc::no_such_file_or_directory);
3715618072SMartin Storsjö return;
3815618072SMartin Storsjö }
3915618072SMartin Storsjö __stream_ = ::FindFirstFileW((root / "*").c_str(), &__data_);
40998a5c88SEric Fiselier if (__stream_ == INVALID_HANDLE_VALUE) {
4115618072SMartin Storsjö ec = detail::make_windows_error(GetLastError());
42998a5c88SEric Fiselier const bool ignore_permission_denied =
43998a5c88SEric Fiselier bool(opts & directory_options::skip_permission_denied);
44e69c65d5SMartin Storsjö if (ignore_permission_denied &&
45e69c65d5SMartin Storsjö ec.value() == static_cast<int>(errc::permission_denied))
46998a5c88SEric Fiselier ec.clear();
47998a5c88SEric Fiselier return;
48998a5c88SEric Fiselier }
4915618072SMartin Storsjö if (!assign())
5015618072SMartin Storsjö advance(ec);
51998a5c88SEric Fiselier }
52998a5c88SEric Fiselier
~__dir_stream()53998a5c88SEric Fiselier ~__dir_stream() noexcept {
54998a5c88SEric Fiselier if (__stream_ == INVALID_HANDLE_VALUE)
55998a5c88SEric Fiselier return;
56998a5c88SEric Fiselier close();
57998a5c88SEric Fiselier }
58998a5c88SEric Fiselier
good() const59998a5c88SEric Fiselier bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
60998a5c88SEric Fiselier
advance(error_code & ec)61998a5c88SEric Fiselier bool advance(error_code& ec) {
6215618072SMartin Storsjö while (::FindNextFileW(__stream_, &__data_)) {
6315618072SMartin Storsjö if (assign())
6415618072SMartin Storsjö return true;
6515618072SMartin Storsjö }
6615618072SMartin Storsjö close();
6715618072SMartin Storsjö return false;
6815618072SMartin Storsjö }
6915618072SMartin Storsjö
assign()7015618072SMartin Storsjö bool assign() {
7115618072SMartin Storsjö if (!wcscmp(__data_.cFileName, L".") || !wcscmp(__data_.cFileName, L".."))
7215618072SMartin Storsjö return false;
73998a5c88SEric Fiselier // FIXME: Cache more of this
74998a5c88SEric Fiselier //directory_entry::__cached_data cdata;
75998a5c88SEric Fiselier //cdata.__type_ = get_file_type(__data_);
76998a5c88SEric Fiselier //cdata.__size_ = get_file_size(__data_);
77998a5c88SEric Fiselier //cdata.__write_time_ = get_write_time(__data_);
78998a5c88SEric Fiselier __entry_.__assign_iter_entry(
79998a5c88SEric Fiselier __root_ / __data_.cFileName,
8015618072SMartin Storsjö directory_entry::__create_iter_result(detail::get_file_type(__data_)));
81998a5c88SEric Fiselier return true;
82998a5c88SEric Fiselier }
83998a5c88SEric Fiselier
84998a5c88SEric Fiselier private:
close()85998a5c88SEric Fiselier error_code close() noexcept {
86998a5c88SEric Fiselier error_code ec;
87998a5c88SEric Fiselier if (!::FindClose(__stream_))
8815618072SMartin Storsjö ec = detail::make_windows_error(GetLastError());
89998a5c88SEric Fiselier __stream_ = INVALID_HANDLE_VALUE;
90998a5c88SEric Fiselier return ec;
91998a5c88SEric Fiselier }
92998a5c88SEric Fiselier
93998a5c88SEric Fiselier HANDLE __stream_{INVALID_HANDLE_VALUE};
9415618072SMartin Storsjö WIN32_FIND_DATAW __data_;
95998a5c88SEric Fiselier
96998a5c88SEric Fiselier public:
97998a5c88SEric Fiselier path __root_;
98998a5c88SEric Fiselier directory_entry __entry_;
99998a5c88SEric Fiselier };
100998a5c88SEric Fiselier #else
101998a5c88SEric Fiselier class __dir_stream {
102998a5c88SEric Fiselier public:
103998a5c88SEric Fiselier __dir_stream() = delete;
104998a5c88SEric Fiselier __dir_stream& operator=(const __dir_stream&) = delete;
105998a5c88SEric Fiselier
__dir_stream(__dir_stream && other)106998a5c88SEric Fiselier __dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
107*48f8a7c4SCorentin Jabot __root_(std::move(other.__root_)),
108*48f8a7c4SCorentin Jabot __entry_(std::move(other.__entry_)) {
109998a5c88SEric Fiselier other.__stream_ = nullptr;
110998a5c88SEric Fiselier }
111998a5c88SEric Fiselier
__dir_stream(const path & root,directory_options opts,error_code & ec)112998a5c88SEric Fiselier __dir_stream(const path& root, directory_options opts, error_code& ec)
113998a5c88SEric Fiselier : __stream_(nullptr), __root_(root) {
114998a5c88SEric Fiselier if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
115998a5c88SEric Fiselier ec = detail::capture_errno();
1167dc705f8SLouis Dionne const bool allow_eacces =
117998a5c88SEric Fiselier bool(opts & directory_options::skip_permission_denied);
1187dc705f8SLouis Dionne if (allow_eacces && ec.value() == EACCES)
119998a5c88SEric Fiselier ec.clear();
120998a5c88SEric Fiselier return;
121998a5c88SEric Fiselier }
122998a5c88SEric Fiselier advance(ec);
123998a5c88SEric Fiselier }
124998a5c88SEric Fiselier
~__dir_stream()125998a5c88SEric Fiselier ~__dir_stream() noexcept {
126998a5c88SEric Fiselier if (__stream_)
127998a5c88SEric Fiselier close();
128998a5c88SEric Fiselier }
129998a5c88SEric Fiselier
good() const130998a5c88SEric Fiselier bool good() const noexcept { return __stream_ != nullptr; }
131998a5c88SEric Fiselier
advance(error_code & ec)132998a5c88SEric Fiselier bool advance(error_code& ec) {
133998a5c88SEric Fiselier while (true) {
134998a5c88SEric Fiselier auto str_type_pair = detail::posix_readdir(__stream_, ec);
135998a5c88SEric Fiselier auto& str = str_type_pair.first;
136998a5c88SEric Fiselier if (str == "." || str == "..") {
137998a5c88SEric Fiselier continue;
138998a5c88SEric Fiselier } else if (ec || str.empty()) {
139998a5c88SEric Fiselier close();
140998a5c88SEric Fiselier return false;
141998a5c88SEric Fiselier } else {
142998a5c88SEric Fiselier __entry_.__assign_iter_entry(
143998a5c88SEric Fiselier __root_ / str,
144998a5c88SEric Fiselier directory_entry::__create_iter_result(str_type_pair.second));
145998a5c88SEric Fiselier return true;
146998a5c88SEric Fiselier }
147998a5c88SEric Fiselier }
148998a5c88SEric Fiselier }
149998a5c88SEric Fiselier
150998a5c88SEric Fiselier private:
close()151998a5c88SEric Fiselier error_code close() noexcept {
152998a5c88SEric Fiselier error_code m_ec;
153998a5c88SEric Fiselier if (::closedir(__stream_) == -1)
154998a5c88SEric Fiselier m_ec = detail::capture_errno();
155998a5c88SEric Fiselier __stream_ = nullptr;
156998a5c88SEric Fiselier return m_ec;
157998a5c88SEric Fiselier }
158998a5c88SEric Fiselier
159998a5c88SEric Fiselier DIR* __stream_{nullptr};
160998a5c88SEric Fiselier
161998a5c88SEric Fiselier public:
162998a5c88SEric Fiselier path __root_;
163998a5c88SEric Fiselier directory_entry __entry_;
164998a5c88SEric Fiselier };
165998a5c88SEric Fiselier #endif
166998a5c88SEric Fiselier
167998a5c88SEric Fiselier // directory_iterator
168998a5c88SEric Fiselier
directory_iterator(const path & p,error_code * ec,directory_options opts)169998a5c88SEric Fiselier directory_iterator::directory_iterator(const path& p, error_code* ec,
170998a5c88SEric Fiselier 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
__increment(error_code * ec)184998a5c88SEric Fiselier directory_iterator& directory_iterator::__increment(error_code* ec) {
185998a5c88SEric Fiselier _LIBCPP_ASSERT(__imp_, "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)) {
190*48f8a7c4SCorentin 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
__dereference() const198998a5c88SEric Fiselier directory_entry const& directory_iterator::__dereference() const {
199998a5c88SEric Fiselier _LIBCPP_ASSERT(__imp_, "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
recursive_directory_iterator(const path & p,directory_options opt,error_code * ec)210998a5c88SEric Fiselier recursive_directory_iterator::recursive_directory_iterator(
211998a5c88SEric Fiselier const path& p, directory_options opt, error_code* ec)
212998a5c88SEric Fiselier : __imp_(nullptr), __rec_(true) {
213998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
214998a5c88SEric Fiselier
215998a5c88SEric Fiselier error_code m_ec;
216998a5c88SEric Fiselier __dir_stream new_s(p, opt, m_ec);
217998a5c88SEric Fiselier if (m_ec)
218998a5c88SEric Fiselier err.report(m_ec);
219998a5c88SEric Fiselier if (m_ec || !new_s.good())
220998a5c88SEric Fiselier return;
221998a5c88SEric Fiselier
222998a5c88SEric Fiselier __imp_ = make_shared<__shared_imp>();
223998a5c88SEric Fiselier __imp_->__options_ = opt;
224*48f8a7c4SCorentin Jabot __imp_->__stack_.push(std::move(new_s));
225998a5c88SEric Fiselier }
226998a5c88SEric Fiselier
__pop(error_code * ec)227998a5c88SEric Fiselier void recursive_directory_iterator::__pop(error_code* ec) {
228998a5c88SEric Fiselier _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
229998a5c88SEric Fiselier if (ec)
230998a5c88SEric Fiselier ec->clear();
231998a5c88SEric Fiselier __imp_->__stack_.pop();
232998a5c88SEric Fiselier if (__imp_->__stack_.size() == 0)
233998a5c88SEric Fiselier __imp_.reset();
234998a5c88SEric Fiselier else
235998a5c88SEric Fiselier __advance(ec);
236998a5c88SEric Fiselier }
237998a5c88SEric Fiselier
options() const238998a5c88SEric Fiselier directory_options recursive_directory_iterator::options() const {
239998a5c88SEric Fiselier return __imp_->__options_;
240998a5c88SEric Fiselier }
241998a5c88SEric Fiselier
depth() const242998a5c88SEric Fiselier int recursive_directory_iterator::depth() const {
243998a5c88SEric Fiselier return __imp_->__stack_.size() - 1;
244998a5c88SEric Fiselier }
245998a5c88SEric Fiselier
__dereference() const246998a5c88SEric Fiselier const directory_entry& recursive_directory_iterator::__dereference() const {
247998a5c88SEric Fiselier return __imp_->__stack_.top().__entry_;
248998a5c88SEric Fiselier }
249998a5c88SEric Fiselier
250998a5c88SEric Fiselier recursive_directory_iterator&
__increment(error_code * ec)251998a5c88SEric Fiselier recursive_directory_iterator::__increment(error_code* ec) {
252998a5c88SEric Fiselier if (ec)
253998a5c88SEric Fiselier ec->clear();
254998a5c88SEric Fiselier if (recursion_pending()) {
255998a5c88SEric Fiselier if (__try_recursion(ec) || (ec && *ec))
256998a5c88SEric Fiselier return *this;
257998a5c88SEric Fiselier }
258998a5c88SEric Fiselier __rec_ = true;
259998a5c88SEric Fiselier __advance(ec);
260998a5c88SEric Fiselier return *this;
261998a5c88SEric Fiselier }
262998a5c88SEric Fiselier
__advance(error_code * ec)263998a5c88SEric Fiselier void recursive_directory_iterator::__advance(error_code* ec) {
264998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
265998a5c88SEric Fiselier
266998a5c88SEric Fiselier const directory_iterator end_it;
267998a5c88SEric Fiselier auto& stack = __imp_->__stack_;
268998a5c88SEric Fiselier error_code m_ec;
269998a5c88SEric Fiselier while (stack.size() > 0) {
270998a5c88SEric Fiselier if (stack.top().advance(m_ec))
271998a5c88SEric Fiselier return;
272998a5c88SEric Fiselier if (m_ec)
273998a5c88SEric Fiselier break;
274998a5c88SEric Fiselier stack.pop();
275998a5c88SEric Fiselier }
276998a5c88SEric Fiselier
277998a5c88SEric Fiselier if (m_ec) {
278*48f8a7c4SCorentin Jabot path root = std::move(stack.top().__root_);
279998a5c88SEric Fiselier __imp_.reset();
2800aa637b2SArthur O'Dwyer err.report(m_ec, "at root " PATH_CSTR_FMT, root.c_str());
281998a5c88SEric Fiselier } else {
282998a5c88SEric Fiselier __imp_.reset();
283998a5c88SEric Fiselier }
284998a5c88SEric Fiselier }
285998a5c88SEric Fiselier
__try_recursion(error_code * ec)286998a5c88SEric Fiselier bool recursive_directory_iterator::__try_recursion(error_code* ec) {
287998a5c88SEric Fiselier ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
288998a5c88SEric Fiselier
289998a5c88SEric Fiselier bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
290998a5c88SEric Fiselier
291998a5c88SEric Fiselier auto& curr_it = __imp_->__stack_.top();
292998a5c88SEric Fiselier
293998a5c88SEric Fiselier bool skip_rec = false;
294998a5c88SEric Fiselier error_code m_ec;
295998a5c88SEric Fiselier if (!rec_sym) {
296998a5c88SEric Fiselier file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
297998a5c88SEric Fiselier if (m_ec && status_known(st))
298998a5c88SEric Fiselier m_ec.clear();
299998a5c88SEric Fiselier if (m_ec || is_symlink(st) || !is_directory(st))
300998a5c88SEric Fiselier skip_rec = true;
301998a5c88SEric Fiselier } else {
302998a5c88SEric Fiselier file_status st(curr_it.__entry_.__get_ft(&m_ec));
303998a5c88SEric Fiselier if (m_ec && status_known(st))
304998a5c88SEric Fiselier m_ec.clear();
305998a5c88SEric Fiselier if (m_ec || !is_directory(st))
306998a5c88SEric Fiselier skip_rec = true;
307998a5c88SEric Fiselier }
308998a5c88SEric Fiselier
309998a5c88SEric Fiselier if (!skip_rec) {
310998a5c88SEric Fiselier __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
311998a5c88SEric Fiselier if (new_it.good()) {
312*48f8a7c4SCorentin Jabot __imp_->__stack_.push(std::move(new_it));
313998a5c88SEric Fiselier return true;
314998a5c88SEric Fiselier }
315998a5c88SEric Fiselier }
316998a5c88SEric Fiselier if (m_ec) {
317998a5c88SEric Fiselier const bool allow_eacess =
318998a5c88SEric Fiselier bool(__imp_->__options_ & directory_options::skip_permission_denied);
319998a5c88SEric Fiselier if (m_ec.value() == EACCES && allow_eacess) {
320998a5c88SEric Fiselier if (ec)
321998a5c88SEric Fiselier ec->clear();
322998a5c88SEric Fiselier } else {
323*48f8a7c4SCorentin Jabot path at_ent = std::move(curr_it.__entry_.__p_);
324998a5c88SEric Fiselier __imp_.reset();
3250aa637b2SArthur O'Dwyer err.report(m_ec, "attempting recursion into " PATH_CSTR_FMT,
3260aa637b2SArthur O'Dwyer at_ent.c_str());
327998a5c88SEric Fiselier }
328998a5c88SEric Fiselier }
329998a5c88SEric Fiselier return false;
330998a5c88SEric Fiselier }
331998a5c88SEric Fiselier
332998a5c88SEric Fiselier _LIBCPP_END_NAMESPACE_FILESYSTEM
333