]>
Commit | Line | Data |
---|---|---|
20effc67 | 1 | // directory.cpp --------------------------------------------------------------------// |
92f5a8d4 TL |
2 | |
3 | // Copyright 2002-2009, 2014 Beman Dawes | |
4 | // Copyright 2001 Dietmar Kuehl | |
1e59de90 | 5 | // Copyright 2019, 2022 Andrey Semashev |
92f5a8d4 TL |
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 | ||
20effc67 | 14 | #include "platform_config.hpp" |
92f5a8d4 | 15 | |
1e59de90 TL |
16 | #include <boost/throw_exception.hpp> |
17 | #include <boost/filesystem/config.hpp> | |
92f5a8d4 TL |
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> | |
22 | ||
23 | #include <cstddef> | |
24 | #include <cerrno> | |
25 | #include <cstring> | |
26 | #include <cstdlib> // std::malloc, std::free | |
1e59de90 | 27 | #include <new> // std::nothrow, std::bad_alloc |
92f5a8d4 TL |
28 | #include <limits> |
29 | #include <string> | |
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> | |
34 | ||
35 | #ifdef BOOST_POSIX_API | |
36 | ||
1e59de90 TL |
37 | #include <sys/types.h> |
38 | #include <sys/stat.h> | |
92f5a8d4 TL |
39 | #include <dirent.h> |
40 | #include <unistd.h> | |
41 | #include <fcntl.h> | |
42 | ||
1e59de90 TL |
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)) && \ | |
49 | !defined(__wasm) | |
92f5a8d4 TL |
50 | #define BOOST_FILESYSTEM_USE_READDIR_R |
51 | #endif | |
52 | ||
1e59de90 TL |
53 | // At least Mac OS X 10.6 and older doesn't support O_CLOEXEC |
54 | #ifndef O_CLOEXEC | |
55 | #define O_CLOEXEC 0 | |
56 | #define BOOST_FILESYSTEM_NO_O_CLOEXEC | |
57 | #endif | |
58 | ||
59 | #include "posix_tools.hpp" | |
60 | ||
92f5a8d4 TL |
61 | #else // BOOST_WINDOWS_API |
62 | ||
63 | #include <cwchar> | |
64 | #include <windows.h> | |
1e59de90 | 65 | #include <boost/winapi/basic_types.hpp> // NTSTATUS_ |
92f5a8d4 TL |
66 | |
67 | #include "windows_tools.hpp" | |
68 | ||
1e59de90 | 69 | #endif // BOOST_WINDOWS_API |
92f5a8d4 | 70 | |
1e59de90 | 71 | #include "atomic_tools.hpp" |
92f5a8d4 | 72 | #include "error_handling.hpp" |
1e59de90 | 73 | #include "private_config.hpp" |
92f5a8d4 TL |
74 | |
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. | |
78 | // | |
79 | // TODO: find out what macros indicate dirent::d_type present in more libraries | |
1e59de90 | 80 | #if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present |
92f5a8d4 TL |
81 | #define BOOST_FILESYSTEM_STATUS_CACHE |
82 | #endif | |
83 | ||
84 | namespace fs = boost::filesystem; | |
85 | using boost::system::error_code; | |
86 | using boost::system::system_category; | |
87 | ||
88 | namespace boost { | |
89 | namespace filesystem { | |
90 | ||
91 | //--------------------------------------------------------------------------------------// | |
92 | // // | |
93 | // directory_entry // | |
94 | // // | |
95 | //--------------------------------------------------------------------------------------// | |
96 | ||
97 | BOOST_FILESYSTEM_DECL | |
98 | file_status directory_entry::get_status(system::error_code* ec) const | |
99 | { | |
1e59de90 | 100 | if (!status_known(m_status)) |
92f5a8d4 | 101 | { |
1e59de90 TL |
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)) | |
106 | { | |
107 | m_status = m_symlink_status; | |
108 | if (ec) | |
109 | ec->clear(); | |
110 | } | |
111 | else | |
112 | { | |
113 | m_status = detail::status(m_path, ec); | |
114 | } | |
92f5a8d4 | 115 | } |
1e59de90 | 116 | else if (ec) |
92f5a8d4 | 117 | { |
1e59de90 | 118 | ec->clear(); |
92f5a8d4 | 119 | } |
92f5a8d4 | 120 | |
1e59de90 | 121 | return m_status; |
92f5a8d4 TL |
122 | } |
123 | ||
124 | BOOST_FILESYSTEM_DECL | |
125 | file_status directory_entry::get_symlink_status(system::error_code* ec) const | |
126 | { | |
1e59de90 TL |
127 | if (!status_known(m_symlink_status)) |
128 | m_symlink_status = detail::symlink_status(m_path, ec); | |
129 | else if (ec) | |
130 | ec->clear(); | |
92f5a8d4 | 131 | |
1e59de90 | 132 | return m_symlink_status; |
92f5a8d4 TL |
133 | } |
134 | ||
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 | |
138 | ||
139 | namespace path_traits { | |
140 | ||
1e59de90 TL |
141 | BOOST_FILESYSTEM_DECL |
142 | void dispatch(directory_entry const& de, | |
92f5a8d4 | 143 | #ifdef BOOST_WINDOWS_API |
1e59de90 | 144 | std::wstring& to, |
92f5a8d4 | 145 | #else |
1e59de90 | 146 | std::string& to, |
92f5a8d4 | 147 | #endif |
1e59de90 | 148 | codecvt_type const&) |
92f5a8d4 | 149 | { |
1e59de90 | 150 | to = de.path().native(); |
92f5a8d4 TL |
151 | } |
152 | ||
1e59de90 TL |
153 | BOOST_FILESYSTEM_DECL |
154 | void dispatch(directory_entry const& de, | |
92f5a8d4 | 155 | #ifdef BOOST_WINDOWS_API |
1e59de90 | 156 | std::wstring& to |
92f5a8d4 | 157 | #else |
1e59de90 | 158 | std::string& to |
92f5a8d4 | 159 | #endif |
1e59de90 | 160 | ) |
92f5a8d4 | 161 | { |
1e59de90 | 162 | to = de.path().native(); |
92f5a8d4 TL |
163 | } |
164 | ||
165 | } // namespace path_traits | |
166 | ||
167 | //--------------------------------------------------------------------------------------// | |
168 | // // | |
169 | // directory_iterator // | |
170 | // // | |
171 | //--------------------------------------------------------------------------------------// | |
172 | ||
173 | namespace detail { | |
174 | ||
1e59de90 TL |
175 | BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_imp_extra_data_alignment = 16u; |
176 | ||
177 | BOOST_FILESYSTEM_DECL void* dir_itr_imp::operator new(std::size_t class_size, std::size_t extra_size) BOOST_NOEXCEPT | |
178 | { | |
179 | if (extra_size > 0) | |
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; | |
182 | ||
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); | |
187 | return p; | |
188 | } | |
189 | ||
190 | BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p, std::size_t extra_size) BOOST_NOEXCEPT | |
191 | { | |
192 | std::free(p); | |
193 | } | |
194 | ||
195 | BOOST_FILESYSTEM_DECL void dir_itr_imp::operator delete(void* p) BOOST_NOEXCEPT | |
196 | { | |
197 | std::free(p); | |
198 | } | |
199 | ||
92f5a8d4 TL |
200 | namespace { |
201 | ||
1e59de90 TL |
202 | inline void* get_dir_itr_imp_extra_data(dir_itr_imp* imp) BOOST_NOEXCEPT |
203 | { | |
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; | |
206 | } | |
207 | ||
92f5a8d4 TL |
208 | #ifdef BOOST_POSIX_API |
209 | ||
1e59de90 TL |
210 | inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT |
211 | { | |
212 | if (imp.handle != NULL) | |
213 | { | |
214 | DIR* h = static_cast< DIR* >(imp.handle); | |
215 | imp.handle = NULL; | |
216 | int err = 0; | |
217 | if (BOOST_UNLIKELY(::closedir(h) != 0)) | |
218 | { | |
219 | err = errno; | |
220 | return error_code(err, system_category()); | |
221 | } | |
222 | } | |
223 | ||
224 | return error_code(); | |
225 | } | |
226 | ||
92f5a8d4 TL |
227 | #if defined(BOOST_FILESYSTEM_USE_READDIR_R) |
228 | ||
229 | // Obtains maximum length of a path, not including the terminating zero | |
230 | inline std::size_t get_path_max() | |
231 | { | |
1e59de90 TL |
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 | |
234 | std::size_t max = 0; | |
235 | errno = 0; | |
236 | long res = ::pathconf("/", _PC_PATH_MAX); | |
237 | if (res < 0) | |
238 | { | |
92f5a8d4 | 239 | #if defined(PATH_MAX) |
1e59de90 | 240 | max = PATH_MAX; |
92f5a8d4 | 241 | #else |
1e59de90 | 242 | max = 4096; |
92f5a8d4 | 243 | #endif |
1e59de90 TL |
244 | } |
245 | else | |
246 | { | |
247 | max = static_cast< std::size_t >(res); // relative root | |
92f5a8d4 | 248 | #if defined(PATH_MAX) |
1e59de90 TL |
249 | if (max < PATH_MAX) |
250 | max = PATH_MAX; | |
92f5a8d4 | 251 | #endif |
1e59de90 | 252 | } |
92f5a8d4 | 253 | |
1e59de90 TL |
254 | if ((max + 1) < sizeof(dirent().d_name)) |
255 | max = sizeof(dirent().d_name) - 1; | |
92f5a8d4 | 256 | |
1e59de90 | 257 | return max; |
92f5a8d4 TL |
258 | } |
259 | ||
260 | // Returns maximum length of a path, not including the terminating zero | |
261 | inline std::size_t path_max() | |
262 | { | |
1e59de90 TL |
263 | static const std::size_t max = get_path_max(); |
264 | return max; | |
92f5a8d4 TL |
265 | } |
266 | ||
267 | #endif // BOOST_FILESYSTEM_USE_READDIR_R | |
268 | ||
1e59de90 TL |
269 | // *result set to NULL on end of directory |
270 | #if !defined(BOOST_FILESYSTEM_USE_READDIR_R) | |
271 | inline | |
272 | #endif | |
273 | int readdir_impl(dir_itr_imp& imp, struct dirent** result) | |
92f5a8d4 | 274 | { |
1e59de90 TL |
275 | errno = 0; |
276 | ||
277 | struct dirent* p = ::readdir(static_cast< DIR* >(imp.handle)); | |
278 | *result = p; | |
279 | if (!p) | |
280 | return errno; | |
281 | return 0; | |
92f5a8d4 TL |
282 | } |
283 | ||
1e59de90 TL |
284 | #if !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
285 | ||
286 | inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result) | |
92f5a8d4 | 287 | { |
1e59de90 TL |
288 | return readdir_impl(imp, result); |
289 | } | |
290 | ||
291 | #else // !defined(BOOST_FILESYSTEM_USE_READDIR_R) | |
292 | ||
293 | int readdir_r_impl(dir_itr_imp& imp, struct dirent** result) | |
294 | { | |
295 | return ::readdir_r | |
296 | ( | |
297 | static_cast< DIR* >(imp.handle), | |
298 | static_cast< struct dirent* >(get_dir_itr_imp_extra_data(&imp)), | |
299 | result | |
300 | ); | |
301 | } | |
302 | ||
303 | int readdir_select_impl(dir_itr_imp& imp, struct dirent** result); | |
304 | ||
305 | typedef int readdir_impl_t(dir_itr_imp& imp, struct dirent** result); | |
306 | ||
307 | //! Pointer to the actual implementation of the copy_file_data implementation | |
308 | readdir_impl_t* readdir_impl_ptr = &readdir_select_impl; | |
309 | ||
310 | void init_readdir_impl() | |
311 | { | |
312 | readdir_impl_t* impl = &readdir_impl; | |
313 | if (::sysconf(_SC_THREAD_SAFE_FUNCTIONS) >= 0) | |
314 | impl = &readdir_r_impl; | |
92f5a8d4 | 315 | |
1e59de90 TL |
316 | filesystem::detail::atomic_store_relaxed(readdir_impl_ptr, impl); |
317 | } | |
318 | ||
319 | struct readdir_initializer | |
320 | { | |
321 | readdir_initializer() | |
322 | { | |
323 | init_readdir_impl(); | |
324 | } | |
325 | }; | |
92f5a8d4 | 326 | |
1e59de90 TL |
327 | BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_FUNC_PTR_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN |
328 | const readdir_initializer readdir_init; | |
329 | ||
330 | int readdir_select_impl(dir_itr_imp& imp, struct dirent** result) | |
331 | { | |
332 | init_readdir_impl(); | |
333 | return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result); | |
334 | } | |
335 | ||
336 | inline int invoke_readdir(dir_itr_imp& imp, struct dirent** result) | |
337 | { | |
338 | return filesystem::detail::atomic_load_relaxed(readdir_impl_ptr)(imp, result); | |
92f5a8d4 TL |
339 | } |
340 | ||
1e59de90 TL |
341 | #endif // !defined(BOOST_FILESYSTEM_USE_READDIR_R) |
342 | ||
343 | error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf) | |
92f5a8d4 | 344 | { |
1e59de90 TL |
345 | dirent* result = NULL; |
346 | int err = invoke_readdir(imp, &result); | |
347 | if (BOOST_UNLIKELY(err != 0)) | |
348 | return error_code(err, system_category()); | |
349 | if (result == NULL) | |
350 | return dir_itr_close(imp); | |
92f5a8d4 | 351 | |
1e59de90 | 352 | filename = result->d_name; |
92f5a8d4 TL |
353 | |
354 | #ifdef BOOST_FILESYSTEM_STATUS_CACHE | |
1e59de90 TL |
355 | if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value |
356 | { | |
357 | sf = symlink_sf = fs::file_status(fs::status_error); | |
358 | } | |
359 | else // filesystem supplies d_type value | |
360 | { | |
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) | |
366 | { | |
367 | sf = fs::file_status(fs::status_error); | |
368 | symlink_sf = fs::file_status(fs::symlink_file); | |
369 | } | |
370 | else | |
371 | sf = symlink_sf = fs::file_status(fs::status_error); | |
92f5a8d4 | 372 | } |
92f5a8d4 | 373 | #else |
1e59de90 TL |
374 | sf = symlink_sf = fs::file_status(fs::status_error); |
375 | #endif | |
376 | return error_code(); | |
377 | } | |
378 | ||
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&) | |
380 | { | |
381 | std::size_t extra_size = 0u; | |
382 | #if defined(BOOST_FILESYSTEM_USE_READDIR_R) | |
383 | { | |
384 | readdir_impl_t* rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr); | |
385 | if (BOOST_UNLIKELY(rdimpl == &readdir_select_impl)) | |
386 | { | |
387 | init_readdir_impl(); | |
388 | rdimpl = filesystem::detail::atomic_load_relaxed(readdir_impl_ptr); | |
389 | } | |
390 | ||
391 | if (rdimpl == &readdir_r_impl) | |
392 | { | |
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" | |
399 | } | |
400 | } | |
401 | #endif // defined(BOOST_FILESYSTEM_USE_READDIR_R) | |
402 | ||
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); | |
406 | ||
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) | |
410 | flags |= O_NOFOLLOW; | |
411 | ||
412 | int fd = ::open(dir.c_str(), flags); | |
413 | if (BOOST_UNLIKELY(fd < 0)) | |
414 | { | |
415 | const int err = errno; | |
416 | return error_code(err, system_category()); | |
417 | } | |
418 | ||
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)) | |
422 | { | |
423 | const int err = errno; | |
424 | close_fd(fd); | |
425 | return error_code(err, system_category()); | |
426 | } | |
92f5a8d4 | 427 | #endif |
1e59de90 TL |
428 | |
429 | pimpl->handle = ::fdopendir(fd); | |
430 | if (BOOST_UNLIKELY(!pimpl->handle)) | |
431 | { | |
432 | const int err = errno; | |
433 | close_fd(fd); | |
434 | return error_code(err, system_category()); | |
435 | } | |
436 | #else // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) | |
437 | pimpl->handle = ::opendir(dir.c_str()); | |
438 | if (BOOST_UNLIKELY(!pimpl->handle)) | |
439 | { | |
440 | const int err = errno; | |
441 | return error_code(err, system_category()); | |
442 | } | |
443 | #endif // defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) | |
444 | ||
445 | // Force initial readdir call by the caller. This will initialize the actual first filename and statuses. | |
446 | first_filename.assign("."); | |
447 | ||
448 | imp.swap(pimpl); | |
449 | return error_code(); | |
92f5a8d4 TL |
450 | } |
451 | ||
1e59de90 TL |
452 | BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ENOENT; |
453 | ||
92f5a8d4 TL |
454 | #else // BOOST_WINDOWS_API |
455 | ||
1e59de90 TL |
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) |
457 | { | |
458 | if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) | |
459 | { | |
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) | |
464 | { | |
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)); | |
469 | } | |
470 | else | |
471 | { | |
472 | symlink_sf.type(fs::status_error); | |
473 | } | |
474 | ||
475 | sf.type(fs::status_error); | |
92f5a8d4 TL |
476 | } |
477 | else | |
478 | { | |
1e59de90 TL |
479 | if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u) |
480 | { | |
481 | sf.type(fs::directory_file); | |
482 | symlink_sf.type(fs::directory_file); | |
483 | } | |
484 | else | |
485 | { | |
486 | sf.type(fs::regular_file); | |
487 | symlink_sf.type(fs::regular_file); | |
488 | } | |
489 | ||
490 | sf.permissions(make_permissions(filename, attrs)); | |
491 | symlink_sf.permissions(sf.permissions()); | |
492 | } | |
493 | } | |
494 | ||
495 | #if !defined(UNDER_CE) | |
496 | ||
497 | //! FILE_ID_128 definition from Windows SDK | |
498 | struct file_id_128 | |
499 | { | |
500 | BYTE Identifier[16]; | |
501 | }; | |
502 | ||
503 | //! FILE_DIRECTORY_INFORMATION definition from Windows DDK. Used by NtQueryDirectoryFile, supported since Windows NT 4.0 (probably). | |
504 | struct file_directory_information | |
505 | { | |
506 | ULONG NextEntryOffset; | |
507 | ULONG FileIndex; | |
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; | |
516 | WCHAR FileName[1]; | |
517 | }; | |
518 | ||
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 | |
521 | { | |
522 | DWORD NextEntryOffset; | |
523 | DWORD FileIndex; | |
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; | |
532 | DWORD EaSize; | |
533 | CCHAR ShortNameLength; | |
534 | WCHAR ShortName[12]; | |
535 | LARGE_INTEGER FileId; | |
536 | WCHAR FileName[1]; | |
537 | }; | |
538 | ||
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 | |
541 | { | |
542 | ULONG NextEntryOffset; | |
543 | ULONG FileIndex; | |
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; | |
552 | ULONG EaSize; | |
553 | WCHAR FileName[1]; | |
554 | }; | |
555 | ||
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 | |
558 | { | |
559 | ULONG NextEntryOffset; | |
560 | ULONG FileIndex; | |
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; | |
569 | ULONG EaSize; | |
570 | ULONG ReparsePointTag; | |
571 | file_id_128 FileId; | |
572 | WCHAR FileName[1]; | |
573 | }; | |
574 | ||
575 | //! Indicates format of the extra data in the directory iterator | |
576 | enum extra_data_format | |
577 | { | |
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 | |
582 | }; | |
583 | ||
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; | |
586 | ||
587 | /*! | |
588 | * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator. | |
589 | * | |
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. | |
592 | */ | |
593 | BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u; | |
594 | ||
595 | inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT | |
596 | { | |
597 | imp.extra_data_format = 0u; | |
598 | imp.current_offset = 0u; | |
599 | ||
600 | if (imp.handle != NULL) | |
601 | { | |
602 | ::CloseHandle(imp.handle); | |
603 | imp.handle = NULL; | |
604 | } | |
605 | ||
606 | return error_code(); | |
607 | } | |
608 | ||
609 | error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf) | |
610 | { | |
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) | |
614 | { | |
615 | case file_id_extd_dir_info_format: | |
616 | { | |
617 | const file_id_extd_dir_info* data = static_cast< const file_id_extd_dir_info* >(current_data); | |
618 | if (data->NextEntryOffset == 0u) | |
619 | { | |
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)) | |
621 | { | |
622 | DWORD error = ::GetLastError(); | |
623 | ||
624 | dir_itr_close(imp); | |
625 | if (error == ERROR_NO_MORE_FILES) | |
626 | goto done; | |
627 | ||
628 | return error_code(error, system_category()); | |
629 | } | |
630 | ||
631 | imp.current_offset = 0u; | |
632 | data = static_cast< const file_id_extd_dir_info* >(extra_data); | |
633 | } | |
634 | else | |
635 | { | |
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); | |
638 | } | |
639 | ||
640 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); | |
641 | set_file_statuses(data->FileAttributes, &data->ReparsePointTag, filename, sf, symlink_sf); | |
642 | } | |
643 | break; | |
644 | ||
645 | case file_full_dir_info_format: | |
646 | { | |
647 | const file_full_dir_info* data = static_cast< const file_full_dir_info* >(current_data); | |
648 | if (data->NextEntryOffset == 0u) | |
649 | { | |
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)) | |
651 | { | |
652 | DWORD error = ::GetLastError(); | |
653 | ||
654 | dir_itr_close(imp); | |
655 | if (error == ERROR_NO_MORE_FILES) | |
656 | goto done; | |
657 | ||
658 | return error_code(error, system_category()); | |
659 | } | |
660 | ||
661 | imp.current_offset = 0u; | |
662 | data = static_cast< const file_full_dir_info* >(extra_data); | |
663 | } | |
664 | else | |
665 | { | |
666 | imp.current_offset += data->NextEntryOffset; | |
667 | data = reinterpret_cast< const file_full_dir_info* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); | |
668 | } | |
669 | ||
670 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); | |
671 | set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf); | |
672 | } | |
673 | break; | |
674 | ||
675 | case file_id_both_dir_info_format: | |
676 | { | |
677 | const file_id_both_dir_info* data = static_cast< const file_id_both_dir_info* >(current_data); | |
678 | if (data->NextEntryOffset == 0u) | |
679 | { | |
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)) | |
681 | { | |
682 | DWORD error = ::GetLastError(); | |
683 | ||
684 | dir_itr_close(imp); | |
685 | if (error == ERROR_NO_MORE_FILES) | |
686 | goto done; | |
687 | ||
688 | return error_code(error, system_category()); | |
689 | } | |
690 | ||
691 | imp.current_offset = 0u; | |
692 | data = static_cast< const file_id_both_dir_info* >(extra_data); | |
693 | } | |
694 | else | |
695 | { | |
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); | |
698 | } | |
699 | ||
700 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); | |
701 | set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf); | |
702 | } | |
703 | break; | |
704 | ||
705 | default: | |
706 | { | |
707 | const file_directory_information* data = static_cast< const file_directory_information* >(current_data); | |
708 | if (data->NextEntryOffset == 0u) | |
709 | { | |
710 | io_status_block iosb; | |
711 | boost::winapi::NTSTATUS_ status = filesystem::detail::atomic_load_relaxed(nt_query_directory_file_api) | |
712 | ( | |
713 | imp.handle, | |
714 | NULL, // Event | |
715 | NULL, // ApcRoutine | |
716 | NULL, // ApcContext | |
717 | &iosb, | |
718 | extra_data, | |
719 | dir_itr_extra_size, | |
720 | file_directory_information_class, | |
721 | FALSE, // ReturnSingleEntry | |
722 | NULL, // FileName | |
723 | FALSE // RestartScan | |
724 | ); | |
725 | ||
726 | if (!NT_SUCCESS(status)) | |
727 | { | |
728 | dir_itr_close(imp); | |
729 | if (status == STATUS_NO_MORE_FILES) | |
730 | goto done; | |
731 | ||
732 | return error_code(translate_ntstatus(status), system_category()); | |
733 | } | |
734 | ||
735 | imp.current_offset = 0u; | |
736 | data = static_cast< const file_directory_information* >(extra_data); | |
737 | } | |
738 | else | |
739 | { | |
740 | imp.current_offset += data->NextEntryOffset; | |
741 | data = reinterpret_cast< const file_directory_information* >(static_cast< const unsigned char* >(current_data) + data->NextEntryOffset); | |
742 | } | |
743 | ||
744 | filename.assign(data->FileName, data->FileName + data->FileNameLength / sizeof(WCHAR)); | |
745 | set_file_statuses(data->FileAttributes, NULL, filename, sf, symlink_sf); | |
746 | } | |
747 | break; | |
748 | } | |
749 | ||
750 | done: | |
751 | return error_code(); | |
752 | } | |
753 | ||
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) | |
755 | { | |
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); | |
759 | ||
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)) | |
765 | { | |
766 | return_last_error: | |
767 | DWORD error = ::GetLastError(); | |
768 | return error_code(error, system_category()); | |
769 | } | |
770 | ||
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)) | |
773 | { | |
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; | |
778 | ||
779 | if (BOOST_UNLIKELY((info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) | |
780 | return make_error_code(system::errc::not_a_directory); | |
781 | ||
782 | if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) | |
783 | { | |
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); | |
786 | } | |
92f5a8d4 TL |
787 | } |
788 | else | |
789 | { | |
1e59de90 TL |
790 | BY_HANDLE_FILE_INFORMATION info; |
791 | BOOL res = ::GetFileInformationByHandle(h.handle, &info); | |
792 | if (BOOST_UNLIKELY(!res)) | |
793 | goto return_last_error; | |
794 | ||
795 | if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) | |
796 | return make_error_code(system::errc::not_a_directory); | |
797 | ||
798 | if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) | |
799 | { | |
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); | |
802 | } | |
92f5a8d4 | 803 | } |
1e59de90 TL |
804 | |
805 | void* extra_data = get_dir_itr_imp_extra_data(pimpl.get()); | |
806 | switch (filesystem::detail::atomic_load_relaxed(g_extra_data_format)) | |
807 | { | |
808 | case file_id_extd_dir_info_format: | |
809 | { | |
810 | if (!get_file_information_by_handle_ex(h.handle, file_id_extd_directory_restart_info_class, extra_data, dir_itr_extra_size)) | |
811 | { | |
812 | DWORD error = ::GetLastError(); | |
813 | ||
814 | if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) | |
815 | { | |
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; | |
824 | } | |
825 | ||
826 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) | |
827 | goto done; | |
828 | ||
829 | return error_code(error, system_category()); | |
830 | } | |
831 | ||
832 | pimpl->extra_data_format = file_id_extd_dir_info_format; | |
833 | ||
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)); | |
836 | ||
837 | set_file_statuses(data->FileAttributes, &data->ReparsePointTag, first_filename, sf, symlink_sf); | |
838 | } | |
839 | break; | |
840 | ||
841 | case file_full_dir_info_format: | |
842 | fallback_to_file_full_dir_info_format: | |
843 | { | |
844 | if (!get_file_information_by_handle_ex(h.handle, file_full_directory_restart_info_class, extra_data, dir_itr_extra_size)) | |
845 | { | |
846 | DWORD error = ::GetLastError(); | |
847 | ||
848 | if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) | |
849 | { | |
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; | |
853 | } | |
854 | ||
855 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) | |
856 | goto done; | |
857 | ||
858 | return error_code(error, system_category()); | |
859 | } | |
860 | ||
861 | pimpl->extra_data_format = file_full_dir_info_format; | |
862 | ||
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)); | |
865 | ||
866 | set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf); | |
867 | } | |
868 | break; | |
869 | ||
870 | case file_id_both_dir_info_format: | |
871 | fallback_to_file_id_both_dir_info: | |
872 | { | |
873 | if (!get_file_information_by_handle_ex(h.handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size)) | |
874 | { | |
875 | DWORD error = ::GetLastError(); | |
876 | ||
877 | if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) | |
878 | goto done; | |
879 | ||
880 | return error_code(error, system_category()); | |
881 | } | |
882 | ||
883 | pimpl->extra_data_format = file_id_both_dir_info_format; | |
884 | ||
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)); | |
887 | ||
888 | set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf); | |
889 | } | |
890 | break; | |
891 | ||
892 | default: | |
893 | { | |
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()); | |
897 | ||
898 | io_status_block iosb; | |
899 | boost::winapi::NTSTATUS_ status = nt_query_directory_file | |
900 | ( | |
901 | h.handle, | |
902 | NULL, // Event | |
903 | NULL, // ApcRoutine | |
904 | NULL, // ApcContext | |
905 | &iosb, | |
906 | extra_data, | |
907 | dir_itr_extra_size, | |
908 | file_directory_information_class, | |
909 | FALSE, // ReturnSingleEntry | |
910 | NULL, // FileName | |
911 | TRUE // RestartScan | |
912 | ); | |
913 | ||
914 | if (!NT_SUCCESS(status)) | |
915 | { | |
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) | |
921 | goto done; | |
922 | ||
923 | return error_code(translate_ntstatus(status), system_category()); | |
924 | } | |
925 | ||
926 | pimpl->extra_data_format = file_directory_information_format; | |
927 | ||
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)); | |
930 | ||
931 | set_file_statuses(data->FileAttributes, NULL, first_filename, sf, symlink_sf); | |
932 | } | |
933 | break; | |
934 | } | |
935 | ||
936 | pimpl->handle = h.handle; | |
937 | h.handle = INVALID_HANDLE_VALUE; | |
938 | ||
939 | done: | |
940 | imp.swap(pimpl); | |
941 | return error_code(); | |
92f5a8d4 | 942 | } |
92f5a8d4 | 943 | |
1e59de90 | 944 | #else // !defined(UNDER_CE) |
92f5a8d4 | 945 | |
1e59de90 TL |
946 | inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT |
947 | { | |
948 | if (imp.handle != NULL) | |
949 | { | |
950 | ::FindClose(imp.handle); | |
951 | imp.handle = NULL; | |
952 | } | |
92f5a8d4 | 953 | |
1e59de90 TL |
954 | return error_code(); |
955 | } | |
956 | ||
957 | error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_status& sf, fs::file_status& symlink_sf) | |
92f5a8d4 | 958 | { |
1e59de90 TL |
959 | WIN32_FIND_DATAW data; |
960 | if (::FindNextFileW(imp.handle, &data) == 0) // fails | |
961 | { | |
962 | DWORD error = ::GetLastError(); | |
963 | dir_itr_close(imp); | |
964 | if (error == ERROR_NO_MORE_FILES) | |
965 | goto done; | |
966 | return error_code(error, system_category()); | |
967 | } | |
968 | ||
969 | filename = data.cFileName; | |
970 | set_file_statuses(data.dwFileAttributes, NULL, filename, sf, symlink_sf); | |
92f5a8d4 | 971 | |
1e59de90 TL |
972 | done: |
973 | return error_code(); | |
974 | } | |
92f5a8d4 | 975 | |
1e59de90 TL |
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) |
977 | { | |
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); | |
981 | ||
982 | // use a form of search Sebastian Martel reports will work with Win98 | |
983 | fs::path dirpath(dir); | |
984 | dirpath.make_preferred(); | |
985 | dirpath /= L"*"; | |
986 | ||
987 | WIN32_FIND_DATAW data; | |
988 | pimpl->handle = ::FindFirstFileW(dirpath.c_str(), &data); | |
989 | if (BOOST_UNLIKELY(pimpl->handle == INVALID_HANDLE_VALUE)) | |
92f5a8d4 | 990 | { |
1e59de90 TL |
991 | pimpl->handle = NULL; // signal eof |
992 | ||
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) | |
999 | goto done; | |
1000 | ||
1001 | return error_code(error, system_category()); | |
92f5a8d4 | 1002 | } |
92f5a8d4 | 1003 | |
1e59de90 TL |
1004 | first_filename = data.cFileName; |
1005 | set_file_statuses(data.dwFileAttributes, NULL, first_filename, sf, symlink_sf); | |
92f5a8d4 | 1006 | |
1e59de90 TL |
1007 | done: |
1008 | imp.swap(pimpl); | |
1009 | return error_code(); | |
1010 | } | |
92f5a8d4 | 1011 | |
1e59de90 | 1012 | #endif // !defined(UNDER_CE) |
92f5a8d4 | 1013 | |
1e59de90 TL |
1014 | BOOST_CONSTEXPR_OR_CONST err_t not_found_error_code = ERROR_PATH_NOT_FOUND; |
1015 | ||
1016 | #endif // BOOST_WINDOWS_API | |
1017 | ||
1018 | } // namespace | |
1019 | ||
1020 | #if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE) | |
1021 | ||
1022 | //! Initializes directory iterator implementation | |
1023 | void init_directory_iterator_impl() BOOST_NOEXCEPT | |
1024 | { | |
1025 | if (filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api) != NULL) | |
1026 | { | |
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); | |
1030 | } | |
92f5a8d4 TL |
1031 | } |
1032 | ||
1e59de90 TL |
1033 | #endif // defined(BOOST_WINDOWS_API) && !defined(UNDER_CE) |
1034 | ||
92f5a8d4 | 1035 | BOOST_FILESYSTEM_DECL |
1e59de90 TL |
1036 | dir_itr_imp::~dir_itr_imp() BOOST_NOEXCEPT |
1037 | { | |
1038 | dir_itr_close(*this); | |
1039 | } | |
1040 | ||
1041 | BOOST_FILESYSTEM_DECL | |
1042 | void directory_iterator_construct(directory_iterator& it, path const& p, unsigned int opts, system::error_code* ec) | |
1043 | { | |
1044 | if (BOOST_UNLIKELY(p.empty())) | |
1045 | { | |
1046 | emit_error(not_found_error_code, p, ec, "boost::filesystem::directory_iterator::construct"); | |
1047 | return; | |
1048 | } | |
92f5a8d4 | 1049 | |
1e59de90 TL |
1050 | if (ec) |
1051 | ec->clear(); | |
1052 | ||
1053 | try | |
1054 | { | |
1055 | boost::intrusive_ptr< detail::dir_itr_imp > imp; | |
1056 | path filename; | |
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); | |
1059 | ||
1060 | while (true) | |
1061 | { | |
1062 | if (result) | |
1063 | { | |
1064 | if (result != make_error_condition(system::errc::permission_denied) || | |
1065 | (opts & static_cast< unsigned int >(directory_options::skip_permission_denied)) == 0u) | |
1066 | { | |
1067 | if (!ec) | |
1068 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::construct", p, result)); | |
1069 | *ec = result; | |
1070 | } | |
1071 | ||
1072 | return; | |
1073 | } | |
1074 | ||
1075 | if (imp->handle == NULL) // eof, make end | |
1076 | return; | |
1077 | ||
1078 | // Not eof | |
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'))))) | |
1083 | { | |
1084 | imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat); | |
1085 | it.m_imp.swap(imp); | |
1086 | return; | |
1087 | } | |
1088 | ||
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); | |
1091 | } | |
1092 | } | |
1093 | catch (std::bad_alloc&) | |
1094 | { | |
1095 | if (!ec) | |
1096 | throw; | |
1097 | ||
1098 | *ec = make_error_code(system::errc::not_enough_memory); | |
1099 | it.m_imp.reset(); | |
1100 | } | |
92f5a8d4 TL |
1101 | } |
1102 | ||
1103 | BOOST_FILESYSTEM_DECL | |
1104 | void directory_iterator_increment(directory_iterator& it, system::error_code* ec) | |
1105 | { | |
1e59de90 | 1106 | BOOST_ASSERT_MSG(!it.is_end(), "attempt to increment end iterator"); |
92f5a8d4 | 1107 | |
1e59de90 TL |
1108 | if (ec) |
1109 | ec->clear(); | |
92f5a8d4 | 1110 | |
1e59de90 | 1111 | try |
92f5a8d4 | 1112 | { |
1e59de90 TL |
1113 | path filename; |
1114 | file_status file_stat, symlink_file_stat; | |
1115 | system::error_code increment_ec; | |
92f5a8d4 | 1116 | |
1e59de90 | 1117 | while (true) |
92f5a8d4 | 1118 | { |
1e59de90 TL |
1119 | increment_ec = dir_itr_increment(*it.m_imp, filename, file_stat, symlink_file_stat); |
1120 | ||
1121 | if (BOOST_UNLIKELY(!!increment_ec)) // happens if filesystem is corrupt, such as on a damaged optical disc | |
1122 | { | |
1123 | boost::intrusive_ptr< detail::dir_itr_imp > imp; | |
1124 | imp.swap(it.m_imp); | |
1125 | path error_path(imp->dir_entry.path().parent_path()); // fix ticket #5900 | |
1126 | if (!ec) | |
1127 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_iterator::operator++", error_path, increment_ec)); | |
1128 | ||
1129 | *ec = increment_ec; | |
1130 | return; | |
1131 | } | |
1132 | ||
1133 | if (it.m_imp->handle == NULL) // eof, make end | |
1134 | { | |
1135 | it.m_imp.reset(); | |
1136 | return; | |
1137 | } | |
1138 | ||
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'))))) | |
1143 | { | |
1144 | it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat); | |
1145 | return; | |
1146 | } | |
92f5a8d4 | 1147 | } |
1e59de90 TL |
1148 | } |
1149 | catch (std::bad_alloc&) | |
1150 | { | |
1151 | if (!ec) | |
1152 | throw; | |
92f5a8d4 | 1153 | |
92f5a8d4 | 1154 | it.m_imp.reset(); |
1e59de90 | 1155 | *ec = make_error_code(system::errc::not_enough_memory); |
92f5a8d4 | 1156 | } |
92f5a8d4 TL |
1157 | } |
1158 | ||
1159 | //--------------------------------------------------------------------------------------// | |
1160 | // // | |
1161 | // recursive_directory_iterator // | |
1162 | // // | |
1163 | //--------------------------------------------------------------------------------------// | |
1164 | ||
1165 | BOOST_FILESYSTEM_DECL | |
1e59de90 | 1166 | void recursive_directory_iterator_construct(recursive_directory_iterator& it, path const& dir_path, unsigned int opts, system::error_code* ec) |
92f5a8d4 | 1167 | { |
1e59de90 TL |
1168 | if (ec) |
1169 | ec->clear(); | |
92f5a8d4 | 1170 | |
1e59de90 TL |
1171 | directory_iterator dir_it; |
1172 | detail::directory_iterator_construct(dir_it, dir_path, opts, ec); | |
1173 | if ((ec && *ec) || dir_it == directory_iterator()) | |
1174 | return; | |
92f5a8d4 | 1175 | |
1e59de90 TL |
1176 | boost::intrusive_ptr< detail::recur_dir_itr_imp > imp; |
1177 | if (!ec) | |
92f5a8d4 | 1178 | { |
1e59de90 TL |
1179 | imp = new detail::recur_dir_itr_imp(opts); |
1180 | } | |
1181 | else | |
1182 | { | |
1183 | imp = new (std::nothrow) detail::recur_dir_itr_imp(opts); | |
1184 | if (BOOST_UNLIKELY(!imp)) | |
1185 | { | |
1186 | *ec = make_error_code(system::errc::not_enough_memory); | |
1187 | return; | |
1188 | } | |
92f5a8d4 | 1189 | } |
92f5a8d4 | 1190 | |
1e59de90 TL |
1191 | try |
1192 | { | |
92f5a8d4 | 1193 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) |
1e59de90 | 1194 | imp->m_stack.push_back(std::move(dir_it)); |
92f5a8d4 | 1195 | #else |
1e59de90 | 1196 | imp->m_stack.push_back(dir_it); |
92f5a8d4 TL |
1197 | #endif |
1198 | ||
1e59de90 | 1199 | it.m_imp.swap(imp); |
92f5a8d4 | 1200 | } |
1e59de90 TL |
1201 | catch (std::bad_alloc&) |
1202 | { | |
1203 | if (ec) | |
1204 | { | |
1205 | *ec = make_error_code(system::errc::not_enough_memory); | |
1206 | return; | |
1207 | } | |
92f5a8d4 | 1208 | |
1e59de90 TL |
1209 | throw; |
1210 | } | |
92f5a8d4 TL |
1211 | } |
1212 | ||
1213 | namespace { | |
1214 | ||
1215 | void recursive_directory_iterator_pop_on_error(detail::recur_dir_itr_imp* imp) | |
1216 | { | |
1e59de90 | 1217 | imp->m_stack.pop_back(); |
92f5a8d4 | 1218 | |
1e59de90 TL |
1219 | while (!imp->m_stack.empty()) |
1220 | { | |
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()) | |
1225 | break; | |
92f5a8d4 | 1226 | |
1e59de90 TL |
1227 | imp->m_stack.pop_back(); |
1228 | } | |
92f5a8d4 TL |
1229 | } |
1230 | ||
1231 | } // namespace | |
1232 | ||
1233 | BOOST_FILESYSTEM_DECL | |
1234 | void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec) | |
1235 | { | |
1e59de90 TL |
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(); | |
92f5a8d4 | 1238 | |
1e59de90 TL |
1239 | if (ec) |
1240 | ec->clear(); | |
92f5a8d4 | 1241 | |
1e59de90 | 1242 | imp->m_stack.pop_back(); |
92f5a8d4 | 1243 | |
1e59de90 | 1244 | while (true) |
92f5a8d4 | 1245 | { |
92f5a8d4 | 1246 | if (imp->m_stack.empty()) |
1e59de90 TL |
1247 | { |
1248 | it.m_imp.reset(); // done, so make end iterator | |
1249 | break; | |
1250 | } | |
92f5a8d4 | 1251 | |
1e59de90 TL |
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)) | |
1256 | { | |
1257 | if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u) | |
1258 | { | |
1259 | // Make an end iterator on errors | |
1260 | it.m_imp.reset(); | |
1261 | } | |
1262 | else | |
1263 | { | |
1264 | recursive_directory_iterator_pop_on_error(imp); | |
1265 | ||
1266 | if (imp->m_stack.empty()) | |
1267 | it.m_imp.reset(); // done, so make end iterator | |
1268 | } | |
1269 | ||
1270 | if (!ec) | |
1271 | BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::recursive_directory_iterator::pop", increment_ec)); | |
1272 | ||
1273 | *ec = increment_ec; | |
1274 | return; | |
1275 | } | |
92f5a8d4 | 1276 | |
1e59de90 TL |
1277 | if (dir_it != directory_iterator()) |
1278 | break; | |
92f5a8d4 | 1279 | |
1e59de90 TL |
1280 | imp->m_stack.pop_back(); |
1281 | } | |
92f5a8d4 TL |
1282 | } |
1283 | ||
1284 | namespace { | |
1285 | ||
1286 | enum push_directory_result | |
1287 | { | |
1e59de90 TL |
1288 | directory_not_pushed = 0u, |
1289 | directory_pushed = 1u, | |
1290 | keep_depth = 1u << 1 | |
92f5a8d4 TL |
1291 | }; |
1292 | ||
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 | |
1295 | { | |
1e59de90 TL |
1296 | push_directory_result result = directory_not_pushed; |
1297 | try | |
92f5a8d4 | 1298 | { |
1e59de90 TL |
1299 | // Discover if the iterator is for a directory that needs to be recursed into, |
1300 | // taking symlinks and options into account. | |
92f5a8d4 | 1301 | |
1e59de90 | 1302 | if ((imp->m_options & static_cast< unsigned int >(directory_options::_detail_no_push)) != 0u) |
92f5a8d4 | 1303 | { |
1e59de90 TL |
1304 | imp->m_options &= ~static_cast< unsigned int >(directory_options::_detail_no_push); |
1305 | return result; | |
92f5a8d4 TL |
1306 | } |
1307 | ||
1e59de90 | 1308 | file_status symlink_stat; |
92f5a8d4 | 1309 | |
1e59de90 TL |
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) | |
1314 | { | |
1315 | symlink_stat = imp->m_stack.back()->symlink_status(ec); | |
1316 | if (ec) | |
1317 | return result; | |
1318 | } | |
92f5a8d4 | 1319 | |
1e59de90 TL |
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. | |
92f5a8d4 | 1327 | |
1e59de90 TL |
1328 | if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat)) |
1329 | { | |
1330 | file_status stat = imp->m_stack.back()->status(ec); | |
1331 | if (BOOST_UNLIKELY(!!ec)) | |
1332 | { | |
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)) | |
1335 | { | |
1336 | // Skip dangling symlink and continue iteration on the current depth level | |
1337 | ec = error_code(); | |
1338 | } | |
1339 | ||
1340 | return result; | |
1341 | } | |
1342 | ||
1343 | if (!fs::is_directory(stat)) | |
1344 | return result; | |
1345 | ||
1346 | if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)()))) | |
1347 | { | |
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; | |
1353 | return result; | |
1354 | } | |
1355 | ||
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()) | |
1358 | { | |
92f5a8d4 | 1359 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) |
1e59de90 | 1360 | imp->m_stack.push_back(std::move(next)); // may throw |
92f5a8d4 | 1361 | #else |
1e59de90 | 1362 | imp->m_stack.push_back(next); // may throw |
92f5a8d4 | 1363 | #endif |
1e59de90 TL |
1364 | return directory_pushed; |
1365 | } | |
1366 | } | |
1367 | } | |
1368 | catch (std::bad_alloc&) | |
1369 | { | |
1370 | ec = make_error_code(system::errc::not_enough_memory); | |
92f5a8d4 | 1371 | } |
92f5a8d4 | 1372 | |
1e59de90 | 1373 | return result; |
92f5a8d4 TL |
1374 | } |
1375 | ||
1376 | } // namespace | |
1377 | ||
1378 | BOOST_FILESYSTEM_DECL | |
1379 | void recursive_directory_iterator_increment(recursive_directory_iterator& it, system::error_code* ec) | |
1380 | { | |
1e59de90 TL |
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(); | |
92f5a8d4 | 1383 | |
1e59de90 TL |
1384 | if (ec) |
1385 | ec->clear(); | |
92f5a8d4 | 1386 | |
1e59de90 | 1387 | system::error_code local_ec; |
92f5a8d4 | 1388 | |
1e59de90 TL |
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) | |
1392 | return; | |
92f5a8d4 | 1393 | |
1e59de90 TL |
1394 | // report errors if any |
1395 | if (BOOST_UNLIKELY(!!local_ec)) | |
92f5a8d4 | 1396 | { |
1e59de90 TL |
1397 | on_error: |
1398 | if ((imp->m_options & static_cast< unsigned int >(directory_options::pop_on_error)) == 0u) | |
1399 | { | |
1400 | // Make an end iterator on errors | |
1401 | it.m_imp.reset(); | |
1402 | } | |
1403 | else | |
1404 | { | |
1405 | if ((push_result & keep_depth) != 0u) | |
1406 | { | |
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; | |
1412 | } | |
1413 | ||
1414 | recursive_directory_iterator_pop_on_error(imp); | |
1415 | ||
1416 | if (imp->m_stack.empty()) | |
1417 | it.m_imp.reset(); // done, so make end iterator | |
1418 | } | |
92f5a8d4 | 1419 | |
1e59de90 TL |
1420 | on_error_return: |
1421 | if (!ec) | |
1422 | BOOST_FILESYSTEM_THROW(filesystem_error("filesystem::recursive_directory_iterator increment error", local_ec)); | |
92f5a8d4 | 1423 | |
1e59de90 TL |
1424 | *ec = local_ec; |
1425 | return; | |
92f5a8d4 TL |
1426 | } |
1427 | ||
1e59de90 TL |
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. | |
1431 | while (true) | |
92f5a8d4 | 1432 | { |
1e59de90 TL |
1433 | if (imp->m_stack.empty()) |
1434 | { | |
1435 | it.m_imp.reset(); // done, so make end iterator | |
1436 | break; | |
1437 | } | |
92f5a8d4 | 1438 | |
1e59de90 TL |
1439 | directory_iterator& dir_it = imp->m_stack.back(); |
1440 | detail::directory_iterator_increment(dir_it, &local_ec); | |
1441 | if (BOOST_UNLIKELY(!!local_ec)) | |
1442 | goto on_error; | |
92f5a8d4 | 1443 | |
1e59de90 TL |
1444 | if (dir_it != directory_iterator()) |
1445 | break; | |
92f5a8d4 | 1446 | |
1e59de90 TL |
1447 | imp->m_stack.pop_back(); |
1448 | } | |
92f5a8d4 TL |
1449 | } |
1450 | ||
1451 | } // namespace detail | |
1452 | ||
1453 | } // namespace filesystem | |
1454 | } // namespace boost |