diff --git a/drivers/cube/cubelinux_store.rs b/drivers/cube/cubelinux_store.rs index 88507b528..b5fea2867 100644 --- a/drivers/cube/cubelinux_store.rs +++ b/drivers/cube/cubelinux_store.rs @@ -1047,7 +1047,17 @@ fn build_merged(image: &[u8], log: &[u8], header: &Header) -> Result<(Merged, u6 /// to it. Returns `Err("no-log")` when there is none, so the caller can fall back to reading /// the image by itself. fn merged_digest(image: &[u8], log: &[u8], header: &Header) -> Result { - if log.len() < WAL_HEADER_LEN || &log[0..4] != WAL_MAGIC { + // An addressed image is a complete store on its own: its index holds every record and says + // where each one is, so a log is an *addition* rather than a requirement. A packed image is + // not — without a log there is nothing this path adds over the image's own order, which is why + // it answers "no log" and lets the caller walk the records it can see. + // + // Requiring a log here regardless of layout is what made a bare v3/v4 image read as a *packed* + // one: the caller fell through to a walk that starts at the v2 header length and reads index + // entries as record frames. It went unnoticed while every userspace-written image was v2, which + // is exactly the kind of bug that a migration finds and a unit test does not. + let addressed = header.version == VERSION_V3 || header.version == VERSION_V4; + if !addressed && (log.len() < WAL_HEADER_LEN || &log[0..4] != WAL_MAGIC) { return Err("no-log"); } let (mut merged, applied) = build_merged(image, log, header)?;