Skip to content

Commit 01b4c14

Browse files
committed
traverse/contents: let the caller choose the enumeration flags
traverse() always read with directory_handle::flags::none, so on Windows it cannot enumerate a directory whose entries exceed what one NtQueryDirectoryFile() call returns: read() gives back errc::operation_would_block and the whole traversal fails. A recursive walk does not get to pick the directories it meets, so there was no way to ask for best-effort enumeration. Add an enumeration_flags parameter to traverse() and contents(), defaulted to flags::none so existing callers are unaffected.
1 parent 2e8404b commit 01b4c14

3 files changed

Lines changed: 22 additions & 8 deletions

File tree

‎include/llfio/v2.0/algorithm/contents.hpp‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ namespace algorithm
6868
{
6969
}
7070

71-
friend inline result<contents_type> contents(const path_handle &dirh, contents_visitor *visitor, size_t threads, bool force_slow_path) noexcept;
71+
friend inline result<contents_type> contents(const path_handle &dirh, contents_visitor *visitor, size_t threads, bool force_slow_path,
72+
directory_handle::flags enumeration_flags) noexcept;
7273

7374
protected:
7475
struct _state_type
@@ -221,7 +222,8 @@ namespace algorithm
221222
in header-only code, as it is very simple.
222223
*/
223224
inline result<contents_visitor::contents_type> contents(const path_handle &dirh, contents_visitor *visitor = nullptr, size_t threads = 0,
224-
bool force_slow_path = false) noexcept
225+
bool force_slow_path = false,
226+
directory_handle::flags enumeration_flags = directory_handle::flags::none) noexcept
225227
{
226228
contents_visitor default_visitor;
227229
if(visitor == nullptr)
@@ -231,7 +233,7 @@ namespace algorithm
231233
contents_visitor::_state_type state(dirh);
232234
OUTCOME_TRY(auto &&dirhpath, dirh.current_path());
233235
state.rootdirpathlen.store(dirhpath.native().size() + 1, std::memory_order_relaxed);
234-
OUTCOME_TRY(traverse(dirh, visitor, threads, &state, force_slow_path));
236+
OUTCOME_TRY(traverse(dirh, visitor, threads, &state, force_slow_path, enumeration_flags));
235237
return {std::move(state.contents)};
236238
}
237239

‎include/llfio/v2.0/algorithm/traverse.hpp‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,17 @@ namespace algorithm
181181
- Fast path, 1 thread, traversed 131,915 directories and 8,254,162 entries in 2.73 seconds (+12%).
182182
183183
- Fast path, 16 threads, traversed 131,915 directories and 8,254,162 entries in 0.525 seconds (+46%).
184+
185+
`enumeration_flags` is passed to each `directory_handle::read()` performed. On Windows,
186+
`NtQueryDirectoryFile()` returns at most about 64KB of entries per call however large a
187+
buffer it is given (issue #137), so a directory larger than that cannot be read as an
188+
atomic snapshot and `read()` fails with `errc::operation_would_block`. Pass
189+
`directory_handle::flags::permit_racy_reads` if you would rather have the entries than
190+
the failure: an enumeration which fits into a single call is still an atomic snapshot,
191+
one which does not reports `is_snapshot()` false.
184192
*/
185-
LLFIO_HEADERS_ONLY_FUNC_SPEC result<size_t> traverse(const path_handle &dirh, traverse_visitor *visitor, size_t threads = 0, void *data = nullptr, bool force_slow_path = false) noexcept;
193+
LLFIO_HEADERS_ONLY_FUNC_SPEC result<size_t> traverse(const path_handle &dirh, traverse_visitor *visitor, size_t threads = 0, void *data = nullptr, bool force_slow_path = false,
194+
directory_handle::flags enumeration_flags = directory_handle::flags::none) noexcept;
186195

187196
} // namespace algorithm
188197

‎include/llfio/v2.0/detail/impl/traverse.ipp‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ LLFIO_V2_NAMESPACE_BEGIN
4646
namespace algorithm
4747
{
4848
LLFIO_HEADERS_ONLY_FUNC_SPEC result<size_t> traverse(const path_handle &_topdirh, traverse_visitor *visitor,
49-
size_t threads, void *data, bool force_slow_path) noexcept
49+
size_t threads, void *data, bool force_slow_path,
50+
directory_handle::flags enumeration_flags) noexcept
5051
{
5152
return visitor->finished(
5253
data,
@@ -91,6 +92,7 @@ namespace algorithm
9192
std::mutex lock;
9293
size_t max_sso_path_size;
9394
traverse_visitor *visitor{nullptr};
95+
directory_handle::flags enumeration_flags{directory_handle::flags::none};
9496
#if 0
9597
struct workitem
9698
{
@@ -211,12 +213,13 @@ namespace algorithm
211213
size_t dirs_processed{0}, known_dirs_remaining{0}, depth_processed{0}, threads_sleeping{0},
212214
threads_running{0};
213215

214-
explicit state_t(size_t _max_sso_path_size, traverse_visitor *_visitor)
216+
explicit state_t(size_t _max_sso_path_size, traverse_visitor *_visitor, directory_handle::flags _enumeration_flags)
215217
: max_sso_path_size(_max_sso_path_size)
216218
, visitor(_visitor)
219+
, enumeration_flags(_enumeration_flags)
217220
{
218221
}
219-
} state(use_slow_path ? size_t(-1) : LLFIO_ALGORITHM_TRAVERSE_MAX_SSO_PATH_SIZE, visitor);
222+
} state(use_slow_path ? size_t(-1) : LLFIO_ALGORITHM_TRAVERSE_MAX_SSO_PATH_SIZE, visitor, enumeration_flags);
220223
struct worker
221224
{
222225
state_t *state{nullptr};
@@ -289,7 +292,7 @@ namespace algorithm
289292
for(;;)
290293
{
291294
buffers = directory_handle::buffers_type{span<directory_handle::buffer_type>(entries.data(), entries.size()), std::move(buffers)};
292-
OUTCOME_TRY(buffers, mydirh->read({std::move(buffers), {}, directory_handle::filter::none}));
295+
OUTCOME_TRY(buffers, mydirh->read({std::move(buffers), state->enumeration_flags, {}, directory_handle::filter::none}));
293296
if(buffers.done())
294297
{
295298
break;

0 commit comments

Comments
 (0)