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