windows_namespace_request_sys/lib.rs
1// Copyright (c) Mike Grier.
2
3//! Owned, marshalable parameter sets for synchronous Win32 namespace calls.
4//!
5//! Win32's namespace and metadata surface -- opening, querying, closing -- is
6//! synchronous-only. A call that blocks on a dead network path blocks the thread
7//! that made it, and no overlapped form exists. This crate makes such a call
8//! **capturable as a value**: an owned parameter set built on one thread and
9//! executed faithfully on another.
10//!
11//! It schedules nothing. It is the catalogue-plus-faithful-execution layer,
12//! testable with no ring, no pool, and no async anywhere near it.
13//!
14//! # What a request is, and is not
15//!
16//! A request carries call **parameters**. It does not carry the impersonation
17//! token or any other thread-scoped state the call runs under -- that belongs to
18//! [`windows-thread-ambient-sys`][ambient], which this crate does not depend on.
19//! The two are siblings rather than a stack: a request can be executed with no
20//! captured context at all, and a context is useful to work that never opens a
21//! file. Whoever owns both pairs them at the submission site.
22//!
23//! A request also chooses **no delivery model**. An opened handle comes back
24//! plain and unassociated, because associating it with a completion port
25//! irreversibly forecloses `IoRing` use, and that choice belongs to a layer that
26//! knows the handle's destination.
27//!
28//! [ambient]: https://docs.rs/windows-thread-ambient-sys
29//!
30//! # Faithful means unaltered
31//!
32//! An entry reports the raw Win32 outcome. `ERROR_FILE_NOT_FOUND` means a
33//! missing directory from an open, an empty directory from a first query, and a
34//! genuine failure from a later one; only a consumer can tell those apart, so
35//! nothing here normalises or reclassifies.
36//!
37//! The code is also snapshotted before any cleanup can overwrite it, because
38//! `GetLastError` is volatile thread state that a `Drop` or a buffer release
39//! will happily clobber. That guarantee is a primitive rather than a rule each
40//! entry remembers: see [`outcome`].
41//!
42//! # A path is copied; a handle is shared
43//!
44//! Several entries take a handle rather than a path, and a request holds a
45//! [`SharedHandle`](win_shared_os_owned_handle::SharedHandle): a share of the
46//! caller's handle, not a new one, and a clone of the request shares it again.
47//! Nothing in this crate duplicates a handle. The distinction is easy to get
48//! backwards: a path is a value and is copied, while a handle is a reference to a
49//! kernel object.
50//!
51//! A request is therefore self-contained with respect to **lifetime** -- the
52//! handle stays open while any holder keeps its share, so a request cannot be
53//! left naming a handle its originator closed -- and is **not** isolated with
54//! respect to **state**. A request names the caller's kernel object, so an
55//! enumeration through it continues the caller's rather than starting its own;
56//! an independent traversal needs a fresh open. A caller holding only a borrow
57//! makes an owned handle itself, visibly, before handing it over.
58//!
59//! # Scope
60//!
61//! One entry per Win32 call; a consumer needing two makes two requests and
62//! sequences them itself. The round-one entry list is audited from three real
63//! consumers rather than chosen by taste, and its omissions are deliberate and
64//! written down. Volume and disk operations are not namespace operations and are in
65//! `win-volume-sys`, not here. See `DESIGN-NOTES.md` in the crate
66//! root.
67//!
68//! # Example
69//!
70//! Capture the parameters on the submitting thread, where a failure is still
71//! the caller's to see and the process current directory still means what the
72//! caller thinks it means, then use them on a worker that saw none of it:
73//!
74//! ```
75//! use std::fs;
76//! use std::thread;
77//!
78//! use windows_namespace_request_sys::{FileType, OpenFile, QueryFileType};
79//! use wtf_string::Wtf16String;
80//!
81//! let path = std::env::temp_dir().join(format!("wnrs-doc-{}.tmp", std::process::id()));
82//! fs::write(&path, b"example")?;
83//!
84//! // Built here, not on the worker: a relative path is fixed to the process
85//! // current directory now, which is shared mutable state that any thread can
86//! // change in between. This one is absolute, so it is handed over as given.
87//! let text = path.to_str().expect("a temporary path is valid UTF-8");
88//! let open = OpenFile::new(Wtf16String::from(text))?;
89//! assert_eq!(open.win32_path().to_string_lossy(), text);
90//!
91//! // A handle is shared, not duplicated: the request holds the caller's
92//! // handle, so it stays open for the worker however the caller lets go.
93//! let query = QueryFileType::new(fs::File::open(&path)?);
94//!
95//! let kind = thread::spawn(move || query.perform())
96//! .join()
97//! .expect("the worker did not panic")?;
98//!
99//! assert_eq!(kind, FileType::DISK);
100//! # fs::remove_file(&path)?;
101//! # Ok::<(), Box<dyn std::error::Error>>(())
102//! ```
103
104#![cfg(windows)]
105#![forbid(unsafe_op_in_unsafe_fn)]
106#![warn(missing_docs)]
107
108mod anchored;
109pub mod close;
110pub mod file_basic;
111pub mod file_id;
112pub mod file_info;
113pub mod file_type;
114pub mod final_path;
115pub mod full_path;
116mod handle;
117pub mod open;
118pub mod open_by_id;
119pub mod outcome;
120pub mod query;
121pub mod reopen;
122pub mod request;
123pub mod volume;
124pub mod watch;
125
126pub use anchored::{AnchoredPath, PathCaptureError};
127pub use close::{CloseFn, CloseRequest};
128pub use file_basic::{FileBasicInfo, QueryFileBasicInfo};
129pub use file_id::{FileIdInfo, QueryFileIdInfo};
130pub use file_info::QueryFileInformationByHandle;
131pub use file_type::{FileType, QueryFileType};
132pub use final_path::{FinalPathError, FinalPathFlags, QueryFinalPath};
133pub use full_path::{FullPathError, ResolveFullPath};
134pub use io_buffers::AlignedBuf;
135pub use open::OpenFile;
136pub use open_by_id::{FileIdentifier, OpenFileByIdentifier};
137pub use outcome::{Outcome, Win32Error};
138pub use query::{FileInformationClass, PerformIntoError, QueryFileInformation};
139pub use reopen::ReOpenFile;
140pub use request::{ConsumingRequest, Request};
141
142/// The owned security descriptor and attributes the requests take: `win-authorization-sys`, where
143/// they were moved to and which is where Microsoft Learn files them (*Security and Identity >
144/// Authorization*). They are re-exported here at the paths they always had.
145pub use win_authorization_sys as security;
146
147/// Compiles the README's examples, so a contract change breaks the build
148/// rather than silently teaching the old answer.
149#[cfg(doctest)]
150#[doc = include_str!("../README.md")]
151struct ReadmeDoctests;
152
153#[cfg(test)]
154mod security_open_tests;
155#[cfg(test)]
156mod shared_handle_tests;
157#[cfg(test)]
158mod tests;
159
160pub use security::{
161 AclState, SecurityAttributes, SecurityCaptureError, SecurityCaptureFailure, SecurityDescriptor,
162 SecurityInformation,
163};
164pub use volume::{QueryVolumeInformation, VolumeInformation};
165pub use watch::{ChangeNotification, NotifyFilter, WatchDirectory};