carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package moves
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/carlosframework/platform/internal/hostclaim"
9 "github.com/superfly/ltx"
10)
11
12// ReplicaSource is where a move's source copy reads from (§7.3, round-3
13// finding 5): the claim holder's location and generation, never the
14// legacy HibernatePrefix guess. keep admits, relative to prefix, only the
15// generation's own LTX files, `<level>/<min>-<max>.ltx` with a level from
16// 0000 to 0009, so the staging lane holds the generation's files flat
17// under replica/<level>/ and nothing else: no other generation (g-*/), no
18// child incarnation, no generation.json, lock.json or closed/.
19//
20// The holder's location never carries a trailing slash (Claim.Validate
21// accepts only instances/<host> or instances/<host>/<incarnation>), so the
22// separator is added here, as genselect.requestLocation does (round-4
23// finding 4). A generation of "" or hostclaim.RootGeneration is the root
24// layout: the level directories sit directly under the location.
25//
26// The generation prefix rule has twins: hostbox.GenerationPrefix (which
27// refuses the root generation and a holder with no session, so a source
28// copy cannot use it) and genselect's unexported prefix and coordinates.
29// The three change together.
30func ReplicaSource(h hostclaim.Holder) (prefix string, keep func(rel string) bool) {
31 base := strings.TrimSuffix(h.Location, "/") + "/"
32 prefix = base
33 if g := h.Generation; g != "" && g != hostclaim.RootGeneration {
34 prefix = base + "g-" + g + "/"
35 }
36 return prefix, levelFile
37}
38
39// levelFile is ReplicaSource's keep: exactly two path parts, a level
40// directory of four lowercase hex digits from 0000 to 0009, and an LTX
41// file name in its canonical form with 0 < min <= max.
42func levelFile(rel string) bool {
43 parts := strings.Split(rel, "/")
44 if len(parts) != 2 || len(parts[0]) != 4 {
45 return false
46 }
47 level, err := strconv.ParseUint(parts[0], 16, 16)
48 if err != nil || level > 9 || fmt.Sprintf("%04x", level) != parts[0] {
49 return false
50 }
51 min, max, err := ltx.ParseFilename(parts[1])
52 return err == nil && min != 0 && min <= max && ltx.FormatFilename(min, max) == parts[1]
53}
54