File: | src/lib/libc/gen/fts.c |
Warning: | line 761, column 4 The expression is an uninitialized value. The computed value will also be garbage |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: fts.c,v 1.61 2021/11/29 03:20:37 deraadt Exp $ */ | ||||
2 | |||||
3 | /*- | ||||
4 | * Copyright (c) 1990, 1993, 1994 | ||||
5 | * The Regents of the University of California. All rights reserved. | ||||
6 | * | ||||
7 | * Redistribution and use in source and binary forms, with or without | ||||
8 | * modification, are permitted provided that the following conditions | ||||
9 | * are met: | ||||
10 | * 1. Redistributions of source code must retain the above copyright | ||||
11 | * notice, this list of conditions and the following disclaimer. | ||||
12 | * 2. Redistributions in binary form must reproduce the above copyright | ||||
13 | * notice, this list of conditions and the following disclaimer in the | ||||
14 | * documentation and/or other materials provided with the distribution. | ||||
15 | * 3. Neither the name of the University nor the names of its contributors | ||||
16 | * may be used to endorse or promote products derived from this software | ||||
17 | * without specific prior written permission. | ||||
18 | * | ||||
19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
29 | * SUCH DAMAGE. | ||||
30 | */ | ||||
31 | |||||
32 | #include <sys/param.h> /* ALIGN ALIGNBYTES */ | ||||
33 | #include <sys/stat.h> | ||||
34 | |||||
35 | #include <dirent.h> | ||||
36 | #include <errno(*__errno()).h> | ||||
37 | #include <fcntl.h> | ||||
38 | #include <fts.h> | ||||
39 | #include <limits.h> | ||||
40 | #include <stdlib.h> | ||||
41 | #include <string.h> | ||||
42 | #include <unistd.h> | ||||
43 | |||||
44 | #define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b)) | ||||
45 | |||||
46 | static FTSENT *fts_alloc(FTS *, const char *, size_t); | ||||
47 | static FTSENT *fts_build(FTS *, int); | ||||
48 | static void fts_lfree(FTSENT *); | ||||
49 | static void fts_load(FTS *, FTSENT *); | ||||
50 | static size_t fts_maxarglen(char * const *); | ||||
51 | static void fts_padjust(FTS *, FTSENT *); | ||||
52 | static int fts_palloc(FTS *, size_t); | ||||
53 | static FTSENT *fts_sort(FTS *, FTSENT *, int); | ||||
54 | static u_short fts_stat(FTS *, FTSENT *, int, int); | ||||
55 | static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); | ||||
56 | |||||
57 | #define ISDOT(a)(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[ 2]))) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) | ||||
58 | |||||
59 | #define CLR(opt)(sp->fts_options &= ~(opt)) (sp->fts_options &= ~(opt)) | ||||
60 | #define ISSET(opt)(sp->fts_options & (opt)) (sp->fts_options & (opt)) | ||||
61 | #define SET(opt)(sp->fts_options |= (opt)) (sp->fts_options |= (opt)) | ||||
62 | |||||
63 | #define FCHDIR(sp, fd)(!(sp->fts_options & (0x0004)) && fchdir(fd)) (!ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) && fchdir(fd)) | ||||
64 | |||||
65 | /* fts_build flags */ | ||||
66 | #define BCHILD1 1 /* fts_children */ | ||||
67 | #define BNAMES2 2 /* fts_children, names only */ | ||||
68 | #define BREAD3 3 /* fts_read */ | ||||
69 | |||||
70 | FTS * | ||||
71 | fts_open(char * const *argv, int options, | ||||
72 | int (*compar)(const FTSENT **, const FTSENT **)) | ||||
73 | { | ||||
74 | FTS *sp; | ||||
75 | FTSENT *p, *root; | ||||
76 | int nitems; | ||||
77 | FTSENT *parent, *prev; | ||||
78 | |||||
79 | /* Options check. */ | ||||
80 | if (options & ~FTS_OPTIONMASK0x00ff) { | ||||
81 | errno(*__errno()) = EINVAL22; | ||||
82 | return (NULL((void *)0)); | ||||
83 | } | ||||
84 | |||||
85 | /* At least one path must be specified. */ | ||||
86 | if (*argv == NULL((void *)0)) { | ||||
87 | errno(*__errno()) = EINVAL22; | ||||
88 | return (NULL((void *)0)); | ||||
89 | } | ||||
90 | |||||
91 | /* Allocate/initialize the stream */ | ||||
92 | if ((sp = calloc(1, sizeof(FTS))) == NULL((void *)0)) | ||||
93 | return (NULL((void *)0)); | ||||
94 | sp->fts_compar = compar; | ||||
95 | sp->fts_options = options; | ||||
96 | |||||
97 | /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ | ||||
98 | if (ISSET(FTS_LOGICAL)(sp->fts_options & (0x0002))) | ||||
99 | SET(FTS_NOCHDIR)(sp->fts_options |= (0x0004)); | ||||
100 | |||||
101 | /* | ||||
102 | * Start out with 1K of path space, and enough, in any case, | ||||
103 | * to hold the user's paths. | ||||
104 | */ | ||||
105 | if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX)(((fts_maxarglen(argv)) > (1024)) ? (fts_maxarglen(argv)) : (1024)))) | ||||
106 | goto mem1; | ||||
107 | |||||
108 | /* Allocate/initialize root's parent. */ | ||||
109 | if ((parent = fts_alloc(sp, "", 0)) == NULL((void *)0)) | ||||
110 | goto mem2; | ||||
111 | parent->fts_level = FTS_ROOTPARENTLEVEL-1; | ||||
112 | |||||
113 | /* Allocate/initialize root(s). */ | ||||
114 | for (root = prev = NULL((void *)0), nitems = 0; *argv; ++argv, ++nitems) { | ||||
115 | if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL((void *)0)) | ||||
116 | goto mem3; | ||||
117 | p->fts_level = FTS_ROOTLEVEL0; | ||||
118 | p->fts_parent = parent; | ||||
119 | p->fts_accpath = p->fts_name; | ||||
120 | p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)(sp->fts_options & (0x0001)), -1); | ||||
121 | |||||
122 | /* Command-line "." and ".." are real directories. */ | ||||
123 | if (p->fts_info == FTS_DOT5) | ||||
124 | p->fts_info = FTS_D1; | ||||
125 | |||||
126 | /* | ||||
127 | * If comparison routine supplied, traverse in sorted | ||||
128 | * order; otherwise traverse in the order specified. | ||||
129 | */ | ||||
130 | if (compar) { | ||||
131 | p->fts_link = root; | ||||
132 | root = p; | ||||
133 | } else { | ||||
134 | p->fts_link = NULL((void *)0); | ||||
135 | if (root == NULL((void *)0)) | ||||
136 | root = p; | ||||
137 | else | ||||
138 | prev->fts_link = p; | ||||
139 | prev = p; | ||||
140 | } | ||||
141 | } | ||||
142 | if (compar && nitems > 1) | ||||
143 | root = fts_sort(sp, root, nitems); | ||||
144 | |||||
145 | /* | ||||
146 | * Allocate a dummy pointer and make fts_read think that we've just | ||||
147 | * finished the node before the root(s); set p->fts_info to FTS_INIT | ||||
148 | * so that everything about the "current" node is ignored. | ||||
149 | */ | ||||
150 | if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL((void *)0)) | ||||
151 | goto mem3; | ||||
152 | sp->fts_cur->fts_link = root; | ||||
153 | sp->fts_cur->fts_info = FTS_INIT9; | ||||
154 | |||||
155 | /* | ||||
156 | * If using chdir(2), grab a file descriptor pointing to dot to ensure | ||||
157 | * that we can get back here; this could be avoided for some paths, | ||||
158 | * but almost certainly not worth the effort. Slashes, symbolic links, | ||||
159 | * and ".." are all fairly nasty problems. Note, if we can't get the | ||||
160 | * descriptor we run anyway, just more slowly. | ||||
161 | */ | ||||
162 | if (!ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) && | ||||
163 | (sp->fts_rfd = open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) | ||||
164 | SET(FTS_NOCHDIR)(sp->fts_options |= (0x0004)); | ||||
165 | |||||
166 | if (nitems == 0) | ||||
167 | free(parent); | ||||
168 | |||||
169 | return (sp); | ||||
170 | |||||
171 | mem3: fts_lfree(root); | ||||
172 | free(parent); | ||||
173 | mem2: free(sp->fts_path); | ||||
174 | mem1: free(sp); | ||||
175 | return (NULL((void *)0)); | ||||
176 | } | ||||
177 | DEF_WEAK(fts_open)__asm__(".weak " "fts_open" " ; " "fts_open" " = " "_libc_fts_open" ); | ||||
178 | |||||
179 | static void | ||||
180 | fts_load(FTS *sp, FTSENT *p) | ||||
181 | { | ||||
182 | size_t len; | ||||
183 | char *cp; | ||||
184 | |||||
185 | /* | ||||
186 | * Load the stream structure for the next traversal. Since we don't | ||||
187 | * actually enter the directory until after the preorder visit, set | ||||
188 | * the fts_accpath field specially so the chdir gets done to the right | ||||
189 | * place and the user can access the first node. From fts_open it's | ||||
190 | * known that the path will fit. | ||||
191 | */ | ||||
192 | len = p->fts_pathlen = p->fts_namelen; | ||||
193 | memmove(sp->fts_path, p->fts_name, len + 1); | ||||
194 | if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { | ||||
195 | len = strlen(++cp); | ||||
196 | memmove(p->fts_name, cp, len + 1); | ||||
197 | p->fts_namelen = len; | ||||
198 | } | ||||
199 | p->fts_accpath = p->fts_path = sp->fts_path; | ||||
200 | sp->fts_dev = p->fts_dev; | ||||
201 | } | ||||
202 | |||||
203 | int | ||||
204 | fts_close(FTS *sp) | ||||
205 | { | ||||
206 | FTSENT *freep, *p; | ||||
207 | int rfd, error = 0; | ||||
208 | |||||
209 | /* | ||||
210 | * This still works if we haven't read anything -- the dummy structure | ||||
211 | * points to the root list, so we step through to the end of the root | ||||
212 | * list which has a valid parent pointer. | ||||
213 | */ | ||||
214 | if (sp->fts_cur) { | ||||
215 | for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL0;) { | ||||
216 | freep = p; | ||||
217 | p = p->fts_link ? p->fts_link : p->fts_parent; | ||||
218 | free(freep); | ||||
219 | } | ||||
220 | free(p); | ||||
221 | } | ||||
222 | |||||
223 | /* Stash the original directory fd if needed. */ | ||||
224 | rfd = ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) ? -1 : sp->fts_rfd; | ||||
225 | |||||
226 | /* Free up child linked list, sort array, path buffer, stream ptr.*/ | ||||
227 | if (sp->fts_child) | ||||
228 | fts_lfree(sp->fts_child); | ||||
229 | free(sp->fts_array); | ||||
230 | free(sp->fts_path); | ||||
231 | free(sp); | ||||
232 | |||||
233 | /* Return to original directory, checking for error. */ | ||||
234 | if (rfd != -1) { | ||||
235 | int saved_errno; | ||||
236 | error = fchdir(rfd); | ||||
237 | saved_errno = errno(*__errno()); | ||||
238 | (void)close(rfd); | ||||
239 | errno(*__errno()) = saved_errno; | ||||
240 | } | ||||
241 | |||||
242 | return (error); | ||||
243 | } | ||||
244 | DEF_WEAK(fts_close)__asm__(".weak " "fts_close" " ; " "fts_close" " = " "_libc_fts_close" ); | ||||
245 | |||||
246 | /* | ||||
247 | * Special case of "/" at the end of the path so that slashes aren't | ||||
248 | * appended which would cause paths to be written as "....//foo". | ||||
249 | */ | ||||
250 | #define NAPPEND(p)(p->fts_path[p->fts_pathlen - 1] == '/' ? p->fts_pathlen - 1 : p->fts_pathlen) \ | ||||
251 | (p->fts_path[p->fts_pathlen - 1] == '/' \ | ||||
252 | ? p->fts_pathlen - 1 : p->fts_pathlen) | ||||
253 | |||||
254 | FTSENT * | ||||
255 | fts_read(FTS *sp) | ||||
256 | { | ||||
257 | FTSENT *p, *tmp; | ||||
258 | int instr; | ||||
259 | char *t; | ||||
260 | int saved_errno; | ||||
261 | |||||
262 | /* If finished or unrecoverable error, return NULL. */ | ||||
263 | if (sp->fts_cur == NULL((void *)0) || ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
264 | return (NULL((void *)0)); | ||||
265 | |||||
266 | /* Set current node pointer. */ | ||||
267 | p = sp->fts_cur; | ||||
268 | |||||
269 | /* Save and zero out user instructions. */ | ||||
270 | instr = p->fts_instr; | ||||
271 | p->fts_instr = FTS_NOINSTR3; | ||||
272 | |||||
273 | /* Any type of file may be re-visited; re-stat and re-turn. */ | ||||
274 | if (instr == FTS_AGAIN1) { | ||||
275 | p->fts_info = fts_stat(sp, p, 0, -1); | ||||
276 | return (p); | ||||
277 | } | ||||
278 | |||||
279 | /* | ||||
280 | * Following a symlink -- SLNONE test allows application to see | ||||
281 | * SLNONE and recover. If indirecting through a symlink, have | ||||
282 | * keep a pointer to current location. If unable to get that | ||||
283 | * pointer, follow fails. | ||||
284 | */ | ||||
285 | if (instr == FTS_FOLLOW2 && | ||||
286 | (p->fts_info == FTS_SL12 || p->fts_info == FTS_SLNONE13)) { | ||||
287 | p->fts_info = fts_stat(sp, p, 1, -1); | ||||
288 | if (p->fts_info == FTS_D1 && !ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
289 | if ((p->fts_symfd = | ||||
290 | open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) { | ||||
291 | p->fts_errno = errno(*__errno()); | ||||
292 | p->fts_info = FTS_ERR7; | ||||
293 | } else | ||||
294 | p->fts_flags |= FTS_SYMFOLLOW0x02; | ||||
295 | } | ||||
296 | return (p); | ||||
297 | } | ||||
298 | |||||
299 | /* Directory in pre-order. */ | ||||
300 | if (p->fts_info == FTS_D1) { | ||||
301 | /* If skipped or crossed mount point, do post-order visit. */ | ||||
302 | if (instr == FTS_SKIP4 || | ||||
303 | (ISSET(FTS_XDEV)(sp->fts_options & (0x0040)) && p->fts_dev != sp->fts_dev)) { | ||||
304 | if (p->fts_flags & FTS_SYMFOLLOW0x02) | ||||
305 | (void)close(p->fts_symfd); | ||||
306 | if (sp->fts_child) { | ||||
307 | fts_lfree(sp->fts_child); | ||||
308 | sp->fts_child = NULL((void *)0); | ||||
309 | } | ||||
310 | p->fts_info = FTS_DP6; | ||||
311 | return (p); | ||||
312 | } | ||||
313 | |||||
314 | /* Rebuild if only read the names and now traversing. */ | ||||
315 | if (sp->fts_child && ISSET(FTS_NAMEONLY)(sp->fts_options & (0x1000))) { | ||||
316 | CLR(FTS_NAMEONLY)(sp->fts_options &= ~(0x1000)); | ||||
317 | fts_lfree(sp->fts_child); | ||||
318 | sp->fts_child = NULL((void *)0); | ||||
319 | } | ||||
320 | |||||
321 | /* | ||||
322 | * Cd to the subdirectory. | ||||
323 | * | ||||
324 | * If have already read and now fail to chdir, whack the list | ||||
325 | * to make the names come out right, and set the parent errno | ||||
326 | * so the application will eventually get an error condition. | ||||
327 | * Set the FTS_DONTCHDIR flag so that when we logically change | ||||
328 | * directories back to the parent we don't do a chdir. | ||||
329 | * | ||||
330 | * If haven't read do so. If the read fails, fts_build sets | ||||
331 | * FTS_STOP or the fts_info field of the node. | ||||
332 | */ | ||||
333 | if (sp->fts_child) { | ||||
334 | if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { | ||||
335 | p->fts_errno = errno(*__errno()); | ||||
336 | p->fts_flags |= FTS_DONTCHDIR0x01; | ||||
337 | for (p = sp->fts_child; p; p = p->fts_link) | ||||
338 | p->fts_accpath = | ||||
339 | p->fts_parent->fts_accpath; | ||||
340 | } | ||||
341 | } else if ((sp->fts_child = fts_build(sp, BREAD3)) == NULL((void *)0)) { | ||||
342 | if (ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
343 | return (NULL((void *)0)); | ||||
344 | return (p); | ||||
345 | } | ||||
346 | p = sp->fts_child; | ||||
347 | sp->fts_child = NULL((void *)0); | ||||
348 | goto name; | ||||
349 | } | ||||
350 | |||||
351 | /* Move to the next node on this level. */ | ||||
352 | next: tmp = p; | ||||
353 | if ((p = p->fts_link)) { | ||||
354 | free(tmp); | ||||
355 | |||||
356 | /* | ||||
357 | * If reached the top, return to the original directory (or | ||||
358 | * the root of the tree), and load the paths for the next root. | ||||
359 | */ | ||||
360 | if (p->fts_level == FTS_ROOTLEVEL0) { | ||||
361 | if (FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd))) { | ||||
362 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
363 | return (NULL((void *)0)); | ||||
364 | } | ||||
365 | fts_load(sp, p); | ||||
366 | return (sp->fts_cur = p); | ||||
367 | } | ||||
368 | |||||
369 | /* | ||||
370 | * User may have called fts_set on the node. If skipped, | ||||
371 | * ignore. If followed, get a file descriptor so we can | ||||
372 | * get back if necessary. | ||||
373 | */ | ||||
374 | if (p->fts_instr == FTS_SKIP4) | ||||
375 | goto next; | ||||
376 | if (p->fts_instr == FTS_FOLLOW2) { | ||||
377 | p->fts_info = fts_stat(sp, p, 1, -1); | ||||
378 | if (p->fts_info == FTS_D1 && !ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
379 | if ((p->fts_symfd = | ||||
380 | open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) { | ||||
381 | p->fts_errno = errno(*__errno()); | ||||
382 | p->fts_info = FTS_ERR7; | ||||
383 | } else | ||||
384 | p->fts_flags |= FTS_SYMFOLLOW0x02; | ||||
385 | } | ||||
386 | p->fts_instr = FTS_NOINSTR3; | ||||
387 | } | ||||
388 | |||||
389 | name: t = sp->fts_path + NAPPEND(p->fts_parent)(p->fts_parent->fts_path[p->fts_parent->fts_pathlen - 1] == '/' ? p->fts_parent->fts_pathlen - 1 : p->fts_parent ->fts_pathlen); | ||||
390 | *t++ = '/'; | ||||
391 | memmove(t, p->fts_name, p->fts_namelen + 1); | ||||
392 | return (sp->fts_cur = p); | ||||
393 | } | ||||
394 | |||||
395 | /* Move up to the parent node. */ | ||||
396 | p = tmp->fts_parent; | ||||
397 | free(tmp); | ||||
398 | |||||
399 | if (p->fts_level == FTS_ROOTPARENTLEVEL-1) { | ||||
400 | /* | ||||
401 | * Done; free everything up and set errno to 0 so the user | ||||
402 | * can distinguish between error and EOF. | ||||
403 | */ | ||||
404 | free(p); | ||||
405 | errno(*__errno()) = 0; | ||||
406 | return (sp->fts_cur = NULL((void *)0)); | ||||
407 | } | ||||
408 | |||||
409 | /* NUL terminate the pathname. */ | ||||
410 | sp->fts_path[p->fts_pathlen] = '\0'; | ||||
411 | |||||
412 | /* | ||||
413 | * Return to the parent directory. If at a root node or came through | ||||
414 | * a symlink, go back through the file descriptor. Otherwise, cd up | ||||
415 | * one directory. | ||||
416 | */ | ||||
417 | if (p->fts_level == FTS_ROOTLEVEL0) { | ||||
418 | if (FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd))) { | ||||
419 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
420 | sp->fts_cur = p; | ||||
421 | return (NULL((void *)0)); | ||||
422 | } | ||||
423 | } else if (p->fts_flags & FTS_SYMFOLLOW0x02) { | ||||
424 | if (FCHDIR(sp, p->fts_symfd)(!(sp->fts_options & (0x0004)) && fchdir(p-> fts_symfd))) { | ||||
425 | saved_errno = errno(*__errno()); | ||||
426 | (void)close(p->fts_symfd); | ||||
427 | errno(*__errno()) = saved_errno; | ||||
428 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
429 | sp->fts_cur = p; | ||||
430 | return (NULL((void *)0)); | ||||
431 | } | ||||
432 | (void)close(p->fts_symfd); | ||||
433 | } else if (!(p->fts_flags & FTS_DONTCHDIR0x01) && | ||||
434 | fts_safe_changedir(sp, p->fts_parent, -1, "..")) { | ||||
435 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
436 | sp->fts_cur = p; | ||||
437 | return (NULL((void *)0)); | ||||
438 | } | ||||
439 | p->fts_info = p->fts_errno ? FTS_ERR7 : FTS_DP6; | ||||
440 | return (sp->fts_cur = p); | ||||
441 | } | ||||
442 | DEF_WEAK(fts_read)__asm__(".weak " "fts_read" " ; " "fts_read" " = " "_libc_fts_read" ); | ||||
443 | |||||
444 | /* | ||||
445 | * Fts_set takes the stream as an argument although it's not used in this | ||||
446 | * implementation; it would be necessary if anyone wanted to add global | ||||
447 | * semantics to fts using fts_set. An error return is allowed for similar | ||||
448 | * reasons. | ||||
449 | */ | ||||
450 | int | ||||
451 | fts_set(FTS *sp, FTSENT *p, int instr) | ||||
452 | { | ||||
453 | if (instr && instr != FTS_AGAIN1 && instr != FTS_FOLLOW2 && | ||||
454 | instr != FTS_NOINSTR3 && instr != FTS_SKIP4) { | ||||
455 | errno(*__errno()) = EINVAL22; | ||||
456 | return (1); | ||||
457 | } | ||||
458 | p->fts_instr = instr; | ||||
459 | return (0); | ||||
460 | } | ||||
461 | DEF_WEAK(fts_set)__asm__(".weak " "fts_set" " ; " "fts_set" " = " "_libc_fts_set" ); | ||||
462 | |||||
463 | FTSENT * | ||||
464 | fts_children(FTS *sp, int instr) | ||||
465 | { | ||||
466 | FTSENT *p; | ||||
467 | int fd; | ||||
468 | |||||
469 | if (instr && instr != FTS_NAMEONLY0x1000) { | ||||
470 | errno(*__errno()) = EINVAL22; | ||||
471 | return (NULL((void *)0)); | ||||
472 | } | ||||
473 | |||||
474 | /* Set current node pointer. */ | ||||
475 | p = sp->fts_cur; | ||||
476 | |||||
477 | /* | ||||
478 | * Errno set to 0 so user can distinguish empty directory from | ||||
479 | * an error. | ||||
480 | */ | ||||
481 | errno(*__errno()) = 0; | ||||
482 | |||||
483 | /* Fatal errors stop here. */ | ||||
484 | if (ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
485 | return (NULL((void *)0)); | ||||
486 | |||||
487 | /* Return logical hierarchy of user's arguments. */ | ||||
488 | if (p->fts_info == FTS_INIT9) | ||||
489 | return (p->fts_link); | ||||
490 | |||||
491 | /* | ||||
492 | * If not a directory being visited in pre-order, stop here. Could | ||||
493 | * allow FTS_DNR, assuming the user has fixed the problem, but the | ||||
494 | * same effect is available with FTS_AGAIN. | ||||
495 | */ | ||||
496 | if (p->fts_info != FTS_D1 /* && p->fts_info != FTS_DNR */) | ||||
497 | return (NULL((void *)0)); | ||||
498 | |||||
499 | /* Free up any previous child list. */ | ||||
500 | if (sp->fts_child) | ||||
501 | fts_lfree(sp->fts_child); | ||||
502 | |||||
503 | if (instr == FTS_NAMEONLY0x1000) { | ||||
504 | SET(FTS_NAMEONLY)(sp->fts_options |= (0x1000)); | ||||
505 | instr = BNAMES2; | ||||
506 | } else | ||||
507 | instr = BCHILD1; | ||||
508 | |||||
509 | /* | ||||
510 | * If using chdir on a relative path and called BEFORE fts_read does | ||||
511 | * its chdir to the root of a traversal, we can lose -- we need to | ||||
512 | * chdir into the subdirectory, and we don't know where the current | ||||
513 | * directory is, so we can't get back so that the upcoming chdir by | ||||
514 | * fts_read will work. | ||||
515 | */ | ||||
516 | if (p->fts_level != FTS_ROOTLEVEL0 || p->fts_accpath[0] == '/' || | ||||
517 | ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
518 | return (sp->fts_child = fts_build(sp, instr)); | ||||
519 | |||||
520 | if ((fd = open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) | ||||
521 | return (NULL((void *)0)); | ||||
522 | sp->fts_child = fts_build(sp, instr); | ||||
523 | if (fchdir(fd)) { | ||||
524 | (void)close(fd); | ||||
525 | return (NULL((void *)0)); | ||||
526 | } | ||||
527 | (void)close(fd); | ||||
528 | return (sp->fts_child); | ||||
529 | } | ||||
530 | DEF_WEAK(fts_children)__asm__(".weak " "fts_children" " ; " "fts_children" " = " "_libc_fts_children" ); | ||||
531 | |||||
532 | /* | ||||
533 | * This is the tricky part -- do not casually change *anything* in here. The | ||||
534 | * idea is to build the linked list of entries that are used by fts_children | ||||
535 | * and fts_read. There are lots of special cases. | ||||
536 | * | ||||
537 | * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is | ||||
538 | * set and it's a physical walk (so that symbolic links can't be directories), | ||||
539 | * we can do things quickly. First, if it's a 4.4BSD file system, the type | ||||
540 | * of the file is in the directory entry. Otherwise, we assume that the number | ||||
541 | * of subdirectories in a node is equal to the number of links to the parent. | ||||
542 | * The former skips all stat calls. The latter skips stat calls in any leaf | ||||
543 | * directories and for any files after the subdirectories in the directory have | ||||
544 | * been found, cutting the stat calls by about 2/3. | ||||
545 | */ | ||||
546 | static FTSENT * | ||||
547 | fts_build(FTS *sp, int type) | ||||
548 | { | ||||
549 | struct dirent *dp; | ||||
550 | FTSENT *p, *head; | ||||
551 | FTSENT *cur, *tail; | ||||
552 | DIR *dirp; | ||||
553 | void *oldaddr; | ||||
554 | size_t len, maxlen; | ||||
555 | int nitems, cderrno, descend, level, nlinks, nostat, doadjust; | ||||
556 | int saved_errno; | ||||
557 | char *cp; | ||||
| |||||
558 | |||||
559 | /* Set current node pointer. */ | ||||
560 | cur = sp->fts_cur; | ||||
561 | |||||
562 | /* | ||||
563 | * Open the directory for reading. If this fails, we're done. | ||||
564 | * If being called from fts_read, set the fts_info field. | ||||
565 | */ | ||||
566 | if ((dirp = opendir(cur->fts_accpath)) == NULL((void *)0)) { | ||||
567 | if (type == BREAD3) { | ||||
568 | cur->fts_info = FTS_DNR4; | ||||
569 | cur->fts_errno = errno(*__errno()); | ||||
570 | } | ||||
571 | return (NULL((void *)0)); | ||||
572 | } | ||||
573 | |||||
574 | /* | ||||
575 | * Nlinks is the number of possible entries of type directory in the | ||||
576 | * directory if we're cheating on stat calls, 0 if we're not doing | ||||
577 | * any stat calls at all, -1 if we're doing stats on everything. | ||||
578 | */ | ||||
579 | if (type == BNAMES2) | ||||
580 | nlinks = 0; | ||||
581 | else if (ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)) && ISSET(FTS_PHYSICAL)(sp->fts_options & (0x0010))) { | ||||
582 | nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020)) ? 0 : 2); | ||||
583 | nostat = 1; | ||||
584 | } else { | ||||
585 | nlinks = -1; | ||||
586 | nostat = 0; | ||||
587 | } | ||||
588 | |||||
589 | #ifdef notdef | ||||
590 | (void)printf("nlinks == %d (cur: %u)\n", nlinks, cur->fts_nlink); | ||||
591 | (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", | ||||
592 | ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)), ISSET(FTS_PHYSICAL)(sp->fts_options & (0x0010)), ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020))); | ||||
593 | #endif | ||||
594 | /* | ||||
595 | * If we're going to need to stat anything or we want to descend | ||||
596 | * and stay in the directory, chdir. If this fails we keep going, | ||||
597 | * but set a flag so we don't chdir after the post-order visit. | ||||
598 | * We won't be able to stat anything, but we can still return the | ||||
599 | * names themselves. Note, that since fts_read won't be able to | ||||
600 | * chdir into the directory, it will have to return different path | ||||
601 | * names than before, i.e. "a/b" instead of "b". Since the node | ||||
602 | * has already been visited in pre-order, have to wait until the | ||||
603 | * post-order visit to return the error. There is a special case | ||||
604 | * here, if there was nothing to stat then it's not an error to | ||||
605 | * not be able to stat. This is all fairly nasty. If a program | ||||
606 | * needed sorted entries or stat information, they had better be | ||||
607 | * checking FTS_NS on the returned nodes. | ||||
608 | */ | ||||
609 | cderrno = 0; | ||||
610 | if (nlinks
| ||||
611 | if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL((void *)0))) { | ||||
612 | if (nlinks && type == BREAD3) | ||||
613 | cur->fts_errno = errno(*__errno()); | ||||
614 | cur->fts_flags |= FTS_DONTCHDIR0x01; | ||||
615 | descend = 0; | ||||
616 | cderrno = errno(*__errno()); | ||||
617 | (void)closedir(dirp); | ||||
618 | dirp = NULL((void *)0); | ||||
619 | } else | ||||
620 | descend = 1; | ||||
621 | } else | ||||
622 | descend = 0; | ||||
623 | |||||
624 | /* | ||||
625 | * Figure out the max file name length that can be stored in the | ||||
626 | * current path -- the inner loop allocates more path as necessary. | ||||
627 | * We really wouldn't have to do the maxlen calculations here, we | ||||
628 | * could do them in fts_read before returning the path, but it's a | ||||
629 | * lot easier here since the length is part of the dirent structure. | ||||
630 | * | ||||
631 | * If not changing directories set a pointer so that can just append | ||||
632 | * each new name into the path. | ||||
633 | */ | ||||
634 | len = NAPPEND(cur)(cur->fts_path[cur->fts_pathlen - 1] == '/' ? cur->fts_pathlen - 1 : cur->fts_pathlen); | ||||
635 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
636 | cp = sp->fts_path + len; | ||||
637 | *cp++ = '/'; | ||||
638 | } | ||||
639 | len++; | ||||
640 | maxlen = sp->fts_pathlen - len; | ||||
641 | |||||
642 | /* | ||||
643 | * fts_level is signed so we must prevent it from wrapping | ||||
644 | * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. | ||||
645 | */ | ||||
646 | level = cur->fts_level; | ||||
647 | if (level < FTS_MAXLEVEL0x7fffffff) | ||||
648 | level++; | ||||
649 | |||||
650 | /* Read the directory, attaching each entry to the `link' pointer. */ | ||||
651 | doadjust = 0; | ||||
652 | for (head = tail = NULL((void *)0), nitems = 0; dirp
| ||||
653 | if (!ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020)) && ISDOT(dp->d_name)(dp->d_name[0] == '.' && (!dp->d_name[1] || (dp ->d_name[1] == '.' && !dp->d_name[2])))) | ||||
654 | continue; | ||||
655 | |||||
656 | if (!(p = fts_alloc(sp, dp->d_name, dp->d_namlen))) | ||||
657 | goto mem1; | ||||
658 | if (dp->d_namlen >= maxlen) { /* include space for NUL */ | ||||
659 | oldaddr = sp->fts_path; | ||||
660 | if (fts_palloc(sp, dp->d_namlen +len + 1)) { | ||||
661 | /* | ||||
662 | * No more memory for path or structures. Save | ||||
663 | * errno, free up the current structure and the | ||||
664 | * structures already allocated. | ||||
665 | */ | ||||
666 | mem1: saved_errno = errno(*__errno()); | ||||
667 | free(p); | ||||
668 | fts_lfree(head); | ||||
669 | (void)closedir(dirp); | ||||
670 | cur->fts_info = FTS_ERR7; | ||||
671 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
672 | errno(*__errno()) = saved_errno; | ||||
673 | return (NULL((void *)0)); | ||||
674 | } | ||||
675 | /* Did realloc() change the pointer? */ | ||||
676 | if (oldaddr != sp->fts_path) { | ||||
677 | doadjust = 1; | ||||
678 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
679 | cp = sp->fts_path + len; | ||||
680 | } | ||||
681 | maxlen = sp->fts_pathlen - len; | ||||
682 | } | ||||
683 | |||||
684 | p->fts_level = level; | ||||
685 | p->fts_parent = sp->fts_cur; | ||||
686 | p->fts_pathlen = len + dp->d_namlen; | ||||
687 | if (p->fts_pathlen < len) { | ||||
688 | /* | ||||
689 | * If we wrap, free up the current structure and | ||||
690 | * the structures already allocated, then error | ||||
691 | * out with ENAMETOOLONG. | ||||
692 | */ | ||||
693 | free(p); | ||||
694 | fts_lfree(head); | ||||
695 | (void)closedir(dirp); | ||||
696 | cur->fts_info = FTS_ERR7; | ||||
697 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
698 | errno(*__errno()) = ENAMETOOLONG63; | ||||
699 | return (NULL((void *)0)); | ||||
700 | } | ||||
701 | |||||
702 | if (cderrno
| ||||
703 | if (nlinks) { | ||||
704 | p->fts_info = FTS_NS10; | ||||
705 | p->fts_errno = cderrno; | ||||
706 | } else | ||||
707 | p->fts_info = FTS_NSOK11; | ||||
708 | p->fts_accpath = cur->fts_accpath; | ||||
709 | } else if (nlinks
| ||||
710 | #ifdef DT_DIR4 | ||||
711 | || (nostat
| ||||
712 | dp->d_type != DT_DIR4 && dp->d_type != DT_UNKNOWN0) | ||||
713 | #endif | ||||
714 | ) { | ||||
715 | p->fts_accpath = | ||||
716 | ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) ? p->fts_path : p->fts_name; | ||||
717 | p->fts_info = FTS_NSOK11; | ||||
718 | } else { | ||||
719 | /* Build a file name for fts_stat to stat. */ | ||||
720 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
721 | p->fts_accpath = p->fts_path; | ||||
722 | memmove(cp, p->fts_name, p->fts_namelen + 1); | ||||
723 | p->fts_info = fts_stat(sp, p, 0, dirfd(dirp)); | ||||
724 | } else { | ||||
725 | p->fts_accpath = p->fts_name; | ||||
726 | p->fts_info = fts_stat(sp, p, 0, -1); | ||||
727 | } | ||||
728 | |||||
729 | /* Decrement link count if applicable. */ | ||||
730 | if (nlinks
| ||||
731 | p->fts_info == FTS_DC2 || p->fts_info == FTS_DOT5)) | ||||
732 | --nlinks; | ||||
733 | } | ||||
734 | |||||
735 | /* We walk in directory order so "ls -f" doesn't get upset. */ | ||||
736 | p->fts_link = NULL((void *)0); | ||||
737 | if (head
| ||||
738 | head = tail = p; | ||||
739 | else { | ||||
740 | tail->fts_link = p; | ||||
741 | tail = p; | ||||
742 | } | ||||
743 | ++nitems; | ||||
744 | } | ||||
745 | if (dirp
| ||||
746 | (void)closedir(dirp); | ||||
747 | |||||
748 | /* | ||||
749 | * If realloc() changed the address of the path, adjust the | ||||
750 | * addresses for the rest of the tree and the dir list. | ||||
751 | */ | ||||
752 | if (doadjust
| ||||
753 | fts_padjust(sp, head); | ||||
754 | |||||
755 | /* | ||||
756 | * If not changing directories, reset the path back to original | ||||
757 | * state. | ||||
758 | */ | ||||
759 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
760 | if (len == sp->fts_pathlen || nitems == 0) | ||||
761 | --cp; | ||||
| |||||
762 | *cp = '\0'; | ||||
763 | } | ||||
764 | |||||
765 | /* | ||||
766 | * If descended after called from fts_children or after called from | ||||
767 | * fts_read and nothing found, get back. At the root level we use | ||||
768 | * the saved fd; if one of fts_open()'s arguments is a relative path | ||||
769 | * to an empty directory, we wind up here with no other way back. If | ||||
770 | * can't get back, we're done. | ||||
771 | */ | ||||
772 | if (descend && (type == BCHILD1 || !nitems) && | ||||
773 | (cur->fts_level == FTS_ROOTLEVEL0 ? FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd)) : | ||||
774 | fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { | ||||
775 | cur->fts_info = FTS_ERR7; | ||||
776 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
777 | return (NULL((void *)0)); | ||||
778 | } | ||||
779 | |||||
780 | /* If didn't find anything, return NULL. */ | ||||
781 | if (!nitems) { | ||||
782 | if (type == BREAD3) | ||||
783 | cur->fts_info = FTS_DP6; | ||||
784 | return (NULL((void *)0)); | ||||
785 | } | ||||
786 | |||||
787 | /* Sort the entries. */ | ||||
788 | if (sp->fts_compar && nitems > 1) | ||||
789 | head = fts_sort(sp, head, nitems); | ||||
790 | return (head); | ||||
791 | } | ||||
792 | |||||
793 | static u_short | ||||
794 | fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) | ||||
795 | { | ||||
796 | FTSENT *t; | ||||
797 | dev_t dev; | ||||
798 | ino_t ino; | ||||
799 | struct stat *sbp, sb; | ||||
800 | int saved_errno; | ||||
801 | const char *path; | ||||
802 | |||||
803 | if (dfd == -1) { | ||||
804 | path = p->fts_accpath; | ||||
805 | dfd = AT_FDCWD-100; | ||||
806 | } else | ||||
807 | path = p->fts_name; | ||||
808 | |||||
809 | /* If user needs stat info, stat buffer already allocated. */ | ||||
810 | sbp = ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)) ? &sb : p->fts_statp; | ||||
811 | |||||
812 | /* | ||||
813 | * If doing a logical walk, or application requested FTS_FOLLOW, do | ||||
814 | * a stat(2). If that fails, check for a non-existent symlink. If | ||||
815 | * fail, set the errno from the stat call. | ||||
816 | */ | ||||
817 | if (ISSET(FTS_LOGICAL)(sp->fts_options & (0x0002)) || follow) { | ||||
818 | if (fstatat(dfd, path, sbp, 0)) { | ||||
819 | saved_errno = errno(*__errno()); | ||||
820 | if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW0x02)) { | ||||
821 | errno(*__errno()) = 0; | ||||
822 | return (FTS_SLNONE13); | ||||
823 | } | ||||
824 | p->fts_errno = saved_errno; | ||||
825 | goto err; | ||||
826 | } | ||||
827 | } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW0x02)) { | ||||
828 | p->fts_errno = errno(*__errno()); | ||||
829 | err: memset(sbp, 0, sizeof(struct stat)); | ||||
830 | return (FTS_NS10); | ||||
831 | } | ||||
832 | |||||
833 | if (S_ISDIR(sbp->st_mode)((sbp->st_mode & 0170000) == 0040000)) { | ||||
834 | /* | ||||
835 | * Set the device/inode. Used to find cycles and check for | ||||
836 | * crossing mount points. Also remember the link count, used | ||||
837 | * in fts_build to limit the number of stat calls. It is | ||||
838 | * understood that these fields are only referenced if fts_info | ||||
839 | * is set to FTS_D. | ||||
840 | */ | ||||
841 | dev = p->fts_dev = sbp->st_dev; | ||||
842 | ino = p->fts_ino = sbp->st_ino; | ||||
843 | p->fts_nlink = sbp->st_nlink; | ||||
844 | |||||
845 | if (ISDOT(p->fts_name)(p->fts_name[0] == '.' && (!p->fts_name[1] || ( p->fts_name[1] == '.' && !p->fts_name[2])))) | ||||
846 | return (FTS_DOT5); | ||||
847 | |||||
848 | /* | ||||
849 | * Cycle detection is done by brute force when the directory | ||||
850 | * is first encountered. If the tree gets deep enough or the | ||||
851 | * number of symbolic links to directories is high enough, | ||||
852 | * something faster might be worthwhile. | ||||
853 | */ | ||||
854 | for (t = p->fts_parent; | ||||
855 | t->fts_level >= FTS_ROOTLEVEL0; t = t->fts_parent) | ||||
856 | if (ino == t->fts_ino && dev == t->fts_dev) { | ||||
857 | p->fts_cycle = t; | ||||
858 | return (FTS_DC2); | ||||
859 | } | ||||
860 | return (FTS_D1); | ||||
861 | } | ||||
862 | if (S_ISLNK(sbp->st_mode)((sbp->st_mode & 0170000) == 0120000)) | ||||
863 | return (FTS_SL12); | ||||
864 | if (S_ISREG(sbp->st_mode)((sbp->st_mode & 0170000) == 0100000)) | ||||
865 | return (FTS_F8); | ||||
866 | return (FTS_DEFAULT3); | ||||
867 | } | ||||
868 | |||||
869 | static FTSENT * | ||||
870 | fts_sort(FTS *sp, FTSENT *head, int nitems) | ||||
871 | { | ||||
872 | FTSENT **ap, *p; | ||||
873 | |||||
874 | /* | ||||
875 | * Construct an array of pointers to the structures and call qsort(3). | ||||
876 | * Reassemble the array in the order returned by qsort. If unable to | ||||
877 | * sort for memory reasons, return the directory entries in their | ||||
878 | * current order. Allocate enough space for the current needs plus | ||||
879 | * 40 so don't realloc one entry at a time. | ||||
880 | */ | ||||
881 | if (nitems > sp->fts_nitems) { | ||||
882 | struct _ftsent **a; | ||||
883 | |||||
884 | if ((a = reallocarray(sp->fts_array, | ||||
885 | nitems + 40, sizeof(FTSENT *))) == NULL((void *)0)) { | ||||
886 | free(sp->fts_array); | ||||
887 | sp->fts_array = NULL((void *)0); | ||||
888 | sp->fts_nitems = 0; | ||||
889 | return (head); | ||||
890 | } | ||||
891 | sp->fts_nitems = nitems + 40; | ||||
892 | sp->fts_array = a; | ||||
893 | } | ||||
894 | for (ap = sp->fts_array, p = head; p; p = p->fts_link) | ||||
895 | *ap++ = p; | ||||
896 | qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); | ||||
897 | for (head = *(ap = sp->fts_array); --nitems; ++ap) | ||||
898 | ap[0]->fts_link = ap[1]; | ||||
899 | ap[0]->fts_link = NULL((void *)0); | ||||
900 | return (head); | ||||
901 | } | ||||
902 | |||||
903 | static FTSENT * | ||||
904 | fts_alloc(FTS *sp, const char *name, size_t namelen) | ||||
905 | { | ||||
906 | FTSENT *p; | ||||
907 | size_t len; | ||||
908 | |||||
909 | /* | ||||
910 | * The file name is a variable length array and no stat structure is | ||||
911 | * necessary if the user has set the nostat bit. Allocate the FTSENT | ||||
912 | * structure, the file name and the stat structure in one chunk, but | ||||
913 | * be careful that the stat structure is reasonably aligned. Since the | ||||
914 | * fts_name field is declared to be of size 1, the fts_name pointer is | ||||
915 | * namelen + 2 before the first possible address of the stat structure. | ||||
916 | */ | ||||
917 | len = sizeof(FTSENT) + namelen; | ||||
918 | if (!ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008))) | ||||
919 | len += sizeof(struct stat) + ALIGNBYTES(sizeof(long) - 1); | ||||
920 | if ((p = calloc(1, len)) == NULL((void *)0)) | ||||
921 | return (NULL((void *)0)); | ||||
922 | |||||
923 | p->fts_path = sp->fts_path; | ||||
924 | p->fts_namelen = namelen; | ||||
925 | p->fts_instr = FTS_NOINSTR3; | ||||
926 | if (!ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008))) | ||||
927 | p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2)(((unsigned long)(p->fts_name + namelen + 2) + (sizeof(long ) - 1)) &~(sizeof(long) - 1)); | ||||
928 | memcpy(p->fts_name, name, namelen); | ||||
929 | |||||
930 | return (p); | ||||
931 | } | ||||
932 | |||||
933 | static void | ||||
934 | fts_lfree(FTSENT *head) | ||||
935 | { | ||||
936 | FTSENT *p; | ||||
937 | |||||
938 | /* Free a linked list of structures. */ | ||||
939 | while ((p = head)) { | ||||
940 | head = head->fts_link; | ||||
941 | free(p); | ||||
942 | } | ||||
943 | } | ||||
944 | |||||
945 | /* | ||||
946 | * Allow essentially unlimited paths; find, rm, ls should all work on any tree. | ||||
947 | * Most systems will allow creation of paths much longer than PATH_MAX, even | ||||
948 | * though the kernel won't resolve them. Add the size (not just what's needed) | ||||
949 | * plus 256 bytes so don't realloc the path 2 bytes at a time. | ||||
950 | */ | ||||
951 | static int | ||||
952 | fts_palloc(FTS *sp, size_t more) | ||||
953 | { | ||||
954 | char *p; | ||||
955 | |||||
956 | /* | ||||
957 | * Check for possible wraparound. | ||||
958 | */ | ||||
959 | more += 256; | ||||
960 | if (sp->fts_pathlen + more < sp->fts_pathlen) { | ||||
961 | free(sp->fts_path); | ||||
962 | sp->fts_path = NULL((void *)0); | ||||
963 | errno(*__errno()) = ENAMETOOLONG63; | ||||
964 | return (1); | ||||
965 | } | ||||
966 | p = recallocarray(sp->fts_path, sp->fts_pathlen, | ||||
967 | sp->fts_pathlen + more, 1); | ||||
968 | if (p == NULL((void *)0)) { | ||||
969 | free(sp->fts_path); | ||||
970 | sp->fts_path = NULL((void *)0); | ||||
971 | return (1); | ||||
972 | } | ||||
973 | sp->fts_pathlen += more; | ||||
974 | sp->fts_path = p; | ||||
975 | return (0); | ||||
976 | } | ||||
977 | |||||
978 | /* | ||||
979 | * When the path is realloc'd, have to fix all of the pointers in structures | ||||
980 | * already returned. | ||||
981 | */ | ||||
982 | static void | ||||
983 | fts_padjust(FTS *sp, FTSENT *head) | ||||
984 | { | ||||
985 | FTSENT *p; | ||||
986 | char *addr = sp->fts_path; | ||||
987 | |||||
988 | #define ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; } { \ | ||||
989 | if ((p)->fts_accpath != (p)->fts_name) { \ | ||||
990 | (p)->fts_accpath = \ | ||||
991 | (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ | ||||
992 | } \ | ||||
993 | (p)->fts_path = addr; \ | ||||
994 | } | ||||
995 | /* Adjust the current set of children. */ | ||||
996 | for (p = sp->fts_child; p; p = p->fts_link) | ||||
997 | ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; }; | ||||
998 | |||||
999 | /* Adjust the rest of the tree, including the current level. */ | ||||
1000 | for (p = head; p->fts_level >= FTS_ROOTLEVEL0;) { | ||||
1001 | ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; }; | ||||
1002 | p = p->fts_link ? p->fts_link : p->fts_parent; | ||||
1003 | } | ||||
1004 | } | ||||
1005 | |||||
1006 | static size_t | ||||
1007 | fts_maxarglen(char * const *argv) | ||||
1008 | { | ||||
1009 | size_t len, max; | ||||
1010 | |||||
1011 | for (max = 0; *argv; ++argv) | ||||
1012 | if ((len = strlen(*argv)) > max) | ||||
1013 | max = len; | ||||
1014 | return (max + 1); | ||||
1015 | } | ||||
1016 | |||||
1017 | /* | ||||
1018 | * Change to dir specified by fd or p->fts_accpath without getting | ||||
1019 | * tricked by someone changing the world out from underneath us. | ||||
1020 | * Assumes p->fts_dev and p->fts_ino are filled in. | ||||
1021 | */ | ||||
1022 | static int | ||||
1023 | fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path) | ||||
1024 | { | ||||
1025 | int ret, oerrno, newfd; | ||||
1026 | struct stat sb; | ||||
1027 | |||||
1028 | newfd = fd; | ||||
1029 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
1030 | return (0); | ||||
1031 | if (fd == -1 && (newfd = open(path, O_RDONLY0x0000|O_DIRECTORY0x20000|O_CLOEXEC0x10000)) == -1) | ||||
1032 | return (-1); | ||||
1033 | if (fstat(newfd, &sb) == -1) { | ||||
1034 | ret = -1; | ||||
1035 | goto bail; | ||||
1036 | } | ||||
1037 | if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { | ||||
1038 | errno(*__errno()) = ENOENT2; /* disinformation */ | ||||
1039 | ret = -1; | ||||
1040 | goto bail; | ||||
1041 | } | ||||
1042 | ret = fchdir(newfd); | ||||
1043 | bail: | ||||
1044 | oerrno = errno(*__errno()); | ||||
1045 | if (fd == -1) | ||||
1046 | (void)close(newfd); | ||||
1047 | errno(*__errno()) = oerrno; | ||||
1048 | return (ret); | ||||
1049 | } |