• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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