diff options
author | Ulrich Drepper <drepper@redhat.com> | 2000-08-14 17:41:59 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2000-08-14 17:41:59 +0000 |
commit | 14860991fcb0bc8ccb3bbb62a12df07cd222af4d (patch) | |
tree | e9f53b98c96490d66ae6d037856f5f6a997144db /sysdeps/unix/sysv/linux/getdents.c | |
parent | 8c2f6130c331614a8b14522c519b31b8d12ca2eb (diff) | |
download | glibc-14860991fcb0bc8ccb3bbb62a12df07cd222af4d.tar.gz glibc-14860991fcb0bc8ccb3bbb62a12df07cd222af4d.tar.xz glibc-14860991fcb0bc8ccb3bbb62a12df07cd222af4d.zip |
Update.
2000-08-14 Jakub Jelinek <jakub@redhat.com> * dirent/Versions (getdirentries64): Export at GLIBC_2.2. * sysdeps/unix/sysv/linux/kernel-features.h (__ASSUME_GETDENTS64_SYSCALL): Define. * sysdeps/unix/sysv/linux/getdents.c (__getdents): Use getdents64 syscall if available to get d_type fields. * sysdeps/unix/sysv/linux/alpha/getdents.c (DIRENT_TYPE): Define. * sysdeps/unix/sysv/linux/arm/Versions (__xstat64, __fxstat64, __lxstat64): Export at GLIBC_2.2. (alphasort64, readdir64, readdir64_r, scandir64, versionsort64): Likewise. * sysdeps/unix/sysv/linux/i386/Versions (getdirentries64): Remove. * sysdeps/unix/sysv/linux/i386/getdents64.c (kernel_dirent64): Define. * sysdeps/unix/sysv/linux/powerpc/Versions (alphasort64, getdirentries64, versionsort64): Remove. * sysdeps/unix/sysv/linux/sparc/sparc32/Versions (alphasort64, getdirentries64, versionsort64): Remove.
Diffstat (limited to 'sysdeps/unix/sysv/linux/getdents.c')
-rw-r--r-- | sysdeps/unix/sysv/linux/getdents.c | 212 |
1 files changed, 164 insertions, 48 deletions
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c index 474bf1989b..19ab9238fe 100644 --- a/sysdeps/unix/sysv/linux/getdents.c +++ b/sysdeps/unix/sysv/linux/getdents.c @@ -32,6 +32,19 @@ #include <linux/posix_types.h> +#include "kernel-features.h" + +#ifdef __NR_getdents64 +#ifndef __ASSUME_GETDENTS64_SYSCALL +#ifndef __GETDENTS +/* The variable is shared between all *getdents* calls. */ +int __have_no_getdents64; +#else +extern int __have_no_getdents64; +#endif +#endif +#endif + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) extern int __syscall_getdents (int fd, char *__unbounded buf, size_t nbytes); @@ -51,8 +64,19 @@ struct kernel_dirent char d_name[256]; }; +struct kernel_dirent64 + { + u_int64_t d_ino; + int64_t d_off; + unsigned short int d_reclen; + unsigned char d_type; + char d_name[256]; + }; + #ifndef __GETDENTS # define __GETDENTS __getdents +#endif +#ifndef DIRENT_TYPE # define DIRENT_TYPE struct dirent #endif #ifndef DIRENT_SET_DP_INO @@ -71,63 +95,155 @@ ssize_t internal_function __GETDENTS (int fd, char *buf, size_t nbytes) { - off_t last_offset = -1; - size_t red_nbytes; - struct kernel_dirent *skdp, *kdp; DIRENT_TYPE *dp; - int retval; - const size_t size_diff = (offsetof (DIRENT_TYPE, d_name) - - offsetof (struct kernel_dirent, d_name)); - - red_nbytes = MIN (nbytes - - ((nbytes / (offsetof (DIRENT_TYPE, d_name) + 14)) - * size_diff), - nbytes - size_diff); - - dp = (DIRENT_TYPE *) buf; - skdp = kdp = __alloca (red_nbytes); - - retval = INLINE_SYSCALL (getdents, 3, fd, - CHECK_N ((char *) kdp, red_nbytes), red_nbytes); - - if (retval == -1) - return -1; + off_t last_offset = -1; + ssize_t retval; - while ((char *) kdp < (char *) skdp + retval) +#ifdef __NR_getdents64 +#ifndef __ASSUME_GETDENTS64_SYSCALL + if (!__have_no_getdents64) +#endif { - const size_t alignment = __alignof__ (DIRENT_TYPE); - /* 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) +#ifndef __ASSUME_GETDENTS64_SYSCALL + int saved_errno = errno; +#endif + char *kbuf = buf; + size_t kbytes = nbytes; + if (offsetof (DIRENT_TYPE, d_name) + < offsetof (struct kernel_dirent64, d_name) + && nbytes <= sizeof (DIRENT_TYPE)) { - /* Our heuristic failed. We read too many entries. Reset - the stream. */ - assert (last_offset != -1); - __lseek (fd, last_offset, SEEK_SET); - - if ((char *) dp == buf) + kbytes = nbytes + offsetof (struct kernel_dirent64, d_name) + - offsetof (DIRENT_TYPE, d_name); + kbuf = __alloca(kbytes); + } + retval = INLINE_SYSCALL (getdents64, 3, fd, CHECK_N(kbuf, kbytes), + kbytes); +#ifndef __ASSUME_GETDENTS64_SYSCALL + if (retval != -1 && errno != -EINVAL) +#endif + { + struct kernel_dirent64 *kdp; + const size_t size_diff = (offsetof (struct kernel_dirent64, d_name) + - offsetof (DIRENT_TYPE, d_name)); + + /* If the structure returned by the kernel is identical to what we + need, don't do any conversions. */ + if (offsetof (DIRENT_TYPE, d_name) + == offsetof (struct kernel_dirent64, d_name) + && sizeof (dp->d_ino) == sizeof (kdp->d_ino) + && sizeof (dp->d_off) == sizeof (kdp->d_off)) + return retval; + + dp = (DIRENT_TYPE *)buf; + kdp = (struct kernel_dirent64 *)kbuf; + while ((char *) kdp < kbuf + retval) { - /* The buffer the user passed in is too small to hold even - one entry. */ - __set_errno (EINVAL); - return -1; + const size_t alignment = __alignof__ (DIRENT_TYPE); + /* Since kdp->d_reclen is already aligned for the kernel + structure this may compute a value that is bigger + than necessary. */ + size_t old_reclen = kdp->d_reclen; + size_t new_reclen = ((old_reclen - size_diff + alignment - 1) + & ~(alignment - 1)); + u_int64_t d_ino = kdp->d_ino; + int64_t d_off = kdp->d_off; + unsigned char d_type = kdp->d_type; + + DIRENT_SET_DP_INO(dp, d_ino); + dp->d_off = d_off; + if ((sizeof (dp->d_ino) != sizeof (kdp->d_ino) + && dp->d_ino != d_ino) + || (sizeof (dp->d_off) != sizeof (kdp->d_off) + && dp->d_off != d_off)) + { + /* Overflow. If there was at least one entry + before this one, return them without error, + otherwise signal overflow. */ + if (last_offset != -1) + { + __lseek (fd, last_offset, SEEK_SET); + return (char *) dp - buf; + } + __set_errno (EOVERFLOW); + return -1; + } + + last_offset = d_off; + dp->d_reclen = new_reclen; + dp->d_type = d_type; + memmove (dp->d_name, kdp->d_name, + old_reclen - offsetof (struct kernel_dirent64, d_name)); + + dp = (DIRENT_TYPE *) ((char *) dp + new_reclen); + kdp = (struct kernel_dirent64 *) ((char *) kdp + old_reclen); } - break; + return (char *) dp - buf; } - last_offset = kdp->d_off; - DIRENT_SET_DP_INO(dp, kdp->d_ino); - dp->d_off = kdp->d_off; - dp->d_reclen = new_reclen; - dp->d_type = DT_UNKNOWN; - memcpy (dp->d_name, kdp->d_name, - kdp->d_reclen - offsetof (struct kernel_dirent, d_name)); - - dp = (DIRENT_TYPE *) ((char *) dp + new_reclen); - kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen); +#ifndef __ASSUME_GETDENTS64_SYSCALL + __set_errno (saved_errno); + __have_no_getdents64 = 1; +#endif + } +#endif + { + size_t red_nbytes; + struct kernel_dirent *skdp, *kdp; + const size_t size_diff = (offsetof (DIRENT_TYPE, d_name) + - offsetof (struct kernel_dirent, d_name)); + + red_nbytes = MIN (nbytes + - ((nbytes / (offsetof (DIRENT_TYPE, d_name) + 14)) + * size_diff), + nbytes - size_diff); + + dp = (DIRENT_TYPE *) buf; + skdp = kdp = __alloca (red_nbytes); + + retval = INLINE_SYSCALL (getdents, 3, fd, + CHECK_N ((char *) kdp, red_nbytes), red_nbytes); + + if (retval == -1) + return -1; + + while ((char *) kdp < (char *) skdp + retval) + { + const size_t alignment = __alignof__ (DIRENT_TYPE); + /* 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. */ + assert (last_offset != -1); + __lseek (fd, last_offset, SEEK_SET); + + if ((char *) dp == buf) + { + /* The buffer the user passed in is too small to hold even + one entry. */ + __set_errno (EINVAL); + return -1; + } + + break; + } + + last_offset = kdp->d_off; + DIRENT_SET_DP_INO(dp, kdp->d_ino); + dp->d_off = kdp->d_off; + dp->d_reclen = new_reclen; + dp->d_type = DT_UNKNOWN; + memcpy (dp->d_name, kdp->d_name, + kdp->d_reclen - offsetof (struct kernel_dirent, d_name)); + + dp = (DIRENT_TYPE *) ((char *) dp + new_reclen); + kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen); + } } return (char *) dp - buf; |