1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
#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;
}
|