1 | // directory.cpp --------------------------------------------------------------------// |
2 | |
3 | // Copyright 2002-2009, 2014 Beman Dawes |
4 | // Copyright 2001 Dietmar Kuehl |
5 | // Copyright 2019, 2022-2024 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/config.hpp> |
17 | #include <boost/filesystem/directory.hpp> |
18 | #include <boost/filesystem/exception.hpp> |
19 | #include <boost/filesystem/operations.hpp> |
20 | #include <boost/filesystem/file_status.hpp> |
21 | |
22 | #include <cstddef> |
23 | #include <cerrno> |
24 | #include <cstring> |
25 | #include <cstdlib> // std::malloc, std::free |
26 | #include <new> // std::nothrow, std::bad_alloc |
27 | #include <limits> |
28 | #include <string> |
29 | #include <utility> // std::move |
30 | #include <boost/assert.hpp> |
31 | #include <boost/system/error_code.hpp> |
32 | #include <boost/smart_ptr/intrusive_ptr.hpp> |
33 | |
34 | #ifdef BOOST_POSIX_API |
35 | |
36 | #include <sys/types.h> |
37 | #include <sys/stat.h> |
38 | #include <dirent.h> |
39 | #include <unistd.h> |
40 | #include <fcntl.h> |
41 | |
42 | #include <boost/scope/unique_fd.hpp> |
43 | |
44 | #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS >= 0) && defined(_SC_THREAD_SAFE_FUNCTIONS) && \ |
45 | !defined(__CYGWIN__) && \ |
46 | !(defined(linux) || defined(__linux) || defined(__linux__)) && \ |
47 | !defined(__ANDROID__) && \ |
48 | (!defined(__hpux) || defined(_REENTRANT)) && \ |
49 | (!defined(_AIX) || defined(__THREAD_SAFE)) && \ |
50 | !defined(__wasm) |
51 | #define BOOST_FILESYSTEM_USE_READDIR_R |
52 | #endif |
53 | |
54 | // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC |
55 | #ifndef O_CLOEXEC |
56 | #define O_CLOEXEC 0 |
57 | #define BOOST_FILESYSTEM_NO_O_CLOEXEC |
58 | #endif |
59 | |
60 | #include "posix_tools.hpp" |
61 | |
62 | #else // BOOST_WINDOWS_API |
63 | |
64 | #include <cwchar> |
65 | #include <windows.h> |
66 | #include <boost/winapi/basic_types.hpp> // NTSTATUS_ |
67 | |
68 | #include "windows_tools.hpp" |
69 | |
70 | #endif // BOOST_WINDOWS_API |
71 | |
72 | #include "atomic_tools.hpp" |
73 | #include "error_handling.hpp" |
74 | #include "private_config.hpp" |
75 | |
76 | #include <boost/filesystem/detail/header.hpp> // must be the last #include |
77 | |
78 | namespace fs = boost::filesystem; |
79 | using boost::system::error_code; |
80 | using boost::system::system_category; |
81 | |
82 | namespace boost { |
83 | namespace filesystem { |
84 | |
85 | //--------------------------------------------------------------------------------------// |
86 | // // |
87 | // directory_entry // |
88 | // // |
89 | //--------------------------------------------------------------------------------------// |
90 | |
91 | BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const |
92 | { |
93 | system::error_code local_ec; |
94 | m_symlink_status = detail::symlink_status(p: m_path, ec: &local_ec); |
95 | |
96 | if (!filesystem::is_symlink(f: m_symlink_status)) |
97 | { |
98 | // Also works if symlink_status fails - set m_status to status_error as well |
99 | m_status = m_symlink_status; |
100 | |
101 | if (BOOST_UNLIKELY(!!local_ec)) |
102 | { |
103 | if (!ec) |
104 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_entry::refresh" , m_path, local_ec)); |
105 | |
106 | *ec = local_ec; |
107 | return; |
108 | } |
109 | |
110 | if (ec) |
111 | ec->clear(); |
112 | } |
113 | else |
114 | { |
115 | m_status = detail::status(p: m_path, ec); |
116 | } |
117 | } |
118 | |
119 | //--------------------------------------------------------------------------------------// |
120 | // // |
121 | // directory_iterator // |
122 | // // |
123 | //--------------------------------------------------------------------------------------// |
124 | |
125 | namespace detail { |
126 | |
127 | #if defined(BOOST_POSIX_API) |
128 | |
129 | //! Opens a directory file and returns a file descriptor. Returns a negative value in case of error. |
130 | boost::scope::unique_fd open_directory(path const& p, directory_options opts, system::error_code& ec) |
131 | { |
132 | ec.clear(); |
133 | |
134 | int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC; |
135 | |
136 | #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
137 | if ((opts & directory_options::_detail_no_follow) != directory_options::none) |
138 | flags |= O_NOFOLLOW; |
139 | #endif |
140 | |
141 | int res; |
142 | while (true) |
143 | { |
144 | res = ::open(file: p.c_str(), oflag: flags); |
145 | if (BOOST_UNLIKELY(res < 0)) |
146 | { |
147 | const int err = errno; |
148 | if (err == EINTR) |
149 | continue; |
150 | ec = system::error_code(err, system::system_category()); |
151 | return boost::scope::unique_fd(); |
152 | } |
153 | |
154 | break; |
155 | } |
156 | |
157 | #if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC) |
158 | boost::scope::unique_fd fd(res); |
159 | |
160 | res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC); |
161 | if (BOOST_UNLIKELY(res < 0)) |
162 | { |
163 | const int err = errno; |
164 | ec = system::error_code(err, system::system_category()); |
165 | return boost::scope::unique_fd(); |
166 | } |
167 | |
168 | return fd; |
169 | #else |
170 | return boost::scope::unique_fd(res); |
171 | #endif |
172 | } |
173 | |
174 | #if defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
175 | |
176 | //! Opens a directory file and returns a file descriptor. Returns a negative value in case of error. |
177 | boost::scope::unique_fd openat_directory(int basedir_fd, path const& p, directory_options opts, system::error_code& ec) |
178 | { |
179 | ec.clear(); |
180 | |
181 | int flags = O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC; |
182 | |
183 | #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
184 | if ((opts & directory_options::_detail_no_follow) != directory_options::none) |
185 | flags |= O_NOFOLLOW; |
186 | #endif |
187 | |
188 | int res; |
189 | while (true) |
190 | { |
191 | res = ::openat(fd: basedir_fd, file: p.c_str(), oflag: flags); |
192 | if (BOOST_UNLIKELY(res < 0)) |
193 | { |
194 | const int err = errno; |
195 | if (err == EINTR) |
196 | continue; |
197 | ec = system::error_code(err, system::system_category()); |
198 | return boost::scope::unique_fd(); |
199 | } |
200 | |
201 | break; |
202 | } |
203 | |
204 | #if defined(BOOST_FILESYSTEM_NO_O_CLOEXEC) && defined(FD_CLOEXEC) |
205 | boost::scope::unique_fd fd(res); |
206 | |
207 | res = ::fcntl(fd.get(), F_SETFD, FD_CLOEXEC); |
208 | if (BOOST_UNLIKELY(res < 0)) |
209 | { |
210 | const int err = errno; |
211 | ec = system::error_code(err, system::system_category()); |
212 | return boost::scope::unique_fd(); |
213 | } |
214 | |
215 | return fd; |
216 | #else |
217 | return boost::scope::unique_fd(res); |
218 | #endif |
219 | } |
220 | |
221 | #endif // defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
222 | |
223 | #endif // defined(BOOST_POSIX_API) |
224 | |
225 | BOOST_CONSTEXPR_OR_CONST std::size_t = 16u; |
226 | |
227 | BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t ) noexcept |
228 | { |
229 | if (extra_size > 0) |
230 | class_size = (class_size + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u); |
231 | std::size_t total_size = class_size + extra_size; |
232 | |
233 | // Return nullptr on OOM |
234 | void* p = std::malloc(size: total_size); |
235 | if (BOOST_LIKELY(p != nullptr)) |
236 | std::memset(s: p, c: 0, n: total_size); |
237 | return p; |
238 | } |
239 | |
240 | BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t ) noexcept |
241 | { |
242 | std::free(ptr: p); |
243 | } |
244 | |
245 | BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) noexcept |
246 | { |
247 | std::free(ptr: p); |
248 | } |
249 | |
250 | namespace { |
251 | |
252 | inline void* (dir_itr_imp* imp) noexcept |
253 | { |
254 | BOOST_CONSTEXPR_OR_CONST std::size_t = (sizeof(dir_itr_imp) + dir_itr_imp_extra_data_alignment - 1u) & ~(dir_itr_imp_extra_data_alignment - 1u); |
255 | return reinterpret_cast< unsigned char* >(imp) + extra_data_offset; |
256 | } |
257 | |
258 | #ifdef BOOST_POSIX_API |
259 | |
260 | inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept |
261 | { |
262 | if (imp.handle != nullptr) |
263 | { |
264 | DIR* h = static_cast< DIR* >(imp.handle); |
265 | imp.handle = nullptr; |
266 | int err = 0; |
267 | if (BOOST_UNLIKELY(::closedir(h) != 0)) |
268 | { |
269 | err = errno; |
270 | return system::error_code(err, system::system_category()); |
271 | } |
272 | } |
273 | |
274 | return error_code(); |
275 | } |
276 | |
277 | #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
278 | |
279 | // Obtains a file descriptor from the directory iterator |
280 | inline int dir_itr_fd(dir_itr_imp const& imp, system::error_code& ec) |
281 | { |
282 | int fd = ::dirfd(dirp: static_cast< DIR* >(imp.handle)); |
283 | if (BOOST_UNLIKELY(fd < 0)) |
284 | { |
285 | int err = errno; |
286 | ec = system::error_code(err, system::system_category()); |
287 | } |
288 | |
289 | return fd; |
290 | } |
291 | |
292 | #endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
293 | |
294 | #if defined(BOOST_FILESYSTEM_USE_READDIR_R) |
295 | |
296 | // Obtains maximum length of a path, not including the terminating zero |
297 | inline std::size_t get_path_max() |
298 | { |
299 | // this code is based on Stevens and Rago, Advanced Programming in the |
300 | // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49 |
301 | std::size_t max = 0; |
302 | errno = 0; |
303 | long res = ::pathconf("/" , _PC_PATH_MAX); |
304 | if (res < 0) |
305 | { |
306 | #if defined(PATH_MAX) |
307 | max = PATH_MAX; |
308 | #else |
309 | max = 4096; |
310 | #endif |
311 | } |
312 | else |
313 | { |
314 | max = static_cast< std::size_t >(res); // relative root |
315 | #if defined(PATH_MAX) |
316 | if (max < PATH_MAX) |
317 | max = PATH_MAX; |
318 | #endif |
319 | } |
320 | |
321 | if ((max + 1) < sizeof(dirent().d_name)) |
322 | max = sizeof(dirent().d_name) - 1; |
323 | |
324 | return max; |
325 | } |
326 | |
327 | // Returns maximum length of a path, not including the terminating zero |
328 | inline std::size_t path_max() |
329 | { |
330 | static const std::size_t max = get_path_max(); |
331 | return max; |
332 | } |
333 | |
334 | #endif // BOOST_FILESYSTEM_USE_READDIR_R |
335 | |
336 | // *result set to nullptr on end of directory |
337 | #if !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
338 | inline |
339 | #endif |
340 | int readdir_impl(dir_itr_imp& imp, struct dirent** result) |
341 | { |
342 | errno = 0; |
343 | |
344 | struct dirent* p = ::readdir(dirp: static_cast< DIR* >(imp.handle)); |
345 | *result = p; |
346 | if (!p) |
347 | return errno; |
348 | return 0; |
349 | } |
350 | |
351 | #if !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
352 | |
353 | inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result) |
354 | { |
355 | return readdir_impl(imp, result); |
356 | } |
357 | |
358 | #else // !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
359 | |
360 | int readdir_r_impl(dir_itr_imp& imp, struct dirent** result) |
361 | { |
362 | return ::readdir_r |
363 | ( |
364 | static_cast< DIR* >(imp.handle), |
365 | static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)), |
366 | result |
367 | ); |
368 | } |
369 | |
370 | int readdir_select_impl(dir_itr_imp& imp, struct dirent** result); |
371 | |
372 | typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result); |
373 | |
374 | //! Pointer to the actual implementation of the copy_file_data implementation |
375 | readdir_impl_t* readdir_impl_ptr = &readdir_select_impl; |
376 | |
377 | void init_readdir_impl() |
378 | { |
379 | readdir_impl_t* impl = &readdir_impl; |
380 | if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0) |
381 | impl = &readdir_r_impl; |
382 | |
383 | filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl); |
384 | } |
385 | |
386 | struct readdir_initializer |
387 | { |
388 | readdir_initializer() |
389 | { |
390 | init_readdir_impl(); |
391 | } |
392 | }; |
393 | |
394 | BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN |
395 | const readdir_initializer readdir_init; |
396 | |
397 | int readdir_select_impl(dir_itr_imp& imp, struct dirent** result) |
398 | { |
399 | init_readdir_impl(); |
400 | return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result); |
401 | } |
402 | |
403 | inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result) |
404 | { |
405 | return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result); |
406 | } |
407 | |
408 | #endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
409 | |
410 | system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf) |
411 | { |
412 | dirent* result = nullptr; |
413 | int err = invoke_readdir(imp, result: &result); |
414 | if (BOOST_UNLIKELY(err != 0)) |
415 | return system::error_code(err, system::system_category()); |
416 | if (result == nullptr) |
417 | return dir_itr_close(imp); |
418 | |
419 | filename = result->d_name; |
420 | |
421 | #if defined(BOOST_FILESYSTEM_HAS_DIRENT_D_TYPE) |
422 | if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value |
423 | { |
424 | sf = symlink_sf = fs::file_status(fs::status_error); |
425 | } |
426 | else // filesystem supplies d_type value |
427 | { |
428 | if (result->d_type == DT_REG) |
429 | sf = symlink_sf = fs::file_status(fs::regular_file); |
430 | else if (result->d_type == DT_DIR) |
431 | sf = symlink_sf = fs::file_status(fs::directory_file); |
432 | else if (result->d_type == DT_LNK) |
433 | { |
434 | sf = fs::file_status(fs::status_error); |
435 | symlink_sf = fs::file_status(fs::symlink_file); |
436 | } |
437 | else |
438 | { |
439 | switch (result->d_type) |
440 | { |
441 | case DT_SOCK: |
442 | sf = symlink_sf = fs::file_status(fs::socket_file); |
443 | break; |
444 | case DT_FIFO: |
445 | sf = symlink_sf = fs::file_status(fs::fifo_file); |
446 | break; |
447 | case DT_BLK: |
448 | sf = symlink_sf = fs::file_status(fs::block_file); |
449 | break; |
450 | case DT_CHR: |
451 | sf = symlink_sf = fs::file_status(fs::character_file); |
452 | break; |
453 | default: |
454 | sf = symlink_sf = fs::file_status(fs::status_error); |
455 | break; |
456 | } |
457 | } |
458 | } |
459 | #else |
460 | sf = symlink_sf = fs::file_status(fs::status_error); |
461 | #endif |
462 | return system::error_code(); |
463 | } |
464 | |
465 | system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status&, fs::file_status&) |
466 | { |
467 | std::size_t = 0u; |
468 | #if defined(BOOST_FILESYSTEM_USE_READDIR_R) |
469 | { |
470 | readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr); |
471 | if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl)) |
472 | { |
473 | init_readdir_impl(); |
474 | rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr); |
475 | } |
476 | |
477 | if (rdimpl == &readdir_r_impl) |
478 | { |
479 | // According to readdir description, there's no reliable way to predict the length of the d_name string. |
480 | // It may exceed NAME_MAX and pathconf(_PC_NAME_MAX) limits. We are being conservative here and allocate |
481 | // buffer that is enough for PATH_MAX as the directory name. Still, this doesn't guarantee there won't be |
482 | // a buffer overrun. The readdir_r API is fundamentally flawed and we should avoid it as much as possible |
483 | // in favor of readdir. |
484 | extra_size = (sizeof(dirent) - sizeof(dirent().d_name)) + path_max() + 1u; // + 1 for "\0" |
485 | } |
486 | } |
487 | #endif // defined(BOOST_FILESYSTEM_USE_READDIR_R) |
488 | |
489 | boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (extra_size) detail::dir_itr_imp()); |
490 | if (BOOST_UNLIKELY(!pimpl)) |
491 | return make_error_code(e: system::errc::not_enough_memory); |
492 | |
493 | #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
494 | boost::scope::unique_fd fd; |
495 | if (params && params->dir_fd) |
496 | { |
497 | fd = std::move(params->dir_fd); |
498 | } |
499 | else |
500 | { |
501 | system::error_code ec; |
502 | fd = open_directory(p: dir, opts, ec); |
503 | if (BOOST_UNLIKELY(!!ec)) |
504 | return ec; |
505 | } |
506 | |
507 | pimpl->handle = ::fdopendir(fd: fd.get()); |
508 | if (BOOST_UNLIKELY(!pimpl->handle)) |
509 | { |
510 | const int err = errno; |
511 | return system::error_code(err, system::system_category()); |
512 | } |
513 | |
514 | // At this point fd will be closed by closedir |
515 | fd.release(); |
516 | #else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
517 | pimpl->handle = ::opendir(dir.c_str()); |
518 | if (BOOST_UNLIKELY(!pimpl->handle)) |
519 | { |
520 | const int err = errno; |
521 | return system::error_code(err, system::system_category()); |
522 | } |
523 | #endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) |
524 | |
525 | // Force initial readdir call by the caller. This will initialize the actual first filename and statuses. |
526 | first_filename.assign(source: "." ); |
527 | |
528 | imp.swap(rhs&: pimpl); |
529 | return system::error_code(); |
530 | } |
531 | |
532 | BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT; |
533 | |
534 | #else // BOOST_WINDOWS_API |
535 | |
536 | 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) |
537 | { |
538 | if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) |
539 | { |
540 | // Reparse points are complex, so don't try to resolve them here; instead just mark |
541 | // them as status_error which causes directory_entry caching to call status() |
542 | // and symlink_status() which do handle reparse points fully |
543 | if (reparse_point_tag) |
544 | { |
545 | // If we have a reparse point tag we can at least populate the symlink status, |
546 | // consistent with symlink_status() behavior |
547 | symlink_sf.type(is_reparse_point_tag_a_symlink(*reparse_point_tag) ? fs::symlink_file : fs::reparse_file); |
548 | symlink_sf.permissions(make_permissions(filename, attrs)); |
549 | } |
550 | else |
551 | { |
552 | symlink_sf.type(fs::status_error); |
553 | } |
554 | |
555 | sf.type(fs::status_error); |
556 | } |
557 | else |
558 | { |
559 | if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u) |
560 | { |
561 | sf.type(fs::directory_file); |
562 | symlink_sf.type(fs::directory_file); |
563 | } |
564 | else |
565 | { |
566 | sf.type(fs::regular_file); |
567 | symlink_sf.type(fs::regular_file); |
568 | } |
569 | |
570 | sf.permissions(make_permissions(filename, attrs)); |
571 | symlink_sf.permissions(sf.permissions()); |
572 | } |
573 | } |
574 | |
575 | //! FILE_ID_128 definition from Windows SDK |
576 | struct file_id_128 |
577 | { |
578 | BYTE Identifier[16]; |
579 | }; |
580 | |
581 | //! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably). |
582 | struct file_directory_information |
583 | { |
584 | ULONG NextEntryOffset; |
585 | ULONG FileIndex; |
586 | LARGE_INTEGER CreationTime; |
587 | LARGE_INTEGER LastAccessTime; |
588 | LARGE_INTEGER LastWriteTime; |
589 | LARGE_INTEGER ChangeTime; |
590 | LARGE_INTEGER EndOfFile; |
591 | LARGE_INTEGER AllocationSize; |
592 | ULONG FileAttributes; |
593 | ULONG FileNameLength; |
594 | WCHAR FileName[1]; |
595 | }; |
596 | |
597 | //! FILE_ID_BOTH_DIR_INFO definition from Windows SDK. Basic support for directory iteration using GetFileInformationByHandleEx, supported since Windows Vista. |
598 | struct file_id_both_dir_info |
599 | { |
600 | DWORD NextEntryOffset; |
601 | DWORD FileIndex; |
602 | LARGE_INTEGER CreationTime; |
603 | LARGE_INTEGER LastAccessTime; |
604 | LARGE_INTEGER LastWriteTime; |
605 | LARGE_INTEGER ChangeTime; |
606 | LARGE_INTEGER EndOfFile; |
607 | LARGE_INTEGER AllocationSize; |
608 | DWORD FileAttributes; |
609 | DWORD FileNameLength; |
610 | DWORD EaSize; |
611 | CCHAR ShortNameLength; |
612 | WCHAR ShortName[12]; |
613 | LARGE_INTEGER FileId; |
614 | WCHAR FileName[1]; |
615 | }; |
616 | |
617 | //! FILE_FULL_DIR_INFO definition from Windows SDK. More lightweight than FILE_ID_BOTH_DIR_INFO, supported since Windows 8. |
618 | struct file_full_dir_info |
619 | { |
620 | ULONG NextEntryOffset; |
621 | ULONG FileIndex; |
622 | LARGE_INTEGER CreationTime; |
623 | LARGE_INTEGER LastAccessTime; |
624 | LARGE_INTEGER LastWriteTime; |
625 | LARGE_INTEGER ChangeTime; |
626 | LARGE_INTEGER EndOfFile; |
627 | LARGE_INTEGER AllocationSize; |
628 | ULONG FileAttributes; |
629 | ULONG FileNameLength; |
630 | ULONG EaSize; |
631 | WCHAR FileName[1]; |
632 | }; |
633 | |
634 | //! 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. |
635 | struct file_id_extd_dir_info |
636 | { |
637 | ULONG NextEntryOffset; |
638 | ULONG FileIndex; |
639 | LARGE_INTEGER CreationTime; |
640 | LARGE_INTEGER LastAccessTime; |
641 | LARGE_INTEGER LastWriteTime; |
642 | LARGE_INTEGER ChangeTime; |
643 | LARGE_INTEGER EndOfFile; |
644 | LARGE_INTEGER AllocationSize; |
645 | ULONG FileAttributes; |
646 | ULONG FileNameLength; |
647 | ULONG EaSize; |
648 | ULONG ReparsePointTag; |
649 | file_id_128 FileId; |
650 | WCHAR FileName[1]; |
651 | }; |
652 | |
653 | //! Indicates format of the extra data in the directory iterator |
654 | enum extra_data_format |
655 | { |
656 | file_directory_information_format, |
657 | file_id_both_dir_info_format, |
658 | file_full_dir_info_format, |
659 | file_id_extd_dir_info_format |
660 | }; |
661 | |
662 | //! Indicates extra data format that should be used by directory iterator by default |
663 | extra_data_format g_extra_data_format = file_directory_information_format; |
664 | |
665 | /*! |
666 | * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator. |
667 | * |
668 | * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename. |
669 | * NTFS, VFAT, exFAT and ReFS support filenames up to 255 UTF-16/UCS-2 characters. (For ReFS, there is no information |
670 | * on the on-disk format, and it is possible that it supports longer filenames, up to 32768 UTF-16/UCS-2 characters.) |
671 | * The buffer cannot be larger than 64k, because up to Windows 8.1, NtQueryDirectoryFile and GetFileInformationByHandleEx |
672 | * fail with ERROR_INVALID_PARAMETER when trying to retrieve the filenames from a network share. |
673 | */ |
674 | BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = 65536u; |
675 | |
676 | inline system::error_code dir_itr_close(dir_itr_imp& imp) noexcept |
677 | { |
678 | imp.extra_data_format = 0u; |
679 | imp.current_offset = 0u; |
680 | |
681 | if (imp.handle != nullptr) |
682 | { |
683 | if (BOOST_LIKELY(imp.close_handle)) |
684 | ::CloseHandle(imp.handle); |
685 | imp.handle = nullptr; |
686 | } |
687 | |
688 | return error_code(); |
689 | } |
690 | |
691 | system::error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf) |
692 | { |
693 | void* extra_data = get_dir_itr_imp_extra_data(&imp); |
694 | const void* current_data = static_cast< const unsigned char* >(extra_data) + imp.current_offset; |
695 | switch (imp.extra_data_format) |
696 | { |
697 | case file_id_extd_dir_info_format: |
698 | { |
699 | const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data); |
700 | if (data->NextEntryOffset == 0u) |
701 | { |
702 | 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)) |
703 | { |
704 | DWORD error = ::GetLastError(); |
705 | |
706 | dir_itr_close(imp); |
707 | if (error == ERROR_NO_MORE_FILES) |
708 | goto done; |
709 | |
710 | return system::error_code(error, system::system_category()); |
711 | } |
712 | |
713 | imp.current_offset = 0u; |
714 | data = static_cast< const file_id_extd_dir_info* >(extra_data); |
715 | } |
716 | else |
717 | { |
718 | imp.current_offset += data->NextEntryOffset; |
719 | data = reinterpret_cast< const file_id_extd_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); |
720 | } |
721 | |
722 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
723 | set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf); |
724 | } |
725 | break; |
726 | |
727 | case file_full_dir_info_format: |
728 | { |
729 | const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data); |
730 | if (data->NextEntryOffset == 0u) |
731 | { |
732 | 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)) |
733 | { |
734 | DWORD error = ::GetLastError(); |
735 | |
736 | dir_itr_close(imp); |
737 | if (error == ERROR_NO_MORE_FILES) |
738 | goto done; |
739 | |
740 | return system::error_code(error, system::system_category()); |
741 | } |
742 | |
743 | imp.current_offset = 0u; |
744 | data = static_cast< const file_full_dir_info* >(extra_data); |
745 | } |
746 | else |
747 | { |
748 | imp.current_offset += data->NextEntryOffset; |
749 | data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); |
750 | } |
751 | |
752 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
753 | set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf); |
754 | } |
755 | break; |
756 | |
757 | case file_id_both_dir_info_format: |
758 | { |
759 | const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data); |
760 | if (data->NextEntryOffset == 0u) |
761 | { |
762 | 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)) |
763 | { |
764 | DWORD error = ::GetLastError(); |
765 | |
766 | dir_itr_close(imp); |
767 | if (error == ERROR_NO_MORE_FILES) |
768 | goto done; |
769 | |
770 | return system::error_code(error, system::system_category()); |
771 | } |
772 | |
773 | imp.current_offset = 0u; |
774 | data = static_cast< const file_id_both_dir_info* >(extra_data); |
775 | } |
776 | else |
777 | { |
778 | imp.current_offset += data->NextEntryOffset; |
779 | data = reinterpret_cast< const file_id_both_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); |
780 | } |
781 | |
782 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
783 | set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf); |
784 | } |
785 | break; |
786 | |
787 | default: |
788 | { |
789 | const file_directory_information* data = static_cast< const file_directory_information* >(current_data); |
790 | if (data->NextEntryOffset == 0u) |
791 | { |
792 | io_status_block iosb; |
793 | boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api) |
794 | ( |
795 | imp.handle, |
796 | nullptr, // Event |
797 | nullptr, // ApcRoutine |
798 | nullptr, // ApcContext |
799 | &iosb, |
800 | extra_data, |
801 | dir_itr_extra_size, |
802 | file_directory_information_class, |
803 | FALSE, // ReturnSingleEntry |
804 | nullptr, // FileName |
805 | FALSE // RestartScan |
806 | ); |
807 | |
808 | if (!NT_SUCCESS(status)) |
809 | { |
810 | dir_itr_close(imp); |
811 | if (status == STATUS_NO_MORE_FILES) |
812 | goto done; |
813 | |
814 | return system::error_code(translate_ntstatus(status), system::system_category()); |
815 | } |
816 | |
817 | imp.current_offset = 0u; |
818 | data = static_cast< const file_directory_information* >(extra_data); |
819 | } |
820 | else |
821 | { |
822 | imp.current_offset += data->NextEntryOffset; |
823 | data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); |
824 | } |
825 | |
826 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
827 | set_file_statuses(data->FileAttributes, nullptr, filename, sf, symlink_sf); |
828 | } |
829 | break; |
830 | } |
831 | |
832 | done: |
833 | return system::error_code(); |
834 | } |
835 | |
836 | //! Returns \c true if the error code indicates that the OS or the filesystem does not support a particular directory info class |
837 | inline bool is_dir_info_class_not_supported(DWORD error) |
838 | { |
839 | // Some mounted filesystems may not support FILE_ID_128 identifiers, which will cause |
840 | // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER, |
841 | // even though in general the operation is supported by the kernel. SMBv1 returns a special error |
842 | // code ERROR_INVALID_LEVEL in this case. |
843 | // Some other filesystems also don't implement other info classes and return ERROR_INVALID_PARAMETER |
844 | // (e.g. see https://github.com/boostorg/filesystem/issues/266), ERROR_GEN_FAILURE, ERROR_INVALID_FUNCTION |
845 | // or ERROR_INTERNAL_ERROR (https://github.com/boostorg/filesystem/issues/286). Treat these error codes |
846 | // as "non-permanent", even though ERROR_INVALID_PARAMETER is also returned if GetFileInformationByHandleEx |
847 | // in general does not support a certain info class. Worst case, we will make extra syscalls on directory |
848 | // iterator construction. |
849 | // Also note that Wine returns ERROR_CALL_NOT_IMPLEMENTED for unimplemented info classes, and |
850 | // up until 7.21 it didn't implement FileIdExtdDirectoryRestartInfo and FileFullDirectoryRestartInfo. |
851 | // (https://bugs.winehq.org/show_bug.cgi?id=53590) |
852 | return error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER || |
853 | error == ERROR_INVALID_LEVEL || error == ERROR_CALL_NOT_IMPLEMENTED || |
854 | error == ERROR_GEN_FAILURE || error == ERROR_INVALID_FUNCTION || |
855 | error == ERROR_INTERNAL_ERROR; |
856 | } |
857 | |
858 | system::error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, directory_options opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf) |
859 | { |
860 | boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp()); |
861 | if (BOOST_UNLIKELY(!pimpl)) |
862 | return make_error_code(system::errc::not_enough_memory); |
863 | |
864 | GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api); |
865 | |
866 | unique_handle h; |
867 | HANDLE iterator_handle; |
868 | bool close_handle = true; |
869 | if (params != nullptr && params->dir_handle != INVALID_HANDLE_VALUE) |
870 | { |
871 | // Operate on externally provided handle, which must be a directory handle |
872 | iterator_handle = params->dir_handle; |
873 | close_handle = params->close_handle; |
874 | } |
875 | else |
876 | { |
877 | DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; |
878 | if ((opts & directory_options::_detail_no_follow) != directory_options::none) |
879 | flags |= FILE_FLAG_OPEN_REPARSE_POINT; |
880 | |
881 | h = create_file_handle(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, flags); |
882 | if (BOOST_UNLIKELY(!h)) |
883 | { |
884 | return_last_error: |
885 | DWORD error = ::GetLastError(); |
886 | return system::error_code(error, system::system_category()); |
887 | } |
888 | |
889 | iterator_handle = h.get(); |
890 | |
891 | if (BOOST_LIKELY(get_file_information_by_handle_ex != nullptr)) |
892 | { |
893 | file_attribute_tag_info info; |
894 | BOOL res = get_file_information_by_handle_ex(iterator_handle, file_attribute_tag_info_class, &info, sizeof(info)); |
895 | if (BOOST_UNLIKELY(!res)) |
896 | { |
897 | // On FAT/exFAT filesystems requesting FILE_ATTRIBUTE_TAG_INFO returns ERROR_INVALID_PARAMETER. See the comment in symlink_status. |
898 | DWORD error = ::GetLastError(); |
899 | if (error == ERROR_INVALID_PARAMETER || error == ERROR_NOT_SUPPORTED) |
900 | goto use_get_file_information_by_handle; |
901 | |
902 | return system::error_code(error, system::system_category()); |
903 | } |
904 | |
905 | if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) |
906 | return make_error_code(system::errc::not_a_directory); |
907 | |
908 | if ((opts & directory_options::_detail_no_follow) != directory_options::none) |
909 | { |
910 | if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_tag_a_symlink(info.ReparseTag)) |
911 | return make_error_code(system::errc::too_many_symbolic_link_levels); |
912 | } |
913 | } |
914 | else |
915 | { |
916 | use_get_file_information_by_handle: |
917 | BY_HANDLE_FILE_INFORMATION info; |
918 | BOOL res = ::GetFileInformationByHandle(iterator_handle, &info); |
919 | if (BOOST_UNLIKELY(!res)) |
920 | goto return_last_error; |
921 | |
922 | if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) |
923 | return make_error_code(system::errc::not_a_directory); |
924 | |
925 | if ((opts & directory_options::_detail_no_follow) != directory_options::none && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) |
926 | { |
927 | error_code ec; |
928 | const ULONG reparse_point_tag = detail::get_reparse_point_tag_ioctl(iterator_handle, dir, &ec); |
929 | if (BOOST_UNLIKELY(!!ec)) |
930 | return ec; |
931 | |
932 | if (detail::is_reparse_point_tag_a_symlink(reparse_point_tag)) |
933 | return make_error_code(system::errc::too_many_symbolic_link_levels); |
934 | } |
935 | } |
936 | } |
937 | |
938 | void* extra_data = get_dir_itr_imp_extra_data(pimpl.get()); |
939 | switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format)) |
940 | { |
941 | case file_id_extd_dir_info_format: |
942 | { |
943 | if (!get_file_information_by_handle_ex(iterator_handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size)) |
944 | { |
945 | DWORD error = ::GetLastError(); |
946 | |
947 | if (is_dir_info_class_not_supported(error)) |
948 | { |
949 | // Fall back to file_full_dir_info_format. |
950 | if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) |
951 | filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format); |
952 | goto fallback_to_file_full_dir_info_format; |
953 | } |
954 | |
955 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) |
956 | goto done; |
957 | |
958 | return system::error_code(error, system::system_category()); |
959 | } |
960 | |
961 | pimpl->extra_data_format = file_id_extd_dir_info_format; |
962 | |
963 | const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(extra_data); |
964 | first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
965 | |
966 | set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf); |
967 | } |
968 | break; |
969 | |
970 | case file_full_dir_info_format: |
971 | fallback_to_file_full_dir_info_format: |
972 | { |
973 | if (!get_file_information_by_handle_ex(iterator_handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size)) |
974 | { |
975 | DWORD error = ::GetLastError(); |
976 | |
977 | if (is_dir_info_class_not_supported(error)) |
978 | { |
979 | // Fall back to file_id_both_dir_info |
980 | if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) |
981 | filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format); |
982 | goto fallback_to_file_id_both_dir_info_format; |
983 | } |
984 | |
985 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) |
986 | goto done; |
987 | |
988 | return system::error_code(error, system::system_category()); |
989 | } |
990 | |
991 | pimpl->extra_data_format = file_full_dir_info_format; |
992 | |
993 | const file_full_dir_info* data = static_cast< const file_full_dir_info* >(extra_data); |
994 | first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
995 | |
996 | set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf); |
997 | } |
998 | break; |
999 | |
1000 | case file_id_both_dir_info_format: |
1001 | fallback_to_file_id_both_dir_info_format: |
1002 | { |
1003 | if (!get_file_information_by_handle_ex(iterator_handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size)) |
1004 | { |
1005 | DWORD error = ::GetLastError(); |
1006 | |
1007 | if (is_dir_info_class_not_supported(error)) |
1008 | { |
1009 | // Fall back to file_directory_information |
1010 | if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) |
1011 | filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_directory_information_format); |
1012 | goto fallback_to_file_directory_information_format; |
1013 | } |
1014 | |
1015 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) |
1016 | goto done; |
1017 | |
1018 | return system::error_code(error, system::system_category()); |
1019 | } |
1020 | |
1021 | pimpl->extra_data_format = file_id_both_dir_info_format; |
1022 | |
1023 | const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(extra_data); |
1024 | first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
1025 | |
1026 | set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf); |
1027 | } |
1028 | break; |
1029 | |
1030 | default: |
1031 | fallback_to_file_directory_information_format: |
1032 | { |
1033 | NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api); |
1034 | if (BOOST_UNLIKELY(!nt_query_directory_file)) |
1035 | return error_code(ERROR_NOT_SUPPORTED, system_category()); |
1036 | |
1037 | io_status_block iosb; |
1038 | boost::winapi::NTSTATUS_ status = nt_query_directory_file |
1039 | ( |
1040 | iterator_handle, |
1041 | nullptr, // Event |
1042 | nullptr, // ApcRoutine |
1043 | nullptr, // ApcContext |
1044 | &iosb, |
1045 | extra_data, |
1046 | dir_itr_extra_size, |
1047 | file_directory_information_class, |
1048 | FALSE, // ReturnSingleEntry |
1049 | nullptr, // FileName |
1050 | TRUE // RestartScan |
1051 | ); |
1052 | |
1053 | if (!NT_SUCCESS(status)) |
1054 | { |
1055 | // Note: an empty root directory has no "." or ".." entries, so this |
1056 | // causes a ERROR_FILE_NOT_FOUND error returned from FindFirstFileW |
1057 | // (which is presumably equivalent to STATUS_NO_SUCH_FILE) which we |
1058 | // do not consider an error. It is treated as eof instead. |
1059 | if (status == STATUS_NO_MORE_FILES || status == STATUS_NO_SUCH_FILE) |
1060 | goto done; |
1061 | |
1062 | return error_code(translate_ntstatus(status), system_category()); |
1063 | } |
1064 | |
1065 | pimpl->extra_data_format = file_directory_information_format; |
1066 | |
1067 | const file_directory_information* data = static_cast< const file_directory_information* >(extra_data); |
1068 | first_filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); |
1069 | |
1070 | set_file_statuses(data->FileAttributes, nullptr, first_filename, sf, symlink_sf); |
1071 | } |
1072 | break; |
1073 | } |
1074 | |
1075 | pimpl->handle = iterator_handle; |
1076 | h.release(); |
1077 | pimpl->close_handle = close_handle; |
1078 | |
1079 | done: |
1080 | imp.swap(pimpl); |
1081 | return system::error_code(); |
1082 | } |
1083 | |
1084 | BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND; |
1085 | |
1086 | #endif // BOOST_WINDOWS_API |
1087 | |
1088 | } // namespace |
1089 | |
1090 | #if defined(BOOST_WINDOWS_API) |
1091 | |
1092 | //! Initializes directory iterator implementation |
1093 | void init_directory_iterator_impl() noexcept |
1094 | { |
1095 | if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != nullptr) |
1096 | { |
1097 | // Enable the latest format we support. It will get downgraded, if needed, as we attempt |
1098 | // to create the directory iterator the first time. |
1099 | filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_extd_dir_info_format); |
1100 | } |
1101 | } |
1102 | |
1103 | #endif // defined(BOOST_WINDOWS_API) |
1104 | |
1105 | BOOST_FILESYSTEM_DECL |
1106 | dir_itr_imp::~dir_itr_imp() noexcept |
1107 | { |
1108 | dir_itr_close(imp&: *this); |
1109 | } |
1110 | |
1111 | BOOST_FILESYSTEM_DECL |
1112 | void directory_iterator_construct(directory_iterator& it, path const& p, directory_options opts, directory_iterator_params* params, system::error_code* ec) |
1113 | { |
1114 | // At most one of the two options may be specified, and follow_directory_symlink is ignored for directory_iterator. |
1115 | BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)); |
1116 | |
1117 | if (BOOST_UNLIKELY(p.empty())) |
1118 | { |
1119 | emit_error(error_num: not_found_error_code, p, ec, message: "boost::filesystem::directory_iterator::construct" ); |
1120 | return; |
1121 | } |
1122 | |
1123 | if (ec) |
1124 | ec->clear(); |
1125 | |
1126 | try |
1127 | { |
1128 | boost::intrusive_ptr< detail::dir_itr_imp > imp; |
1129 | path filename; |
1130 | file_status file_stat, symlink_file_stat; |
1131 | system::error_code result = dir_itr_create(imp, dir: p, opts, params, first_filename&: filename, file_stat, symlink_file_stat); |
1132 | |
1133 | while (true) |
1134 | { |
1135 | if (result) |
1136 | { |
1137 | if (result != make_error_condition(e: system::errc::permission_denied) || |
1138 | (opts & directory_options::skip_permission_denied) == directory_options::none) |
1139 | { |
1140 | if (!ec) |
1141 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct" , p, result)); |
1142 | *ec = result; |
1143 | } |
1144 | |
1145 | return; |
1146 | } |
1147 | |
1148 | if (imp->handle == nullptr) // eof, make end |
1149 | return; |
1150 | |
1151 | // Not eof |
1152 | const path::string_type::value_type* filename_str = filename.c_str(); |
1153 | if (!(filename_str[0] == path::dot // dot or dot-dot |
1154 | && (filename_str[1] == static_cast< path::string_type::value_type >('\0') || |
1155 | (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))) |
1156 | { |
1157 | path full_path(p); |
1158 | path_algorithms::append_v4(left&: full_path, right: filename); |
1159 | imp->dir_entry.assign_with_status |
1160 | ( |
1161 | p: static_cast< path&& >(full_path), |
1162 | st: file_stat, |
1163 | symlink_st: symlink_file_stat |
1164 | ); |
1165 | it.m_imp.swap(rhs&: imp); |
1166 | return; |
1167 | } |
1168 | |
1169 | // If dot or dot-dot name produced by the underlying API, skip it until the first actual file |
1170 | result = dir_itr_increment(imp&: *imp, filename, sf&: file_stat, symlink_sf&: symlink_file_stat); |
1171 | } |
1172 | } |
1173 | catch (std::bad_alloc&) |
1174 | { |
1175 | if (!ec) |
1176 | throw; |
1177 | |
1178 | *ec = make_error_code(e: system::errc::not_enough_memory); |
1179 | it.m_imp.reset(); |
1180 | } |
1181 | } |
1182 | |
1183 | BOOST_FILESYSTEM_DECL |
1184 | void directory_iterator_increment(directory_iterator& it, system::error_code* ec) |
1185 | { |
1186 | BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator" ); |
1187 | |
1188 | if (ec) |
1189 | ec->clear(); |
1190 | |
1191 | try |
1192 | { |
1193 | path filename; |
1194 | file_status file_stat, symlink_file_stat; |
1195 | system::error_code increment_ec; |
1196 | |
1197 | while (true) |
1198 | { |
1199 | increment_ec = dir_itr_increment(imp&: *it.m_imp, filename, sf&: file_stat, symlink_sf&: symlink_file_stat); |
1200 | |
1201 | if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc |
1202 | { |
1203 | boost::intrusive_ptr< detail::dir_itr_imp > imp; |
1204 | imp.swap(rhs&: it.m_imp); |
1205 | path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900 |
1206 | if (!ec) |
1207 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++" , error_path, increment_ec)); |
1208 | |
1209 | *ec = increment_ec; |
1210 | return; |
1211 | } |
1212 | |
1213 | if (it.m_imp->handle == nullptr) // eof, make end |
1214 | { |
1215 | it.m_imp.reset(); |
1216 | return; |
1217 | } |
1218 | |
1219 | const path::string_type::value_type* filename_str = filename.c_str(); |
1220 | if (!(filename_str[0] == path::dot // !(dot or dot-dot) |
1221 | && (filename_str[1] == static_cast< path::string_type::value_type >('\0') || |
1222 | (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))) |
1223 | { |
1224 | it.m_imp->dir_entry.replace_filename_with_status(p: filename, st: file_stat, symlink_st: symlink_file_stat); |
1225 | return; |
1226 | } |
1227 | } |
1228 | } |
1229 | catch (std::bad_alloc&) |
1230 | { |
1231 | if (!ec) |
1232 | throw; |
1233 | |
1234 | it.m_imp.reset(); |
1235 | *ec = make_error_code(e: system::errc::not_enough_memory); |
1236 | } |
1237 | } |
1238 | |
1239 | //--------------------------------------------------------------------------------------// |
1240 | // // |
1241 | // recursive_directory_iterator // |
1242 | // // |
1243 | //--------------------------------------------------------------------------------------// |
1244 | |
1245 | BOOST_FILESYSTEM_DECL |
1246 | void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, directory_options opts, system::error_code* ec) |
1247 | { |
1248 | // At most one of the two options may be specified |
1249 | BOOST_ASSERT((opts & (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)) != (directory_options::follow_directory_symlink | directory_options::_detail_no_follow)); |
1250 | |
1251 | if (ec) |
1252 | ec->clear(); |
1253 | |
1254 | directory_iterator dir_it; |
1255 | detail::directory_iterator_construct(it&: dir_it, p: dir_path, opts, params: nullptr, ec); |
1256 | if ((ec && *ec) || dir_it == directory_iterator()) |
1257 | return; |
1258 | |
1259 | boost::intrusive_ptr< detail::recur_dir_itr_imp > imp; |
1260 | if (!ec) |
1261 | { |
1262 | imp = new detail::recur_dir_itr_imp(opts); |
1263 | } |
1264 | else |
1265 | { |
1266 | imp = new (std::nothrow) detail::recur_dir_itr_imp(opts); |
1267 | if (BOOST_UNLIKELY(!imp)) |
1268 | { |
1269 | *ec = make_error_code(e: system::errc::not_enough_memory); |
1270 | return; |
1271 | } |
1272 | } |
1273 | |
1274 | try |
1275 | { |
1276 | imp->m_stack.push_back(x: std::move(dir_it)); |
1277 | it.m_imp.swap(rhs&: imp); |
1278 | } |
1279 | catch (std::bad_alloc&) |
1280 | { |
1281 | if (ec) |
1282 | { |
1283 | *ec = make_error_code(e: system::errc::not_enough_memory); |
1284 | return; |
1285 | } |
1286 | |
1287 | throw; |
1288 | } |
1289 | } |
1290 | |
1291 | namespace { |
1292 | |
1293 | void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp) |
1294 | { |
1295 | imp->m_stack.pop_back(); |
1296 | |
1297 | while (!imp->m_stack.empty()) |
1298 | { |
1299 | directory_iterator& dir_it = imp->m_stack.back(); |
1300 | system::error_code increment_ec; |
1301 | detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec); |
1302 | if (!increment_ec && dir_it != directory_iterator()) |
1303 | break; |
1304 | |
1305 | imp->m_stack.pop_back(); |
1306 | } |
1307 | } |
1308 | |
1309 | } // namespace |
1310 | |
1311 | BOOST_FILESYSTEM_DECL |
1312 | void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec) |
1313 | { |
1314 | BOOST_ASSERT_MSG(!it.is_end(), "pop() on end recursive_directory_iterator" ); |
1315 | detail::recur_dir_itr_imp* const imp = it.m_imp.get(); |
1316 | |
1317 | if (ec) |
1318 | ec->clear(); |
1319 | |
1320 | imp->m_stack.pop_back(); |
1321 | |
1322 | while (true) |
1323 | { |
1324 | if (imp->m_stack.empty()) |
1325 | { |
1326 | it.m_imp.reset(); // done, so make end iterator |
1327 | break; |
1328 | } |
1329 | |
1330 | directory_iterator& dir_it = imp->m_stack.back(); |
1331 | system::error_code increment_ec; |
1332 | detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec); |
1333 | if (BOOST_UNLIKELY(!!increment_ec)) |
1334 | { |
1335 | if ((imp->m_options & directory_options::pop_on_error) == directory_options::none) |
1336 | { |
1337 | // Make an end iterator on errors |
1338 | it.m_imp.reset(); |
1339 | } |
1340 | else |
1341 | { |
1342 | recursive_directory_iterator_pop_on_error(imp); |
1343 | |
1344 | if (imp->m_stack.empty()) |
1345 | it.m_imp.reset(); // done, so make end iterator |
1346 | } |
1347 | |
1348 | if (!ec) |
1349 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop" , increment_ec)); |
1350 | |
1351 | *ec = increment_ec; |
1352 | return; |
1353 | } |
1354 | |
1355 | if (dir_it != directory_iterator()) |
1356 | break; |
1357 | |
1358 | imp->m_stack.pop_back(); |
1359 | } |
1360 | } |
1361 | |
1362 | BOOST_FILESYSTEM_DECL |
1363 | void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec) |
1364 | { |
1365 | enum push_directory_result : unsigned int |
1366 | { |
1367 | directory_not_pushed = 0u, |
1368 | directory_pushed = 1u, |
1369 | keep_depth = 1u << 1u |
1370 | }; |
1371 | |
1372 | struct local |
1373 | { |
1374 | //! Attempts to descend into a directory |
1375 | static push_directory_result push_directory(detail::recur_dir_itr_imp* imp, system::error_code& ec) |
1376 | { |
1377 | push_directory_result result = directory_not_pushed; |
1378 | try |
1379 | { |
1380 | // Discover if the iterator is for a directory that needs to be recursed into, |
1381 | // taking symlinks and options into account. |
1382 | |
1383 | if ((imp->m_options & directory_options::_detail_no_push) != directory_options::none) |
1384 | { |
1385 | imp->m_options &= ~directory_options::_detail_no_push; |
1386 | return result; |
1387 | } |
1388 | |
1389 | file_type symlink_ft = status_error; |
1390 | |
1391 | #if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1392 | int parentdir_fd = -1; |
1393 | path dir_it_filename; |
1394 | #elif defined(BOOST_WINDOWS_API) |
1395 | unique_handle direntry_handle; |
1396 | #endif |
1397 | |
1398 | // If we are not recursing into symlinks, we are going to have to know if the |
1399 | // stack top is a symlink, so get symlink_status and verify no error occurred. |
1400 | if ((imp->m_options & directory_options::follow_directory_symlink) == directory_options::none || |
1401 | (imp->m_options & directory_options::skip_dangling_symlinks) != directory_options::none) |
1402 | { |
1403 | #if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1404 | directory_iterator const& dir_it = imp->m_stack.back(); |
1405 | if (filesystem::type_present(f: dir_it->m_symlink_status)) |
1406 | { |
1407 | symlink_ft = dir_it->m_symlink_status.type(); |
1408 | } |
1409 | else |
1410 | { |
1411 | parentdir_fd = dir_itr_fd(imp: *dir_it.m_imp, ec); |
1412 | if (ec) |
1413 | return result; |
1414 | |
1415 | dir_it_filename = detail::path_algorithms::filename_v4(p: dir_it->path()); |
1416 | |
1417 | symlink_ft = detail::symlink_status_impl(p: dir_it_filename, ec: &ec, basedir_fd: parentdir_fd).type(); |
1418 | if (ec) |
1419 | return result; |
1420 | } |
1421 | #elif defined(BOOST_WINDOWS_API) |
1422 | directory_iterator const& dir_it = imp->m_stack.back(); |
1423 | if (filesystem::type_present(dir_it->m_symlink_status)) |
1424 | { |
1425 | symlink_ft = dir_it->m_symlink_status.type(); |
1426 | } |
1427 | else |
1428 | { |
1429 | boost::winapi::NTSTATUS_ status = nt_create_file_handle_at |
1430 | ( |
1431 | direntry_handle, |
1432 | static_cast< HANDLE >(dir_it.m_imp->handle), |
1433 | detail::path_algorithms::filename_v4(dir_it->path()), |
1434 | 0u, // FileAttributes |
1435 | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE, |
1436 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
1437 | FILE_OPEN, |
1438 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT |
1439 | ); |
1440 | |
1441 | if (NT_SUCCESS(status)) |
1442 | { |
1443 | symlink_ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type(); |
1444 | } |
1445 | else if (status == STATUS_NOT_IMPLEMENTED) |
1446 | { |
1447 | symlink_ft = dir_it->symlink_file_type(ec); |
1448 | } |
1449 | else |
1450 | { |
1451 | if (!not_found_ntstatus(status)) |
1452 | ec.assign(translate_ntstatus(status), system::system_category()); |
1453 | |
1454 | return result; |
1455 | } |
1456 | |
1457 | if (ec) |
1458 | return result; |
1459 | } |
1460 | #else |
1461 | symlink_ft = imp->m_stack.back()->symlink_file_type(ec); |
1462 | if (ec) |
1463 | return result; |
1464 | #endif |
1465 | } |
1466 | |
1467 | // Logic for following predicate was contributed by Daniel Aarno to handle cyclic |
1468 | // symlinks correctly and efficiently, fixing ticket #5652. |
1469 | // if (((m_options & directory_options::follow_directory_symlink) == directory_options::follow_directory_symlink |
1470 | // || !is_symlink(m_stack.back()->symlink_status())) |
1471 | // && is_directory(m_stack.back()->status())) ... |
1472 | // The predicate code has since been rewritten to pass error_code arguments, |
1473 | // per ticket #5653. |
1474 | |
1475 | if ((imp->m_options & directory_options::follow_directory_symlink) != directory_options::none || symlink_ft != symlink_file) |
1476 | { |
1477 | directory_iterator const& dir_it = imp->m_stack.back(); |
1478 | |
1479 | // Don't query the file type from the filesystem yet, if not known. We will use dir_it for that below. |
1480 | file_type ft = dir_it->m_status.type(); |
1481 | if (ft != status_error && ft != directory_file) |
1482 | return result; |
1483 | |
1484 | #if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1485 | if (parentdir_fd < 0) |
1486 | { |
1487 | parentdir_fd = dir_itr_fd(imp: *dir_it.m_imp, ec); |
1488 | if (ec) |
1489 | return result; |
1490 | |
1491 | dir_it_filename = detail::path_algorithms::filename_v4(p: dir_it->path()); |
1492 | } |
1493 | |
1494 | // Try to open the file as a directory right away. This effectively tests whether the file is a directory, and, if it is, opens the directory in one system call. |
1495 | detail::directory_iterator_params params{ .dir_fd: detail::openat_directory(basedir_fd: parentdir_fd, p: dir_it_filename, opts: imp->m_options, ec) }; |
1496 | if (!!ec) |
1497 | { |
1498 | if |
1499 | ( |
1500 | // Skip non-directory files |
1501 | ec == system::error_code(ENOTDIR, system::system_category()) || |
1502 | ( |
1503 | // Skip dangling symlink, if requested by options |
1504 | ec == system::error_code(ENOENT, system::system_category()) && symlink_ft == symlink_file && |
1505 | (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks) |
1506 | ) |
1507 | ) |
1508 | { |
1509 | ec.clear(); |
1510 | } |
1511 | |
1512 | return result; |
1513 | } |
1514 | #else // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1515 | #if defined(BOOST_WINDOWS_API) |
1516 | if (!!direntry_handle && symlink_ft == symlink_file) |
1517 | { |
1518 | // Close the symlink to reopen the target file below |
1519 | direntry_handle.reset(); |
1520 | } |
1521 | |
1522 | if (!direntry_handle) |
1523 | { |
1524 | boost::winapi::NTSTATUS_ status = nt_create_file_handle_at |
1525 | ( |
1526 | direntry_handle, |
1527 | static_cast< HANDLE >(dir_it.m_imp->handle), |
1528 | detail::path_algorithms::filename_v4(dir_it->path()), |
1529 | 0u, // FileAttributes |
1530 | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE, |
1531 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
1532 | FILE_OPEN, |
1533 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT |
1534 | ); |
1535 | |
1536 | if (NT_SUCCESS(status)) |
1537 | { |
1538 | goto get_file_type_by_handle; |
1539 | } |
1540 | else if (status == STATUS_NOT_IMPLEMENTED) |
1541 | { |
1542 | ft = dir_it->file_type(ec); |
1543 | } |
1544 | else |
1545 | { |
1546 | ec.assign(translate_ntstatus(status), system::system_category()); |
1547 | } |
1548 | } |
1549 | else |
1550 | { |
1551 | get_file_type_by_handle: |
1552 | ft = detail::status_by_handle(direntry_handle.get(), dir_it->path(), &ec).type(); |
1553 | } |
1554 | #else // defined(BOOST_WINDOWS_API) |
1555 | if (ft == status_error) |
1556 | ft = dir_it->file_type(ec); |
1557 | #endif // defined(BOOST_WINDOWS_API) |
1558 | |
1559 | if (BOOST_UNLIKELY(!!ec)) |
1560 | { |
1561 | if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file && |
1562 | (imp->m_options & (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == (directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) |
1563 | { |
1564 | // Skip dangling symlink and continue iteration on the current depth level |
1565 | ec.clear(); |
1566 | } |
1567 | |
1568 | return result; |
1569 | } |
1570 | |
1571 | if (ft != directory_file) |
1572 | return result; |
1573 | #endif // defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1574 | |
1575 | if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)()))) |
1576 | { |
1577 | // We cannot let depth to overflow |
1578 | ec = make_error_code(e: system::errc::value_too_large); |
1579 | // When depth overflow happens, avoid popping the current directory iterator |
1580 | // and attempt to continue iteration on the current depth. |
1581 | result = keep_depth; |
1582 | return result; |
1583 | } |
1584 | |
1585 | #if defined(BOOST_POSIX_API) && defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) |
1586 | directory_iterator next; |
1587 | detail::directory_iterator_construct(it&: next, p: dir_it->path(), opts: imp->m_options, params: ¶ms, ec: &ec); |
1588 | #elif defined(BOOST_WINDOWS_API) |
1589 | detail::directory_iterator_params params; |
1590 | params.dir_handle = direntry_handle.get(); |
1591 | params.close_handle = true; |
1592 | directory_iterator next; |
1593 | detail::directory_iterator_construct(next, dir_it->path(), imp->m_options, ¶ms, &ec); |
1594 | #else |
1595 | directory_iterator next(dir_it->path(), imp->m_options, ec); |
1596 | #endif |
1597 | if (BOOST_LIKELY(!ec)) |
1598 | { |
1599 | #if defined(BOOST_WINDOWS_API) |
1600 | direntry_handle.release(); |
1601 | #endif |
1602 | if (!next.is_end()) |
1603 | { |
1604 | imp->m_stack.push_back(x: std::move(next)); // may throw |
1605 | return directory_pushed; |
1606 | } |
1607 | } |
1608 | } |
1609 | } |
1610 | catch (std::bad_alloc&) |
1611 | { |
1612 | ec = make_error_code(e: system::errc::not_enough_memory); |
1613 | } |
1614 | |
1615 | return result; |
1616 | } |
1617 | }; |
1618 | |
1619 | BOOST_ASSERT_MSG(!it.is_end(), "increment() on end recursive_directory_iterator" ); |
1620 | detail::recur_dir_itr_imp* const imp = it.m_imp.get(); |
1621 | |
1622 | if (ec) |
1623 | ec->clear(); |
1624 | |
1625 | system::error_code local_ec; |
1626 | |
1627 | // if various conditions are met, push a directory_iterator into the iterator stack |
1628 | push_directory_result push_result = local::push_directory(imp, ec&: local_ec); |
1629 | if (push_result == directory_pushed) |
1630 | return; |
1631 | |
1632 | // report errors if any |
1633 | if (BOOST_UNLIKELY(!!local_ec)) |
1634 | { |
1635 | on_error: |
1636 | if ((imp->m_options & directory_options::pop_on_error) == directory_options::none) |
1637 | { |
1638 | // Make an end iterator on errors |
1639 | it.m_imp.reset(); |
1640 | } |
1641 | else |
1642 | { |
1643 | if ((push_result & keep_depth) != 0u) |
1644 | { |
1645 | system::error_code increment_ec; |
1646 | directory_iterator& dir_it = imp->m_stack.back(); |
1647 | detail::directory_iterator_increment(it&: dir_it, ec: &increment_ec); |
1648 | if (!increment_ec && !dir_it.is_end()) |
1649 | goto on_error_return; |
1650 | } |
1651 | |
1652 | recursive_directory_iterator_pop_on_error(imp); |
1653 | |
1654 | if (imp->m_stack.empty()) |
1655 | it.m_imp.reset(); // done, so make end iterator |
1656 | } |
1657 | |
1658 | on_error_return: |
1659 | if (!ec) |
1660 | BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error" , local_ec)); |
1661 | |
1662 | *ec = local_ec; |
1663 | return; |
1664 | } |
1665 | |
1666 | // Do the actual increment operation on the top iterator in the iterator |
1667 | // stack, popping the stack if necessary, until either the stack is empty or a |
1668 | // non-end iterator is reached. |
1669 | while (true) |
1670 | { |
1671 | if (imp->m_stack.empty()) |
1672 | { |
1673 | it.m_imp.reset(); // done, so make end iterator |
1674 | break; |
1675 | } |
1676 | |
1677 | directory_iterator& dir_it = imp->m_stack.back(); |
1678 | detail::directory_iterator_increment(it&: dir_it, ec: &local_ec); |
1679 | if (BOOST_UNLIKELY(!!local_ec)) |
1680 | goto on_error; |
1681 | |
1682 | if (!dir_it.is_end()) |
1683 | break; |
1684 | |
1685 | imp->m_stack.pop_back(); |
1686 | } |
1687 | } |
1688 | |
1689 | } // namespace detail |
1690 | |
1691 | } // namespace filesystem |
1692 | } // namespace boost |
1693 | |
1694 | #include <boost/filesystem/detail/footer.hpp> |
1695 | |