//go:build linux package host import ( "errors" "os" "path/filepath" "strconv" "testing" ) func rev3HoldTarget(t *testing.T, fx *writersKernelFixture, pid, fd int, target string) { t.Helper() dir := filepath.Join(fx.proc, strconv.Itoa(pid), "task", strconv.Itoa(pid), "fd") if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } if err := os.Symlink(target, filepath.Join(dir, strconv.Itoa(fd))); err != nil { t.Fatal(err) } } func TestRev3NoProcessCoversDeletedDatabaseAndWAL(t *testing.T) { for _, suffix := range []string{"", "-wal"} { t.Run(suffix, func(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) target := fx.db + suffix for _, path := range []string{fx.db, fx.db + "-wal"} { if err := os.Remove(path); err != nil { t.Fatal(err) } } rev3HoldTarget(t, fx, 31, 8, target+" (deleted)") if _, err := fx.noProcess(t, false); err == nil { t.Fatalf("deleted holder passed: %v", err) } }) } } func TestRev3NoProcessCoversNestedInstallationFiles(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) nested := filepath.Join(filepath.Dir(fx.db), "sqlite", "scratch.db") if err := os.MkdirAll(filepath.Dir(nested), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(nested, nil, 0o600); err != nil { t.Fatal(err) } rev3HoldTarget(t, fx, 32, 9, nested) if _, err := fx.noProcess(t, false); !errors.Is(err, ErrClaimStopIdentity) { t.Fatalf("nested holder passed: %v", err) } } func TestRev3NoProcessScansWhenInstallationDirectoryIsEmpty(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) if err := os.Remove(fx.db); err != nil { t.Fatal(err) } if err := os.Remove(fx.db + "-wal"); err != nil { t.Fatal(err) } target := filepath.Join(filepath.Dir(fx.db), "gone.db") rev3HoldTarget(t, fx, 33, 10, target+" (deleted)") if _, err := fx.noProcess(t, false); !errors.Is(err, ErrClaimStopIdentity) { t.Fatalf("empty directory suppressed descriptor scan: %v", err) } } func TestRev3NoProcessPrefixHasPathBoundary(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) sibling := filepath.Dir(fx.db) + "-old" if err := os.Mkdir(sibling, 0o700); err != nil { t.Fatal(err) } target := filepath.Join(sibling, "data.db") if err := os.WriteFile(target, nil, 0o600); err != nil { t.Fatal(err) } rev3HoldTarget(t, fx, 34, 11, target) if _, err := fx.noProcess(t, false); err != nil { t.Fatalf("prefix sibling matched: %v", err) } } func TestRev3NoProcessDescriptorScanErrorsFailClosed(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) dir := filepath.Join(fx.proc, "35", "task", "35", "fd") if err := os.MkdirAll(dir, 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "12"), nil, 0o600); err != nil { t.Fatal(err) } if _, err := fx.noProcess(t, false); err == nil { t.Fatal("unreadable descriptor entry passed") } } func TestRev3NoProcessCoversUnlinkedNestedFile(t *testing.T) { fx := newWritersKernelFixture(t) fx.dropAgentHolds(t) for _, p := range []string{fx.db, fx.db + "-wal"} { if err := os.Remove(p); err != nil { t.Fatal(err) } } nested := filepath.Join(filepath.Dir(fx.db), "sqlite", "gone.db") if err := os.MkdirAll(filepath.Dir(nested), 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(nested, nil, 0600); err != nil { t.Fatal(err) } if err := os.Remove(nested); err != nil { t.Fatal(err) } rev3HoldTarget(t, fx, 36, 13, nested+" (deleted)") if _, err := fx.noProcess(t, false); !errors.Is(err, ErrClaimStopIdentity) { t.Fatalf("unlinked nested holder passed: %v", err) } }