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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// Copyright (c) Mike Grier.
//! The `GetFileInformationByHandleEx` entry.
//!
//! Entry 5 of the audited catalogue, and the most-called namespace operation
//! across all three audited consumers.
//!
//! # Why it is here despite being trivial to marshal
//!
//! This entry needs almost no marshaling work: its inputs are a handle, a
//! scalar class, and a buffer size, with no pointer into caller memory
//! anywhere. That invites the conclusion that it does not belong in a catalogue
//! at all. Membership is decided by whether a **blocking** namespace call needs
//! performing off the caller's thread, not by whether it is awkward to marshal
//! -- the latter test would select for our implementation convenience rather
//! than for consumer need. On the former test this is the call whose lack of an
//! overlapped form is why an unassociated handle had to become a first-class
//! destination at all.
//!
//! # This returns bytes, and does not parse them
//!
//! The audited classes have two result shapes -- fixed-size out-parameters and
//! variable-length batches -- and both collapse to one owned buffer here,
//! because this crate returns bytes plus the unaltered outcome. Per-class
//! parsing stays with the consumer that already owns it.
//!
//! Two constraints on that buffer are not negotiable:
//!
//! - It must be **8-byte aligned**. A `Vec<u8>` guarantees byte alignment and
//! nothing more, and a misaligned batch is not a subtle problem: the very
//! first query fails with `ERROR_NOACCESS`. [`AlignedBuffer`] states the
//! alignment rather than arriving at it by luck.
//! - The call **reports no written length**. A batch is walked by its own
//! next-entry offsets, so the whole buffer comes back and the consumer bounds
//! its own reads, rather than this entry inventing a byte count it cannot
//! know.
//!
//! # Only two classes touch the enumeration cursor
//!
//! Measured on Windows 11 Enterprise 10.0.28000, `aarch64-pc-windows-msvc`,
//! against a real directory with a deliberately small buffer:
//!
//! | Question | Measured |
//! |---|---|
//! | Does a duplicated handle share the enumeration cursor? | **Yes** -- a clean continuation |
//! | Control: do two separate opens share it? | **No** -- the second restarts |
//! | Does closing the duplicate disturb the source? | **No** |
//! | Does an interleaved `FileBasicInfo`, `FileIdInfo`, or non-`Ex` query disturb it? | **No** |
//!
//! So the contract is narrower than "handle-taking entries are hazardous":
//! **only the two directory-enumeration classes mutate the shared cursor**, and
//! every other query is a pure read that composes freely with an enumeration in
//! progress, on the same handle or on a duplicate. What follows is specific --
//! a duplicate is *not* an independent enumeration, and an independent
//! traversal needs a fresh open.
//!
//! # This is single-shot, and is not a streaming engine
//!
//! An entry covering the two directory classes otherwise looks like a second
//! implementation of a shipped streaming enumerator. It is not. This entry is
//! **single-shot**: one call, one batch, and the *client* sequences the next,
//! which is the one-entry-per-Win32-call rule applied literally. A consumer
//! wanting streaming enumeration -- with the cursor, refill loop, quanta, and
//! backpressure owned for it -- wants
//! [windows-file-enumeration-sys](https://docs.rs/windows-file-enumeration-sys)
//! and should not rebuild that loop out of single-shot calls.
//!
//! All five audited classes stay reachable here regardless, because restricting
//! them would narrow the entry for a no-consumer reason.
//!
//! # No ambient context is needed
//!
//! Access was checked at the open. That is exactly why the enumeration crate
//! applies impersonation only around `CreateFileW`, and it makes this the
//! clearest case that a request and a context are **paired at submission**
//! rather than fused.
use ;
use crateAlignedBuffer;
use crate;
use crate;
/// The alignment a directory-information batch requires.
///
/// A `FILE_ID_EXTD_DIR_INFO` contains `i64` fields and the API keeps every
/// record in a batch on an 8-byte boundary -- but only if the batch itself
/// starts on one. Changing this value is a breaking change.
const BATCH_ALIGNMENT: usize = ;
/// Which information the query asks for.
///
/// A newtype over `FILE_INFO_BY_HANDLE_CLASS` rather than an enum, because
/// Windows defines classes this crate has never heard of and refusing them
/// would narrow the entry. The named constants are the five the audit found;
/// any other class reaches Windows unaltered through [`from_raw`](Self::from_raw).
///
/// # Example
///
/// ```
/// use windows_namespace_request_sys::query::FileInformationClass;
/// use windows_sys::Win32::Storage::FileSystem::{FileBasicInfo, FileStandardInfo};
///
/// assert_eq!(FileInformationClass::BASIC.as_raw(), FileBasicInfo);
///
/// // A class with no named constant here is still expressible.
/// let standard = FileInformationClass::from_raw(FileStandardInfo);
/// assert_eq!(standard.as_raw(), FileStandardInfo);
/// ```
;
/// An owned, marshalable parameter set for `GetFileInformationByHandleEx`.
///
/// # Example
///
/// ```
/// use std::fs;
/// use std::os::windows::io::AsHandle;
///
/// use windows_namespace_request_sys::query::{FileInformationClass, QueryFileInformation};
/// use windows_namespace_request_sys::CapturedHandle;
///
/// let path = std::env::temp_dir().join(format!("wnrs-q-{}.tmp", std::process::id()));
/// fs::write(&path, b"example")?;
/// let file = fs::File::open(&path)?;
///
/// let request = QueryFileInformation::new(
/// CapturedHandle::capture(file.as_handle())?,
/// FileInformationClass::BASIC,
/// )
/// .with_capacity(256);
///
/// // The whole buffer comes back: the call reports no written length, so the
/// // consumer bounds its own reads rather than trusting a count we invented.
/// let bytes = request.perform()?;
/// assert_eq!(bytes.len(), 256);
/// assert_eq!(bytes.as_ptr() as usize % 8, 0);
/// # drop(file);
/// # fs::remove_file(&path)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```