carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package hostfiles
4
5import (
6 "crypto/sha256"
7 "encoding/hex"
8 "errors"
9 "os"
10 "path/filepath"
11 "syscall"
12 "testing"
13 "time"
14
15 "github.com/carlosframework/platform/internal/instancefs"
16)
17
18// presentBounded runs Present and fails the test if it does not return: a
19// read that opened a planted FIFO without O_NONBLOCK would block for ever.
20func presentBounded(t *testing.T, dir, envPath string, m Manifest, envAbsent bool) (bool, error) {
21 t.Helper()
22 type res struct {
23 ok bool
24 err error
25 }
26 done := make(chan res, 1)
27 go func() { ok, err := Present(dir, envPath, m, envAbsent); done <- res{ok, err} }()
28 select {
29 case r := <-done:
30 return r.ok, r.err
31 case <-time.After(20 * time.Second):
32 t.Fatal("Present did not return: it blocked on what the tenant planted")
33 return false, nil
34 }
35}
36
37func presentEntry(body []byte) Entry {
38 sum := sha256.Sum256(body)
39 return Entry{Object: "o", SHA256: hex.EncodeToString(sum[:]), Size: int64(len(body)), Seq: 1}
40}
41
42// Present reads the files directory and the env file, both in the
43// tenant's instance directory, through main's safe open (instancefs): a
44// symlink at a name or beneath the root, a FIFO, or a file larger than
45// its entry is not present and is never read through or waited on; a
46// symlink at the root itself, or in the path to it, is an error.
47func TestPresentReadsTenantGroundThroughTheSafeOpen(t *testing.T) {
48 body := []byte("delta")
49 env := []byte("CARLOS_ADMIN_TOKEN=t\n")
50 m := Manifest{V: ManifestVersion, Entries: map[string]Entry{"b/c/d.bin": presentEntry(body)}, Platform: map[string]Entry{EnvItem: presentEntry(env)}}
51 // setup lays out a good install: inst/files/b/c/d.bin and inst/shop.example.env.
52 setup := func(t *testing.T) (files, envPath string) {
53 t.Helper()
54 inst := filepath.Join(t.TempDir(), "inst")
55 files = filepath.Join(inst, "files")
56 if err := os.MkdirAll(filepath.Join(files, "b", "c"), 0o700); err != nil {
57 t.Fatal(err)
58 }
59 if err := os.WriteFile(filepath.Join(files, "b", "c", "d.bin"), body, 0o600); err != nil {
60 t.Fatal(err)
61 }
62 envPath = filepath.Join(inst, "shop.example.env")
63 if err := os.WriteFile(envPath, env, 0o600); err != nil {
64 t.Fatal(err)
65 }
66 if ok, err := Present(files, envPath, m, false); err != nil || !ok {
67 t.Fatalf("a good install: %v %v", ok, err)
68 }
69 return files, envPath
70 }
71 // linkTo replaces path with a symlink to a copy of the same bytes or
72 // directory elsewhere, so a read that followed it would be satisfied.
73 linkTo := func(t *testing.T, path string) {
74 t.Helper()
75 moved := filepath.Join(t.TempDir(), "moved")
76 if err := os.Rename(path, moved); err != nil {
77 t.Fatal(err)
78 }
79 if err := os.Symlink(moved, path); err != nil {
80 t.Fatal(err)
81 }
82 }
83 fifo := func(t *testing.T, path string) {
84 t.Helper()
85 if err := os.Remove(path); err != nil {
86 t.Fatal(err)
87 }
88 if err := syscall.Mkfifo(path, 0o600); err != nil {
89 t.Fatal(err)
90 }
91 }
92 grow := func(t *testing.T, path string, keep []byte) {
93 t.Helper()
94 if err := os.WriteFile(path, append(append([]byte(nil), keep...), "more"...), 0o600); err != nil {
95 t.Fatal(err)
96 }
97 }
98 for _, tc := range []struct {
99 name string
100 plant func(t *testing.T, files, envPath string)
101 wantErr error // nil: not present, no error
102 }{
103 {"a symlink at a file", func(t *testing.T, files, _ string) { linkTo(t, filepath.Join(files, "b", "c", "d.bin")) }, nil},
104 {"a symlinked directory beneath the root", func(t *testing.T, files, _ string) { linkTo(t, filepath.Join(files, "b")) }, nil},
105 {"a FIFO at a file", func(t *testing.T, files, _ string) { fifo(t, filepath.Join(files, "b", "c", "d.bin")) }, nil},
106 {"a file larger than its entry", func(t *testing.T, files, _ string) { grow(t, filepath.Join(files, "b", "c", "d.bin"), body) }, nil},
107 {"a symlink at the files root", func(t *testing.T, files, _ string) { linkTo(t, files) }, instancefs.ErrSymlink},
108 {"a symlink at the env file", func(t *testing.T, _, envPath string) { linkTo(t, envPath) }, nil},
109 {"a FIFO at the env file", func(t *testing.T, _, envPath string) { fifo(t, envPath) }, nil},
110 {"an env file larger than its entry", func(t *testing.T, _, envPath string) { grow(t, envPath, env) }, nil},
111 {"a symlinked instance directory", func(t *testing.T, _, envPath string) { linkTo(t, filepath.Dir(envPath)) }, instancefs.ErrSymlink},
112 } {
113 t.Run(tc.name, func(t *testing.T) {
114 files, envPath := setup(t)
115 tc.plant(t, files, envPath)
116 ok, err := presentBounded(t, files, envPath, m, false)
117 if ok || !errors.Is(err, tc.wantErr) || (tc.wantErr == nil && err != nil) {
118 t.Fatalf("Present = %v, %v; want false, %v", ok, err, tc.wantErr)
119 }
120 })
121 }
122}
123
124// With the caller's evidence of no env file, an env file is "none" only
125// when its directory opened with no symlink anywhere and the name is not
126// in it: a FIFO or a link there is something, not none, and is never
127// opened blocking; a linked directory is an error; a missing one is not
128// evidence.
129func TestPresentReadsNoEnvFileOnlyFromADirectoryThatOpened(t *testing.T) {
130 none := Manifest{V: ManifestVersion, Entries: map[string]Entry{}, Platform: map[string]Entry{}}
131 for _, tc := range []struct {
132 name string
133 plant func(t *testing.T, envPath string)
134 want bool
135 wantErr error
136 }{
137 {"none there", func(*testing.T, string) {}, true, nil},
138 {"a FIFO", func(t *testing.T, envPath string) {
139 if err := syscall.Mkfifo(envPath, 0o600); err != nil {
140 t.Fatal(err)
141 }
142 }, false, nil},
143 {"a symlink to nothing", func(t *testing.T, envPath string) {
144 if err := os.Symlink(filepath.Join(t.TempDir(), "nothing"), envPath); err != nil {
145 t.Fatal(err)
146 }
147 }, false, nil},
148 {"a symlinked directory", func(t *testing.T, envPath string) {
149 dir := filepath.Dir(envPath)
150 moved := filepath.Join(t.TempDir(), "moved")
151 if err := os.Rename(dir, moved); err != nil {
152 t.Fatal(err)
153 }
154 if err := os.Symlink(moved, dir); err != nil {
155 t.Fatal(err)
156 }
157 }, false, instancefs.ErrSymlink},
158 {"a missing directory", func(t *testing.T, envPath string) {
159 if err := os.Remove(filepath.Dir(envPath)); err != nil {
160 t.Fatal(err)
161 }
162 }, false, nil},
163 } {
164 t.Run(tc.name, func(t *testing.T) {
165 dir := filepath.Join(t.TempDir(), "inst")
166 if err := os.Mkdir(dir, 0o700); err != nil {
167 t.Fatal(err)
168 }
169 envPath := filepath.Join(dir, "shop.example.env")
170 tc.plant(t, envPath)
171 ok, err := presentBounded(t, t.TempDir(), envPath, none, true)
172 if ok != tc.want || !errors.Is(err, tc.wantErr) || (tc.wantErr == nil && err != nil) {
173 t.Fatalf("Present = %v, %v; want %v, %v", ok, err, tc.want, tc.wantErr)
174 }
175 })
176 }
177}
178
179// A file far larger than its entry is answered from its size, never read
180// whole: a tenant's sparse terabyte costs nothing to make.
181func TestPresentNeverReadsPastAnEntrysSize(t *testing.T) {
182 body := []byte("delta")
183 m := Manifest{V: ManifestVersion, Entries: map[string]Entry{"big": presentEntry(body)}, Platform: map[string]Entry{}}
184 dir := t.TempDir()
185 f, err := os.Create(filepath.Join(dir, "big"))
186 if err != nil {
187 t.Fatal(err)
188 }
189 if _, err := f.Write(body); err != nil {
190 t.Fatal(err)
191 }
192 if err := f.Truncate(1 << 40); err != nil {
193 f.Close()
194 t.Skipf("no sparse terabyte here: %v", err)
195 }
196 f.Close()
197 if ok, err := presentBounded(t, dir, filepath.Join(t.TempDir(), "shop.example.env"), m, true); ok || err != nil {
198 t.Fatalf("Present = %v, %v; want false", ok, err)
199 }
200}
201
202// An empty entry's size and digest are what a FIFO with no writer reads
203// as (zero bytes, then EOF), so only the regular-file check tells the two
204// apart: a FIFO at an empty file's name is not present.
205func TestPresentNeverTakesAFIFOForAnEmptyFile(t *testing.T) {
206 m := Manifest{V: ManifestVersion, Entries: map[string]Entry{"empty": presentEntry(nil)}, Platform: map[string]Entry{}}
207 dir := t.TempDir()
208 if err := os.WriteFile(filepath.Join(dir, "empty"), nil, 0o600); err != nil {
209 t.Fatal(err)
210 }
211 env := filepath.Join(t.TempDir(), "shop.example.env")
212 if ok, err := Present(dir, env, m, true); err != nil || !ok {
213 t.Fatalf("an empty file: %v %v", ok, err)
214 }
215 if err := os.Remove(filepath.Join(dir, "empty")); err != nil {
216 t.Fatal(err)
217 }
218 if err := syscall.Mkfifo(filepath.Join(dir, "empty"), 0o600); err != nil {
219 t.Fatal(err)
220 }
221 if ok, err := presentBounded(t, dir, env, m, true); ok || err != nil {
222 t.Fatalf("a FIFO at an empty file's name: %v, %v; want false", ok, err)
223 }
224}
225