1 // operations.cpp --------------------------------------------------------------------//
3 // Copyright 2002-2009, 2014 Beman Dawes
4 // Copyright 2001 Dietmar Kuehl
5 // Copyright 2019 Andrey Semashev
7 // Distributed under the Boost Software License, Version 1.0.
8 // See http://www.boost.org/LICENSE_1_0.txt
10 // See library home page at http://www.boost.org/libs/filesystem
12 //--------------------------------------------------------------------------------------//
14 #ifndef BOOST_SYSTEM_NO_DEPRECATED
15 # define BOOST_SYSTEM_NO_DEPRECATED
18 #ifndef _POSIX_PTHREAD_SEMANTICS
19 # define _POSIX_PTHREAD_SEMANTICS // Sun readdir_r() needs this
22 // Include Boost.Predef first so that windows.h is guaranteed to be not included
23 #include <boost/predef/os/windows.h>
25 #include <boost/winapi/config.hpp>
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>
36 #include <cstdlib> // std::malloc, std::free
37 #include <new> // std::nothrow, std::bad_alloc
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>
45 #ifdef BOOST_POSIX_API
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
61 #else // BOOST_WINDOWS_API
66 #include "windows_tools.hpp"
68 #endif // BOOST_WINDOWS_API
70 #include "error_handling.hpp"
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.
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
82 namespace fs = boost::filesystem;
83 using boost::system::error_code;
84 using boost::system::system_category;
87 namespace filesystem {
89 //--------------------------------------------------------------------------------------//
93 //--------------------------------------------------------------------------------------//
96 file_status directory_entry::get_status(system::error_code* ec) const
98 if (!status_known(m_status))
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))
105 m_status = m_symlink_status;
111 m_status = detail::status(m_path, ec);
122 BOOST_FILESYSTEM_DECL
123 file_status directory_entry::get_symlink_status(system::error_code* ec) const
125 if (!status_known(m_symlink_status))
126 m_symlink_status = detail::symlink_status(m_path, ec);
130 return m_symlink_status;
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
137 namespace path_traits {
139 void dispatch(const directory_entry& de,
140 #ifdef BOOST_WINDOWS_API
147 to = de.path().native();
150 void dispatch(const directory_entry& de,
151 #ifdef BOOST_WINDOWS_API
158 to = de.path().native();
161 } // namespace path_traits
163 //--------------------------------------------------------------------------------------//
165 // directory_iterator //
167 //--------------------------------------------------------------------------------------//
173 #ifdef BOOST_POSIX_API
175 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
177 // Obtains maximum length of a path, not including the terminating zero
178 inline std::size_t get_path_max()
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
184 long res = ::pathconf("/", _PC_PATH_MAX);
187 #if defined(PATH_MAX)
195 max = static_cast< std::size_t >(res); // relative root
196 #if defined(PATH_MAX)
202 if ((max + 1) < sizeof(dirent().d_name))
203 max = sizeof(dirent().d_name) - 1;
208 // Returns maximum length of a path, not including the terminating zero
209 inline std::size_t path_max()
211 static const std::size_t max = get_path_max();
215 #endif // BOOST_FILESYSTEM_USE_READDIR_R
217 error_code dir_itr_first(void*& handle, void*& buffer,
218 const char* dir, std::string& target,
219 fs::file_status&, fs::file_status&)
221 if ((handle = ::opendir(dir)) == 0)
223 const int err = errno;
224 return error_code(err, system_category());
226 target.assign("."); // string was static but caused trouble
227 // when iteration called from dtor, after
228 // static had already been destroyed
232 // *result set to NULL on end of directory
233 inline int readdir_r_simulator(DIR* dirp, void*& buffer, struct dirent** result)
235 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
238 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0)
240 struct dirent* storage = static_cast< struct dirent* >(buffer);
241 if (BOOST_UNLIKELY(!storage))
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);
256 return ::readdir_r(dirp, storage, result);
262 struct dirent* p = ::readdir(dirp);
269 error_code dir_itr_increment(void*& handle, void*& buffer,
270 std::string& target, fs::file_status& sf, fs::file_status& symlink_sf)
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());
277 return fs::detail::dir_itr_close(handle, buffer);
279 target = result->d_name;
281 #ifdef BOOST_FILESYSTEM_STATUS_CACHE
282 if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value
284 sf = symlink_sf = fs::file_status(fs::status_error);
286 else // filesystem supplies d_type value
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)
294 sf = fs::file_status(fs::status_error);
295 symlink_sf = fs::file_status(fs::symlink_file);
298 sf = symlink_sf = fs::file_status(fs::status_error);
301 sf = symlink_sf = fs::file_status(fs::status_error);
306 #else // BOOST_WINDOWS_API
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.
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"*";
321 WIN32_FIND_DATAW data;
322 if ((handle = ::FindFirstFileW(dirpath.c_str(), &data))
323 == INVALID_HANDLE_VALUE)
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() );
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
338 sf.type(fs::status_error);
339 symlink_sf.type(fs::status_error);
343 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
345 sf.type(fs::directory_file);
346 symlink_sf.type(fs::directory_file);
350 sf.type(fs::regular_file);
351 symlink_sf.type(fs::regular_file);
353 sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
354 symlink_sf.permissions(sf.permissions());
359 error_code dir_itr_increment(void*& handle, std::wstring& target, fs::file_status& sf, fs::file_status& symlink_sf)
361 WIN32_FIND_DATAW data;
362 if (::FindNextFileW(handle, &data)== 0)// fails
364 DWORD error = ::GetLastError();
365 fs::detail::dir_itr_close(handle);
366 return error_code(error == ERROR_NO_MORE_FILES ? 0 : error, system_category());
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
374 sf.type(fs::status_error);
375 symlink_sf.type(fs::status_error);
379 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
381 sf.type(fs::directory_file);
382 symlink_sf.type(fs::directory_file);
386 sf.type(fs::regular_file);
387 symlink_sf.type(fs::regular_file);
389 sf.permissions(make_permissions(data.cFileName, data.dwFileAttributes));
390 symlink_sf.permissions(sf.permissions());
396 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code =
397 #ifdef BOOST_WINDOWS_API
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
411 #if defined(BOOST_POSIX_API)
416 #ifdef BOOST_POSIX_API
426 DIR* h = static_cast<DIR*>(handle);
429 if (BOOST_UNLIKELY(::closedir(h) != 0))
432 return error_code(err, system_category());
450 BOOST_FILESYSTEM_DECL
451 void directory_iterator_construct(directory_iterator& it, const path& p, unsigned int opts, system::error_code* ec)
453 if (error(p.empty() ? not_found_error_code : 0, p, ec,
454 "boost::filesystem::directory_iterator::construct"))
459 boost::intrusive_ptr< detail::dir_itr_imp > imp;
462 imp = new detail::dir_itr_imp();
466 imp = new (std::nothrow) detail::dir_itr_imp();
467 if (BOOST_UNLIKELY(!imp))
469 *ec = make_error_code(system::errc::not_enough_memory);
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)
482 p.c_str(), filename, file_stat, symlink_file_stat);
486 if (result != make_error_condition(system::errc::permission_denied) ||
487 (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u)
489 error(result.value(), p,
490 ec, "boost::filesystem::directory_iterator::construct");
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'))))
506 detail::directory_iterator_increment(it, ec);
510 catch (std::bad_alloc&)
515 *ec = make_error_code(boost::system::errc::not_enough_memory);
520 BOOST_FILESYSTEM_DECL
521 void directory_iterator_increment(directory_iterator& it, system::error_code* ec)
523 BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator");
530 path::string_type filename;
531 file_status file_stat, symlink_file_stat;
532 system::error_code increment_ec;
536 increment_ec = dir_itr_increment(it.m_imp->handle,
537 # if defined(BOOST_POSIX_API)
540 filename, file_stat, symlink_file_stat);
542 if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc
544 boost::intrusive_ptr< detail::dir_itr_imp > imp;
546 path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900
549 BOOST_FILESYSTEM_THROW(
550 filesystem_error("boost::filesystem::directory_iterator::operator++",
558 if (it.m_imp->handle == NULL) // eof, make end
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')))))
569 it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat);
574 catch (std::bad_alloc&)
580 *ec = make_error_code(boost::system::errc::not_enough_memory);
584 //--------------------------------------------------------------------------------------//
586 // recursive_directory_iterator //
588 //--------------------------------------------------------------------------------------//
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)
596 directory_iterator dir_it;
597 detail::directory_iterator_construct(dir_it, dir_path, opts, ec);
598 if ((ec && *ec) || dir_it == directory_iterator())
601 boost::intrusive_ptr< detail::recur_dir_itr_imp > imp;
604 imp = new detail::recur_dir_itr_imp(opts);
608 imp = new (std::nothrow) detail::recur_dir_itr_imp(opts);
609 if (BOOST_UNLIKELY(!imp))
611 *ec = make_error_code(system::errc::not_enough_memory);
618 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
619 imp->m_stack.push_back(std::move(dir_it));
621 imp->m_stack.push_back(dir_it);
626 catch (std::bad_alloc&)
630 *ec = make_error_code(system::errc::not_enough_memory);
640 void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp)
642 imp->m_stack.pop_back();
644 while (!imp->m_stack.empty())
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())
652 imp->m_stack.pop_back();
658 BOOST_FILESYSTEM_DECL
659 void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec)
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();
667 imp->m_stack.pop_back();
671 if (imp->m_stack.empty())
673 it.m_imp.reset(); // done, so make end iterator
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))
682 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
684 // Make an end iterator on errors
689 recursive_directory_iterator_pop_on_error(imp);
691 if (imp->m_stack.empty())
692 it.m_imp.reset(); // done, so make end iterator
697 BOOST_FILESYSTEM_THROW(
698 filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec));
705 if (dir_it != directory_iterator())
708 imp->m_stack.pop_back();
714 enum push_directory_result
716 directory_not_pushed = 0u,
717 directory_pushed = 1u,
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
724 push_directory_result result = directory_not_pushed;
727 // Discover if the iterator is for a directory that needs to be recursed into,
728 // taking symlinks and options into account.
730 if ((imp->m_options & static_cast< unsigned int >(directory_options::_detail_no_push)) != 0u)
732 imp->m_options &= ~static_cast< unsigned int >(directory_options::_detail_no_push);
736 file_status symlink_stat;
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)
743 symlink_stat = imp->m_stack.back()->symlink_status(ec);
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,
756 if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat))
758 file_status stat = imp->m_stack.back()->status(ec);
759 if (BOOST_UNLIKELY(!!ec))
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))
765 // Skip dangling symlink and continue iteration on the current depth level
772 if (!fs::is_directory(stat))
775 if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)())))
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.
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())
788 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
789 imp->m_stack.push_back(std::move(next)); // may throw
791 imp->m_stack.push_back(next); // may throw
793 return directory_pushed;
797 catch (std::bad_alloc&)
799 ec = make_error_code(system::errc::not_enough_memory);
807 BOOST_FILESYSTEM_DECL
808 void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec)
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();
816 system::error_code local_ec;
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)
823 // report errors if any
824 if (BOOST_UNLIKELY(!!local_ec))
827 if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u)
829 // Make an end iterator on errors
834 if ((push_result & keep_depth) != 0u)
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;
843 recursive_directory_iterator_pop_on_error(imp);
845 if (imp->m_stack.empty())
846 it.m_imp.reset(); // done, so make end iterator
852 BOOST_FILESYSTEM_THROW(filesystem_error(
853 "filesystem::recursive_directory_iterator increment error",
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.
866 if (imp->m_stack.empty())
868 it.m_imp.reset(); // done, so make end iterator
872 directory_iterator& dir_it = imp->m_stack.back();
873 detail::directory_iterator_increment(dir_it, &local_ec);
874 if (BOOST_UNLIKELY(!!local_ec))
877 if (dir_it != directory_iterator())
880 imp->m_stack.pop_back();
884 } // namespace detail
886 } // namespace filesystem