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