summaryrefslogtreecommitdiffstats
path: root/libc/sysdeps/linux/common/getdents.c
diff options
context:
space:
mode:
Diffstat (limited to 'libc/sysdeps/linux/common/getdents.c')
-rw-r--r--libc/sysdeps/linux/common/getdents.c44
1 files changed, 36 insertions, 8 deletions
diff --git a/libc/sysdeps/linux/common/getdents.c b/libc/sysdeps/linux/common/getdents.c
index 11c2570c4..55c8f85ee 100644
--- a/libc/sysdeps/linux/common/getdents.c
+++ b/libc/sysdeps/linux/common/getdents.c
@@ -15,10 +15,10 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/syscall.h>
+#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.
@@ -38,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
-
-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);
+#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)
+
ssize_t __getdents (int fd, char *buf, size_t nbytes)
{
struct dirent *dp;
@@ -107,7 +135,7 @@ attribute_hidden strong_alias(__getdents,__getdents64)
#elif __WORDSIZE == 32
-libc_hidden_proto(memmove)
+/* Experimentally off - libc_hidden_proto(memmove) */
extern __typeof(__getdents) __getdents64 attribute_hidden;
ssize_t __getdents (int fd, char *buf, size_t nbytes)