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
38 : __stream_(__ds
.__stream_
), __root_(std::move(__ds
.__root_
)), __entry_(std::move(__ds
.__entry_
)) {
39 __ds
.__stream_
= INVALID_HANDLE_VALUE
;
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
);
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
))
60 ~__dir_stream() noexcept
{
61 if (__stream_
== INVALID_HANDLE_VALUE
)
66 bool good() const noexcept
{ return __stream_
!= INVALID_HANDLE_VALUE
; }
68 bool advance(error_code
& ec
) {
69 while (::FindNextFileW(__stream_
, &__data_
)) {
78 if (!wcscmp(__data_
.cFileName
, L
".") || !wcscmp(__data_
.cFileName
, L
".."))
80 __entry_
.__assign_iter_entry(
81 __root_
/ __data_
.cFileName
,
82 directory_entry::__create_iter_cached_result(
83 detail::get_file_type(__data_
),
84 detail::get_file_size(__data_
),
85 detail::get_file_perm(__data_
),
86 detail::get_write_time(__data_
)));
91 error_code
close() noexcept
{
93 if (!::FindClose(__stream_
))
94 ec
= detail::make_windows_error(GetLastError());
95 __stream_
= INVALID_HANDLE_VALUE
;
99 HANDLE __stream_
{INVALID_HANDLE_VALUE
};
100 WIN32_FIND_DATAW __data_
;
104 directory_entry __entry_
;
109 __dir_stream() = delete;
110 __dir_stream
& operator=(const __dir_stream
&) = delete;
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;
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
)
128 ~__dir_stream() noexcept
{
133 bool good() const noexcept
{ return __stream_
!= nullptr; }
135 bool advance(error_code
& ec
) {
137 auto str_type_pair
= detail::posix_readdir(__stream_
, ec
);
138 auto& str
= str_type_pair
.first
;
139 if (str
== "." || str
== "..") {
141 } else if (ec
|| str
.empty()) {
145 __entry_
.__assign_iter_entry(__root_
/ str
, directory_entry::__create_iter_result(str_type_pair
.second
));
152 error_code
close() noexcept
{
154 if (::closedir(__stream_
) == -1)
155 m_ec
= detail::capture_errno();
160 DIR* __stream_
{nullptr};
164 directory_entry __entry_
;
168 // directory_iterator
170 directory_iterator::directory_iterator(const path
& p
, error_code
* ec
, directory_options opts
) {
171 ErrorHandler
<void> err("directory_iterator::directory_iterator(...)", ec
, &p
);
174 __imp_
= make_shared
<__dir_stream
>(p
, opts
, m_ec
);
177 if (!__imp_
->good()) {
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
);
189 if (!__imp_
->advance(m_ec
)) {
190 path root
= std::move(__imp_
->__root_
);
193 err
.report(m_ec
, "at root " PATH_CSTR_FMT
, root
.c_str());
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_
;
203 // recursive_directory_iterator
205 struct recursive_directory_iterator::__shared_imp
{
206 stack
<__dir_stream
> __stack_
;
207 directory_options __options_
;
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
);
215 __dir_stream
new_s(p
, opt
, m_ec
);
218 if (m_ec
|| !new_s
.good())
221 __imp_
= make_shared
<__shared_imp
>();
222 __imp_
->__options_
= opt
;
223 __imp_
->__stack_
.push(std::move(new_s
));
226 void recursive_directory_iterator::__pop(error_code
* ec
) {
227 _LIBCPP_ASSERT_NON_NULL(__imp_
!= nullptr, "Popping the end iterator");
230 __imp_
->__stack_
.pop();
231 if (__imp_
->__stack_
.size() == 0)
237 directory_options
recursive_directory_iterator::options() const { return __imp_
->__options_
; }
239 int recursive_directory_iterator::depth() const { return __imp_
->__stack_
.size() - 1; }
241 const directory_entry
& recursive_directory_iterator::__dereference() const { return __imp_
->__stack_
.top().__entry_
; }
243 recursive_directory_iterator
& recursive_directory_iterator::__increment(error_code
* ec
) {
246 if (recursion_pending()) {
247 if (__try_recursion(ec
) || (ec
&& *ec
))
255 void recursive_directory_iterator::__advance(error_code
* ec
) {
256 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
258 const directory_iterator end_it
;
259 auto& stack
= __imp_
->__stack_
;
261 while (stack
.size() > 0) {
262 if (stack
.top().advance(m_ec
))
270 path root
= std::move(stack
.top().__root_
);
272 err
.report(m_ec
, "at root " PATH_CSTR_FMT
, root
.c_str());
278 bool recursive_directory_iterator::__try_recursion(error_code
* ec
) {
279 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
281 bool rec_sym
= bool(options() & directory_options::follow_directory_symlink
);
283 auto& curr_it
= __imp_
->__stack_
.top();
285 bool skip_rec
= false;
288 file_status
st(curr_it
.__entry_
.__get_sym_ft(&m_ec
));
289 if (m_ec
&& status_known(st
))
291 if (m_ec
|| is_symlink(st
) || !is_directory(st
))
294 file_status
st(curr_it
.__entry_
.__get_ft(&m_ec
));
295 if (m_ec
&& status_known(st
))
297 if (m_ec
|| !is_directory(st
))
302 __dir_stream
new_it(curr_it
.__entry_
.path(), __imp_
->__options_
, m_ec
);
304 __imp_
->__stack_
.push(std::move(new_it
));
309 const bool allow_eacess
= bool(__imp_
->__options_
& directory_options::skip_permission_denied
);
310 if (m_ec
.value() == EACCES
&& allow_eacess
) {
314 path at_ent
= std::move(curr_it
.__entry_
.__p_
);
316 err
.report(m_ec
, "attempting recursion into " PATH_CSTR_FMT
, at_ent
.c_str());
322 _LIBCPP_END_NAMESPACE_FILESYSTEM