]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/filesystem/src/directory.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / filesystem / src / directory.cpp
1 // directory.cpp --------------------------------------------------------------------//
2
3 // Copyright 2002-2009, 2014 Beman Dawes
4 // Copyright 2001 Dietmar Kuehl
5 // Copyright 2019, 2022 Andrey Semashev
6
7 // Distributed under the Boost Software License, Version 1.0.
8 // See http://www.boost.org/LICENSE_1_0.txt
9
10 // See library home page at http://www.boost.org/libs/filesystem
11
12 //--------------------------------------------------------------------------------------//
13
14 #include "platform_config.hpp"
15
16 #include <boost/throw_exception.hpp>
17 #include <boost/filesystem/config.hpp>
18 #include <boost/filesystem/directory.hpp>
19 #include <boost/filesystem/exception.hpp>
20 #include <boost/filesystem/operations.hpp>
21 #include <boost/filesystem/file_status.hpp>
22
23 #include <cstddef>
24 #include <cerrno>
25 #include <cstring>
26 #include <cstdlib> // std::malloc, std::free
27 #include <new> // std::nothrow, std::bad_alloc
28 #include <limits>
29 #include <string>
30 #include <utility> // std::move
31 #include <boost/assert.hpp>
32 #include <boost/system/error_code.hpp>
33 #include <boost/smart_ptr/intrusive_ptr.hpp>
34
35 #ifdef BOOST_POSIX_API
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <dirent.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42
43 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS >= 0) && defined(_SC_THREAD_SAFE_FUNCTIONS) && \
44 !defined(__CYGWIN__) && \
45 !(defined(linux) || defined(__linux) || defined(__linux__)) && \
46 !defined(__ANDROID__) && \
47 (!defined(__hpux) || defined(_REENTRANT)) && \
48 (!defined(_AIX) || defined(__THREAD_SAFE)) && \
49 !defined(__wasm)
50 #define BOOST_FILESYSTEM_USE_READDIR_R
51 #endif
52
53 // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
54 #ifndef O_CLOEXEC
55 #define O_CLOEXEC 0
56 #define BOOST_FILESYSTEM_NO_O_CLOEXEC
57 #endif
58
59 #include "posix_tools.hpp"
60
61 #else // BOOST_WINDOWS_API
62
63 #include <cwchar>
64 #include <windows.h>
65 #include <boost/winapi/basic_types.hpp> // NTSTATUS_
66
67 #include "windows_tools.hpp"
68
69 #endif // BOOST_WINDOWS_API
70
71 #include "atomic_tools.hpp"
72 #include "error_handling.hpp"
73 #include "private_config.hpp"
74
75 // BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in
76 // dir_itr_increment. The config tests are placed here because some of the
77 // macros being tested come from dirent.h.
78 //
79 // TODO: find out what macros indicate dirent::d_type present in more libraries
80 #if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present
81 #define BOOST_FILESYSTEM_STATUS_CACHE
82 #endif
83
84 namespace fs = boost::filesystem;
85 using boost::system::error_code;
86 using boost::system::system_category;
87
88 namespace boost {
89 namespace filesystem {
90
91 //--------------------------------------------------------------------------------------//
92 // //
93 // directory_entry //
94 // //
95 //--------------------------------------------------------------------------------------//
96
97 BOOST_FILESYSTEM_DECL
98 file_status directory_entry::get_status(system::error_code* ec) const
99 {
100 if (!status_known(m_status))
101 {
102 // optimization: if the symlink status is known, and it isn't a symlink,
103 // then status and symlink_status are identical so just copy the
104 // symlink status to the regular status.
105 if (status_known(m_symlink_status) && !is_symlink(m_symlink_status))
106 {
107 m_status = m_symlink_status;
108 if (ec)
109 ec->clear();
110 }
111 else
112 {
113 m_status = detail::status(m_path, ec);
114 }
115 }
116 else if (ec)
117 {
118 ec->clear();
119 }
120
121 return m_status;
122 }
123
124 BOOST_FILESYSTEM_DECL
125 file_status directory_entry::get_symlink_status(system::error_code* ec) const
126 {
127 if (!status_known(m_symlink_status))
128 m_symlink_status = detail::symlink_status(m_path, ec);
129 else if (ec)
130 ec->clear();
131
132 return m_symlink_status;
133 }
134
135 // dispatch directory_entry supplied here rather than in
136 // <boost/filesystem/path_traits.hpp>, thus avoiding header circularity.
137 // test cases are in operations_unit_test.cpp
138
139 namespace path_traits {
140
141 BOOST_FILESYSTEM_DECL
142 void dispatch(directory_entry const& de,
143 #ifdef BOOST_WINDOWS_API
144 std::wstring& to,
145 #else
146 std::string& to,
147 #endif
148 codecvt_type const&)
149 {
150 to = de.path().native();
151 }
152
153 BOOST_FILESYSTEM_DECL
154 void dispatch(directory_entry const& de,
155 #ifdef BOOST_WINDOWS_API
156 std::wstring& to
157 #else
158 std::string& to
159 #endif
160 )
161 {
162 to = de.path().native();
163 }
164
165 } // namespace path_traits
166
167 //--------------------------------------------------------------------------------------//
168 // //
169 // directory_iterator //
170 // //
171 //--------------------------------------------------------------------------------------//
172
173 namespace detail {
174
175 BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u;
176
177 BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t extra_size) BOOST_NOEXCEPT
178 {
179 if (extra_size > 0)
180 class_size = (class_size + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
181 std::size_t total_size = class_size + extra_size;
182
183 // Return NULL on OOM
184 void* p = std::malloc(total_size);
185 if (BOOST_LIKELY(p != NULL))
186 std::memset(p, 0, total_size);
187 return p;
188 }
189
190 BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT
191 {
192 std::free(p);
193 }
194
195 BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) BOOST_NOEXCEPT
196 {
197 std::free(p);
198 }
199
200 namespace {
201
202 inline void* get_dir_itr_imp_extra_data(dir_itr_imp* imp) BOOST_NOEXCEPT
203 {
204 BOOST_CONSTEXPR_OR_CONST std::size_t extra_data_offset = (sizeof(dir_itr_imp) + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
205 return reinterpret_cast< unsigned char* >(imp) + extra_data_offset;
206 }
207
208 #ifdef BOOST_POSIX_API
209
210 inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
211 {
212 if (imp.handle != NULL)
213 {
214 DIR* h = static_cast< DIR* >(imp.handle);
215 imp.handle = NULL;
216 int err = 0;
217 if (BOOST_UNLIKELY(::closedir(h) != 0))
218 {
219 err = errno;
220 return error_code(err, system_category());
221 }
222 }
223
224 return error_code();
225 }
226
227 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
228
229 // Obtains maximum length of a path, not including the terminating zero
230 inline std::size_t get_path_max()
231 {
232 // this code is based on Stevens and Rago, Advanced Programming in the
233 // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
234 std::size_t max = 0;
235 errno = 0;
236 long res = ::pathconf("/", _PC_PATH_MAX);
237 if (res < 0)
238 {
239 #if defined(PATH_MAX)
240 max = PATH_MAX;
241 #else
242 max = 4096;
243 #endif
244 }
245 else
246 {
247 max = static_cast< std::size_t >(res); // relative root
248 #if defined(PATH_MAX)
249 if (max < PATH_MAX)
250 max = PATH_MAX;
251 #endif
252 }
253
254 if ((max + 1) < sizeof(dirent().d_name))
255 max = sizeof(dirent().d_name) - 1;
256
257 return max;
258 }
259
260 // Returns maximum length of a path, not including the terminating zero
261 inline std::size_t path_max()
262 {
263 static const std::size_t max = get_path_max();
264 return max;
265 }
266
267 #endif // BOOST_FILESYSTEM_USE_READDIR_R
268
269 // *result set to NULL on end of directory
270 #if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
271 inline
272 #endif
273 int readdir_impl(dir_itr_imp& imp, struct dirent** result)
274 {
275 errno = 0;
276
277 struct dirent* p = ::readdir(static_cast< DIR* >(imp.handle));
278 *result = p;
279 if (!p)
280 return errno;
281 return 0;
282 }
283
284 #if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
285
286 inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
287 {
288 return readdir_impl(imp, result);
289 }
290
291 #else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
292
293 int readdir_r_impl(dir_itr_imp& imp, struct dirent** result)
294 {
295 return ::readdir_r
296 (
297 static_cast< DIR* >(imp.handle),
298 static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)),
299 result
300 );
301 }
302
303 int readdir_select_impl(dir_itr_imp& imp, struct dirent** result);
304
305 typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result);
306
307 //! Pointer to the actual implementation of the copy_file_data implementation
308 readdir_impl_t* readdir_impl_ptr = &readdir_select_impl;
309
310 void init_readdir_impl()
311 {
312 readdir_impl_t* impl = &readdir_impl;
313 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
314 impl = &readdir_r_impl;
315
316 filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl);
317 }
318
319 struct readdir_initializer
320 {
321 readdir_initializer()
322 {
323 init_readdir_impl();
324 }
325 };
326
327 BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
328 const readdir_initializer readdir_init;
329
330 int readdir_select_impl(dir_itr_imp& imp, struct dirent** result)
331 {
332 init_readdir_impl();
333 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
334 }
335
336 inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
337 {
338 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
339 }
340
341 #endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
342
343 error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
344 {
345 dirent* result = NULL;
346 int err = invoke_readdir(imp, &result);
347 if (BOOST_UNLIKELY(err != 0))
348 return error_code(err, system_category());
349 if (result == NULL)
350 return dir_itr_close(imp);
351
352 filename = result->d_name;
353
354 #ifdef BOOST_FILESYSTEM_STATUS_CACHE
355 if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
356 {
357 sf = symlink_sf = fs::file_status(fs::status_error);
358 }
359 else // filesystem supplies d_type value
360 {
361 if (result->d_type == DT_DIR)
362 sf = symlink_sf = fs::file_status(fs::directory_file);
363 else if (result->d_type == DT_REG)
364 sf = symlink_sf = fs::file_status(fs::regular_file);
365 else if (result->d_type == DT_LNK)
366 {
367 sf = fs::file_status(fs::status_error);
368 symlink_sf = fs::file_status(fs::symlink_file);
369 }
370 else
371 sf = symlink_sf = fs::file_status(fs::status_error);
372 }
373 #else
374 sf = symlink_sf = fs::file_status(fs::status_error);
375 #endif
376 return error_code();
377 }
378
379 error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status&, fs::file_status&)
380 {
381 std::size_t extra_size = 0u;
382 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
383 {
384 readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
385 if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl))
386 {
387 init_readdir_impl();
388 rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
389 }
390
391 if (rdimpl == &readdir_r_impl)
392 {
393 // According to readdir description, there's no reliable way to predict the length of the d_name string.
394 // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
395 // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
396 // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
397 // in favor of readdir.
398 extra_size = (sizeof(dirent) - sizeof(dirent().d_name)) + path_max() + 1u; // + 1 for "\0"
399 }
400 }
401 #endif // defined(BOOST_FILESYSTEM_USE_READDIR_R)
402
403 boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (extra_size) detail::dir_itr_imp());
404 if (BOOST_UNLIKELY(!pimpl))
405 return make_error_code(system::errc::not_enough_memory);
406
407 #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
408 int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
409 if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
410 flags |= O_NOFOLLOW;
411
412 int fd = ::open(dir.c_str(), flags);
413 if (BOOST_UNLIKELY(fd < 0))
414 {
415 const int err = errno;
416 return error_code(err, system_category());
417 }
418
419 #if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
420 int res = ::fcntl(fd, F_SETFD, FD_CLOEXEC);
421 if (BOOST_UNLIKELY(res < 0))
422 {
423 const int err = errno;
424 close_fd(fd);
425 return error_code(err, system_category());
426 }
427 #endif
428
429 pimpl->handle = ::fdopendir(fd);
430 if (BOOST_UNLIKELY(!pimpl->handle))
431 {
432 const int err = errno;
433 close_fd(fd);
434 return error_code(err, system_category());
435 }
436 #else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
437 pimpl->handle = ::opendir(dir.c_str());
438 if (BOOST_UNLIKELY(!pimpl->handle))
439 {
440 const int err = errno;
441 return error_code(err, system_category());
442 }
443 #endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
444
445 // Force initial readdir call by the caller. This will initialize the actual first filename and statuses.
446 first_filename.assign(".");
447
448 imp.swap(pimpl);
449 return error_code();
450 }
451
452 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT;
453
454 #else // BOOST_WINDOWS_API
455
456 inline void set_file_statuses(DWORD attrs, const ULONG* reparse_point_tag, fs::path const& filename, fs::file_status& sf, fs::file_status& symlink_sf)
457 {
458 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
459 {
460 // Reparse points are complex, so don't try to resolve them here; instead just mark
461 // them as status_error which causes directory_entry caching to call status()
462 // and symlink_status() which do handle reparse points fully
463 if (reparse_point_tag)
464 {
465 // If we have a reparse point tag we can at least populate the symlink status,
466 // consistent with symlink_status() behavior
467 symlink_sf.type(is_reparse_point_tag_a_symlink(*reparse_point_tag) ? fs::symlink_file : fs::reparse_file);
468 symlink_sf.permissions(make_permissions(filename, attrs));
469 }
470 else
471 {
472 symlink_sf.type(fs::status_error);
473 }
474
475 sf.type(fs::status_error);
476 }
477 else
478 {
479 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
480 {
481 sf.type(fs::directory_file);
482 symlink_sf.type(fs::directory_file);
483 }
484 else
485 {
486 sf.type(fs::regular_file);
487 symlink_sf.type(fs::regular_file);
488 }
489
490 sf.permissions(make_permissions(filename, attrs));
491 symlink_sf.permissions(sf.permissions());
492 }
493 }
494
495 #if !defined(UNDER_CE)
496
497 //! FILE_ID_128 definition from Windows SDK
498 struct file_id_128
499 {
500 BYTE Identifier[16];
501 };
502
503 //! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably).
504 struct file_directory_information
505 {
506 ULONG NextEntryOffset;
507 ULONG FileIndex;
508 LARGE_INTEGER CreationTime;
509 LARGE_INTEGER LastAccessTime;
510 LARGE_INTEGER LastWriteTime;
511 LARGE_INTEGER ChangeTime;
512 LARGE_INTEGER EndOfFile;
513 LARGE_INTEGER AllocationSize;
514 ULONG FileAttributes;
515 ULONG FileNameLength;
516 WCHAR FileName[1];
517 };
518
519 //! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista.
520 struct file_id_both_dir_info
521 {
522 DWORD NextEntryOffset;
523 DWORD FileIndex;
524 LARGE_INTEGER CreationTime;
525 LARGE_INTEGER LastAccessTime;
526 LARGE_INTEGER LastWriteTime;
527 LARGE_INTEGER ChangeTime;
528 LARGE_INTEGER EndOfFile;
529 LARGE_INTEGER AllocationSize;
530 DWORD FileAttributes;
531 DWORD FileNameLength;
532 DWORD EaSize;
533 CCHAR ShortNameLength;
534 WCHAR ShortName[12];
535 LARGE_INTEGER FileId;
536 WCHAR FileName[1];
537 };
538
539 //! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8.
540 struct file_full_dir_info
541 {
542 ULONG NextEntryOffset;
543 ULONG FileIndex;
544 LARGE_INTEGER CreationTime;
545 LARGE_INTEGER LastAccessTime;
546 LARGE_INTEGER LastWriteTime;
547 LARGE_INTEGER ChangeTime;
548 LARGE_INTEGER EndOfFile;
549 LARGE_INTEGER AllocationSize;
550 ULONG FileAttributes;
551 ULONG FileNameLength;
552 ULONG EaSize;
553 WCHAR FileName[1];
554 };
555
556 //! FILE_ID_EXTD_DIR_INFO definition from Windows SDK. Provides reparse point tag, which saves us querying it with a few separate syscalls. Supported since Windows 8.
557 struct file_id_extd_dir_info
558 {
559 ULONG NextEntryOffset;
560 ULONG FileIndex;
561 LARGE_INTEGER CreationTime;
562 LARGE_INTEGER LastAccessTime;
563 LARGE_INTEGER LastWriteTime;
564 LARGE_INTEGER ChangeTime;
565 LARGE_INTEGER EndOfFile;
566 LARGE_INTEGER AllocationSize;
567 ULONG FileAttributes;
568 ULONG FileNameLength;
569 ULONG EaSize;
570 ULONG ReparsePointTag;
571 file_id_128 FileId;
572 WCHAR FileName[1];
573 };
574
575 //! Indicates format of the extra data in the directory iterator
576 enum extra_data_format
577 {
578 file_directory_information_format,
579 file_id_both_dir_info_format,
580 file_full_dir_info_format,
581 file_id_extd_dir_info_format
582 };
583
584 //! Indicates extra data format that should be used by directory iterator by default
585 extra_data_format g_extra_data_format = file_directory_information_format;
586
587 /*!
588 * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator.
589 *
590 * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename.
591 * NTFS, VFAT, exFAT support filenames up to 255 UTF-16/UCS-2 characters. ReFS supports filenames up to 32768 UTF-16 characters.
592 */
593 BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u;
594
595 inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
596 {
597 imp.extra_data_format = 0u;
598 imp.current_offset = 0u;
599
600 if (imp.handle != NULL)
601 {
602 ::CloseHandle(imp.handle);
603 imp.handle = NULL;
604 }
605
606 return error_code();
607 }
608
609 error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
610 {
611 void* extra_data = get_dir_itr_imp_extra_data(&imp);
612 const void* current_data = static_cast< const unsigned char* >(extra_data) + imp.current_offset;
613 switch (imp.extra_data_format)
614 {
615 case file_id_extd_dir_info_format:
616 {
617 const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data);
618 if (data->NextEntryOffset == 0u)
619 {
620 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_extd_directory_info_class, extra_data, dir_itr_extra_size))
621 {
622 DWORD error = ::GetLastError();
623
624 dir_itr_close(imp);
625 if (error == ERROR_NO_MORE_FILES)
626 goto done;
627
628 return error_code(error, system_category());
629 }
630
631 imp.current_offset = 0u;
632 data = static_cast< const file_id_extd_dir_info* >(extra_data);
633 }
634 else
635 {
636 imp.current_offset += data->NextEntryOffset;
637 data = reinterpret_cast< const file_id_extd_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
638 }
639
640 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
641 set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf);
642 }
643 break;
644
645 case file_full_dir_info_format:
646 {
647 const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data);
648 if (data->NextEntryOffset == 0u)
649 {
650 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_full_directory_info_class, extra_data, dir_itr_extra_size))
651 {
652 DWORD error = ::GetLastError();
653
654 dir_itr_close(imp);
655 if (error == ERROR_NO_MORE_FILES)
656 goto done;
657
658 return error_code(error, system_category());
659 }
660
661 imp.current_offset = 0u;
662 data = static_cast< const file_full_dir_info* >(extra_data);
663 }
664 else
665 {
666 imp.current_offset += data->NextEntryOffset;
667 data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
668 }
669
670 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
671 set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
672 }
673 break;
674
675 case file_id_both_dir_info_format:
676 {
677 const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data);
678 if (data->NextEntryOffset == 0u)
679 {
680 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api)(imp.handle, file_id_both_directory_info_class, extra_data, dir_itr_extra_size))
681 {
682 DWORD error = ::GetLastError();
683
684 dir_itr_close(imp);
685 if (error == ERROR_NO_MORE_FILES)
686 goto done;
687
688 return error_code(error, system_category());
689 }
690
691 imp.current_offset = 0u;
692 data = static_cast< const file_id_both_dir_info* >(extra_data);
693 }
694 else
695 {
696 imp.current_offset += data->NextEntryOffset;
697 data = reinterpret_cast< const file_id_both_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
698 }
699
700 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
701 set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
702 }
703 break;
704
705 default:
706 {
707 const file_directory_information* data = static_cast< const file_directory_information* >(current_data);
708 if (data->NextEntryOffset == 0u)
709 {
710 io_status_block iosb;
711 boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api)
712 (
713 imp.handle,
714 NULL, // Event
715 NULL, // ApcRoutine
716 NULL, // ApcContext
717 &iosb,
718 extra_data,
719 dir_itr_extra_size,
720 file_directory_information_class,
721 FALSE, // ReturnSingleEntry
722 NULL, // FileName
723 FALSE // RestartScan
724 );
725
726 if (!NT_SUCCESS(status))
727 {
728 dir_itr_close(imp);
729 if (status == STATUS_NO_MORE_FILES)
730 goto done;
731
732 return error_code(translate_ntstatus(status), system_category());
733 }
734
735 imp.current_offset = 0u;
736 data = static_cast< const file_directory_information* >(extra_data);
737 }
738 else
739 {
740 imp.current_offset += data->NextEntryOffset;
741 data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
742 }
743
744 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
745 set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf);
746 }
747 break;
748 }
749
750 done:
751 return error_code();
752 }
753
754 error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
755 {
756 boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp());
757 if (BOOST_UNLIKELY(!pimpl))
758 return make_error_code(system::errc::not_enough_memory);
759
760 DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
761 if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
762 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
763 handle_wrapper h(create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags));
764 if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE))
765 {
766 return_last_error:
767 DWORD error = ::GetLastError();
768 return error_code(error, system_category());
769 }
770
771 GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
772 if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL))
773 {
774 file_attribute_tag_info info;
775 BOOL res = get_file_information_by_handle_ex(h.handle, file_attribute_tag_info_class, &info, sizeof(info));
776 if (BOOST_UNLIKELY(!res))
777 goto return_last_error;
778
779 if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
780 return make_error_code(system::errc::not_a_directory);
781
782 if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
783 {
784 if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag))
785 return make_error_code(system::errc::too_many_symbolic_link_levels);
786 }
787 }
788 else
789 {
790 BY_HANDLE_FILE_INFORMATION info;
791 BOOL res = ::GetFileInformationByHandle(h.handle, &info);
792 if (BOOST_UNLIKELY(!res))
793 goto return_last_error;
794
795 if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
796 return make_error_code(system::errc::not_a_directory);
797
798 if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u)
799 {
800 if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_a_symlink_ioctl(h.handle))
801 return make_error_code(system::errc::too_many_symbolic_link_levels);
802 }
803 }
804
805 void* extra_data = get_dir_itr_imp_extra_data(pimpl.get());
806 switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format))
807 {
808 case file_id_extd_dir_info_format:
809 {
810 if (!get_file_information_by_handle_ex(h.handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size))
811 {
812 DWORD error = ::GetLastError();
813
814 if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER)
815 {
816 // Fall back to file_full_dir_info_format.
817 // Note that some mounted filesystems may not support FILE_ID_128 identifiers, which will cause
818 // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER,
819 // even though in general the operation is supported by the kernel. So don't downgrade to
820 // FileFullDirectoryRestartInfo permanently in this case - only for this particular iterator.
821 if (error == ERROR_NOT_SUPPORTED)
822 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format);
823 goto fallback_to_file_full_dir_info_format;
824 }
825
826 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
827 goto done;
828
829 return error_code(error, system_category());
830 }
831
832 pimpl->extra_data_format = file_id_extd_dir_info_format;
833
834 const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(extra_data);
835 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
836
837 set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf);
838 }
839 break;
840
841 case file_full_dir_info_format:
842 fallback_to_file_full_dir_info_format:
843 {
844 if (!get_file_information_by_handle_ex(h.handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size))
845 {
846 DWORD error = ::GetLastError();
847
848 if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER)
849 {
850 // Fall back to file_id_both_dir_info
851 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format);
852 goto fallback_to_file_id_both_dir_info;
853 }
854
855 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
856 goto done;
857
858 return error_code(error, system_category());
859 }
860
861 pimpl->extra_data_format = file_full_dir_info_format;
862
863 const file_full_dir_info* data = static_cast< const file_full_dir_info* >(extra_data);
864 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
865
866 set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
867 }
868 break;
869
870 case file_id_both_dir_info_format:
871 fallback_to_file_id_both_dir_info:
872 {
873 if (!get_file_information_by_handle_ex(h.handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size))
874 {
875 DWORD error = ::GetLastError();
876
877 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
878 goto done;
879
880 return error_code(error, system_category());
881 }
882
883 pimpl->extra_data_format = file_id_both_dir_info_format;
884
885 const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(extra_data);
886 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
887
888 set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
889 }
890 break;
891
892 default:
893 {
894 NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api);
895 if (BOOST_UNLIKELY(!nt_query_directory_file))
896 return error_code(ERROR_NOT_SUPPORTED, system_category());
897
898 io_status_block iosb;
899 boost::winapi::NTSTATUS_ status = nt_query_directory_file
900 (
901 h.handle,
902 NULL, // Event
903 NULL, // ApcRoutine
904 NULL, // ApcContext
905 &iosb,
906 extra_data,
907 dir_itr_extra_size,
908 file_directory_information_class,
909 FALSE, // ReturnSingleEntry
910 NULL, // FileName
911 TRUE // RestartScan
912 );
913
914 if (!NT_SUCCESS(status))
915 {
916 // Note: an empty root directory has no "." or ".." entries, so this
917 // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW
918 // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we
919 // do not consider an error. It is treated as eof instead.
920 if (status == STATUS_NO_MORE_FILES || status == STATUS_NO_SUCH_FILE)
921 goto done;
922
923 return error_code(translate_ntstatus(status), system_category());
924 }
925
926 pimpl->extra_data_format = file_directory_information_format;
927
928 const file_directory_information* data = static_cast< const file_directory_information* >(extra_data);
929 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
930
931 set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf);
932 }
933 break;
934 }
935
936 pimpl->handle = h.handle;
937 h.handle = INVALID_HANDLE_VALUE;
938
939 done:
940 imp.swap(pimpl);
941 return error_code();
942 }
943
944 #else // !defined(UNDER_CE)
945
946 inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT
947 {
948 if (imp.handle != NULL)
949 {
950 ::FindClose(imp.handle);
951 imp.handle = NULL;
952 }
953
954 return error_code();
955 }
956
957 error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
958 {
959 WIN32_FIND_DATAW data;
960 if (::FindNextFileW(imp.handle, &data) == 0) // fails
961 {
962 DWORD error = ::GetLastError();
963 dir_itr_close(imp);
964 if (error == ERROR_NO_MORE_FILES)
965 goto done;
966 return error_code(error, system_category());
967 }
968
969 filename = data.cFileName;
970 set_file_statuses(data.dwFileAttributes, NULL, filename, sf, symlink_sf);
971
972 done:
973 return error_code();
974 }
975
976 error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
977 {
978 boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (static_cast< std::size_t >(0u)) detail::dir_itr_imp());
979 if (BOOST_UNLIKELY(!pimpl))
980 return make_error_code(system::errc::not_enough_memory);
981
982 // use a form of search Sebastian Martel reports will work with Win98
983 fs::path dirpath(dir);
984 dirpath.make_preferred();
985 dirpath /= L"*";
986
987 WIN32_FIND_DATAW data;
988 pimpl->handle = ::FindFirstFileW(dirpath.c_str(), &data);
989 if (BOOST_UNLIKELY(pimpl->handle == INVALID_HANDLE_VALUE))
990 {
991 pimpl->handle = NULL; // signal eof
992
993 // Note: an empty root directory has no "." or ".." entries, so this
994 // causes a ERROR_FILE_NOT_FOUND error which we do not consider an
995 // error. It is treated as eof instead.
996 // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551
997 DWORD error = ::GetLastError();
998 if (error == ERROR_FILE_NOT_FOUND || error == ERROR_NO_MORE_FILES)
999 goto done;
1000
1001 return error_code(error, system_category());
1002 }
1003
1004 first_filename = data.cFileName;
1005 set_file_statuses(data.dwFileAttributes, NULL, first_filename, sf, symlink_sf);
1006
1007 done:
1008 imp.swap(pimpl);
1009 return error_code();
1010 }
1011
1012 #endif // !defined(UNDER_CE)
1013
1014 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND;
1015
1016 #endif // BOOST_WINDOWS_API
1017
1018 } // namespace
1019
1020 #if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
1021
1022 //! Initializes directory iterator implementation
1023 void init_directory_iterator_impl() BOOST_NOEXCEPT
1024 {
1025 if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != NULL)
1026 {
1027 // Enable the latest format we support. It will get downgraded, if needed, as we attempt
1028 // to create the directory iterator the first time.
1029 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_extd_dir_info_format);
1030 }
1031 }
1032
1033 #endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
1034
1035 BOOST_FILESYSTEM_DECL
1036 dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT
1037 {
1038 dir_itr_close(*this);
1039 }
1040
1041 BOOST_FILESYSTEM_DECL
1042 void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec)
1043 {
1044 if (BOOST_UNLIKELY(p.empty()))
1045 {
1046 emit_error(not_found_error_code, p, ec, "boost::filesystem::directory_iterator::construct");
1047 return;
1048 }
1049
1050 if (ec)
1051 ec->clear();
1052
1053 try
1054 {
1055 boost::intrusive_ptr< detail::dir_itr_imp > imp;
1056 path filename;
1057 file_status file_stat, symlink_file_stat;
1058 system::error_code result = dir_itr_create(imp, p, opts, filename, file_stat, symlink_file_stat);
1059
1060 while (true)
1061 {
1062 if (result)
1063 {
1064 if (result != make_error_condition(system::errc::permission_denied) ||
1065 (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u)
1066 {
1067 if (!ec)
1068 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p, result));
1069 *ec = result;
1070 }
1071
1072 return;
1073 }
1074
1075 if (imp->handle == NULL) // eof, make end
1076 return;
1077
1078 // Not eof
1079 const path::string_type::value_type* filename_str = filename.c_str();
1080 if (!(filename_str[0] == path::dot // dot or dot-dot
1081 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
1082 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
1083 {
1084 imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat);
1085 it.m_imp.swap(imp);
1086 return;
1087 }
1088
1089 // If dot or dot-dot name produced by the underlying API, skip it until the first actual file
1090 result = dir_itr_increment(*imp, filename, file_stat, symlink_file_stat);
1091 }
1092 }
1093 catch (std::bad_alloc&)
1094 {
1095 if (!ec)
1096 throw;
1097
1098 *ec = make_error_code(system::errc::not_enough_memory);
1099 it.m_imp.reset();
1100 }
1101 }
1102
1103 BOOST_FILESYSTEM_DECL
1104 void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
1105 {
1106 BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
1107
1108 if (ec)
1109 ec->clear();
1110
1111 try
1112 {
1113 path filename;
1114 file_status file_stat, symlink_file_stat;
1115 system::error_code increment_ec;
1116
1117 while (true)
1118 {
1119 increment_ec = dir_itr_increment(*it.m_imp, filename, file_stat, symlink_file_stat);
1120
1121 if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
1122 {
1123 boost::intrusive_ptr< detail::dir_itr_imp > imp;
1124 imp.swap(it.m_imp);
1125 path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900
1126 if (!ec)
1127 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++", error_path, increment_ec));
1128
1129 *ec = increment_ec;
1130 return;
1131 }
1132
1133 if (it.m_imp->handle == NULL) // eof, make end
1134 {
1135 it.m_imp.reset();
1136 return;
1137 }
1138
1139 const path::string_type::value_type* filename_str = filename.c_str();
1140 if (!(filename_str[0] == path::dot // !(dot or dot-dot)
1141 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
1142 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
1143 {
1144 it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat);
1145 return;
1146 }
1147 }
1148 }
1149 catch (std::bad_alloc&)
1150 {
1151 if (!ec)
1152 throw;
1153
1154 it.m_imp.reset();
1155 *ec = make_error_code(system::errc::not_enough_memory);
1156 }
1157 }
1158
1159 //--------------------------------------------------------------------------------------//
1160 // //
1161 // recursive_directory_iterator //
1162 // //
1163 //--------------------------------------------------------------------------------------//
1164
1165 BOOST_FILESYSTEM_DECL
1166 void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, unsigned int opts, system::error_code* ec)
1167 {
1168 if (ec)
1169 ec->clear();
1170
1171 directory_iterator dir_it;
1172 detail::directory_iterator_construct(dir_it, dir_path, opts, ec);
1173 if ((ec && *ec) || dir_it == directory_iterator())
1174 return;
1175
1176 boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
1177 if (!ec)
1178 {
1179 imp = new detail::recur_dir_itr_imp(opts);
1180 }
1181 else
1182 {
1183 imp = new (std::nothrow) detail::recur_dir_itr_imp(opts);
1184 if (BOOST_UNLIKELY(!imp))
1185 {
1186 *ec = make_error_code(system::errc::not_enough_memory);
1187 return;
1188 }
1189 }
1190
1191 try
1192 {
1193 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1194 imp->m_stack.push_back(std::move(dir_it));
1195 #else
1196 imp->m_stack.push_back(dir_it);
1197 #endif
1198
1199 it.m_imp.swap(imp);
1200 }
1201 catch (std::bad_alloc&)
1202 {
1203 if (ec)
1204 {
1205 *ec = make_error_code(system::errc::not_enough_memory);
1206 return;
1207 }
1208
1209 throw;
1210 }
1211 }
1212
1213 namespace {
1214
1215 void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
1216 {
1217 imp->m_stack.pop_back();
1218
1219 while (!imp->m_stack.empty())
1220 {
1221 directory_iterator& dir_it = imp->m_stack.back();
1222 system::error_code increment_ec;
1223 detail::directory_iterator_increment(dir_it, &increment_ec);
1224 if (!increment_ec && dir_it != directory_iterator())
1225 break;
1226
1227 imp->m_stack.pop_back();
1228 }
1229 }
1230
1231 } // namespace
1232
1233 BOOST_FILESYSTEM_DECL
1234 void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
1235 {
1236 BOOST_ASSERT_MSG(!it.is_end(), "pop() on end recursive_directory_iterator");
1237 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
1238
1239 if (ec)
1240 ec->clear();
1241
1242 imp->m_stack.pop_back();
1243
1244 while (true)
1245 {
1246 if (imp->m_stack.empty())
1247 {
1248 it.m_imp.reset(); // done, so make end iterator
1249 break;
1250 }
1251
1252 directory_iterator& dir_it = imp->m_stack.back();
1253 system::error_code increment_ec;
1254 detail::directory_iterator_increment(dir_it, &increment_ec);
1255 if (BOOST_UNLIKELY(!!increment_ec))
1256 {
1257 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
1258 {
1259 // Make an end iterator on errors
1260 it.m_imp.reset();
1261 }
1262 else
1263 {
1264 recursive_directory_iterator_pop_on_error(imp);
1265
1266 if (imp->m_stack.empty())
1267 it.m_imp.reset(); // done, so make end iterator
1268 }
1269
1270 if (!ec)
1271 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec));
1272
1273 *ec = increment_ec;
1274 return;
1275 }
1276
1277 if (dir_it != directory_iterator())
1278 break;
1279
1280 imp->m_stack.pop_back();
1281 }
1282 }
1283
1284 namespace {
1285
1286 enum push_directory_result
1287 {
1288 directory_not_pushed = 0u,
1289 directory_pushed = 1u,
1290 keep_depth = 1u << 1
1291 };
1292
1293 // Returns: true if push occurs, otherwise false. Always returns false on error.
1294 inline push_directory_result recursive_directory_iterator_push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec) BOOST_NOEXCEPT
1295 {
1296 push_directory_result result = directory_not_pushed;
1297 try
1298 {
1299 // Discover if the iterator is for a directory that needs to be recursed into,
1300 // taking symlinks and options into account.
1301
1302 if ((imp->m_options & static_cast< unsigned int >(directory_options::_detail_no_push)) != 0u)
1303 {
1304 imp->m_options &= ~static_cast< unsigned int >(directory_options::_detail_no_push);
1305 return result;
1306 }
1307
1308 file_status symlink_stat;
1309
1310 // if we are not recursing into symlinks, we are going to have to know if the
1311 // stack top is a symlink, so get symlink_status and verify no error occurred
1312 if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) == 0u ||
1313 (imp->m_options & static_cast< unsigned int >(directory_options::skip_dangling_symlinks)) != 0u)
1314 {
1315 symlink_stat = imp->m_stack.back()->symlink_status(ec);
1316 if (ec)
1317 return result;
1318 }
1319
1320 // Logic for following predicate was contributed by Daniel Aarno to handle cyclic
1321 // symlinks correctly and efficiently, fixing ticket #5652.
1322 // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink
1323 // || !is_symlink(m_stack.back()->symlink_status()))
1324 // && is_directory(m_stack.back()->status())) ...
1325 // The predicate code has since been rewritten to pass error_code arguments,
1326 // per ticket #5653.
1327
1328 if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat))
1329 {
1330 file_status stat = imp->m_stack.back()->status(ec);
1331 if (BOOST_UNLIKELY(!!ec))
1332 {
1333 if (ec == make_error_condition(system::errc::no_such_file_or_directory) && fs::is_symlink(symlink_stat) &&
1334 (imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
1335 {
1336 // Skip dangling symlink and continue iteration on the current depth level
1337 ec = error_code();
1338 }
1339
1340 return result;
1341 }
1342
1343 if (!fs::is_directory(stat))
1344 return result;
1345
1346 if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))
1347 {
1348 // We cannot let depth to overflow
1349 ec = make_error_code(system::errc::value_too_large);
1350 // When depth overflow happens, avoid popping the current directory iterator
1351 // and attempt to continue iteration on the current depth.
1352 result = keep_depth;
1353 return result;
1354 }
1355
1356 directory_iterator next(imp->m_stack.back()->path(), static_cast< BOOST_SCOPED_ENUM_NATIVE(directory_options) >(imp->m_options), ec);
1357 if (!ec && next != directory_iterator())
1358 {
1359 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1360 imp->m_stack.push_back(std::move(next)); // may throw
1361 #else
1362 imp->m_stack.push_back(next); // may throw
1363 #endif
1364 return directory_pushed;
1365 }
1366 }
1367 }
1368 catch (std::bad_alloc&)
1369 {
1370 ec = make_error_code(system::errc::not_enough_memory);
1371 }
1372
1373 return result;
1374 }
1375
1376 } // namespace
1377
1378 BOOST_FILESYSTEM_DECL
1379 void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
1380 {
1381 BOOST_ASSERT_MSG(!it.is_end(), "increment() on end recursive_directory_iterator");
1382 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
1383
1384 if (ec)
1385 ec->clear();
1386
1387 system::error_code local_ec;
1388
1389 // if various conditions are met, push a directory_iterator into the iterator stack
1390 push_directory_result push_result = recursive_directory_iterator_push_directory(imp, local_ec);
1391 if (push_result == directory_pushed)
1392 return;
1393
1394 // report errors if any
1395 if (BOOST_UNLIKELY(!!local_ec))
1396 {
1397 on_error:
1398 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
1399 {
1400 // Make an end iterator on errors
1401 it.m_imp.reset();
1402 }
1403 else
1404 {
1405 if ((push_result & keep_depth) != 0u)
1406 {
1407 system::error_code increment_ec;
1408 directory_iterator& dir_it = imp->m_stack.back();
1409 detail::directory_iterator_increment(dir_it, &increment_ec);
1410 if (!increment_ec && dir_it != directory_iterator())
1411 goto on_error_return;
1412 }
1413
1414 recursive_directory_iterator_pop_on_error(imp);
1415
1416 if (imp->m_stack.empty())
1417 it.m_imp.reset(); // done, so make end iterator
1418 }
1419
1420 on_error_return:
1421 if (!ec)
1422 BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec));
1423
1424 *ec = local_ec;
1425 return;
1426 }
1427
1428 // Do the actual increment operation on the top iterator in the iterator
1429 // stack, popping the stack if necessary, until either the stack is empty or a
1430 // non-end iterator is reached.
1431 while (true)
1432 {
1433 if (imp->m_stack.empty())
1434 {
1435 it.m_imp.reset(); // done, so make end iterator
1436 break;
1437 }
1438
1439 directory_iterator& dir_it = imp->m_stack.back();
1440 detail::directory_iterator_increment(dir_it, &local_ec);
1441 if (BOOST_UNLIKELY(!!local_ec))
1442 goto on_error;
1443
1444 if (dir_it != directory_iterator())
1445 break;
1446
1447 imp->m_stack.pop_back();
1448 }
1449 }
1450
1451 } // namespace detail
1452
1453 } // namespace filesystem
1454 } // namespace boost