(CFLAGS-tst-align.c): Add -mpreferred-stack-boundary=4.
[glibc.git] / sysdeps / unix / sysv / linux / getdents.c
blobd473486de8f3335c7c5109a31249d35e6df91e0c
1 /* Copyright (C) 1993, 1995-2003, 2004 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
17 02111-1307 USA. */
19 #include <alloca.h>
20 #include <assert.h>
21 #include <errno.h>
22 #include <dirent.h>
23 #include <stddef.h>
24 #include <stdint.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/param.h>
28 #include <sys/types.h>
30 #include <sysdep.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
40 # ifndef __GETDENTS
41 /* The variable is shared between all *getdents* calls. */
42 int __have_no_getdents64;
43 # else
44 extern int __have_no_getdents64;
45 # endif
46 # define have_no_getdents64_defined 1
47 # endif
48 #endif
49 #ifndef have_no_getdents64_defined
50 # define __have_no_getdents64 0
51 #endif
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: */
62 struct kernel_dirent
64 long int d_ino;
65 __kernel_off_t d_off;
66 unsigned short int d_reclen;
67 char d_name[256];
70 struct kernel_dirent64
72 uint64_t d_ino;
73 int64_t d_off;
74 unsigned short int d_reclen;
75 unsigned char d_type;
76 char d_name[256];
79 #ifndef __GETDENTS
80 # define __GETDENTS __getdents
81 #endif
82 #ifndef DIRENT_TYPE
83 # define DIRENT_TYPE struct dirent
84 #endif
85 #ifndef DIRENT_SET_DP_INO
86 # define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value)
87 #endif
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. */
97 ssize_t
98 internal_function
99 __GETDENTS (int fd, char *buf, size_t nbytes)
101 ssize_t retval;
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
109 this now. */
110 if (retval != -1)
112 union
114 struct kernel_dirent k;
115 struct dirent u;
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);
129 return retval;
131 #endif
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;
140 # endif
141 union
143 struct kernel_dirent64 k;
144 DIRENT_TYPE u;
145 char b[1];
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),
157 kbytes);
158 # ifndef __ASSUME_GETDENTS64_SYSCALL
159 if (retval != -1 || (errno != EINVAL && errno != ENOSYS))
160 # endif
162 const size_t size_diff = (offsetof (struct kernel_dirent64, d_name)
163 - offsetof (DIRENT_TYPE, d_name));
165 /* Return the error if encountered. */
166 if (retval == -1)
167 return -1;
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))
175 return retval;
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. */
180 inp = kbuf;
181 outp = (void *) buf;
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
188 than necessary. */
189 size_t old_reclen = inp->k.d_reclen;
190 size_t new_reclen = ((old_reclen - size_diff + alignment - 1)
191 & ~(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);
220 return -1;
223 last_offset = d_off;
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;
237 # endif
239 #endif
241 size_t red_nbytes;
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))
248 * size_diff),
249 nbytes - size_diff);
251 skdp = kdp = __alloca (red_nbytes);
253 retval = INLINE_SYSCALL (getdents, 3, fd,
254 CHECK_N ((char *) kdp, red_nbytes), red_nbytes);
256 if (retval == -1)
257 return -1;
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)
266 & ~(alignment - 1));
267 if ((char *) dp + new_reclen > buf + nbytes)
269 /* Our heuristic failed. We read too many entries. Reset
270 the stream. */
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
277 one entry. */
278 __set_errno (EINVAL);
279 return -1;
282 break;
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;