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#[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}