diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/unix/sysv/linux/alpha/getdents.c | 1 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/arm/Versions | 15 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/getdents.c | 212 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/i386/Versions | 2 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/i386/getdents64.c | 1 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/kernel-features.h | 6 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/powerpc/Versions | 8 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/sparc/sparc32/Versions | 9 |
8 files changed, 187 insertions, 67 deletions
diff --git a/sysdeps/unix/sysv/linux/alpha/getdents.c b/sysdeps/unix/sysv/linux/alpha/getdents.c index 6deb87e2e4..175be9df85 100644 --- a/sysdeps/unix/sysv/linux/alpha/getdents.c +++ b/sysdeps/unix/sysv/linux/alpha/getdents.c @@ -1,3 +1,4 @@ +#define DIRENT_TYPE struct dirent64 #define DIRENT_SET_DP_INO(dp, value) \ do { (dp)->d_ino = (value); (dp)->__pad = 0; } while (0) #define __getdents64 __no___getdents64_decl diff --git a/sysdeps/unix/sysv/linux/arm/Versions b/sysdeps/unix/sysv/linux/arm/Versions index 4ac5b58a9b..5498086253 100644 --- a/sysdeps/unix/sysv/linux/arm/Versions +++ b/sysdeps/unix/sysv/linux/arm/Versions @@ -11,7 +11,22 @@ libc { outb; outw; outl; } GLIBC_2.2 { + # functions used in other libraries + __xstat64; __fxstat64; __lxstat64; + + # a* + alphasort64; + # New rlimit interface getrlimit; setrlimit; getrlimit64; + + # r* + readdir64; readdir64_r; + + # s* + scandir64; + + # v* + versionsort64; } } 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; diff --git a/sysdeps/unix/sysv/linux/i386/Versions b/sysdeps/unix/sysv/linux/i386/Versions index 58c7b3d9b1..b7af24b1b3 100644 --- a/sysdeps/unix/sysv/linux/i386/Versions +++ b/sysdeps/unix/sysv/linux/i386/Versions @@ -19,8 +19,6 @@ libc { # a* alphasort64; - # g* - getdirentries64; # New rlimit interface getrlimit; setrlimit; getrlimit64; diff --git a/sysdeps/unix/sysv/linux/i386/getdents64.c b/sysdeps/unix/sysv/linux/i386/getdents64.c index dac046fa0c..bbfff20cf0 100644 --- a/sysdeps/unix/sysv/linux/i386/getdents64.c +++ b/sysdeps/unix/sysv/linux/i386/getdents64.c @@ -36,6 +36,7 @@ versioned_symbol (libc, __getdents64, getdents64, GLIBC_2_2); #define __GETDENTS __old_getdents64 #define DIRENT_TYPE struct __old_dirent64 #define kernel_dirent old_kernel_dirent +#define kernel_dirent64 old_kernel_dirent64 #include <sysdeps/unix/sysv/linux/getdents.c> diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h index e7699a62f8..2f9d12bb28 100644 --- a/sysdeps/unix/sysv/linux/kernel-features.h +++ b/sysdeps/unix/sysv/linux/kernel-features.h @@ -136,3 +136,9 @@ #if __LINUX_KERNEL_VERSION >= 132097 && (defined __i386__ || defined __sparc__) # define __ASSUME_FCNTL64 1 #endif + +/* The getdents64 syscall was introduced in 2.4.0-test7. We test for + 2.4.1 for the earliest version we know the syscall is available. */ +#if __LINUX_KERNEL_VERSION >= 132097 +# define __ASSUME_GETDENTS64_SYSCALL 1 +#endif diff --git a/sysdeps/unix/sysv/linux/powerpc/Versions b/sysdeps/unix/sysv/linux/powerpc/Versions index 6466be2cc5..1ea93d74f3 100644 --- a/sysdeps/unix/sysv/linux/powerpc/Versions +++ b/sysdeps/unix/sysv/linux/powerpc/Versions @@ -9,11 +9,6 @@ libc { # functions used in other libraries __xstat64; __fxstat64; __lxstat64; - # a* - alphasort64; - - # g* - getdirentries64; # New rlimit interface getrlimit; setrlimit; getrlimit64; setrlimit64; @@ -22,8 +17,5 @@ libc { # s* scandir64; - - # v* - versionsort64; } } diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/Versions b/sysdeps/unix/sysv/linux/sparc/sparc32/Versions index fcb9df31ec..2448fa2d37 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/Versions +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/Versions @@ -9,19 +9,10 @@ libc { # functions used in other libraries __xstat64; __fxstat64; __lxstat64; - # a* - alphasort64; - - # g* - getdirentries64; - # r* readdir64; readdir64_r; # s* scandir64; - - # v* - versionsort64; } } |