diff options
author | marcel <marcel@FreeBSD.org> | 2013-08-09 19:10:56 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2013-08-09 19:10:56 +0000 |
commit | b239ca1a30625ac48b3722e376bb94579c7e43f3 (patch) | |
tree | f2bbbd25e4ea9251c91bb3a97e4f61ab8adad0e5 /sys/boot | |
parent | 2895e1352cf3788606924d800c3a5c589520ea00 (diff) | |
download | FreeBSD-src-b239ca1a30625ac48b3722e376bb94579c7e43f3.zip FreeBSD-src-b239ca1a30625ac48b3722e376bb94579c7e43f3.tar.gz |
Fix the freaddir implementation for the stand-alone interpreter.
Bug pointed out by: Jan Beich <jbeich@tormail.org>
Diffstat (limited to 'sys/boot')
-rw-r--r-- | sys/boot/ficl/loader.c | 50 |
1 files changed, 39 insertions, 11 deletions
diff --git a/sys/boot/ficl/loader.c b/sys/boot/ficl/loader.c index 03ef95f..f39b1eb 100644 --- a/sys/boot/ficl/loader.c +++ b/sys/boot/ficl/loader.c @@ -518,12 +518,12 @@ static void pfread(FICL_VM *pVM) static void pfreaddir(FICL_VM *pVM) { #ifdef TESTMAIN - static union { - struct dirent dirent; - char buf[512]; - } u; - off_t off; - int len; + static struct dirent dirent; + struct stat sb; + char *buf; + off_t off, ptr; + u_int blksz; + int bufsz; #endif struct dirent *d; int fd; @@ -539,11 +539,39 @@ static void pfreaddir(FICL_VM *pVM) * We do the best we can to make freaddir work, but it's not at * all guaranteed. */ - off = lseek(fd, 0LL, SEEK_CUR); - len = getdents(fd, u.buf, sizeof(u.buf)); - d = (len != -1) ? &u.dirent : NULL; - if (d != NULL) - lseek(fd, off + d->d_reclen, SEEK_SET); + d = NULL; + buf = NULL; + do { + if (fd == -1) + break; + if (fstat(fd, &sb) == -1) + break; + blksz = (sb.st_blksize) ? sb.st_blksize : getpagesize(); + if ((blksz & (blksz - 1)) != 0) + break; + buf = malloc(blksz); + if (buf == NULL) + break; + off = lseek(fd, 0LL, SEEK_CUR); + if (off == -1) + break; + ptr = off; + if (lseek(fd, 0, SEEK_SET) == -1) + break; + bufsz = getdents(fd, buf, blksz); + while (bufsz > 0 && bufsz <= ptr) { + ptr -= bufsz; + bufsz = getdents(fd, buf, blksz); + } + if (bufsz <= 0) + break; + d = (void *)(buf + ptr); + dirent = *d; + off += d->d_reclen; + d = (lseek(fd, off, SEEK_SET) != off) ? NULL : &dirent; + } while (0); + if (buf != NULL) + free(buf); #else d = readdirfd(fd); #endif |