1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::ffi::c_void; 15 use std::fs::File; 16 use std::mem::{size_of, zeroed}; 17 use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; 18 use std::ptr::null_mut; 19 use std::sync::atomic::{AtomicUsize, Ordering}; 20 use std::sync::{Arc, Mutex}; 21 use std::{fmt, io}; 22 23 use windows_sys::Win32::Foundation::{ 24 RtlNtStatusToDosError, HANDLE, INVALID_HANDLE_VALUE, NTSTATUS, STATUS_NOT_FOUND, 25 STATUS_PENDING, STATUS_SUCCESS, UNICODE_STRING, 26 }; 27 use windows_sys::Win32::Storage::FileSystem::{ 28 NtCreateFile, SetFileCompletionNotificationModes, FILE_OPEN, FILE_SHARE_READ, FILE_SHARE_WRITE, 29 SYNCHRONIZE, 30 }; 31 use windows_sys::Win32::System::WindowsProgramming::{ 32 NtDeviceIoControlFile, FILE_SKIP_SET_EVENT_ON_HANDLE, IO_STATUS_BLOCK, IO_STATUS_BLOCK_0, 33 OBJECT_ATTRIBUTES, 34 }; 35 36 use crate::sys::windows::iocp::CompletionPort; 37 38 pub const POLL_RECEIVE: u32 = 0x0001; 39 pub const POLL_RECEIVE_EXPEDITED: u32 = 0x0002; 40 pub const POLL_SEND: u32 = 0x0004; 41 pub const POLL_DISCONNECT: u32 = 0x0008; 42 pub const POLL_ABORT: u32 = 0x0010; 43 pub const POLL_LOCAL_CLOSE: u32 = 0x0020; 44 pub const POLL_ACCEPT: u32 = 0x0080; 45 pub const POLL_CONNECT_FAIL: u32 = 0x0100; 46 47 pub const ALL_EVENTS: u32 = POLL_RECEIVE 48 | POLL_RECEIVE_EXPEDITED 49 | POLL_SEND 50 | POLL_DISCONNECT 51 | POLL_ACCEPT 52 | POLL_LOCAL_CLOSE 53 | POLL_ABORT 54 | POLL_CONNECT_FAIL; 55 56 const AFD_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { 57 Length: size_of::<OBJECT_ATTRIBUTES>() as u32, 58 RootDirectory: 0, 59 ObjectName: &OBJ_NAME as *const _ as *mut _, 60 Attributes: 0, 61 SecurityDescriptor: null_mut(), 62 SecurityQualityOfService: null_mut(), 63 }; 64 const OBJ_NAME: UNICODE_STRING = UNICODE_STRING { 65 Length: (AFD_HELPER_NAME.len() * size_of::<u16>()) as u16, 66 MaximumLength: (AFD_HELPER_NAME.len() * size_of::<u16>()) as u16, 67 Buffer: AFD_HELPER_NAME.as_ptr() as *mut _, 68 }; 69 const AFD_HELPER_NAME: &[u16] = &[ 70 '\\' as _, 'D' as _, 'e' as _, 'v' as _, 'i' as _, 'c' as _, 'e' as _, '\\' as _, 'A' as _, 71 'f' as _, 'd' as _, '\\' as _, 'Y' as _, 'l' as _, 'o' as _, 'n' as _, 'g' as _, 72 ]; 73 74 static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(0); 75 const IOCTL_AFD_POLL: u32 = 0x00012024; 76 77 #[link(name = "ntdll")] 78 extern "system" { NtCancelIoFileEx( FileHandle: HANDLE, IoRequestToCancel: *mut IO_STATUS_BLOCK, IoStatusBlock: *mut IO_STATUS_BLOCK, ) -> NTSTATUS79 fn NtCancelIoFileEx( 80 FileHandle: HANDLE, 81 IoRequestToCancel: *mut IO_STATUS_BLOCK, 82 IoStatusBlock: *mut IO_STATUS_BLOCK, 83 ) -> NTSTATUS; 84 } 85 86 /// Asynchronous file descriptor 87 /// Implementing a single file handle to monitor multiple Io operations using 88 /// the IO multiplexing model. 89 #[derive(Debug)] 90 pub struct Afd { 91 fd: File, 92 } 93 94 impl Afd { 95 /// Creates a new Afd and add it to CompletionPort new(cp: &CompletionPort) -> io::Result<Afd>96 fn new(cp: &CompletionPort) -> io::Result<Afd> { 97 let mut afd_device_handle: HANDLE = INVALID_HANDLE_VALUE; 98 let mut io_status_block = IO_STATUS_BLOCK { 99 Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, 100 Information: 0, 101 }; 102 103 let fd = unsafe { 104 let status = NtCreateFile( 105 &mut afd_device_handle as *mut _, 106 SYNCHRONIZE, 107 &AFD_ATTRIBUTES as *const _ as *mut _, 108 &mut io_status_block, 109 null_mut(), 110 0, 111 FILE_SHARE_READ | FILE_SHARE_WRITE, 112 FILE_OPEN, 113 0, 114 null_mut(), 115 0, 116 ); 117 118 if status != STATUS_SUCCESS { 119 let raw_error = io::Error::from_raw_os_error(RtlNtStatusToDosError(status) as i32); 120 121 let msg = format!("Failed to open \\Device\\Afd\\Ylong: {raw_error}"); 122 return Err(io::Error::new(raw_error.kind(), msg)); 123 } 124 125 File::from_raw_handle(afd_device_handle as RawHandle) 126 }; 127 128 let token = NEXT_TOKEN.fetch_add(2, Ordering::Relaxed) + 2; 129 let afd = Afd { fd }; 130 // Add Afd to CompletionPort 131 cp.add_handle(token, &afd.fd)?; 132 133 syscall!( 134 SetFileCompletionNotificationModes( 135 afd_device_handle, 136 FILE_SKIP_SET_EVENT_ON_HANDLE as u8, 137 ), 138 afd 139 ) 140 } 141 142 /// System call poll( &self, info: &mut AfdPollInfo, iosb: *mut IO_STATUS_BLOCK, overlapped: *mut c_void, ) -> io::Result<bool>143 pub(crate) unsafe fn poll( 144 &self, 145 info: &mut AfdPollInfo, 146 iosb: *mut IO_STATUS_BLOCK, 147 overlapped: *mut c_void, 148 ) -> io::Result<bool> { 149 let afd_info = info as *mut _ as *mut c_void; 150 (*iosb).Anonymous.Status = STATUS_PENDING; 151 152 let status = NtDeviceIoControlFile( 153 self.fd.as_raw_handle() as HANDLE, 154 0, 155 None, 156 overlapped, 157 iosb, 158 IOCTL_AFD_POLL, 159 afd_info, 160 size_of::<AfdPollInfo>() as u32, 161 afd_info, 162 size_of::<AfdPollInfo>() as u32, 163 ); 164 165 match status { 166 STATUS_SUCCESS => Ok(true), 167 // this is expected. 168 STATUS_PENDING => Ok(false), 169 _ => Err(io::Error::from_raw_os_error( 170 RtlNtStatusToDosError(status) as i32 171 )), 172 } 173 } 174 175 /// System call to cancel File HANDLE. cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()>176 pub(crate) unsafe fn cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()> { 177 if (*iosb).Anonymous.Status != STATUS_PENDING { 178 return Ok(()); 179 } 180 181 let mut cancel_iosb = IO_STATUS_BLOCK { 182 Anonymous: IO_STATUS_BLOCK_0 { Status: 0 }, 183 Information: 0, 184 }; 185 let status = NtCancelIoFileEx(self.fd.as_raw_handle() as HANDLE, iosb, &mut cancel_iosb); 186 match status { 187 STATUS_SUCCESS | STATUS_NOT_FOUND => Ok(()), 188 _ => Err(io::Error::from_raw_os_error( 189 RtlNtStatusToDosError(status) as i32 190 )), 191 } 192 } 193 } 194 195 /// A group which contains Afds. 196 #[derive(Debug)] 197 pub(crate) struct AfdGroup { 198 cp: Arc<CompletionPort>, 199 afd_group: Mutex<Vec<Arc<Afd>>>, 200 } 201 202 /// Up to 32 Arc points per Afd. 203 const POLL_GROUP__MAX_GROUP_SIZE: usize = 32; 204 205 impl AfdGroup { 206 /// Creates a new AfdGroup. new(cp: Arc<CompletionPort>) -> AfdGroup207 pub(crate) fn new(cp: Arc<CompletionPort>) -> AfdGroup { 208 AfdGroup { 209 afd_group: Mutex::new(Vec::new()), 210 cp, 211 } 212 } 213 214 /// Gets a new point to File. acquire(&self) -> io::Result<Arc<Afd>>215 pub(crate) fn acquire(&self) -> io::Result<Arc<Afd>> { 216 let mut afd_group = self.afd_group.lock().unwrap(); 217 218 // When the last File has more than 32 Arc Points, creates a new File. 219 if afd_group.len() == 0 220 || Arc::strong_count(afd_group.last().unwrap()) > POLL_GROUP__MAX_GROUP_SIZE 221 { 222 let arc = Arc::new(Afd::new(&self.cp)?); 223 afd_group.push(arc); 224 } 225 226 match afd_group.last() { 227 Some(arc) => Ok(arc.clone()), 228 None => unreachable!( 229 "Cannot acquire afd, {:#?}, afd_group: {:#?}", 230 self, afd_group 231 ), 232 } 233 } 234 235 /// Delete Afd that is no longer in use from AfdGroup. release_unused_afd(&self)236 pub(crate) fn release_unused_afd(&self) { 237 let mut afd_group = self.afd_group.lock().unwrap(); 238 afd_group.retain(|g| Arc::strong_count(g) > 1); 239 } 240 } 241 242 #[repr(C)] 243 pub struct AfdPollInfo { 244 pub timeout: i64, 245 pub number_of_handles: u32, 246 pub exclusive: u32, 247 pub handles: [AfdPollHandleInfo; 1], 248 } 249 250 impl AfdPollInfo { zeroed() -> AfdPollInfo251 pub(crate) fn zeroed() -> AfdPollInfo { 252 unsafe { zeroed() } 253 } 254 } 255 256 impl fmt::Debug for AfdPollInfo { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 258 f.debug_struct("AfdPollInfo").finish() 259 } 260 } 261 262 #[repr(C)] 263 #[derive(Debug)] 264 pub struct AfdPollHandleInfo { 265 /// SockState base_socket 266 pub handle: HANDLE, 267 pub events: u32, 268 pub status: NTSTATUS, 269 } 270 271 unsafe impl Send for AfdPollHandleInfo {} 272