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