trona_posix Overview
trona_posix is the crate that turns POSIX-shaped Rust calls into substrate IPC messages.
Every posix_* function in it is a thin translator: read the arguments, build a TronaMsg, pick the right server endpoint from substrate::caps, call trona::ipc::call_ctx, and unpack the reply.
The crate is 6,927 lines across 17 files.
It is the crate basaltc’s C ABI wrappers call into — when a C program does write(fd, buf, len), the chain is:
basaltc::unistd::write
→ trona_posix::file::posix_write
→ trona::ipc::call_ctx (label = VFS_WRITE)
→ trona::syscall::syscall(SYS_CALL, ...)
→ kernel IPC fastpath
→ vfs server handles the request
There is no meaningful work in trona_posix beyond argument marshalling and error translation.
The actual POSIX semantics — how a read from a pipe blocks, how O_NONBLOCK affects a socket, how mmap(fd) resolves the backing store — live either in the consumed server (VFS, mmsrv, …) or in the kernel.
trona_posix is the wire protocol.
Module map
The crate is flat: there are no subdirectories other than arch/ for per-architecture fork.S.
| Module | Lines | Role | Page |
|---|---|---|---|
|
965 |
POSIX thread lifecycle — |
|
|
892 |
Process management and time — |
|
|
753 |
File I/O — |
|
|
731 |
Sockets — full BSD/POSIX surface ( |
|
|
644 |
Module declarations, global signal state, error-code translation helpers, C ABI exports, re-exports from |
this page |
|
587 |
Everything that does not fit elsewhere — |
scattered |
|
441 |
The |
|
|
394 |
DNS client — |
|
|
386 |
POSIX signal delivery — |
|
|
207 |
Multiplexing — |
|
|
206 |
Internal-only SHM-based bulk transfer path for payloads larger than the IPC register window. Called by |
|
|
190 |
|
|
|
123 |
|
|
|
110 |
Per-thread accessors — |
|
|
8 |
Trivial shim. Exists for symmetry with the other substrate-style modules. |
— |
|
3 |
Trivial shim. |
— |
|
4 |
Trivial shim. |
— |
Plus arch/x86_64/fork.S (154 lines) and arch/aarch64/fork.S (129 lines) — the fork trampolines covered in fork.S and Linker Scripts.
Server delegation map
Every posix_* function eventually targets one of six endpoints in the substrate cap table.
The delegation pattern is fixed:
| Module | Primary target | Typical labels |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lazily resolved via |
|
|
|
|
IPC Protocol Labels is the authoritative source for the full label catalog.
The trona_err_to_posix translator
Every posix_* function that returns an i32 uses the internal trona_err_to_posix helper in posix/lib.rs to convert a substrate TRONA_* error into a negative POSIX errno value:
pub(crate) fn trona_err_to_posix(label: u64) -> i32 {
match label {
TRONA_OK => 0,
TRONA_NOT_FOUND => -2, // ENOENT
TRONA_ALREADY_EXISTS => -17, // EEXIST
TRONA_SLOT_OCCUPIED => -17, // EEXIST
TRONA_ALREADY_MAPPED => -17, // EEXIST
TRONA_INVALID_ARGUMENT => -22, // EINVAL
TRONA_OUT_OF_MEMORY => -12, // ENOMEM
TRONA_BUSY => -16, // EBUSY
TRONA_ALREADY_BOUND => -16, // EBUSY
TRONA_WOULD_BLOCK => -11, // EAGAIN
TRONA_IN_PROGRESS => -115, // EINPROGRESS
TRONA_BAD_ADDRESS => -14, // EFAULT
TRONA_INSUFFICIENT_RIGHTS => -13, // EACCES
// ...
}
}
The return is negative so basaltc can distinguish errors from success by sign without carrying a separate flag word.
Callers in basaltc flip the sign, store the positive value in thread-local errno, and return -1 to the C caller.
Notice that multiple TRONA_* codes can collapse into the same POSIX errno: TRONA_ALREADY_EXISTS, TRONA_SLOT_OCCUPIED, and TRONA_ALREADY_MAPPED all become EEXIST, because POSIX does not distinguish between "a file already exists with this name" and "a capability slot was already occupied by a prior step".
The substrate-side distinction is preserved only at the trona layer, which is sometimes useful for debugging but is opaque to POSIX code.
Global signal state
posix/lib.rs also owns the global signal state shared between signals.rs and the C ABI exports:
pub static mut __sig_handlers: [AtomicU64; NSIG];
pub static mut __sig_blocked_mask: AtomicU64;
pub static mut __sig_sa_mask: [AtomicU64; NSIG];
pub static mut __sig_sa_flags: [AtomicU32; NSIG];
pub static mut __sig_last_restart: AtomicBool;
-
__sig_handlers[i]is the user-provided handler function pointer, orSIG_DFL = 0/SIG_IGN = 1. -
__sig_blocked_maskis the bitmask of currently blocked signals. -
__sig_sa_mask[i]is the extra mask a handler runs under (the sa_mask fromsigaction). -
_sig_sa_flags[i]is theSA*flags for signali. -
__sig_last_restartrecords whether the most recently delivered signal hadSA_RESTARTset.
These live in the crate root because both signals.rs (the installer and internal dispatcher) and the C ABI exports in lib.rs (the direct entry points basaltc calls) need to read and write them.
See Process and Signals for how these are used.
Why there is no sync module here
It is worth calling out that trona_posix has no sync.rs.
Every pthread mutex, rwlock, condvar, and once in basaltc is ultimately a trona::sync::* primitive from substrate.
The pthread layer in pthread.rs adds the POSIX semantics on top (recursive / errorcheck flavors, sa_mask for cancellation handlers, etc.) but the underlying lock is substrate’s.
This is the opposite of how glibc is structured, where libpthread owns its own lock implementation.
In SaltyOS, sync primitives have to work before trona_posix is loaded (the slot allocator needs a spinlock during rtld startup), so they live in substrate; trona_posix::pthread is a wrapper layer, not an implementation layer.
See Synchronization Primitives for the details.
Related pages
-
File I/O and *at() Family — the
fileandatmodules. -
Process and Signals — the
procandsignalsmodules. -
Sockets and DNS — the
socketanddnsmodules. -
Poll, Pipe, and Bulk I/O — the
poll,pipe, andbulkmodules. -
POSIX Memory Management — the
mmmodule. -
POSIX Threads — the
pthreadandtlsmodules. -
IPC — the substrate primitive every
posix_*call flows through. -
basalt: The trona boundary — the view from the C side.