]> git.saurik.com Git - apple/libc.git/blob - gen/fts.c
Libc-1158.20.4.tar.gz
[apple/libc.git] / gen / fts.c
1 /*
2 * Copyright (c) 1999, 2000, 2003, 2005, 2008, 2012, 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * Copyright (c) 1990, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. Neither the name of the University nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
50 */
51
52 /* $OpenBSD: fts.c,v 1.51 2015/09/12 13:32:24 guenther Exp $ */
53
54 #include <sys/param.h>
55 #include <sys/stat.h>
56
57 #include <assert.h>
58 #include <dirent.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <fts.h>
62 #include <limits.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <stdbool.h>
67 #include <sys/vnode.h>
68 #include <sys/attr.h>
69
70 #ifdef __BLOCKS__
71 #include <Block.h>
72 #endif /* __BLOCKS__ */
73 #include <malloc_private.h>
74
75 static FTSENT *fts_alloc(FTS *, char *, int);
76 static FTSENT *fts_build(FTS *, int);
77 static void fts_lfree(FTSENT *);
78 static void fts_load(FTS *, FTSENT *);
79 static size_t fts_maxarglen(char * const *);
80 static void fts_padjust(FTS *, FTSENT *);
81 static int fts_palloc(FTS *, size_t);
82 static FTSENT *fts_sort(FTS *, FTSENT *, int);
83 static u_short fts_stat(FTS *, FTSENT *, int, int);
84 static u_short fts_stat2(FTS *, FTSENT *, int, int, struct stat *);
85 static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
86
87 #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
88
89 #define CLR(opt) (sp->fts_options &= ~(opt))
90 #define ISSET(opt) (sp->fts_options & (opt))
91 #define SET(opt) (sp->fts_options |= (opt))
92
93 #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd))
94
95 /* fts_build flags */
96 #define BCHILD 1 /* fts_children */
97 #define BNAMES 2 /* fts_children, names only */
98 #define BREAD 3 /* fts_read */
99
100 /* 5653270
101 * For directories containing > 64k subdirectories (or HFS+ with > 64k files
102 * and subdirectories), struct stat's st_nlink (16 bits) will overflow. This
103 * causes the case with FTS_NOSTAT and FTS_PHYSICAL set to prematurely stop
104 * recursing into subdirectories, because of an optimization that expects
105 * st_nlink to be the number of subdirectories (once that number has been
106 * encountered, no further calls to stat should be needed).
107 *
108 * However, on Mac OS X, another optimization largely nullifies the st_nlink
109 * optimization. struct dirent contains d_type, which can distinguish
110 * directories from files without initially calling stat. So stat is only
111 * called on known directories, rather than on other files. With this
112 * optimization, the difference in also using the st_nlink optimization is
113 * pretty minimal (tests show an improvement of a percent or two, probably
114 * due to additional if statement clauses that need to be evaluated).
115 *
116 * So removing the st_nlink optimization code will fix the > 64k subdirectories
117 * problem. And if we replace the multiple if clause logic with a single
118 * switch statement, we can recover the minimal performance lose. We can
119 * go even further and for the case of FTS_NOSTAT and FTS_LOGICAL set, we
120 * can use d_type to also distinguish symbolic links, and so we only need to
121 * call stat on directories and symlinks, not on all files. This provides
122 * a significant performance boost in that special case.
123 */
124 /*
125 * The following macros defines values of the dostat variable, which is or-ed
126 * with the value of d_type, and the result used in a switch statement to
127 * determine whether to call stat or not. (We order the macros to minimize
128 * the size of any jump table that the compiler may generate.)
129 */
130 #define F_SHIFT 4 /* shift to leave space for d_type */
131 #define F_NOSTAT (0 << F_SHIFT) /* don't do any stat's */
132 #define F_STATDIRSYM (1 << F_SHIFT) /* only stat directories and symlinks (and unknowns) */
133 #define F_ALWAYSSTAT (2 << F_SHIFT) /* always stat */
134 #define F_STATDIR (3 << F_SHIFT) /* only stat directories (and unknowns) */
135 #define F_D_TYPE (4 << F_SHIFT) /* only stat directories but use d_type */
136 #define F_D_TYPESYM (5 << F_SHIFT) /* only stat directories and symlinks but use d_type */
137
138 static FTS *
139 __fts_open(char * const *argv, FTS *sp)
140 {
141 FTSENT *p, *root;
142 int nitems;
143 FTSENT *parent, *tmp;
144 int len;
145
146 /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
147 if (ISSET(FTS_LOGICAL))
148 SET(FTS_NOCHDIR);
149
150 /*
151 * Start out with 1K of path space, and enough, in any case,
152 * to hold the user's paths.
153 */
154 if (fts_palloc(sp, MAX(fts_maxarglen(argv), PATH_MAX)))
155 goto mem1;
156
157 /* Allocate/initialize root's parent. */
158 if ((parent = fts_alloc(sp, "", 0)) == NULL)
159 goto mem2;
160 parent->fts_level = FTS_ROOTPARENTLEVEL;
161
162 /* Allocate/initialize root(s). */
163 for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
164 /* Don't allow zero-length paths. */
165 if ((len = strlen(*argv)) == 0) {
166 errno = ENOENT;
167 goto mem3;
168 }
169
170 if ((p = fts_alloc(sp, *argv, len)) == NULL)
171 goto mem3;
172 p->fts_level = FTS_ROOTLEVEL;
173 p->fts_parent = parent;
174 p->fts_accpath = p->fts_name;
175 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOWDIR) ? -1 : ISSET(FTS_COMFOLLOW), -1);
176
177 /* Command-line "." and ".." are real directories. */
178 if (p->fts_info == FTS_DOT)
179 p->fts_info = FTS_D;
180
181 /*
182 * If comparison routine supplied, traverse in sorted
183 * order; otherwise traverse in the order specified.
184 */
185 if (sp->fts_compar) {
186 p->fts_link = root;
187 root = p;
188 } else {
189 p->fts_link = NULL;
190 if (root == NULL)
191 tmp = root = p;
192 else {
193 tmp->fts_link = p;
194 tmp = p;
195 }
196 }
197 }
198 if (sp->fts_compar && nitems > 1)
199 root = fts_sort(sp, root, nitems);
200
201 /*
202 * Allocate a dummy pointer and make fts_read think that we've just
203 * finished the node before the root(s); set p->fts_info to FTS_INIT
204 * so that everything about the "current" node is ignored.
205 */
206 if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
207 goto mem3;
208 sp->fts_cur->fts_link = root;
209 sp->fts_cur->fts_info = FTS_INIT;
210
211 /*
212 * If using chdir(2), grab a file descriptor pointing to dot to ensure
213 * that we can get back here; this could be avoided for some paths,
214 * but almost certainly not worth the effort. Slashes, symbolic links,
215 * and ".." are all fairly nasty problems. Note, if we can't get the
216 * descriptor we run anyway, just more slowly.
217 */
218 if (!ISSET(FTS_NOCHDIR) &&
219 (sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC)) < 0)
220 SET(FTS_NOCHDIR);
221
222 if (nitems == 0)
223 free(parent);
224
225 return (sp);
226
227 mem3: fts_lfree(root);
228 free(parent);
229 mem2: free(sp->fts_path);
230 mem1: free(sp);
231 return (NULL);
232 }
233
234 FTS *
235 fts_open(char * const *argv, int options, int (*compar)())
236 {
237 FTS *sp;
238
239 /* Options check. */
240 if (options & ~FTS_OPTIONMASK) {
241 errno = EINVAL;
242 return (NULL);
243 }
244 if (options & FTS_NOSTAT_TYPE) options |= FTS_NOSTAT;
245
246 /* Allocate/initialize the stream */
247 if ((sp = calloc(1, sizeof(FTS))) == NULL)
248 return (NULL);
249 sp->fts_compar = compar;
250 sp->fts_options = options;
251
252 return __fts_open(argv, sp);
253 }
254
255 #ifdef __BLOCKS__
256 FTS *
257 fts_open_b(char * const *argv, int options, int (^compar)(const FTSENT **, const FTSENT **))
258 {
259 FTS *sp;
260
261 /* Options check. */
262 if (options & ~FTS_OPTIONMASK) {
263 errno = EINVAL;
264 return (NULL);
265 }
266 if (options & FTS_NOSTAT_TYPE) options |= FTS_NOSTAT;
267
268 /* Allocate/initialize the stream */
269 if ((sp = calloc(1, sizeof(FTS))) == NULL)
270 return (NULL);
271 sp->fts_compar_b = (int (^)())Block_copy(compar);
272 sp->fts_options = options | FTS_BLOCK_COMPAR;
273
274 return __fts_open(argv, sp);
275 }
276 #endif /* __BLOCKS__ */
277
278 static void
279 fts_load(FTS *sp, FTSENT *p)
280 {
281 int len;
282 char *cp;
283
284 /*
285 * Load the stream structure for the next traversal. Since we don't
286 * actually enter the directory until after the preorder visit, set
287 * the fts_accpath field specially so the chdir gets done to the right
288 * place and the user can access the first node. From fts_open it's
289 * known that the path will fit.
290 */
291 len = p->fts_pathlen = p->fts_namelen;
292 memmove(sp->fts_path, p->fts_name, len + 1);
293 if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
294 len = strlen(++cp);
295 memmove(p->fts_name, cp, len + 1);
296 p->fts_namelen = len;
297 }
298 p->fts_accpath = p->fts_path = sp->fts_path;
299 sp->fts_dev = p->fts_dev;
300 }
301
302 int
303 fts_close(FTS *sp)
304 {
305 FTSENT *freep, *p;
306 int rfd, error = 0;
307
308 /*
309 * This still works if we haven't read anything -- the dummy structure
310 * points to the root list, so we step through to the end of the root
311 * list which has a valid parent pointer.
312 */
313 if (sp->fts_cur) {
314 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
315 freep = p;
316 p = p->fts_link ? p->fts_link : p->fts_parent;
317 free(freep);
318 }
319 free(p);
320 }
321
322 /* Stash the original directory fd if needed. */
323 rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;
324
325 /* Free up child linked list, sort array, path buffer, stream ptr.*/
326 if (sp->fts_child){
327 fts_lfree(sp->fts_child);
328 }
329 free(sp->fts_array); sp->fts_array = NULL;
330 free(sp->fts_path); sp->fts_path = NULL;
331
332 #ifdef __BLOCKS__
333 /* Free up any block pointer. */
334 if (ISSET(FTS_BLOCK_COMPAR) && sp->fts_compar_b != NULL)
335 Block_release(sp->fts_compar_b);
336 #endif /* __BLOCKS__ */
337
338 /* Free up the stream pointer. */
339 free(sp);
340
341 /* Return to original directory, checking for error. */
342 if (rfd != -1) {
343 int saved_errno = errno;
344 if (fchdir(rfd) != 0){
345 error = -1;
346 saved_errno = errno;
347 }
348 if (close(rfd) != 0){
349 error = -1;
350 saved_errno = errno;
351 }
352 errno = saved_errno;
353 }
354
355 return (error);
356 }
357
358 /*
359 * Special case a root of "/" so that slashes aren't appended which would
360 * cause paths to be written as "//foo".
361 */
362 #define NAPPEND(p) \
363 (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
364 p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
365
366 FTSENT *
367 fts_read(FTS *sp)
368 {
369 FTSENT *p, *tmp;
370 int instr;
371 char *t;
372 int saved_errno;
373
374 /* If finished or unrecoverable error, return NULL. */
375 if (sp->fts_cur == NULL || ISSET(FTS_STOP))
376 return (NULL);
377
378 /* Set current node pointer. */
379 p = sp->fts_cur;
380
381 /* Save and zero out user instructions. */
382 instr = p->fts_instr;
383 p->fts_instr = FTS_NOINSTR;
384
385 /* Any type of file may be re-visited; re-stat and re-turn. */
386 if (instr == FTS_AGAIN) {
387 p->fts_info = fts_stat(sp, p, 0, -1);
388 return (p);
389 }
390
391 /*
392 * Following a symlink -- SLNONE test allows application to see
393 * SLNONE and recover. If indirecting through a symlink, have
394 * keep a pointer to current location. If unable to get that
395 * pointer, follow fails.
396 */
397 if (instr == FTS_FOLLOW &&
398 (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
399 p->fts_info = fts_stat(sp, p, 1, -1);
400 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
401 if ((p->fts_symfd =
402 open(".", O_RDONLY | O_CLOEXEC)) < 0) {
403 p->fts_errno = errno;
404 p->fts_info = FTS_ERR;
405 } else {
406 p->fts_flags |= FTS_SYMFOLLOW;
407 }
408 }
409 return (p);
410 }
411
412 /* Directory in pre-order. */
413 if (p->fts_info == FTS_D) {
414 /* If skipped or crossed mount point, do post-order visit. */
415 if (instr == FTS_SKIP ||
416 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
417 if (p->fts_flags & FTS_SYMFOLLOW)
418 (void)close(p->fts_symfd);
419 if (sp->fts_child) {
420 fts_lfree(sp->fts_child);
421 sp->fts_child = NULL;
422 }
423 p->fts_info = FTS_DP;
424 return (p);
425 }
426
427 /* Rebuild if only read the names and now traversing. */
428 if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
429 CLR(FTS_NAMEONLY);
430 fts_lfree(sp->fts_child);
431 sp->fts_child = NULL;
432 }
433
434 /*
435 * Cd to the subdirectory.
436 *
437 * If have already read and now fail to chdir, whack the list
438 * to make the names come out right, and set the parent errno
439 * so the application will eventually get an error condition.
440 * Set the FTS_DONTCHDIR flag so that when we logically change
441 * directories back to the parent we don't do a chdir.
442 *
443 * If haven't read do so. If the read fails, fts_build sets
444 * FTS_STOP or the fts_info field of the node.
445 */
446 if (sp->fts_child) {
447 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
448 p->fts_errno = errno;
449 p->fts_flags |= FTS_DONTCHDIR;
450 for (p = sp->fts_child; p; p = p->fts_link)
451 p->fts_accpath =
452 p->fts_parent->fts_accpath;
453 }
454 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
455 if (ISSET(FTS_STOP))
456 return (NULL);
457 return (p);
458 }
459 p = sp->fts_child;
460 sp->fts_child = NULL;
461 goto name;
462 }
463
464 /* Move to the next node on this level. */
465 next: tmp = p;
466 if ((p = p->fts_link)) {
467 free(tmp);
468
469 /*
470 * If reached the top, return to the original directory (or
471 * the root of the tree), and load the paths for the next root.
472 */
473 if (p->fts_level == FTS_ROOTLEVEL) {
474 if (FCHDIR(sp, sp->fts_rfd)) {
475 SET(FTS_STOP);
476 return (NULL);
477 }
478 fts_load(sp, p);
479 return (sp->fts_cur = p);
480 }
481
482 /*
483 * User may have called fts_set on the node. If skipped,
484 * ignore. If followed, get a file descriptor so we can
485 * get back if necessary.
486 */
487 if (p->fts_instr == FTS_SKIP) {
488 goto next;
489 }
490 if (p->fts_instr == FTS_FOLLOW) {
491 p->fts_info = fts_stat(sp, p, 1, -1);
492 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
493 if ((p->fts_symfd =
494 open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
495 p->fts_errno = errno;
496 p->fts_info = FTS_ERR;
497 } else {
498 p->fts_flags |= FTS_SYMFOLLOW;
499 }
500 }
501 p->fts_instr = FTS_NOINSTR;
502 }
503
504 name: t = sp->fts_path + NAPPEND(p->fts_parent);
505 *t++ = '/';
506 memmove(t, p->fts_name, p->fts_namelen + 1);
507 return (sp->fts_cur = p);
508 }
509
510 /* Move up to the parent node. */
511 p = tmp->fts_parent;
512 free(tmp);
513
514 if (p->fts_level == FTS_ROOTPARENTLEVEL) {
515 /*
516 * Done; free everything up and set errno to 0 so the user
517 * can distinguish between error and EOF.
518 */
519 free(p);
520 errno = 0;
521 return (sp->fts_cur = NULL);
522 }
523
524 /* NUL terminate the pathname. */
525 sp->fts_path[p->fts_pathlen] = '\0';
526
527 /*
528 * Return to the parent directory. If at a root node or came through
529 * a symlink, go back through the file descriptor. Otherwise, cd up
530 * one directory.
531 */
532 if (p->fts_level == FTS_ROOTLEVEL) {
533 if (FCHDIR(sp, sp->fts_rfd)) {
534 SET(FTS_STOP);
535 sp->fts_cur = p;
536 return (NULL);
537 }
538 } else if (p->fts_flags & FTS_SYMFOLLOW) {
539 if (FCHDIR(sp, p->fts_symfd)) {
540 saved_errno = errno;
541 (void)close(p->fts_symfd);
542 errno = saved_errno;
543 SET(FTS_STOP);
544 sp->fts_cur = p;
545 return (NULL);
546 }
547 (void)close(p->fts_symfd);
548 } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
549 fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
550 SET(FTS_STOP);
551 sp->fts_cur = p;
552 return (NULL);
553 }
554 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
555 return (sp->fts_cur = p);
556 }
557
558 /*
559 * Fts_set takes the stream as an argument although it's not used in this
560 * implementation; it would be necessary if anyone wanted to add global
561 * semantics to fts using fts_set. An error return is allowed for similar
562 * reasons.
563 */
564 /* ARGSUSED */
565 int
566 fts_set(FTS __unused *sp, FTSENT *p, int instr)
567 {
568 if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
569 instr != FTS_NOINSTR && instr != FTS_SKIP) {
570 errno = EINVAL;
571 return (1);
572 }
573 p->fts_instr = instr;
574 return (0);
575 }
576
577 FTSENT *
578 fts_children(FTS *sp, int instr)
579 {
580 FTSENT *p;
581 int fd;
582
583 if (instr && instr != FTS_NAMEONLY) {
584 errno = EINVAL;
585 return (NULL);
586 }
587
588 /* Set current node pointer. */
589 p = sp->fts_cur;
590
591 /*
592 * Errno set to 0 so user can distinguish empty directory from
593 * an error.
594 */
595 errno = 0;
596
597 /* Fatal errors stop here. */
598 if (ISSET(FTS_STOP))
599 return (NULL);
600
601 /* Return logical hierarchy of user's arguments. */
602 if (p->fts_info == FTS_INIT)
603 return (p->fts_link);
604
605 /*
606 * If not a directory being visited in pre-order, stop here. Could
607 * allow FTS_DNR, assuming the user has fixed the problem, but the
608 * same effect is available with FTS_AGAIN.
609 */
610 if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
611 return (NULL);
612
613 /* Free up any previous child list. */
614 if (sp->fts_child)
615 fts_lfree(sp->fts_child);
616
617 if (instr == FTS_NAMEONLY) {
618 SET(FTS_NAMEONLY);
619 instr = BNAMES;
620 } else
621 instr = BCHILD;
622
623 /*
624 * If using chdir on a relative path and called BEFORE fts_read does
625 * its chdir to the root of a traversal, we can lose -- we need to
626 * chdir into the subdirectory, and we don't know where the current
627 * directory is, so we can't get back so that the upcoming chdir by
628 * fts_read will work.
629 */
630 if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
631 ISSET(FTS_NOCHDIR))
632 return (sp->fts_child = fts_build(sp, instr));
633
634 if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
635 return (NULL);
636 sp->fts_child = fts_build(sp, instr);
637 if (fchdir(fd)) {
638 (void)close(fd);
639 return (NULL);
640 }
641 (void)close(fd);
642 return (sp->fts_child);
643 }
644
645 typedef struct __attribute__((packed)) {
646 uint32_t length;
647
648 /* common attributes */
649 attribute_set_t attrset;
650 attrreference_t name;
651 dev_t st_dev;
652 fsobj_type_t objtype;
653 struct timespec st_birthtimespec;
654 struct timespec st_mtimespec;
655 struct timespec st_ctimespec;
656 struct timespec st_atimespec;
657 uid_t st_uid;
658 gid_t st_gid;
659 uint32_t accessmask;
660 uint32_t st_flags;
661 uint64_t st_ino;
662
663 /* non-directory attributes */
664 uint32_t st_nlink;
665 off_t allocsize;
666 uint32_t st_blksize;
667 uint32_t st_rdev;
668 off_t st_size;
669 } attrListAttributes;
670
671 typedef struct __attribute__((packed)) {
672 uint32_t length;
673
674 /* common attributes */
675 attribute_set_t attrset;
676 attrreference_t name;
677 dev_t st_dev;
678 fsobj_type_t objtype;
679 uint64_t st_ino;
680
681 /* non-directory attributes */
682 uint32_t st_nlink;
683 uint32_t st_rdev;
684 } attrListAttributes_nostat;
685
686 #define ATTR_BUF_SIZE (32*1024)
687
688 typedef struct {
689 DIR *dirp;
690
691 struct attrlist requested_attrs;
692 void *attrbuf;
693 union {
694 attrListAttributes *curattr;
695 attrListAttributes_nostat *curattr_nostat;
696 };
697 int dirfd;
698 bool done;
699 bool nostat;
700 bool needs_dot;
701 bool needs_dotdot;
702
703 int entry_count;
704 int cur_entry;
705 } dir_handle;
706
707 typedef struct {
708 char *d_name;
709 size_t d_namlen;
710 struct stat sb;
711 int d_type;
712 bool stat_valid;
713 } dir_entry;
714
715 static bool
716 advance_directory(dir_handle *handle)
717 {
718 if (handle->done) return true;
719
720 assert(handle->dirfd != -1);
721 handle->entry_count = getattrlistbulk(handle->dirfd, &handle->requested_attrs,
722 handle->attrbuf, ATTR_BUF_SIZE, FSOPT_PACK_INVAL_ATTRS);
723 if (handle->entry_count == -1) {
724 goto error;
725 } else if (handle->entry_count == 0) {
726 /* No more entries. */
727 handle->done = true;
728 }
729 handle->cur_entry = 0;
730 handle->curattr = handle->attrbuf;
731 return true;
732
733 error: {
734 int saved_errno = errno;
735 close(handle->dirfd);
736 handle->dirfd = -1;
737 errno = saved_errno;
738 return false;
739 }
740 }
741
742 static bool
743 open_directory(FTS *sp, dir_handle *handle, const char *path)
744 {
745 memset(handle, 0, sizeof(*handle));
746
747 handle->nostat = ISSET(FTS_NOSTAT);
748 handle->needs_dot = handle->needs_dotdot = ISSET(FTS_SEEDOT);
749
750 handle->dirfd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC);
751 if (handle->dirfd == -1) goto fallback;
752
753 handle->attrbuf = malloc(ATTR_BUF_SIZE);
754 if (!handle->attrbuf) goto fallback;
755
756 handle->requested_attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
757 if (!handle->nostat) {
758 handle->requested_attrs.commonattr =
759 ATTR_CMN_RETURNED_ATTRS|
760 ATTR_CMN_NAME|ATTR_CMN_DEVID|ATTR_CMN_OBJTYPE|
761 ATTR_CMN_CRTIME|ATTR_CMN_MODTIME|ATTR_CMN_CHGTIME|ATTR_CMN_ACCTIME|
762 ATTR_CMN_OWNERID|ATTR_CMN_GRPID|ATTR_CMN_ACCESSMASK|ATTR_CMN_FLAGS|
763 ATTR_CMN_FILEID;
764 handle->requested_attrs.fileattr = ATTR_FILE_LINKCOUNT|ATTR_FILE_ALLOCSIZE|
765 ATTR_FILE_IOBLOCKSIZE|ATTR_FILE_DEVTYPE|ATTR_FILE_DATALENGTH;
766 } else {
767 handle->requested_attrs.commonattr = ATTR_CMN_RETURNED_ATTRS|
768 ATTR_CMN_NAME|ATTR_CMN_DEVID|ATTR_CMN_OBJTYPE|
769 ATTR_CMN_FILEID;
770 handle->requested_attrs.fileattr = ATTR_FILE_LINKCOUNT;
771 }
772
773 if (advance_directory(handle)) {
774 /*
775 * We successfully read the first attribute buffer,
776 * so we'll use getdirentriesattr/getattrlistbulk.
777 */
778 return true;
779 }
780
781 fallback:
782 if (handle->dirfd != -1) close(handle->dirfd);
783 handle->dirfd = -1;
784 free(handle->attrbuf);
785 handle->attrbuf = NULL;
786 handle->dirp = opendir(path);
787 return (handle->dirp != NULL);
788 }
789
790 static bool
791 read_dirent(dir_handle *handle, dir_entry *entry)
792 {
793 if (handle->dirp) {
794 struct dirent *de = readdir(handle->dirp);
795 if (de == NULL) return false;
796 entry->d_name = de->d_name;
797 entry->d_namlen = de->d_namlen;
798 entry->d_type = de->d_type;
799 entry->stat_valid = false;
800 return true;
801 }
802
803 /*
804 * There are three states that our dir_handle can be in:
805 * - real getattrlistbulk use (entry_count >= 0) - the rest of this function
806 * - fallback - handled above with dirp
807 * - closed fallback (dirp == NULL) - handled with this check
808 */
809 if (handle->dirfd == -1) {
810 return false;
811 }
812
813 if (handle->needs_dot) {
814 handle->needs_dot = false;
815 entry->d_name = ".";
816 entry->d_namlen = strlen(".");
817 entry->d_type = DT_DIR;
818 entry->stat_valid = false;
819 return true;
820 } else if (handle->needs_dotdot) {
821 handle->needs_dotdot = false;
822 entry->d_name = "..";
823 entry->d_namlen = strlen("..");
824 entry->d_type = DT_DIR;
825 entry->stat_valid = false;
826 return true;
827 }
828
829 if (handle->cur_entry == handle->entry_count) {
830 if (handle->done) return false; /* Already done with the directory. */
831 if (!advance_directory(handle)) return false; /* Reading the next buffer failed. */
832 if (handle->done) return false; /* We're now done with the directory. */
833 }
834
835 bzero(entry, sizeof(*entry));
836
837 attrListAttributes *curattr_stat = NULL;
838 attrListAttributes_nostat *curattr_nostat = NULL;
839 if (!handle->nostat) {
840 curattr_stat = handle->curattr;
841 handle->cur_entry++;
842 handle->curattr = (attrListAttributes*)(((char*)curattr_stat) + curattr_stat->length);
843
844 entry->d_name = ((char*)&curattr_stat->name) + curattr_stat->name.attr_dataoffset;
845 /* attr_length includes the null terminator, but readdir's d_namlen doesn't. */
846 entry->d_namlen = curattr_stat->name.attr_length - 1;
847 } else {
848 curattr_nostat = handle->curattr_nostat;
849 handle->cur_entry++;
850 handle->curattr_nostat = (attrListAttributes_nostat*)(((char*)curattr_nostat) + curattr_nostat->length);
851
852 entry->d_name = ((char*)&curattr_nostat->name) + curattr_nostat->name.attr_dataoffset;
853 /* attr_length includes the null terminator, but readdir's d_namlen doesn't. */
854 entry->d_namlen = curattr_nostat->name.attr_length - 1;
855 }
856
857 int stat_type = 0;
858
859 switch (handle->nostat ? curattr_nostat->objtype : curattr_stat->objtype) {
860 case VREG:
861 entry->d_type = DT_REG;
862 stat_type = S_IFREG;
863 break;
864 case VDIR:
865 entry->d_type = DT_DIR;
866 /* Force a stat call so we don't have to guess on st_size, st_blocks, etc. */
867 // stat_type = S_IFDIR;
868 break;
869 case VBLK:
870 entry->d_type = DT_BLK;
871 stat_type = S_IFBLK;
872 break;
873 case VCHR:
874 entry->d_type = DT_CHR;
875 stat_type = S_IFCHR;
876 break;
877 case VLNK:
878 entry->d_type = DT_LNK;
879 stat_type = S_IFLNK;
880 break;
881 case VSOCK:
882 entry->d_type = DT_SOCK;
883 stat_type = S_IFSOCK;
884 break;
885 case VFIFO:
886 entry->d_type = DT_FIFO;
887 stat_type = S_IFIFO;
888 break;
889 default:
890 entry->d_type = DT_UNKNOWN;
891 break;
892 }
893
894 if (!handle->nostat && stat_type) {
895 entry->stat_valid = true;
896
897 /*
898 * Make sure we got all the attributes we need to fill out a stat structure.
899 */
900
901 attrgroup_t requiredCommon = handle->requested_attrs.commonattr;
902 attrgroup_t requiredFile = handle->requested_attrs.fileattr;
903
904 if ((entry->d_type != DT_BLK) && (entry->d_type != DT_CHR)) {
905 /* It's okay for ATTR_FILE_DEVTYPE to be missing if the entry isn't a block or character device. */
906 curattr_stat->st_rdev = 0;
907 requiredFile &= ~ATTR_FILE_DEVTYPE;
908 }
909
910 if ((curattr_stat->attrset.commonattr & requiredCommon) != requiredCommon ||
911 (curattr_stat->attrset.fileattr & requiredFile) != requiredFile) {
912 /* Some of our required attributes are missing. */
913 entry->stat_valid = false;
914 }
915 } else {
916 entry->stat_valid = false;
917 }
918
919 if (entry->stat_valid) {
920
921 #define COPY_FIELD(fld) entry->sb.fld = curattr_stat->fld
922 COPY_FIELD(st_dev);
923 /* COPY_FIELD(st_mode); Handled below. */
924 COPY_FIELD(st_nlink);
925 COPY_FIELD(st_ino);
926 COPY_FIELD(st_uid);
927 COPY_FIELD(st_gid);
928 COPY_FIELD(st_rdev);
929 COPY_FIELD(st_atimespec);
930 COPY_FIELD(st_mtimespec);
931 COPY_FIELD(st_ctimespec);
932 #if __DARWIN_64_BIT_INO_T
933 COPY_FIELD(st_birthtimespec);
934 #endif /* __DARWIN_64_BIT_INO_T */
935 COPY_FIELD(st_size);
936 /* COPY_FIELD(st_blocks); Handled below. */
937 COPY_FIELD(st_blksize);
938 COPY_FIELD(st_flags);
939 #undef COPY_FIELD
940
941 /* We have to handle some fields specially. */
942 entry->sb.st_mode = (curattr_stat->accessmask & ~S_IFMT) | stat_type;
943 entry->sb.st_blocks = howmany(curattr_stat->allocsize, 512); /* Matches vn_stat implementation in xnu. */
944 }
945 return true;
946 }
947
948 static int
949 dir_fd(dir_handle *handle)
950 {
951 return handle->dirp ? dirfd(handle->dirp) : handle->dirfd;
952 }
953
954 static void
955 close_directory(dir_handle *handle)
956 {
957 if (handle->dirp) {
958 closedir(handle->dirp);
959 handle->dirp = NULL;
960 }
961 if (handle->dirfd != -1) {
962 close(handle->dirfd);
963 handle->dirfd = -1;
964 }
965 free(handle->attrbuf);
966 handle->attrbuf = NULL;
967 }
968
969 /*
970 * This is the tricky part -- do not casually change *anything* in here. The
971 * idea is to build the linked list of entries that are used by fts_children
972 * and fts_read. There are lots of special cases.
973 *
974 * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
975 * set, we can use d_type to determine if the entry is a directory (or for
976 * logical walks, a directory or symlink) and not call stat for other file
977 * types. This cuts the number of stat calls significantly.
978 */
979 static FTSENT *
980 fts_build(FTS *sp, int type)
981 {
982 dir_entry de = { 0 };
983 dir_entry *dp = &de;
984 FTSENT *p, *head;
985 int nitems;
986 FTSENT *cur, *tail;
987 dir_handle dirp;
988 void *oldaddr;
989 int len, maxlen;
990 int cderrno, descend, level, dostat, doadjust;
991 int saved_errno;
992 char *cp;
993
994 /* Set current node pointer. */
995 cur = sp->fts_cur;
996
997 /*
998 * Open the directory for reading. If this fails, we're done.
999 * If being called from fts_read, set the fts_info field.
1000 */
1001 if (!open_directory(sp, &dirp, cur->fts_accpath)) {
1002 if (type == BREAD) {
1003 cur->fts_info = FTS_DNR;
1004 cur->fts_errno = errno;
1005 }
1006 return (NULL);
1007 }
1008
1009 if (type == BNAMES)
1010 dostat = F_NOSTAT;
1011 else if (ISSET(FTS_NOSTAT_TYPE))
1012 dostat = ISSET(FTS_PHYSICAL) ? F_D_TYPE : F_D_TYPESYM;
1013 else if (ISSET(FTS_NOSTAT))
1014 dostat = ISSET(FTS_PHYSICAL) ? F_STATDIR : F_STATDIRSYM;
1015 else
1016 dostat = F_ALWAYSSTAT;
1017
1018 #ifdef notdef
1019 (void)printf("dostat == %d\n", dostat);
1020 (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
1021 ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
1022 #endif
1023 /*
1024 * If we're going to need to stat anything or we want to descend
1025 * and stay in the directory, chdir. If this fails we keep going,
1026 * but set a flag so we don't chdir after the post-order visit.
1027 * We won't be able to stat anything, but we can still return the
1028 * names themselves. Note, that since fts_read won't be able to
1029 * chdir into the directory, it will have to return different path
1030 * names than before, i.e. "a/b" instead of "b". Since the node
1031 * has already been visited in pre-order, have to wait until the
1032 * post-order visit to return the error. There is a special case
1033 * here, if there was nothing to stat then it's not an error to
1034 * not be able to stat. This is all fairly nasty. If a program
1035 * needed sorted entries or stat information, they had better be
1036 * checking FTS_NS on the returned nodes.
1037 */
1038 cderrno = 0;
1039 if (dostat || type == BREAD) {
1040 if (fts_safe_changedir(sp, cur, dir_fd(&dirp), NULL)) {
1041 if (dostat && type == BREAD) {
1042 cur->fts_errno = errno;
1043 }
1044 cur->fts_flags |= FTS_DONTCHDIR;
1045 descend = 0;
1046 cderrno = errno;
1047 close_directory(&dirp);
1048 } else {
1049 descend = 1;
1050 }
1051 } else {
1052 descend = 0;
1053 }
1054
1055 /*
1056 * Figure out the max file name length that can be stored in the
1057 * current path -- the inner loop allocates more path as necessary.
1058 * We really wouldn't have to do the maxlen calculations here, we
1059 * could do them in fts_read before returning the path, but it's a
1060 * lot easier here since the length is part of the dirent structure.
1061 *
1062 * If not changing directories set a pointer so that can just append
1063 * each new name into the path.
1064 */
1065 len = NAPPEND(cur);
1066 if (ISSET(FTS_NOCHDIR)) {
1067 cp = sp->fts_path + len;
1068 *cp++ = '/';
1069 }
1070 len++;
1071 maxlen = sp->fts_pathlen - len;
1072
1073 /*
1074 * fts_level is signed so we must prevent it from wrapping
1075 * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL.
1076 */
1077 level = cur->fts_level;
1078 if (level < FTS_MAXLEVEL)
1079 level++;
1080
1081 /* Read the directory, attaching each entry to the `link' pointer. */
1082 doadjust = 0;
1083 for (head = tail = NULL, nitems = 0; read_dirent(&dirp, dp) ; ) {
1084 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
1085 continue;
1086
1087 if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL)
1088 goto mem1;
1089 if (dp->d_namlen >= (size_t)maxlen) { /* include space for NUL */
1090 oldaddr = sp->fts_path;
1091 if (fts_palloc(sp, dp->d_namlen +len + 1)) {
1092 /*
1093 * No more memory for path or structures. Save
1094 * errno, free up the current structure and the
1095 * structures already allocated.
1096 */
1097 mem1: saved_errno = errno;
1098 free(p);
1099 fts_lfree(head);
1100 close_directory(&dirp);
1101 cur->fts_info = FTS_ERR;
1102 SET(FTS_STOP);
1103 errno = saved_errno;
1104 return (NULL);
1105 }
1106 /* Did realloc() change the pointer? */
1107 if (oldaddr != sp->fts_path) {
1108 doadjust = 1;
1109 if (ISSET(FTS_NOCHDIR))
1110 cp = sp->fts_path + len;
1111 }
1112 maxlen = sp->fts_pathlen - len;
1113 }
1114
1115 if (len + dp->d_namlen >= USHRT_MAX) {
1116 /*
1117 * In an FTSENT, fts_pathlen is a u_short so it is
1118 * possible to wraparound here. If we do, free up
1119 * the current structure and the structures already
1120 * allocated, then error out with ENAMETOOLONG.
1121 */
1122 free(p);
1123 fts_lfree(head);
1124 close_directory(&dirp);
1125 cur->fts_info = FTS_ERR;
1126 SET(FTS_STOP);
1127 errno = ENAMETOOLONG;
1128 return (NULL);
1129 }
1130 p->fts_level = level;
1131 p->fts_parent = sp->fts_cur;
1132 p->fts_pathlen = len + dp->d_namlen;
1133
1134 if (cderrno) {
1135 if (dostat) {
1136 p->fts_info = FTS_NS;
1137 p->fts_errno = cderrno;
1138 } else
1139 p->fts_info = FTS_NSOK;
1140 p->fts_accpath = cur->fts_accpath;
1141 } else {
1142 /*
1143 * We need to know all file types values that d_type may
1144 * be set to. So if that changes, the following needs
1145 * to be modified appropriately.
1146 */
1147 switch(dostat | dp->d_type) {
1148 case (F_STATDIR | DT_UNKNOWN):
1149 case (F_STATDIR | DT_DIR):
1150 case (F_STATDIRSYM | DT_UNKNOWN):
1151 case (F_STATDIRSYM | DT_DIR):
1152 case (F_STATDIRSYM | DT_LNK):
1153 case (F_ALWAYSSTAT | DT_UNKNOWN):
1154 case (F_ALWAYSSTAT | DT_FIFO):
1155 case (F_ALWAYSSTAT | DT_CHR):
1156 case (F_ALWAYSSTAT | DT_DIR):
1157 case (F_ALWAYSSTAT | DT_BLK):
1158 case (F_ALWAYSSTAT | DT_REG):
1159 case (F_ALWAYSSTAT | DT_LNK):
1160 case (F_ALWAYSSTAT | DT_SOCK):
1161 case (F_ALWAYSSTAT | DT_WHT):
1162 case (F_D_TYPE | DT_UNKNOWN):
1163 case (F_D_TYPE | DT_DIR):
1164 case (F_D_TYPESYM | DT_UNKNOWN):
1165 case (F_D_TYPESYM | DT_DIR):
1166 case (F_D_TYPESYM | DT_LNK):
1167 /* Build a file name for fts_stat to stat. */
1168 if (ISSET(FTS_NOCHDIR)) {
1169 p->fts_accpath = p->fts_path;
1170 memmove(cp, p->fts_name, p->fts_namelen + 1);
1171 p->fts_info = fts_stat2(sp, p, 0, dir_fd(&dirp), dp->stat_valid ? &dp->sb : NULL);
1172 } else {
1173 p->fts_accpath = p->fts_name;
1174 p->fts_info = fts_stat2(sp, p, 0, -1, dp->stat_valid ? &dp->sb : NULL);
1175 }
1176 break;
1177 case (F_D_TYPE | DT_FIFO):
1178 case (F_D_TYPE | DT_CHR):
1179 case (F_D_TYPE | DT_BLK):
1180 case (F_D_TYPE | DT_SOCK):
1181 case (F_D_TYPESYM | DT_FIFO):
1182 case (F_D_TYPESYM | DT_CHR):
1183 case (F_D_TYPESYM | DT_BLK):
1184 case (F_D_TYPESYM | DT_SOCK):
1185 p->fts_info = FTS_DEFAULT;
1186 goto common_no_stat;
1187 case (F_D_TYPE | DT_REG):
1188 case (F_D_TYPESYM | DT_REG):
1189 p->fts_info = FTS_F;
1190 goto common_no_stat;
1191 case (F_D_TYPE | DT_LNK):
1192 p->fts_info = FTS_SL;
1193 goto common_no_stat;
1194 case (F_D_TYPE | DT_WHT):
1195 case (F_D_TYPESYM | DT_WHT):
1196 p->fts_info = FTS_W;
1197 goto common_no_stat;
1198 default:
1199 /* No stat necessary */
1200 p->fts_info = FTS_NSOK;
1201 common_no_stat:
1202 p->fts_accpath =
1203 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
1204 break;
1205 }
1206 }
1207
1208 /* We walk in directory order so "ls -f" doesn't get upset. */
1209 p->fts_link = NULL;
1210 if (head == NULL)
1211 head = tail = p;
1212 else {
1213 tail->fts_link = p;
1214 tail = p;
1215 }
1216 ++nitems;
1217 }
1218 close_directory(&dirp);
1219
1220 /*
1221 * If realloc() changed the address of the path, adjust the
1222 * addresses for the rest of the tree and the dir list.
1223 */
1224 if (doadjust)
1225 fts_padjust(sp, head);
1226
1227 /*
1228 * If not changing directories, reset the path back to original
1229 * state.
1230 */
1231 if (ISSET(FTS_NOCHDIR)) {
1232 if (len == sp->fts_pathlen || nitems == 0)
1233 --cp;
1234 *cp = '\0';
1235 }
1236
1237 /*
1238 * If descended after called from fts_children or after called from
1239 * fts_read and nothing found, get back. At the root level we use
1240 * the saved fd; if one of fts_open()'s arguments is a relative path
1241 * to an empty directory, we wind up here with no other way back. If
1242 * can't get back, we're done.
1243 */
1244 if (descend && (type == BCHILD || !nitems) &&
1245 (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) :
1246 fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
1247 cur->fts_info = FTS_ERR;
1248 SET(FTS_STOP);
1249 return (NULL);
1250 }
1251
1252 /* If didn't find anything, return NULL. */
1253 if (!nitems) {
1254 if (type == BREAD)
1255 cur->fts_info = FTS_DP;
1256 return (NULL);
1257 }
1258
1259 /* Sort the entries. */
1260 if (sp->fts_compar && nitems > 1)
1261 head = fts_sort(sp, head, nitems);
1262 return (head);
1263 }
1264
1265 static u_short
1266 fts_stat(FTS *sp, FTSENT *p, int follow, int dfd)
1267 {
1268 return fts_stat2(sp, p, follow, dfd, NULL);
1269 }
1270
1271 static u_short
1272 fts_stat2(FTS *sp, FTSENT *p, int follow, int dfd, struct stat *insb)
1273 {
1274 FTSENT *t;
1275 dev_t dev;
1276 ino_t ino;
1277 struct stat *sbp, sb;
1278 int saved_errno;
1279 const char *path;
1280
1281 if (dfd == -1) {
1282 path = p->fts_accpath;
1283 dfd = AT_FDCWD;
1284 } else {
1285 path = p->fts_name;
1286 }
1287
1288 /* If user needs stat info, stat buffer already allocated. */
1289 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
1290
1291 /*
1292 * getattrlistbulk always returns information about the link, not its
1293 * destination, so if we are doing a logical walk we need to detect links
1294 * and do a stat anyway.
1295 */
1296 if (follow) {
1297 insb = NULL;
1298 } else if (insb && ISSET(FTS_LOGICAL) && ((insb->st_mode & S_IFLNK) == S_IFLNK)) {
1299 insb = NULL;
1300 }
1301
1302 if (insb) {
1303 memcpy(sbp, insb, sizeof(*insb));
1304 } else if (ISSET(FTS_LOGICAL) || follow) {
1305 /*
1306 * If doing a logical walk, or application requested FTS_FOLLOW, do
1307 * a stat(2). If that fails, check for a non-existent symlink. If
1308 * fail, set the errno from the stat call.
1309 */
1310 if (fstatat(dfd, path, sbp, 0)) {
1311 saved_errno = errno;
1312 if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
1313 if (saved_errno == ELOOP)
1314 p->fts_errno = ELOOP;
1315 errno = 0;
1316 return (FTS_SLNONE);
1317 }
1318 p->fts_errno = saved_errno;
1319 goto err;
1320 }
1321 /*
1322 * For FTS_COMFOLLOWDIR, drop back to lstat unless we have
1323 * a directory
1324 */
1325 if (follow == -1 && !S_ISDIR(sbp->st_mode)) {
1326 if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
1327 p->fts_errno = errno;
1328 goto err;
1329 }
1330 }
1331 } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
1332 p->fts_errno = errno;
1333 err: memset(sbp, 0, sizeof(struct stat));
1334 return (FTS_NS);
1335 }
1336
1337 if (S_ISDIR(sbp->st_mode)) {
1338 /*
1339 * Set the device/inode. Used to find cycles and check for
1340 * crossing mount points. Also remember the link count, used
1341 * in fts_build to limit the number of stat calls. It is
1342 * understood that these fields are only referenced if fts_info
1343 * is set to FTS_D.
1344 */
1345 dev = p->fts_dev = sbp->st_dev;
1346 ino = p->fts_ino = sbp->st_ino;
1347 p->fts_nlink = sbp->st_nlink;
1348
1349 if (ISDOT(p->fts_name))
1350 return (FTS_DOT);
1351
1352 /*
1353 * Cycle detection is done by brute force when the directory
1354 * is first encountered. If the tree gets deep enough or the
1355 * number of symbolic links to directories is high enough,
1356 * something faster might be worthwhile.
1357 */
1358 for (t = p->fts_parent;
1359 t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
1360 if (ino == t->fts_ino && dev == t->fts_dev) {
1361 p->fts_cycle = t;
1362 return (FTS_DC);
1363 }
1364 return (FTS_D);
1365 }
1366 if (S_ISLNK(sbp->st_mode))
1367 return (FTS_SL);
1368 if (S_ISREG(sbp->st_mode))
1369 return (FTS_F);
1370 return (FTS_DEFAULT);
1371 }
1372
1373 static FTSENT *
1374 fts_sort(FTS *sp, FTSENT *head, int nitems)
1375 {
1376 FTSENT **ap, *p;
1377
1378 /*
1379 * Construct an array of pointers to the structures and call qsort(3).
1380 * Reassemble the array in the order returned by qsort. If unable to
1381 * sort for memory reasons, return the directory entries in their
1382 * current order. Allocate enough space for the current needs plus
1383 * 40 so don't realloc one entry at a time.
1384 */
1385 if (nitems > sp->fts_nitems) {
1386 struct _ftsent **a;
1387
1388 sp->fts_nitems = nitems + 40;
1389 if ((a = reallocarray(sp->fts_array,
1390 sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
1391 free(sp->fts_array);
1392 sp->fts_array = NULL;
1393 sp->fts_nitems = 0;
1394 return (head);
1395 }
1396 sp->fts_array = a;
1397 }
1398 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1399 *ap++ = p;
1400 #ifdef __BLOCKS__
1401 if (ISSET(FTS_BLOCK_COMPAR))
1402 qsort_b((void *)sp->fts_array, nitems, sizeof(FTSENT *), (int (^)(const void *, const void *))sp->fts_compar_b);
1403 else
1404 #endif /* __BLOCKS__ */
1405 qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
1406 for (head = *(ap = sp->fts_array); --nitems; ++ap)
1407 ap[0]->fts_link = ap[1];
1408 ap[0]->fts_link = NULL;
1409 return (head);
1410 }
1411
1412 static FTSENT *
1413 fts_alloc(FTS *sp, char *name, int namelen)
1414 {
1415 FTSENT *p;
1416 size_t len;
1417
1418 /*
1419 * The file name is a variable length array and no stat structure is
1420 * necessary if the user has set the nostat bit. Allocate the FTSENT
1421 * structure, the file name and the stat structure in one chunk, but
1422 * be careful that the stat structure is reasonably aligned. Since the
1423 * fts_name field is declared to be of size 1, the fts_name pointer is
1424 * namelen + 2 before the first possible address of the stat structure.
1425 */
1426 len = sizeof(FTSENT) + namelen;
1427 if (!ISSET(FTS_NOSTAT))
1428 len += sizeof(struct stat) + ALIGNBYTES;
1429 if ((p = calloc(1, len)) == NULL)
1430 return (NULL);
1431
1432 p->fts_path = sp->fts_path;
1433 p->fts_namelen = namelen;
1434 p->fts_instr = FTS_NOINSTR;
1435 if (!ISSET(FTS_NOSTAT))
1436 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
1437 memcpy(p->fts_name, name, namelen);
1438
1439 return (p);
1440 }
1441
1442 static void
1443 fts_lfree(FTSENT *head)
1444 {
1445 FTSENT *p;
1446
1447 /* Free a linked list of structures. */
1448 while ((p = head)) {
1449 head = head->fts_link;
1450 free(p);
1451 }
1452 }
1453
1454 /*
1455 * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
1456 * Most systems will allow creation of paths much longer than PATH_MAX, even
1457 * though the kernel won't resolve them. Add the size (not just what's needed)
1458 * plus 256 bytes so don't realloc the path 2 bytes at a time.
1459 */
1460 static int
1461 fts_palloc(FTS *sp, size_t more)
1462 {
1463 char *p;
1464
1465 sp->fts_pathlen += more + 256;
1466 /*
1467 * Check for possible wraparound. In an FTS, fts_pathlen is
1468 * a signed int but in an FTSENT it is an unsigned short.
1469 * We limit fts_pathlen to USHRT_MAX to be safe in both cases.
1470 */
1471 if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) {
1472 free(sp->fts_path);
1473 sp->fts_path = NULL;
1474 errno = ENAMETOOLONG;
1475 return (1);
1476 }
1477 p = realloc(sp->fts_path, sp->fts_pathlen);
1478 if (p == NULL) {
1479 free(sp->fts_path);
1480 sp->fts_path = NULL;
1481 return (1);
1482 }
1483 sp->fts_path = p;
1484 return (0);
1485 }
1486
1487 /*
1488 * When the path is realloc'd, have to fix all of the pointers in structures
1489 * already returned.
1490 */
1491 static void
1492 fts_padjust(FTS *sp, FTSENT *head)
1493 {
1494 FTSENT *p;
1495 char *addr = sp->fts_path;
1496
1497 #define ADJUST(p) { \
1498 if ((p)->fts_accpath != (p)->fts_name) { \
1499 (p)->fts_accpath = \
1500 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
1501 } \
1502 (p)->fts_path = addr; \
1503 }
1504 /* Adjust the current set of children. */
1505 for (p = sp->fts_child; p; p = p->fts_link)
1506 ADJUST(p);
1507
1508 /* Adjust the rest of the tree, including the current level. */
1509 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
1510 ADJUST(p);
1511 p = p->fts_link ? p->fts_link : p->fts_parent;
1512 }
1513 }
1514
1515 static size_t
1516 fts_maxarglen(char * const *argv)
1517 {
1518 size_t len, max;
1519
1520 for (max = 0; *argv; ++argv)
1521 if ((len = strlen(*argv)) > max)
1522 max = len;
1523 return (max + 1);
1524 }
1525
1526 /*
1527 * Change to dir specified by fd or p->fts_accpath without getting
1528 * tricked by someone changing the world out from underneath us.
1529 * Assumes p->fts_dev and p->fts_ino are filled in.
1530 */
1531 static int
1532 fts_safe_changedir(FTS *sp, FTSENT *p, int fd, char *path)
1533 {
1534 int ret, oerrno, newfd;
1535 struct stat sb;
1536
1537 newfd = fd;
1538 if (ISSET(FTS_NOCHDIR))
1539 return (0);
1540 if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC)) < 0)
1541 return (-1);
1542 if (fstat(newfd, &sb)) {
1543 ret = -1;
1544 goto bail;
1545 }
1546 if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
1547 errno = ENOENT; /* disinformation */
1548 ret = -1;
1549 goto bail;
1550 }
1551 ret = fchdir(newfd);
1552 bail:
1553 oerrno = errno;
1554 if (fd < 0)
1555 (void)close(newfd);
1556 errno = oerrno;
1557 return (ret);
1558 }