diff options
-rw-r--r-- | libc/sysdeps/linux/common/getdents.c | 39 | ||||
-rw-r--r-- | libpthread/linuxthreads/sysdeps/pthread/kernel-features.h | 8 |
2 files changed, 41 insertions, 6 deletions
diff --git a/libc/sysdeps/linux/common/getdents.c b/libc/sysdeps/linux/common/getdents.c index d858eab91..97c6d8b06 100644 --- a/libc/sysdeps/linux/common/getdents.c +++ b/libc/sysdeps/linux/common/getdents.c @@ -18,8 +18,7 @@ #include <bits/kernel_types.h> /* With newer versions of linux, the getdents syscall returns d_type - * information after the name field. Someday, we should add support for - * that instead of always calling getdents64 ... + * information after the name field. * * See __ASSUME_GETDENTS32_D_TYPE in glibc's kernel-features.h for specific * version / arch details. @@ -39,14 +38,42 @@ struct kernel_dirent ssize_t __getdents (int fd, char *buf, size_t nbytes) attribute_hidden; -#if ! defined __UCLIBC_HAS_LFS__ || ! defined __NR_getdents64 +#define __NR___syscall_getdents __NR_getdents +static inline _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count); + +#ifdef __ASSUME_GETDENTS32_D_TYPE +ssize_t __getdents (int fd, char *buf, size_t nbytes) +{ + ssize_t retval; + + retval = __syscall_getdents(fd, (unsigned char *)buf, nbytes); + + /* The kernel added the d_type value after the name. Change + this now. */ + if (retval != -1) { + union { + struct kernel_dirent k; + struct dirent u; + } *kbuf = (void *) buf; + + while ((char *) kbuf < buf + retval) { + char d_type = *((char *) kbuf + kbuf->k.d_reclen - 1); + memmove (kbuf->u.d_name, kbuf->k.d_name, + strlen (kbuf->k.d_name) + 1); + kbuf->u.d_type = d_type; + + kbuf = (void *) ((char *) kbuf + kbuf->k.d_reclen); + } + } + + return retval; +} + +#elif ! defined __UCLIBC_HAS_LFS__ || ! defined __NR_getdents64 /* Experimentally off - libc_hidden_proto(memcpy) */ libc_hidden_proto(lseek) -#define __NR___syscall_getdents __NR_getdents -static __inline__ _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count); - ssize_t __getdents (int fd, char *buf, size_t nbytes) { struct dirent *dp; diff --git a/libpthread/linuxthreads/sysdeps/pthread/kernel-features.h b/libpthread/linuxthreads/sysdeps/pthread/kernel-features.h index 424922ec0..88a71828b 100644 --- a/libpthread/linuxthreads/sysdeps/pthread/kernel-features.h +++ b/libpthread/linuxthreads/sysdeps/pthread/kernel-features.h @@ -62,6 +62,14 @@ # define __ASSUME_VFORK_SYSCALL 1 #endif +/* Starting with version 2.6.4-rc1 the getdents syscall returns d_type + * information as well and in between 2.6.5 and 2.6.8 most compat wrappers + * were fixed too. Except s390{,x} which was fixed in 2.6.11. */ +#if (__LINUX_KERNEL_VERSION >= 0x020608 && !defined __s390__) \ + || (__LINUX_KERNEL_VERSION >= 0x02060b && defined __s390__) +# define __ASSUME_GETDENTS32_D_TYPE 1 +#endif + /* These features were surely available with 2.4.12. */ #if __LINUX_KERNEL_VERSION >= 132108 && defined __mc68000__ # define __ASSUME_MMAP2_SYSCALL 1 |