v0.7.4: probe-based introspection — remote shares classify, gparted bug fixed, Storage pagination

Introspection (the headline): detection is now probe-based. Instead of
grepping raw sectors for filename strings, we walk the ISO9660
directory tree and check whether the well-known boot files actually
exist — and the same probes run over NFS READ3 / SFTP seek-reads, so
share-hosted ISOs finally classify instead of registering as Unknown.

iso-store:
- New iso_fs module: the read-only ISO9660 walker (generalized from
  http-api) over an IsoReadAt trait — local files, NFS, SFTP, and the
  in-memory test images all share it. Iterative walk, 4 MiB directory
  cap, strict-mastering trailing-dot normalization (VMLINUZ.;1 now
  matches /vmlinuz), CachingReadAt collapses repeated directory reads
  during the probe pass (~60 → ~6 round-trips per remote ISO).
- introspect.rs rewritten (INTROSPECT_REV 2): PVD label → El Torito →
  /sources/boot.wim probe → verified Linux kernel+initrd probe table →
  local-only 16 MiB UDF-Windows scan → filename-token fallback.
  * Fixes the false-Windows bug: any Linux ISO shipping GRUB/syslinux
    chainload modules contains the literal "bootmgr", so gparted-live
    classified as WindowsPe. Linux probes now run first; the byte scan
    only sees ISOs nothing else claimed. Local ISOs re-probe once on
    startup via the rev bump — no re-upload.
  * Kernel entries are emitted only when kernel+initrd verifiably
    exist (no more guessed paths that 404 at boot). Debian-live /
    d-i netinst / CoreOS shapes classify for the UI but keep their
    working sanboot entries (their boot protocols need args we don't
    render yet; CoreOS additionally needs its embedded ignition).
  * Label + filename vocab extended: rhcos/coreos/openshift/okd,
    gparted/clonezilla/kali/tails, almalinux/rocky, sles, manjaro.
- NFS + SFTP managers: per-ISO IsoReadAt readers (READ3-at-offset with
  short-read looping / seek+read_exact), background introspection pass
  after each scan — entries register instantly with a provisional
  filename-based report (rev 0, optimistic sanboot preserved) and
  upgrade in place as probes land (30s/ISO timeout, failures keep the
  provisional). locate_in_iso() exposes the walker to the HTTP layer.
- remote_cache: introspection results persisted per protocol keyed
  share/path@size and gated on INTROSPECT_REV — container restarts
  re-probe only new/replaced ISOs; upgrades re-probe exactly once.
- SMB: smbclient can't seek, so SMB ISOs get the filename-token family
  (rev stays 0 → sanboot entry + "awaiting introspection" label).
- IsoStore::update_external_introspection swaps in completed reports
  and regenerates boot entries, preserving category/password.

http-api:
- /iso/{id}/{*path} now serves files from inside NFS/SFTP-hosted ISOs
  (remote ISO9660 lookup + ranged share stream) — verified kernel
  entries on remote Linux ISOs are actually bootable, end to end.
- iso_fs.rs deleted in favor of the shared iso-store module.
- full_flow fixtures build real directory trees via the shared
  test-image builder (new iso-store feature) — a label-only blob no
  longer earns a kernel entry, by design.

webui:
- Available images: paged 5 per page with a quiet footer pager
  (Showing X–Y of N · Prev/Next), filter-then-paginate, page resets on
  search input. Fifty images is five clean pages, not a scroll wall.
- Hosts/Queue profile: "Unattended file (in Storage → Advanced)" so
  the picker says where the files live.
- Row badge keys on introspect_rev: probed remote ISOs read like local
  ones; un-probed say "awaiting introspection".

Validation: clippy pedantic clean, fmt clean, 316 workspace tests
green (+17: walker, probe shapes incl. gparted regression + CoreOS,
filename table, cache round-trips), webui syntax-checked.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Miles Ward
2026-06-12 15:21:14 -04:00
co-authored by Claude Opus 4.8
parent 934cfbab46
commit 6524aa4118
18 changed files with 1810 additions and 401 deletions
+22
View File
@@ -415,6 +415,28 @@ impl IsoStore {
self.inner.write().isos.insert(id, meta);
}
/// v0.7.4: swap in a completed introspection for an external ISO and
/// regenerate its boot entries. Used by the NFS/SFTP managers'
/// background probe pass — the scan registers a provisional
/// (filename-only) report immediately so startup and share-add stay
/// fast, then this upgrades each entry as its probe finishes.
/// Operator-set fields (category, password) are preserved; returns
/// `false` when the id is gone (share removed or re-scanned away
/// mid-probe), which callers treat as a benign no-op.
pub fn update_external_introspection(
&self,
id: &str,
introspection: IntrospectionReport,
) -> bool {
let mut g = self.inner.write();
let Some(m) = g.isos.get_mut(id) else {
return false;
};
m.boot_entries = generate_boot_entries(&m.id, &m.filename, &introspection);
m.introspection = introspection;
true
}
/// Drop every entry that belongs to `share_id`. Used by the SMB
/// and NFS share managers when an operator removes a share, or
/// before re-scanning to clean out stale entries. The same id