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
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/syscall.h>
31 libc_hidden_proto(memcpy)
32 libc_hidden_proto(lseek)
35 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
42 unsigned short d_reclen;
46 #define __NR___syscall_getdents __NR_getdents
47 static inline _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count);
50 ssize_t attribute_hidden __getdents (int fd, char *buf, size_t nbytes)
53 off_t last_offset = -1;
56 struct kernel_dirent *skdp, *kdp;
57 const size_t size_diff = (offsetof (struct dirent, d_name)
58 - offsetof (struct kernel_dirent, d_name));
60 red_nbytes = MIN (nbytes - ((nbytes /
61 (offsetof (struct dirent, d_name) + 14)) * size_diff),
64 dp = (struct dirent *) buf;
65 skdp = kdp = alloca (red_nbytes);
67 retval = __syscall_getdents(fd, (char *)kdp, red_nbytes);
71 while ((char *) kdp < (char *) skdp + retval) {
72 const size_t alignment = __alignof__ (struct dirent);
73 /* Since kdp->d_reclen is already aligned for the kernel structure
74 this may compute a value that is bigger than necessary. */
75 size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
77 if ((char *) dp + new_reclen > buf + nbytes) {
78 /* Our heuristic failed. We read too many entries. Reset
80 assert (last_offset != -1);
81 lseek(fd, last_offset, SEEK_SET);
83 if ((char *) dp == buf) {
84 /* The buffer the user passed in is too small to hold even
92 last_offset = kdp->d_off;
93 dp->d_ino = kdp->d_ino;
94 dp->d_off = kdp->d_off;
95 dp->d_reclen = new_reclen;
96 dp->d_type = DT_UNKNOWN;
97 memcpy (dp->d_name, kdp->d_name,
98 kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
99 dp = (struct dirent *) ((char *) dp + new_reclen);
100 kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
102 return (char *) dp - buf;