@@ -0,0 +1,390 @@
1// Tests for symbolic links: symlink(), and open() following them.
2// Every check is done by the program itself; each prints OK or FAIL.
3
4#include "kernel/types.h"
5#include "kernel/stat.h"
6#include "kernel/fcntl.h"
7#include "kernel/fs.h"
8#include "kernel/param.h"
9#include "user/user.h"
10
11static int failed;
12
13static void
14result(char *name, int ok) 15{
16 printf("symlinktest: %s: %s\n", name, ok ? "OK" : "FAIL"); 17 if (!ok)
18 failed = 1;
19}
20
21// write s into a new (or truncated) file.
22static int
23mkfile(char *path, char *s)
24{
26 if (fd < 0)
27 return -1;
30 return n == strlen(s) ? 0 : -1; 31}
32
33// does opening path (with flags) give exactly the bytes s?
34static int
35hasdata(char *path, int flags, char *s) 36{
38 int fd, n;
39
41 return 0;
44 if (n < 0)
45 return 0;
48}
49
50// fstat of path opened with flags; -1 if it cannot be opened.
51static int
52statof(char *path, int flags, struct stat *st) 53{
54 int fd, r;
55
57 return -1;
60 return r;
61}
62
63static void
64cleanup(void)
65{
67 int i;
68
69 char *names[] = {"/stt/a", "/stt/l1", "/stt/l2", "/stt/rl", "/stt/rl2",
70 "/stt/later", "/stt/dl", "/stt/x", "/stt/y", "/stt/s",
71 "/stt/d/f", "/stt/d", "/stt/dlink", "/stt/sub", "/stt/b",
72 "/stt/lb", "/stt/f2", "/stt/tmp", "/stt/c/t", 0};
73 for (i = 0; names[i]; i++)
75 for (i = 0; i <= MAXSYMLINK; i++) {
77 name[6] = '0' + i % 10; 78 if (i == 10)
81 }
82 for (i = 0; i < 4; i++) {
86 }
89}
90
91static void
92basic(void)
93{
95 int fd, ok;
96
97 ok = symlink("/stt/a", "/stt/l1") == 0 && hasdata("/stt/l1", 0, "hello");
98 ok = ok && statof("/stt/l1", 0, &st) == 0 && statof("/stt/a", 0, &sa) == 0;
100 // writing through the link changes the target.
102 ok = write(fd, "HELLO", 5) == 5; 104 } else
105 ok = 0;
106 ok = ok && hasdata("/stt/a", 0, "HELLO");
107 result("basic", ok);
108}
109
110static void
111relative(void)
112{
113 int ok;
114
115 // "a" means a in the link's directory /stt, not in the
116 // current directory /, where there is no a.
117 ok = symlink("a", "/stt/rl") == 0 && open("/a", O_RDONLY) < 0; 118 ok = ok && hasdata("/stt/rl", 0, "HELLO");
119 ok = ok && mkdir("/stt/sub") == 0 && chdir("/stt/sub") == 0; 120 ok = ok && hasdata("../rl", 0, "HELLO");
122 result("relative", ok);
123}
124
125static void
126dangling(void)
127{
128 int ok;
129
130 ok = symlink("/stt/later", "/stt/dl") == 0 && open("/stt/dl", O_RDONLY) < 0; 131 ok = ok && mkfile("/stt/later", "later") == 0 && hasdata("/stt/dl", 0, "later");
132 result("dangling", ok);
133}
134
135static void
136nofollow(void)
137{
139 int ok;
140
141 ok = statof("/stt/l1", O_NOFOLLOW, &st) == 0;
142 ok = ok && st.type == T_SYMLINK && st.size == 6 && st.nlink == 1; 143 ok = ok && hasdata("/stt/l1", O_NOFOLLOW, "/stt/a");
144 ok = ok && open("/stt/l1", O_RDWR | O_NOFOLLOW) < 0; 145 // the target's link count does not count symbolic links.
146 ok = ok && statof("/stt/a", 0, &sa) == 0 && sa.nlink == 1; 147 result("nofollow", ok);
148}
149
150static void
151chain(void)
152{
153 char name[16], target[16]; 154 int i, ok = 1;
155
156 // c10 -> /stt/a, c9 -> c10, ..., c0 -> c1.
157 for (i = 0; i <= MAXSYMLINK; i++) {
161 target[1] = '0' + i + 1;
162 if (i == 9)
164 if (i == 10) {
167 }
168 if (symlink(target, name) < 0) 169 ok = 0;
170 }
171 // from c1, MAXSYMLINK links lead to a; from c0, one too many.
172 ok = ok && hasdata("/stt/c1", 0, "HELLO");
174 ok = ok && symlink("/stt/l1", "/stt/l2") == 0 && hasdata("/stt/l2", 0, "HELLO");
175 result("chain", ok);
176}
177
178static void
179cycle(void)
180{
181 int ok;
182
183 ok = symlink("y", "/stt/x") == 0 && symlink("x", "/stt/y") == 0;
185 ok = ok && symlink("s", "/stt/s") == 0 && open("/stt/s", O_RDONLY) < 0; 187 result("cycle", ok);
188}
189
190static void
191directory(void)
192{
195 int fd, found = 0, ok; 196
197 ok = mkdir("/stt/d") == 0 && mkfile("/stt/d/f", "f") == 0; 198 ok = ok && symlink("d", "/stt/dlink") == 0;
199 ok = ok && statof("/stt/dlink", 0, &st) == 0 && statof("/stt/d", 0, &sd) == 0;
202 while (read(fd, &de, sizeof(de)) == sizeof(de)) 206 }
209 result("directory", ok);
210
211 // only open follows, and only the last name in the path.
213 result("not followed in the middle of a path or by chdir", ok);
214}
215
216static void
217createthrough(void)
218{
219 int ok;
220
221 // O_CREATE through a dangling link does not create the target.
222 ok = symlink("b", "/stt/lb") == 0;
224 ok = ok && mkfile("/stt/b", "old") == 0 && mkfile("/stt/lb", "bb") == 0;
225 ok = ok && hasdata("/stt/b", 0, "bb");
226 result("create through a link", ok);
227}
228
229static void
230unlinks(void)
231{
234
235 // unlink removes the link, not the target.
237 ok = ok && statof("/stt/a", 0, &sa) == 0 && sa.nlink == 1; 238 ok = ok && hasdata("/stt/a", 0, "HELLO");
239 result("unlink", ok);
240
241 // a hard link to a symbolic link counts in the link's nlink.
242 ok = link("/stt/rl", "/stt/rl2") == 0; 243 ok = ok && statof("/stt/rl2", O_NOFOLLOW, &st) == 0;
244 ok = ok && st.type == T_SYMLINK && st.nlink == 2; 245 ok = ok && unlink("/stt/rl") == 0 && hasdata("/stt/rl2", 0, "HELLO"); 246 result("hard link to a symlink", ok);
247
248 // the inode of an unlinked symlink is freed: the next file gets it.
249 ok = symlink("/stt/a", "/stt/tmp") == 0;
250 ok = ok && statof("/stt/tmp", O_NOFOLLOW, &st) == 0;
252 ok = ok && unlink("/stt/tmp") == 0 && mkfile("/stt/f2", "x") == 0; 253 ok = ok && statof("/stt/f2", 0, &st) == 0 && st.ino == ino; 254 result("unlinked symlink's inode freed", ok);
255}
256
257static void
258errors(void)
259{
260 int ok;
261
262 ok = symlink("", "/stt/e") < 0;
263 ok = ok && symlink("/stt/x", "/stt/a") < 0;
264 ok = ok && symlink("/stt/a", "/stt/nodir/x") < 0;
265 result("errors", ok);
266}
267
268// 3 processes create, remove and open the links /stt/c/l0..l3,
269// whose targets are t (a file) or another of the links. Each must
270// have reached t through links, and read links, at least once.
271static int
272worker(int id, int rounds, int out)
273{
274 char name[16], target[16], buf[16]; 276 unsigned int r = id * 7919 + 1;
277 int i, fd, n, made = 0, gone = 0, followed = 0, seen = 0;
278
279 for (i = 0; i < rounds; i++) {
280 r = r * 1103515245 + 12345;
282 name[8] = '0' + (r >> 8) % 4; 283 switch ((r >> 16) % 4) {
284 case 0:
285 if ((r >> 20) % 3 == 0)
287 else {
289 target[1] = '0' + (r >> 24) % 4;
290 }
291 if (symlink(target, name) == 0) 292 made++;
293 break;
294 case 1:
296 gone++;
297 break;
298 case 2: // following must end at t, or fail
303 printf("worker %d: bad follow of %s\n", id, name); 304 return 1;
305 }
306 followed++;
308 }
309 break;
310 case 3: // the link itself must hold a whole target
313 buf[n < 0 ? 0 : n] = 0; 314 if (fstat(fd, &st) < 0 || st.type != T_SYMLINK || 316 (n != 2 || buf[0] != 'l' || buf[1] < '0' || buf[1] > '3'))) { 318 return 1;
319 }
320 seen++;
322 }
323 break;
324 }
325 }
326 // one small write into a pipe is not interleaved with others.
327 int counts[5] = {id, made, gone, followed, seen};
328 if (write(out, counts, sizeof(counts)) != sizeof(counts)) 329 return 1;
330 // a run in which nothing was followed or read proves nothing.
331 return followed > 0 && seen > 0 ? 0 : 1;
332}
333
334static void
335concurrent(int rounds)
336{
337 int i, pid, xs, ok, fds[2], c[5]; 338
339 ok = mkdir("/stt/c") == 0 && mkfile("/stt/c/t", "target") == 0; 340 ok = ok && pipe(fds) == 0; 341 for (i = 0; ok && i < 3; i++) {
344 ok = 0;
347 exit(worker(i, rounds, fds[1])); 348 }
349 }
351 for (i = 0; i < 3; i++) {
352 if (wait(&xs) < 0 || xs != 0) 353 ok = 0;
354 }
355 while (read(fds[0], c, sizeof(c)) == sizeof(c)) 356 printf("symlinktest: worker %d: %d links made, %d removed, " 357 "%d opened through links, %d links read\n",
358 c[0], c[1], c[2], c[3], c[4]);
360 result("concurrent", ok);
361}
362
363int
365{
366 int rounds = 2000;
367
368 if (argc > 1)
371 cleanup();
372 if (mkdir("/stt") < 0 || mkfile("/stt/a", "hello") < 0) { 373 printf("symlinktest: cannot set up /stt\n"); 375 }
376 basic();
377 relative();
378 dangling();
379 nofollow();
380 chain();
381 cycle();
382 directory();
383 createthrough();
384 unlinks();
385 errors();
386 concurrent(rounds);
387 cleanup();
388 printf("symlinktest: %s\n", failed ? "SOME TESTS FAILED" : "ALL OK"); 390}