whyno-gather is the OS-level state-gathering crate in the whyno project. It collects Linux file-permission metadata — stat(), POSIX ACLs, filesystem flags, mount options, MAC labels (SELinux/AppArmor), and process capabilities — and packages everything into a single SystemState struct for the check pipeline.
What It Does
Given an absolute file path, a resolved subject (user/group identity), and an operation, whyno-gather walks the entire ancestor chain from / to the target and collects:
- File stat — ownership (uid/gid), mode bits, file type, device ID
- POSIX ACLs — parsed directly from
system.posix_acl_accessxattr bytes - Filesystem flags — immutable and append-only flags via
ioctl(FS_IOC_GETFLAGS) - Mount info — filesystem type, device, mountpoint from
/proc/self/mountinfo - Mount options —
read_only,noexec,nosuidfromstatvfs()(authoritative, no text parsing) - MAC labels — SELinux file/process contexts and AppArmor profiles
- Process info — effective capabilities, UID/GID from
/proc/<pid>/status
Features
- Single entry point — call
gather_state()and get a completeSystemState - Ancestor walk — every directory from
/to the target is stat'd, ACL-read, and flag-checked - Authoritative mount flags —
statvfs()is the source of truth forro/noexec/nosuid; mountinfo text options are fallback only - Subject resolution — resolve by username, UID, PID, or systemd service name
- Graceful degradation — each probe returns
Probe::Known(T),Probe::Unknown, orProbe::Inaccessibleinstead of failing the entire gather - Feature-gated MAC — SELinux and AppArmor support are opt-in via Cargo features
Installation
[]
= "0.3"
Feature Flags
| Flag | What it enables | Default |
|---|---|---|
selinux |
SELinux context gathering via the selinux crate |
off |
apparmor |
AppArmor profile detection | off |
ext4-tests |
Integration tests that require an ext4 filesystem | off |
acl-tests |
Integration tests that require POSIX ACL support | off |
Quick Start
Gather full state for a file
use ;
use Operation;
use Path;
let subject = resolve_username?;
let state = gather_state?;
// state.walk — stat, ACL, and flags for every ancestor from / to target
// state.mounts — mount table with statvfs-verified options
// state.mac_state — SELinux/AppArmor labels (if features enabled)
Resolve a subject by UID
use resolve_uid;
let subject = resolve_uid?; // www-data
println!;
Resolve a subject by PID
use resolve_pid;
let subject = resolve_pid?;
// Reads /proc/<pid>/status for UID, GID, supplementary groups, and capabilities
Resolve a systemd service
use resolve_service;
let subject = resolve_service?;
// Looks up MainPID via systemctl, then resolves via /proc
Modules
| Module | Purpose |
|---|---|
acl |
POSIX ACL reading — parses raw xattr bytes into typed ACL entries |
stat |
File stat gathering — ownership, mode, file type |
statvfs |
Mount option probing via statvfs() syscall |
mountinfo |
Mount table parsing from /proc/self/mountinfo |
proc |
Process info — reads /proc/<pid>/status for UIDs, GIDs, capabilities |
subject |
Subject resolution — username, UID, PID, or service → ResolvedSubject |
fsflags |
Filesystem flags — immutable/append-only via ioctl(FS_IOC_GETFLAGS) |
mac |
MAC label gathering — SELinux contexts and AppArmor profiles |
error |
GatherError enum covering all failure modes |
Error Handling
All fallible operations return Result<…, GatherError>. Main variants:
RelativePath— path must be absoluteMountInfoUnreadable— could not read/proc/self/mountinfoStatFailed—stat()call failed for a pathUserNotFound/UidNotFound— subject resolution failedProcUnreadable— could not read/proc/<pid>/statusServiceResolveFailed— systemd service PID lookup failedParseError— malformed/procor passwd content
Individual per-component probes (ACLs, flags, MAC labels) use Probe::Unknown rather than hard errors — a single inaccessible attribute does not abort the entire gather.
Dependencies
whyno-core = "0.3"— shared types (SystemState,Operation,Probe, etc.)nix— safe Rust bindings forstat(),ioctl, user/group resolutionlibc— raw syscall constants and typesthiserror— structured error typesselinux(optional) — SELinux context API (selinuxfeature only)
Requirements
- Linux only — relies on
/proc, xattrs,ioctl, andstatvfs - Rust 1.78+ (workspace MSRV)
Integration Pattern
use ;
use ;
use Path;
let subject = resolve_username?;
let state = gather_state?;
let report = run_checks;
if !report.is_allowed