1 /* Copyright (C) 1993, 1995-2003, 2004, 2006 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>
31 #include <sys/syscall.h>
32 #include <bp-checks.h>
34 #include <linux/posix_types.h>
36 #include <kernel-features.h>
38 #ifdef __NR_getdents64
39 # ifndef __ASSUME_GETDENTS64_SYSCALL
41 /* The variable is shared between all *getdents* calls. */
42 int __have_no_getdents64 attribute_hidden
;
44 extern int __have_no_getdents64 attribute_hidden
;
46 # define have_no_getdents64_defined 1
49 #ifndef have_no_getdents64_defined
50 # define __have_no_getdents64 0
53 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
55 /* For Linux we need a special version of this file since the
56 definition of `struct dirent' is not the same for the kernel and
57 the libc. There is one additional field which might be introduced
58 in the kernel structure in the future.
60 Here is the kernel definition of `struct dirent' as of 2.1.20: */
66 unsigned short int d_reclen
;
70 struct kernel_dirent64
74 unsigned short int d_reclen
;
80 # define __GETDENTS __getdents
83 # define DIRENT_TYPE struct dirent
85 #ifndef DIRENT_SET_DP_INO
86 # define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value)
89 /* The problem here is that we cannot simply read the next NBYTES
90 bytes. We need to take the additional field into account. We use
91 some heuristic. Assuming the directory contains names with 14
92 characters on average we can compute an estimated number of entries
93 which fit in the buffer. Taking this number allows us to specify a
94 reasonable number of bytes to read. If we should be wrong, we can
95 reset the file descriptor. In practice the kernel is limiting the
96 amount of data returned much more then the reduced buffer size. */
99 __GETDENTS (int fd
, char *buf
, size_t nbytes
)
103 #ifdef __ASSUME_GETDENTS32_D_TYPE
104 if (sizeof (DIRENT_TYPE
) == sizeof (struct dirent
))
106 retval
= INLINE_SYSCALL (getdents
, 3, fd
, CHECK_N(buf
, nbytes
), nbytes
);
108 /* The kernel added the d_type value after the name. Change
114 struct kernel_dirent k
;
116 } *kbuf
= (void *) buf
;
118 while ((char *) kbuf
< buf
+ retval
)
120 char d_type
= *((char *) kbuf
+ kbuf
->k
.d_reclen
- 1);
121 memmove (kbuf
->u
.d_name
, kbuf
->k
.d_name
,
122 strlen (kbuf
->k
.d_name
) + 1);
123 kbuf
->u
.d_type
= d_type
;
125 kbuf
= (void *) ((char *) kbuf
+ kbuf
->k
.d_reclen
);
133 off64_t last_offset
= -1;
135 #ifdef __NR_getdents64
136 if (!__have_no_getdents64
)
138 # ifndef __ASSUME_GETDENTS64_SYSCALL
139 int saved_errno
= errno
;
143 struct kernel_dirent64 k
;
146 } *kbuf
= (void *) buf
, *outp
, *inp
;
147 size_t kbytes
= nbytes
;
148 if (offsetof (DIRENT_TYPE
, d_name
)
149 < offsetof (struct kernel_dirent64
, d_name
)
150 && nbytes
<= sizeof (DIRENT_TYPE
))
152 kbytes
= nbytes
+ offsetof (struct kernel_dirent64
, d_name
)
153 - offsetof (DIRENT_TYPE
, d_name
);
154 kbuf
= __alloca(kbytes
);
156 retval
= INLINE_SYSCALL (getdents64
, 3, fd
, CHECK_N(kbuf
, kbytes
),
158 # ifndef __ASSUME_GETDENTS64_SYSCALL
159 if (retval
!= -1 || (errno
!= EINVAL
&& errno
!= ENOSYS
))
162 const size_t size_diff
= (offsetof (struct kernel_dirent64
, d_name
)
163 - offsetof (DIRENT_TYPE
, d_name
));
165 /* Return the error if encountered. */
169 /* If the structure returned by the kernel is identical to what we
170 need, don't do any conversions. */
171 if (offsetof (DIRENT_TYPE
, d_name
)
172 == offsetof (struct kernel_dirent64
, d_name
)
173 && sizeof (outp
->u
.d_ino
) == sizeof (inp
->k
.d_ino
)
174 && sizeof (outp
->u
.d_off
) == sizeof (inp
->k
.d_off
))
177 /* These two pointers might alias the same memory buffer.
178 Standard C requires that we always use the same type for them,
179 so we must use the union type. */
183 while (&inp
->b
< &kbuf
->b
+ retval
)
185 const size_t alignment
= __alignof__ (DIRENT_TYPE
);
186 /* Since inp->k.d_reclen is already aligned for the kernel
187 structure this may compute a value that is bigger
189 size_t old_reclen
= inp
->k
.d_reclen
;
190 size_t new_reclen
= ((old_reclen
- size_diff
+ alignment
- 1)
193 /* Copy the data out of the old structure into temporary space.
194 Then copy the name, which may overlap if BUF == KBUF. */
195 const uint64_t d_ino
= inp
->k
.d_ino
;
196 const int64_t d_off
= inp
->k
.d_off
;
197 const uint8_t d_type
= inp
->k
.d_type
;
199 memmove (outp
->u
.d_name
, inp
->k
.d_name
,
200 old_reclen
- offsetof (struct kernel_dirent64
, d_name
));
202 /* Now we have copied the data from INP and access only OUTP. */
204 DIRENT_SET_DP_INO (&outp
->u
, d_ino
);
205 outp
->u
.d_off
= d_off
;
206 if ((sizeof (outp
->u
.d_ino
) != sizeof (inp
->k
.d_ino
)
207 && outp
->u
.d_ino
!= d_ino
)
208 || (sizeof (outp
->u
.d_off
) != sizeof (inp
->k
.d_off
)
209 && outp
->u
.d_off
!= d_off
))
211 /* Overflow. If there was at least one entry
212 before this one, return them without error,
213 otherwise signal overflow. */
214 if (last_offset
!= -1)
216 __lseek64 (fd
, last_offset
, SEEK_SET
);
217 return outp
->b
- buf
;
219 __set_errno (EOVERFLOW
);
224 outp
->u
.d_reclen
= new_reclen
;
225 outp
->u
.d_type
= d_type
;
227 inp
= (void *) inp
+ old_reclen
;
228 outp
= (void *) outp
+ new_reclen
;
231 return outp
->b
- buf
;
234 # ifndef __ASSUME_GETDENTS64_SYSCALL
235 __set_errno (saved_errno
);
236 __have_no_getdents64
= 1;
242 struct kernel_dirent
*skdp
, *kdp
;
243 const size_t size_diff
= (offsetof (DIRENT_TYPE
, d_name
)
244 - offsetof (struct kernel_dirent
, d_name
));
246 red_nbytes
= MIN (nbytes
247 - ((nbytes
/ (offsetof (DIRENT_TYPE
, d_name
) + 14))
251 skdp
= kdp
= __alloca (red_nbytes
);
253 retval
= INLINE_SYSCALL (getdents
, 3, fd
,
254 CHECK_N ((char *) kdp
, red_nbytes
), red_nbytes
);
259 DIRENT_TYPE
*dp
= (DIRENT_TYPE
*) buf
;
260 while ((char *) kdp
< (char *) skdp
+ retval
)
262 const size_t alignment
= __alignof__ (DIRENT_TYPE
);
263 /* Since kdp->d_reclen is already aligned for the kernel structure
264 this may compute a value that is bigger than necessary. */
265 size_t new_reclen
= ((kdp
->d_reclen
+ size_diff
+ alignment
- 1)
267 if ((char *) dp
+ new_reclen
> buf
+ nbytes
)
269 /* Our heuristic failed. We read too many entries. Reset
271 assert (last_offset
!= -1);
272 __lseek64 (fd
, last_offset
, SEEK_SET
);
274 if ((char *) dp
== buf
)
276 /* The buffer the user passed in is too small to hold even
278 __set_errno (EINVAL
);
285 last_offset
= kdp
->d_off
;
286 DIRENT_SET_DP_INO(dp
, kdp
->d_ino
);
287 dp
->d_off
= kdp
->d_off
;
288 dp
->d_reclen
= new_reclen
;
289 dp
->d_type
= DT_UNKNOWN
;
290 memcpy (dp
->d_name
, kdp
->d_name
,
291 kdp
->d_reclen
- offsetof (struct kernel_dirent
, d_name
));
293 dp
= (DIRENT_TYPE
*) ((char *) dp
+ new_reclen
);
294 kdp
= (struct kernel_dirent
*) (((char *) kdp
) + kdp
->d_reclen
);
297 return (char *) dp
- buf
;