An empty partition is blank: lsblk's PTTYPE on a partition is the parent's (R-1093, unreleased)
lsblk prints the disk's table type on every partition, so InspectDevice read an empty partition as "device has a partition table" and the format path asked the typed erase confirmation. lsblk's PTTYPE is now set aside only when blkid -p on the partition itself proved it read the device and that it is a partition of a table of that exact type (PART_ENTRY_NUMBER + matching PART_ENTRY_SCHEME). A filesystem or a nested table in the partition, a silent blkid, a whole disk or a different scheme keep the data-bearing reading. Same allow-listed argv, no sudoers change. Test TestInspect_R1093_PartitionVerdicts, red-proved against the old predicate.
This commit is contained in:
1 parent
27bbdaaa22
commit
f7b7308c4d
3 files changed
+115
-2
No files matched your search
@@ -222,3 +222,82 @@ func TestInspect_ReadsFilesystemUUID(t *testing.T) {
|
||||
t.Fatalf("FSUUID = %q, want the blkid UUID", p.FSUUID)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- R-1093: an EMPTY partition is blank; lsblk's PTTYPE on a partition is the PARENT disk's ----------
|
||||
|
||||
// blkidEmptyGPTPart is `blkid -p -o export /dev/sdb1` for an empty GPT partition: only the partition's
|
||||
// own entry in the parent table (PART_ENTRY_*), no TYPE, no PTTYPE — nothing inside the partition.
|
||||
const blkidEmptyGPTPart = "DEVNAME=/dev/sdb1\nPART_ENTRY_SCHEME=gpt\nPART_ENTRY_UUID=3b0c1a52-55a4-4c4e-9d0b-7a1f0e0f7c11\n" +
|
||||
"PART_ENTRY_TYPE=0fc63daf-8483-4772-8e79-3d69d8477de4\nPART_ENTRY_NUMBER=1\nPART_ENTRY_OFFSET=2048\n" +
|
||||
"PART_ENTRY_SIZE=2097152\nPART_ENTRY_DISK=8:16\n"
|
||||
|
||||
// lsblkPartInheritsGPT is `lsblk -J -o NAME,FSTYPE,PTTYPE,MOUNTPOINT /dev/sdb1`: lsblk prints the DISK's
|
||||
// table type on the partition — the input that made the agent read an empty partition as data-bearing.
|
||||
const lsblkPartInheritsGPT = `{"blockdevices":[{"name":"sdb1","fstype":null,"pttype":"gpt","mountpoint":null}]}`
|
||||
|
||||
// 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
|
||||
// inheritedPartitionTable call) → BOTH empty-partition rows (GPT and dos) FAIL with
|
||||
// `DataBearing=true reason="device has a partition table", want blank`. Restored.
|
||||
func TestInspect_R1093_PartitionVerdicts(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
dev string
|
||||
blkid string
|
||||
blkidErr error
|
||||
lsblk string
|
||||
wantBear bool
|
||||
wantReason string
|
||||
}{
|
||||
{name: "empty GPT partition is blank (the R-1093 case)", dev: "/dev/sdb1",
|
||||
blkid: blkidEmptyGPTPart, blkidErr: errors.New("exit status 2"), 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",
|
||||
lsblk: `{"blockdevices":[{"name":"sdb2","fstype":null,"pttype":"dos","mountpoint":null}]}`,
|
||||
wantBear: false, wantReason: "device is blank"},
|
||||
{name: "partition with a filesystem stays data-bearing", dev: "/dev/sdb1",
|
||||
blkid: "DEVNAME=/dev/sdb1\nUUID=8c1e\nTYPE=ext4\nUSAGE=filesystem\n" + blkidEmptyGPTPart[len("DEVNAME=/dev/sdb1\n"):],
|
||||
lsblk: `{"blockdevices":[{"name":"sdb1","fstype":"ext4","pttype":"gpt","mountpoint":null}]}`,
|
||||
wantBear: true, wantReason: "device has a ext4 filesystem"},
|
||||
{name: "partition holding its OWN (nested) table stays data-bearing", dev: "/dev/sdb1",
|
||||
blkid: "DEVNAME=/dev/sdb1\nPTUUID=0b8d4358\nPTTYPE=dos\n" + blkidEmptyGPTPart[len("DEVNAME=/dev/sdb1\n"):],
|
||||
lsblk: lsblkPartInheritsGPT,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{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"},
|
||||
{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,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{name: "whole disk with a GPT stays data-bearing", dev: "/dev/sdb",
|
||||
blkid: "DEVNAME=/dev/sdb\nPTUUID=a07f88ff\nPTTYPE=gpt\n",
|
||||
lsblk: `{"blockdevices":[{"name":"sdb","fstype":null,"pttype":"gpt","mountpoint":null}]}`,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
{name: "whole disk: blkid silent, lsblk GPT stands", dev: "/dev/sdb",
|
||||
blkid: "", lsblk: `{"blockdevices":[{"name":"sdb","fstype":null,"pttype":"gpt","mountpoint":null}]}`,
|
||||
wantBear: true, wantReason: "device has a partition table"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
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},
|
||||
}
|
||||
p, err := newSudo(r).InspectDevice(context.Background(), c.dev)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !p.Probed {
|
||||
t.Fatal("expected a clean probe (lsblk read cleanly)")
|
||||
}
|
||||
if p.DataBearing() != c.wantBear || p.Reason() != c.wantReason {
|
||||
t.Fatalf("DataBearing=%v reason=%q, want DataBearing=%v reason=%q (probe %+v)",
|
||||
p.DataBearing(), p.Reason(), c.wantBear, c.wantReason, p)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user