2 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
16 #include <sys/param.h>
17 #include <sys/types.h>
18 #include <sys/syscall.h>
19 #include <bits/kernel_types.h>
21 #if defined __UCLIBC_HAS_LFS__ && defined __NR_getdents64
24 # define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
27 struct kernel_dirent64
31 unsigned short d_reclen;
36 # define __NR___syscall_getdents64 __NR_getdents64
37 static __inline__ _syscall3(int, __syscall_getdents64, int, fd, unsigned char *, dirp, size_t, count)
39 ssize_t __getdents64 (int fd, char *buf, size_t nbytes) attribute_hidden;
40 ssize_t __getdents64 (int fd, char *buf, size_t nbytes)
43 off64_t last_offset = -1;
46 struct kernel_dirent64 *skdp, *kdp;
47 const size_t size_diff = (offsetof (struct dirent64, d_name)
48 - offsetof (struct kernel_dirent64, d_name));
50 red_nbytes = MIN (nbytes - ((nbytes /
51 (offsetof (struct dirent64, d_name) + 14)) * size_diff),
54 dp = (struct dirent64 *) buf;
55 skdp = kdp = alloca (red_nbytes);
57 retval = __syscall_getdents64(fd, (unsigned char *)kdp, red_nbytes);
61 while ((char *) kdp < (char *) skdp + retval) {
62 const size_t alignment = __alignof__ (struct dirent64);
63 /* Since kdp->d_reclen is already aligned for the kernel structure
64 this may compute a value that is bigger than necessary. */
65 size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
67 if ((char *) dp + new_reclen > buf + nbytes) {
68 /* Our heuristic failed. We read too many entries. Reset
70 assert (last_offset != -1);
71 lseek64(fd, last_offset, SEEK_SET);
73 if ((char *) dp == buf) {
74 /* The buffer the user passed in is too small to hold even
82 last_offset = kdp->d_off;
83 dp->d_ino = kdp->d_ino;
84 dp->d_off = kdp->d_off;
85 dp->d_reclen = new_reclen;
86 dp->d_type = kdp->d_type;
87 memcpy (dp->d_name, kdp->d_name,
88 kdp->d_reclen - offsetof (struct kernel_dirent64, d_name));
89 dp = (struct dirent64 *) ((char *) dp + new_reclen);
90 kdp = (struct kernel_dirent64 *) (((char *) kdp) + kdp->d_reclen);
92 return (char *) dp - buf;
96 /* since getdents doesnt give us d_type but getdents64 does, try and
97 * use getdents64 as much as possible */
98 attribute_hidden strong_alias(__getdents64,__getdents)