The partition probe fails closed on every path (R-1093 review, unreleased)
PART_ENTRY_* comes from the parent's table, so it proves nothing about a partition's contents unless blkid also completed: lsblk's PTTYPE is now set aside only on a blkid exit 0 (exit 2, an I/O error or a missing binary keep "device has a partition table"). An lsblk that exits 0 with output that does not parse into a device, or lists none, leaves the device unprobed instead of blank (a hole that predates R-1093). Six more TestInspect_R1093_PartitionVerdicts rows, each red-proved.
This commit is contained in:
@@ -238,9 +238,12 @@ const lsblkPartInheritsGPT = `{"blockdevices":[{"name":"sdb1","fstype":null,"ptt
|
||||
// TestInspect_R1093_PartitionVerdicts pins the CONSEQUENCE (DataBearing + Reason, what decides whether
|
||||
// the typed erase confirmation is asked) for a partition, in both directions.
|
||||
//
|
||||
// COMPANION RED-PROOF (observed 2026-10-18): restore the pre-fix `if pttype != "" {` (drop the
|
||||
// COMPANION RED-PROOFS (observed 2026-10-18): (1) restore the pre-fix `if pttype != "" {` (drop the
|
||||
// inheritedPartitionTable call) → BOTH empty-partition rows (GPT and dos) FAIL with
|
||||
// `DataBearing=true reason="device has a partition table", want blank`. Restored.
|
||||
// `DataBearing=true reason="device has a partition table", want blank`. (2) drop the `blkidErr != nil`
|
||||
// check → the "exit 2" and "I/O error" rows FAIL with `DataBearing=false reason="device is blank"`.
|
||||
// (3) drop `&& parsed` → both lsblk-exit-0 rows FAIL with `Probed=true … reason="device is blank"`
|
||||
// (the unparsable-output hole predates R-1093; closed here). Restored.
|
||||
func TestInspect_R1093_PartitionVerdicts(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -248,11 +251,13 @@ func TestInspect_R1093_PartitionVerdicts(t *testing.T) {
|
||||
blkid string
|
||||
blkidErr error
|
||||
lsblk string
|
||||
lsblkErr error
|
||||
wantBear bool
|
||||
wantReason string
|
||||
unprobed bool // the verdict must be "could not be reliably inspected"
|
||||
}{
|
||||
{name: "empty GPT partition is blank (the R-1093 case)", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, blkidErr: errors.New("exit status 2"), lsblk: lsblkPartInheritsGPT,
|
||||
blkid: blkidEmptyGPTPart, lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: false, wantReason: "device is blank"},
|
||||
{name: "empty MBR (dos) partition is blank", dev: "/dev/sdb2",
|
||||
blkid: "DEVNAME=/dev/sdb2\nPART_ENTRY_SCHEME=dos\nPART_ENTRY_TYPE=0x83\nPART_ENTRY_NUMBER=2\nPART_ENTRY_DISK=8:16\n",
|
||||
@@ -269,6 +274,26 @@ func TestInspect_R1093_PartitionVerdicts(t *testing.T) {
|
||||
{name: "blkid printed nothing: lsblk's PTTYPE stands (fail-safe)", dev: "/dev/sdb1",
|
||||
blkid: "", blkidErr: errors.New("sudo: a password is required"), lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
// Fail-closed paths (the 2026-10-18 review): PART_ENTRY_* is read from the PARENT's table, so it
|
||||
// proves nothing about the partition's contents unless blkid also completed (exit 0).
|
||||
{name: "blkid exit 2 with only PART_ENTRY output: lsblk stands", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, blkidErr: errors.New("exit status 2"), lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{name: "blkid I/O error after PART_ENTRY output: lsblk stands", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, blkidErr: errors.New("exit status 1"), lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{name: "blkid binary missing: lsblk stands", dev: "/dev/sdb1",
|
||||
blkid: "", blkidErr: errors.New(`exec: "/usr/sbin/blkid": file does not exist`), lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{name: "lsblk exit 0 with unparsable output: unprobed", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, lsblk: `lsblk: something odd`,
|
||||
wantBear: true, unprobed: true},
|
||||
{name: "lsblk exit 0 with no device listed: unprobed", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, lsblk: `{"blockdevices":[]}`,
|
||||
wantBear: true, unprobed: true},
|
||||
{name: "lsblk failed: unprobed", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, lsblk: "", lsblkErr: errors.New("exit status 32"),
|
||||
wantBear: true, unprobed: true},
|
||||
{name: "scheme differs from lsblk's PTTYPE: lsblk stands", dev: "/dev/sdb1",
|
||||
blkid: strings.Replace(blkidEmptyGPTPart, "PART_ENTRY_SCHEME=gpt", "PART_ENTRY_SCHEME=dos", 1),
|
||||
lsblk: lsblkPartInheritsGPT,
|
||||
@@ -285,12 +310,19 @@ func TestInspect_R1093_PartitionVerdicts(t *testing.T) {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
r := &scriptedRunner{
|
||||
outputs: map[string][]byte{"blkid": []byte(c.blkid), "lsblk": []byte(c.lsblk)},
|
||||
errs: map[string]error{"blkid": c.blkidErr},
|
||||
errs: map[string]error{"blkid": c.blkidErr, "lsblk": c.lsblkErr},
|
||||
}
|
||||
p, err := newSudo(r).InspectDevice(context.Background(), c.dev)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.unprobed {
|
||||
if p.Probed || !p.DataBearing() || p.Reason() != "device could not be reliably inspected" {
|
||||
t.Fatalf("Probed=%v DataBearing=%v reason=%q, want unprobed + data-bearing (probe %+v)",
|
||||
p.Probed, p.DataBearing(), p.Reason(), p)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !p.Probed {
|
||||
t.Fatal("expected a clean probe (lsblk read cleanly)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user