1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
17 #include "file_descriptor.h"
19 #if defined(_LIBCPP_WIN32API)
20 # define WIN32_LEAN_AND_MEAN
24 # include <dirent.h> // for DIR & friends
27 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
29 using detail::ErrorHandler
;
31 #if defined(_LIBCPP_WIN32API)
34 __dir_stream() = delete;
35 __dir_stream
& operator=(const __dir_stream
&) = delete;
37 __dir_stream(__dir_stream
&& __ds
) noexcept
: __stream_(__ds
.__stream_
),
38 __root_(std::move(__ds
.__root_
)),
39 __entry_(std::move(__ds
.__entry_
)) {
40 __ds
.__stream_
= INVALID_HANDLE_VALUE
;
43 __dir_stream(const path
& root
, directory_options opts
, error_code
& ec
)
44 : __stream_(INVALID_HANDLE_VALUE
), __root_(root
) {
45 if (root
.native().empty()) {
46 ec
= make_error_code(errc::no_such_file_or_directory
);
49 __stream_
= ::FindFirstFileW((root
/ "*").c_str(), &__data_
);
50 if (__stream_
== INVALID_HANDLE_VALUE
) {
51 ec
= detail::make_windows_error(GetLastError());
52 const bool ignore_permission_denied
=
53 bool(opts
& directory_options::skip_permission_denied
);
54 if (ignore_permission_denied
&&
55 ec
.value() == static_cast<int>(errc::permission_denied
))
63 ~__dir_stream() noexcept
{
64 if (__stream_
== INVALID_HANDLE_VALUE
)
69 bool good() const noexcept
{ return __stream_
!= INVALID_HANDLE_VALUE
; }
71 bool advance(error_code
& ec
) {
72 while (::FindNextFileW(__stream_
, &__data_
)) {
81 if (!wcscmp(__data_
.cFileName
, L
".") || !wcscmp(__data_
.cFileName
, L
".."))
83 // FIXME: Cache more of this
84 //directory_entry::__cached_data cdata;
85 //cdata.__type_ = get_file_type(__data_);
86 //cdata.__size_ = get_file_size(__data_);
87 //cdata.__write_time_ = get_write_time(__data_);
88 __entry_
.__assign_iter_entry(
89 __root_
/ __data_
.cFileName
,
90 directory_entry::__create_iter_result(detail::get_file_type(__data_
)));
95 error_code
close() noexcept
{
97 if (!::FindClose(__stream_
))
98 ec
= detail::make_windows_error(GetLastError());
99 __stream_
= INVALID_HANDLE_VALUE
;
103 HANDLE __stream_
{INVALID_HANDLE_VALUE
};
104 WIN32_FIND_DATAW __data_
;
108 directory_entry __entry_
;
113 __dir_stream() = delete;
114 __dir_stream
& operator=(const __dir_stream
&) = delete;
116 __dir_stream(__dir_stream
&& other
) noexcept
: __stream_(other
.__stream_
),
117 __root_(std::move(other
.__root_
)),
118 __entry_(std::move(other
.__entry_
)) {
119 other
.__stream_
= nullptr;
122 __dir_stream(const path
& root
, directory_options opts
, error_code
& ec
)
123 : __stream_(nullptr), __root_(root
) {
124 if ((__stream_
= ::opendir(root
.c_str())) == nullptr) {
125 ec
= detail::capture_errno();
126 const bool allow_eacces
=
127 bool(opts
& directory_options::skip_permission_denied
);
128 if (allow_eacces
&& ec
.value() == EACCES
)
135 ~__dir_stream() noexcept
{
140 bool good() const noexcept
{ return __stream_
!= nullptr; }
142 bool advance(error_code
& ec
) {
144 auto str_type_pair
= detail::posix_readdir(__stream_
, ec
);
145 auto& str
= str_type_pair
.first
;
146 if (str
== "." || str
== "..") {
148 } else if (ec
|| str
.empty()) {
152 __entry_
.__assign_iter_entry(
154 directory_entry::__create_iter_result(str_type_pair
.second
));
161 error_code
close() noexcept
{
163 if (::closedir(__stream_
) == -1)
164 m_ec
= detail::capture_errno();
169 DIR* __stream_
{nullptr};
173 directory_entry __entry_
;
177 // directory_iterator
179 directory_iterator::directory_iterator(const path
& p
, error_code
* ec
,
180 directory_options opts
) {
181 ErrorHandler
<void> err("directory_iterator::directory_iterator(...)", ec
, &p
);
184 __imp_
= make_shared
<__dir_stream
>(p
, opts
, m_ec
);
187 if (!__imp_
->good()) {
194 directory_iterator
& directory_iterator::__increment(error_code
* ec
) {
195 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_
, "Attempting to increment an invalid iterator");
196 ErrorHandler
<void> err("directory_iterator::operator++()", ec
);
199 if (!__imp_
->advance(m_ec
)) {
200 path root
= std::move(__imp_
->__root_
);
203 err
.report(m_ec
, "at root " PATH_CSTR_FMT
, root
.c_str());
208 directory_entry
const& directory_iterator::__dereference() const {
209 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_
, "Attempting to dereference an invalid iterator");
210 return __imp_
->__entry_
;
213 // recursive_directory_iterator
215 struct recursive_directory_iterator::__shared_imp
{
216 stack
<__dir_stream
> __stack_
;
217 directory_options __options_
;
220 recursive_directory_iterator::recursive_directory_iterator(
221 const path
& p
, directory_options opt
, error_code
* ec
)
222 : __imp_(nullptr), __rec_(true) {
223 ErrorHandler
<void> err("recursive_directory_iterator", ec
, &p
);
226 __dir_stream
new_s(p
, opt
, m_ec
);
229 if (m_ec
|| !new_s
.good())
232 __imp_
= make_shared
<__shared_imp
>();
233 __imp_
->__options_
= opt
;
234 __imp_
->__stack_
.push(std::move(new_s
));
237 void recursive_directory_iterator::__pop(error_code
* ec
) {
238 _LIBCPP_ASSERT_UNCATEGORIZED(__imp_
, "Popping the end iterator");
241 __imp_
->__stack_
.pop();
242 if (__imp_
->__stack_
.size() == 0)
248 directory_options
recursive_directory_iterator::options() const {
249 return __imp_
->__options_
;
252 int recursive_directory_iterator::depth() const {
253 return __imp_
->__stack_
.size() - 1;
256 const directory_entry
& recursive_directory_iterator::__dereference() const {
257 return __imp_
->__stack_
.top().__entry_
;
260 recursive_directory_iterator
&
261 recursive_directory_iterator::__increment(error_code
* ec
) {
264 if (recursion_pending()) {
265 if (__try_recursion(ec
) || (ec
&& *ec
))
273 void recursive_directory_iterator::__advance(error_code
* ec
) {
274 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
276 const directory_iterator end_it
;
277 auto& stack
= __imp_
->__stack_
;
279 while (stack
.size() > 0) {
280 if (stack
.top().advance(m_ec
))
288 path root
= std::move(stack
.top().__root_
);
290 err
.report(m_ec
, "at root " PATH_CSTR_FMT
, root
.c_str());
296 bool recursive_directory_iterator::__try_recursion(error_code
* ec
) {
297 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
299 bool rec_sym
= bool(options() & directory_options::follow_directory_symlink
);
301 auto& curr_it
= __imp_
->__stack_
.top();
303 bool skip_rec
= false;
306 file_status
st(curr_it
.__entry_
.__get_sym_ft(&m_ec
));
307 if (m_ec
&& status_known(st
))
309 if (m_ec
|| is_symlink(st
) || !is_directory(st
))
312 file_status
st(curr_it
.__entry_
.__get_ft(&m_ec
));
313 if (m_ec
&& status_known(st
))
315 if (m_ec
|| !is_directory(st
))
320 __dir_stream
new_it(curr_it
.__entry_
.path(), __imp_
->__options_
, m_ec
);
322 __imp_
->__stack_
.push(std::move(new_it
));
327 const bool allow_eacess
=
328 bool(__imp_
->__options_
& directory_options::skip_permission_denied
);
329 if (m_ec
.value() == EACCES
&& allow_eacess
) {
333 path at_ent
= std::move(curr_it
.__entry_
.__p_
);
335 err
.report(m_ec
, "attempting recursion into " PATH_CSTR_FMT
,
342 _LIBCPP_END_NAMESPACE_FILESYSTEM