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:
2026-10-18 19:47:15 +02:00
parent 8561dc8a5c
commit 0963bae3c3
3 changed files with 60 additions and 20 deletions
+36 -4
View File
@@ -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)")
}