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