win_handles_sys/duplicate.rs
1// Copyright (c) 2026 Mike Grier
2// Split from crates/win-sync-sys/src/duplicate.rs at a9a97bf6.
3//! Duplicating a handle, with the access and the inheritance chosen: `DuplicateHandle`.
4
5use std::io;
6use std::ops::BitOr;
7use std::os::windows::io::{AsHandle, AsRawHandle, FromRawHandle, OwnedHandle};
8use std::ptr;
9
10use windows_sys::Win32::Foundation::{DUPLICATE_SAME_ACCESS, DuplicateHandle, FALSE, HANDLE, TRUE};
11use windows_sys::Win32::System::Threading::{
12 EVENT_MODIFY_STATE, GetCurrentProcess, SYNCHRONIZATION_SYNCHRONIZE,
13};
14
15/// An access mask for a duplicated handle: what the holder of the handle may do with the object.
16///
17/// A mask is a set of rights, combined with `|`. The rights an object type defines are named by
18/// Win32 (`EVENT_MODIFY_STATE` for an event, `SEMAPHORE_MODIFY_STATE` for a semaphore, and so on),
19/// and the two the code this was moved from needed are constants here, `SYNCHRONIZE`, which every
20/// object type defines, and `EVENT_MODIFY_STATE`, an event's; any other is
21/// [`from_bits`](Self::from_bits).
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub struct Access(u32);
24
25impl Access {
26 /// `SYNCHRONIZE`: the right to wait on the object, and nothing else. A handle with only this
27 /// can be waited on and cannot change the object.
28 pub const SYNCHRONIZE: Self = Self(SYNCHRONIZATION_SYNCHRONIZE);
29
30 /// `EVENT_MODIFY_STATE`: the right to set and reset an event.
31 pub const EVENT_MODIFY_STATE: Self = Self(EVENT_MODIFY_STATE);
32
33 /// A mask from its Win32 bits.
34 #[must_use]
35 pub const fn from_bits(bits: u32) -> Self {
36 Self(bits)
37 }
38
39 /// The mask's Win32 bits.
40 #[must_use]
41 pub const fn bits(self) -> u32 {
42 self.0
43 }
44}
45
46impl BitOr for Access {
47 type Output = Self;
48
49 fn bitor(self, other: Self) -> Self {
50 Self(self.0 | other.0)
51 }
52}
53
54/// The access a duplicate carries: the source's own, or a mask chosen for it.
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub enum DuplicateAccess {
57 /// Whatever access the source handle has, as `DUPLICATE_SAME_ACCESS` asks. Not a mask of
58 /// nothing: the duplicate can do what the source can.
59 Same,
60 /// Exactly this mask, whatever the source holds.
61 Chosen(Access),
62}
63
64/// Whether a child process can inherit a duplicate.
65///
66/// This is the duplicate's own attribute, set by the call and independent of the source's: an
67/// inheritable source can be duplicated to a handle that is not, and the reverse.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69pub enum Inheritance {
70 /// Not inheritable: a process this one creates does not receive the handle.
71 NotInheritable,
72 /// Inheritable: a process this one creates receives the handle when it is created asking to
73 /// inherit handles.
74 Inheritable,
75}
76
77/// A second handle to the object `handle` refers to, in this process, with the access and
78/// inheritance chosen.
79///
80/// With [`DuplicateAccess::Chosen`] the result can do no more than the mask allows, which is the
81/// use: a handle with `SYNCHRONIZE` alone can wait on an object and change nothing about it. With
82/// [`DuplicateAccess::Same`] it can do what the source can, so a caller that only needs another
83/// handle to the object does not have to name the rights. The object lives until every handle to it
84/// is closed, and closing the result does not touch the source.
85///
86/// **Windows does not confine the result to the source's access.** A duplicate may ask for rights
87/// the source handle lacks and get them, so a holder of a narrow handle can make a wider one with
88/// this very function. The narrowing is therefore a guard against a handle being used for what it
89/// was not handed out for, not a boundary against code in the process that sets out to cross it --
90/// no such boundary exists inside a process, and raw `DuplicateHandle` crosses it just as well.
91///
92/// A chosen access of nothing is accepted and gives a handle that can do nothing.
93///
94/// # Errors
95///
96/// The error from `DuplicateHandle`: access denied for a chosen mask Windows refuses, such as one
97/// with every bit set, and otherwise only when the process cannot get another handle, which no test
98/// reaches.
99pub fn duplicate_handle(
100 handle: impl AsHandle,
101 access: DuplicateAccess,
102 inheritance: Inheritance,
103) -> io::Result<OwnedHandle> {
104 let (desired_access, options) = match access {
105 DuplicateAccess::Same => (0, DUPLICATE_SAME_ACCESS),
106 DuplicateAccess::Chosen(mask) => (mask.bits(), 0),
107 };
108 let inherit = match inheritance {
109 Inheritance::NotInheritable => FALSE,
110 Inheritance::Inheritable => TRUE,
111 };
112 let mut duplicate: HANDLE = ptr::null_mut();
113 // SAFETY: both process handles are the current-process pseudo-handle, the source is open for
114 // the borrow, and `duplicate` is a valid out pointer.
115 let ok = unsafe {
116 DuplicateHandle(
117 GetCurrentProcess(),
118 handle.as_handle().as_raw_handle(),
119 GetCurrentProcess(),
120 &mut duplicate,
121 desired_access,
122 inherit,
123 options,
124 )
125 };
126 if ok == FALSE {
127 return Err(io::Error::last_os_error());
128 }
129 // SAFETY: the call succeeded, so `duplicate` is a new handle nothing else owns.
130 Ok(unsafe { OwnedHandle::from_raw_handle(duplicate) })
131}
132
133/// A second handle to the object `handle` refers to, in this process, carrying exactly `access`.
134///
135/// This is [`duplicate_handle`] with [`DuplicateAccess::Chosen`] and [`Inheritance::NotInheritable`],
136/// which is what its documentation says of the access, the narrowing and the errors. Not
137/// inheritable by child processes.
138///
139/// # Errors
140///
141/// The errors of [`duplicate_handle`].
142pub fn duplicate_handle_with_access(
143 handle: impl AsHandle,
144 access: Access,
145) -> io::Result<OwnedHandle> {
146 duplicate_handle(
147 handle,
148 DuplicateAccess::Chosen(access),
149 Inheritance::NotInheritable,
150 )
151}
152
153#[cfg(test)]
154mod tests;