/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * dirseek.c * * Tests seeking on directories (both legally and illegally). * * Makes a test subdirectory in the current directory. * * Intended for the file system assignment. Should run (on SFS) * when that assignment is complete. * * Note: checks a few things that are not _strictly_ guaranteed * by the official semantics of getdirentry() but that are more * or less necessary in a sane implementation, like that the * current seek position returned after seeking is the same * position that was requested. If you believe your * implementation is legal and the the test is rejecting it * gratuitously, please contact the course staff. */ #include #include #include #include #include #include #include #define TESTDIR "seektestdir" static struct { const char *name; int make_it; off_t pos; } testfiles[] = { { ".", 0, -1 }, { "..", 0, -1 }, { "ridcully", 1, -1 }, { "weatherwax", 1, -1 }, { "ogg", 1, -1 }, { "vorbis", 1, -1 }, { "verence", 1, -1 }, { "magrat", 1, -1 }, { "agnes", 1, -1 }, { "rincewind", 1, -1 }, { "angua", 1, -1 }, { "cherry", 1, -1 }, { "dorfl", 1, -1 }, { "nobby", 1, -1 }, { "carrot", 1, -1 }, { "vimes", 1, -1 }, { "detritus", 1, -1 }, { "twoflower", 1, -1 }, { "teatime", 1, -1 }, { "qu", 1, -1 }, { NULL, 0, 0 } }; /************************************************************/ /* Test code */ /************************************************************/ static int dirfd; static int findentry(const char *name) { int i; for (i=0; testfiles[i].name; i++) { if (!strcmp(testfiles[i].name, name)) { return i; } } return -1; } static void openit(void) { dirfd = open(".", O_RDONLY); if (dirfd < 0) { err(1, ".: open"); } } static void closeit(void) { if (close(dirfd)<0) { err(1, ".: close"); } dirfd = -1; } static void readit(void) { char buf[4096]; off_t pos; int len; int n, i, ix; for (i=0; testfiles[i].name; i++) { testfiles[i].pos = -1; } pos = lseek(dirfd, 0, SEEK_CUR); if (pos < 0) { err(1, ".: lseek(0, SEEK_CUR)"); } n = 0; while ((len = getdirentry(dirfd, buf, sizeof(buf)-1)) > 0) { if ((unsigned)len >= sizeof(buf)-1) { errx(1, ".: entry %d: getdirentry returned " "invalid length %d", n, len); } buf[len] = 0; ix = findentry(buf); if (ix < 0) { errx(1, ".: entry %d: getdirentry returned " "unexpected name %s", n, buf); } if (testfiles[ix].pos >= 0) { errx(1, ".: entry %d: getdirentry returned " "%s a second time", n, buf); } testfiles[ix].pos = pos; pos = lseek(dirfd, 0, SEEK_CUR); if (pos < 0) { err(1, ".: lseek(0, SEEK_CUR)"); } n++; } if (len<0) { err(1, ".: entry %d: getdirentry", n); } for (i=0; testfiles[i].name; i++) { if (testfiles[i].pos < 0) { errx(1, ".: getdirentry failed to return %s", testfiles[i].name); } } if (i!=n) { /* * If all of the other checks have passed, this should not * be able to fail. But... just in case I forgot something * or there's a bug... */ errx(1, ".: getdirentry returned %d names, not %d (huh...?)", n, i); } } static void firstread(void) { off_t pos; pos = lseek(dirfd, 0, SEEK_CUR); if (pos < 0) { err(1, ".: lseek(0, SEEK_CUR)"); } if (pos != 0) { errx(1, ".: File position after open not 0"); } printf("Scanning directory...\n"); readit(); } static void doreadat0(void) { off_t pos; printf("Rewinding directory and reading it again...\n"); pos = lseek(dirfd, 0, SEEK_SET); if (pos < 0) { err(1, ".: lseek(0, SEEK_SET)"); } if (pos != 0) { errx(1, ".: lseek(0, SEEK_SET) returned %ld", (long) pos); } readit(); } static void readone(const char *shouldbe) { char buf[4096]; int len; len = getdirentry(dirfd, buf, sizeof(buf)-1); if (len < 0) { err(1, ".: getdirentry"); } if ((unsigned)len >= sizeof(buf)-1) { errx(1, ".: getdirentry returned invalid length %d", len); } buf[len] = 0; if (strcmp(buf, shouldbe)) { errx(1, ".: getdirentry returned %s (expected %s)", buf, shouldbe); } } static void doreadone(int which) { off_t pos; pos = lseek(dirfd, testfiles[which].pos, SEEK_SET); if (pos<0) { err(1, ".: lseek(%ld, SEEK_SET)", (long) testfiles[which].pos); } if (pos != testfiles[which].pos) { errx(1, ".: lseek(%ld, SEEK_SET) returned %ld", (long) testfiles[which].pos, (long) pos); } readone(testfiles[which].name); } static void readallonebyone(void) { int i; printf("Trying to read each entry again...\n"); for (i=0; testfiles[i].name; i++) { doreadone(i); } } static void readallrandomly(void) { int n, i, x; printf("Trying to read a bunch of entries randomly...\n"); for (i=0; testfiles[i].name; i++); n = i; srandom(39584); for (i=0; i<512; i++) { x = (int)(random()%n); doreadone(x); } } static void readateof(void) { char buf[4096]; int len; len = getdirentry(dirfd, buf, sizeof(buf)-1); if (len < 0) { err(1, ".: at EOF: getdirentry"); } if (len==0) { return; } if ((unsigned)len >= sizeof(buf)-1) { errx(1, ".: at EOF: getdirentry returned " "invalid length %d", len); } buf[len] = 0; errx(1, ".: at EOF: got unexpected name %s", buf); } static void doreadateof(void) { off_t pos; int i; printf("Trying to read after going to EOF...\n"); pos = lseek(dirfd, 0, SEEK_END); if (pos<0) { err(1, ".: lseek(0, SEEK_END)"); } for (i=0; testfiles[i].name; i++) { if (pos <= testfiles[i].pos) { errx(1, ".: EOF position %ld below position %ld of %s", pos, testfiles[i].pos, testfiles[i].name); } } readateof(); } static void inval_read(void) { char buf[4096]; int len; len = getdirentry(dirfd, buf, sizeof(buf)-1); /* Any result is ok, as long as the system doesn't crash */ (void)len; } static void dobadreads(void) { off_t pos, pos2, eof; int valid, i, k=0; printf("Trying some possibly invalid reads...\n"); eof = lseek(dirfd, 0, SEEK_END); if (eof < 0) { err(1, ".: lseek(0, SEEK_END)"); } for (pos=0; pos < eof; pos++) { valid = 0; for (i=0; testfiles[i].name; i++) { if (pos==testfiles[i].pos) { valid = 1; } } if (valid) { /* don't try offsets that are known to be valid */ continue; } pos2 = lseek(dirfd, pos, SEEK_SET); if (pos2 < 0) { /* this is ok */ } else { inval_read(); k++; } } if (k>0) { printf("Survived %d invalid reads...\n", k); } else { printf("Couldn't find any invalid offsets to try...\n"); } printf("Trying to read beyond EOF...\n"); pos2 = lseek(dirfd, eof + 1000, SEEK_SET); if (pos2 < 0) { /* this is ok */ } else { inval_read(); } } static void dotest(void) { printf("Opening directory...\n"); openit(); printf("Running tests...\n"); /* read the whole directory */ firstread(); /* make sure eof behaves right */ readateof(); /* read all the filenames again by seeking */ readallonebyone(); /* try reading at eof */ doreadateof(); /* read a bunch of the filenames over and over again */ readallrandomly(); /* rewind and read the whole thing again, to make sure that works */ doreadat0(); /* do invalid reads */ dobadreads(); /* rewind again to make sure the invalid attempts didn't break it */ doreadat0(); printf("Closing directory...\n"); closeit(); } /************************************************************/ /* Setup code */ /************************************************************/ static void mkfile(const char *name) { int fd, i, r; static const char message[] = "The turtle moves!\n"; char buf[32*sizeof(message)+1]; buf[0]=0; for (i=0; i<32; i++) { strcat(buf, message); } /* Use O_EXCL, because we know the file shouldn't already be there */ fd = open(name, O_WRONLY|O_CREAT|O_EXCL, 0664); if (fd<0) { err(1, "%s: create", name); } r = write(fd, buf, strlen(buf)); if (r<0) { err(1, "%s: write", name); } if ((unsigned)r != strlen(buf)) { errx(1, "%s: short write (%d bytes)", name, r); } if (close(fd)<0) { err(1, "%s: close", name); } } static void setup(void) { int i; printf("Making directory %s...\n", TESTDIR); /* Create a directory */ if (mkdir(TESTDIR, 0775)<0) { err(1, "%s: mkdir", TESTDIR); } /* Switch to it */ if (chdir(TESTDIR)<0) { err(1, "%s: chdir", TESTDIR); } printf("Making some files...\n"); /* Populate it */ for (i=0; testfiles[i].name; i++) { if (testfiles[i].make_it) { mkfile(testfiles[i].name); } testfiles[i].pos = -1; } } static void cleanup(void) { int i; printf("Cleaning up...\n"); /* Remove the files */ for (i=0; testfiles[i].name; i++) { if (testfiles[i].make_it) { if (remove(testfiles[i].name)<0) { err(1, "%s: remove", testfiles[i].name); } } } /* Leave the dir */ if (chdir("..")<0) { err(1, "..: chdir"); } /* Remove the dir */ if (rmdir(TESTDIR)<0) { err(1, "%s: rmdir", TESTDIR); } } int main(void) { setup(); /* Do the whole thing twice */ dotest(); dotest(); cleanup(); return 0; }