catalejo_sys/exception/backend.rs
1//! PID-aware access to the process singleton for Catalejo's built-in records.
2
3use core::{num::NonZero, ptr::NonNull};
4use std::{
5 io,
6 os::fd::{AsRawFd, BorrowedFd},
7};
8
9use crate::ffi::binding;
10
11use super::status;
12
13/// A process-local proof that Catalejo's linked fault routines are published.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct Backend(
16 /// The process-lifetime C singleton established for the creating PID.
17 NonNull<binding::catalejo_fault_backend>,
18 /// The nonzero process identifier for which the singleton was published.
19 NonZero<u32>,
20);
21
22// SAFETY: The pointer names process-lifetime singleton storage. C does not mutate an initialized
23// backend within one pid, and a fork leaves only the calling thread in the child before rebuild.
24unsafe impl Send for Backend {}
25
26// SAFETY: Every thread in one pid observes the same immutable initialized backend.
27unsafe impl Sync for Backend {}
28
29impl Backend {
30 /// Initialize or reuse the process backend through a Mirilla descriptor.
31 ///
32 /// # Safety
33 ///
34 /// `device` must be a descriptor created by Mirilla.
35 ///
36 /// # Errors
37 ///
38 /// This returns creation, mapping, loading, publication, or stale-rebuild failures.
39 #[inline]
40 pub unsafe fn initialize(device: BorrowedFd<'_>) -> io::Result<Self> {
41 let mut target_value = core::ptr::null();
42
43 // SAFETY: The caller supplies the descriptor contract and the output points to local
44 // storage. C publishes a process-lifetime singleton pointer only on success.
45 let target_state = unsafe {
46 binding::catalejo_fault_backend_initialize(device.as_raw_fd(), &raw mut target_value)
47 };
48
49 Self::lift(target_state, target_value)
50 }
51
52 /// Retrieve the backend initialized for the calling pid.
53 ///
54 /// # Errors
55 ///
56 /// This returns not-found before initialization and stale in a fork child.
57 #[inline]
58 pub fn retrieve() -> io::Result<Self> {
59 let mut target_value = core::ptr::null();
60
61 // SAFETY: C writes either null with an error or its process singleton pointer.
62 let target_state =
63 unsafe { binding::catalejo_fault_backend_retrieve(&raw mut target_value) };
64
65 Self::lift(target_state, target_value)
66 }
67
68 /// Lift a successful singleton result.
69 fn lift(
70 target_status: core::ffi::c_int,
71 raw: *const binding::catalejo_fault_backend,
72 ) -> io::Result<Self> {
73 status(target_status)?;
74
75 let raw = NonNull::new(raw.cast_mut())
76 .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?;
77 let process_id = NonZero::new(std::process::id())
78 .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?;
79
80 Ok(Self(raw, process_id))
81 }
82
83 /// Verify that this backend was published for the calling process.
84 ///
85 /// # Errors
86 ///
87 /// This returns stale when the handle was inherited across `fork`.
88 #[inline]
89 pub fn validate(&self) -> io::Result<()> {
90 let &Self(_, process_id) = self;
91 let current_process = std::process::id();
92
93 match process_id.get() == current_process {
94 true => Ok(()),
95 false => Err(io::Error::from_raw_os_error(libc::ESTALE)),
96 }
97 }
98}