review pass: mount safety, abort/quit robustness, UI fixes

Engine / safety:
- verify mount points against /proc/self/mountinfo instead of path
  existence; refuse to sync into a leftover (unmounted) directory that
  would otherwise receive the library on the root filesystem
- resolve udisks block devices by label when the path is not mounted,
  and never let findmnt fall back to the containing root filesystem
- abort now SIGTERMs the whole child process group (sh -c/podkit and
  rsync children included), escalating to SIGKILL after 2s
- quitting while a sync runs aborts and waits for the engine instead of
  orphaning the child
- atomically mark the engine busy before spawning to prevent double
  syncs; persist state before publishing completion
- don't follow symlinked directories while scanning (recursion loops)
- prune empty directories recursively when removing stale files

UI:
- help overlay was clipped (hardcoded height): size it from content
- speed/ETA were pushed onto a line the progress panel clipped; show
  them right-aligned on the bytes row
- leaving log follow with u/d now scrolls from the bottom instead of
  jumping to the top of the log
- reload config with r (label updated), refresh mounts on completion so
  last-sync ages are current
- fix width underflows on narrow terminals; terminal-aware sidebar
  scrolling

Tests: mountinfo unescape, process-group abort, and a TestBackend render
smoke test covering all tabs, small terminals and the help overlay.
This commit is contained in:
Sebastian
2026-09-10 19:35:03 +02:00
parent 5b1e8b742b
commit f566162c29
11 changed files with 470 additions and 138 deletions
+4 -3
View File
@@ -130,12 +130,13 @@ pub struct Config {
}
impl Config {
fn refresh_mounts(&mut self) {
/// Refresh `Device::mounted` from the kernel mount table (no subprocess).
pub fn refresh_mounts(&mut self) {
for d in self.devices.iter_mut() {
d.mounted = d
.mount_point
.as_ref()
.map(|p| p.exists())
.as_deref()
.map(crate::disk::is_mounted)
.unwrap_or(false);
}
}