|
| 1 | +//! # Safe(r) wrappers around Windows API functions. |
| 2 | +//! |
| 3 | +//! This module contains fairly thin wrappers around Windows API functions, |
| 4 | +//! aimed at centralising safety instead of having unsafe blocks spread |
| 5 | +//! throughout higher level code. This makes it much easier to audit FFI safety. |
| 6 | +//! |
| 7 | +//! Not all functions can be made completely safe without more context but in |
| 8 | +//! such cases we should still endeavour to reduce the caller's burden of safety |
| 9 | +//! as much as possible. |
| 10 | +//! |
| 11 | +//! ## Guidelines for wrappers |
| 12 | +//! |
| 13 | +//! Items here should be named similarly to their raw Windows API name, except |
| 14 | +//! that they follow Rust's case conventions. E.g. function names are |
| 15 | +//! lower_snake_case. The idea here is that it should be easy for a Windows |
| 16 | +//! C/C++ programmer to identify the underlying function that's being wrapped |
| 17 | +//! while not looking too out of place in Rust code. |
| 18 | +//! |
| 19 | +//! Every use of an `unsafe` block must have a related SAFETY comment, even if |
| 20 | +//! it's trivially safe (for example, see `get_last_error`). Public unsafe |
| 21 | +//! functions must document what the caller has to do to call them safely. |
| 22 | +//! |
| 23 | +//! Avoid unchecked `as` casts. For integers, either assert that the integer |
| 24 | +//! is in range or use `try_into` instead. For pointers, prefer to use |
| 25 | +//! `ptr.cast::<Type>()` when possible. |
| 26 | +//! |
| 27 | +//! This module must only depend on core and not on std types as the eventual |
| 28 | +//! hope is to have std depend on sys and not the other way around. |
| 29 | +//! However, some amount of glue code may currently be necessary so such code |
| 30 | +//! should go in sys/windows/mod.rs rather than here. See `IoResult` as an example. |
| 31 | +
|
| 32 | +use core::ffi::c_void; |
| 33 | +use core::ptr::addr_of; |
| 34 | + |
| 35 | +use super::c; |
| 36 | + |
| 37 | +/// Helper method for getting the size of `T` as a u32. |
| 38 | +/// Errors at compile time if the size would overflow. |
| 39 | +/// |
| 40 | +/// While a type larger than u32::MAX is unlikely, it is possible if only because of a bug. |
| 41 | +/// However, one key motivation for this function is to avoid the temptation to |
| 42 | +/// use frequent `as` casts. This is risky because they are too powerful. |
| 43 | +/// For example, the following will compile today: |
| 44 | +/// |
| 45 | +/// `std::mem::size_of::<u64> as u32` |
| 46 | +/// |
| 47 | +/// Note that `size_of` is never actually called, instead a function pointer is |
| 48 | +/// converted to a `u32`. Clippy would warn about this but, alas, it's not run |
| 49 | +/// on the standard library. |
| 50 | +const fn win32_size_of<T: Sized>() -> u32 { |
| 51 | + // Const assert that the size is less than u32::MAX. |
| 52 | + // Uses a trait to workaround restriction on using generic types in inner items. |
| 53 | + trait Win32SizeOf: Sized { |
| 54 | + const WIN32_SIZE_OF: u32 = { |
| 55 | + let size = core::mem::size_of::<Self>(); |
| 56 | + assert!(size <= u32::MAX as usize); |
| 57 | + size as u32 |
| 58 | + }; |
| 59 | + } |
| 60 | + impl<T: Sized> Win32SizeOf for T {} |
| 61 | + |
| 62 | + T::WIN32_SIZE_OF |
| 63 | +} |
| 64 | + |
| 65 | +/// The `SetFileInformationByHandle` function takes a generic parameter by |
| 66 | +/// making the user specify the type (class), a pointer to the data and its |
| 67 | +/// size. This trait allows attaching that information to a Rust type so that |
| 68 | +/// [`set_file_information_by_handle`] can be called safely. |
| 69 | +/// |
| 70 | +/// This trait is designed so that it can support variable sized types. |
| 71 | +/// However, currently Rust's std only uses fixed sized structures. |
| 72 | +/// |
| 73 | +/// # Safety |
| 74 | +/// |
| 75 | +/// * `as_ptr` must return a pointer to memory that is readable up to `size` bytes. |
| 76 | +/// * `CLASS` must accurately reflect the type pointed to by `as_ptr`. E.g. |
| 77 | +/// the `FILE_BASIC_INFO` structure has the class `FileBasicInfo`. |
| 78 | +pub unsafe trait SetFileInformation { |
| 79 | + /// The type of information to set. |
| 80 | + const CLASS: i32; |
| 81 | + /// A pointer to the file information to set. |
| 82 | + fn as_ptr(&self) -> *const c_void; |
| 83 | + /// The size of the type pointed to by `as_ptr`. |
| 84 | + fn size(&self) -> u32; |
| 85 | +} |
| 86 | +/// Helper trait for implementing `SetFileInformation` for statically sized types. |
| 87 | +unsafe trait SizedSetFileInformation: Sized { |
| 88 | + const CLASS: i32; |
| 89 | +} |
| 90 | +unsafe impl<T: SizedSetFileInformation> SetFileInformation for T { |
| 91 | + const CLASS: i32 = T::CLASS; |
| 92 | + fn as_ptr(&self) -> *const c_void { |
| 93 | + addr_of!(*self).cast::<c_void>() |
| 94 | + } |
| 95 | + fn size(&self) -> u32 { |
| 96 | + win32_size_of::<Self>() |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +// SAFETY: FILE_BASIC_INFO, FILE_END_OF_FILE_INFO, FILE_ALLOCATION_INFO, |
| 101 | +// FILE_DISPOSITION_INFO, FILE_DISPOSITION_INFO_EX and FILE_IO_PRIORITY_HINT_INFO |
| 102 | +// are all plain `repr(C)` structs that only contain primitive types. |
| 103 | +// The given information classes correctly match with the struct. |
| 104 | +unsafe impl SizedSetFileInformation for c::FILE_BASIC_INFO { |
| 105 | + const CLASS: i32 = c::FileBasicInfo; |
| 106 | +} |
| 107 | +unsafe impl SizedSetFileInformation for c::FILE_END_OF_FILE_INFO { |
| 108 | + const CLASS: i32 = c::FileEndOfFileInfo; |
| 109 | +} |
| 110 | +unsafe impl SizedSetFileInformation for c::FILE_ALLOCATION_INFO { |
| 111 | + const CLASS: i32 = c::FileAllocationInfo; |
| 112 | +} |
| 113 | +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO { |
| 114 | + const CLASS: i32 = c::FileDispositionInfo; |
| 115 | +} |
| 116 | +unsafe impl SizedSetFileInformation for c::FILE_DISPOSITION_INFO_EX { |
| 117 | + const CLASS: i32 = c::FileDispositionInfoEx; |
| 118 | +} |
| 119 | +unsafe impl SizedSetFileInformation for c::FILE_IO_PRIORITY_HINT_INFO { |
| 120 | + const CLASS: i32 = c::FileIoPriorityHintInfo; |
| 121 | +} |
| 122 | + |
| 123 | +#[inline] |
| 124 | +pub fn set_file_information_by_handle<T: SetFileInformation>( |
| 125 | + handle: c::HANDLE, |
| 126 | + info: &T, |
| 127 | +) -> Result<(), WinError> { |
| 128 | + unsafe fn set_info( |
| 129 | + handle: c::HANDLE, |
| 130 | + class: i32, |
| 131 | + info: *const c_void, |
| 132 | + size: u32, |
| 133 | + ) -> Result<(), WinError> { |
| 134 | + let result = c::SetFileInformationByHandle(handle, class, info, size); |
| 135 | + (result != 0).then_some(()).ok_or_else(|| get_last_error()) |
| 136 | + } |
| 137 | + // SAFETY: The `SetFileInformation` trait ensures that this is safe. |
| 138 | + unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } |
| 139 | +} |
| 140 | + |
| 141 | +/// Gets the error from the last function. |
| 142 | +/// This must be called immediately after the function that sets the error to |
| 143 | +/// avoid the risk of another function overwriting it. |
| 144 | +pub fn get_last_error() -> WinError { |
| 145 | + // SAFETY: This just returns a thread-local u32 and has no other effects. |
| 146 | + unsafe { WinError { code: c::GetLastError() } } |
| 147 | +} |
| 148 | + |
| 149 | +/// An error code as returned by [`get_last_error`]. |
| 150 | +/// |
| 151 | +/// This is usually a 16-bit Win32 error code but may be a 32-bit HRESULT or NTSTATUS. |
| 152 | +/// Check the documentation of the Windows API function being called for expected errors. |
| 153 | +#[derive(Clone, Copy, PartialEq, Eq)] |
| 154 | +#[repr(transparent)] |
| 155 | +pub struct WinError { |
| 156 | + pub code: u32, |
| 157 | +} |
0 commit comments