1// directory.cpp --------------------------------------------------------------------//
2
3// Copyright 2002-2009, 2014 Beman Dawes
4// Copyright 2001 Dietmar Kuehl
5// Copyright 2019, 2022-2024 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/filesystem/config.hpp>
17#include <boost/filesystem/directory.hpp>
18#include <boost/filesystem/exception.hpp>
19#include <boost/filesystem/operations.hpp>
20#include <boost/filesystem/file_status.hpp>
21
22#include <cstddef>
23#include <cerrno>
24#include <cstring>
25#include <cstdlib> // std::malloc, std::free
26#include <new> // std::nothrow, std::bad_alloc
27#include <limits>
28#include <string>
29#include <utility> // std::move
30#include <boost/assert.hpp>
31#include <boost/system/error_code.hpp>
32#include <boost/smart_ptr/intrusive_ptr.hpp>
33
34#ifdef BOOST_POSIX_API
35
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <dirent.h>
39#include <unistd.h>
40#include <fcntl.h>
41
42#include <boost/scope/unique_fd.hpp>
43
44#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS >= 0) && defined(_SC_THREAD_SAFE_FUNCTIONS) && \
45 !defined(__CYGWIN__) && \
46 !(defined(linux) || defined(__linux) || defined(__linux__)) && \
47 !defined(__ANDROID__) && \
48 (!defined(__hpux) || defined(_REENTRANT)) && \
49 (!defined(_AIX) || defined(__THREAD_SAFE)) && \
50 !defined(__wasm)
51#define BOOST_FILESYSTEM_USE_READDIR_R
52#endif
53
54// At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
55#ifndef O_CLOEXEC
56#define O_CLOEXEC 0
57#define BOOST_FILESYSTEM_NO_O_CLOEXEC
58#endif
59
60#include "posix_tools.hpp"
61
62#else // BOOST_WINDOWS_API
63
64#include <cwchar>
65#include <windows.h>
66#include <boost/winapi/basic_types.hpp> // NTSTATUS_
67
68#include "windows_tools.hpp"
69
70#endif // BOOST_WINDOWS_API
71
72#include "atomic_tools.hpp"
73#include "error_handling.hpp"
74#include "private_config.hpp"
75
76#include <boost/filesystem/detail/header.hpp> // must be the last #include
77
78namespace fs = boost::filesystem;
79using boost::system::error_code;
80using boost::system::system_category;
81
82namespace boost {
83namespace filesystem {
84
85//--------------------------------------------------------------------------------------//
86// //
87// directory_entry //
88// //
89//--------------------------------------------------------------------------------------//
90
91BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const
92{
93 system::error_code local_ec;
94 m_symlink_status = detail::symlink_status(p: m_path, ec: &local_ec);
95
96 if (!filesystem::is_symlink(f: m_symlink_status))
97 {
98 // Also works if symlink_status fails - set m_status to status_error as well
99 m_status = m_symlink_status;
100
101 if (BOOST_UNLIKELY(!!local_ec))
102 {
103 if (!ec)
104 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_entry::refresh", m_path, local_ec));
105
106 *ec = local_ec;
107 return;
108 }
109
110 if (ec)
111 ec->clear();
112 }
113 else
114 {
115 m_status = detail::status(p: m_path, ec);
116 }
117}
118
119//--------------------------------------------------------------------------------------//
120// //
121// directory_iterator //
122// //
123//--------------------------------------------------------------------------------------//
124
125namespace detail {
126
127#if defined(BOOST_POSIX_API)
128
129//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
130boost::scope::unique_fd open_directory(path const& p, directory_options opts, system::error_code& ec)
131{
132 ec.clear();
133
134 int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
135
136#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
137 if ((opts & directory_options::_detail_no_follow) != directory_options::none)
138 flags |= O_NOFOLLOW;
139#endif
140
141 int res;
142 while (true)
143 {
144 res = ::open(file: p.c_str(), oflag: flags);
145 if (BOOST_UNLIKELY(res < 0))
146 {
147 const int err = errno;
148 if (err == EINTR)
149 continue;
150 ec = system::error_code(err, system::system_category());
151 return boost::scope::unique_fd();
152 }
153
154 break;
155 }
156
157#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
158 boost::scope::unique_fd fd(res);
159
160 res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
161 if (BOOST_UNLIKELY(res < 0))
162 {
163 const int err = errno;
164 ec = system::error_code(err, system::system_category());
165 return boost::scope::unique_fd();
166 }
167
168 return fd;
169#else
170 return boost::scope::unique_fd(res);
171#endif
172}
173
174#if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
175
176//! Opens a directory file and returns a file descriptor. Returns a negative value in case of error.
177boost::scope::unique_fd openat_directory(int basedir_fd, path const& p, directory_options opts, system::error_code& ec)
178{
179 ec.clear();
180
181 int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC;
182
183#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
184 if ((opts & directory_options::_detail_no_follow) != directory_options::none)
185 flags |= O_NOFOLLOW;
186#endif
187
188 int res;
189 while (true)
190 {
191 res = ::openat(fd: basedir_fd, file: p.c_str(), oflag: flags);
192 if (BOOST_UNLIKELY(res < 0))
193 {
194 const int err = errno;
195 if (err == EINTR)
196 continue;
197 ec = system::error_code(err, system::system_category());
198 return boost::scope::unique_fd();
199 }
200
201 break;
202 }
203
204#if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
205 boost::scope::unique_fd fd(res);
206
207 res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
208 if (BOOST_UNLIKELY(res < 0))
209 {
210 const int err = errno;
211 ec = system::error_code(err, system::system_category());
212 return boost::scope::unique_fd();
213 }
214
215 return fd;
216#else
217 return boost::scope::unique_fd(res);
218#endif
219}
220
221#endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
222
223#endif // defined(BOOST_POSIX_API)
224
225BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u;
226
227BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t extra_size) noexcept
228{
229 if (extra_size > 0)
230 class_size = (class_size + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u);
231 std::size_t total_size = class_size + extra_size;
232
233 // Return nullptr on OOM
234 void* p = std::malloc(size: total_size);
235 if (BOOST_LIKELY(p != nullptr))
236 std::memset(s: p, c: 0, n: total_size);
237 return p;
238}
239
240BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) noexcept
241{
242 std::free(ptr: p);
243}
244
245BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) noexcept
246{
247 std::free(ptr: p);
248}
249
250namespace {
251
252inline void* get_dir_itr_imp_extra_data(dir_itr_imp* imp) noexcept
253{
254 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);
255 return reinterpret_cast< unsigned char* >(imp) + extra_data_offset;
256}
257
258#ifdef BOOST_POSIX_API
259
260inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept
261{
262 if (imp.handle != nullptr)
263 {
264 DIR* h = static_cast< DIR* >(imp.handle);
265 imp.handle = nullptr;
266 int err = 0;
267 if (BOOST_UNLIKELY(::closedir(h) != 0))
268 {
269 err = errno;
270 return system::error_code(err, system::system_category());
271 }
272 }
273
274 return error_code();
275}
276
277#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
278
279// Obtains a file descriptor from the directory iterator
280inline int dir_itr_fd(dir_itr_imp const& imp, system::error_code& ec)
281{
282 int fd = ::dirfd(dirp: static_cast< DIR* >(imp.handle));
283 if (BOOST_UNLIKELY(fd < 0))
284 {
285 int err = errno;
286 ec = system::error_code(err, system::system_category());
287 }
288
289 return fd;
290}
291
292#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
293
294#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
295
296// Obtains maximum length of a path, not including the terminating zero
297inline std::size_t get_path_max()
298{
299 // this code is based on Stevens and Rago, Advanced Programming in the
300 // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
301 std::size_t max = 0;
302 errno = 0;
303 long res = ::pathconf("/", _PC_PATH_MAX);
304 if (res < 0)
305 {
306#if defined(PATH_MAX)
307 max = PATH_MAX;
308#else
309 max = 4096;
310#endif
311 }
312 else
313 {
314 max = static_cast< std::size_t >(res); // relative root
315#if defined(PATH_MAX)
316 if (max < PATH_MAX)
317 max = PATH_MAX;
318#endif
319 }
320
321 if ((max + 1) < sizeof(dirent().d_name))
322 max = sizeof(dirent().d_name) - 1;
323
324 return max;
325}
326
327// Returns maximum length of a path, not including the terminating zero
328inline std::size_t path_max()
329{
330 static const std::size_t max = get_path_max();
331 return max;
332}
333
334#endif // BOOST_FILESYSTEM_USE_READDIR_R
335
336// *result set to nullptr on end of directory
337#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
338inline
339#endif
340int readdir_impl(dir_itr_imp& imp, struct dirent** result)
341{
342 errno = 0;
343
344 struct dirent* p = ::readdir(dirp: static_cast< DIR* >(imp.handle));
345 *result = p;
346 if (!p)
347 return errno;
348 return 0;
349}
350
351#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
352
353inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
354{
355 return readdir_impl(imp, result);
356}
357
358#else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
359
360int readdir_r_impl(dir_itr_imp& imp, struct dirent** result)
361{
362 return ::readdir_r
363 (
364 static_cast< DIR* >(imp.handle),
365 static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)),
366 result
367 );
368}
369
370int readdir_select_impl(dir_itr_imp& imp, struct dirent** result);
371
372typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result);
373
374//! Pointer to the actual implementation of the copy_file_data implementation
375readdir_impl_t* readdir_impl_ptr = &readdir_select_impl;
376
377void init_readdir_impl()
378{
379 readdir_impl_t* impl = &readdir_impl;
380 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
381 impl = &readdir_r_impl;
382
383 filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl);
384}
385
386struct readdir_initializer
387{
388 readdir_initializer()
389 {
390 init_readdir_impl();
391 }
392};
393
394BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
395const readdir_initializer readdir_init;
396
397int readdir_select_impl(dir_itr_imp& imp, struct dirent** result)
398{
399 init_readdir_impl();
400 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
401}
402
403inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
404{
405 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
406}
407
408#endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
409
410system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
411{
412 dirent* result = nullptr;
413 int err = invoke_readdir(imp, result: &result);
414 if (BOOST_UNLIKELY(err != 0))
415 return system::error_code(err, system::system_category());
416 if (result == nullptr)
417 return dir_itr_close(imp);
418
419 filename = result->d_name;
420
421#if defined(BOOST_FILESYSTEM_HAS_DIRENT_D_TYPE)
422 if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
423 {
424 sf = symlink_sf = fs::file_status(fs::status_error);
425 }
426 else // filesystem supplies d_type value
427 {
428 if (result->d_type == DT_REG)
429 sf = symlink_sf = fs::file_status(fs::regular_file);
430 else if (result->d_type == DT_DIR)
431 sf = symlink_sf = fs::file_status(fs::directory_file);
432 else if (result->d_type == DT_LNK)
433 {
434 sf = fs::file_status(fs::status_error);
435 symlink_sf = fs::file_status(fs::symlink_file);
436 }
437 else
438 {
439 switch (result->d_type)
440 {
441 case DT_SOCK:
442 sf = symlink_sf = fs::file_status(fs::socket_file);
443 break;
444 case DT_FIFO:
445 sf = symlink_sf = fs::file_status(fs::fifo_file);
446 break;
447 case DT_BLK:
448 sf = symlink_sf = fs::file_status(fs::block_file);
449 break;
450 case DT_CHR:
451 sf = symlink_sf = fs::file_status(fs::character_file);
452 break;
453 default:
454 sf = symlink_sf = fs::file_status(fs::status_error);
455 break;
456 }
457 }
458 }
459#else
460 sf = symlink_sf = fs::file_status(fs::status_error);
461#endif
462 return system::error_code();
463}
464
465system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status&, fs::file_status&)
466{
467 std::size_t extra_size = 0u;
468#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
469 {
470 readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
471 if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl))
472 {
473 init_readdir_impl();
474 rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr);
475 }
476
477 if (rdimpl == &readdir_r_impl)
478 {
479 // According to readdir description, there's no reliable way to predict the length of the d_name string.
480 // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
481 // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
482 // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
483 // in favor of readdir.
484 extra_size = (sizeof(dirent) - sizeof(dirent().d_name)) + path_max() + 1u; // + 1 for "\0"
485 }
486 }
487#endif // defined(BOOST_FILESYSTEM_USE_READDIR_R)
488
489 boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (extra_size) detail::dir_itr_imp());
490 if (BOOST_UNLIKELY(!pimpl))
491 return make_error_code(e: system::errc::not_enough_memory);
492
493#if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
494 boost::scope::unique_fd fd;
495 if (params && params->dir_fd)
496 {
497 fd = std::move(params->dir_fd);
498 }
499 else
500 {
501 system::error_code ec;
502 fd = open_directory(p: dir, opts, ec);
503 if (BOOST_UNLIKELY(!!ec))
504 return ec;
505 }
506
507 pimpl->handle = ::fdopendir(fd: fd.get());
508 if (BOOST_UNLIKELY(!pimpl->handle))
509 {
510 const int err = errno;
511 return system::error_code(err, system::system_category());
512 }
513
514 // At this point fd will be closed by closedir
515 fd.release();
516#else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
517 pimpl->handle = ::opendir(dir.c_str());
518 if (BOOST_UNLIKELY(!pimpl->handle))
519 {
520 const int err = errno;
521 return system::error_code(err, system::system_category());
522 }
523#endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
524
525 // Force initial readdir call by the caller. This will initialize the actual first filename and statuses.
526 first_filename.assign(source: ".");
527
528 imp.swap(rhs&: pimpl);
529 return system::error_code();
530}
531
532BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT;
533
534#else // BOOST_WINDOWS_API
535
536inline void set_file_statuses(DWORD attrs, const ULONG* reparse_point_tag, fs::path const& filename, fs::file_status& sf, fs::file_status& symlink_sf)
537{
538 if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
539 {
540 // Reparse points are complex, so don't try to resolve them here; instead just mark
541 // them as status_error which causes directory_entry caching to call status()
542 // and symlink_status() which do handle reparse points fully
543 if (reparse_point_tag)
544 {
545 // If we have a reparse point tag we can at least populate the symlink status,
546 // consistent with symlink_status() behavior
547 symlink_sf.type(is_reparse_point_tag_a_symlink(*reparse_point_tag) ? fs::symlink_file : fs::reparse_file);
548 symlink_sf.permissions(make_permissions(filename, attrs));
549 }
550 else
551 {
552 symlink_sf.type(fs::status_error);
553 }
554
555 sf.type(fs::status_error);
556 }
557 else
558 {
559 if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u)
560 {
561 sf.type(fs::directory_file);
562 symlink_sf.type(fs::directory_file);
563 }
564 else
565 {
566 sf.type(fs::regular_file);
567 symlink_sf.type(fs::regular_file);
568 }
569
570 sf.permissions(make_permissions(filename, attrs));
571 symlink_sf.permissions(sf.permissions());
572 }
573}
574
575//! FILE_ID_128 definition from Windows SDK
576struct file_id_128
577{
578 BYTE Identifier[16];
579};
580
581//! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably).
582struct file_directory_information
583{
584 ULONG NextEntryOffset;
585 ULONG FileIndex;
586 LARGE_INTEGER CreationTime;
587 LARGE_INTEGER LastAccessTime;
588 LARGE_INTEGER LastWriteTime;
589 LARGE_INTEGER ChangeTime;
590 LARGE_INTEGER EndOfFile;
591 LARGE_INTEGER AllocationSize;
592 ULONG FileAttributes;
593 ULONG FileNameLength;
594 WCHAR FileName[1];
595};
596
597//! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista.
598struct file_id_both_dir_info
599{
600 DWORD NextEntryOffset;
601 DWORD FileIndex;
602 LARGE_INTEGER CreationTime;
603 LARGE_INTEGER LastAccessTime;
604 LARGE_INTEGER LastWriteTime;
605 LARGE_INTEGER ChangeTime;
606 LARGE_INTEGER EndOfFile;
607 LARGE_INTEGER AllocationSize;
608 DWORD FileAttributes;
609 DWORD FileNameLength;
610 DWORD EaSize;
611 CCHAR ShortNameLength;
612 WCHAR ShortName[12];
613 LARGE_INTEGER FileId;
614 WCHAR FileName[1];
615};
616
617//! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8.
618struct file_full_dir_info
619{
620 ULONG NextEntryOffset;
621 ULONG FileIndex;
622 LARGE_INTEGER CreationTime;
623 LARGE_INTEGER LastAccessTime;
624 LARGE_INTEGER LastWriteTime;
625 LARGE_INTEGER ChangeTime;
626 LARGE_INTEGER EndOfFile;
627 LARGE_INTEGER AllocationSize;
628 ULONG FileAttributes;
629 ULONG FileNameLength;
630 ULONG EaSize;
631 WCHAR FileName[1];
632};
633
634//! 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.
635struct file_id_extd_dir_info
636{
637 ULONG NextEntryOffset;
638 ULONG FileIndex;
639 LARGE_INTEGER CreationTime;
640 LARGE_INTEGER LastAccessTime;
641 LARGE_INTEGER LastWriteTime;
642 LARGE_INTEGER ChangeTime;
643 LARGE_INTEGER EndOfFile;
644 LARGE_INTEGER AllocationSize;
645 ULONG FileAttributes;
646 ULONG FileNameLength;
647 ULONG EaSize;
648 ULONG ReparsePointTag;
649 file_id_128 FileId;
650 WCHAR FileName[1];
651};
652
653//! Indicates format of the extra data in the directory iterator
654enum extra_data_format
655{
656 file_directory_information_format,
657 file_id_both_dir_info_format,
658 file_full_dir_info_format,
659 file_id_extd_dir_info_format
660};
661
662//! Indicates extra data format that should be used by directory iterator by default
663extra_data_format g_extra_data_format = file_directory_information_format;
664
665/*!
666 * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator.
667 *
668 * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename.
669 * NTFS, VFAT, exFAT and ReFS support filenames up to 255 UTF-16/UCS-2 characters. (For ReFS, there is no information
670 * on the on-disk format, and it is possible that it supports longer filenames, up to 32768 UTF-16/UCS-2 characters.)
671 * The buffer cannot be larger than 64k, because up to Windows 8.1, NtQueryDirectoryFile and GetFileInformationByHandleEx
672 * fail with ERROR_INVALID_PARAMETER when trying to retrieve the filenames from a network share.
673 */
674BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = 65536u;
675
676inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept
677{
678 imp.extra_data_format = 0u;
679 imp.current_offset = 0u;
680
681 if (imp.handle != nullptr)
682 {
683 if (BOOST_LIKELY(imp.close_handle))
684 ::CloseHandle(imp.handle);
685 imp.handle = nullptr;
686 }
687
688 return error_code();
689}
690
691system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
692{
693 void* extra_data = get_dir_itr_imp_extra_data(&imp);
694 const void* current_data = static_cast< const unsigned char* >(extra_data) + imp.current_offset;
695 switch (imp.extra_data_format)
696 {
697 case file_id_extd_dir_info_format:
698 {
699 const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data);
700 if (data->NextEntryOffset == 0u)
701 {
702 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))
703 {
704 DWORD error = ::GetLastError();
705
706 dir_itr_close(imp);
707 if (error == ERROR_NO_MORE_FILES)
708 goto done;
709
710 return system::error_code(error, system::system_category());
711 }
712
713 imp.current_offset = 0u;
714 data = static_cast< const file_id_extd_dir_info* >(extra_data);
715 }
716 else
717 {
718 imp.current_offset += data->NextEntryOffset;
719 data = reinterpret_cast< const file_id_extd_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
720 }
721
722 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
723 set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf);
724 }
725 break;
726
727 case file_full_dir_info_format:
728 {
729 const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data);
730 if (data->NextEntryOffset == 0u)
731 {
732 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))
733 {
734 DWORD error = ::GetLastError();
735
736 dir_itr_close(imp);
737 if (error == ERROR_NO_MORE_FILES)
738 goto done;
739
740 return system::error_code(error, system::system_category());
741 }
742
743 imp.current_offset = 0u;
744 data = static_cast< const file_full_dir_info* >(extra_data);
745 }
746 else
747 {
748 imp.current_offset += data->NextEntryOffset;
749 data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
750 }
751
752 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
753 set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
754 }
755 break;
756
757 case file_id_both_dir_info_format:
758 {
759 const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data);
760 if (data->NextEntryOffset == 0u)
761 {
762 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))
763 {
764 DWORD error = ::GetLastError();
765
766 dir_itr_close(imp);
767 if (error == ERROR_NO_MORE_FILES)
768 goto done;
769
770 return system::error_code(error, system::system_category());
771 }
772
773 imp.current_offset = 0u;
774 data = static_cast< const file_id_both_dir_info* >(extra_data);
775 }
776 else
777 {
778 imp.current_offset += data->NextEntryOffset;
779 data = reinterpret_cast< const file_id_both_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
780 }
781
782 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
783 set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
784 }
785 break;
786
787 default:
788 {
789 const file_directory_information* data = static_cast< const file_directory_information* >(current_data);
790 if (data->NextEntryOffset == 0u)
791 {
792 io_status_block iosb;
793 boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api)
794 (
795 imp.handle,
796 nullptr, // Event
797 nullptr, // ApcRoutine
798 nullptr, // ApcContext
799 &iosb,
800 extra_data,
801 dir_itr_extra_size,
802 file_directory_information_class,
803 FALSE, // ReturnSingleEntry
804 nullptr, // FileName
805 FALSE // RestartScan
806 );
807
808 if (!NT_SUCCESS(status))
809 {
810 dir_itr_close(imp);
811 if (status == STATUS_NO_MORE_FILES)
812 goto done;
813
814 return system::error_code(translate_ntstatus(status), system::system_category());
815 }
816
817 imp.current_offset = 0u;
818 data = static_cast< const file_directory_information* >(extra_data);
819 }
820 else
821 {
822 imp.current_offset += data->NextEntryOffset;
823 data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset);
824 }
825
826 filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
827 set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf);
828 }
829 break;
830 }
831
832done:
833 return system::error_code();
834}
835
836//! Returns \c true if the error code indicates that the OS or the filesystem does not support a particular directory info class
837inline bool is_dir_info_class_not_supported(DWORD error)
838{
839 // Some mounted filesystems may not support FILE_ID_128 identifiers, which will cause
840 // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER,
841 // even though in general the operation is supported by the kernel. SMBv1 returns a special error
842 // code ERROR_INVALID_LEVEL in this case.
843 // Some other filesystems also don't implement other info classes and return ERROR_INVALID_PARAMETER
844 // (e.g. see https://github.com/boostorg/filesystem/issues/266), ERROR_GEN_FAILURE, ERROR_INVALID_FUNCTION
845 // or ERROR_INTERNAL_ERROR (https://github.com/boostorg/filesystem/issues/286). Treat these error codes
846 // as "non-permanent", even though ERROR_INVALID_PARAMETER is also returned if GetFileInformationByHandleEx
847 // in general does not support a certain info class. Worst case, we will make extra syscalls on directory
848 // iterator construction.
849 // Also note that Wine returns ERROR_CALL_NOT_IMPLEMENTED for unimplemented info classes, and
850 // up until 7.21 it didn't implement FileIdExtdDirectoryRestartInfo and FileFullDirectoryRestartInfo.
851 // (https://bugs.winehq.org/show_bug.cgi?id=53590)
852 return error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER ||
853 error == ERROR_INVALID_LEVEL || error == ERROR_CALL_NOT_IMPLEMENTED ||
854 error == ERROR_GEN_FAILURE || error == ERROR_INVALID_FUNCTION ||
855 error == ERROR_INTERNAL_ERROR;
856}
857
858system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf)
859{
860 boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp());
861 if (BOOST_UNLIKELY(!pimpl))
862 return make_error_code(system::errc::not_enough_memory);
863
864 GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api);
865
866 unique_handle h;
867 HANDLE iterator_handle;
868 bool close_handle = true;
869 if (params != nullptr && params->dir_handle != INVALID_HANDLE_VALUE)
870 {
871 // Operate on externally provided handle, which must be a directory handle
872 iterator_handle = params->dir_handle;
873 close_handle = params->close_handle;
874 }
875 else
876 {
877 DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
878 if ((opts & directory_options::_detail_no_follow) != directory_options::none)
879 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
880
881 h = create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, flags);
882 if (BOOST_UNLIKELY(!h))
883 {
884 return_last_error:
885 DWORD error = ::GetLastError();
886 return system::error_code(error, system::system_category());
887 }
888
889 iterator_handle = h.get();
890
891 if (BOOST_LIKELY(get_file_information_by_handle_ex != nullptr))
892 {
893 file_attribute_tag_info info;
894 BOOL res = get_file_information_by_handle_ex(iterator_handle, file_attribute_tag_info_class, &info, sizeof(info));
895 if (BOOST_UNLIKELY(!res))
896 {
897 // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER. See the comment in symlink_status.
898 DWORD error = ::GetLastError();
899 if (error == ERROR_INVALID_PARAMETER || error == ERROR_NOT_SUPPORTED)
900 goto use_get_file_information_by_handle;
901
902 return system::error_code(error, system::system_category());
903 }
904
905 if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
906 return make_error_code(system::errc::not_a_directory);
907
908 if ((opts & directory_options::_detail_no_follow) != directory_options::none)
909 {
910 if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag))
911 return make_error_code(system::errc::too_many_symbolic_link_levels);
912 }
913 }
914 else
915 {
916 use_get_file_information_by_handle:
917 BY_HANDLE_FILE_INFORMATION info;
918 BOOL res = ::GetFileInformationByHandle(iterator_handle, &info);
919 if (BOOST_UNLIKELY(!res))
920 goto return_last_error;
921
922 if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u))
923 return make_error_code(system::errc::not_a_directory);
924
925 if ((opts & directory_options::_detail_no_follow) != directory_options::none && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u)
926 {
927 error_code ec;
928 const ULONG reparse_point_tag = detail::get_reparse_point_tag_ioctl(iterator_handle, dir, &ec);
929 if (BOOST_UNLIKELY(!!ec))
930 return ec;
931
932 if (detail::is_reparse_point_tag_a_symlink(reparse_point_tag))
933 return make_error_code(system::errc::too_many_symbolic_link_levels);
934 }
935 }
936 }
937
938 void* extra_data = get_dir_itr_imp_extra_data(pimpl.get());
939 switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format))
940 {
941 case file_id_extd_dir_info_format:
942 {
943 if (!get_file_information_by_handle_ex(iterator_handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size))
944 {
945 DWORD error = ::GetLastError();
946
947 if (is_dir_info_class_not_supported(error))
948 {
949 // Fall back to file_full_dir_info_format.
950 if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
951 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format);
952 goto fallback_to_file_full_dir_info_format;
953 }
954
955 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
956 goto done;
957
958 return system::error_code(error, system::system_category());
959 }
960
961 pimpl->extra_data_format = file_id_extd_dir_info_format;
962
963 const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(extra_data);
964 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
965
966 set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf);
967 }
968 break;
969
970 case file_full_dir_info_format:
971 fallback_to_file_full_dir_info_format:
972 {
973 if (!get_file_information_by_handle_ex(iterator_handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size))
974 {
975 DWORD error = ::GetLastError();
976
977 if (is_dir_info_class_not_supported(error))
978 {
979 // Fall back to file_id_both_dir_info
980 if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
981 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format);
982 goto fallback_to_file_id_both_dir_info_format;
983 }
984
985 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
986 goto done;
987
988 return system::error_code(error, system::system_category());
989 }
990
991 pimpl->extra_data_format = file_full_dir_info_format;
992
993 const file_full_dir_info* data = static_cast< const file_full_dir_info* >(extra_data);
994 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
995
996 set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
997 }
998 break;
999
1000 case file_id_both_dir_info_format:
1001 fallback_to_file_id_both_dir_info_format:
1002 {
1003 if (!get_file_information_by_handle_ex(iterator_handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size))
1004 {
1005 DWORD error = ::GetLastError();
1006
1007 if (is_dir_info_class_not_supported(error))
1008 {
1009 // Fall back to file_directory_information
1010 if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED)
1011 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_directory_information_format);
1012 goto fallback_to_file_directory_information_format;
1013 }
1014
1015 if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND)
1016 goto done;
1017
1018 return system::error_code(error, system::system_category());
1019 }
1020
1021 pimpl->extra_data_format = file_id_both_dir_info_format;
1022
1023 const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(extra_data);
1024 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
1025
1026 set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
1027 }
1028 break;
1029
1030 default:
1031 fallback_to_file_directory_information_format:
1032 {
1033 NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api);
1034 if (BOOST_UNLIKELY(!nt_query_directory_file))
1035 return error_code(ERROR_NOT_SUPPORTED, system_category());
1036
1037 io_status_block iosb;
1038 boost::winapi::NTSTATUS_ status = nt_query_directory_file
1039 (
1040 iterator_handle,
1041 nullptr, // Event
1042 nullptr, // ApcRoutine
1043 nullptr, // ApcContext
1044 &iosb,
1045 extra_data,
1046 dir_itr_extra_size,
1047 file_directory_information_class,
1048 FALSE, // ReturnSingleEntry
1049 nullptr, // FileName
1050 TRUE // RestartScan
1051 );
1052
1053 if (!NT_SUCCESS(status))
1054 {
1055 // Note: an empty root directory has no "." or ".." entries, so this
1056 // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW
1057 // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we
1058 // do not consider an error. It is treated as eof instead.
1059 if (status == STATUS_NO_MORE_FILES || status == STATUS_NO_SUCH_FILE)
1060 goto done;
1061
1062 return error_code(translate_ntstatus(status), system_category());
1063 }
1064
1065 pimpl->extra_data_format = file_directory_information_format;
1066
1067 const file_directory_information* data = static_cast< const file_directory_information* >(extra_data);
1068 first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR));
1069
1070 set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf);
1071 }
1072 break;
1073 }
1074
1075 pimpl->handle = iterator_handle;
1076 h.release();
1077 pimpl->close_handle = close_handle;
1078
1079done:
1080 imp.swap(pimpl);
1081 return system::error_code();
1082}
1083
1084BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND;
1085
1086#endif // BOOST_WINDOWS_API
1087
1088} // namespace
1089
1090#if defined(BOOST_WINDOWS_API)
1091
1092//! Initializes directory iterator implementation
1093void init_directory_iterator_impl() noexcept
1094{
1095 if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != nullptr)
1096 {
1097 // Enable the latest format we support. It will get downgraded, if needed, as we attempt
1098 // to create the directory iterator the first time.
1099 filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_extd_dir_info_format);
1100 }
1101}
1102
1103#endif // defined(BOOST_WINDOWS_API)
1104
1105BOOST_FILESYSTEM_DECL
1106dir_itr_imp::~dir_itr_imp() noexcept
1107{
1108 dir_itr_close(imp&: *this);
1109}
1110
1111BOOST_FILESYSTEM_DECL
1112void directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, directory_iterator_params* params, system::error_code* ec)
1113{
1114 // At most one of the two options may be specified, and follow_directory_symlink is ignored for directory_iterator.
1115 BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow));
1116
1117 if (BOOST_UNLIKELY(p.empty()))
1118 {
1119 emit_error(error_num: not_found_error_code, p, ec, message: "boost::filesystem::directory_iterator::construct");
1120 return;
1121 }
1122
1123 if (ec)
1124 ec->clear();
1125
1126 try
1127 {
1128 boost::intrusive_ptr< detail::dir_itr_imp > imp;
1129 path filename;
1130 file_status file_stat, symlink_file_stat;
1131 system::error_code result = dir_itr_create(imp, dir: p, opts, params, first_filename&: filename, file_stat, symlink_file_stat);
1132
1133 while (true)
1134 {
1135 if (result)
1136 {
1137 if (result != make_error_condition(e: system::errc::permission_denied) ||
1138 (opts & directory_options::skip_permission_denied) == directory_options::none)
1139 {
1140 if (!ec)
1141 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p, result));
1142 *ec = result;
1143 }
1144
1145 return;
1146 }
1147
1148 if (imp->handle == nullptr) // eof, make end
1149 return;
1150
1151 // Not eof
1152 const path::string_type::value_type* filename_str = filename.c_str();
1153 if (!(filename_str[0] == path::dot // dot or dot-dot
1154 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
1155 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
1156 {
1157 path full_path(p);
1158 path_algorithms::append_v4(left&: full_path, right: filename);
1159 imp->dir_entry.assign_with_status
1160 (
1161 p: static_cast< path&& >(full_path),
1162 st: file_stat,
1163 symlink_st: symlink_file_stat
1164 );
1165 it.m_imp.swap(rhs&: imp);
1166 return;
1167 }
1168
1169 // If dot or dot-dot name produced by the underlying API, skip it until the first actual file
1170 result = dir_itr_increment(imp&: *imp, filename, sf&: file_stat, symlink_sf&: symlink_file_stat);
1171 }
1172 }
1173 catch (std::bad_alloc&)
1174 {
1175 if (!ec)
1176 throw;
1177
1178 *ec = make_error_code(e: system::errc::not_enough_memory);
1179 it.m_imp.reset();
1180 }
1181}
1182
1183BOOST_FILESYSTEM_DECL
1184void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
1185{
1186 BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
1187
1188 if (ec)
1189 ec->clear();
1190
1191 try
1192 {
1193 path filename;
1194 file_status file_stat, symlink_file_stat;
1195 system::error_code increment_ec;
1196
1197 while (true)
1198 {
1199 increment_ec = dir_itr_increment(imp&: *it.m_imp, filename, sf&: file_stat, symlink_sf&: symlink_file_stat);
1200
1201 if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
1202 {
1203 boost::intrusive_ptr< detail::dir_itr_imp > imp;
1204 imp.swap(rhs&: it.m_imp);
1205 path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900
1206 if (!ec)
1207 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++", error_path, increment_ec));
1208
1209 *ec = increment_ec;
1210 return;
1211 }
1212
1213 if (it.m_imp->handle == nullptr) // eof, make end
1214 {
1215 it.m_imp.reset();
1216 return;
1217 }
1218
1219 const path::string_type::value_type* filename_str = filename.c_str();
1220 if (!(filename_str[0] == path::dot // !(dot or dot-dot)
1221 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
1222 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
1223 {
1224 it.m_imp->dir_entry.replace_filename_with_status(p: filename, st: file_stat, symlink_st: symlink_file_stat);
1225 return;
1226 }
1227 }
1228 }
1229 catch (std::bad_alloc&)
1230 {
1231 if (!ec)
1232 throw;
1233
1234 it.m_imp.reset();
1235 *ec = make_error_code(e: system::errc::not_enough_memory);
1236 }
1237}
1238
1239//--------------------------------------------------------------------------------------//
1240// //
1241// recursive_directory_iterator //
1242// //
1243//--------------------------------------------------------------------------------------//
1244
1245BOOST_FILESYSTEM_DECL
1246void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, directory_options opts, system::error_code* ec)
1247{
1248 // At most one of the two options may be specified
1249 BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow));
1250
1251 if (ec)
1252 ec->clear();
1253
1254 directory_iterator dir_it;
1255 detail::directory_iterator_construct(it&: dir_it, p: dir_path, opts, params: nullptr, ec);
1256 if ((ec && *ec) || dir_it == directory_iterator())
1257 return;
1258
1259 boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
1260 if (!ec)
1261 {
1262 imp = new detail::recur_dir_itr_imp(opts);
1263 }
1264 else
1265 {
1266 imp = new (std::nothrow) detail::recur_dir_itr_imp(opts);
1267 if (BOOST_UNLIKELY(!imp))
1268 {
1269 *ec = make_error_code(e: system::errc::not_enough_memory);
1270 return;
1271 }
1272 }
1273
1274 try
1275 {
1276 imp->m_stack.push_back(x: std::move(dir_it));
1277 it.m_imp.swap(rhs&: imp);
1278 }
1279 catch (std::bad_alloc&)
1280 {
1281 if (ec)
1282 {
1283 *ec = make_error_code(e: system::errc::not_enough_memory);
1284 return;
1285 }
1286
1287 throw;
1288 }
1289}
1290
1291namespace {
1292
1293void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
1294{
1295 imp->m_stack.pop_back();
1296
1297 while (!imp->m_stack.empty())
1298 {
1299 directory_iterator& dir_it = imp->m_stack.back();
1300 system::error_code increment_ec;
1301 detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec);
1302 if (!increment_ec && dir_it != directory_iterator())
1303 break;
1304
1305 imp->m_stack.pop_back();
1306 }
1307}
1308
1309} // namespace
1310
1311BOOST_FILESYSTEM_DECL
1312void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
1313{
1314 BOOST_ASSERT_MSG(!it.is_end(), "pop() on end recursive_directory_iterator");
1315 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
1316
1317 if (ec)
1318 ec->clear();
1319
1320 imp->m_stack.pop_back();
1321
1322 while (true)
1323 {
1324 if (imp->m_stack.empty())
1325 {
1326 it.m_imp.reset(); // done, so make end iterator
1327 break;
1328 }
1329
1330 directory_iterator& dir_it = imp->m_stack.back();
1331 system::error_code increment_ec;
1332 detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec);
1333 if (BOOST_UNLIKELY(!!increment_ec))
1334 {
1335 if ((imp->m_options & directory_options::pop_on_error) == directory_options::none)
1336 {
1337 // Make an end iterator on errors
1338 it.m_imp.reset();
1339 }
1340 else
1341 {
1342 recursive_directory_iterator_pop_on_error(imp);
1343
1344 if (imp->m_stack.empty())
1345 it.m_imp.reset(); // done, so make end iterator
1346 }
1347
1348 if (!ec)
1349 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec));
1350
1351 *ec = increment_ec;
1352 return;
1353 }
1354
1355 if (dir_it != directory_iterator())
1356 break;
1357
1358 imp->m_stack.pop_back();
1359 }
1360}
1361
1362BOOST_FILESYSTEM_DECL
1363void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
1364{
1365 enum push_directory_result : unsigned int
1366 {
1367 directory_not_pushed = 0u,
1368 directory_pushed = 1u,
1369 keep_depth = 1u << 1u
1370 };
1371
1372 struct local
1373 {
1374 //! Attempts to descend into a directory
1375 static push_directory_result push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec)
1376 {
1377 push_directory_result result = directory_not_pushed;
1378 try
1379 {
1380 // Discover if the iterator is for a directory that needs to be recursed into,
1381 // taking symlinks and options into account.
1382
1383 if ((imp->m_options & directory_options::_detail_no_push) != directory_options::none)
1384 {
1385 imp->m_options &= ~directory_options::_detail_no_push;
1386 return result;
1387 }
1388
1389 file_type symlink_ft = status_error;
1390
1391#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1392 int parentdir_fd = -1;
1393 path dir_it_filename;
1394#elif defined(BOOST_WINDOWS_API)
1395 unique_handle direntry_handle;
1396#endif
1397
1398 // If we are not recursing into symlinks, we are going to have to know if the
1399 // stack top is a symlink, so get symlink_status and verify no error occurred.
1400 if ((imp->m_options & directory_options::follow_directory_symlink) == directory_options::none ||
1401 (imp->m_options & directory_options::skip_dangling_symlinks) != directory_options::none)
1402 {
1403#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1404 directory_iterator const& dir_it = imp->m_stack.back();
1405 if (filesystem::type_present(f: dir_it->m_symlink_status))
1406 {
1407 symlink_ft = dir_it->m_symlink_status.type();
1408 }
1409 else
1410 {
1411 parentdir_fd = dir_itr_fd(imp: *dir_it.m_imp, ec);
1412 if (ec)
1413 return result;
1414
1415 dir_it_filename = detail::path_algorithms::filename_v4(p: dir_it->path());
1416
1417 symlink_ft = detail::symlink_status_impl(p: dir_it_filename, ec: &ec, basedir_fd: parentdir_fd).type();
1418 if (ec)
1419 return result;
1420 }
1421#elif defined(BOOST_WINDOWS_API)
1422 directory_iterator const& dir_it = imp->m_stack.back();
1423 if (filesystem::type_present(dir_it->m_symlink_status))
1424 {
1425 symlink_ft = dir_it->m_symlink_status.type();
1426 }
1427 else
1428 {
1429 boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
1430 (
1431 direntry_handle,
1432 static_cast< HANDLE >(dir_it.m_imp->handle),
1433 detail::path_algorithms::filename_v4(dir_it->path()),
1434 0u, // FileAttributes
1435 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
1436 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1437 FILE_OPEN,
1438 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
1439 );
1440
1441 if (NT_SUCCESS(status))
1442 {
1443 symlink_ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type();
1444 }
1445 else if (status == STATUS_NOT_IMPLEMENTED)
1446 {
1447 symlink_ft = dir_it->symlink_file_type(ec);
1448 }
1449 else
1450 {
1451 if (!not_found_ntstatus(status))
1452 ec.assign(translate_ntstatus(status), system::system_category());
1453
1454 return result;
1455 }
1456
1457 if (ec)
1458 return result;
1459 }
1460#else
1461 symlink_ft = imp->m_stack.back()->symlink_file_type(ec);
1462 if (ec)
1463 return result;
1464#endif
1465 }
1466
1467 // Logic for following predicate was contributed by Daniel Aarno to handle cyclic
1468 // symlinks correctly and efficiently, fixing ticket #5652.
1469 // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink
1470 // || !is_symlink(m_stack.back()->symlink_status()))
1471 // && is_directory(m_stack.back()->status())) ...
1472 // The predicate code has since been rewritten to pass error_code arguments,
1473 // per ticket #5653.
1474
1475 if ((imp->m_options & directory_options::follow_directory_symlink) != directory_options::none || symlink_ft != symlink_file)
1476 {
1477 directory_iterator const& dir_it = imp->m_stack.back();
1478
1479 // Don't query the file type from the filesystem yet, if not known. We will use dir_it for that below.
1480 file_type ft = dir_it->m_status.type();
1481 if (ft != status_error && ft != directory_file)
1482 return result;
1483
1484#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1485 if (parentdir_fd < 0)
1486 {
1487 parentdir_fd = dir_itr_fd(imp: *dir_it.m_imp, ec);
1488 if (ec)
1489 return result;
1490
1491 dir_it_filename = detail::path_algorithms::filename_v4(p: dir_it->path());
1492 }
1493
1494 // Try to open the file as a directory right away. This effectively tests whether the file is a directory, and, if it is, opens the directory in one system call.
1495 detail::directory_iterator_params params{ .dir_fd: detail::openat_directory(basedir_fd: parentdir_fd, p: dir_it_filename, opts: imp->m_options, ec) };
1496 if (!!ec)
1497 {
1498 if
1499 (
1500 // Skip non-directory files
1501 ec == system::error_code(ENOTDIR, system::system_category()) ||
1502 (
1503 // Skip dangling symlink, if requested by options
1504 ec == system::error_code(ENOENT, system::system_category()) && symlink_ft == symlink_file &&
1505 (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)
1506 )
1507 )
1508 {
1509 ec.clear();
1510 }
1511
1512 return result;
1513 }
1514#else // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1515#if defined(BOOST_WINDOWS_API)
1516 if (!!direntry_handle && symlink_ft == symlink_file)
1517 {
1518 // Close the symlink to reopen the target file below
1519 direntry_handle.reset();
1520 }
1521
1522 if (!direntry_handle)
1523 {
1524 boost::winapi::NTSTATUS_ status = nt_create_file_handle_at
1525 (
1526 direntry_handle,
1527 static_cast< HANDLE >(dir_it.m_imp->handle),
1528 detail::path_algorithms::filename_v4(dir_it->path()),
1529 0u, // FileAttributes
1530 FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE,
1531 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1532 FILE_OPEN,
1533 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
1534 );
1535
1536 if (NT_SUCCESS(status))
1537 {
1538 goto get_file_type_by_handle;
1539 }
1540 else if (status == STATUS_NOT_IMPLEMENTED)
1541 {
1542 ft = dir_it->file_type(ec);
1543 }
1544 else
1545 {
1546 ec.assign(translate_ntstatus(status), system::system_category());
1547 }
1548 }
1549 else
1550 {
1551 get_file_type_by_handle:
1552 ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type();
1553 }
1554#else // defined(BOOST_WINDOWS_API)
1555 if (ft == status_error)
1556 ft = dir_it->file_type(ec);
1557#endif // defined(BOOST_WINDOWS_API)
1558
1559 if (BOOST_UNLIKELY(!!ec))
1560 {
1561 if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file &&
1562 (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
1563 {
1564 // Skip dangling symlink and continue iteration on the current depth level
1565 ec.clear();
1566 }
1567
1568 return result;
1569 }
1570
1571 if (ft != directory_file)
1572 return result;
1573#endif // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1574
1575 if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))
1576 {
1577 // We cannot let depth to overflow
1578 ec = make_error_code(e: system::errc::value_too_large);
1579 // When depth overflow happens, avoid popping the current directory iterator
1580 // and attempt to continue iteration on the current depth.
1581 result = keep_depth;
1582 return result;
1583 }
1584
1585#if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS)
1586 directory_iterator next;
1587 detail::directory_iterator_construct(it&: next, p: dir_it->path(), opts: imp->m_options, params: &params, ec: &ec);
1588#elif defined(BOOST_WINDOWS_API)
1589 detail::directory_iterator_params params;
1590 params.dir_handle = direntry_handle.get();
1591 params.close_handle = true;
1592 directory_iterator next;
1593 detail::directory_iterator_construct(next, dir_it->path(), imp->m_options, &params, &ec);
1594#else
1595 directory_iterator next(dir_it->path(), imp->m_options, ec);
1596#endif
1597 if (BOOST_LIKELY(!ec))
1598 {
1599#if defined(BOOST_WINDOWS_API)
1600 direntry_handle.release();
1601#endif
1602 if (!next.is_end())
1603 {
1604 imp->m_stack.push_back(x: std::move(next)); // may throw
1605 return directory_pushed;
1606 }
1607 }
1608 }
1609 }
1610 catch (std::bad_alloc&)
1611 {
1612 ec = make_error_code(e: system::errc::not_enough_memory);
1613 }
1614
1615 return result;
1616 }
1617 };
1618
1619 BOOST_ASSERT_MSG(!it.is_end(), "increment() on end recursive_directory_iterator");
1620 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
1621
1622 if (ec)
1623 ec->clear();
1624
1625 system::error_code local_ec;
1626
1627 // if various conditions are met, push a directory_iterator into the iterator stack
1628 push_directory_result push_result = local::push_directory(imp, ec&: local_ec);
1629 if (push_result == directory_pushed)
1630 return;
1631
1632 // report errors if any
1633 if (BOOST_UNLIKELY(!!local_ec))
1634 {
1635 on_error:
1636 if ((imp->m_options & directory_options::pop_on_error) == directory_options::none)
1637 {
1638 // Make an end iterator on errors
1639 it.m_imp.reset();
1640 }
1641 else
1642 {
1643 if ((push_result & keep_depth) != 0u)
1644 {
1645 system::error_code increment_ec;
1646 directory_iterator& dir_it = imp->m_stack.back();
1647 detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec);
1648 if (!increment_ec && !dir_it.is_end())
1649 goto on_error_return;
1650 }
1651
1652 recursive_directory_iterator_pop_on_error(imp);
1653
1654 if (imp->m_stack.empty())
1655 it.m_imp.reset(); // done, so make end iterator
1656 }
1657
1658 on_error_return:
1659 if (!ec)
1660 BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec));
1661
1662 *ec = local_ec;
1663 return;
1664 }
1665
1666 // Do the actual increment operation on the top iterator in the iterator
1667 // stack, popping the stack if necessary, until either the stack is empty or a
1668 // non-end iterator is reached.
1669 while (true)
1670 {
1671 if (imp->m_stack.empty())
1672 {
1673 it.m_imp.reset(); // done, so make end iterator
1674 break;
1675 }
1676
1677 directory_iterator& dir_it = imp->m_stack.back();
1678 detail::directory_iterator_increment(it&: dir_it, ec: &local_ec);
1679 if (BOOST_UNLIKELY(!!local_ec))
1680 goto on_error;
1681
1682 if (!dir_it.is_end())
1683 break;
1684
1685 imp->m_stack.pop_back();
1686 }
1687}
1688
1689} // namespace detail
1690
1691} // namespace filesystem
1692} // namespace boost
1693
1694#include <boost/filesystem/detail/footer.hpp>
1695

source code of boost/libs/filesystem/src/directory.cpp