diff options
Diffstat (limited to 'libc/sysdeps')
-rw-r--r-- | libc/sysdeps/linux/common/getdents.c | 39 |
1 files changed, 33 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; |