Stephen C. Tweedie wrote:
> > Which is unfortunate because the computed LSEEK_CUR appears in Glibc's
> > readdir(). It's a corner case. To remove the LSEEK_CUR, unfortunately
> > you have to use another syscall in the normal case.
>
> What is the problem? You just cannot assume that the pointer
> progresses in a nice, arithmetic patter in directories. If you are
> at lseek() point P and read Q bytes, you are not going to end up
> at P+Q: the actual file location you end up at depends on how much
> padding there was in the directory and how much extra filesystem
> metadata was there that didn't get returned to user space.
Glibc assumes you end up at P+Q, so it is broken. It's a corner
case that won't have shown up in tests. Here's the code from
glibc-2.1-990920/sysdeps/unix/sysv/linux/getdents.c:
retval = INLINE_SYSCALL (getdents, 3, fd, (char *) kdp, red_nbytes);
if (retval == -1)
return -1;
while ((char *) kdp < (char *) skdp + retval)
{
const size_t alignment = __alignof__ (struct dirent);
/* Since kdp->d_reclen is already aligned for the kernel structure
this may compute a value that is bigger than necessary. */
size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
& ~(alignment - 1));
if ((char *) dp + new_reclen > buf + nbytes)
{
/* Our heuristic failed. We read too many entries. Reset
the stream. `last_offset' contains the last known
position. If it is zero this is the first record we are
reading. In this case do a relative search. */
if (last_offset == 0)
__lseek (fd, -retval, SEEK_CUR);
else
__lseek (fd, last_offset, SEEK_SET);
break;
}
last_offset = kdp->d_off;
Ironically, an older Glibc did this right and it was changed as an
optimisation.
-- Jamie
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/
This archive was generated by hypermail 2b29 : Fri Apr 07 2000 - 21:00:14 EST