@@ -0,0 +1,425 @@
1// renametest: tests for rename().
2//
3// renametest functional tests, then seven processes renaming
4// between two directories at once on all harts.
5// renametest concurrent N only the concurrent test, N rounds.
6// renametest crash renames back and forth until the machine is
7// killed; renametest check, after a reboot, says
8// whether every rename happened completely or not
9// at all. A driver outside xv6 must do the killing.
10
11#include "kernel/types.h"
12#include "kernel/stat.h"
13#include "kernel/fcntl.h"
14#include "kernel/fs.h"
15#include "user/user.h"
16
17static int failures;
18
19static void
20writefile(char *path, char *s)
21{
24 printf("renametest: cannot write %s\n", path); 26 }
28}
29
30// Does the file hold exactly s?
31static int
32holds(char *path, char *s)
33{
35 int fd, n;
36
38 return 0;
42}
43
44static int
45exists(char *path)
46{
48 return stat(path, &st) == 0; 49}
50
51static uint
53{
55 if (stat(path, &st) < 0) 56 return 0;
58}
59
60static int
62{
64 if (stat(path, &st) < 0) 65 return -1;
67}
68
69// Record one check; print the first failure of each test.
71static void
72expect(int ok, char *what)
73{
74 if (!ok && bad++ == 0) 75 printf("renametest: failed: %s\n", what); 76}
77
78static void
80{
84}
85
86// Remove path and everything under it.
87static void
88rmrf(char *path)
89{
93 int fd;
94
95 if (stat(path, &st) < 0) 96 return;
100 *p++ = '/';
101 while (read(fd, &de, sizeof(de)) == sizeof(de)) { 104 continue;
108 }
110 }
112}
113
114static void
115samedir(void)
116{
117 uint i;
118
119 writefile("rt/f1", "one");
121 expect(rename("rt/f1", "rt/f2") == 0, "rename rt/f1 rt/f2");
122 expect(!exists("rt/f1"), "rt/f1 still exists");
123 expect(ino("rt/f2") == i, "rt/f2 is not the same inode"); 124 expect(nlink("rt/f2") == 1, "rt/f2 nlink is not 1"); 125 expect(holds("rt/f2", "one"), "rt/f2 lost its content");
126 result("same directory");
127}
128
129static void
130crossdir(void)
131{
132 uint i = ino("rt/f2"); 133
136 // down: the old parent is above the new one
137 expect(rename("rt/f2", "rt/a/b/f") == 0, "rename rt/f2 rt/a/b/f");
138 // sideways: unrelated parents
140 expect(rename("rt/a/b/f", "rt/c/f") == 0, "rename rt/a/b/f rt/c/f");
141 // up: the new parent is above the old one
142 expect(rename("rt/c/f", "rt/f3") == 0, "rename rt/c/f rt/f3");
143 expect(!exists("rt/f2") && !exists("rt/a/b/f") && !exists("rt/c/f"),
144 "an old name still exists");
145 expect(ino("rt/f3") == i && nlink("rt/f3") == 1, "rt/f3 inode or nlink"); 146 expect(holds("rt/f3", "one"), "rt/f3 lost its content");
147 result("across directories");
148}
149
150static void
151replace(void)
152{
155 uint i;
156 int fd;
157
158 writefile("rt/new", "new");
159 writefile("rt/old", "old!");
161 fd = open("rt/old", O_RDONLY); // keeps the replaced file alive 162 expect(rename("rt/new", "rt/old") == 0, "rename rt/new rt/old");
163 expect(!exists("rt/new"), "rt/new still exists");
164 expect(ino("rt/old") == i && holds("rt/old", "new"), "rt/old is not new"); 165 expect(fstat(fd, &st) == 0 && st.nlink == 0, "replaced file nlink not 0"); 167 "replaced file unreadable while open");
168 close(fd); // its last reference: the kernel frees it now 169
170 // two names for one file: rename does nothing, successfully
171 link("rt/old", "rt/h"); 172 expect(rename("rt/old", "rt/h") == 0, "rename of a file onto itself");
173 expect(exists("rt/old") && exists("rt/h") && nlink("rt/h") == 2, 174 "hard links changed");
176 result("replace a file");
177}
178
179static void
180movedir(void)
181{
182 int na, nc, nd;
183
186 na = nlink("rt/a"); // 1 + subdirectories b and d 188 nd = nlink("rt/a/d"); 189 expect(rename("rt/a/d", "rt/c/d") == 0, "rename rt/a/d rt/c/d");
190 expect(!exists("rt/a/d") && exists("rt/c/d/e"), "the subtree did not move");
191 expect(ino("rt/c/d/..") == ino("rt/c"), "rt/c/d/.. is not rt/c"); 192 expect(ino("rt/c/d/e/..") == ino("rt/c/d"), "rt/c/d/e/.. is wrong"); 193 expect(nlink("rt/a") == na - 1, "old parent nlink"); 194 expect(nlink("rt/c") == nc + 1, "new parent nlink"); 195 expect(nlink("rt/c/d") == nd, "moved directory nlink"); 196 // within one directory nothing but the name changes
197 expect(rename("rt/c/d", "rt/c/d2") == 0, "rename rt/c/d rt/c/d2");
198 expect(ino("rt/c/d2/..") == ino("rt/c") && nlink("rt/c") == nc + 1, 199 "rename within rt/c changed .. or nlink");
200 result("move a directory");
201}
202
203static void
204cycle(void)
205{
206 int nc = nlink("rt/c"); 207
208 expect(rename("rt/c", "rt/c/x") < 0, "moved rt/c into itself");
209 expect(rename("rt/c", "rt/c/d2/e/x") < 0, "moved rt/c below itself");
210 expect(rename("rt", "rt/a/b/x") < 0, "moved rt below itself");
211 expect(exists("rt/c/d2/e") && nlink("rt/c") == nc, "rt/c changed"); 212 result("refuse a cycle");
213}
214
215static void
216refusals(void)
217{
218 writefile("rt/g", "g");
219 expect(rename("rt/c/.", "rt/x") < 0, "renamed rt/c/.");
220 expect(rename("rt/c/..", "rt/x") < 0, "renamed rt/c/..");
221 expect(rename("rt/g", "rt/c/..") < 0, "renamed onto rt/c/..");
222 expect(rename("rt/g", "rt/c") < 0, "replaced a directory");
223 expect(rename("rt/c", "rt/g") < 0, "replaced a file with a directory");
224 expect(rename("rt/nothere", "rt/x") < 0, "renamed a missing file");
225 expect(rename("/", "rt/x") < 0, "renamed /");
226 expect(rename("rt/g", "rt/nothere/x") < 0, "renamed into a missing dir");
227 expect(holds("rt/g", "g") && exists("rt/c/d2") && !exists("rt/x"),
228 "a refused rename changed something");
229 result("refusals");
230}
231
232static int rounds = 200;
233
234// Seven processes on three harts. Two rename files between ct/a
235// and ct/b in opposite directions; one renames between ct/a and
236// its subdirectory ct/a/s; one moves the directory m between
237// ct/a and ct/b, and each time also tries to move m's current
238// parent into m, which must fail. Those four do a fixed number of
239// rounds. Until they finish, one process keeps trying to unlink
240// ct/a/s (which must fail: it is not empty), and two churn names
241// in ct/a and ct/b (create, rename onto an existing file, rename
242// away, unlink), so that names change under the other renames.
243static void
244concurrent(void)
245{
246 static char *moves[3][2] = {
247 {"ct/a/x", "ct/b/y"},
248 {"ct/b/z", "ct/a/w"},
249 {"ct/a/s/q", "ct/a/q"},
250 };
251 int i, j, pid, xs, watchdog, t0, done, ok; 252
253 rmrf("ct");
258 rename("ct/s", "ct/a/s");
259 writefile("ct/a/x", "x");
260 writefile("ct/b/z", "z");
261 writefile("ct/a/s/q", "q");
262 writefile("ct/a/s/keep", "keep");
264 expect(ino("ct/a/s") < ino("ct/a"), "setup: ct/a/s numbered after ct/a"); 265
267
269 if (watchdog == 0) {
270 pause(3 * rounds); // 60 seconds for the usual 200 rounds 271 printf("renametest: concurrent: not finished after %d seconds\n", 272 3 * rounds / 10);
274 }
275 for (i = 0; i < 7; i++) {
277 int nbad = 0;
278 if (i < 3) {
279 for (j = 0; j < rounds; j++) {
280 nbad += rename(moves[i][0], moves[i][1]) < 0;
281 nbad += rename(moves[i][1], moves[i][0]) < 0;
282 }
283 } else if (i == 3) {
284 for (j = 0; j < rounds; j++) {
285 nbad += rename("ct/a/m", "ct/b/m") < 0;
286 nbad += rename("ct/b", "ct/b/m/x") == 0; // a cycle
287 nbad += rename("ct/b/m", "ct/a/m") < 0;
288 nbad += rename("ct/a", "ct/a/m/x") == 0; // a cycle
289 }
290 } else if (i == 4) {
291 while (!exists("ct/stop"))
292 nbad += unlink("ct/a/s") == 0; 293 } else {
294 // churners: their own renames may fail when the other
295 // churner got there first; only the totals are checked
296 while (!exists("ct/stop")) {
297 if (i == 5) {
299 rename("ct/a/t", "ct/b/t");
300 } else {
301 rename("ct/b/t", "ct/a/u");
303 }
304 }
305 }
307 }
308 }
309 for (done = 0; done < 7;) {
311 if (pid == watchdog) { 312 watchdog = -1;
313 continue;
314 }
315 expect(xs == 0, "a worker's rename or unlink did the wrong thing");
316 if (++done == 4)
317 writefile("ct/stop", ""); // the fixed-round workers are done
318 }
319 if (watchdog > 0) {
322 }
323 expect(exists("ct/a/x") && exists("ct/b/z") && exists("ct/a/s/q") &&
324 exists("ct/a/m"),
325 "a file or directory is missing");
326 expect(!exists("ct/b/y") && !exists("ct/a/w") && !exists("ct/a/q") &&
327 !exists("ct/b/m"),
328 "a file or directory has two names");
329 // link counts: ct holds a and b, ct/a holds s and m, ct/b none
331 "a directory's link count is wrong");
332 expect(ino("ct/a/m/..") == ino("ct/a"), "ct/a/m/.. is not ct/a"); 333 ok = nlink("ct/a/x") == 1 && nlink("ct/b/z") == 1 && nlink("ct/a/s/q") == 1; 334 expect(ok && (!exists("ct/b/t") || nlink("ct/b/t") == 1) && 335 (!exists("ct/a/t") || nlink("ct/a/t") == 1), 336 "a file's link count is wrong");
337 printf("renametest: concurrent: %d renames, %d refused cycles in %d ticks\n", 338 8 * rounds, 2 * rounds, uptime() - t0); 339 result("concurrent");
340 rmrf("ct");
341}
342
343// Crash mode: f is a file and d a directory, each in cr/a or
344// cr/b; each round moves both to the other directory.
345static void
346crash(void)
347{
348 char *fa = "cr/a/f", *fb = "cr/b/f", *da = "cr/a/d", *db = "cr/b/d";
349 int n, fin, din;
350
351 if (!exists("cr")) {
355 writefile(fa, "crash");
357 }
358 fin = exists(fa); // is f in cr/a?
359 din = exists(da);
360 printf("renametest: crash: renaming until killed\n"); 361 for (n = 1;; n++) {
362 if (rename(fin ? fa : fb, fin ? fb : fa) < 0)
363 printf("renametest: crash: cannot move f\n"); 364 fin = !fin;
365 if (rename(din ? da : db, din ? db : da) < 0)
366 printf("renametest: crash: cannot move d\n"); 367 din = !din;
368 if (n % 50 == 0)
369 printf("renametest: crash: %d rounds\n", n); 370 }
371}
372
373static void
374check(void)
375{
376 int fa = exists("cr/a/f"), fb = exists("cr/b/f");
377 int da = exists("cr/a/d"), db = exists("cr/b/d");
378 char *f = fa ? "cr/a/f" : "cr/b/f";
379
380 expect(fa + fb == 1, "f has not exactly one name");
381 expect(da + db == 1, "d has not exactly one name");
382 expect(nlink(f) == 1 && holds(f, "crash"), "f nlink or content"); 383 expect(ino(da ? "cr/a/d/.." : "cr/b/d/..") == ino(da ? "cr/a" : "cr/b"), 384 "d's .. is not its parent");
385 expect(nlink("cr/a") == 1 + da && nlink("cr/b") == 1 + db, 386 "parent nlink does not match where d is");
387 printf("renametest: check: f in cr/%s (%d name%s), d in cr/%s (%d name%s)\n", 388 fa ? "a" : "b", fa + fb, fa + fb == 1 ? "" : "s", da ? "a" : "b",
389 da + db, da + db == 1 ? "" : "s");
390 result("check");
391}
392
393int
395{
396 if (argc == 2 && strcmp(argv[1], "crash") == 0) 397 crash();
398 if (argc == 3 && strcmp(argv[1], "concurrent") == 0) { 400 concurrent();
402 }
403 if (argc == 2 && strcmp(argv[1], "check") == 0) { 404 check();
406 }
407 rmrf("rt");
408 if (mkdir("rt") < 0) { 409 printf("renametest: mkdir rt failed\n"); 411 }
412 samedir();
413 crossdir();
414 replace();
415 movedir();
416 cycle();
417 refusals();
418 concurrent();
419 rmrf("rt");
420 if (failures)
421 printf("renametest: SOME TESTS FAILED\n"); 422 else
423 printf("renametest: ALL OK (crash consistency: see renametest crash)\n"); 425}