Skip to main content

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}