package host import ( "context" "errors" "os" "path/filepath" "time" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" "github.com/carlosframework/platform/internal/seeds" ) func rootSeedsFloorDir(opt string) string { return filepath.Join(opt, "system", "guard", "seeds") } func (b rootFloorBinary) forSeeds() rootFloorBinary { b.Protocol = b.SeedsProtocol; return b } func readRootSeedsBinary(opt, path string) (rootFloorBinary, error) { b, err := readRootFloorBinary(opt, path) return b.forSeeds(), err } func readEffectiveSeedsFloor(source rootFloorIdentitySource, opt, dd string) (*moves.Floor, error) { id, required, err := source.read() if err != nil { return nil, err } if required { return readRootSeedsBound(opt, id) } f, err := readLocalSeedsFloor(dd) if f == nil { return nil, err } return &moves.Floor{Min: f.Min, MinRelease: f.MinRelease}, err } func refreshRootSeedsFloor(ctx context.Context, store release.Store, opt, dd string, id rootFloorIdentity) error { f, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true) if err != nil { return err } defer f.close() raw, _, rerr := store.Get(ctx, seeds.FloorKey) var bound *moves.Floor if rerr == nil { // Unsigned legacy publication cannot arm a root seeds floor. if verified, err := verifyRootSeedsPublication(raw, id); err == nil { if err := acceptRootSeedsPublication(ctx, f, raw); err != nil { return err } bound = verified } } published, err := readRootSeedsBound(opt, id) if err != nil { return err } if published == nil { return nil } legacy, lerr := rootProtocolLegacy(dd, seeds.FloorFile, seeds.FloorMinReleaseFile) if errors.Is(rerr, os.ErrNotExist) { rerr = nil } unlock, err := f.private.Lock(ctx, "floor.lock") if err != nil { return err } defer unlock() capped, err := f.capLegacy(legacy) if err != nil { return err } local := floorMaximum(published, bound) for _, fl := range capped { local = floorMaximum(local, fl) } generation := uint64(1) if r, e := f.record(rootFloorAccepted); e == nil { generation = r.Generation + 1 } r := rootFloorRecord{Schema: 1, Identity: id, Generation: generation, Floor: local} failure := errors.Join(rerr, lerr) return errors.Join(failure, f.persist(r, local, failure == nil)) } func judgeRootSeedsPrev(ctx context.Context, opt string, id rootFloorIdentity) error { bound, err := readRootSeedsBound(opt, id) if err != nil || bound == nil { return err } f, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true) if err != nil { return err } defer f.close() return (rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootSeedsBinary(opt, path) }, idle: rootFloorDowngradeIdle}).judge(ctx) } func rootRefreshAndJudgeSeedsPrev(id rootFloorIdentity) error { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() opt, dd := release.OptDir(), dataDir() return withPrevLock(opt, func() error { store, err := openDeployStore(ctx) if err != nil { return err } if err := refreshRootSeedsFloor(ctx, store, opt, dd, id); err != nil { return err } return judgeRootSeedsPrev(ctx, opt, id) }) } func (a *systemApplier) seedsActivationRefusal(version string) error { floor, err := readEffectiveSeedsFloor(a.rootMoves, a.optDir, a.dataDir) if err != nil { return refuse("seeds protocol floor: the floor on this box cannot be read: %v", err) } floor = a.activationFloor(floor, true) if floor == nil { return nil } if ok, _ := seeds.ReleaseAtOrAbove(version, floor.MinRelease); !ok { return refuse("seeds protocol floor: %s is below %s", version, floor.MinRelease) } return nil } func rootSeedsAllowsBinary(opt string, id rootFloorIdentity, path string) error { floor, err := readRootSeedsBound(opt, id) if err != nil { return err } if floor == nil { return nil } b, err := readRootSeedsBinary(opt, path) if err != nil { return err } if !b.supports(floor) && rootFloorAvailable(opt, floor, true, path) { return errRootFloorHistory } return nil } func withRootSeedsFields(f edgesFile, opt string) edgesFile { f.SeedsProtocol = seeds.ProtocolVersion f.SeedsFloorState = "quarantined" f.SeedsFloorMinRelease, f.PrevSeedsAllowedFor = "", "" p, err := readRootProtocolProjection(rootSeedsFloorDir(opt)) if err != nil { return f } if p.Identity.Deployment != os.Getenv("CARLOS_DEPLOYMENT_BUCKET") || p.Identity.Box != f.Box { return f } public, err := rootstate.Open(rootSeedsFloorDir(opt), false, true) if err != nil { return f } defer public.Close() raw, err := public.Read("publication.json") if err != nil { return f } if _, err := verifyRootSeedsPublication(raw, p.Identity); err != nil { return f } ad, err := rootFloorAdvertisementFor(public, p) if err != nil || rootstate.Sync(public) != nil { return f } f.SeedsFloorState, f.SeedsFloorGeneration = ad.State, ad.Generation f.SeedsFloorMinRelease, f.PrevSeedsAllowedFor = ad.MinRelease, ad.PrevAllowedFor return f } func (a *systemApplier) rootProtocolAllowsBinary(path string) error { id, required, err := a.rootMoves.read() if err != nil || !required { return err } b, err := readRootFloorBinary(a.optDir, path) if err != nil { return err } floor, err := readRootProtocolBound(rootFloorDir(a.optDir), id) if err != nil { return err } if !b.supports(floor) && rootFloorAvailable(a.optDir, floor, false, path) { return errRootFloorHistory } return rootSeedsAllowsBinary(a.optDir, id, path) }