home *** CD-ROM | disk | FTP | other *** search
- #include <dirent.h>
- #include <errno.h>
- #include <sys/syscall.h>
-
- #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
- #define ROUND_UP(x) (((x)+3) & ~3)
-
- /*
- * readdir fills up the buffer with the readdir system call.
- *
- * The old-style kernel readdir interface accepted, but ignored, a
- * third parameter which was set to one by the library and which was
- * reserved to allow future libraries to read more than one dirent at
- * a time. The return value in this case was simply the length of the
- * name of the dirent returned.
- *
- * Right now the readdir system call accepts a buffer size as its
- * third argument, and returns the number of bytes written into this
- * buffer. The dirents returned are of variable length, and the
- * d_reclen member gives the length, in bytes, of each entry in the
- * buffer. Currently only the ext2fs filesystem ever returns more
- * than one dirent, but all filesystems return the number of bytes
- * written.
- *
- * For compatibility with older kernels, the library must still accept
- * return values less than the length of a dirent.
- */
- struct dirent *readdir(DIR * dir)
- {
- int result;
- int count;
- struct dirent *de;
- char *p;
-
- if (!dir) {
- errno = EBADF;
- return NULL;
- }
-
- repeat:
- if (dir->dd_max == 1 || dir->dd_size <= dir->dd_loc) {
- count = dir->dd_max;
- #if defined(__PIC__) || defined (__pic__)
- __asm__ volatile ("pushl %%ebx\n\t"
- "movl %%esi,%%ebx\n\t"
- "int $0x80\n\t"
- "popl %%ebx"
- :"=a" (result)
- :"0" (SYS_readdir),"S" (dir->dd_fd),
- "c" ((long) dir->dd_buf),"d" (count));
- #else
- __asm__("int $0x80"
- :"=a" (result)
- :"0" (SYS_readdir),"b" (dir->dd_fd),
- "c" ((long) dir->dd_buf),"d" (count));
- #endif
- if (result <= 0) {
- if (result < 0)
- errno = -result;
- return NULL;
- }
-
- if (result > dir->dd_max) {
- /* This should never happen on modern multi-dirent kernels.
- If it does occur, resort to old-style one-at-a-time dirent
- access, and assume the kernel has returned only one dirent
- this time. */
- result = 1;
- dir->dd_max = 1;
- }
- dir->dd_size = result;
- dir->dd_loc = 0;
- }
-
- de = (struct dirent *) (((char *)dir->dd_buf) + dir->dd_loc);
-
- /* Do some sanity checks to getting confused by old kernels */
- p = (char *) de;
- p += de->d_off;
- if (dir->dd_max > 1 &&
- (p > (((char *) dir->dd_buf) + dir->dd_size) ||
- de->d_reclen < 1 ||
- de->d_off < (NAME_OFFSET(de) + de->d_reclen))) {
- /* If the kernel doesn't appear to adhere to modern readdir()
- semantics, only rely on it to return one dirent at a time. */
- dir->dd_max = 1;
- dir->dd_size = 0;
- if (dir->dd_loc) {
- dir->dd_size = 0;
- goto repeat;
- }
- }
-
- dir->dd_loc += de->d_off;
- return de;
- }
-