]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
20effc67 1// directory.cpp --------------------------------------------------------------------//
92f5a8d4
TL
2
3// Copyright 2002-2009, 2014 Beman Dawes
4// Copyright 2001 Dietmar Kuehl
1e59de90 5// Copyright 2019, 2022 Andrey Semashev
92f5a8d4
TL
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
20effc67 14#include "platform_config.hpp"
92f5a8d4 15
1e59de90
TL
16#include <boost/throw_exception.hpp>
17#include <boost/filesystem/config.hpp>
92f5a8d4
TL
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
1e59de90 27#include <new> // std::nothrow, std::bad_alloc
92f5a8d4
TL
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
1e59de90
TL
37#include <sys/types.h>
38#include <sys/stat.h>
92f5a8d4
TL
39#include <dirent.h>
40#include <unistd.h>
41#include <fcntl.h>
42
1e59de90
TL
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)
92f5a8d4
TL
50#define BOOST_FILESYSTEM_USE_READDIR_R
51#endif
52
1e59de90
TL
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
92f5a8d4
TL
61#else // BOOST_WINDOWS_API
62
63#include <cwchar>
64#include <windows.h>
1e59de90 65#include <boost/winapi/basic_types.hpp> // NTSTATUS_
92f5a8d4
TL
66
67#include "windows_tools.hpp"
68
1e59de90 69#endif // BOOST_WINDOWS_API
92f5a8d4 70
1e59de90 71#include "atomic_tools.hpp"
92f5a8d4 72#include "error_handling.hpp"
1e59de90 73#include "private_config.hpp"
92f5a8d4
TL
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
1e59de90 80#if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present
92f5a8d4
TL
81#define BOOST_FILESYSTEM_STATUS_CACHE
82#endif
83
84namespace fs = boost::filesystem;
85using boost::system::error_code;
86using boost::system::system_category;
87
88namespace boost {
89namespace filesystem {
90
91//--------------------------------------------------------------------------------------//
92// //
93// directory_entry //
94// //
95//--------------------------------------------------------------------------------------//
96
97BOOST_FILESYSTEM_DECL
98file_status directory_entry::get_status(system::error_code* ec) const
99{
1e59de90 100 if (!status_known(m_status))
92f5a8d4 101 {
1e59de90
TL
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 }
92f5a8d4 115 }
1e59de90 116 else if (ec)
92f5a8d4 117 {
1e59de90 118 ec->clear();
92f5a8d4 119 }
92f5a8d4 120
1e59de90 121 return m_status;
92f5a8d4
TL
122}
123
124BOOST_FILESYSTEM_DECL
125file_status directory_entry::get_symlink_status(system::error_code* ec) const
126{
1e59de90
TL
127 if (!status_known(m_symlink_status))
128 m_symlink_status = detail::symlink_status(m_path, ec);
129 else if (ec)
130 ec->clear();
92f5a8d4 131
1e59de90 132 return m_symlink_status;
92f5a8d4
TL
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
139namespace path_traits {
140
1e59de90
TL
141BOOST_FILESYSTEM_DECL
142void dispatch(directory_entry const& de,
92f5a8d4 143#ifdef BOOST_WINDOWS_API
1e59de90 144 std::wstring& to,
92f5a8d4 145#else
1e59de90 146 std::string& to,
92f5a8d4 147#endif
1e59de90 148 codecvt_type const&)
92f5a8d4 149{
1e59de90 150 to = de.path().native();
92f5a8d4
TL
151}
152
1e59de90
TL
153BOOST_FILESYSTEM_DECL
154void dispatch(directory_entry const& de,
92f5a8d4 155#ifdef BOOST_WINDOWS_API
1e59de90 156 std::wstring& to
92f5a8d4 157#else
1e59de90 158 std::string& to
92f5a8d4 159#endif
1e59de90 160)
92f5a8d4 161{
1e59de90 162 to = de.path().native();
92f5a8d4
TL
163}
164
165} // namespace path_traits
166
167//--------------------------------------------------------------------------------------//
168// //
169// directory_iterator //
170// //
171//--------------------------------------------------------------------------------------//
172
173namespace detail {
174
1e59de90
TL
175BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u;
176
177BOOST_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
190BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT
191{
192 std::free(p);
193}
194
195BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) BOOST_NOEXCEPT
196{
197 std::free(p);
198}
199
92f5a8d4
TL
200namespace {
201
1e59de90
TL
202inline 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
92f5a8d4
TL
208#ifdef BOOST_POSIX_API
209
1e59de90
TL
210inline 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
92f5a8d4
TL
227#if defined(BOOST_FILESYSTEM_USE_READDIR_R)
228
229// Obtains maximum length of a path, not including the terminating zero
230inline std::size_t get_path_max()
231{
1e59de90
TL
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 {
92f5a8d4 239#if defined(PATH_MAX)
1e59de90 240 max = PATH_MAX;
92f5a8d4 241#else
1e59de90 242 max = 4096;
92f5a8d4 243#endif
1e59de90
TL
244 }
245 else
246 {
247 max = static_cast< std::size_t >(res); // relative root
92f5a8d4 248#if defined(PATH_MAX)
1e59de90
TL
249 if (max < PATH_MAX)
250 max = PATH_MAX;
92f5a8d4 251#endif
1e59de90 252 }
92f5a8d4 253
1e59de90
TL
254 if ((max + 1) < sizeof(dirent().d_name))
255 max = sizeof(dirent().d_name) - 1;
92f5a8d4 256
1e59de90 257 return max;
92f5a8d4
TL
258}
259
260// Returns maximum length of a path, not including the terminating zero
261inline std::size_t path_max()
262{
1e59de90
TL
263 static const std::size_t max = get_path_max();
264 return max;
92f5a8d4
TL
265}
266
267#endif // BOOST_FILESYSTEM_USE_READDIR_R
268
1e59de90
TL
269// *result set to NULL on end of directory
270#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
271inline
272#endif
273int readdir_impl(dir_itr_imp& imp, struct dirent** result)
92f5a8d4 274{
1e59de90
TL
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;
92f5a8d4
TL
282}
283
1e59de90
TL
284#if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
285
286inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
92f5a8d4 287{
1e59de90
TL
288 return readdir_impl(imp, result);
289}
290
291#else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
292
293int 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
303int readdir_select_impl(dir_itr_imp& imp, struct dirent** result);
304
305typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result);
306
307//! Pointer to the actual implementation of the copy_file_data implementation
308readdir_impl_t* readdir_impl_ptr = &readdir_select_impl;
309
310void init_readdir_impl()
311{
312 readdir_impl_t* impl = &readdir_impl;
313 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
314 impl = &readdir_r_impl;
92f5a8d4 315
1e59de90
TL
316 filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl);
317}
318
319struct readdir_initializer
320{
321 readdir_initializer()
322 {
323 init_readdir_impl();
324 }
325};
92f5a8d4 326
1e59de90
TL
327BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
328const readdir_initializer readdir_init;
329
330int 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
336inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result)
337{
338 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result);
92f5a8d4
TL
339}
340
1e59de90
TL
341#endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
342
343error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
92f5a8d4 344{
1e59de90
TL
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);
92f5a8d4 351
1e59de90 352 filename = result->d_name;
92f5a8d4
TL
353
354#ifdef BOOST_FILESYSTEM_STATUS_CACHE
1e59de90
TL
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);
92f5a8d4 372 }
92f5a8d4 373#else
1e59de90
TL
374 sf = symlink_sf = fs::file_status(fs::status_error);
375#endif
376 return error_code();
377}
378
379error_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 }
92f5a8d4 427#endif
1e59de90
TL
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();
92f5a8d4
TL
450}
451
1e59de90
TL
452BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT;
453
92f5a8d4
TL
454#else // BOOST_WINDOWS_API
455
1e59de90
TL
456inline 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);
92f5a8d4
TL
476 }
477 else
478 {
1e59de90
TL
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
498struct 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).
504struct 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.
520struct 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.
540struct 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.
557struct 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
576enum 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
585extra_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 */
593BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u;
594
595inline 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
609error_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
750done:
751 return error_code();
752}
753
754error_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 }
92f5a8d4
TL
787 }
788 else
789 {
1e59de90
TL
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 }
92f5a8d4 803 }
1e59de90
TL
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
939done:
940 imp.swap(pimpl);
941 return error_code();
92f5a8d4 942}
92f5a8d4 943
1e59de90 944#else // !defined(UNDER_CE)
92f5a8d4 945
1e59de90
TL
946inline 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 }
92f5a8d4 953
1e59de90
TL
954 return error_code();
955}
956
957error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf)
92f5a8d4 958{
1e59de90
TL
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);
92f5a8d4 971
1e59de90
TL
972done:
973 return error_code();
974}
92f5a8d4 975
1e59de90
TL
976error_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))
92f5a8d4 990 {
1e59de90
TL
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());
92f5a8d4 1002 }
92f5a8d4 1003
1e59de90
TL
1004 first_filename = data.cFileName;
1005 set_file_statuses(data.dwFileAttributes, NULL, first_filename, sf, symlink_sf);
92f5a8d4 1006
1e59de90
TL
1007done:
1008 imp.swap(pimpl);
1009 return error_code();
1010}
92f5a8d4 1011
1e59de90 1012#endif // !defined(UNDER_CE)
92f5a8d4 1013
1e59de90
TL
1014BOOST_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
1023void 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 }
92f5a8d4
TL
1031}
1032
1e59de90
TL
1033#endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
1034
92f5a8d4 1035BOOST_FILESYSTEM_DECL
1e59de90
TL
1036dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT
1037{
1038 dir_itr_close(*this);
1039}
1040
1041BOOST_FILESYSTEM_DECL
1042void 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 }
92f5a8d4 1049
1e59de90
TL
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 }
92f5a8d4
TL
1101}
1102
1103BOOST_FILESYSTEM_DECL
1104void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
1105{
1e59de90 1106 BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
92f5a8d4 1107
1e59de90
TL
1108 if (ec)
1109 ec->clear();
92f5a8d4 1110
1e59de90 1111 try
92f5a8d4 1112 {
1e59de90
TL
1113 path filename;
1114 file_status file_stat, symlink_file_stat;
1115 system::error_code increment_ec;
92f5a8d4 1116
1e59de90 1117 while (true)
92f5a8d4 1118 {
1e59de90
TL
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 }
92f5a8d4 1147 }
1e59de90
TL
1148 }
1149 catch (std::bad_alloc&)
1150 {
1151 if (!ec)
1152 throw;
92f5a8d4 1153
92f5a8d4 1154 it.m_imp.reset();
1e59de90 1155 *ec = make_error_code(system::errc::not_enough_memory);
92f5a8d4 1156 }
92f5a8d4
TL
1157}
1158
1159//--------------------------------------------------------------------------------------//
1160// //
1161// recursive_directory_iterator //
1162// //
1163//--------------------------------------------------------------------------------------//
1164
1165BOOST_FILESYSTEM_DECL
1e59de90 1166void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, unsigned int opts, system::error_code* ec)
92f5a8d4 1167{
1e59de90
TL
1168 if (ec)
1169 ec->clear();
92f5a8d4 1170
1e59de90
TL
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;
92f5a8d4 1175
1e59de90
TL
1176 boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
1177 if (!ec)
92f5a8d4 1178 {
1e59de90
TL
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 }
92f5a8d4 1189 }
92f5a8d4 1190
1e59de90
TL
1191 try
1192 {
92f5a8d4 1193#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1e59de90 1194 imp->m_stack.push_back(std::move(dir_it));
92f5a8d4 1195#else
1e59de90 1196 imp->m_stack.push_back(dir_it);
92f5a8d4
TL
1197#endif
1198
1e59de90 1199 it.m_imp.swap(imp);
92f5a8d4 1200 }
1e59de90
TL
1201 catch (std::bad_alloc&)
1202 {
1203 if (ec)
1204 {
1205 *ec = make_error_code(system::errc::not_enough_memory);
1206 return;
1207 }
92f5a8d4 1208
1e59de90
TL
1209 throw;
1210 }
92f5a8d4
TL
1211}
1212
1213namespace {
1214
1215void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
1216{
1e59de90 1217 imp->m_stack.pop_back();
92f5a8d4 1218
1e59de90
TL
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;
92f5a8d4 1226
1e59de90
TL
1227 imp->m_stack.pop_back();
1228 }
92f5a8d4
TL
1229}
1230
1231} // namespace
1232
1233BOOST_FILESYSTEM_DECL
1234void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
1235{
1e59de90
TL
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();
92f5a8d4 1238
1e59de90
TL
1239 if (ec)
1240 ec->clear();
92f5a8d4 1241
1e59de90 1242 imp->m_stack.pop_back();
92f5a8d4 1243
1e59de90 1244 while (true)
92f5a8d4 1245 {
92f5a8d4 1246 if (imp->m_stack.empty())
1e59de90
TL
1247 {
1248 it.m_imp.reset(); // done, so make end iterator
1249 break;
1250 }
92f5a8d4 1251
1e59de90
TL
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 }
92f5a8d4 1276
1e59de90
TL
1277 if (dir_it != directory_iterator())
1278 break;
92f5a8d4 1279
1e59de90
TL
1280 imp->m_stack.pop_back();
1281 }
92f5a8d4
TL
1282}
1283
1284namespace {
1285
1286enum push_directory_result
1287{
1e59de90
TL
1288 directory_not_pushed = 0u,
1289 directory_pushed = 1u,
1290 keep_depth = 1u << 1
92f5a8d4
TL
1291};
1292
1293// Returns: true if push occurs, otherwise false. Always returns false on error.
1294inline push_directory_result recursive_directory_iterator_push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec) BOOST_NOEXCEPT
1295{
1e59de90
TL
1296 push_directory_result result = directory_not_pushed;
1297 try
92f5a8d4 1298 {
1e59de90
TL
1299 // Discover if the iterator is for a directory that needs to be recursed into,
1300 // taking symlinks and options into account.
92f5a8d4 1301
1e59de90 1302 if ((imp->m_options & static_cast< unsigned int >(directory_options::_detail_no_push)) != 0u)
92f5a8d4 1303 {
1e59de90
TL
1304 imp->m_options &= ~static_cast< unsigned int >(directory_options::_detail_no_push);
1305 return result;
92f5a8d4
TL
1306 }
1307
1e59de90 1308 file_status symlink_stat;
92f5a8d4 1309
1e59de90
TL
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 }
92f5a8d4 1319
1e59de90
TL
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.
92f5a8d4 1327
1e59de90
TL
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 {
92f5a8d4 1359#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1e59de90 1360 imp->m_stack.push_back(std::move(next)); // may throw
92f5a8d4 1361#else
1e59de90 1362 imp->m_stack.push_back(next); // may throw
92f5a8d4 1363#endif
1e59de90
TL
1364 return directory_pushed;
1365 }
1366 }
1367 }
1368 catch (std::bad_alloc&)
1369 {
1370 ec = make_error_code(system::errc::not_enough_memory);
92f5a8d4 1371 }
92f5a8d4 1372
1e59de90 1373 return result;
92f5a8d4
TL
1374}
1375
1376} // namespace
1377
1378BOOST_FILESYSTEM_DECL
1379void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
1380{
1e59de90
TL
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();
92f5a8d4 1383
1e59de90
TL
1384 if (ec)
1385 ec->clear();
92f5a8d4 1386
1e59de90 1387 system::error_code local_ec;
92f5a8d4 1388
1e59de90
TL
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;
92f5a8d4 1393
1e59de90
TL
1394 // report errors if any
1395 if (BOOST_UNLIKELY(!!local_ec))
92f5a8d4 1396 {
1e59de90
TL
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 }
92f5a8d4 1419
1e59de90
TL
1420 on_error_return:
1421 if (!ec)
1422 BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec));
92f5a8d4 1423
1e59de90
TL
1424 *ec = local_ec;
1425 return;
92f5a8d4
TL
1426 }
1427
1e59de90
TL
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)
92f5a8d4 1432 {
1e59de90
TL
1433 if (imp->m_stack.empty())
1434 {
1435 it.m_imp.reset(); // done, so make end iterator
1436 break;
1437 }
92f5a8d4 1438
1e59de90
TL
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;
92f5a8d4 1443
1e59de90
TL
1444 if (dir_it != directory_iterator())
1445 break;
92f5a8d4 1446
1e59de90
TL
1447 imp->m_stack.pop_back();
1448 }
92f5a8d4
TL
1449}
1450
1451} // namespace detail
1452
1453} // namespace filesystem
1454} // namespace boost