]>
Commit | Line | Data |
---|---|---|
942de61a | 1 | /* Read a directory in reentrant mode. Linux LFS version. |
2b778ceb | 2 | Copyright (C) 1997-2021 Free Software Foundation, Inc. |
942de61a AZ |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
942de61a AZ |
18 | |
19 | /* When _DIRENT_MATCHES_DIRENT64 is defined we can alias 'readdir64' to | |
20 | 'readdir'. However the function signatures are not equal due | |
21 | different return types, so we need to suppress {__}readdir so weak | |
22 | and strong alias do not throw conflicting types errors. */ | |
23 | #define readdir_r __no_readdir_r_decl | |
24 | #define __readdir_r __no___readdir_r_decl | |
25 | #include <dirent.h> | |
905ae44c AZ |
26 | #undef __readdir_r |
27 | #undef readdir_r | |
942de61a | 28 | |
905ae44c AZ |
29 | /* Read a directory entry from DIRP. */ |
30 | int | |
31 | __readdir64_r (DIR *dirp, struct dirent64 *entry, struct dirent64 **result) | |
32 | { | |
33 | struct dirent64 *dp; | |
34 | size_t reclen; | |
35 | const int saved_errno = errno; | |
36 | int ret; | |
9756dfe1 | 37 | |
905ae44c AZ |
38 | __libc_lock_lock (dirp->lock); |
39 | ||
40 | do | |
41 | { | |
42 | if (dirp->offset >= dirp->size) | |
43 | { | |
44 | /* We've emptied out our buffer. Refill it. */ | |
45 | ||
46 | size_t maxread = dirp->allocation; | |
47 | ssize_t bytes; | |
48 | ||
49 | maxread = dirp->allocation; | |
50 | ||
51 | bytes = __getdents64 (dirp->fd, dirp->data, maxread); | |
52 | if (bytes <= 0) | |
53 | { | |
54 | /* On some systems getdents fails with ENOENT when the | |
55 | open directory has been rmdir'd already. POSIX.1 | |
56 | requires that we treat this condition like normal EOF. */ | |
57 | if (bytes < 0 && errno == ENOENT) | |
58 | { | |
59 | bytes = 0; | |
60 | __set_errno (saved_errno); | |
61 | } | |
62 | if (bytes < 0) | |
63 | dirp->errcode = errno; | |
64 | ||
65 | dp = NULL; | |
66 | break; | |
67 | } | |
68 | dirp->size = (size_t) bytes; | |
69 | ||
70 | /* Reset the offset into the buffer. */ | |
71 | dirp->offset = 0; | |
72 | } | |
73 | ||
74 | dp = (struct dirent64 *) &dirp->data[dirp->offset]; | |
75 | ||
76 | reclen = dp->d_reclen; | |
77 | ||
78 | dirp->offset += reclen; | |
79 | ||
80 | dirp->filepos = dp->d_off; | |
81 | ||
82 | if (reclen > offsetof (struct dirent64, d_name) + NAME_MAX + 1) | |
83 | { | |
84 | /* The record is very long. It could still fit into the | |
85 | caller-supplied buffer if we can skip padding at the | |
86 | end. */ | |
87 | size_t namelen = _D_EXACT_NAMLEN (dp); | |
88 | if (namelen <= NAME_MAX) | |
89 | reclen = offsetof (struct dirent64, d_name) + namelen + 1; | |
90 | else | |
91 | { | |
92 | /* The name is too long. Ignore this file. */ | |
93 | dirp->errcode = ENAMETOOLONG; | |
94 | dp->d_ino = 0; | |
95 | continue; | |
96 | } | |
97 | } | |
98 | ||
99 | /* Skip deleted and ignored files. */ | |
100 | } | |
101 | while (dp->d_ino == 0); | |
102 | ||
103 | if (dp != NULL) | |
104 | { | |
105 | *result = memcpy (entry, dp, reclen); | |
106 | entry->d_reclen = reclen; | |
107 | ret = 0; | |
108 | } | |
109 | else | |
110 | { | |
111 | *result = NULL; | |
112 | ret = dirp->errcode; | |
113 | } | |
114 | ||
115 | __libc_lock_unlock (dirp->lock); | |
116 | ||
117 | return ret; | |
118 | } | |
9756dfe1 | 119 | |
942de61a AZ |
120 | |
121 | #if _DIRENT_MATCHES_DIRENT64 | |
122 | strong_alias (__readdir64_r, __readdir_r) | |
123 | weak_alias (__readdir64_r, readdir_r) | |
1ab18a5b | 124 | weak_alias (__readdir64_r, readdir64_r) |
942de61a AZ |
125 | #else |
126 | /* The compat code expects the 'struct direct' with d_ino being a __ino_t | |
127 | instead of __ino64_t. */ | |
128 | # include <shlib-compat.h> | |
129 | versioned_symbol (libc, __readdir64_r, readdir64_r, GLIBC_2_2); | |
130 | # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) | |
131 | # include <olddirent.h> | |
905ae44c AZ |
132 | |
133 | int | |
134 | attribute_compat_text_section | |
135 | __old_readdir64_r (DIR *dirp, struct __old_dirent64 *entry, | |
136 | struct __old_dirent64 **result) | |
137 | { | |
138 | struct __old_dirent64 *dp; | |
139 | size_t reclen; | |
140 | const int saved_errno = errno; | |
141 | int ret; | |
142 | ||
143 | __libc_lock_lock (dirp->lock); | |
144 | ||
145 | do | |
146 | { | |
147 | if (dirp->offset >= dirp->size) | |
148 | { | |
149 | /* We've emptied out our buffer. Refill it. */ | |
150 | ||
151 | size_t maxread = dirp->allocation; | |
152 | ssize_t bytes; | |
153 | ||
154 | maxread = dirp->allocation; | |
155 | ||
156 | bytes = __old_getdents64 (dirp->fd, dirp->data, maxread); | |
157 | if (bytes <= 0) | |
158 | { | |
159 | /* On some systems getdents fails with ENOENT when the | |
160 | open directory has been rmdir'd already. POSIX.1 | |
161 | requires that we treat this condition like normal EOF. */ | |
162 | if (bytes < 0 && errno == ENOENT) | |
163 | { | |
164 | bytes = 0; | |
165 | __set_errno (saved_errno); | |
166 | } | |
167 | if (bytes < 0) | |
168 | dirp->errcode = errno; | |
169 | ||
170 | dp = NULL; | |
171 | break; | |
172 | } | |
173 | dirp->size = (size_t) bytes; | |
174 | ||
175 | /* Reset the offset into the buffer. */ | |
176 | dirp->offset = 0; | |
177 | } | |
178 | ||
179 | dp = (struct __old_dirent64 *) &dirp->data[dirp->offset]; | |
180 | ||
181 | reclen = dp->d_reclen; | |
182 | ||
183 | dirp->offset += reclen; | |
184 | ||
185 | dirp->filepos = dp->d_off; | |
186 | ||
187 | if (reclen > offsetof (struct __old_dirent64, d_name) + NAME_MAX + 1) | |
188 | { | |
189 | /* The record is very long. It could still fit into the | |
190 | caller-supplied buffer if we can skip padding at the | |
191 | end. */ | |
192 | size_t namelen = _D_EXACT_NAMLEN (dp); | |
193 | if (namelen <= NAME_MAX) | |
194 | reclen = offsetof (struct __old_dirent64, d_name) + namelen + 1; | |
195 | else | |
196 | { | |
197 | /* The name is too long. Ignore this file. */ | |
198 | dirp->errcode = ENAMETOOLONG; | |
199 | dp->d_ino = 0; | |
200 | continue; | |
201 | } | |
202 | } | |
203 | ||
204 | /* Skip deleted and ignored files. */ | |
205 | } | |
206 | while (dp->d_ino == 0); | |
207 | ||
208 | if (dp != NULL) | |
209 | { | |
210 | *result = memcpy (entry, dp, reclen); | |
211 | entry->d_reclen = reclen; | |
212 | ret = 0; | |
213 | } | |
214 | else | |
215 | { | |
216 | *result = NULL; | |
217 | ret = dirp->errcode; | |
218 | } | |
219 | ||
220 | __libc_lock_unlock (dirp->lock); | |
221 | ||
222 | return ret; | |
223 | } | |
224 | ||
942de61a AZ |
225 | compat_symbol (libc, __old_readdir64_r, readdir64_r, GLIBC_2_1); |
226 | # endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */ | |
227 | #endif /* _DIRENT_MATCHES_DIRENT64 */ |