pub fn write_atomic(path: &Path, bytes: &[u8]) -> Result<()>Expand description
Durably write bytes to path via create-tmp + fsync + rename + parent fsync.
The temporary file uses a UUIDv7-derived suffix to avoid collision with any
concurrent writer targeting the same final path, and is opened with
create_new so a stale suffix collision fails safely instead of clobbering.
Callers that already hold the parent directory pass any Path; missing
parents are created before the temp file is opened.
If any step between temp creation and rename fails, the temp file is
unlinked via an RAII guard so we never leak zero-byte .tmp files into
the spool. A repeatedly-failing writer would otherwise fill the inode
table on ext4/xfs long before the disk is full — an operational silent
death.
Semantics of Ok(()) vs Err(...) after rename: once the tmp file
has been atomically renamed onto path, the caller can consider the
data durably visible. A post-rename sync_directory failure means the
dirent may not survive an immediate power loss on POSIX-conformant
filesystems (xfs, btrfs, ext4 with data=ordered), but the file is
present and readable for every observer running now. Historically this
function still returned Err in that case (round-12 F5), which
misled callers whose retry logic assumes “Err → not present”: they
would either double-write (harmless but wasted IO) or, worse, treat
the write as failed and skip session-state advancement while the
file was in fact readable — producing a hard split between on-disk
state and in-registry accounting.
Fix: post-rename sync_directory failure now becomes a
tracing::warn! (best-effort) and Ok(()) is returned. Callers
that need a stronger guarantee should call sync_directory again
after their own operation completes. A dedicated counter is not
registered here because fsutil cannot depend on av-core’s
metrics registry without a cycle; harness-level callers can wrap
this with their own counter if needed.