1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (c) 2026 Mike Grier
//! Windows-only platform layer for asynchronous flat directory enumeration.
//!
//! One request enumerates one directory. The crate owns bounded submission and
//! completion rings, lossless backpressure, cancellation, submitter security
//! context transport, and a caller-buffered `GetFileInformationByHandleEx`
//! engine. Recursive traversal belongs in a separate layer that composes these
//! flat requests.
//!
//! # Native values stay native
//!
//! Names and paths are native-width WTF-16 ([`wtf_string`]), so an ill-formed
//! surrogate a filesystem contains survives the round trip. Times are signed
//! Windows tick counts ([`WindowsFileTimestamp`]), attributes are the raw
//! `FILE_ATTRIBUTE_*` bitmask, and a file ID keeps the record's exact 16 bytes.
//! Nothing is converted eagerly into a portable shape whose losses a caller
//! could not undo.
//!
//! # Safety
//!
//! The public surface is entirely safe: every FFI call the native engine makes
//! is confined to a single caller-owned, size-checked buffer
//! ([`EnumerationRequest::with_buffer_capacity`]), and no directory entry is
//! ever opened individually -- the engine reads only from the batched
//! `GetFileInformationByHandleEx` listing of the one directory handle the
//! request named. A submitted enumeration's security context is captured
//! synchronously on the submitter's own thread, before the request becomes
//! visible to any worker, so the later directory open always runs as whoever
//! asked for it rather than as the pool. The unsafe internals that make this
//! true -- buffer aliasing, handle ownership, and thread-pool callback
//! lifetime -- are recorded in [DESIGN-NOTES.md][1] and [DESIGN-RATIONALE.md][2].
//!
//! [1]: https://github.com/MikeGrier/windows-threadpool-sys/blob/main/crates/windows-file-enumeration-sys/DESIGN-NOTES.md
//! [2]: https://github.com/MikeGrier/windows-threadpool-sys/blob/main/crates/windows-file-enumeration-sys/DESIGN-RATIONALE.md
//!
//! # Building a predicate
//!
//! Build a request for one directory, delivering only files larger than 4 KiB
//! whose names end in `.log`:
//!
//! ```no_run
//! use windows_file_enumeration_sys::{
//! ComparisonOperator, EntryType, EnumerationRequest, NamePattern, PatternToken,
//! PredicateClause, QueryByExample,
//! };
//! use wtf_string::Wtf16String;
//!
//! let suffix = NamePattern::empty()
//! .with(PatternToken::AnyRun)
//! .with(PatternToken::Literal(Wtf16String::from(".log")));
//!
//! let query = QueryByExample::new()
//! .with(PredicateClause::Name {
//! pattern: suffix,
//! case: Default::default(),
//! negated: false,
//! })?
//! .with(PredicateClause::IsType {
//! entry_type: EntryType::File,
//! negated: false,
//! })?
//! .with(PredicateClause::LogicalSize {
//! operator: ComparisonOperator::Greater,
//! value: 4096,
//! })?;
//!
//! let request = EnumerationRequest::for_path("C:/logs".as_ref())?.with_predicate(query);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! # Running an enumeration
//!
//! [`Session::new`] returns a producing [`Session`] and its single [`Receiver`].
//! [`Session::try_begin`] captures the caller's own security context and starts
//! the enumeration; entries and exactly one [`Completion::Terminal`] arrive on
//! the receiver, every entry of one enumeration before its terminal:
//!
//! ```no_run
//! use windows_file_enumeration_sys::{Completion, EnumerationRequest, Session};
//!
//! let (session, receiver) = Session::new(8, 8)?;
//! let request = EnumerationRequest::for_path("C:/logs".as_ref())?;
//! session.try_begin(request)?.detach();
//!
//! while let Some(completion) = receiver.recv() {
//! match completion {
//! Completion::Entry { entry, .. } => println!("{}", entry.name()),
//! Completion::Terminal { outcome, .. } => {
//! println!("finished: {outcome:?}");
//! break;
//! }
//! }
//! }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! # Traversal-style submission
//!
//! A recursive traversal layer captures one security context and reuses it for
//! every directory in the tree with [`Session::try_begin_with_token`], instead
//! of paying a fresh capture per directory on whatever thread happens to be
//! submitting:
//!
//! ```no_run
//! use windows_file_enumeration_sys::{EnumerationRequest, Session};
//! use windows_impersonation_token_sys::ImpersonationToken;
//!
//! let (session, receiver) = Session::new(8, 8)?;
//! let token = ImpersonationToken::capture()?;
//!
//! for directory in ["C:/logs", "C:/logs/archive"] {
//! let request = EnumerationRequest::for_path(directory.as_ref())?;
//! session
//! .try_begin_with_token(request, token.clone())?
//! .detach();
//! }
//! # drop(receiver);
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use WindowsFileTimestamp;