win_sync_sys/duplicate.rs
1// Copyright (c) 2026 Mike Grier
2//! Duplicating a handle with a chosen access: `DuplicateHandle`.
3
4use std::io;
5use std::ops::BitOr;
6use std::os::windows::io::{AsHandle, AsRawHandle, FromRawHandle, OwnedHandle};
7use std::ptr;
8
9use windows_sys::Win32::Foundation::{DuplicateHandle, FALSE, HANDLE};
10use windows_sys::Win32::System::Threading::{
11 EVENT_MODIFY_STATE, GetCurrentProcess, SYNCHRONIZATION_SYNCHRONIZE,
12};
13
14/// An access mask for a duplicated handle: what the holder of the handle may do with the object.
15///
16/// A mask is a set of rights, combined with `|`. The rights an object type defines are named by
17/// Win32 (`EVENT_MODIFY_STATE` for an event, `SEMAPHORE_MODIFY_STATE` for a semaphore, and so on),
18/// and the ones this crate's own objects need are constants here; any other is
19/// [`from_bits`](Self::from_bits).
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct Access(u32);
22
23impl Access {
24 /// `SYNCHRONIZE`: the right to wait on the object, and nothing else. A handle with only this
25 /// can be waited on and cannot change the object.
26 pub const SYNCHRONIZE: Self = Self(SYNCHRONIZATION_SYNCHRONIZE);
27
28 /// `EVENT_MODIFY_STATE`: the right to set and reset an event.
29 pub const EVENT_MODIFY_STATE: Self = Self(EVENT_MODIFY_STATE);
30
31 /// A mask from its Win32 bits.
32 #[must_use]
33 pub const fn from_bits(bits: u32) -> Self {
34 Self(bits)
35 }
36
37 /// The mask's Win32 bits.
38 #[must_use]
39 pub const fn bits(self) -> u32 {
40 self.0
41 }
42}
43
44impl BitOr for Access {
45 type Output = Self;
46
47 fn bitor(self, other: Self) -> Self {
48 Self(self.0 | other.0)
49 }
50}
51
52/// A second handle to the object `handle` refers to, in this process, carrying exactly `access`.
53///
54/// The result can do no more than `access` allows, which is the use: a handle with `SYNCHRONIZE`
55/// alone can wait on an object and change nothing about it. The object lives until every handle to
56/// it is closed, and closing the result does not touch the source.
57///
58/// **Windows does not confine the result to the source's access.** A duplicate may ask for rights
59/// the source handle lacks and get them, so a holder of a narrow handle can make a wider one with
60/// this very function. The narrowing is therefore a guard against a handle being used for what it
61/// was not handed out for, not a boundary against code in the process that sets out to cross it --
62/// no such boundary exists inside a process, and raw `DuplicateHandle` crosses it just as well.
63///
64/// Not inheritable by child processes. An access of nothing is accepted and gives a handle that
65/// can do nothing.
66///
67/// # Errors
68///
69/// The error from `DuplicateHandle`: access denied for a mask Windows refuses, such as one with
70/// every bit set, and otherwise only when the process cannot get another handle, which no test
71/// reaches.
72pub fn duplicate_handle_with_access(
73 handle: impl AsHandle,
74 access: Access,
75) -> io::Result<OwnedHandle> {
76 let mut duplicate: HANDLE = ptr::null_mut();
77 // SAFETY: both process handles are the current-process pseudo-handle, the source is open for
78 // the borrow, and `duplicate` is a valid out pointer.
79 let ok = unsafe {
80 DuplicateHandle(
81 GetCurrentProcess(),
82 handle.as_handle().as_raw_handle(),
83 GetCurrentProcess(),
84 &mut duplicate,
85 access.bits(),
86 FALSE,
87 0,
88 )
89 };
90 if ok == FALSE {
91 return Err(io::Error::last_os_error());
92 }
93 // SAFETY: the call succeeded, so `duplicate` is a new handle nothing else owns.
94 Ok(unsafe { OwnedHandle::from_raw_handle(duplicate) })
95}
96
97#[cfg(test)]
98mod tests;