1 // directory.cpp --------------------------------------------------------------------//
2
3 // Copyright 2002-2009, 2014 Beman Dawes
4 // Copyright 2001 Dietmar Kuehl
5 // Copyright 2019 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/directory.hpp>
17 #include <boost/filesystem/exception.hpp>
18 #include <boost/filesystem/operations.hpp>
19 #include <boost/filesystem/file_status.hpp>
20
21 #include <cstddef>
22 #include <cerrno>
23 #include <cstring>
24 #include <cstdlib> // std::malloc, std::free
25 #include <new> // std::nothrow, std::bad_alloc
26 #include <limits>
27 #include <string>
28 #include <utility> // std::move
29 #include <boost/assert.hpp>
30 #include <boost/system/error_code.hpp>
31 #include <boost/smart_ptr/intrusive_ptr.hpp>
32
33 #ifdef BOOST_POSIX_API
34
35 #include <dirent.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38
39 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS+0 >= 0)\
40 && defined(_SC_THREAD_SAFE_FUNCTIONS)\
41 && !defined(__CYGWIN__)\
42 && !(defined(linux) || defined(__linux) || defined(__linux__))\
43 && !defined(__ANDROID__)\
44 && (!defined(__hpux) || defined(_REENTRANT)) \
45 && (!defined(_AIX) || defined(__THREAD_SAFE))\
46 && !defined(__wasm)
47 #define BOOST_FILESYSTEM_USE_READDIR_R
48 #endif
49
50 #else // BOOST_WINDOWS_API
51
52 #include <cwchar>
53 #include <windows.h>
54
55 #include "windows_tools.hpp"
56
57 #endif // BOOST_WINDOWS_API
58
59 #include "error_handling.hpp"
60
61 // BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in
62 // dir_itr_increment. The config tests are placed here because some of the
63 // macros being tested come from dirent.h.
64 //
65 // TODO: find out what macros indicate dirent::d_type present in more libraries
66 #if defined(BOOST_WINDOWS_API)\
67 || defined(_DIRENT_HAVE_D_TYPE)// defined by GNU C library if d_type present
68 #define BOOST_FILESYSTEM_STATUS_CACHE
69 #endif
70
71 namespace fs = boost::filesystem;
72 using boost::system::error_code;
73 using boost::system::system_category;
74
75 namespace boost {
76 namespace filesystem {
77
78 //--------------------------------------------------------------------------------------//
79 // //
80 // directory_entry //
81 // //
82 //--------------------------------------------------------------------------------------//
83
84 BOOST_FILESYSTEM_DECL
get_status(system::error_code * ec) const85 file_status directory_entry::get_status(system::error_code* ec) const
86 {
87 if (!status_known(m_status))
88 {
89 // optimization: if the symlink status is known, and it isn't a symlink,
90 // then status and symlink_status are identical so just copy the
91 // symlink status to the regular status.
92 if (status_known(m_symlink_status) && !is_symlink(m_symlink_status))
93 {
94 m_status = m_symlink_status;
95 if (ec != 0)
96 ec->clear();
97 }
98 else
99 {
100 m_status = detail::status(m_path, ec);
101 }
102 }
103 else if (ec != 0)
104 {
105 ec->clear();
106 }
107
108 return m_status;
109 }
110
111 BOOST_FILESYSTEM_DECL
get_symlink_status(system::error_code * ec) const112 file_status directory_entry::get_symlink_status(system::error_code* ec) const
113 {
114 if (!status_known(m_symlink_status))
115 m_symlink_status = detail::symlink_status(m_path, ec);
116 else if (ec != 0)
117 ec->clear();
118
119 return m_symlink_status;
120 }
121
122 // dispatch directory_entry supplied here rather than in
123 // <boost/filesystem/path_traits.hpp>, thus avoiding header circularity.
124 // test cases are in operations_unit_test.cpp
125
126 namespace path_traits {
127
dispatch(const directory_entry & de,std::wstring & to,const codecvt_type &)128 void dispatch(const directory_entry& de,
129 #ifdef BOOST_WINDOWS_API
130 std::wstring& to,
131 #else
132 std::string& to,
133 #endif
134 const codecvt_type&)
135 {
136 to = de.path().native();
137 }
138
dispatch(const directory_entry & de,std::wstring & to)139 void dispatch(const directory_entry& de,
140 #ifdef BOOST_WINDOWS_API
141 std::wstring& to
142 #else
143 std::string& to
144 #endif
145 )
146 {
147 to = de.path().native();
148 }
149
150 } // namespace path_traits
151
152 //--------------------------------------------------------------------------------------//
153 // //
154 // directory_iterator //
155 // //
156 //--------------------------------------------------------------------------------------//
157
158 namespace detail {
159
160 namespace {
161
162 #ifdef BOOST_POSIX_API
163
164 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
165
166 // Obtains maximum length of a path, not including the terminating zero
get_path_max()167 inline std::size_t get_path_max()
168 {
169 // this code is based on Stevens and Rago, Advanced Programming in the
170 // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
171 std::size_t max = 0;
172 errno = 0;
173 long res = ::pathconf("/", _PC_PATH_MAX);
174 if (res < 0)
175 {
176 #if defined(PATH_MAX)
177 max = PATH_MAX;
178 #else
179 max = 4096;
180 #endif
181 }
182 else
183 {
184 max = static_cast< std::size_t >(res); // relative root
185 #if defined(PATH_MAX)
186 if (max < PATH_MAX)
187 max = PATH_MAX;
188 #endif
189 }
190
191 if ((max + 1) < sizeof(dirent().d_name))
192 max = sizeof(dirent().d_name) - 1;
193
194 return max;
195 }
196
197 // Returns maximum length of a path, not including the terminating zero
path_max()198 inline std::size_t path_max()
199 {
200 static const std::size_t max = get_path_max();
201 return max;
202 }
203
204 #endif // BOOST_FILESYSTEM_USE_READDIR_R
205
dir_itr_first(void * & handle,void * & buffer,const char * dir,std::string & target,fs::file_status &,fs::file_status &)206 error_code dir_itr_first(void*& handle, void*& buffer,
207 const char* dir, std::string& target,
208 fs::file_status&, fs::file_status&)
209 {
210 if ((handle = ::opendir(dir)) == 0)
211 {
212 const int err = errno;
213 return error_code(err, system_category());
214 }
215 target.assign("."); // string was static but caused trouble
216 // when iteration called from dtor, after
217 // static had already been destroyed
218 return error_code();
219 }
220
221 // *result set to NULL on end of directory
readdir_r_simulator(DIR * dirp,void * & buffer,struct dirent ** result)222 inline int readdir_r_simulator(DIR* dirp, void*& buffer, struct dirent** result)
223 {
224 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
225 errno = 0;
226
227 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
228 {
229 struct dirent* storage = static_cast< struct dirent* >(buffer);
230 if (BOOST_UNLIKELY(!storage))
231 {
232 // According to readdir description, there's no reliable way to predict the length of the d_name string.
233 // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
234 // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
235 // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
236 // in favor of readdir.
237 const std::size_t name_size = path_max();
238 const std::size_t buffer_size = (sizeof(dirent) - sizeof(dirent().d_name)) + name_size + 1; // + 1 for "\0"
239 buffer = storage = static_cast< struct dirent* >(std::malloc(buffer_size));
240 if (BOOST_UNLIKELY(!storage))
241 return boost::system::errc::not_enough_memory;
242 std::memset(storage, 0, buffer_size);
243 }
244
245 return ::readdir_r(dirp, storage, result);
246 }
247 #endif
248
249 errno = 0;
250
251 struct dirent* p = ::readdir(dirp);
252 *result = p;
253 if (!p)
254 return errno;
255 return 0;
256 }
257
dir_itr_increment(void * & handle,void * & buffer,std::string & target,fs::file_status & sf,fs::file_status & symlink_sf)258 error_code dir_itr_increment(void*& handle, void*& buffer,
259 std::string& target, fs::file_status& sf, fs::file_status& symlink_sf)
260 {
261 dirent* result = NULL;
262 int err = readdir_r_simulator(static_cast<DIR*>(handle), buffer, &result);
263 if (BOOST_UNLIKELY(err != 0))
264 return error_code(err, system_category());
265 if (result == NULL)
266 return fs::detail::dir_itr_close(handle, buffer);
267
268 target = result->d_name;
269
270 #ifdef BOOST_FILESYSTEM_STATUS_CACHE
271 if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
272 {
273 sf = symlink_sf = fs::file_status(fs::status_error);
274 }
275 else // filesystem supplies d_type value
276 {
277 if (result->d_type == DT_DIR)
278 sf = symlink_sf = fs::file_status(fs::directory_file);
279 else if (result->d_type == DT_REG)
280 sf = symlink_sf = fs::file_status(fs::regular_file);
281 else if (result->d_type == DT_LNK)
282 {
283 sf = fs::file_status(fs::status_error);
284 symlink_sf = fs::file_status(fs::symlink_file);
285 }
286 else
287 sf = symlink_sf = fs::file_status(fs::status_error);
288 }
289 #else
290 sf = symlink_sf = fs::file_status(fs::status_error);
291 #endif
292 return error_code();
293 }
294
295 #else // BOOST_WINDOWS_API
296
297 error_code dir_itr_first(void*& handle, const fs::path& dir,
298 std::wstring& target, fs::file_status& sf, fs::file_status& symlink_sf)
299 // Note: an empty root directory has no "." or ".." entries, so this
300 // causes a ERROR_FILE_NOT_FOUND error which we do not considered an
301 // error. It is treated as eof instead.
302 {
303 // use a form of search Sebastian Martel reports will work with Win98
304 std::wstring dirpath(dir.wstring());
305 dirpath += (dirpath.empty()
306 || (dirpath[dirpath.size()-1] != L'\\'
307 && dirpath[dirpath.size()-1] != L'/'
308 && dirpath[dirpath.size()-1] != L':'))? L"\\*" : L"*";
309
310 WIN32_FIND_DATAW data;
311 if ((handle = ::FindFirstFileW(dirpath.c_str(), &data))
312 == INVALID_HANDLE_VALUE)
313 {
314 handle = 0; // signal eof
315 DWORD error = ::GetLastError();
316 return error_code( (error == ERROR_FILE_NOT_FOUND
317 // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551
318 || error == ERROR_NO_MORE_FILES)
319 ? 0 : error, system_category() );
320 }
321 target = data.cFileName;
322 if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
323 // reparse points are complex, so don't try to handle them here; instead just mark
324 // them as status_error which causes directory_entry caching to call status()
325 // and symlink_status() which do handle reparse points fully
326 {
327 sf.type(fs::status_error);
328 symlink_sf.type(fs::status_error);
329 }
330 else
331 {
332 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
333 {
334 sf.type(fs::directory_file);
335 symlink_sf.type(fs::directory_file);
336 }
337 else
338 {
339 sf.type(fs::regular_file);
340 symlink_sf.type(fs::regular_file);
341 }
342 sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
343 symlink_sf.permissions(sf.permissions());
344 }
345 return error_code();
346 }
347
348 error_code dir_itr_increment(void*& handle, std::wstring& target, fs::file_status& sf, fs::file_status& symlink_sf)
349 {
350 WIN32_FIND_DATAW data;
351 if (::FindNextFileW(handle, &data)== 0)// fails
352 {
353 DWORD error = ::GetLastError();
354 fs::detail::dir_itr_close(handle);
355 return error_code(error == ERROR_NO_MORE_FILES ? 0 : error, system_category());
356 }
357 target = data.cFileName;
358 if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
359 // reparse points are complex, so don't try to handle them here; instead just mark
360 // them as status_error which causes directory_entry caching to call status()
361 // and symlink_status() which do handle reparse points fully
362 {
363 sf.type(fs::status_error);
364 symlink_sf.type(fs::status_error);
365 }
366 else
367 {
368 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
369 {
370 sf.type(fs::directory_file);
371 symlink_sf.type(fs::directory_file);
372 }
373 else
374 {
375 sf.type(fs::regular_file);
376 symlink_sf.type(fs::regular_file);
377 }
378 sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
379 symlink_sf.permissions(sf.permissions());
380 }
381 return error_code();
382 }
383 #endif
384
385 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code =
386 #ifdef BOOST_WINDOWS_API
387 ERROR_PATH_NOT_FOUND
388 #else
389 ENOENT
390 #endif
391 ;
392
393 } // namespace
394
395 // dir_itr_close is called both from the ~dir_itr_imp()destructor
396 // and dir_itr_increment()
397 BOOST_FILESYSTEM_DECL
dir_itr_close(void * & handle,void * & buffer)398 system::error_code dir_itr_close( // never throws
399 void*& handle
400 #if defined(BOOST_POSIX_API)
401 , void*& buffer
402 #endif
403 ) BOOST_NOEXCEPT
404 {
405 #ifdef BOOST_POSIX_API
406
407 if (buffer != NULL)
408 {
409 std::free(buffer);
410 buffer = NULL;
411 }
412
413 if (handle != NULL)
414 {
415 DIR* h = static_cast<DIR*>(handle);
416 handle = NULL;
417 int err = 0;
418 if (BOOST_UNLIKELY(::closedir(h) != 0))
419 {
420 err = errno;
421 return error_code(err, system_category());
422 }
423 }
424
425 return error_code();
426
427 #else
428
429 if (handle != NULL)
430 {
431 ::FindClose(handle);
432 handle = NULL;
433 }
434 return error_code();
435
436 #endif
437 }
438
439 BOOST_FILESYSTEM_DECL
directory_iterator_construct(directory_iterator & it,const path & p,unsigned int opts,system::error_code * ec)440 void directory_iterator_construct(directory_iterator& it, const path& p, unsigned int opts, system::error_code* ec)
441 {
442 if (error(p.empty() ? not_found_error_code : 0, p, ec,
443 "boost::filesystem::directory_iterator::construct"))
444 {
445 return;
446 }
447
448 boost::intrusive_ptr< detail::dir_itr_imp > imp;
449 if (!ec)
450 {
451 imp = new detail::dir_itr_imp();
452 }
453 else
454 {
455 imp = new (std::nothrow) detail::dir_itr_imp();
456 if (BOOST_UNLIKELY(!imp))
457 {
458 *ec = make_error_code(system::errc::not_enough_memory);
459 return;
460 }
461 }
462
463 try
464 {
465 path::string_type filename;
466 file_status file_stat, symlink_file_stat;
467 error_code result = dir_itr_first(imp->handle,
468 # if defined(BOOST_POSIX_API)
469 imp->buffer,
470 # endif
471 p.c_str(), filename, file_stat, symlink_file_stat);
472
473 if (result)
474 {
475 if (result != make_error_condition(system::errc::permission_denied) ||
476 (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u)
477 {
478 error(result.value(), p,
479 ec, "boost::filesystem::directory_iterator::construct");
480 }
481
482 return;
483 }
484
485 if (imp->handle)
486 {
487 // Not eof
488 it.m_imp.swap(imp);
489 it.m_imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat);
490 const path::string_type::value_type* filename_str = filename.c_str();
491 if (filename_str[0] == path::dot // dot or dot-dot
492 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
493 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))
494 {
495 detail::directory_iterator_increment(it, ec);
496 }
497 }
498 }
499 catch (std::bad_alloc&)
500 {
501 if (!ec)
502 throw;
503
504 *ec = make_error_code(boost::system::errc::not_enough_memory);
505 it.m_imp.reset();
506 }
507 }
508
509 BOOST_FILESYSTEM_DECL
directory_iterator_increment(directory_iterator & it,system::error_code * ec)510 void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
511 {
512 BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
513
514 if (ec)
515 ec->clear();
516
517 try
518 {
519 path::string_type filename;
520 file_status file_stat, symlink_file_stat;
521 system::error_code increment_ec;
522
523 for (;;)
524 {
525 increment_ec = dir_itr_increment(it.m_imp->handle,
526 # if defined(BOOST_POSIX_API)
527 it.m_imp->buffer,
528 # endif
529 filename, file_stat, symlink_file_stat);
530
531 if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
532 {
533 boost::intrusive_ptr< detail::dir_itr_imp > imp;
534 imp.swap(it.m_imp);
535 path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900
536 if (ec == NULL)
537 {
538 BOOST_FILESYSTEM_THROW(
539 filesystem_error("boost::filesystem::directory_iterator::operator++",
540 error_path,
541 increment_ec));
542 }
543 *ec = increment_ec;
544 return;
545 }
546
547 if (it.m_imp->handle == NULL) // eof, make end
548 {
549 it.m_imp.reset();
550 return;
551 }
552
553 const path::string_type::value_type* filename_str = filename.c_str();
554 if (!(filename_str[0] == path::dot // !(dot or dot-dot)
555 && (filename_str[1] == static_cast< path::string_type::value_type >('\0') ||
556 (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0')))))
557 {
558 it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat);
559 return;
560 }
561 }
562 }
563 catch (std::bad_alloc&)
564 {
565 if (!ec)
566 throw;
567
568 it.m_imp.reset();
569 *ec = make_error_code(boost::system::errc::not_enough_memory);
570 }
571 }
572
573 //--------------------------------------------------------------------------------------//
574 // //
575 // recursive_directory_iterator //
576 // //
577 //--------------------------------------------------------------------------------------//
578
579 BOOST_FILESYSTEM_DECL
recursive_directory_iterator_construct(recursive_directory_iterator & it,const path & dir_path,unsigned int opts,system::error_code * ec)580 void recursive_directory_iterator_construct(recursive_directory_iterator& it, const path& dir_path, unsigned int opts, system::error_code* ec)
581 {
582 if (ec)
583 ec->clear();
584
585 directory_iterator dir_it;
586 detail::directory_iterator_construct(dir_it, dir_path, opts, ec);
587 if ((ec && *ec) || dir_it == directory_iterator())
588 return;
589
590 boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
591 if (!ec)
592 {
593 imp = new detail::recur_dir_itr_imp(opts);
594 }
595 else
596 {
597 imp = new (std::nothrow) detail::recur_dir_itr_imp(opts);
598 if (BOOST_UNLIKELY(!imp))
599 {
600 *ec = make_error_code(system::errc::not_enough_memory);
601 return;
602 }
603 }
604
605 try
606 {
607 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
608 imp->m_stack.push_back(std::move(dir_it));
609 #else
610 imp->m_stack.push_back(dir_it);
611 #endif
612
613 it.m_imp.swap(imp);
614 }
615 catch (std::bad_alloc&)
616 {
617 if (ec)
618 {
619 *ec = make_error_code(system::errc::not_enough_memory);
620 return;
621 }
622
623 throw;
624 }
625 }
626
627 namespace {
628
recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp * imp)629 void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
630 {
631 imp->m_stack.pop_back();
632
633 while (!imp->m_stack.empty())
634 {
635 directory_iterator& dir_it = imp->m_stack.back();
636 system::error_code increment_ec;
637 detail::directory_iterator_increment(dir_it, &increment_ec);
638 if (!increment_ec && dir_it != directory_iterator())
639 break;
640
641 imp->m_stack.pop_back();
642 }
643 }
644
645 } // namespace
646
647 BOOST_FILESYSTEM_DECL
recursive_directory_iterator_pop(recursive_directory_iterator & it,system::error_code * ec)648 void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
649 {
650 BOOST_ASSERT_MSG(!it.is_end(), "pop() on end recursive_directory_iterator");
651 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
652
653 if (ec)
654 ec->clear();
655
656 imp->m_stack.pop_back();
657
658 while (true)
659 {
660 if (imp->m_stack.empty())
661 {
662 it.m_imp.reset(); // done, so make end iterator
663 break;
664 }
665
666 directory_iterator& dir_it = imp->m_stack.back();
667 system::error_code increment_ec;
668 detail::directory_iterator_increment(dir_it, &increment_ec);
669 if (BOOST_UNLIKELY(!!increment_ec))
670 {
671 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
672 {
673 // Make an end iterator on errors
674 it.m_imp.reset();
675 }
676 else
677 {
678 recursive_directory_iterator_pop_on_error(imp);
679
680 if (imp->m_stack.empty())
681 it.m_imp.reset(); // done, so make end iterator
682 }
683
684 if (ec == NULL)
685 {
686 BOOST_FILESYSTEM_THROW(
687 filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec));
688 }
689
690 *ec = increment_ec;
691 return;
692 }
693
694 if (dir_it != directory_iterator())
695 break;
696
697 imp->m_stack.pop_back();
698 }
699 }
700
701 namespace {
702
703 enum push_directory_result
704 {
705 directory_not_pushed = 0u,
706 directory_pushed = 1u,
707 keep_depth = 1u << 1
708 };
709
710 // Returns: true if push occurs, otherwise false. Always returns false on error.
recursive_directory_iterator_push_directory(detail::recur_dir_itr_imp * imp,system::error_code & ec)711 inline push_directory_result recursive_directory_iterator_push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec) BOOST_NOEXCEPT
712 {
713 push_directory_result result = directory_not_pushed;
714 try
715 {
716 // Discover if the iterator is for a directory that needs to be recursed into,
717 // taking symlinks and options into account.
718
719 if ((imp->m_options & static_cast< unsigned int >(directory_options::_detail_no_push)) != 0u)
720 {
721 imp->m_options &= ~static_cast< unsigned int >(directory_options::_detail_no_push);
722 return result;
723 }
724
725 file_status symlink_stat;
726
727 // if we are not recursing into symlinks, we are going to have to know if the
728 // stack top is a symlink, so get symlink_status and verify no error occurred
729 if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) == 0u ||
730 (imp->m_options & static_cast< unsigned int >(directory_options::skip_dangling_symlinks)) != 0u)
731 {
732 symlink_stat = imp->m_stack.back()->symlink_status(ec);
733 if (ec)
734 return result;
735 }
736
737 // Logic for following predicate was contributed by Daniel Aarno to handle cyclic
738 // symlinks correctly and efficiently, fixing ticket #5652.
739 // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink
740 // || !is_symlink(m_stack.back()->symlink_status()))
741 // && is_directory(m_stack.back()->status())) ...
742 // The predicate code has since been rewritten to pass error_code arguments,
743 // per ticket #5653.
744
745 if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat))
746 {
747 file_status stat = imp->m_stack.back()->status(ec);
748 if (BOOST_UNLIKELY(!!ec))
749 {
750 if (ec == make_error_condition(system::errc::no_such_file_or_directory) && fs::is_symlink(symlink_stat) &&
751 (imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
752 == static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks))
753 {
754 // Skip dangling symlink and continue iteration on the current depth level
755 ec = error_code();
756 }
757
758 return result;
759 }
760
761 if (!fs::is_directory(stat))
762 return result;
763
764 if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))
765 {
766 // We cannot let depth to overflow
767 ec = make_error_code(system::errc::value_too_large);
768 // When depth overflow happens, avoid popping the current directory iterator
769 // and attempt to continue iteration on the current depth.
770 result = keep_depth;
771 return result;
772 }
773
774 directory_iterator next(imp->m_stack.back()->path(), static_cast< BOOST_SCOPED_ENUM_NATIVE(directory_options) >(imp->m_options), ec);
775 if (!ec && next != directory_iterator())
776 {
777 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
778 imp->m_stack.push_back(std::move(next)); // may throw
779 #else
780 imp->m_stack.push_back(next); // may throw
781 #endif
782 return directory_pushed;
783 }
784 }
785 }
786 catch (std::bad_alloc&)
787 {
788 ec = make_error_code(system::errc::not_enough_memory);
789 }
790
791 return result;
792 }
793
794 } // namespace
795
796 BOOST_FILESYSTEM_DECL
recursive_directory_iterator_increment(recursive_directory_iterator & it,system::error_code * ec)797 void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
798 {
799 BOOST_ASSERT_MSG(!it.is_end(), "increment() on end recursive_directory_iterator");
800 detail::recur_dir_itr_imp* const imp = it.m_imp.get();
801
802 if (ec)
803 ec->clear();
804
805 system::error_code local_ec;
806
807 // if various conditions are met, push a directory_iterator into the iterator stack
808 push_directory_result push_result = recursive_directory_iterator_push_directory(imp, local_ec);
809 if (push_result == directory_pushed)
810 return;
811
812 // report errors if any
813 if (BOOST_UNLIKELY(!!local_ec))
814 {
815 on_error:
816 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
817 {
818 // Make an end iterator on errors
819 it.m_imp.reset();
820 }
821 else
822 {
823 if ((push_result & keep_depth) != 0u)
824 {
825 system::error_code increment_ec;
826 directory_iterator& dir_it = imp->m_stack.back();
827 detail::directory_iterator_increment(dir_it, &increment_ec);
828 if (!increment_ec && dir_it != directory_iterator())
829 goto on_error_return;
830 }
831
832 recursive_directory_iterator_pop_on_error(imp);
833
834 if (imp->m_stack.empty())
835 it.m_imp.reset(); // done, so make end iterator
836 }
837
838 on_error_return:
839 if (ec == NULL)
840 {
841 BOOST_FILESYSTEM_THROW(filesystem_error(
842 "filesystem::recursive_directory_iterator increment error",
843 local_ec));
844 }
845
846 *ec = local_ec;
847 return;
848 }
849
850 // Do the actual increment operation on the top iterator in the iterator
851 // stack, popping the stack if necessary, until either the stack is empty or a
852 // non-end iterator is reached.
853 while (true)
854 {
855 if (imp->m_stack.empty())
856 {
857 it.m_imp.reset(); // done, so make end iterator
858 break;
859 }
860
861 directory_iterator& dir_it = imp->m_stack.back();
862 detail::directory_iterator_increment(dir_it, &local_ec);
863 if (BOOST_UNLIKELY(!!local_ec))
864 goto on_error;
865
866 if (dir_it != directory_iterator())
867 break;
868
869 imp->m_stack.pop_back();
870 }
871 }
872
873 } // namespace detail
874
875 } // namespace filesystem
876 } // namespace boost
877