/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- findentry
- openit
- closeit
- readit
- firstread
- doreadat0
- readone
- doreadone
- readallonebyone
- readallrandomly
- readateof
- doreadateof
- inval_read
- dobadreads
- dotest
- mkfile
- setup
- cleanup
- main
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3 * The President and Fellows of Harvard College.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * dirseek.c
32 *
33 * Tests seeking on directories (both legally and illegally).
34 *
35 * Makes a test subdirectory in the current directory.
36 *
37 * Intended for the file system assignment. Should run (on SFS)
38 * when that assignment is complete.
39 *
40 * Note: checks a few things that are not _strictly_ guaranteed
41 * by the official semantics of getdirentry() but that are more
42 * or less necessary in a sane implementation, like that the
43 * current seek position returned after seeking is the same
44 * position that was requested. If you believe your
45 * implementation is legal and the the test is rejecting it
46 * gratuitously, please contact the course staff.
47 */
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <err.h>
56
57 #define TESTDIR "seektestdir"
58
59 static struct {
60 const char *name;
61 int make_it;
62 off_t pos;
63 } testfiles[] = {
64 { ".", 0, -1 },
65 { "..", 0, -1 },
66 { "ridcully", 1, -1 },
67 { "weatherwax", 1, -1 },
68 { "ogg", 1, -1 },
69 { "vorbis", 1, -1 },
70 { "verence", 1, -1 },
71 { "magrat", 1, -1 },
72 { "agnes", 1, -1 },
73 { "rincewind", 1, -1 },
74 { "angua", 1, -1 },
75 { "cherry", 1, -1 },
76 { "dorfl", 1, -1 },
77 { "nobby", 1, -1 },
78 { "carrot", 1, -1 },
79 { "vimes", 1, -1 },
80 { "detritus", 1, -1 },
81 { "twoflower", 1, -1 },
82 { "teatime", 1, -1 },
83 { "qu", 1, -1 },
84 { NULL, 0, 0 }
85 };
86
87 /************************************************************/
88 /* Test code */
89 /************************************************************/
90
91 static int dirfd;
92
93 static
94 int
95 findentry(const char *name)
96 {
97 int i;
98
99 for (i=0; testfiles[i].name; i++) {
100 if (!strcmp(testfiles[i].name, name)) {
101 return i;
102 }
103 }
104 return -1;
105 }
106
107 static
108 void
109 openit(void)
110 {
111 dirfd = open(".", O_RDONLY);
112 if (dirfd < 0) {
113 err(1, ".: open");
114 }
115 }
116
117 static
118 void
119 closeit(void)
120 {
121 if (close(dirfd)<0) {
122 err(1, ".: close");
123 }
124 dirfd = -1;
125 }
126
127 static
128 void
129 readit(void)
130 {
131 char buf[4096];
132 off_t pos;
133 int len;
134 int n, i, ix;
135
136 for (i=0; testfiles[i].name; i++) {
137 testfiles[i].pos = -1;
138 }
139
140 pos = lseek(dirfd, 0, SEEK_CUR);
141 if (pos < 0) {
142 err(1, ".: lseek(0, SEEK_CUR)");
143 }
144 n = 0;
145
146 while ((len = getdirentry(dirfd, buf, sizeof(buf)-1)) > 0) {
147
148 if ((unsigned)len >= sizeof(buf)-1) {
149 errx(1, ".: entry %d: getdirentry returned "
150 "invalid length %d", n, len);
151 }
152 buf[len] = 0;
153 ix = findentry(buf);
154 if (ix < 0) {
155 errx(1, ".: entry %d: getdirentry returned "
156 "unexpected name %s", n, buf);
157 }
158
159 if (testfiles[ix].pos >= 0) {
160 errx(1, ".: entry %d: getdirentry returned "
161 "%s a second time", n, buf);
162 }
163
164 testfiles[ix].pos = pos;
165
166 pos = lseek(dirfd, 0, SEEK_CUR);
167 if (pos < 0) {
168 err(1, ".: lseek(0, SEEK_CUR)");
169 }
170 n++;
171 }
172 if (len<0) {
173 err(1, ".: entry %d: getdirentry", n);
174 }
175
176 for (i=0; testfiles[i].name; i++) {
177 if (testfiles[i].pos < 0) {
178 errx(1, ".: getdirentry failed to return %s",
179 testfiles[i].name);
180 }
181 }
182 if (i!=n) {
183 /*
184 * If all of the other checks have passed, this should not
185 * be able to fail. But... just in case I forgot something
186 * or there's a bug...
187 */
188
189 errx(1, ".: getdirentry returned %d names, not %d (huh...?)",
190 n, i);
191 }
192 }
193
194 static
195 void
196 firstread(void)
197 {
198 off_t pos;
199
200 pos = lseek(dirfd, 0, SEEK_CUR);
201 if (pos < 0) {
202 err(1, ".: lseek(0, SEEK_CUR)");
203 }
204 if (pos != 0) {
205 errx(1, ".: File position after open not 0");
206 }
207
208 printf("Scanning directory...\n");
209
210 readit();
211 }
212
213 static
214 void
215 doreadat0(void)
216 {
217 off_t pos;
218
219 printf("Rewinding directory and reading it again...\n");
220
221 pos = lseek(dirfd, 0, SEEK_SET);
222 if (pos < 0) {
223 err(1, ".: lseek(0, SEEK_SET)");
224 }
225 if (pos != 0) {
226 errx(1, ".: lseek(0, SEEK_SET) returned %ld", (long) pos);
227 }
228
229 readit();
230 }
231
232 static
233 void
234 readone(const char *shouldbe)
235 {
236 char buf[4096];
237 int len;
238
239 len = getdirentry(dirfd, buf, sizeof(buf)-1);
240 if (len < 0) {
241 err(1, ".: getdirentry");
242 }
243 if ((unsigned)len >= sizeof(buf)-1) {
244 errx(1, ".: getdirentry returned invalid length %d", len);
245 }
246 buf[len] = 0;
247
248 if (strcmp(buf, shouldbe)) {
249 errx(1, ".: getdirentry returned %s (expected %s)",
250 buf, shouldbe);
251 }
252 }
253
254 static
255 void
256 doreadone(int which)
257 {
258 off_t pos;
259 pos = lseek(dirfd, testfiles[which].pos, SEEK_SET);
260 if (pos<0) {
261 err(1, ".: lseek(%ld, SEEK_SET)", (long) testfiles[which].pos);
262 }
263 if (pos != testfiles[which].pos) {
264 errx(1, ".: lseek(%ld, SEEK_SET) returned %ld",
265 (long) testfiles[which].pos, (long) pos);
266 }
267
268 readone(testfiles[which].name);
269 }
270
271 static
272 void
273 readallonebyone(void)
274 {
275 int i;
276
277 printf("Trying to read each entry again...\n");
278 for (i=0; testfiles[i].name; i++) {
279 doreadone(i);
280 }
281 }
282
283 static
284 void
285 readallrandomly(void)
286 {
287 int n, i, x;
288
289 printf("Trying to read a bunch of entries randomly...\n");
290
291 for (i=0; testfiles[i].name; i++);
292 n = i;
293
294 srandom(39584);
295 for (i=0; i<512; i++) {
296 x = (int)(random()%n);
297 doreadone(x);
298 }
299 }
300
301 static
302 void
303 readateof(void)
304 {
305 char buf[4096];
306 int len;
307
308 len = getdirentry(dirfd, buf, sizeof(buf)-1);
309 if (len < 0) {
310 err(1, ".: at EOF: getdirentry");
311 }
312 if (len==0) {
313 return;
314 }
315 if ((unsigned)len >= sizeof(buf)-1) {
316 errx(1, ".: at EOF: getdirentry returned "
317 "invalid length %d", len);
318 }
319 buf[len] = 0;
320 errx(1, ".: at EOF: got unexpected name %s", buf);
321 }
322
323 static
324 void
325 doreadateof(void)
326 {
327 off_t pos;
328 int i;
329
330 printf("Trying to read after going to EOF...\n");
331
332 pos = lseek(dirfd, 0, SEEK_END);
333 if (pos<0) {
334 err(1, ".: lseek(0, SEEK_END)");
335 }
336
337 for (i=0; testfiles[i].name; i++) {
338 if (pos <= testfiles[i].pos) {
339 errx(1, ".: EOF position %ld below position %ld of %s",
340 pos, testfiles[i].pos, testfiles[i].name);
341 }
342 }
343
344 readateof();
345 }
346
347 static
348 void
349 inval_read(void)
350 {
351 char buf[4096];
352 int len;
353
354 len = getdirentry(dirfd, buf, sizeof(buf)-1);
355
356 /* Any result is ok, as long as the system doesn't crash */
357 (void)len;
358 }
359
360 static
361 void
362 dobadreads(void)
363 {
364 off_t pos, pos2, eof;
365 int valid, i, k=0;
366
367 printf("Trying some possibly invalid reads...\n");
368
369 eof = lseek(dirfd, 0, SEEK_END);
370 if (eof < 0) {
371 err(1, ".: lseek(0, SEEK_END)");
372 }
373
374 for (pos=0; pos < eof; pos++) {
375 valid = 0;
376 for (i=0; testfiles[i].name; i++) {
377 if (pos==testfiles[i].pos) {
378 valid = 1;
379 }
380 }
381 if (valid) {
382 /* don't try offsets that are known to be valid */
383 continue;
384 }
385
386 pos2 = lseek(dirfd, pos, SEEK_SET);
387 if (pos2 < 0) {
388 /* this is ok */
389 }
390 else {
391 inval_read();
392 k++;
393 }
394 }
395
396 if (k>0) {
397 printf("Survived %d invalid reads...\n", k);
398 }
399 else {
400 printf("Couldn't find any invalid offsets to try...\n");
401 }
402
403 printf("Trying to read beyond EOF...\n");
404 pos2 = lseek(dirfd, eof + 1000, SEEK_SET);
405 if (pos2 < 0) {
406 /* this is ok */
407 }
408 else {
409 inval_read();
410 }
411 }
412
413 static
414 void
415 dotest(void)
416 {
417 printf("Opening directory...\n");
418 openit();
419
420 printf("Running tests...\n");
421
422 /* read the whole directory */
423 firstread();
424
425 /* make sure eof behaves right */
426 readateof();
427
428 /* read all the filenames again by seeking */
429 readallonebyone();
430
431 /* try reading at eof */
432 doreadateof();
433
434 /* read a bunch of the filenames over and over again */
435 readallrandomly();
436
437 /* rewind and read the whole thing again, to make sure that works */
438 doreadat0();
439
440 /* do invalid reads */
441 dobadreads();
442
443 /* rewind again to make sure the invalid attempts didn't break it */
444 doreadat0();
445
446 printf("Closing directory...\n");
447 closeit();
448 }
449
450 /************************************************************/
451 /* Setup code */
452 /************************************************************/
453
454 static
455 void
456 mkfile(const char *name)
457 {
458 int fd, i, r;
459 static const char message[] = "The turtle moves!\n";
460 char buf[32*sizeof(message)+1];
461
462 buf[0]=0;
463 for (i=0; i<32; i++) {
464 strcat(buf, message);
465 }
466
467 /* Use O_EXCL, because we know the file shouldn't already be there */
468 fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0664);
469 if (fd<0) {
470 err(1, "%s: create", name);
471 }
472
473 r = write(fd, buf, strlen(buf));
474 if (r<0) {
475 err(1, "%s: write", name);
476 }
477 if ((unsigned)r != strlen(buf)) {
478 errx(1, "%s: short write (%d bytes)", name, r);
479 }
480
481 if (close(fd)<0) {
482 err(1, "%s: close", name);
483 }
484 }
485
486 static
487 void
488 setup(void)
489 {
490 int i;
491
492 printf("Making directory %s...\n", TESTDIR);
493
494 /* Create a directory */
495 if (mkdir(TESTDIR, 0775)<0) {
496 err(1, "%s: mkdir", TESTDIR);
497 }
498
499 /* Switch to it */
500 if (chdir(TESTDIR)<0) {
501 err(1, "%s: chdir", TESTDIR);
502 }
503
504 printf("Making some files...\n");
505
506 /* Populate it */
507 for (i=0; testfiles[i].name; i++) {
508 if (testfiles[i].make_it) {
509 mkfile(testfiles[i].name);
510 }
511 testfiles[i].pos = -1;
512 }
513 }
514
515 static
516 void
517 cleanup(void)
518 {
519 int i;
520
521 printf("Cleaning up...\n");
522
523 /* Remove the files */
524 for (i=0; testfiles[i].name; i++) {
525 if (testfiles[i].make_it) {
526 if (remove(testfiles[i].name)<0) {
527 err(1, "%s: remove", testfiles[i].name);
528 }
529 }
530 }
531
532 /* Leave the dir */
533 if (chdir("..")<0) {
534 err(1, "..: chdir");
535 }
536
537 /* Remove the dir */
538 if (rmdir(TESTDIR)<0) {
539 err(1, "%s: rmdir", TESTDIR);
540 }
541 }
542
543
544 int
545 main()
546 {
547 setup();
548
549 /* Do the whole thing twice */
550 dotest();
551 dotest();
552
553 cleanup();
554 return 0;
555 }