1 //===------------------ directory_iterator.cpp ----------------------------===//
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 //===----------------------------------------------------------------------===//
11 #if defined(_LIBCPP_WIN32API)
12 #define WIN32_LEAN_AND_MEAN
19 #include "filesystem_common.h"
21 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
26 #if !defined(_LIBCPP_WIN32API)
29 template <class DirEntT
, class = decltype(DirEntT::d_type
)>
30 static file_type
get_file_type(DirEntT
* ent
, int) {
31 switch (ent
->d_type
) {
33 return file_type::block
;
35 return file_type::character
;
37 return file_type::directory
;
39 return file_type::fifo
;
41 return file_type::symlink
;
43 return file_type::regular
;
45 return file_type::socket
;
46 // Unlike in lstat, hitting "unknown" here simply means that the underlying
47 // filesystem doesn't support d_type. Report is as 'none' so we correctly
48 // set the cache to empty.
52 return file_type::none
;
54 #endif // defined(DT_BLK)
56 template <class DirEntT
>
57 static file_type
get_file_type(DirEntT
* ent
, long) {
58 return file_type::none
;
61 static pair
<string_view
, file_type
> posix_readdir(DIR* dir_stream
,
63 struct dirent
* dir_entry_ptr
= nullptr;
64 errno
= 0; // zero errno in order to detect errors
66 if ((dir_entry_ptr
= ::readdir(dir_stream
)) == nullptr) {
71 return {dir_entry_ptr
->d_name
, get_file_type(dir_entry_ptr
, 0)};
76 static file_type
get_file_type(const WIN32_FIND_DATA
& data
) {
77 //auto attrs = data.dwFileAttributes;
79 return file_type::unknown
;
81 static uintmax_t get_file_size(const WIN32_FIND_DATA
& data
) {
82 return (data
.nFileSizeHigh
* (MAXDWORD
+ 1)) + data
.nFileSizeLow
;
84 static file_time_type
get_write_time(const WIN32_FIND_DATA
& data
) {
86 const FILETIME
& time
= data
.ftLastWriteTime
;
87 tmp
.u
.LowPart
= time
.dwLowDateTime
;
88 tmp
.u
.HighPart
= time
.dwHighDateTime
;
89 return file_time_type(file_time_type::duration(tmp
.QuadPart
));
97 using detail::ErrorHandler
;
99 #if defined(_LIBCPP_WIN32API)
102 __dir_stream() = delete;
103 __dir_stream
& operator=(const __dir_stream
&) = delete;
105 __dir_stream(__dir_stream
&& __ds
) noexcept
: __stream_(__ds
.__stream_
),
106 __root_(move(__ds
.__root_
)),
107 __entry_(move(__ds
.__entry_
)) {
108 __ds
.__stream_
= INVALID_HANDLE_VALUE
;
111 __dir_stream(const path
& root
, directory_options opts
, error_code
& ec
)
112 : __stream_(INVALID_HANDLE_VALUE
), __root_(root
) {
113 __stream_
= ::FindFirstFile(root
.c_str(), &__data_
);
114 if (__stream_
== INVALID_HANDLE_VALUE
) {
115 ec
= error_code(::GetLastError(), generic_category());
116 const bool ignore_permission_denied
=
117 bool(opts
& directory_options::skip_permission_denied
);
118 if (ignore_permission_denied
&& ec
.value() == ERROR_ACCESS_DENIED
)
124 ~__dir_stream() noexcept
{
125 if (__stream_
== INVALID_HANDLE_VALUE
)
130 bool good() const noexcept
{ return __stream_
!= INVALID_HANDLE_VALUE
; }
132 bool advance(error_code
& ec
) {
133 while (::FindNextFile(__stream_
, &__data_
)) {
134 if (!strcmp(__data_
.cFileName
, ".") || strcmp(__data_
.cFileName
, ".."))
136 // FIXME: Cache more of this
137 //directory_entry::__cached_data cdata;
138 //cdata.__type_ = get_file_type(__data_);
139 //cdata.__size_ = get_file_size(__data_);
140 //cdata.__write_time_ = get_write_time(__data_);
141 __entry_
.__assign_iter_entry(
142 __root_
/ __data_
.cFileName
,
143 directory_entry::__create_iter_result(detail::get_file_type(__data
)));
146 ec
= error_code(::GetLastError(), generic_category());
152 error_code
close() noexcept
{
154 if (!::FindClose(__stream_
))
155 ec
= error_code(::GetLastError(), generic_category());
156 __stream_
= INVALID_HANDLE_VALUE
;
160 HANDLE __stream_
{INVALID_HANDLE_VALUE
};
161 WIN32_FIND_DATA __data_
;
165 directory_entry __entry_
;
170 __dir_stream() = delete;
171 __dir_stream
& operator=(const __dir_stream
&) = delete;
173 __dir_stream(__dir_stream
&& other
) noexcept
: __stream_(other
.__stream_
),
174 __root_(move(other
.__root_
)),
175 __entry_(move(other
.__entry_
)) {
176 other
.__stream_
= nullptr;
179 __dir_stream(const path
& root
, directory_options opts
, error_code
& ec
)
180 : __stream_(nullptr), __root_(root
) {
181 if ((__stream_
= ::opendir(root
.c_str())) == nullptr) {
182 ec
= detail::capture_errno();
183 const bool allow_eacess
=
184 bool(opts
& directory_options::skip_permission_denied
);
185 if (allow_eacess
&& ec
.value() == EACCES
)
192 ~__dir_stream() noexcept
{
197 bool good() const noexcept
{ return __stream_
!= nullptr; }
199 bool advance(error_code
& ec
) {
201 auto str_type_pair
= detail::posix_readdir(__stream_
, ec
);
202 auto& str
= str_type_pair
.first
;
203 if (str
== "." || str
== "..") {
205 } else if (ec
|| str
.empty()) {
209 __entry_
.__assign_iter_entry(
211 directory_entry::__create_iter_result(str_type_pair
.second
));
218 error_code
close() noexcept
{
220 if (::closedir(__stream_
) == -1)
221 m_ec
= detail::capture_errno();
226 DIR* __stream_
{nullptr};
230 directory_entry __entry_
;
234 // directory_iterator
236 directory_iterator::directory_iterator(const path
& p
, error_code
* ec
,
237 directory_options opts
) {
238 ErrorHandler
<void> err("directory_iterator::directory_iterator(...)", ec
, &p
);
241 __imp_
= make_shared
<__dir_stream
>(p
, opts
, m_ec
);
244 if (!__imp_
->good()) {
251 directory_iterator
& directory_iterator::__increment(error_code
* ec
) {
252 _LIBCPP_ASSERT(__imp_
, "Attempting to increment an invalid iterator");
253 ErrorHandler
<void> err("directory_iterator::operator++()", ec
);
256 if (!__imp_
->advance(m_ec
)) {
257 path root
= move(__imp_
->__root_
);
260 err
.report(m_ec
, "at root \"%s\"", root
);
265 directory_entry
const& directory_iterator::__dereference() const {
266 _LIBCPP_ASSERT(__imp_
, "Attempting to dereference an invalid iterator");
267 return __imp_
->__entry_
;
270 // recursive_directory_iterator
272 struct recursive_directory_iterator::__shared_imp
{
273 stack
<__dir_stream
> __stack_
;
274 directory_options __options_
;
277 recursive_directory_iterator::recursive_directory_iterator(
278 const path
& p
, directory_options opt
, error_code
* ec
)
279 : __imp_(nullptr), __rec_(true) {
280 ErrorHandler
<void> err("recursive_directory_iterator", ec
, &p
);
283 __dir_stream
new_s(p
, opt
, m_ec
);
286 if (m_ec
|| !new_s
.good())
289 __imp_
= make_shared
<__shared_imp
>();
290 __imp_
->__options_
= opt
;
291 __imp_
->__stack_
.push(move(new_s
));
294 void recursive_directory_iterator::__pop(error_code
* ec
) {
295 _LIBCPP_ASSERT(__imp_
, "Popping the end iterator");
298 __imp_
->__stack_
.pop();
299 if (__imp_
->__stack_
.size() == 0)
305 directory_options
recursive_directory_iterator::options() const {
306 return __imp_
->__options_
;
309 int recursive_directory_iterator::depth() const {
310 return __imp_
->__stack_
.size() - 1;
313 const directory_entry
& recursive_directory_iterator::__dereference() const {
314 return __imp_
->__stack_
.top().__entry_
;
317 recursive_directory_iterator
&
318 recursive_directory_iterator::__increment(error_code
* ec
) {
321 if (recursion_pending()) {
322 if (__try_recursion(ec
) || (ec
&& *ec
))
330 void recursive_directory_iterator::__advance(error_code
* ec
) {
331 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
333 const directory_iterator end_it
;
334 auto& stack
= __imp_
->__stack_
;
336 while (stack
.size() > 0) {
337 if (stack
.top().advance(m_ec
))
345 path root
= move(stack
.top().__root_
);
347 err
.report(m_ec
, "at root \"%s\"", root
);
353 bool recursive_directory_iterator::__try_recursion(error_code
* ec
) {
354 ErrorHandler
<void> err("recursive_directory_iterator::operator++()", ec
);
356 bool rec_sym
= bool(options() & directory_options::follow_directory_symlink
);
358 auto& curr_it
= __imp_
->__stack_
.top();
360 bool skip_rec
= false;
363 file_status
st(curr_it
.__entry_
.__get_sym_ft(&m_ec
));
364 if (m_ec
&& status_known(st
))
366 if (m_ec
|| is_symlink(st
) || !is_directory(st
))
369 file_status
st(curr_it
.__entry_
.__get_ft(&m_ec
));
370 if (m_ec
&& status_known(st
))
372 if (m_ec
|| !is_directory(st
))
377 __dir_stream
new_it(curr_it
.__entry_
.path(), __imp_
->__options_
, m_ec
);
379 __imp_
->__stack_
.push(move(new_it
));
384 const bool allow_eacess
=
385 bool(__imp_
->__options_
& directory_options::skip_permission_denied
);
386 if (m_ec
.value() == EACCES
&& allow_eacess
) {
390 path at_ent
= move(curr_it
.__entry_
.__p_
);
392 err
.report(m_ec
, "attempting recursion into \"%s\"", at_ent
);
398 _LIBCPP_END_NAMESPACE_FILESYSTEM