package storage import ( "context" "io" "os" "path/filepath" "reflect" "testing" ) // forgetRunner records every privileged call and plays the root half of `felhom-priv-apply unit-forget`: the unit file // disappears from the unit dir, so the CONSEQUENCE (the drive is no longer listed) is observable, not just the calls. type forgetRunner struct { unitDir string calls [][]string } func (r *forgetRunner) RunStdin(ctx context.Context, _ io.Reader, name string, args ...string) ([]byte, []byte, error) { return r.Run(ctx, name, args...) } func (r *forgetRunner) Run(_ context.Context, name string, args ...string) ([]byte, []byte, error) { r.calls = append(r.calls, append([]string{name}, args...)) if len(args) == 2 && args[0] == "unit-forget" { _ = os.Remove(filepath.Join(r.unitDir, args[1])) } return nil, nil, nil } type forgetCase struct { intent DriveIntent present bool // the by-uuid link resolves mounted bool enabled bool // the wants-symlink exists mountErr bool } const forgetUUID = "4ebfd36d-87d3-4b63-839e-a5a710fc799a" func runForget(t *testing.T, c forgetCase, intent IntentReader) (int, *forgetRunner, *RegistryKnownTargets, string) { t.Helper() unitDir := t.TempDir() unit := "mnt-media_a.mount" writeUnit(t, unitDir, unit, renderMountUnit(MountSpec{Name: forgetUUID, UUID: forgetUUID, Where: "/mnt/media_a", FSType: "ext4"})) if c.enabled { if err := os.MkdirAll(filepath.Join(unitDir, "multi-user.target.wants"), 0o755); err != nil { t.Fatal(err) } if err := os.Symlink(filepath.Join(unitDir, unit), filepath.Join(unitDir, "multi-user.target.wants", unit)); err != nil { t.Fatal(err) } } devRoot := t.TempDir() if err := os.MkdirAll(filepath.Join(devRoot, "by-uuid"), 0o755); err != nil { t.Fatal(err) } if c.present { dev := filepath.Join(devRoot, "sdz1") if err := os.WriteFile(dev, nil, 0o600); err != nil { t.Fatal(err) } if err := os.Symlink(dev, filepath.Join(devRoot, "by-uuid", forgetUUID)); err != nil { t.Fatal(err) } } withDevDiskRoot(t, devRoot) hr := &fakeHostReader{} if c.mounted { hr.mounts = []Mount{{MountPoint: "/mnt/media_a", FSType: "ext4"}} } if c.mountErr { hr.mountsErr = os.ErrPermission } rr := &forgetRunner{unitDir: unitDir} ops := NewSudoHostOps(SudoHostOpsConfig{Runner: rr, Bins: Binaries{}.withDefaults(), UnitDir: unitDir, StageDir: t.TempDir(), Host: hr, Logger: quietLogger()}) if intent == nil { intent = &fakeIntent{m: map[string]DriveIntent{"uuid:" + forgetUUID: c.intent}} } n := ops.ForgetRetiredDrives(context.Background(), intent) return n, rr, NewRegistryKnownTargets(unitDir, intent, quietLogger()), unitDir } // The R-1124 case: decommissioned, then unplugged. The unit is disabled and removed through the root checker, systemd // is reloaded, and the CONSEQUENCE — the agent no longer lists the drive — holds. RED-PROOF (P1/redproof-forget.txt): // with ForgetRetiredDrives' body replaced by `return 0` this test fails (the drive is still listed). func TestForgetRetiredDrives_DecommissionedAndGone_UnitRemovedAndNotListed(t *testing.T) { n, rr, known, _ := runForget(t, forgetCase{intent: IntentDecommissioned, enabled: true}, nil) if n != 1 { t.Fatalf("removed = %d, want 1 (calls %v)", n, rr.calls) } pa := Binaries{}.withDefaults() want := [][]string{ {pa.Systemctl, "disable", "--", "mnt-media_a.mount"}, {pa.PrivApply, "unit-forget", "mnt-media_a.mount"}, {pa.Systemctl, "daemon-reload"}, } if !reflect.DeepEqual(rr.calls, want) { t.Fatalf("calls = %v\nwant %v", rr.calls, want) } got, err := known.Known(context.Background()) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatalf("the forgotten drive is still listed: %+v", got) } } // Already disabled (the Tester 1 shape after a manual disable): no disable call, the removal still runs. func TestForgetRetiredDrives_AlreadyDisabled_NoDisableCall(t *testing.T) { n, rr, _, _ := runForget(t, forgetCase{intent: IntentDecommissioned}, nil) pa := Binaries{}.withDefaults() want := [][]string{{pa.PrivApply, "unit-forget", "mnt-media_a.mount"}, {pa.Systemctl, "daemon-reload"}} if n != 1 || !reflect.DeepEqual(rr.calls, want) { t.Fatalf("n=%d calls=%v want %v", n, rr.calls, want) } } // Everything that must KEEP its unit: a decommissioned drive still plugged in (re-enroll is one click — H3), one still // mounted, an enrolled or ejected drive that is merely absent (it comes back), a drive with no recorded intent, and an // unreadable mount table. No privileged call at all, and the drive stays listed where it was listed. // RED-PROOF: with the present-drive check removed, the "present" case calls unit-forget and fails. func TestForgetRetiredDrives_KeepsEveryOtherUnit(t *testing.T) { for name, c := range map[string]forgetCase{ "decommissioned but present": {intent: IntentDecommissioned, present: true, enabled: true}, "decommissioned but mounted": {intent: IntentDecommissioned, mounted: true}, "enrolled and absent": {intent: IntentEnrolled}, "ejected and absent": {intent: IntentEjected}, "no intent": {intent: IntentNew}, "mount table unreadable": {intent: IntentDecommissioned, mountErr: true}, } { t.Run(name, func(t *testing.T) { n, rr, _, unitDir := runForget(t, c, nil) if n != 0 || len(rr.calls) != 0 { t.Fatalf("n=%d calls=%v — this unit must be kept", n, rr.calls) } if _, err := os.Stat(filepath.Join(unitDir, "mnt-media_a.mount")); err != nil { t.Fatalf("unit file gone: %v", err) } }) } } // No intent store on the host (open failure): nothing is known to be retired, so nothing is forgotten. func TestForgetRetiredDrives_NilIntent_ForgetsNothing(t *testing.T) { unitDir := t.TempDir() writeUnit(t, unitDir, "mnt-media_a.mount", renderMountUnit(MountSpec{Name: "x", UUID: forgetUUID, Where: "/mnt/media_a"})) rr := &forgetRunner{unitDir: unitDir} ops := NewSudoHostOps(SudoHostOpsConfig{Runner: rr, UnitDir: unitDir, StageDir: t.TempDir(), Host: &fakeHostReader{}, Logger: quietLogger()}) if n := ops.ForgetRetiredDrives(context.Background(), nil); n != 0 || len(rr.calls) != 0 { t.Fatalf("n=%d calls=%v", n, rr.calls) } }