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:
+18
-16
@@ -419,7 +419,7 @@ func (h *SudoHostOps) InspectDevice(ctx context.Context, device string) (DeviceP
|
||||
// any TYPE/PTTYPE/USAGE line is positive data-bearing evidence. Its exit code is NOT relied
|
||||
// on (blkid exits 2 on a blank device) — output presence is what matters. A broken/empty
|
||||
// blkid simply adds no positive evidence; lsblk (below) is the read-success authority.
|
||||
bout, _, _ := h.runner.Run(ctx, h.bins.Blkid, "-p", "-o", "export", device)
|
||||
bout, _, berr := h.runner.Run(ctx, h.bins.Blkid, "-p", "-o", "export", device)
|
||||
blk := parseBlkidExport(bout)
|
||||
for k, v := range blk {
|
||||
switch k {
|
||||
@@ -440,11 +440,11 @@ func (h *SudoHostOps) InspectDevice(ctx context.Context, device string) (DeviceP
|
||||
// lsblk -J is the READ-SUCCESS authority + the partition/mount view. It exits 0 on any valid
|
||||
// device (blank or not), so a clean parse means the agent reliably read the device. If lsblk
|
||||
// fails, Probed stays false → DataBearing()=true (fail-safe — never call a device blank on a
|
||||
// failed read).
|
||||
// failed read). An exit 0 whose output does not parse into a device is NOT a read either (R-1093
|
||||
// review): it stays unprobed, never "blank".
|
||||
lout, _, lerr := h.runner.Run(ctx, h.bins.Lsblk, "-J", "-o", "NAME,FSTYPE,PTTYPE,MOUNTPOINT", device)
|
||||
if lerr == nil {
|
||||
if hasChildren, mounted, fstype, pttype, parsed := parseLsblkDevice(lout); lerr == nil && parsed {
|
||||
probe.Probed = true
|
||||
hasChildren, mounted, fstype, pttype := parseLsblkDevice(lout)
|
||||
if hasChildren {
|
||||
probe.HasPartitions = true
|
||||
}
|
||||
@@ -457,7 +457,7 @@ func (h *SudoHostOps) InspectDevice(ctx context.Context, device string) (DeviceP
|
||||
probe.FSType = fstype
|
||||
}
|
||||
}
|
||||
if pttype != "" && !inheritedPartitionTable(blk, pttype) {
|
||||
if pttype != "" && !inheritedPartitionTable(blk, berr, pttype) {
|
||||
probe.HasPartitionTable = true
|
||||
}
|
||||
}
|
||||
@@ -470,13 +470,14 @@ func (h *SudoHostOps) InspectDevice(ctx context.Context, device string) (DeviceP
|
||||
//
|
||||
// The partition's OWN signatures come from `blkid -p` on the partition itself (above): a nested table
|
||||
// inside it is its PTTYPE, a filesystem is its TYPE. So lsblk's value is set aside ONLY when blkid
|
||||
// positively proved two things — that it read the device (it printed output), and that the device is a
|
||||
// partition of a table of exactly that type (PART_ENTRY_NUMBER + PART_ENTRY_SCHEME == lsblk's PTTYPE).
|
||||
// Anything else keeps the old, safe reading: a blkid that failed or printed nothing, a whole disk, or a
|
||||
// scheme that differs from lsblk's (then lsblk sees a table blkid did not attribute to the parent).
|
||||
// Pinned by TestInspect_R1093_* (hostops_disk_test.go).
|
||||
func inheritedPartitionTable(blkid map[string]string, lsblkPTType string) bool {
|
||||
if blkid["PART_ENTRY_NUMBER"] == "" {
|
||||
// positively proved two things — that it completed its probe of the device (exit 0: PART_ENTRY_* alone
|
||||
// comes from the PARENT's table, so output without a clean exit does not prove the partition's contents
|
||||
// were read), and that the device is a partition of a table of exactly that type (PART_ENTRY_NUMBER +
|
||||
// PART_ENTRY_SCHEME == lsblk's PTTYPE). Anything else keeps the old, safe reading: a blkid that failed,
|
||||
// exited non-zero or printed nothing, a whole disk, or a scheme that differs from lsblk's (then lsblk sees
|
||||
// a table blkid did not attribute to the parent). Pinned by TestInspect_R1093_* (hostops_disk_test.go).
|
||||
func inheritedPartitionTable(blkid map[string]string, blkidErr error, lsblkPTType string) bool {
|
||||
if blkidErr != nil || blkid["PART_ENTRY_NUMBER"] == "" {
|
||||
return false
|
||||
}
|
||||
scheme := blkid["PART_ENTRY_SCHEME"]
|
||||
@@ -561,13 +562,14 @@ type lsblkDevice struct {
|
||||
}
|
||||
|
||||
// parseLsblkDevice parses `lsblk -J -o NAME,FSTYPE,PTTYPE,MOUNTPOINT <device>` for the top device:
|
||||
// whether it has child partitions, is mounted (itself or any child), and its fstype/pttype.
|
||||
func parseLsblkDevice(out []byte) (hasChildren, mounted bool, fstype, pttype string) {
|
||||
// whether it has child partitions, is mounted (itself or any child), and its fstype/pttype. parsed=false
|
||||
// when the output is not a device listing — the caller must then treat the device as unprobed.
|
||||
func parseLsblkDevice(out []byte) (hasChildren, mounted bool, fstype, pttype string, parsed bool) {
|
||||
var doc struct {
|
||||
BlockDevices []lsblkDevice `json:"blockdevices"`
|
||||
}
|
||||
if json.Unmarshal(out, &doc) != nil || len(doc.BlockDevices) == 0 {
|
||||
return false, false, "", ""
|
||||
return false, false, "", "", false
|
||||
}
|
||||
d := doc.BlockDevices[0]
|
||||
fstype, pttype = d.FSType, d.PTType
|
||||
@@ -578,7 +580,7 @@ func parseLsblkDevice(out []byte) (hasChildren, mounted bool, fstype, pttype str
|
||||
mounted = true
|
||||
}
|
||||
}
|
||||
return hasChildren, mounted, fstype, pttype
|
||||
return hasChildren, mounted, fstype, pttype, true
|
||||
}
|
||||
|
||||
// NoopHostOps is the safe fallback when the privileged surface is unavailable or declined
|
||||
|
||||
Reference in New Issue
Block a user