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