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 instancefs
4
5import (
6 "errors"
7 "os"
8 "path/filepath"
9 "syscall"
10 "testing"
11)
12
13// A new directory takes its mode from mkdirat and its group and setgid
14// from its parent, the way the kernel hands them out; the stat returned is
15// what is on disk.
16func TestEnsureDirBeneathCreatesAndInheritsSetgid(t *testing.T) {
17 dir := t.TempDir()
18 if err := os.Chmod(dir, 0o750|os.ModeSetgid); err != nil {
19 t.Fatal(err)
20 }
21 st, err := EnsureDirBeneath(dir, "run", 0o700)
22 if err != nil {
23 t.Fatalf("EnsureDirBeneath: %v", err)
24 }
25 fi, err := os.Lstat(filepath.Join(dir, "run"))
26 if err != nil {
27 t.Fatal(err)
28 }
29 if !fi.IsDir() || fi.Mode().Perm() != 0o700 || fi.Mode()&os.ModeSetgid == 0 {
30 t.Fatalf("mode = %v, want a directory 0700 with setgid inherited", fi.Mode())
31 }
32 if st.Mode != fi.Mode() || st.UID != os.Geteuid() || st.GID != int(fi.Sys().(*syscall.Stat_t).Gid) {
33 t.Fatalf("stat = %+v, disk says %v", st, fi.Mode())
34 }
35}
36
37// An existing directory is reported as it stands and never changed: a
38// chmod by a caller outside the directory's group clears setgid, so the
39// caller verifies instead.
40func TestEnsureDirBeneathLeavesAnExistingDirectoryAlone(t *testing.T) {
41 dir := t.TempDir()
42 d := filepath.Join(dir, "d")
43 if err := os.Mkdir(d, 0o700); err != nil {
44 t.Fatal(err)
45 }
46 if err := os.Chmod(d, 0o777|os.ModeSetgid); err != nil {
47 t.Fatal(err)
48 }
49 st, err := EnsureDirBeneath(dir, "d", 0o700)
50 if err != nil {
51 t.Fatalf("EnsureDirBeneath over an existing directory: %v", err)
52 }
53 want := os.ModeDir | os.ModeSetgid | 0o777
54 if st.Mode != want {
55 t.Errorf("stat mode = %v, want %v", st.Mode, want)
56 }
57 if fi, _ := os.Lstat(d); fi.Mode() != want {
58 t.Errorf("mode changed to %v", fi.Mode())
59 }
60}
61
62func TestEnsureDirBeneathRefusesASymlinkAndLeavesItsTargetAlone(t *testing.T) {
63 dir, elsewhere := t.TempDir(), t.TempDir()
64 if err := os.Chmod(elsewhere, 0o755); err != nil {
65 t.Fatal(err)
66 }
67 if err := os.Symlink(elsewhere, filepath.Join(dir, "d")); err != nil {
68 t.Fatal(err)
69 }
70 if _, err := EnsureDirBeneath(dir, "d", 0o700); !errors.Is(err, ErrSymlink) {
71 t.Fatalf("err = %v, want ErrSymlink", err)
72 }
73 fi, _ := os.Stat(elsewhere)
74 if fi.Mode().Perm() != 0o755 {
75 t.Fatalf("the symlink's target was changed to %v", fi.Mode())
76 }
77}
78
79func TestEnsureDirBeneathRefusesAFileAndAPathName(t *testing.T) {
80 dir := t.TempDir()
81 if err := os.WriteFile(filepath.Join(dir, "f"), nil, 0o600); err != nil {
82 t.Fatal(err)
83 }
84 if _, err := EnsureDirBeneath(dir, "f", 0o700); err == nil {
85 t.Fatal("a regular file accepted as a directory")
86 }
87 if _, err := EnsureDirBeneath(dir, "a/b", 0o700); !errors.Is(err, ErrEscapes) {
88 t.Fatalf("a multi-component name: err = %v, want ErrEscapes", err)
89 }
90 if _, err := EnsureDirBeneath(dir, "..", 0o700); !errors.Is(err, ErrEscapes) {
91 t.Fatalf("..: err = %v, want ErrEscapes", err)
92 }
93}
94
95func TestEnsureDirBeneathRefusesASymlinkedParent(t *testing.T) {
96 base, real := t.TempDir(), t.TempDir()
97 link := filepath.Join(base, "link")
98 if err := os.Symlink(real, link); err != nil {
99 t.Fatal(err)
100 }
101 if _, err := EnsureDirBeneath(link, "d", 0o700); !errors.Is(err, ErrSymlink) {
102 t.Fatalf("err = %v, want ErrSymlink", err)
103 }
104 if _, err := os.Lstat(filepath.Join(real, "d")); !errors.Is(err, os.ErrNotExist) {
105 t.Fatal("a directory was created through the symlinked parent")
106 }
107}
108
109func TestStatDirReportsModeAndGroupAndRefusesASymlink(t *testing.T) {
110 base := t.TempDir()
111 d := filepath.Join(base, "d")
112 if err := os.Mkdir(d, 0o700); err != nil {
113 t.Fatal(err)
114 }
115 if err := os.Chmod(d, 0o750|os.ModeSetgid); err != nil {
116 t.Fatal(err)
117 }
118 st, err := StatDir(d)
119 if err != nil {
120 t.Fatal(err)
121 }
122 if st.Mode != os.ModeDir|os.ModeSetgid|0o750 || st.GID != os.Getegid() {
123 t.Fatalf("stat = %+v", st)
124 }
125 if err := os.Symlink(d, filepath.Join(base, "link")); err != nil {
126 t.Fatal(err)
127 }
128 if _, err := StatDir(filepath.Join(base, "link")); !errors.Is(err, ErrSymlink) {
129 t.Fatalf("err = %v, want ErrSymlink", err)
130 }
131 if _, err := StatDir(filepath.Join(base, "absent")); !errors.Is(err, os.ErrNotExist) {
132 t.Fatalf("err = %v, want ErrNotExist", err)
133 }
134}
135
136func TestRenameBetweenMovesFilesAndDirectories(t *testing.T) {
137 src, dst := t.TempDir(), t.TempDir()
138 if err := os.WriteFile(filepath.Join(src, "h.db"), []byte("db"), 0o600); err != nil {
139 t.Fatal(err)
140 }
141 if err := os.Mkdir(filepath.Join(src, "shadow"), 0o700); err != nil {
142 t.Fatal(err)
143 }
144 if err := RenameBetween(src, "h.db", dst, "h.db"); err != nil {
145 t.Fatalf("file: %v", err)
146 }
147 if err := RenameBetween(src, "shadow", dst, "shadow"); err != nil {
148 t.Fatalf("directory: %v", err)
149 }
150 if b, err := os.ReadFile(filepath.Join(dst, "h.db")); err != nil || string(b) != "db" {
151 t.Fatalf("moved file: %q %v", b, err)
152 }
153 if fi, err := os.Lstat(filepath.Join(dst, "shadow")); err != nil || !fi.IsDir() {
154 t.Fatalf("moved directory: %v %v", fi, err)
155 }
156}
157
158// A symlink at the source name is moved as the link it is, never followed:
159// rename works on directory entries.
160func TestRenameBetweenMovesASymlinkAsALink(t *testing.T) {
161 src, dst, elsewhere := t.TempDir(), t.TempDir(), t.TempDir()
162 target := filepath.Join(elsewhere, "secret")
163 if err := os.WriteFile(target, []byte("s"), 0o600); err != nil {
164 t.Fatal(err)
165 }
166 if err := os.Symlink(target, filepath.Join(src, "h.env")); err != nil {
167 t.Fatal(err)
168 }
169 if err := RenameBetween(src, "h.env", dst, "h.env"); err != nil {
170 t.Fatal(err)
171 }
172 if fi, err := os.Lstat(filepath.Join(dst, "h.env")); err != nil || fi.Mode()&os.ModeSymlink == 0 {
173 t.Fatalf("destination is not the link: %v %v", fi, err)
174 }
175 if _, err := os.Lstat(target); err != nil {
176 t.Fatalf("the link's target moved: %v", err)
177 }
178}
179
180func TestRenameBetweenRefusesASymlinkedDirectoryOnEitherSide(t *testing.T) {
181 base, real := t.TempDir(), t.TempDir()
182 link := filepath.Join(base, "link")
183 if err := os.Symlink(real, link); err != nil {
184 t.Fatal(err)
185 }
186 plain := t.TempDir()
187 if err := os.WriteFile(filepath.Join(plain, "h.db"), []byte("db"), 0o600); err != nil {
188 t.Fatal(err)
189 }
190 if err := RenameBetween(plain, "h.db", link, "h.db"); !errors.Is(err, ErrSymlink) {
191 t.Fatalf("symlinked destination: err = %v, want ErrSymlink", err)
192 }
193 if _, err := os.Lstat(filepath.Join(real, "h.db")); !errors.Is(err, os.ErrNotExist) {
194 t.Fatal("the file landed through the symlinked destination")
195 }
196 if err := os.WriteFile(filepath.Join(real, "x"), nil, 0o600); err != nil {
197 t.Fatal(err)
198 }
199 if err := RenameBetween(link, "x", plain, "x"); !errors.Is(err, ErrSymlink) {
200 t.Fatalf("symlinked source: err = %v, want ErrSymlink", err)
201 }
202}
203
204func TestRenameBetweenRefusesPathNames(t *testing.T) {
205 a, b := t.TempDir(), t.TempDir()
206 for _, bad := range [][2]string{{"x/y", "y"}, {"y", "../y"}, {"", "y"}, {"y", "."}} {
207 if err := RenameBetween(a, bad[0], b, bad[1]); !errors.Is(err, ErrEscapes) {
208 t.Errorf("%q -> %q: err = %v, want ErrEscapes", bad[0], bad[1], err)
209 }
210 }
211}
212
213func TestRemoveBeneathRemovesTheEntryNotItsTarget(t *testing.T) {
214 dir, elsewhere := t.TempDir(), t.TempDir()
215 target := filepath.Join(elsewhere, "keep")
216 if err := os.WriteFile(target, []byte("k"), 0o600); err != nil {
217 t.Fatal(err)
218 }
219 if err := os.Symlink(target, filepath.Join(dir, "link")); err != nil {
220 t.Fatal(err)
221 }
222 if err := RemoveBeneath(dir, "link"); err != nil {
223 t.Fatal(err)
224 }
225 if _, err := os.Lstat(target); err != nil {
226 t.Fatalf("the link's target was removed: %v", err)
227 }
228 if err := RemoveBeneath(dir, "link"); !errors.Is(err, os.ErrNotExist) {
229 t.Fatalf("second remove: err = %v, want ErrNotExist", err)
230 }
231}
232
233func TestRemoveAllBeneathRemovesATree(t *testing.T) {
234 dir := t.TempDir()
235 for _, p := range []string{"run/a", "run/sub/b", "run/sub/deeper/c"} {
236 full := filepath.Join(dir, p)
237 if err := os.MkdirAll(filepath.Dir(full), 0o700); err != nil {
238 t.Fatal(err)
239 }
240 if err := os.WriteFile(full, []byte("x"), 0o600); err != nil {
241 t.Fatal(err)
242 }
243 }
244 if err := RemoveAllBeneath(dir, "run"); err != nil {
245 t.Fatal(err)
246 }
247 if _, err := os.Lstat(filepath.Join(dir, "run")); !os.IsNotExist(err) {
248 t.Fatalf("run survived: %v", err)
249 }
250 if err := RemoveAllBeneath(dir, "run"); err != nil {
251 t.Fatalf("a missing name must be nil: %v", err)
252 }
253}
254
255func TestRemoveAllBeneathNeverFollowsASymlink(t *testing.T) {
256 dir, outside := t.TempDir(), t.TempDir()
257 keep := filepath.Join(outside, "keep")
258 if err := os.WriteFile(keep, []byte("x"), 0o600); err != nil {
259 t.Fatal(err)
260 }
261 if err := os.MkdirAll(filepath.Join(dir, "run"), 0o700); err != nil {
262 t.Fatal(err)
263 }
264 if err := os.Symlink(outside, filepath.Join(dir, "run", "link")); err != nil {
265 t.Fatal(err)
266 }
267 if err := os.Symlink(outside, filepath.Join(dir, "top")); err != nil {
268 t.Fatal(err)
269 }
270 if err := RemoveAllBeneath(dir, "run"); err != nil {
271 t.Fatal(err)
272 }
273 if err := RemoveAllBeneath(dir, "top"); err != nil {
274 t.Fatal(err)
275 }
276 if _, err := os.Stat(keep); err != nil {
277 t.Fatalf("a symlink's target was removed: %v", err)
278 }
279 if _, err := os.Lstat(filepath.Join(dir, "top")); !os.IsNotExist(err) {
280 t.Fatalf("the top-level symlink itself survived: %v", err)
281 }
282}
283
284func TestRemoveAllBeneathRefusesPathsAndSymlinkedParents(t *testing.T) {
285 dir, outside := t.TempDir(), t.TempDir()
286 if err := RemoveAllBeneath(dir, "a/b"); !errors.Is(err, ErrEscapes) {
287 t.Errorf("a/b: %v, want ErrEscapes", err)
288 }
289 link := filepath.Join(dir, "parent")
290 if err := os.Symlink(outside, link); err != nil {
291 t.Fatal(err)
292 }
293 if err := os.Mkdir(filepath.Join(outside, "run"), 0o700); err != nil {
294 t.Fatal(err)
295 }
296 if err := RemoveAllBeneath(link, "run"); !errors.Is(err, ErrSymlink) {
297 t.Errorf("symlinked parent: %v, want ErrSymlink", err)
298 }
299 if _, err := os.Stat(filepath.Join(outside, "run")); err != nil {
300 t.Errorf("removed through a symlinked parent: %v", err)
301 }
302}
303