carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "errors"
6 "github.com/carlosframework/platform/internal/moves"
7 "os"
8 "path/filepath"
9
10 "github.com/carlosframework/platform/internal/rootparticipation"
11 "github.com/carlosframework/platform/internal/rootstate"
12)
13
14func rootFloorPrevVerdict(j rootFloorJudge) error {
15 p, err := j.floors.projection()
16 if err != nil {
17 if j.floors.private != nil {
18 if _, e := j.floors.record(rootFloorAccepted); e == nil {
19 return err
20 }
21 }
22 bound, e := j.floors.localBound()
23 if e != nil {
24 return e
25 }
26 b, e := j.binary(j.prev)
27 if e != nil {
28 return e
29 }
30 if !j.permits(b, bound) {
31 return errRootFloorHistory
32 }
33 return nil
34 }
35 raw, err := j.floors.public.Read("prev.json")
36 if err != nil {
37 return err
38 }
39 var v rootFloorVerdict
40 if len(raw) > movesFloorMaxBytes || rootparticipation.Decode(raw, &v) != nil || v.Identity != p.Identity || v.Generation != p.Generation || v.PrevAbsent {
41 return errRootFloorHistory
42 }
43 binary, err := j.binary(j.prev)
44 if err != nil {
45 return err
46 }
47 if !sameRootFloorImage(binary, v.Prev) || !j.permits(binary, p.Floor) {
48 return errRootFloorHistory
49 }
50 return nil
51}
52
53func (j rootFloorJudge) permits(b rootFloorBinary, bound *moves.Floor) bool {
54 if !b.supports(nil) {
55 return false
56 }
57 if b.supports(bound) {
58 return true
59 }
60 paths := []string{j.current, j.prev}
61 if filepath.IsAbs(j.current) {
62 paths = append(paths, filepath.Join(systemRootDir(filepath.Dir(j.current)), "staging", "carlos"))
63 }
64 for _, path := range paths {
65 if path == "" {
66 continue
67 }
68 other, err := j.binary(path)
69 if err == nil && other.supports(bound) {
70 return false
71 }
72 }
73 return true
74}
75
76// Called under release, previous-binary and floor exclusion. A rejected prev
77// falls back to the certified current executor, including below a raised bound.
78func rootFloorBootCandidate(ctx context.Context, j rootFloorJudge, journal bool, reader func(context.Context, string) error, idle func(context.Context) error) (string, error) {
79 bound, err := j.floors.localBound()
80 if err != nil {
81 return "", err
82 }
83 if journal && rootFloorPrevVerdict(j) == nil && idle(ctx) == nil && reader(ctx, j.prev) == nil {
84 return j.prev, nil
85 }
86 current, err := j.binary(j.current)
87 if err != nil {
88 return "", err
89 }
90 if !current.supports(nil) {
91 return "", errRootFloorHistory
92 }
93 if !current.supports(bound) {
94 rootFloorAlert()
95 }
96 return j.current, nil
97}
98
99// The additional argument deliberately fails in an older floor-allows-prev
100// reader. It is an internal unit protocol, not an operator reset interface.
101func rootFloorAllowsPrev(opt string) error {
102 if !filepath.IsAbs(opt) || filepath.Clean(opt) != opt {
103 return errFloorGateUsage
104 }
105 id, required, err := rootMovesFloorIdentity()
106 if err != nil || !required {
107 return errors.Join(err, errRootFloorHistory)
108 }
109 return rootFloorAllowsPrevIdentity(opt, id)
110}
111func rootFloorAllowsPrevIdentity(opt string, id rootFloorIdentity) error {
112 f, err := openRootMovesFloors(opt, id, true)
113 if err != nil {
114 return err
115 }
116 defer f.close()
117 if err := rootSeedsAllowsBinary(opt, id, systemPrevPath(opt)); err != nil {
118 return err
119 }
120 return rootFloorPrevVerdict(rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootFloorBinary(opt, path) }})
121}
122
123func withRootFloorGuardCandidate(ctx context.Context, opt string, id rootFloorIdentity, journal bool, effect func(string) error) error {
124 return withClaimRootRelease(ctx, true, func() (rootstate.Store, error) { return rootstate.Open(rootReleasePrivate, true, false) }, func() error {
125 return withPrevLock(opt, func() error {
126 idleErr := rootFloorDowngradeIdle(ctx)
127 f, err := openRootMovesFloors(opt, id, true)
128 if err != nil {
129 return err
130 }
131 defer f.close()
132 unlock, err := f.private.Lock(ctx, "floor.lock")
133 if err != nil {
134 return err
135 }
136 defer unlock()
137 seeds, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true)
138 if err != nil {
139 return err
140 }
141 defer seeds.close()
142 unlockSeeds, err := seeds.private.Lock(ctx, "floor.lock")
143 if err != nil {
144 return err
145 }
146 defer unlockSeeds()
147 j := rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootFloorBinary(opt, path) }}
148 candidate, err := rootFloorBootCandidate(ctx, j, journal, func(ctx context.Context, path string) error {
149 _, err := productionSysOps().run(ctx, systemVersionWait, path, "system", "floor-allows-prev", "--root", opt)
150 return err
151 }, func(context.Context) error { return idleErr })
152 if err != nil {
153 return err
154 }
155 if candidate != j.current {
156 if err := rootSeedsAllowsBinary(opt, id, candidate); err != nil {
157 candidate = j.current
158 }
159 }
160 if candidate == j.current {
161 b, err := j.binary(candidate)
162 if err != nil {
163 return err
164 }
165 bound, err := readRootSeedsBound(opt, id)
166 if err != nil {
167 return err
168 }
169 movesBound, err := f.localBound()
170 if err != nil {
171 return err
172 }
173 if !b.forSeeds().supports(bound) && b.supports(movesBound) {
174 rootFloorAlert()
175 }
176 }
177
178 return effect(candidate)
179 })
180 })
181}
182
183func rootFloorJournalPresent(opt string) (bool, error) {
184 f, err := rootstate.OpenAuthorityFile(filepath.Join(systemRootDir(opt), "journal.json"))
185 if errors.Is(err, os.ErrNotExist) {
186 return false, nil
187 }
188 if err != nil {
189 return false, err
190 }
191 return true, f.Close()
192}
193
194func sameRootFloorImage(a, b rootFloorBinary) bool {
195 // Each observation reopens a root-owned image through checked ancestry
196 // and verifies its bytes against the signed identity. Device numbers and
197 // inode numbers are not authorization across boots or signed repairs.
198 return a.Digest != "" && a.Digest == b.Digest && a.Version == b.Version && a.Protocol == b.Protocol && a.SeedsProtocol == b.SeedsProtocol
199}
200