package host import ( "context" "errors" "github.com/carlosframework/platform/internal/moves" "os" "path/filepath" "github.com/carlosframework/platform/internal/rootparticipation" "github.com/carlosframework/platform/internal/rootstate" ) func rootFloorPrevVerdict(j rootFloorJudge) error { p, err := j.floors.projection() if err != nil { if j.floors.private != nil { if _, e := j.floors.record(rootFloorAccepted); e == nil { return err } } bound, e := j.floors.localBound() if e != nil { return e } b, e := j.binary(j.prev) if e != nil { return e } if !j.permits(b, bound) { return errRootFloorHistory } return nil } raw, err := j.floors.public.Read("prev.json") if err != nil { return err } var v rootFloorVerdict if len(raw) > movesFloorMaxBytes || rootparticipation.Decode(raw, &v) != nil || v.Identity != p.Identity || v.Generation != p.Generation || v.PrevAbsent { return errRootFloorHistory } binary, err := j.binary(j.prev) if err != nil { return err } if !sameRootFloorImage(binary, v.Prev) || !j.permits(binary, p.Floor) { return errRootFloorHistory } return nil } func (j rootFloorJudge) permits(b rootFloorBinary, bound *moves.Floor) bool { if !b.supports(nil) { return false } if b.supports(bound) { return true } paths := []string{j.current, j.prev} if filepath.IsAbs(j.current) { paths = append(paths, filepath.Join(systemRootDir(filepath.Dir(j.current)), "staging", "carlos")) } for _, path := range paths { if path == "" { continue } other, err := j.binary(path) if err == nil && other.supports(bound) { return false } } return true } // Called under release, previous-binary and floor exclusion. A rejected prev // falls back to the certified current executor, including below a raised bound. func rootFloorBootCandidate(ctx context.Context, j rootFloorJudge, journal bool, reader func(context.Context, string) error, idle func(context.Context) error) (string, error) { bound, err := j.floors.localBound() if err != nil { return "", err } if journal && rootFloorPrevVerdict(j) == nil && idle(ctx) == nil && reader(ctx, j.prev) == nil { return j.prev, nil } current, err := j.binary(j.current) if err != nil { return "", err } if !current.supports(nil) { return "", errRootFloorHistory } if !current.supports(bound) { rootFloorAlert() } return j.current, nil } // The additional argument deliberately fails in an older floor-allows-prev // reader. It is an internal unit protocol, not an operator reset interface. func rootFloorAllowsPrev(opt string) error { if !filepath.IsAbs(opt) || filepath.Clean(opt) != opt { return errFloorGateUsage } id, required, err := rootMovesFloorIdentity() if err != nil || !required { return errors.Join(err, errRootFloorHistory) } return rootFloorAllowsPrevIdentity(opt, id) } func rootFloorAllowsPrevIdentity(opt string, id rootFloorIdentity) error { f, err := openRootMovesFloors(opt, id, true) if err != nil { return err } defer f.close() if err := rootSeedsAllowsBinary(opt, id, systemPrevPath(opt)); err != nil { return err } return rootFloorPrevVerdict(rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootFloorBinary(opt, path) }}) } func withRootFloorGuardCandidate(ctx context.Context, opt string, id rootFloorIdentity, journal bool, effect func(string) error) error { return withClaimRootRelease(ctx, true, func() (rootstate.Store, error) { return rootstate.Open(rootReleasePrivate, true, false) }, func() error { return withPrevLock(opt, func() error { idleErr := rootFloorDowngradeIdle(ctx) f, err := openRootMovesFloors(opt, id, true) if err != nil { return err } defer f.close() unlock, err := f.private.Lock(ctx, "floor.lock") if err != nil { return err } defer unlock() seeds, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true) if err != nil { return err } defer seeds.close() unlockSeeds, err := seeds.private.Lock(ctx, "floor.lock") if err != nil { return err } defer unlockSeeds() j := rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootFloorBinary(opt, path) }} candidate, err := rootFloorBootCandidate(ctx, j, journal, func(ctx context.Context, path string) error { _, err := productionSysOps().run(ctx, systemVersionWait, path, "system", "floor-allows-prev", "--root", opt) return err }, func(context.Context) error { return idleErr }) if err != nil { return err } if candidate != j.current { if err := rootSeedsAllowsBinary(opt, id, candidate); err != nil { candidate = j.current } } if candidate == j.current { b, err := j.binary(candidate) if err != nil { return err } bound, err := readRootSeedsBound(opt, id) if err != nil { return err } movesBound, err := f.localBound() if err != nil { return err } if !b.forSeeds().supports(bound) && b.supports(movesBound) { rootFloorAlert() } } return effect(candidate) }) }) } func rootFloorJournalPresent(opt string) (bool, error) { f, err := rootstate.OpenAuthorityFile(filepath.Join(systemRootDir(opt), "journal.json")) if errors.Is(err, os.ErrNotExist) { return false, nil } if err != nil { return false, err } return true, f.Close() } func sameRootFloorImage(a, b rootFloorBinary) bool { // Each observation reopens a root-owned image through checked ancestry // and verifies its bytes against the signed identity. Device numbers and // inode numbers are not authorization across boots or signed repairs. return a.Digest != "" && a.Digest == b.Digest && a.Version == b.Version && a.Protocol == b.Protocol && a.SeedsProtocol == b.SeedsProtocol }