roughtime 0.1.0

A no_std-capable Roughtime secure time-sync client with pluggable crypto backends
Documentation
//! The Linux [`SystemClock`](crate::clock::SystemClock) implementation, backed by
//! `clock_gettime`/`clock_settime`.

use crate::clock::{ClockError, SystemClock, Time};

/// The default Linux [`SystemClock`] implementation.
#[derive(Debug, Clone, Copy)]
pub struct LinuxClock;

impl SystemClock for LinuxClock {
    fn get() -> Result<Time, ClockError> {
        let mut ts = libc::timespec {
            tv_sec: 0,
            tv_nsec: 0,
        };
        // SAFETY: `ts` is a valid, exclusively-owned stack-local `timespec`; `clock_gettime`
        // only writes into it and reports failure via its return value — no memory-safety risk.
        #[allow(unsafe_code)]
        let rc = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &raw mut ts) };
        if rc == 0 {
            Ok(Time {
                secs: u64::try_from(ts.tv_sec).unwrap_or(0),
                nanos: u32::try_from(ts.tv_nsec).unwrap_or(0),
            })
        } else {
            Err(os_error())
        }
    }

    fn set(time: Time) -> Result<(), ClockError> {
        let ts = libc::timespec {
            tv_sec: i64::try_from(time.secs).unwrap_or(i64::MAX),
            tv_nsec: i64::from(time.nanos),
        };
        // SAFETY: `ts` is a valid stack-local `timespec`; `clock_settime` only mutates kernel
        // clock state and reports failure via its return value — no memory-safety risk. We
        // check the return code and translate `EPERM` (missing CAP_SYS_TIME) into a graceful
        // `Err` rather than panicking, per this crate's "never panic on missing privilege or
        // unsupported target" contract for `SystemClock` implementations.
        #[allow(unsafe_code)]
        let rc = unsafe { libc::clock_settime(libc::CLOCK_REALTIME, &raw const ts) };
        if rc == 0 { Ok(()) } else { Err(os_error()) }
    }
}

fn os_error() -> ClockError {
    // SAFETY: `__errno_location` returns a pointer to thread-local storage that is always
    // valid to read from the calling thread; we only read the value, never store the pointer.
    #[allow(unsafe_code)]
    let errno = unsafe { *libc::__errno_location() };
    if errno == libc::EPERM {
        ClockError::PermissionDenied
    } else {
        ClockError::Os(errno)
    }
}