v0.5.9: El Torito boot detection + retroactive re-introspect; static SSO login button
Storage / boot detection - Add El Torito boot-catalog detection to ISO introspection. This is the authoritative "can this boot at all?" signal: any ISO with a boot catalog (BSDs, ESXi, firmware tools, custom spins) is bootable via iPXE sanboot; a data/appliance ISO (e.g. a VMware vCenter bundle) has none and is honestly flagged. Replaces the crude ">1.5 GB ⇒ unbootable" size guess. - Re-introspect stale LOCAL ISOs on startup via an introspection-revision gate (INTROSPECT_REV). ISOs uploaded by an older binary carried a frozen family/boot profile — most visibly a Windows 11 ISO tagged Unknown before the UDF/UTF-16 detection landed, which then showed "won't boot" forever. An upgrade now re-probes and fixes them in place; no delete-and-re-upload. - WebUI bootability() keys off family / kernel / el_torito / remote-source instead of the size heuristic; dashboard family counts now bucket Windows / Linux / other honestly instead of lumping everything non-Windows under "Linux". SSO login button - The "Sign in with …" button keyed off the auth-gated /api/sso, which 401s pre-auth — so the button only survived on a stale in-memory config and vanished instance-wide on any fresh login-page load. Ship a minimal, non-sensitive SSO descriptor (enabled + idp_name + idp_logo_url, no metadata/entity-ID) on the public /api/me; the login card reads that. The button is now static whenever SSO is usable. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cb51b8db75
commit
06695c3d77
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum DistroFamily {
|
||||
DebianUbuntu,
|
||||
@@ -22,10 +22,22 @@ pub enum DistroFamily {
|
||||
Arch,
|
||||
Alpine,
|
||||
WindowsPe,
|
||||
#[default]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// Bumped whenever the introspection logic changes in a way that should
|
||||
/// re-classify already-uploaded ISOs. On startup the store re-runs
|
||||
/// `introspect` on any *local* ISO whose persisted report predates this
|
||||
/// revision (see `IsoStore::load_from_disk`), so an upgrade fixes stale
|
||||
/// metadata — e.g. a Windows 11 ISO tagged `Unknown` by an older binary —
|
||||
/// without the operator having to delete and re-upload it.
|
||||
///
|
||||
/// rev 1 (v0.5.9): added El Torito boot-catalog detection + broadened
|
||||
/// Windows (UDF/UTF-16) detection becomes retroactive.
|
||||
pub const INTROSPECT_REV: u32 = 1;
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct IntrospectionReport {
|
||||
pub family: DistroFamily,
|
||||
pub volume_label: Option<String>,
|
||||
@@ -35,17 +47,28 @@ pub struct IntrospectionReport {
|
||||
pub initrd_paths: Vec<String>,
|
||||
/// True if `sources/boot.wim` present — Windows install media.
|
||||
pub has_boot_wim: bool,
|
||||
/// True if the ISO carries an El Torito boot catalog — i.e. it is
|
||||
/// bootable by BIOS/UEFI firmware and therefore by iPXE `sanboot`
|
||||
/// (emulated CD). This is the authoritative "can this boot at all?"
|
||||
/// signal for ISOs we can't classify as Linux or Windows (BSDs, ESXi,
|
||||
/// firmware tools, custom spins). A *data* ISO (e.g. a VMware vCenter
|
||||
/// appliance bundle) has no boot catalog and reports `false`. v0.5.9.
|
||||
#[serde(default)]
|
||||
pub el_torito: bool,
|
||||
/// Revision of the introspection logic that produced this report. Old
|
||||
/// `meta.json` files without the field deserialize as 0, which is
|
||||
/// below [`INTROSPECT_REV`], triggering a one-time re-introspect on
|
||||
/// the next startup. v0.5.9.
|
||||
#[serde(default)]
|
||||
pub introspect_rev: u32,
|
||||
}
|
||||
|
||||
/// Probe an ISO file on disk. Never fails — on unrecoverable IO error we log
|
||||
/// and return an `Unknown` family so the uploader still sees a record.
|
||||
pub fn introspect(path: &Path) -> IntrospectionReport {
|
||||
let mut report = IntrospectionReport {
|
||||
family: DistroFamily::Unknown,
|
||||
volume_label: None,
|
||||
kernel_path: None,
|
||||
initrd_paths: Vec::new(),
|
||||
has_boot_wim: false,
|
||||
introspect_rev: INTROSPECT_REV,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let Ok(mut f) = std::fs::File::open(path) else {
|
||||
@@ -68,6 +91,11 @@ pub fn introspect(path: &Path) -> IntrospectionReport {
|
||||
}
|
||||
}
|
||||
|
||||
// Does the ISO have an El Torito boot catalog? This is what decides
|
||||
// whether an ISO we *can't* otherwise classify is bootable at all —
|
||||
// a bootable ISO sanboots; a data/appliance ISO (no catalog) can't.
|
||||
report.el_torito = detect_el_torito(&mut f);
|
||||
|
||||
// Cheap content scan: read the first ~64 MiB, look for signature filenames.
|
||||
// This is enough to identify `sources/boot.wim` (Windows) and common
|
||||
// kernel/initrd paths for the major Linux distros.
|
||||
@@ -222,6 +250,44 @@ fn filename_looks_windows(path: &Path) -> bool {
|
||||
TOKENS.iter().any(|t| name.contains(t))
|
||||
}
|
||||
|
||||
/// The boot-system identifier string in an El Torito Boot Record Volume
|
||||
/// Descriptor (offset 7, NUL-padded to 32 bytes).
|
||||
const EL_TORITO_ID: &[u8] = b"EL TORITO SPECIFICATION";
|
||||
|
||||
/// Detect an El Torito boot catalog — the marker that an ISO is bootable
|
||||
/// by BIOS/UEFI firmware (and thus by iPXE `sanboot`).
|
||||
///
|
||||
/// The ISO9660 Volume Descriptor Set starts at LBA 16 (offset 0x8000) and
|
||||
/// runs one 2048-byte descriptor per sector until a Set Terminator
|
||||
/// (type 0xFF). A Boot Record descriptor (type 0x00) whose 32-byte boot
|
||||
/// system identifier reads "EL TORITO SPECIFICATION" means the image
|
||||
/// declares an El Torito boot catalog. We only confirm its presence — we
|
||||
/// don't parse the catalog (sanboot/the firmware does that). The walk is
|
||||
/// capped so a malformed/huge image can't spin us. v0.5.9.
|
||||
fn detect_el_torito(f: &mut std::fs::File) -> bool {
|
||||
let mut vd = [0u8; 2048];
|
||||
for lba in 16u64..32 {
|
||||
if f.seek(SeekFrom::Start(lba * 2048)).is_err() || f.read_exact(&mut vd).is_err() {
|
||||
return false;
|
||||
}
|
||||
// Every descriptor in the set carries the "CD001" magic; once it's
|
||||
// missing we've walked off the end of a valid set.
|
||||
if &vd[1..6] != b"CD001" {
|
||||
return false;
|
||||
}
|
||||
match vd[0] {
|
||||
// Boot Record descriptor carrying the El Torito signature.
|
||||
0x00 if vd[7..7 + EL_TORITO_ID.len()] == *EL_TORITO_ID => return true,
|
||||
// Volume Descriptor Set Terminator — nothing bootable found.
|
||||
0xFF => return false,
|
||||
// Any other descriptor (incl. a non-El-Torito boot record) —
|
||||
// keep walking the set.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -260,6 +326,40 @@ mod tests {
|
||||
assert!(!contains_utf16le_ci(b"boot.wim plain ascii", "boot.wim"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn el_torito_boot_catalog_detected() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
// Helper: stamp a 2048-byte descriptor at `lba` with type + magic.
|
||||
let stamp = |img: &mut [u8], lba: usize, ty: u8| {
|
||||
let off = lba * 2048;
|
||||
img[off] = ty;
|
||||
img[off + 1..off + 6].copy_from_slice(b"CD001");
|
||||
};
|
||||
|
||||
// Bootable image: PVD @16, El Torito Boot Record @17, terminator @18.
|
||||
let mut boot = vec![0u8; 2048 * 19];
|
||||
stamp(&mut boot, 16, 0x01);
|
||||
stamp(&mut boot, 17, 0x00);
|
||||
boot[17 * 2048 + 7..17 * 2048 + 7 + EL_TORITO_ID.len()].copy_from_slice(EL_TORITO_ID);
|
||||
stamp(&mut boot, 18, 0xFF);
|
||||
let bp = dir.path().join("boot.iso");
|
||||
std::fs::write(&bp, &boot).unwrap();
|
||||
let mut f = std::fs::File::open(&bp).unwrap();
|
||||
assert!(
|
||||
detect_el_torito(&mut f),
|
||||
"El Torito boot record should match"
|
||||
);
|
||||
|
||||
// Data/appliance image: PVD @16, terminator @17, no boot record.
|
||||
let mut data = vec![0u8; 2048 * 18];
|
||||
stamp(&mut data, 16, 0x01);
|
||||
stamp(&mut data, 17, 0xFF);
|
||||
let dp = dir.path().join("data.iso");
|
||||
std::fs::write(&dp, &data).unwrap();
|
||||
let mut f2 = std::fs::File::open(&dp).unwrap();
|
||||
assert!(!detect_el_torito(&mut f2), "data ISO has no boot catalog");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filename_hint_catches_windows_isos() {
|
||||
use std::path::Path;
|
||||
|
||||
Reference in New Issue
Block a user