• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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