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