cap-std
cap-std copied to clipboard
Use `NtCreateFile` on Windows
As @kubkon observed, NtOpenFile provides a more efficient and likely more reliable way to implement openat-like behavior on Windows. As an example, Rust is now using it this way in its remove_dir_all implementation.
Microsoft officially documents NtOpenFile as an Internal API which may change between releases or even service packs of Windows, however this particular function, NtOpenFile, has apparently been stable for a long time, and since Rust itself is now depending on it, it would seem to be sufficiently stable for cap-std to use as well.
Use NtCreateFile instead of NtOpenFile; see https://github.com/rust-lang/rust/pull/93206.
My current thinking on this has changed since that PR. At the time using any Nt* function was controversial so I was eager to be cautious above all else, especially given it's the standard library. Since then I've had more discussion with folks at Microsoft and I now feel much more relaxed about use of Nt* functions where it can be justified. So long as they're well documented and have been around for awhile, they are not going to break.
Btw, the Calling Internal APIs page, while scary, is somewhat outdated. There now exists an ntdll.lib which is distributed with the Windows SDK and allows linking with many ntdll functions without using GetProcAddress. If using a gnu (rather than msvc) toolchain it can not necessarily be assumed that the SDK is installed but if the function is part of the windows and windows-sys crates then this will be in the import libraries distributed by those crates (and these are also official Microsoft projects).
Oh wow, I wasn't previously aware that windows-sys has bindings for NtCreateFile and NtOpenFile.
Also, cap-std needs the ability to create files, so it appears we do need NtCreateFile.
One thing we'll need to figure out is how to map from security QoS flags to NtCreateFile. There's a SecurityQualityOfService inside the ObjectAttributes parameter, though it doesn't look like a trivial mapping.
It shouldn't be too difficult. SECURITY_QUALITY_OF_SERVICE is basically just an enum and two bools. Very roughly it goes something like:
ImpersonationLevel = (qos_flags >> 16) & 0b11;
ContextTrackingMode = (qos_flags & SECURITY_CONTEXT_TRACKING) != 0
EffectiveOnly = (qos_flags & SECURITY_EFFECTIVE_ONLY) != 0