1 /* Copyright (C) 1993,95,96,97,98,99,2000,2001 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
;
44 extern int __have_no_getdents64
;
49 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
51 extern int __syscall_getdents (int fd
, char *__unbounded buf
, unsigned int nbytes
);
52 extern int __syscall_getdents64 (int fd
, char *__unbounded buf
, unsigned int nbytes
);
54 /* For Linux we need a special version of this file since the
55 definition of `struct dirent' is not the same for the kernel and
56 the libc. There is one additional field which might be introduced
57 in the kernel structure in the future.
59 Here is the kernel definition of `struct dirent' as of 2.1.20: */
65 unsigned short int d_reclen
;
69 struct kernel_dirent64
73 unsigned short int d_reclen
;
79 # define __GETDENTS __getdents
82 # define DIRENT_TYPE struct dirent
84 #ifndef DIRENT_SET_DP_INO
85 # define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value)
88 /* The problem here is that we cannot simply read the next NBYTES
89 bytes. We need to take the additional field into account. We use
90 some heuristic. Assuming the directory contains names with 14
91 characters on average we can compute an estimated number of entries
92 which fit in the buffer. Taking this number allows us to specify a
93 reasonable number of bytes to read. If we should be wrong, we can
94 reset the file descriptor. In practice the kernel is limiting the
95 amount of data returned much more then the reduced buffer size. */
98 __GETDENTS (int fd
, char *buf
, size_t nbytes
)
101 off64_t last_offset
= -1;
104 #ifdef __NR_getdents64
105 # ifndef __ASSUME_GETDENTS64_SYSCALL
106 if (!__have_no_getdents64
)
109 # ifndef __ASSUME_GETDENTS64_SYSCALL
110 int saved_errno
= errno
;
113 size_t kbytes
= nbytes
;
114 if (offsetof (DIRENT_TYPE
, d_name
)
115 < offsetof (struct kernel_dirent64
, d_name
)
116 && nbytes
<= sizeof (DIRENT_TYPE
))
118 kbytes
= nbytes
+ offsetof (struct kernel_dirent64
, d_name
)
119 - offsetof (DIRENT_TYPE
, d_name
);
120 kbuf
= __alloca(kbytes
);
122 retval
= INLINE_SYSCALL (getdents64
, 3, fd
, CHECK_N(kbuf
, kbytes
),
124 # ifndef __ASSUME_GETDENTS64_SYSCALL
125 if (retval
!= -1 && errno
!= -EINVAL
)
128 struct kernel_dirent64
*kdp
;
129 const size_t size_diff
= (offsetof (struct kernel_dirent64
, d_name
)
130 - offsetof (DIRENT_TYPE
, d_name
));
132 /* If the structure returned by the kernel is identical to what we
133 need, don't do any conversions. */
134 if (offsetof (DIRENT_TYPE
, d_name
)
135 == offsetof (struct kernel_dirent64
, d_name
)
136 && sizeof (dp
->d_ino
) == sizeof (kdp
->d_ino
)
137 && sizeof (dp
->d_off
) == sizeof (kdp
->d_off
))
140 dp
= (DIRENT_TYPE
*)buf
;
141 kdp
= (struct kernel_dirent64
*) kbuf
;
142 while ((char *) kdp
< kbuf
+ retval
)
144 const size_t alignment
= __alignof__ (DIRENT_TYPE
);
145 /* Since kdp->d_reclen is already aligned for the kernel
146 structure this may compute a value that is bigger
148 size_t old_reclen
= kdp
->d_reclen
;
149 size_t new_reclen
= ((old_reclen
- size_diff
+ alignment
- 1)
151 uint64_t d_ino
= kdp
->d_ino
;
152 int64_t d_off
= kdp
->d_off
;
153 unsigned char d_type
= kdp
->d_type
;
155 DIRENT_SET_DP_INO (dp
, d_ino
);
157 if ((sizeof (dp
->d_ino
) != sizeof (kdp
->d_ino
)
158 && dp
->d_ino
!= d_ino
)
159 || (sizeof (dp
->d_off
) != sizeof (kdp
->d_off
)
160 && dp
->d_off
!= d_off
))
162 /* Overflow. If there was at least one entry
163 before this one, return them without error,
164 otherwise signal overflow. */
165 if (last_offset
!= -1)
167 __lseek64 (fd
, last_offset
, SEEK_SET
);
168 return (char *) dp
- buf
;
170 __set_errno (EOVERFLOW
);
175 dp
->d_reclen
= new_reclen
;
177 memmove (dp
->d_name
, kdp
->d_name
,
178 old_reclen
- offsetof (struct kernel_dirent64
, d_name
));
180 dp
= (DIRENT_TYPE
*) ((char *) dp
+ new_reclen
);
181 kdp
= (struct kernel_dirent64
*) ((char *) kdp
+ old_reclen
);
184 return (char *) dp
- buf
;
187 # ifndef __ASSUME_GETDENTS64_SYSCALL
188 __set_errno (saved_errno
);
189 __have_no_getdents64
= 1;
195 struct kernel_dirent
*skdp
, *kdp
;
196 const size_t size_diff
= (offsetof (DIRENT_TYPE
, d_name
)
197 - offsetof (struct kernel_dirent
, d_name
));
199 red_nbytes
= MIN (nbytes
200 - ((nbytes
/ (offsetof (DIRENT_TYPE
, d_name
) + 14))
204 dp
= (DIRENT_TYPE
*) buf
;
205 skdp
= kdp
= __alloca (red_nbytes
);
207 retval
= INLINE_SYSCALL (getdents
, 3, fd
,
208 CHECK_N ((char *) kdp
, red_nbytes
), red_nbytes
);
213 while ((char *) kdp
< (char *) skdp
+ retval
)
215 const size_t alignment
= __alignof__ (DIRENT_TYPE
);
216 /* Since kdp->d_reclen is already aligned for the kernel structure
217 this may compute a value that is bigger than necessary. */
218 size_t new_reclen
= ((kdp
->d_reclen
+ size_diff
+ alignment
- 1)
220 if ((char *) dp
+ new_reclen
> buf
+ nbytes
)
222 /* Our heuristic failed. We read too many entries. Reset
224 assert (last_offset
!= -1);
225 __lseek64 (fd
, last_offset
, SEEK_SET
);
227 if ((char *) dp
== buf
)
229 /* The buffer the user passed in is too small to hold even
231 __set_errno (EINVAL
);
238 last_offset
= kdp
->d_off
;
239 DIRENT_SET_DP_INO(dp
, kdp
->d_ino
);
240 dp
->d_off
= kdp
->d_off
;
241 dp
->d_reclen
= new_reclen
;
242 dp
->d_type
= DT_UNKNOWN
;
243 memcpy (dp
->d_name
, kdp
->d_name
,
244 kdp
->d_reclen
- offsetof (struct kernel_dirent
, d_name
));
246 dp
= (DIRENT_TYPE
*) ((char *) dp
+ new_reclen
);
247 kdp
= (struct kernel_dirent
*) (((char *) kdp
) + kdp
->d_reclen
);
251 return (char *) dp
- buf
;