1 // Copyright 2022 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use super::Result;
6 use std::{
7 convert::TryFrom,
8 fs::File,
9 io::{Stderr, Stdin, Stdout},
10 ops::Drop,
11 };
12
13 use crate::descriptor::{
14 AsRawDescriptor, Descriptor, FromRawDescriptor, IntoRawDescriptor, SafeDescriptor,
15 };
16 use std::{
17 ffi::CString,
18 marker::{Send, Sync},
19 mem::MaybeUninit,
20 os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
21 sync::Once,
22 };
23 use win_util::{duplicate_handle, win32_wide_string};
24 use winapi::{
25 shared::minwindef::{BOOL, HMODULE, TRUE},
26 um::{
27 handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
28 libloaderapi,
29 },
30 };
31
32 pub type RawDescriptor = RawHandle;
33
34 pub const INVALID_DESCRIPTOR: RawDescriptor = INVALID_HANDLE_VALUE;
35
36 impl PartialEq for SafeDescriptor {
eq(&self, other: &Self) -> bool37 fn eq(&self, other: &Self) -> bool {
38 return compare_object_handles(self.descriptor, other.as_raw_descriptor());
39 }
40 }
41
42 impl Drop for SafeDescriptor {
drop(&mut self)43 fn drop(&mut self) {
44 unsafe { CloseHandle(self.descriptor) };
45 }
46 }
47
48 impl AsRawHandle for SafeDescriptor {
as_raw_handle(&self) -> RawHandle49 fn as_raw_handle(&self) -> RawHandle {
50 self.as_raw_descriptor()
51 }
52 }
53
54 static KERNELBASE_INIT: Once = Once::new();
55 static mut KERNELBASE_LIBRARY: MaybeUninit<HMODULE> = MaybeUninit::uninit();
56
compare_object_handles(first: RawHandle, second: RawHandle) -> bool57 fn compare_object_handles(first: RawHandle, second: RawHandle) -> bool {
58 KERNELBASE_INIT.call_once(|| {
59 unsafe {
60 *KERNELBASE_LIBRARY.as_mut_ptr() =
61 libloaderapi::LoadLibraryW(win32_wide_string("Kernelbase").as_ptr());
62 };
63 });
64 let handle = unsafe { KERNELBASE_LIBRARY.assume_init() };
65 if handle.is_null() {
66 return first == second;
67 }
68
69 let addr = CString::new("CompareObjectHandles").unwrap();
70 let addr_ptr = addr.as_ptr();
71 let symbol = unsafe { libloaderapi::GetProcAddress(handle, addr_ptr) };
72 if symbol.is_null() {
73 return first == second;
74 }
75
76 let func = unsafe {
77 std::mem::transmute::<
78 *mut winapi::shared::minwindef::__some_function,
79 extern "system" fn(RawHandle, RawHandle) -> BOOL,
80 >(symbol)
81 };
82 let ret = func(first, second);
83 ret == TRUE
84 }
85
86 impl TryFrom<&dyn AsRawHandle> for SafeDescriptor {
87 type Error = std::io::Error;
88
try_from(handle: &dyn AsRawHandle) -> std::result::Result<Self, Self::Error>89 fn try_from(handle: &dyn AsRawHandle) -> std::result::Result<Self, Self::Error> {
90 Ok(SafeDescriptor {
91 descriptor: duplicate_handle(handle.as_raw_handle())?,
92 })
93 }
94 }
95
96 impl SafeDescriptor {
97 /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
98 /// share the same underlying count within the kernel.
try_clone(&self) -> Result<SafeDescriptor>99 pub fn try_clone(&self) -> Result<SafeDescriptor> {
100 // Safe because `duplicate_handle` will return a valid handle, or at the very least error
101 // out.
102 Ok(unsafe {
103 SafeDescriptor::from_raw_descriptor(win_util::duplicate_handle(self.descriptor)?)
104 })
105 }
106 }
107
108 // On Windows, RawHandles are represented by raw pointers but are not used as such in
109 // rust code, and are therefore safe to send between threads.
110 unsafe impl Send for SafeDescriptor {}
111 unsafe impl Sync for SafeDescriptor {}
112
113 // On Windows, RawHandles are represented by raw pointers but are opaque to the
114 // userspace and cannot be derefenced by rust code, and are therefore safe to
115 // send between threads.
116 unsafe impl Send for Descriptor {}
117 unsafe impl Sync for Descriptor {}
118
119 macro_rules! AsRawDescriptor {
120 ($name:ident) => {
121 impl AsRawDescriptor for $name {
122 fn as_raw_descriptor(&self) -> RawDescriptor {
123 return self.as_raw_handle();
124 }
125 }
126 };
127 }
128
129 macro_rules! FromRawDescriptor {
130 ($name:ident) => {
131 impl FromRawDescriptor for $name {
132 unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
133 return $name::from_raw_handle(descriptor);
134 }
135 }
136 };
137 }
138
139 macro_rules! IntoRawDescriptor {
140 ($name:ident) => {
141 impl IntoRawDescriptor for $name {
142 fn into_raw_descriptor(self) -> RawDescriptor {
143 return self.into_raw_handle();
144 }
145 }
146 };
147 }
148
149 // Implementations for File. This enables the File-type to use the cross-platform
150 // RawDescriptor, but does not mean File should be used as a generic
151 // descriptor container. That should go to either SafeDescriptor or another more
152 // relevant container type.
153 // TODO(b/148971445): Ensure there are no usages of File that aren't actually files.
154 AsRawDescriptor!(File);
155 FromRawDescriptor!(File);
156 IntoRawDescriptor!(File);
157 AsRawDescriptor!(Stdin);
158 AsRawDescriptor!(Stdout);
159 AsRawDescriptor!(Stderr);
160
161 #[test]
162 #[allow(clippy::eq_op)]
clone_equality()163 fn clone_equality() {
164 use super::Event;
165 use crate::descriptor::IntoRawDescriptor;
166
167 let evt = Event::new().unwrap();
168 let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(evt.into_raw_descriptor()) };
169
170 assert_eq!(descriptor, descriptor);
171
172 assert_eq!(
173 descriptor,
174 descriptor.try_clone().expect("failed to clone event")
175 );
176
177 let evt2 = Event::new().unwrap();
178 let another = unsafe { SafeDescriptor::from_raw_descriptor(evt2.into_raw_descriptor()) };
179
180 assert_ne!(descriptor, another);
181 }
182