| //! # Safe(r) wrappers around Windows API functions. |
| //! |
| //! This module contains fairly thin wrappers around Windows API functions, |
| //! aimed at centralising safety instead of having unsafe blocks spread |
| //! throughout higher level code. This makes it much easier to audit FFI safety. |
| //! |
| //! Not all functions can be made completely safe without more context but in |
| //! such cases we should still endeavour to reduce the caller's burden of safety |
| //! as much as possible. |
| //! |
| //! ## Guidelines for wrappers |
| //! |
| //! Items here should be named similarly to their raw Windows API name, except |
| //! that they follow Rust's case conventions. E.g. function names are |
| //! lower_snake_case. The idea here is that it should be easy for a Windows |
| //! C/C++ programmer to identify the underlying function that's being wrapped |
| //! while not looking too out of place in Rust code. |
| //! |
| //! Every use of an `unsafe` block must have a related SAFETY comment, even if |
| //! it's trivially safe (for example, see `get_last_error`). Public unsafe |
| //! functions must document what the caller has to do to call them safely. |
| //! |
| //! Avoid unchecked `as` casts. For integers, either assert that the integer |
| //! is in range or use `try_into` instead. For pointers, prefer to use |
| //! `ptr.cast::<Type>()` when possible. |
| //! |
| //! This module must only depend on core and not on std types as the eventual |
| //! hope is to have std depend on sys and not the other way around. |
| //! However, some amount of glue code may currently be necessary so such code |
| //! should go in sys/pal/windows/mod.rs rather than here. See `IoResult` as an example. |
| |
| use core::ffi::c_void; |
| use core::marker::PhantomData; |
| |
| use super::c; |
| |
| /// Creates a null-terminated UTF-16 string from a str. |
| pub macro wide_str($str:literal) {{ |
| const _: () = { |
| if core::slice::memchr::memchr(0, $str.as_bytes()).is_some() { |
| panic!("null terminated strings cannot contain interior nulls"); |
| } |
| }; |
| crate::sys::pal::windows::api::utf16!(concat!($str, '\0')) |
| }} |
| |
| /// Creates a UTF-16 string from a str without null termination. |
| pub macro utf16($str:expr) {{ |
| const UTF8: &str = $str; |
| const UTF16_LEN: usize = crate::sys::pal::windows::api::utf16_len(UTF8); |
| const UTF16: [u16; UTF16_LEN] = crate::sys::pal::windows::api::to_utf16(UTF8); |
| &UTF16 |
| }} |
| |
| #[cfg(test)] |
| mod tests; |
| |
| /// Gets the UTF-16 length of a UTF-8 string, for use in the wide_str macro. |
| pub const fn utf16_len(s: &str) -> usize { |
| let s = s.as_bytes(); |
| let mut i = 0; |
| let mut len = 0; |
| while i < s.len() { |
| // the length of a UTF-8 encoded code-point is given by the number of |
| // leading ones, except in the case of ASCII. |
| let utf8_len = match s[i].leading_ones() { |
| 0 => 1, |
| n => n as usize, |
| }; |
| i += utf8_len; |
| // Note that UTF-16 surrogates (U+D800 to U+DFFF) are not encodable as UTF-8, |
| // so (unlike with WTF-8) we don't have to worry about how they'll get re-encoded. |
| len += if utf8_len < 4 { 1 } else { 2 }; |
| } |
| len |
| } |
| |
| /// Const convert UTF-8 to UTF-16, for use in the wide_str macro. |
| /// |
| /// Note that this is designed for use in const contexts so is not optimized. |
| pub const fn to_utf16<const UTF16_LEN: usize>(s: &str) -> [u16; UTF16_LEN] { |
| let mut output = [0_u16; UTF16_LEN]; |
| let mut pos = 0; |
| let s = s.as_bytes(); |
| let mut i = 0; |
| while i < s.len() { |
| match s[i].leading_ones() { |
| // Decode UTF-8 based on its length. |
| // See https://en.wikipedia.org/wiki/UTF-8 |
| 0 => { |
| // ASCII is the same in both encodings |
| output[pos] = s[i] as u16; |
| i += 1; |
| pos += 1; |
| } |
| 2 => { |
| // Bits: 110xxxxx 10xxxxxx |
| output[pos] = ((s[i] as u16 & 0b11111) << 6) | (s[i + 1] as u16 & 0b111111); |
| i += 2; |
| pos += 1; |
| } |
| 3 => { |
| // Bits: 1110xxxx 10xxxxxx 10xxxxxx |
| output[pos] = ((s[i] as u16 & 0b1111) << 12) |
| | ((s[i + 1] as u16 & 0b111111) << 6) |
| | (s[i + 2] as u16 & 0b111111); |
| i += 3; |
| pos += 1; |
| } |
| 4 => { |
| // Bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
| let mut c = ((s[i] as u32 & 0b111) << 18) |
| | ((s[i + 1] as u32 & 0b111111) << 12) |
| | ((s[i + 2] as u32 & 0b111111) << 6) |
| | (s[i + 3] as u32 & 0b111111); |
| // re-encode as UTF-16 (see https://en.wikipedia.org/wiki/UTF-16) |
| // - Subtract 0x10000 from the code point |
| // - For the high surrogate, shift right by 10 then add 0xD800 |
| // - For the low surrogate, take the low 10 bits then add 0xDC00 |
| c -= 0x10000; |
| output[pos] = ((c >> 10) + 0xD800) as u16; |
| output[pos + 1] = ((c & 0b1111111111) + 0xDC00) as u16; |
| i += 4; |
| pos += 2; |
| } |
| // valid UTF-8 cannot have any other values |
| _ => unreachable!(), |
| } |
| } |
| output |
| } |
| |
| /// Helper method for getting the size of `T` as a u32. |
| /// Errors at compile time if the size would overflow. |
| /// |
| /// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug. |
| /// However, one key motivation for this function is to avoid the temptation to |
| /// use frequent `as` casts. This is risky because they are too powerful. |
| /// For example, the following will compile today: |
| /// |
| /// `size_of::<u64> as u32` |
| /// |
| /// Note that `size_of` is never actually called, instead a function pointer is |
| /// converted to a `u32`. Clippy would warn about this but, alas, it's not run |
| /// on the standard library. |
| const fn win32_size_of<T: Sized>() -> u32 { |
| // Const assert that the size does not exceed u32::MAX. |
| // Uses a trait to workaround restriction on using generic types in inner items. |
| trait Win32SizeOf: Sized { |
| const WIN32_SIZE_OF: u32 = { |
| let size = size_of::<Self>(); |
| assert!(size <= u32::MAX as usize); |
| size as u32 |
| }; |
| } |
| impl<T: Sized> Win32SizeOf for T {} |
| |
| T::WIN32_SIZE_OF |
| } |
| |
| /// The `SetFileInformationByHandle` function takes a generic parameter by |
| /// making the user specify the type (class), a pointer to the data and its |
| /// size. This trait allows attaching that information to a Rust type so that |
| /// [`set_file_information_by_handle`] can be called safely. |
| /// |
| /// This trait is designed so that it can support variable sized types. |
| /// However, currently Rust's std only uses fixed sized structures. |
| /// |
| /// # Safety |
| /// |
| /// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes. |
| /// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g. |
| /// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`. |
| pub unsafe trait SetFileInformation { |
| /// The type of information to set. |
| const CLASS: i32; |
| /// A pointer to the file information to set. |
| fn as_ptr(&self) -> *const c_void; |
| /// The size of the type pointed to by `as_ptr`. |
| fn size(&self) -> u32; |
| } |
| /// Helper trait for implementing `SetFileInformation` for statically sized types. |
| unsafe trait SizedSetFileInformation: Sized { |
| const CLASS: i32; |
| } |
| unsafe impl<T: SizedSetFileInformation> SetFileInformation for T { |
| const CLASS: i32 = T::CLASS; |
| fn as_ptr(&self) -> *const c_void { |
| (&raw const *self).cast::<c_void>() |
| } |
| fn size(&self) -> u32 { |
| win32_size_of::<Self>() |
| } |
| } |
| |
| // SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO, |
| // FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO |
| // are all plain `repr(C)` structs that only contain primitive types. |
| // The given information classes correctly match with the struct. |
| unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO { |
| const CLASS: i32 = c::FileBasicInfo; |
| } |
| unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO { |
| const CLASS: i32 = c::FileEndOfFileInfo; |
| } |
| unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO { |
| const CLASS: i32 = c::FileAllocationInfo; |
| } |
| unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO { |
| const CLASS: i32 = c::FileDispositionInfo; |
| } |
| unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX { |
| const CLASS: i32 = c::FileDispositionInfoEx; |
| } |
| unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO { |
| const CLASS: i32 = c::FileIoPriorityHintInfo; |
| } |
| |
| #[inline] |
| pub fn set_file_information_by_handle<T: SetFileInformation>( |
| handle: c::HANDLE, |
| info: &T, |
| ) -> Result<(), WinError> { |
| unsafe fn set_info( |
| handle: c::HANDLE, |
| class: i32, |
| info: *const c_void, |
| size: u32, |
| ) -> Result<(), WinError> { |
| unsafe { |
| let result = c::SetFileInformationByHandle(handle, class, info, size); |
| (result != 0).then_some(()).ok_or_else(get_last_error) |
| } |
| } |
| // SAFETY: The `SetFileInformation` trait ensures that this is safe. |
| unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } |
| } |
| |
| /// Gets the error from the last function. |
| /// This must be called immediately after the function that sets the error to |
| /// avoid the risk of another function overwriting it. |
| pub fn get_last_error() -> WinError { |
| // SAFETY: This just returns a thread-local u32 and has no other effects. |
| unsafe { WinError { code: c::GetLastError() } } |
| } |
| |
| /// An error code as returned by [`get_last_error`]. |
| /// |
| /// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS. |
| /// Check the documentation of the Windows API function being called for expected errors. |
| #[derive(Clone, Copy, PartialEq, Eq)] |
| #[repr(transparent)] |
| pub struct WinError { |
| pub code: u32, |
| } |
| impl WinError { |
| pub const fn new(code: u32) -> Self { |
| Self { code } |
| } |
| } |
| |
| // Error code constants. |
| // The constant names should be the same as the winapi constants except for the leading `ERROR_`. |
| // Due to the sheer number of codes, error codes should only be added here on an as-needed basis. |
| // However, they should never be removed as the assumption is they may be useful again in the future. |
| #[allow(unused)] |
| impl WinError { |
| /// Success is not an error. |
| /// Some Windows APIs do use this to distinguish between a zero return and an error return |
| /// but we should never return this to users as an error. |
| pub const SUCCESS: Self = Self::new(c::ERROR_SUCCESS); |
| // tidy-alphabetical-start |
| pub const ACCESS_DENIED: Self = Self::new(c::ERROR_ACCESS_DENIED); |
| pub const ALREADY_EXISTS: Self = Self::new(c::ERROR_ALREADY_EXISTS); |
| pub const BAD_NETPATH: Self = Self::new(c::ERROR_BAD_NETPATH); |
| pub const BAD_NET_NAME: Self = Self::new(c::ERROR_BAD_NET_NAME); |
| pub const CANT_ACCESS_FILE: Self = Self::new(c::ERROR_CANT_ACCESS_FILE); |
| pub const DELETE_PENDING: Self = Self::new(c::ERROR_DELETE_PENDING); |
| pub const DIRECTORY: Self = Self::new(c::ERROR_DIRECTORY); |
| pub const DIR_NOT_EMPTY: Self = Self::new(c::ERROR_DIR_NOT_EMPTY); |
| pub const FILE_NOT_FOUND: Self = Self::new(c::ERROR_FILE_NOT_FOUND); |
| pub const INSUFFICIENT_BUFFER: Self = Self::new(c::ERROR_INSUFFICIENT_BUFFER); |
| pub const INVALID_FUNCTION: Self = Self::new(c::ERROR_INVALID_FUNCTION); |
| pub const INVALID_HANDLE: Self = Self::new(c::ERROR_INVALID_HANDLE); |
| pub const INVALID_PARAMETER: Self = Self::new(c::ERROR_INVALID_PARAMETER); |
| pub const NOT_FOUND: Self = Self::new(c::ERROR_NOT_FOUND); |
| pub const NOT_SUPPORTED: Self = Self::new(c::ERROR_NOT_SUPPORTED); |
| pub const NO_MORE_FILES: Self = Self::new(c::ERROR_NO_MORE_FILES); |
| pub const OPERATION_ABORTED: Self = Self::new(c::ERROR_OPERATION_ABORTED); |
| pub const PATH_NOT_FOUND: Self = Self::new(c::ERROR_PATH_NOT_FOUND); |
| pub const SHARING_VIOLATION: Self = Self::new(c::ERROR_SHARING_VIOLATION); |
| pub const TIMEOUT: Self = Self::new(c::ERROR_TIMEOUT); |
| // tidy-alphabetical-end |
| } |
| |
| /// A wrapper around a UNICODE_STRING that is equivalent to `&[u16]`. |
| /// |
| /// It is preferable to use the `unicode_str!` macro as that contains mitigations for #143078. |
| /// |
| /// If the MaximumLength field of the underlying UNICODE_STRING is greater than |
| /// the Length field then you can test if the string is null terminated by inspecting |
| /// the u16 directly after the string. You cannot otherwise depend on nul termination. |
| #[derive(Copy, Clone)] |
| pub struct UnicodeStrRef<'a> { |
| s: c::UNICODE_STRING, |
| lifetime: PhantomData<&'a [u16]>, |
| } |
| |
| static EMPTY_STRING_NULL_TERMINATED: &[u16] = &[0]; |
| |
| impl UnicodeStrRef<'_> { |
| const fn new(slice: &[u16], is_null_terminated: bool) -> Self { |
| let (len, max_len, ptr) = if slice.is_empty() { |
| (0, 2, EMPTY_STRING_NULL_TERMINATED.as_ptr().cast_mut()) |
| } else { |
| let len = slice.len() - (is_null_terminated as usize); |
| (len * 2, size_of_val(slice), slice.as_ptr().cast_mut()) |
| }; |
| Self { |
| s: c::UNICODE_STRING { Length: len as _, MaximumLength: max_len as _, Buffer: ptr }, |
| lifetime: PhantomData, |
| } |
| } |
| |
| pub const fn from_slice_with_nul(slice: &[u16]) -> Self { |
| if !slice.is_empty() { |
| debug_assert!(slice[slice.len() - 1] == 0); |
| } |
| Self::new(slice, true) |
| } |
| |
| pub const fn from_slice(slice: &[u16]) -> Self { |
| Self::new(slice, false) |
| } |
| |
| /// Returns a pointer to the underlying UNICODE_STRING |
| pub const fn as_ptr(&self) -> *const c::UNICODE_STRING { |
| &self.s |
| } |
| } |
| |
| /// Create a UnicodeStringRef from a literal str or a u16 array. |
| /// |
| /// To mitigate #143078, when using a literal str the created UNICODE_STRING |
| /// will be nul terminated. The MaximumLength field of the UNICODE_STRING will |
| /// be set greater than the Length field to indicate that a nul may be present. |
| /// |
| /// If using a u16 array, the array is used exactly as provided and you cannot |
| /// count on the string being nul terminated. |
| /// This should generally be used for strings that come from the OS. |
| /// |
| /// **NOTE:** we lack a UNICODE_STRING builder type as we don't currently have |
| /// a use for it. If needing to dynamically build a UNICODE_STRING, the builder |
| /// should try to ensure there's a nul one past the end of the string. |
| pub macro unicode_str { |
| ($str:literal) => {const { |
| crate::sys::pal::windows::api::UnicodeStrRef::from_slice_with_nul( |
| crate::sys::pal::windows::api::wide_str!($str), |
| ) |
| }}, |
| ($array:expr) => { |
| crate::sys::pal::windows::api::UnicodeStrRef::from_slice( |
| $array, |
| ) |
| } |
| } |