Skip to main content

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;