Skip to main content

Module path

Module path 

Source
Expand description

The request path contract: what a caller may name, and what gets stored.

§Duplicated on purpose, for now

This is a second copy of the preparation that ships in windows-file-enumeration-sys, not a replacement for it. That crate is released and this one is not, so making it depend here would make it unpublishable; the copy keeps the working crate untouched while this one is proven. The de-duplication happens after this branch merges with main, and is scheduled as a checklist item – it is not a duplicate that nobody circled back to.

§A resolved path is not a session-independent path

GetFullPathNameW does not verify what it produces – though for a drive-relative path naming another drive it does query the filesystem, and may rewrite that drive’s recorded entry (see crate::full_path). It collapses ./.. lexically, and it additionally roots most paths that are not fully qualified against process state – the current directory, or for a root-relative path that directory’s root, or for a drive-relative path naming another drive the entry recorded for it in the =C: environment variables – used verbatim when accepted, so it need not even be on that drive, and replaced by the drive root when not (for the current drive the process directory is used and the entry makes no difference). It is therefore not a lexical call as a whole, which is what makes resolving on the submitting thread meaningful.

“Most” rather than “every”, because a legacy device name short-circuits the rooting entirely. prepare hands the input to that call without device handling of its own, so prepare("CON") yields \\.\CON – a device, not a file under the current directory – and the same holds for CON:, NUL, LPT1: and the rest of the legacy set. A caller passing through an untrusted name should know that. Anything with more after it (CON.txt, a\CON) roots normally – except for NUL, the one member a path in front of it does not save: prepare(r"C:\NUL") is \\.\NUL, so a fully qualified path is not by itself evidence that a name refers to a file on that volume. Only a suffix (NUL.txt) takes it out. The full shape is in crate::full_path, which documents the call itself.

What it never does is expand a drive letter, and a drive letter resolves against the logon session of whatever token is in effect. So a path prepared on a submitting thread and opened on a worker under a captured token from another session can name a different device. Preparation closes the current-directory race; it does not close that one, and nothing here should be read as implying otherwise.

A request resolves its path when it is built, on the submitting thread. Deferring that to a worker would let the meaning of a relative path change between submission and execution, because the process current directory is shared mutable state that nothing in this crate controls. Resolving early also separates the two concerns cleanly: string resolution happens here, and the privileged open happens later under the captured token.

§Two path families

A \\?\ path is verbatim: Win32 disables path parsing for it, so the crate stores it code unit for code unit. It is checked for full qualification – the one property the prefix promises and a caller can get wrong – and otherwise left alone. Trailing separators and ./.. components are preserved, because in verbatim form they are literal name components rather than syntax.

Everything else, including \\.\ device paths, goes through GetFullPathNameW. Those forms are normalised by Win32, so resolving them here produces exactly the path a later CreateFileW would have used.

§Why ordinary paths stop at MAX_PATH

Whether CreateFileW accepts a longer ordinary path depends on the host executable’s longPathAware manifest and on system policy – neither of which this crate controls, and both of which belong to whoever embeds it. Letting them decide would make the same call succeed in one host and fail in another. The crate instead draws the line itself: ordinary paths stop at MAX_PATH, and a caller who wants a longer one says so explicitly with a fully qualified \\?\ path, which has never depended on the manifest.

Structs§

PathError
A synchronous failure while preparing a caller’s path.
PreparedPath
A path that has been through prepare: the exact path a worker will open.

Enums§

PathFailure
Why a caller’s path could not be prepared.

Functions§

prepare
Validate and, where the contract calls for it, resolve a caller’s path.