//go:build linux && rootrunner package host import ( "errors" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/rootstate" "golang.org/x/sys/unix" ) // Production filesystem and private state, with fixture retirement authority. // This does not exercise an installed root service or authenticated retirement. func TestRootServingParkDisplacedRoot(t *testing.T) { servingRootRunner(t) for _, fault := range []string{"", "old-record", "lost-rmdir-reply", "submount", "unknown-root-with-event"} { t.Run(fault, func(t *testing.T) { root, err := os.MkdirTemp("/var/lib", "carlos-rootrunner-displaced-") if err != nil { t.Fatal(err) } t.Cleanup(func() { if err := os.RemoveAll(root); err != nil { t.Error(err) } }) data := filepath.Join(root, "data") servingRunnerMount(t, data) state, err := rootstate.Open(filepath.Join(root, "authority"), true, false) if err != nil { t.Fatal(err) } t.Cleanup(func() { if err := state.Close(); err != nil { t.Error(err) } }) r := servingRunnerPark(t, data, state) r.Complete = true // completion is a fixture, not claim-writer evidence if fault == "old-record" { r.Namespace = servingArtifactDirectory{} } fs := newServingParkFS(data, state) ledger := &servingParkLedger{state: state} save := func() error { return ledger.write("fixture.json", r) } if err := save(); err != nil { t.Fatal(err) } path := filepath.Join(data, "instances", ".serving-parked") saved := path + "-saved" if err := os.Rename(path, saved); err != nil { t.Fatal(err) } if err := os.Mkdir(path, 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(path, "unknown"), []byte("retained unknown bytes"), 0600); err != nil { t.Fatal(err) } q, err := fs.recoverNamespace(t.Context(), r.Source.Parent) if err != nil || q.Previous == nil { t.Fatal("quarantine", err) } if fault == "submount" { if err := unix.Mount(saved, saved, "", unix.MS_BIND, ""); err != nil { t.Fatal(err) } t.Cleanup(func() { if err := unix.Unmount(saved, 0); err != nil { t.Error(err) } }) } if fault == "unknown-root-with-event" { // Move the genuine event into an unrecorded root while keeping its // inode. Event identity alone must not grant parent authority. outside := filepath.Join(data, "instances", "container") if err := os.Mkdir(outside, 0700); err != nil { t.Fatal(err) } if err := os.Rename(saved, filepath.Join(outside, "known")); err != nil { t.Fatal(err) } if err := os.Mkdir(saved, 0700); err != nil { t.Fatal(err) } if err := os.Rename(filepath.Join(outside, "known", r.Authorization.ID), filepath.Join(saved, r.Authorization.ID)); err != nil { t.Fatal(err) } } if fault == "submount" || fault == "unknown-root-with-event" { writes := 0 r.Deleting, r.Cause = true, "expiry" if err := fs.Delete(&r, func() error { writes++; return nil }); err == nil || writes != 0 { t.Fatal("unproven root accepted", writes, err) } for suffix, want := range map[string]string{"": "database", "-wal": "WAL", "-shm": "SHM"} { body, err := os.ReadFile(filepath.Join(saved, r.Authorization.ID, r.Authorization.Successor.Host+".db"+suffix)) if err != nil || string(body) != want { t.Fatal("refusal lost bytes", err) } } } else { if err := fs.CheckParked(&r); err != nil { t.Fatal("displaced root", err) } r.Deleting, r.Cause = true, "expiry" if err := save(); err != nil { t.Fatal(err) } if fault == "lost-rmdir-reply" { fs.unlink = func(fd int, name string, flags int) error { if err := unix.Unlinkat(fd, name, flags); err != nil { return err } if flags == unix.AT_REMOVEDIR { return unix.EIO } return nil } if err := fs.Delete(&r, save); !errors.Is(err, unix.EIO) { t.Fatal("missing lost reply", err) } body, err := state.Read("fixture.json") if err != nil || servingParkDecode(body, &r) != nil { t.Fatal("retained progress", err) } fs = newServingParkFS(data, state) } if err := fs.Delete(&r, save); err != nil { t.Fatal("delete recorded event", err) } if !r.EventRemoved || r.Namespace != q.Previous.Root { t.Fatal("missing recorded root or completion") } if err := fs.Delete(&r, save); err != nil { t.Fatal("replay", err) } if _, err := os.Stat(filepath.Join(saved, r.Authorization.ID)); !errors.Is(err, os.ErrNotExist) { t.Fatal("event remains", err) } } body, err := os.ReadFile(filepath.Join(filepath.Dir(path), q.name(), "unknown")) if err != nil || string(body) != "retained unknown bytes" { t.Fatal("quarantine changed", err) } }) } }