diff --git a/modules/zfs/cmd/zfs-tools/bus.go b/modules/zfs/cmd/zfs-tools/bus.go index 347e0d57e..5aabd9049 100644 --- a/modules/zfs/cmd/zfs-tools/bus.go +++ b/modules/zfs/cmd/zfs-tools/bus.go @@ -162,15 +162,22 @@ func (busLedger) Apply(instance, operation string, body any, call string) error return err } -// absentInstance says whether a ledger get said the instance is not there, which an instance never written is. -func absentInstance(err error) bool { - s := strings.ToLower(err.Error()) - for _, w := range []string{"not found", "no instance", "no such", "does not exist", "unknown instance", "not kept"} { - if strings.Contains(s, w) { - return true - } +// takenOf reads a ledger get's answer: an instance never written answers state null and sequence 0, which is empty (A-40). +func takenOf(raw []byte) (struct { + Taken map[string]LedgerEntry `json:"taken"` +}, error) { + var body struct { + Taken map[string]LedgerEntry `json:"taken"` } - return false + var st buscalls.InstanceState + if err := json.Unmarshal(raw, &st); err != nil { + return body, err + } + if st.Sequence == 0 && (len(st.State) == 0 || string(st.State) == "null") { + return body, nil + } + err := json.Unmarshal(st.State, &body) + return body, err } func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) { @@ -179,19 +186,10 @@ func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) { for _, kind := range keepKinds { raw, err := stdio.BusState(buscalls.LedgerGet, buscalls.InstanceRequest{Ledger: LedgerName, Instance: LedgerInstance(kind, ds)}) if err != nil { - if absentInstance(err) { - continue - } return nil, err } - var st buscalls.InstanceState - if err := json.Unmarshal(raw, &st); err != nil { - return nil, err - } - var body struct { - Taken map[string]LedgerEntry `json:"taken"` - } - if err := json.Unmarshal(st.State, &body); err != nil { + body, err := takenOf(raw) + if err != nil { return nil, err } for _, name := range sortedKeys(body.Taken) { diff --git a/modules/zfs/cmd/zfs-tools/ledger_absent_test.go b/modules/zfs/cmd/zfs-tools/ledger_absent_test.go new file mode 100644 index 000000000..e9681d22e --- /dev/null +++ b/modules/zfs/cmd/zfs-tools/ledger_absent_test.go @@ -0,0 +1,21 @@ +package main + +import "testing" + +func TestTakenOfLedgerAnswers(t *testing.T) { + for _, c := range []struct { + name, raw string + want int + err bool + }{ + {"never written", `{"instance":"x","state":null,"sequence":0}`, 0, false}, + {"written", `{"instance":"x","state":{"taken":{"a":{}}},"sequence":3}`, 1, false}, + {"bad state", `{"instance":"x","state":"oops","sequence":3}`, 0, true}, + {"bad answer", `nope`, 0, true}, + } { + b, err := takenOf([]byte(c.raw)) + if (err != nil) != c.err || len(b.Taken) != c.want { + t.Errorf("%s: %v %d", c.name, err, len(b.Taken)) + } + } +}