1 // directory.cpp --------------------------------------------------------------------//
3 // Copyright 2002-2009, 2014 Beman Dawes
4 // Copyright 2001 Dietmar Kuehl
5 // Copyright 2019, 2022 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 #include "platform_config.hpp"
16 #include <boost/throw_exception.hpp>
17 #include <boost/filesystem/config.hpp>
18 #include <boost/filesystem/directory.hpp>
19 #include <boost/filesystem/exception.hpp>
20 #include <boost/filesystem/operations.hpp>
21 #include <boost/filesystem/file_status.hpp>
26 #include <cstdlib> // std::malloc, std::free
27 #include <new> // std::nothrow, std::bad_alloc
30 #include <utility> // std::move
31 #include <boost/assert.hpp>
32 #include <boost/system/error_code.hpp>
33 #include <boost/smart_ptr/intrusive_ptr.hpp>
35 #ifdef BOOST_POSIX_API
37 #include <sys/types.h>
43 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS >= 0) && defined(_SC_THREAD_SAFE_FUNCTIONS) && \
44 !defined(__CYGWIN__) && \
45 !(defined(linux) || defined(__linux) || defined(__linux__)) && \
46 !defined(__ANDROID__) && \
47 (!defined(__hpux) || defined(_REENTRANT)) && \
48 (!defined(_AIX) || defined(__THREAD_SAFE)) && \
50 #define BOOST_FILESYSTEM_USE_READDIR_R
53 // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC
56 #define BOOST_FILESYSTEM_NO_O_CLOEXEC
59 #include "posix_tools.hpp"
61 #else // BOOST_WINDOWS_API
65 #include <boost/winapi/basic_types.hpp> // NTSTATUS_
67 #include "windows_tools.hpp"
69 #endif // BOOST_WINDOWS_API
71 #include "atomic_tools.hpp"
72 #include "error_handling.hpp"
73 #include "private_config.hpp"
75 // BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in
76 // dir_itr_increment. The config tests are placed here because some of the
77 // macros being tested come from dirent.h.
79 // TODO: find out what macros indicate dirent::d_type present in more libraries
80 #if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present
81 #define BOOST_FILESYSTEM_STATUS_CACHE
84 namespace fs
= boost::filesystem
;
85 using boost::system::error_code
;
86 using boost::system::system_category
;
89 namespace filesystem
{
91 //--------------------------------------------------------------------------------------//
95 //--------------------------------------------------------------------------------------//
98 file_status
directory_entry::get_status(system::error_code
* ec
) const
100 if (!status_known(m_status
))
102 // optimization: if the symlink status is known, and it isn't a symlink,
103 // then status and symlink_status are identical so just copy the
104 // symlink status to the regular status.
105 if (status_known(m_symlink_status
) && !is_symlink(m_symlink_status
))
107 m_status
= m_symlink_status
;
113 m_status
= detail::status(m_path
, ec
);
124 BOOST_FILESYSTEM_DECL
125 file_status
directory_entry::get_symlink_status(system::error_code
* ec
) const
127 if (!status_known(m_symlink_status
))
128 m_symlink_status
= detail::symlink_status(m_path
, ec
);
132 return m_symlink_status
;
135 // dispatch directory_entry supplied here rather than in
136 // <boost/filesystem/path_traits.hpp>, thus avoiding header circularity.
137 // test cases are in operations_unit_test.cpp
139 namespace path_traits
{
141 BOOST_FILESYSTEM_DECL
142 void dispatch(directory_entry
const& de
,
143 #ifdef BOOST_WINDOWS_API
150 to
= de
.path().native();
153 BOOST_FILESYSTEM_DECL
154 void dispatch(directory_entry
const& de
,
155 #ifdef BOOST_WINDOWS_API
162 to
= de
.path().native();
165 } // namespace path_traits
167 //--------------------------------------------------------------------------------------//
169 // directory_iterator //
171 //--------------------------------------------------------------------------------------//
175 BOOST_CONSTEXPR_OR_CONST
std::size_t dir_itr_imp_extra_data_alignment
= 16u;
177 BOOST_FILESYSTEM_DECL
void* dir_itr_imp::operator new(std::size_t class_size
, std::size_t extra_size
) BOOST_NOEXCEPT
180 class_size
= (class_size
+ dir_itr_imp_extra_data_alignment
- 1u) & ~(dir_itr_imp_extra_data_alignment
- 1u);
181 std::size_t total_size
= class_size
+ extra_size
;
183 // Return NULL on OOM
184 void* p
= std::malloc(total_size
);
185 if (BOOST_LIKELY(p
!= NULL
))
186 std::memset(p
, 0, total_size
);
190 BOOST_FILESYSTEM_DECL
void dir_itr_imp::operator delete(void* p
, std::size_t extra_size
) BOOST_NOEXCEPT
195 BOOST_FILESYSTEM_DECL
void dir_itr_imp::operator delete(void* p
) BOOST_NOEXCEPT
202 inline void* get_dir_itr_imp_extra_data(dir_itr_imp
* imp
) BOOST_NOEXCEPT
204 BOOST_CONSTEXPR_OR_CONST
std::size_t extra_data_offset
= (sizeof(dir_itr_imp
) + dir_itr_imp_extra_data_alignment
- 1u) & ~(dir_itr_imp_extra_data_alignment
- 1u);
205 return reinterpret_cast< unsigned char* >(imp
) + extra_data_offset
;
208 #ifdef BOOST_POSIX_API
210 inline system::error_code
dir_itr_close(dir_itr_imp
& imp
) BOOST_NOEXCEPT
212 if (imp
.handle
!= NULL
)
214 DIR* h
= static_cast< DIR* >(imp
.handle
);
217 if (BOOST_UNLIKELY(::closedir(h
) != 0))
220 return error_code(err
, system_category());
227 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
229 // Obtains maximum length of a path, not including the terminating zero
230 inline std::size_t get_path_max()
232 // this code is based on Stevens and Rago, Advanced Programming in the
233 // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49
236 long res
= ::pathconf("/", _PC_PATH_MAX
);
239 #if defined(PATH_MAX)
247 max
= static_cast< std::size_t >(res
); // relative root
248 #if defined(PATH_MAX)
254 if ((max
+ 1) < sizeof(dirent().d_name
))
255 max
= sizeof(dirent().d_name
) - 1;
260 // Returns maximum length of a path, not including the terminating zero
261 inline std::size_t path_max()
263 static const std::size_t max
= get_path_max();
267 #endif // BOOST_FILESYSTEM_USE_READDIR_R
269 // *result set to NULL on end of directory
270 #if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
273 int readdir_impl(dir_itr_imp
& imp
, struct dirent
** result
)
277 struct dirent
* p
= ::readdir(static_cast< DIR* >(imp
.handle
));
284 #if !defined(BOOST_FILESYSTEM_USE_READDIR_R)
286 inline int invoke_readdir(dir_itr_imp
& imp
, struct dirent
** result
)
288 return readdir_impl(imp
, result
);
291 #else // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
293 int readdir_r_impl(dir_itr_imp
& imp
, struct dirent
** result
)
297 static_cast< DIR* >(imp
.handle
),
298 static_cast< struct dirent
* >(get_dir_itr_imp_extra_data(&imp
)),
303 int readdir_select_impl(dir_itr_imp
& imp
, struct dirent
** result
);
305 typedef int readdir_impl_t(dir_itr_imp
& imp
, struct dirent
** result
);
307 //! Pointer to the actual implementation of the copy_file_data implementation
308 readdir_impl_t
* readdir_impl_ptr
= &readdir_select_impl
;
310 void init_readdir_impl()
312 readdir_impl_t
* impl
= &readdir_impl
;
313 if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS
) >= 0)
314 impl
= &readdir_r_impl
;
316 filesystem::detail::atomic_store_relaxed(readdir_impl_ptr
, impl
);
319 struct readdir_initializer
321 readdir_initializer()
327 BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY
) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
328 const readdir_initializer readdir_init
;
330 int readdir_select_impl(dir_itr_imp
& imp
, struct dirent
** result
)
333 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr
)(imp
, result
);
336 inline int invoke_readdir(dir_itr_imp
& imp
, struct dirent
** result
)
338 return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr
)(imp
, result
);
341 #endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R)
343 error_code
dir_itr_increment(dir_itr_imp
& imp
, fs::path
& filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
345 dirent
* result
= NULL
;
346 int err
= invoke_readdir(imp
, &result
);
347 if (BOOST_UNLIKELY(err
!= 0))
348 return error_code(err
, system_category());
350 return dir_itr_close(imp
);
352 filename
= result
->d_name
;
354 #ifdef BOOST_FILESYSTEM_STATUS_CACHE
355 if (result
->d_type
== DT_UNKNOWN
) // filesystem does not supply d_type value
357 sf
= symlink_sf
= fs::file_status(fs::status_error
);
359 else // filesystem supplies d_type value
361 if (result
->d_type
== DT_DIR
)
362 sf
= symlink_sf
= fs::file_status(fs::directory_file
);
363 else if (result
->d_type
== DT_REG
)
364 sf
= symlink_sf
= fs::file_status(fs::regular_file
);
365 else if (result
->d_type
== DT_LNK
)
367 sf
= fs::file_status(fs::status_error
);
368 symlink_sf
= fs::file_status(fs::symlink_file
);
371 sf
= symlink_sf
= fs::file_status(fs::status_error
);
374 sf
= symlink_sf
= fs::file_status(fs::status_error
);
379 error_code
dir_itr_create(boost::intrusive_ptr
< detail::dir_itr_imp
>& imp
, fs::path
const& dir
, unsigned int opts
, fs::path
& first_filename
, fs::file_status
&, fs::file_status
&)
381 std::size_t extra_size
= 0u;
382 #if defined(BOOST_FILESYSTEM_USE_READDIR_R)
384 readdir_impl_t
* rdimpl
= filesystem::detail::atomic_load_relaxed(readdir_impl_ptr
);
385 if (BOOST_UNLIKELY(rdimpl
== &readdir_select_impl
))
388 rdimpl
= filesystem::detail::atomic_load_relaxed(readdir_impl_ptr
);
391 if (rdimpl
== &readdir_r_impl
)
393 // According to readdir description, there's no reliable way to predict the length of the d_name string.
394 // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate
395 // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be
396 // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible
397 // in favor of readdir.
398 extra_size
= (sizeof(dirent
) - sizeof(dirent().d_name
)) + path_max() + 1u; // + 1 for "\0"
401 #endif // defined(BOOST_FILESYSTEM_USE_READDIR_R)
403 boost::intrusive_ptr
< detail::dir_itr_imp
> pimpl(new (extra_size
) detail::dir_itr_imp());
404 if (BOOST_UNLIKELY(!pimpl
))
405 return make_error_code(system::errc::not_enough_memory
);
407 #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
408 int flags
= O_DIRECTORY
| O_RDONLY
| O_NONBLOCK
| O_CLOEXEC
;
409 if ((opts
& static_cast< unsigned int >(directory_options::_detail_no_follow
)) != 0u)
412 int fd
= ::open(dir
.c_str(), flags
);
413 if (BOOST_UNLIKELY(fd
< 0))
415 const int err
= errno
;
416 return error_code(err
, system_category());
419 #if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC)
420 int res
= ::fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
421 if (BOOST_UNLIKELY(res
< 0))
423 const int err
= errno
;
425 return error_code(err
, system_category());
429 pimpl
->handle
= ::fdopendir(fd
);
430 if (BOOST_UNLIKELY(!pimpl
->handle
))
432 const int err
= errno
;
434 return error_code(err
, system_category());
436 #else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
437 pimpl
->handle
= ::opendir(dir
.c_str());
438 if (BOOST_UNLIKELY(!pimpl
->handle
))
440 const int err
= errno
;
441 return error_code(err
, system_category());
443 #endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW)
445 // Force initial readdir call by the caller. This will initialize the actual first filename and statuses.
446 first_filename
.assign(".");
452 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code
= ENOENT
;
454 #else // BOOST_WINDOWS_API
456 inline void set_file_statuses(DWORD attrs
, const ULONG
* reparse_point_tag
, fs::path
const& filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
458 if ((attrs
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0u)
460 // Reparse points are complex, so don't try to resolve them here; instead just mark
461 // them as status_error which causes directory_entry caching to call status()
462 // and symlink_status() which do handle reparse points fully
463 if (reparse_point_tag
)
465 // If we have a reparse point tag we can at least populate the symlink status,
466 // consistent with symlink_status() behavior
467 symlink_sf
.type(is_reparse_point_tag_a_symlink(*reparse_point_tag
) ? fs::symlink_file
: fs::reparse_file
);
468 symlink_sf
.permissions(make_permissions(filename
, attrs
));
472 symlink_sf
.type(fs::status_error
);
475 sf
.type(fs::status_error
);
479 if ((attrs
& FILE_ATTRIBUTE_DIRECTORY
) != 0u)
481 sf
.type(fs::directory_file
);
482 symlink_sf
.type(fs::directory_file
);
486 sf
.type(fs::regular_file
);
487 symlink_sf
.type(fs::regular_file
);
490 sf
.permissions(make_permissions(filename
, attrs
));
491 symlink_sf
.permissions(sf
.permissions());
495 #if !defined(UNDER_CE)
497 //! FILE_ID_128 definition from Windows SDK
503 //! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably).
504 struct file_directory_information
506 ULONG NextEntryOffset
;
508 LARGE_INTEGER CreationTime
;
509 LARGE_INTEGER LastAccessTime
;
510 LARGE_INTEGER LastWriteTime
;
511 LARGE_INTEGER ChangeTime
;
512 LARGE_INTEGER EndOfFile
;
513 LARGE_INTEGER AllocationSize
;
514 ULONG FileAttributes
;
515 ULONG FileNameLength
;
519 //! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista.
520 struct file_id_both_dir_info
522 DWORD NextEntryOffset
;
524 LARGE_INTEGER CreationTime
;
525 LARGE_INTEGER LastAccessTime
;
526 LARGE_INTEGER LastWriteTime
;
527 LARGE_INTEGER ChangeTime
;
528 LARGE_INTEGER EndOfFile
;
529 LARGE_INTEGER AllocationSize
;
530 DWORD FileAttributes
;
531 DWORD FileNameLength
;
533 CCHAR ShortNameLength
;
535 LARGE_INTEGER FileId
;
539 //! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8.
540 struct file_full_dir_info
542 ULONG NextEntryOffset
;
544 LARGE_INTEGER CreationTime
;
545 LARGE_INTEGER LastAccessTime
;
546 LARGE_INTEGER LastWriteTime
;
547 LARGE_INTEGER ChangeTime
;
548 LARGE_INTEGER EndOfFile
;
549 LARGE_INTEGER AllocationSize
;
550 ULONG FileAttributes
;
551 ULONG FileNameLength
;
556 //! FILE_ID_EXTD_DIR_INFO definition from Windows SDK. Provides reparse point tag, which saves us querying it with a few separate syscalls. Supported since Windows 8.
557 struct file_id_extd_dir_info
559 ULONG NextEntryOffset
;
561 LARGE_INTEGER CreationTime
;
562 LARGE_INTEGER LastAccessTime
;
563 LARGE_INTEGER LastWriteTime
;
564 LARGE_INTEGER ChangeTime
;
565 LARGE_INTEGER EndOfFile
;
566 LARGE_INTEGER AllocationSize
;
567 ULONG FileAttributes
;
568 ULONG FileNameLength
;
570 ULONG ReparsePointTag
;
575 //! Indicates format of the extra data in the directory iterator
576 enum extra_data_format
578 file_directory_information_format
,
579 file_id_both_dir_info_format
,
580 file_full_dir_info_format
,
581 file_id_extd_dir_info_format
584 //! Indicates extra data format that should be used by directory iterator by default
585 extra_data_format g_extra_data_format
= file_directory_information_format
;
588 * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator.
590 * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename.
591 * NTFS, VFAT, exFAT support filenames up to 255 UTF-16/UCS-2 characters. ReFS supports filenames up to 32768 UTF-16 characters.
593 BOOST_CONSTEXPR_OR_CONST
std::size_t dir_itr_extra_size
= sizeof(file_id_extd_dir_info
) + 65536u;
595 inline system::error_code
dir_itr_close(dir_itr_imp
& imp
) BOOST_NOEXCEPT
597 imp
.extra_data_format
= 0u;
598 imp
.current_offset
= 0u;
600 if (imp
.handle
!= NULL
)
602 ::CloseHandle(imp
.handle
);
609 error_code
dir_itr_increment(dir_itr_imp
& imp
, fs::path
& filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
611 void* extra_data
= get_dir_itr_imp_extra_data(&imp
);
612 const void* current_data
= static_cast< const unsigned char* >(extra_data
) + imp
.current_offset
;
613 switch (imp
.extra_data_format
)
615 case file_id_extd_dir_info_format
:
617 const file_id_extd_dir_info
* data
= static_cast< const file_id_extd_dir_info
* >(current_data
);
618 if (data
->NextEntryOffset
== 0u)
620 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api
)(imp
.handle
, file_id_extd_directory_info_class
, extra_data
, dir_itr_extra_size
))
622 DWORD error
= ::GetLastError();
625 if (error
== ERROR_NO_MORE_FILES
)
628 return error_code(error
, system_category());
631 imp
.current_offset
= 0u;
632 data
= static_cast< const file_id_extd_dir_info
* >(extra_data
);
636 imp
.current_offset
+= data
->NextEntryOffset
;
637 data
= reinterpret_cast< const file_id_extd_dir_info
* >(static_cast< const unsigned char* >(current_data
) + data
->NextEntryOffset
);
640 filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
641 set_file_statuses(data
->FileAttributes
, &data
->ReparsePointTag
, filename
, sf
, symlink_sf
);
645 case file_full_dir_info_format
:
647 const file_full_dir_info
* data
= static_cast< const file_full_dir_info
* >(current_data
);
648 if (data
->NextEntryOffset
== 0u)
650 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api
)(imp
.handle
, file_full_directory_info_class
, extra_data
, dir_itr_extra_size
))
652 DWORD error
= ::GetLastError();
655 if (error
== ERROR_NO_MORE_FILES
)
658 return error_code(error
, system_category());
661 imp
.current_offset
= 0u;
662 data
= static_cast< const file_full_dir_info
* >(extra_data
);
666 imp
.current_offset
+= data
->NextEntryOffset
;
667 data
= reinterpret_cast< const file_full_dir_info
* >(static_cast< const unsigned char* >(current_data
) + data
->NextEntryOffset
);
670 filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
671 set_file_statuses(data
->FileAttributes
, NULL
, filename
, sf
, symlink_sf
);
675 case file_id_both_dir_info_format
:
677 const file_id_both_dir_info
* data
= static_cast< const file_id_both_dir_info
* >(current_data
);
678 if (data
->NextEntryOffset
== 0u)
680 if (!filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api
)(imp
.handle
, file_id_both_directory_info_class
, extra_data
, dir_itr_extra_size
))
682 DWORD error
= ::GetLastError();
685 if (error
== ERROR_NO_MORE_FILES
)
688 return error_code(error
, system_category());
691 imp
.current_offset
= 0u;
692 data
= static_cast< const file_id_both_dir_info
* >(extra_data
);
696 imp
.current_offset
+= data
->NextEntryOffset
;
697 data
= reinterpret_cast< const file_id_both_dir_info
* >(static_cast< const unsigned char* >(current_data
) + data
->NextEntryOffset
);
700 filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
701 set_file_statuses(data
->FileAttributes
, NULL
, filename
, sf
, symlink_sf
);
707 const file_directory_information
* data
= static_cast< const file_directory_information
* >(current_data
);
708 if (data
->NextEntryOffset
== 0u)
710 io_status_block iosb
;
711 boost::winapi::NTSTATUS_ status
= filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api
)
720 file_directory_information_class
,
721 FALSE
, // ReturnSingleEntry
726 if (!NT_SUCCESS(status
))
729 if (status
== STATUS_NO_MORE_FILES
)
732 return error_code(translate_ntstatus(status
), system_category());
735 imp
.current_offset
= 0u;
736 data
= static_cast< const file_directory_information
* >(extra_data
);
740 imp
.current_offset
+= data
->NextEntryOffset
;
741 data
= reinterpret_cast< const file_directory_information
* >(static_cast< const unsigned char* >(current_data
) + data
->NextEntryOffset
);
744 filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
745 set_file_statuses(data
->FileAttributes
, NULL
, filename
, sf
, symlink_sf
);
754 error_code
dir_itr_create(boost::intrusive_ptr
< detail::dir_itr_imp
>& imp
, fs::path
const& dir
, unsigned int opts
, fs::path
& first_filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
756 boost::intrusive_ptr
< detail::dir_itr_imp
> pimpl(new (dir_itr_extra_size
) detail::dir_itr_imp());
757 if (BOOST_UNLIKELY(!pimpl
))
758 return make_error_code(system::errc::not_enough_memory
);
760 DWORD flags
= FILE_FLAG_BACKUP_SEMANTICS
;
761 if ((opts
& static_cast< unsigned int >(directory_options::_detail_no_follow
)) != 0u)
762 flags
|= FILE_FLAG_OPEN_REPARSE_POINT
;
763 handle_wrapper
h(create_file_handle(dir
, FILE_LIST_DIRECTORY
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, flags
));
764 if (BOOST_UNLIKELY(h
.handle
== INVALID_HANDLE_VALUE
))
767 DWORD error
= ::GetLastError();
768 return error_code(error
, system_category());
771 GetFileInformationByHandleEx_t
* get_file_information_by_handle_ex
= filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api
);
772 if (BOOST_LIKELY(get_file_information_by_handle_ex
!= NULL
))
774 file_attribute_tag_info info
;
775 BOOL res
= get_file_information_by_handle_ex(h
.handle
, file_attribute_tag_info_class
, &info
, sizeof(info
));
776 if (BOOST_UNLIKELY(!res
))
777 goto return_last_error
;
779 if (BOOST_UNLIKELY((info
.FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0u))
780 return make_error_code(system::errc::not_a_directory
);
782 if ((opts
& static_cast< unsigned int >(directory_options::_detail_no_follow
)) != 0u)
784 if ((info
.FileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0u && is_reparse_point_tag_a_symlink(info
.ReparseTag
))
785 return make_error_code(system::errc::too_many_symbolic_link_levels
);
790 BY_HANDLE_FILE_INFORMATION info
;
791 BOOL res
= ::GetFileInformationByHandle(h
.handle
, &info
);
792 if (BOOST_UNLIKELY(!res
))
793 goto return_last_error
;
795 if (BOOST_UNLIKELY((info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0u))
796 return make_error_code(system::errc::not_a_directory
);
798 if ((opts
& static_cast< unsigned int >(directory_options::_detail_no_follow
)) != 0u)
800 if ((info
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) != 0u && is_reparse_point_a_symlink_ioctl(h
.handle
))
801 return make_error_code(system::errc::too_many_symbolic_link_levels
);
805 void* extra_data
= get_dir_itr_imp_extra_data(pimpl
.get());
806 switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format
))
808 case file_id_extd_dir_info_format
:
810 if (!get_file_information_by_handle_ex(h
.handle
, file_id_extd_directory_restart_info_class
, extra_data
, dir_itr_extra_size
))
812 DWORD error
= ::GetLastError();
814 if (error
== ERROR_NOT_SUPPORTED
|| error
== ERROR_INVALID_PARAMETER
)
816 // Fall back to file_full_dir_info_format.
817 // Note that some mounted filesystems may not support FILE_ID_128 identifiers, which will cause
818 // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER,
819 // even though in general the operation is supported by the kernel. So don't downgrade to
820 // FileFullDirectoryRestartInfo permanently in this case - only for this particular iterator.
821 if (error
== ERROR_NOT_SUPPORTED
)
822 filesystem::detail::atomic_store_relaxed(g_extra_data_format
, file_full_dir_info_format
);
823 goto fallback_to_file_full_dir_info_format
;
826 if (error
== ERROR_NO_MORE_FILES
|| error
== ERROR_FILE_NOT_FOUND
)
829 return error_code(error
, system_category());
832 pimpl
->extra_data_format
= file_id_extd_dir_info_format
;
834 const file_id_extd_dir_info
* data
= static_cast< const file_id_extd_dir_info
* >(extra_data
);
835 first_filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
837 set_file_statuses(data
->FileAttributes
, &data
->ReparsePointTag
, first_filename
, sf
, symlink_sf
);
841 case file_full_dir_info_format
:
842 fallback_to_file_full_dir_info_format
:
844 if (!get_file_information_by_handle_ex(h
.handle
, file_full_directory_restart_info_class
, extra_data
, dir_itr_extra_size
))
846 DWORD error
= ::GetLastError();
848 if (error
== ERROR_NOT_SUPPORTED
|| error
== ERROR_INVALID_PARAMETER
)
850 // Fall back to file_id_both_dir_info
851 filesystem::detail::atomic_store_relaxed(g_extra_data_format
, file_id_both_dir_info_format
);
852 goto fallback_to_file_id_both_dir_info
;
855 if (error
== ERROR_NO_MORE_FILES
|| error
== ERROR_FILE_NOT_FOUND
)
858 return error_code(error
, system_category());
861 pimpl
->extra_data_format
= file_full_dir_info_format
;
863 const file_full_dir_info
* data
= static_cast< const file_full_dir_info
* >(extra_data
);
864 first_filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
866 set_file_statuses(data
->FileAttributes
, NULL
, first_filename
, sf
, symlink_sf
);
870 case file_id_both_dir_info_format
:
871 fallback_to_file_id_both_dir_info
:
873 if (!get_file_information_by_handle_ex(h
.handle
, file_id_both_directory_restart_info_class
, extra_data
, dir_itr_extra_size
))
875 DWORD error
= ::GetLastError();
877 if (error
== ERROR_NO_MORE_FILES
|| error
== ERROR_FILE_NOT_FOUND
)
880 return error_code(error
, system_category());
883 pimpl
->extra_data_format
= file_id_both_dir_info_format
;
885 const file_id_both_dir_info
* data
= static_cast< const file_id_both_dir_info
* >(extra_data
);
886 first_filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
888 set_file_statuses(data
->FileAttributes
, NULL
, first_filename
, sf
, symlink_sf
);
894 NtQueryDirectoryFile_t
* nt_query_directory_file
= filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api
);
895 if (BOOST_UNLIKELY(!nt_query_directory_file
))
896 return error_code(ERROR_NOT_SUPPORTED
, system_category());
898 io_status_block iosb
;
899 boost::winapi::NTSTATUS_ status
= nt_query_directory_file
908 file_directory_information_class
,
909 FALSE
, // ReturnSingleEntry
914 if (!NT_SUCCESS(status
))
916 // Note: an empty root directory has no "." or ".." entries, so this
917 // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW
918 // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we
919 // do not consider an error. It is treated as eof instead.
920 if (status
== STATUS_NO_MORE_FILES
|| status
== STATUS_NO_SUCH_FILE
)
923 return error_code(translate_ntstatus(status
), system_category());
926 pimpl
->extra_data_format
= file_directory_information_format
;
928 const file_directory_information
* data
= static_cast< const file_directory_information
* >(extra_data
);
929 first_filename
.assign(data
->FileName
, data
->FileName
+ data
->FileNameLength
/ sizeof(WCHAR
));
931 set_file_statuses(data
->FileAttributes
, NULL
, first_filename
, sf
, symlink_sf
);
936 pimpl
->handle
= h
.handle
;
937 h
.handle
= INVALID_HANDLE_VALUE
;
944 #else // !defined(UNDER_CE)
946 inline system::error_code
dir_itr_close(dir_itr_imp
& imp
) BOOST_NOEXCEPT
948 if (imp
.handle
!= NULL
)
950 ::FindClose(imp
.handle
);
957 error_code
dir_itr_increment(dir_itr_imp
& imp
, fs::path
& filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
959 WIN32_FIND_DATAW data
;
960 if (::FindNextFileW(imp
.handle
, &data
) == 0) // fails
962 DWORD error
= ::GetLastError();
964 if (error
== ERROR_NO_MORE_FILES
)
966 return error_code(error
, system_category());
969 filename
= data
.cFileName
;
970 set_file_statuses(data
.dwFileAttributes
, NULL
, filename
, sf
, symlink_sf
);
976 error_code
dir_itr_create(boost::intrusive_ptr
< detail::dir_itr_imp
>& imp
, fs::path
const& dir
, unsigned int opts
, fs::path
& first_filename
, fs::file_status
& sf
, fs::file_status
& symlink_sf
)
978 boost::intrusive_ptr
< detail::dir_itr_imp
> pimpl(new (static_cast< std::size_t >(0u)) detail::dir_itr_imp());
979 if (BOOST_UNLIKELY(!pimpl
))
980 return make_error_code(system::errc::not_enough_memory
);
982 // use a form of search Sebastian Martel reports will work with Win98
983 fs::path
dirpath(dir
);
984 dirpath
.make_preferred();
987 WIN32_FIND_DATAW data
;
988 pimpl
->handle
= ::FindFirstFileW(dirpath
.c_str(), &data
);
989 if (BOOST_UNLIKELY(pimpl
->handle
== INVALID_HANDLE_VALUE
))
991 pimpl
->handle
= NULL
; // signal eof
993 // Note: an empty root directory has no "." or ".." entries, so this
994 // causes a ERROR_FILE_NOT_FOUND error which we do not consider an
995 // error. It is treated as eof instead.
996 // Windows Mobile returns ERROR_NO_MORE_FILES; see ticket #3551
997 DWORD error
= ::GetLastError();
998 if (error
== ERROR_FILE_NOT_FOUND
|| error
== ERROR_NO_MORE_FILES
)
1001 return error_code(error
, system_category());
1004 first_filename
= data
.cFileName
;
1005 set_file_statuses(data
.dwFileAttributes
, NULL
, first_filename
, sf
, symlink_sf
);
1009 return error_code();
1012 #endif // !defined(UNDER_CE)
1014 BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code
= ERROR_PATH_NOT_FOUND
;
1016 #endif // BOOST_WINDOWS_API
1020 #if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
1022 //! Initializes directory iterator implementation
1023 void init_directory_iterator_impl() BOOST_NOEXCEPT
1025 if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api
) != NULL
)
1027 // Enable the latest format we support. It will get downgraded, if needed, as we attempt
1028 // to create the directory iterator the first time.
1029 filesystem::detail::atomic_store_relaxed(g_extra_data_format
, file_id_extd_dir_info_format
);
1033 #endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE)
1035 BOOST_FILESYSTEM_DECL
1036 dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT
1038 dir_itr_close(*this);
1041 BOOST_FILESYSTEM_DECL
1042 void directory_iterator_construct(directory_iterator
& it
, path
const& p
, unsigned int opts
, system::error_code
* ec
)
1044 if (BOOST_UNLIKELY(p
.empty()))
1046 emit_error(not_found_error_code
, p
, ec
, "boost::filesystem::directory_iterator::construct");
1055 boost::intrusive_ptr
< detail::dir_itr_imp
> imp
;
1057 file_status file_stat
, symlink_file_stat
;
1058 system::error_code result
= dir_itr_create(imp
, p
, opts
, filename
, file_stat
, symlink_file_stat
);
1064 if (result
!= make_error_condition(system::errc::permission_denied
) ||
1065 (opts
& static_cast< unsigned int >(directory_options::skip_permission_denied
)) == 0u)
1068 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p
, result
));
1075 if (imp
->handle
== NULL
) // eof, make end
1079 const path::string_type::value_type
* filename_str
= filename
.c_str();
1080 if (!(filename_str
[0] == path::dot
// dot or dot-dot
1081 && (filename_str
[1] == static_cast< path::string_type::value_type
>('\0') ||
1082 (filename_str
[1] == path::dot
&& filename_str
[2] == static_cast< path::string_type::value_type
>('\0')))))
1084 imp
->dir_entry
.assign(p
/ filename
, file_stat
, symlink_file_stat
);
1089 // If dot or dot-dot name produced by the underlying API, skip it until the first actual file
1090 result
= dir_itr_increment(*imp
, filename
, file_stat
, symlink_file_stat
);
1093 catch (std::bad_alloc
&)
1098 *ec
= make_error_code(system::errc::not_enough_memory
);
1103 BOOST_FILESYSTEM_DECL
1104 void directory_iterator_increment(directory_iterator
& it
, system::error_code
* ec
)
1106 BOOST_ASSERT_MSG(!it
.is_end(), "attempt to increment end iterator");
1114 file_status file_stat
, symlink_file_stat
;
1115 system::error_code increment_ec
;
1119 increment_ec
= dir_itr_increment(*it
.m_imp
, filename
, file_stat
, symlink_file_stat
);
1121 if (BOOST_UNLIKELY(!!increment_ec
)) // happens if filesystem is corrupt, such as on a damaged optical disc
1123 boost::intrusive_ptr
< detail::dir_itr_imp
> imp
;
1125 path
error_path(imp
->dir_entry
.path().parent_path()); // fix ticket #5900
1127 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++", error_path
, increment_ec
));
1133 if (it
.m_imp
->handle
== NULL
) // eof, make end
1139 const path::string_type::value_type
* filename_str
= filename
.c_str();
1140 if (!(filename_str
[0] == path::dot
// !(dot or dot-dot)
1141 && (filename_str
[1] == static_cast< path::string_type::value_type
>('\0') ||
1142 (filename_str
[1] == path::dot
&& filename_str
[2] == static_cast< path::string_type::value_type
>('\0')))))
1144 it
.m_imp
->dir_entry
.replace_filename(filename
, file_stat
, symlink_file_stat
);
1149 catch (std::bad_alloc
&)
1155 *ec
= make_error_code(system::errc::not_enough_memory
);
1159 //--------------------------------------------------------------------------------------//
1161 // recursive_directory_iterator //
1163 //--------------------------------------------------------------------------------------//
1165 BOOST_FILESYSTEM_DECL
1166 void recursive_directory_iterator_construct(recursive_directory_iterator
& it
, path
const& dir_path
, unsigned int opts
, system::error_code
* ec
)
1171 directory_iterator dir_it
;
1172 detail::directory_iterator_construct(dir_it
, dir_path
, opts
, ec
);
1173 if ((ec
&& *ec
) || dir_it
== directory_iterator())
1176 boost::intrusive_ptr
< detail::recur_dir_itr_imp
> imp
;
1179 imp
= new detail::recur_dir_itr_imp(opts
);
1183 imp
= new (std::nothrow
) detail::recur_dir_itr_imp(opts
);
1184 if (BOOST_UNLIKELY(!imp
))
1186 *ec
= make_error_code(system::errc::not_enough_memory
);
1193 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1194 imp
->m_stack
.push_back(std::move(dir_it
));
1196 imp
->m_stack
.push_back(dir_it
);
1201 catch (std::bad_alloc
&)
1205 *ec
= make_error_code(system::errc::not_enough_memory
);
1215 void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp
* imp
)
1217 imp
->m_stack
.pop_back();
1219 while (!imp
->m_stack
.empty())
1221 directory_iterator
& dir_it
= imp
->m_stack
.back();
1222 system::error_code increment_ec
;
1223 detail::directory_iterator_increment(dir_it
, &increment_ec
);
1224 if (!increment_ec
&& dir_it
!= directory_iterator())
1227 imp
->m_stack
.pop_back();
1233 BOOST_FILESYSTEM_DECL
1234 void recursive_directory_iterator_pop(recursive_directory_iterator
& it
, system::error_code
* ec
)
1236 BOOST_ASSERT_MSG(!it
.is_end(), "pop() on end recursive_directory_iterator");
1237 detail::recur_dir_itr_imp
* const imp
= it
.m_imp
.get();
1242 imp
->m_stack
.pop_back();
1246 if (imp
->m_stack
.empty())
1248 it
.m_imp
.reset(); // done, so make end iterator
1252 directory_iterator
& dir_it
= imp
->m_stack
.back();
1253 system::error_code increment_ec
;
1254 detail::directory_iterator_increment(dir_it
, &increment_ec
);
1255 if (BOOST_UNLIKELY(!!increment_ec
))
1257 if ((imp
->m_options
& static_cast< unsigned int >(directory_options::pop_on_error
)) == 0u)
1259 // Make an end iterator on errors
1264 recursive_directory_iterator_pop_on_error(imp
);
1266 if (imp
->m_stack
.empty())
1267 it
.m_imp
.reset(); // done, so make end iterator
1271 BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec
));
1277 if (dir_it
!= directory_iterator())
1280 imp
->m_stack
.pop_back();
1286 enum push_directory_result
1288 directory_not_pushed
= 0u,
1289 directory_pushed
= 1u,
1290 keep_depth
= 1u << 1
1293 // Returns: true if push occurs, otherwise false. Always returns false on error.
1294 inline push_directory_result
recursive_directory_iterator_push_directory(detail::recur_dir_itr_imp
* imp
, system::error_code
& ec
) BOOST_NOEXCEPT
1296 push_directory_result result
= directory_not_pushed
;
1299 // Discover if the iterator is for a directory that needs to be recursed into,
1300 // taking symlinks and options into account.
1302 if ((imp
->m_options
& static_cast< unsigned int >(directory_options::_detail_no_push
)) != 0u)
1304 imp
->m_options
&= ~static_cast< unsigned int >(directory_options::_detail_no_push
);
1308 file_status symlink_stat
;
1310 // if we are not recursing into symlinks, we are going to have to know if the
1311 // stack top is a symlink, so get symlink_status and verify no error occurred
1312 if ((imp
->m_options
& static_cast< unsigned int >(directory_options::follow_directory_symlink
)) == 0u ||
1313 (imp
->m_options
& static_cast< unsigned int >(directory_options::skip_dangling_symlinks
)) != 0u)
1315 symlink_stat
= imp
->m_stack
.back()->symlink_status(ec
);
1320 // Logic for following predicate was contributed by Daniel Aarno to handle cyclic
1321 // symlinks correctly and efficiently, fixing ticket #5652.
1322 // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink
1323 // || !is_symlink(m_stack.back()->symlink_status()))
1324 // && is_directory(m_stack.back()->status())) ...
1325 // The predicate code has since been rewritten to pass error_code arguments,
1326 // per ticket #5653.
1328 if ((imp
->m_options
& static_cast< unsigned int >(directory_options::follow_directory_symlink
)) != 0u || !fs::is_symlink(symlink_stat
))
1330 file_status stat
= imp
->m_stack
.back()->status(ec
);
1331 if (BOOST_UNLIKELY(!!ec
))
1333 if (ec
== make_error_condition(system::errc::no_such_file_or_directory
) && fs::is_symlink(symlink_stat
) &&
1334 (imp
->m_options
& static_cast< unsigned int >(directory_options::follow_directory_symlink
| directory_options::skip_dangling_symlinks
)) == static_cast< unsigned int >(directory_options::follow_directory_symlink
| directory_options::skip_dangling_symlinks
))
1336 // Skip dangling symlink and continue iteration on the current depth level
1343 if (!fs::is_directory(stat
))
1346 if (BOOST_UNLIKELY((imp
->m_stack
.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits
< int >::max
)())))
1348 // We cannot let depth to overflow
1349 ec
= make_error_code(system::errc::value_too_large
);
1350 // When depth overflow happens, avoid popping the current directory iterator
1351 // and attempt to continue iteration on the current depth.
1352 result
= keep_depth
;
1356 directory_iterator
next(imp
->m_stack
.back()->path(), static_cast< BOOST_SCOPED_ENUM_NATIVE(directory_options
) >(imp
->m_options
), ec
);
1357 if (!ec
&& next
!= directory_iterator())
1359 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
1360 imp
->m_stack
.push_back(std::move(next
)); // may throw
1362 imp
->m_stack
.push_back(next
); // may throw
1364 return directory_pushed
;
1368 catch (std::bad_alloc
&)
1370 ec
= make_error_code(system::errc::not_enough_memory
);
1378 BOOST_FILESYSTEM_DECL
1379 void recursive_directory_iterator_increment(recursive_directory_iterator
& it
, system::error_code
* ec
)
1381 BOOST_ASSERT_MSG(!it
.is_end(), "increment() on end recursive_directory_iterator");
1382 detail::recur_dir_itr_imp
* const imp
= it
.m_imp
.get();
1387 system::error_code local_ec
;
1389 // if various conditions are met, push a directory_iterator into the iterator stack
1390 push_directory_result push_result
= recursive_directory_iterator_push_directory(imp
, local_ec
);
1391 if (push_result
== directory_pushed
)
1394 // report errors if any
1395 if (BOOST_UNLIKELY(!!local_ec
))
1398 if ((imp
->m_options
& static_cast< unsigned int >(directory_options::pop_on_error
)) == 0u)
1400 // Make an end iterator on errors
1405 if ((push_result
& keep_depth
) != 0u)
1407 system::error_code increment_ec
;
1408 directory_iterator
& dir_it
= imp
->m_stack
.back();
1409 detail::directory_iterator_increment(dir_it
, &increment_ec
);
1410 if (!increment_ec
&& dir_it
!= directory_iterator())
1411 goto on_error_return
;
1414 recursive_directory_iterator_pop_on_error(imp
);
1416 if (imp
->m_stack
.empty())
1417 it
.m_imp
.reset(); // done, so make end iterator
1422 BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec
));
1428 // Do the actual increment operation on the top iterator in the iterator
1429 // stack, popping the stack if necessary, until either the stack is empty or a
1430 // non-end iterator is reached.
1433 if (imp
->m_stack
.empty())
1435 it
.m_imp
.reset(); // done, so make end iterator
1439 directory_iterator
& dir_it
= imp
->m_stack
.back();
1440 detail::directory_iterator_increment(dir_it
, &local_ec
);
1441 if (BOOST_UNLIKELY(!!local_ec
))
1444 if (dir_it
!= directory_iterator())
1447 imp
->m_stack
.pop_back();
1451 } // namespace detail
1453 } // namespace filesystem
1454 } // namespace boost