1 /* Copyright (C) 1993, 1995-2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 #include <sys/param.h>
29 #include <sys/types.h>
30 #include <sys/syscall.h>
32 libc_hidden_proto(memcpy)
33 libc_hidden_proto(lseek64)
35 #if defined __UCLIBC_HAS_LFS__ && defined __NR_getdents64
38 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
41 struct kernel_dirent64
45 unsigned short d_reclen;
51 #define __NR___syscall_getdents64 __NR_getdents64
52 static inline _syscall3(int, __syscall_getdents64, int, fd, unsigned char *, dirp, size_t, count);
55 ssize_t attribute_hidden __getdents64 (int fd, char *buf, size_t nbytes)
58 off64_t last_offset = -1;
61 struct kernel_dirent64 *skdp, *kdp;
62 const size_t size_diff = (offsetof (struct dirent64, d_name)
63 - offsetof (struct kernel_dirent64, d_name));
65 red_nbytes = MIN (nbytes - ((nbytes /
66 (offsetof (struct dirent64, d_name) + 14)) * size_diff),
69 dp = (struct dirent64 *) buf;
70 skdp = kdp = alloca (red_nbytes);
72 retval = __syscall_getdents64(fd, (char *)kdp, red_nbytes);
76 while ((char *) kdp < (char *) skdp + retval) {
77 const size_t alignment = __alignof__ (struct dirent64);
78 /* Since kdp->d_reclen is already aligned for the kernel structure
79 this may compute a value that is bigger than necessary. */
80 size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
82 if ((char *) dp + new_reclen > buf + nbytes) {
83 /* Our heuristic failed. We read too many entries. Reset
85 assert (last_offset != -1);
86 lseek64(fd, last_offset, SEEK_SET);
88 if ((char *) dp == buf) {
89 /* The buffer the user passed in is too small to hold even
97 last_offset = kdp->d_off;
98 dp->d_ino = kdp->d_ino;
99 dp->d_off = kdp->d_off;
100 dp->d_reclen = new_reclen;
101 dp->d_type = DT_UNKNOWN;
102 memcpy (dp->d_name, kdp->d_name,
103 kdp->d_reclen - offsetof (struct kernel_dirent64, d_name));
104 dp = (struct dirent64 *) ((char *) dp + new_reclen);
105 kdp = (struct kernel_dirent64 *) (((char *) kdp) + kdp->d_reclen);
107 return (char *) dp - buf;
110 extern ssize_t __getdents (int fd, char *buf, size_t nbytes) attribute_hidden;
111 ssize_t attribute_hidden __getdents64 (int fd, char *buf, size_t nbytes)
113 return(__getdents(fd, buf, nbytes));
115 #endif /* __UCLIBC_HAS_LFS__ */