carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "context"
5 "fmt"
6 "io/fs"
7 "os"
8 "path/filepath"
9)
10
11// migrationRefusal names the root and path the migration census could not
12// classify, so the operator sees which entry stopped it. It still wraps the
13// census's own error.
14func migrationRefusal(err error, root, rel string) error {
15 return fmt.Errorf("%w: migration census: %s entry %q is not classified", err, root, rel)
16}
17
18// CheckMigrationRoots accounts for every managed entry under offline exclusion.
19// Only independently retained inventory may classify an installation subtree.
20func CheckMigrationRoots(ctx context.Context, roots map[string]string, shared map[string][]string, inventories []StopInventory) error {
21 entries := map[string]map[string]StopInventoryEntry{}
22 for _, in := range inventories {
23 if !in.Complete {
24 return ErrTypedInventory
25 }
26 for _, e := range in.Entries {
27 if e.Kind != "path" {
28 continue
29 }
30 if roots[e.ManagedRoot] == "" || !managedName(e.RelativePath) || e.Materialization != "materialized" || e.Device == 0 || e.Inode == 0 {
31 return ErrTypedInventory
32 }
33 if entries[e.ManagedRoot] == nil {
34 entries[e.ManagedRoot] = map[string]StopInventoryEntry{}
35 }
36 if _, ok := entries[e.ManagedRoot][e.RelativePath]; ok {
37 return ErrTypedInventory
38 }
39 entries[e.ManagedRoot][e.RelativePath] = e
40 }
41 }
42 for _, name := range []string{"data", "opt", "units"} {
43 root := roots[name]
44 if !filepath.IsAbs(root) {
45 return ErrTypedInventory
46 }
47 info, err := os.Lstat(root)
48 if err != nil {
49 return err
50 }
51 if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
52 return ErrTypedInventory
53 }
54 rules, err := censusRules(shared[name], "migration-census.invalid")
55 if err != nil {
56 return err
57 }
58 count := 0
59 if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
60 if walkErr != nil {
61 return walkErr
62 }
63 if err := ctx.Err(); err != nil {
64 return err
65 }
66 count++
67 if count > 65536 {
68 return ErrTypedInventory
69 }
70 rel, err := filepath.Rel(root, path)
71 if err != nil {
72 return err
73 }
74 rel = filepath.ToSlash(rel)
75 if rel == "." {
76 return nil
77 }
78 if e, ok := entries[name][rel]; ok {
79 if err := VerifyInventoryIdentity(root, e); err != nil {
80 return err
81 }
82
83 delete(entries[name], rel)
84 if d.IsDir() {
85 return filepath.SkipDir
86 }
87 return nil
88 }
89 if rule, ok := rules[rel]; ok {
90 err := rule.check(path, d)
91 if err != nil && err != filepath.SkipDir {
92 return migrationRefusal(err, name, rel)
93 }
94 return err
95 }
96 if name == "units" && censusVendorUnitLink(root, path) {
97 return nil
98 }
99 if censusInTree(rules, rel) && (d.IsDir() || d.Type().IsRegular()) {
100 return nil
101 }
102 if d.IsDir() {
103 return nil
104 }
105 return migrationRefusal(ErrTypedInventory, name, rel)
106 }); err != nil {
107 return err
108 }
109 if len(entries[name]) != 0 {
110 return ErrTypedInventory
111 }
112 }
113 return nil
114}
115