Skip to main content

windows_erg/pipes/
types.rs

1use std::borrow::Cow;
2use std::time::{Duration, SystemTime};
3
4use windows::Win32::Foundation::HANDLE;
5
6use crate::error::InvalidParameterError;
7use crate::security::SecurityDescriptor;
8use crate::utils::OwnedHandle;
9use crate::{Error, Result};
10
11/// Canonical named pipe path.
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct PipeName(String);
14
15impl PipeName {
16    /// Prefix required by Win32 named pipes.
17    pub const PREFIX: &'static str = r"\\.\pipe\";
18
19    /// Validate and create a named pipe path.
20    pub fn new(path: impl Into<String>) -> Result<Self> {
21        let path = path.into();
22        if path.is_empty() {
23            return Err(Error::InvalidParameter(InvalidParameterError::new(
24                "path",
25                "Pipe name cannot be empty",
26            )));
27        }
28        if !path.starts_with(Self::PREFIX) {
29            return Err(Error::InvalidParameter(InvalidParameterError::new(
30                "path",
31                "Pipe name must start with \\\\.\\pipe\\",
32            )));
33        }
34        if path == Self::PREFIX {
35            return Err(Error::InvalidParameter(InvalidParameterError::new(
36                "path",
37                "Pipe name must include a segment after \\\\.\\pipe\\",
38            )));
39        }
40
41        Ok(Self(path))
42    }
43
44    /// Return the canonical named pipe path.
45    pub fn as_str(&self) -> &str {
46        &self.0
47    }
48
49    /// Create a canonical pipe name from a relative NamedPipe directory entry.
50    pub fn from_relative_name(name: impl AsRef<str>) -> Result<Self> {
51        let name = name.as_ref();
52        Self::new(format!("{}{}", Self::PREFIX, name))
53    }
54}
55
56impl std::fmt::Display for PipeName {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        write!(f, "{}", self.0)
59    }
60}
61
62/// Snapshot metadata for a named pipe currently present in the local pipe namespace.
63#[derive(Debug, Clone)]
64pub struct NamedPipeInfo {
65    /// Canonical pipe path (for example `\\.\pipe\my-pipe`).
66    pub pipe_name: PipeName,
67    /// Relative entry name as returned by the NamedPipe filesystem.
68    pub relative_name: String,
69    /// Optional creation time when the filesystem reports it.
70    pub creation_time: Option<SystemTime>,
71    /// Optional last access time when the filesystem reports it.
72    pub last_access_time: Option<SystemTime>,
73    /// Optional last write time when the filesystem reports it.
74    pub last_write_time: Option<SystemTime>,
75    /// Optional metadata change time when the filesystem reports it.
76    pub change_time: Option<SystemTime>,
77    /// End-of-file size reported by the filesystem.
78    pub end_of_file: i64,
79    /// Allocation size reported by the filesystem.
80    pub allocation_size: i64,
81    /// Raw Win32 file attribute bits.
82    pub file_attributes: u32,
83    /// Directory file index when reported by the filesystem.
84    pub file_index: u32,
85    /// Optional local pipe state details from `FilePipeLocalInformation`.
86    pub local_info: Option<NamedPipeLocalInfo>,
87}
88
89impl NamedPipeInfo {
90    /// Return the canonical pipe path.
91    pub fn pipe_name(&self) -> &PipeName {
92        &self.pipe_name
93    }
94}
95
96/// Local named-pipe state details returned by `FilePipeLocalInformation`.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct NamedPipeLocalInfo {
99    /// Named pipe type as reported by the kernel.
100    pub named_pipe_type: u32,
101    /// Server/client configuration value.
102    pub named_pipe_configuration: u32,
103    /// Maximum pipe instances allowed.
104    pub maximum_instances: u32,
105    /// Current number of connected/open instances.
106    pub current_instances: u32,
107    /// Inbound quota size in bytes.
108    pub inbound_quota: u32,
109    /// Bytes currently available for reading.
110    pub read_data_available: u32,
111    /// Outbound quota size in bytes.
112    pub outbound_quota: u32,
113    /// Remaining write quota in bytes.
114    pub write_quota_available: u32,
115    /// Current pipe state value.
116    pub named_pipe_state: u32,
117    /// Whether this handle points at server or client end.
118    pub named_pipe_end: u32,
119}
120
121/// Change detected between named pipe snapshots.
122#[derive(Debug, Clone)]
123pub enum NamedPipeChange {
124    /// A pipe is present in the current snapshot but was absent previously.
125    Appeared(NamedPipeInfo),
126    /// A pipe disappeared since the previous snapshot.
127    Removed(NamedPipeInfo),
128}
129
130pub(crate) fn filetime_to_system_time(filetime: i64) -> Option<SystemTime> {
131    const FILETIME_TO_UNIX_EPOCH: i64 = 116_444_736_000_000_000;
132
133    if filetime <= 0 {
134        return None;
135    }
136
137    let intervals_since_unix = filetime.saturating_sub(FILETIME_TO_UNIX_EPOCH);
138    let seconds = intervals_since_unix.div_euclid(10_000_000) as u64;
139    let nanos = (intervals_since_unix.rem_euclid(10_000_000) as u32) * 100;
140
141    Some(SystemTime::UNIX_EPOCH + Duration::new(seconds, nanos))
142}
143
144/// Access direction for a named pipe instance.
145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum NamedPipeOpenMode {
147    /// Read-only server endpoint.
148    Inbound,
149    /// Write-only server endpoint.
150    Outbound,
151    /// Read/write server endpoint.
152    Duplex,
153}
154
155/// Message semantics for a named pipe.
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum NamedPipeType {
158    /// Byte stream mode.
159    Byte,
160    /// Message-framed mode.
161    Message,
162}
163
164/// Security attributes used when creating or opening a pipe handle.
165#[derive(Debug, Clone)]
166pub struct PipeSecurityOptions {
167    /// Whether spawned child processes can inherit this handle.
168    pub inherit_handle: bool,
169    /// Optional descriptor model used for ACL/owner semantics.
170    pub security_descriptor: Option<SecurityDescriptor>,
171}
172
173impl PipeSecurityOptions {
174    /// Create default security options.
175    pub fn new() -> Self {
176        Self {
177            inherit_handle: false,
178            security_descriptor: None,
179        }
180    }
181
182    /// Enable or disable handle inheritance.
183    pub fn inherit_handle(mut self, inherit_handle: bool) -> Self {
184        self.inherit_handle = inherit_handle;
185        self
186    }
187
188    /// Set a structured security descriptor model.
189    pub fn security_descriptor(mut self, security_descriptor: SecurityDescriptor) -> Self {
190        self.security_descriptor = Some(security_descriptor);
191        self
192    }
193}
194
195impl Default for PipeSecurityOptions {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201/// Server-side named pipe endpoint handle.
202#[derive(Debug)]
203pub struct PipeServerEndpoint {
204    handle: OwnedHandle,
205    pipe_name: PipeName,
206    open_mode: NamedPipeOpenMode,
207    pipe_type: NamedPipeType,
208}
209
210impl PipeServerEndpoint {
211    /// Create a server endpoint from a raw handle.
212    pub(crate) fn from_raw(
213        handle: HANDLE,
214        close_on_drop: bool,
215        pipe_name: PipeName,
216        open_mode: NamedPipeOpenMode,
217        pipe_type: NamedPipeType,
218    ) -> Self {
219        Self {
220            handle: OwnedHandle::with_ownership(handle, close_on_drop),
221            pipe_name,
222            open_mode,
223            pipe_type,
224        }
225    }
226
227    /// Return underlying Win32 handle.
228    pub fn raw_handle(&self) -> HANDLE {
229        self.handle.raw()
230    }
231
232    /// Return named pipe path.
233    pub fn pipe_name(&self) -> &PipeName {
234        &self.pipe_name
235    }
236
237    /// Return open direction.
238    pub fn open_mode(&self) -> NamedPipeOpenMode {
239        self.open_mode
240    }
241
242    /// Return byte/message behavior.
243    pub fn pipe_type(&self) -> NamedPipeType {
244        self.pipe_type
245    }
246
247    /// Configure whether this handle should be closed on drop.
248    pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
249        self.handle.set_close_on_drop(close_on_drop);
250    }
251}
252
253/// Client-side named pipe endpoint handle.
254#[derive(Debug)]
255pub struct PipeClientEndpoint {
256    handle: OwnedHandle,
257    pipe_name: PipeName,
258    open_mode: NamedPipeOpenMode,
259}
260
261impl PipeClientEndpoint {
262    /// Create a client endpoint from a raw handle.
263    pub(crate) fn from_raw(
264        handle: HANDLE,
265        close_on_drop: bool,
266        pipe_name: PipeName,
267        open_mode: NamedPipeOpenMode,
268    ) -> Self {
269        Self {
270            handle: OwnedHandle::with_ownership(handle, close_on_drop),
271            pipe_name,
272            open_mode,
273        }
274    }
275
276    /// Return underlying Win32 handle.
277    pub fn raw_handle(&self) -> HANDLE {
278        self.handle.raw()
279    }
280
281    /// Return named pipe path.
282    pub fn pipe_name(&self) -> &PipeName {
283        &self.pipe_name
284    }
285
286    /// Return open direction.
287    pub fn open_mode(&self) -> NamedPipeOpenMode {
288        self.open_mode
289    }
290
291    /// Configure whether this handle should be closed on drop.
292    pub fn set_close_on_drop(&mut self, close_on_drop: bool) {
293        self.handle.set_close_on_drop(close_on_drop);
294    }
295}
296
297pub(crate) fn to_cow_pipe_name(pipe_name: Option<&PipeName>) -> Cow<'static, str> {
298    match pipe_name {
299        Some(name) => Cow::Owned(name.to_string()),
300        None => Cow::Borrowed("<unnamed pipe>"),
301    }
302}