#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#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 }
};
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) {
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);
(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) {
continue;
}
pos2 = lseek(dirfd, pos, SEEK_SET);
if (pos2 < 0) {
}
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) {
}
else {
inval_read();
}
}
static
void
dotest(void)
{
printf("Opening directory...\n");
openit();
printf("Running tests...\n");
firstread();
readateof();
readallonebyone();
doreadateof();
readallrandomly();
doreadat0();
dobadreads();
doreadat0();
printf("Closing directory...\n");
closeit();
}
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);
}
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);
if (mkdir(TESTDIR, 0775)<0) {
err(1, "%s: mkdir", TESTDIR);
}
if (chdir(TESTDIR)<0) {
err(1, "%s: chdir", TESTDIR);
}
printf("Making some files...\n");
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");
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);
}
}
}
if (chdir("..")<0) {
err(1, "..: chdir");
}
if (rmdir(TESTDIR)<0) {
err(1, "%s: rmdir", TESTDIR);
}
}
int
main()
{
setup();
dotest();
dotest();
cleanup();
return 0;
}