]> git.saurik.com Git - apple/libc.git/blame - gen/fts.c
Libc-1244.50.9.tar.gz
[apple/libc.git] / gen / fts.c
CommitLineData
e9ce8d39 1/*
974e3884 2 * Copyright (c) 1999, 2000, 2003, 2005, 2008, 2012, 2016 Apple Inc. All rights reserved.
e9ce8d39
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
734aad71
A
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
e9ce8d39
A
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
734aad71
A
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.
e9ce8d39
A
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.
974e3884 35 * 3. Neither the name of the University nor the names of its contributors
e9ce8d39
A
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
974e3884 52/* $OpenBSD: fts.c,v 1.51 2015/09/12 13:32:24 guenther Exp $ */
e9ce8d39
A
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>
974e3884 61#include <limits.h>
e9ce8d39
A
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
974e3884
A
65#include <stdbool.h>
66#include <sys/vnode.h>
67#include <sys/attr.h>
b061a43b 68#include <os/assumes.h>
974e3884 69
34e8f829
A
70#ifdef __BLOCKS__
71#include <Block.h>
72#endif /* __BLOCKS__ */
974e3884 73#include <malloc_private.h>
e9ce8d39 74
b061a43b 75static FTSENT *fts_alloc(FTS *, char *, ssize_t);
9385eb3d
A
76static FTSENT *fts_build(FTS *, int);
77static void fts_lfree(FTSENT *);
78static void fts_load(FTS *, FTSENT *);
79static size_t fts_maxarglen(char * const *);
974e3884 80static void fts_padjust(FTS *, FTSENT *);
9385eb3d
A
81static int fts_palloc(FTS *, size_t);
82static FTSENT *fts_sort(FTS *, FTSENT *, int);
974e3884
A
83static u_short fts_stat(FTS *, FTSENT *, int, int);
84static u_short fts_stat2(FTS *, FTSENT *, int, int, struct stat *);
85static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
e9ce8d39 86
974e3884 87#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
e9ce8d39 88
974e3884
A
89#define CLR(opt) (sp->fts_options &= ~(opt))
90#define ISSET(opt) (sp->fts_options & (opt))
91#define SET(opt) (sp->fts_options |= (opt))
e9ce8d39 92
e9ce8d39
A
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
34e8f829
A
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) */
6465356a
A
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 */
34e8f829
A
137
138static FTS *
974e3884 139__fts_open(char * const *argv, FTS *sp)
34e8f829 140{
974e3884
A
141 FTSENT *p, *root;
142 int nitems;
143 FTSENT *parent, *tmp;
b061a43b 144 ssize_t len;
e9ce8d39 145
e9ce8d39
A
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 */
974e3884 154 if (fts_palloc(sp, MAX(fts_maxarglen(argv), PATH_MAX)))
e9ce8d39
A
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) {
b061a43b 164 len = strlen(*argv);
974e3884
A
165 if ((p = fts_alloc(sp, *argv, len)) == NULL)
166 goto mem3;
e9ce8d39
A
167 p->fts_level = FTS_ROOTLEVEL;
168 p->fts_parent = parent;
169 p->fts_accpath = p->fts_name;
974e3884 170 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOWDIR) ? -1 : ISSET(FTS_COMFOLLOW), -1);
e9ce8d39
A
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 */
34e8f829 180 if (sp->fts_compar) {
e9ce8d39
A
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 }
34e8f829 193 if (sp->fts_compar && nitems > 1)
e9ce8d39
A
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 /*
974e3884 207 * If using chdir(2), grab a file descriptor pointing to dot to ensure
e9ce8d39
A
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 */
974e3884
A
213 if (!ISSET(FTS_NOCHDIR) &&
214 (sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC)) < 0)
e9ce8d39
A
215 SET(FTS_NOCHDIR);
216
974e3884
A
217 if (nitems == 0)
218 free(parent);
219
e9ce8d39
A
220 return (sp);
221
222mem3: fts_lfree(root);
223 free(parent);
224mem2: free(sp->fts_path);
225mem1: free(sp);
226 return (NULL);
227}
228
34e8f829 229FTS *
974e3884 230fts_open(char * const *argv, int options, int (*compar)())
34e8f829 231{
974e3884 232 FTS *sp;
34e8f829
A
233
234 /* Options check. */
235 if (options & ~FTS_OPTIONMASK) {
236 errno = EINVAL;
237 return (NULL);
238 }
6465356a 239 if (options & FTS_NOSTAT_TYPE) options |= FTS_NOSTAT;
34e8f829
A
240
241 /* Allocate/initialize the stream */
974e3884 242 if ((sp = calloc(1, sizeof(FTS))) == NULL)
34e8f829 243 return (NULL);
34e8f829
A
244 sp->fts_compar = compar;
245 sp->fts_options = options;
246
247 return __fts_open(argv, sp);
248}
249
250#ifdef __BLOCKS__
251FTS *
974e3884 252fts_open_b(char * const *argv, int options, int (^compar)(const FTSENT **, const FTSENT **))
34e8f829 253{
974e3884 254 FTS *sp;
34e8f829
A
255
256 /* Options check. */
257 if (options & ~FTS_OPTIONMASK) {
258 errno = EINVAL;
259 return (NULL);
260 }
6465356a 261 if (options & FTS_NOSTAT_TYPE) options |= FTS_NOSTAT;
34e8f829
A
262
263 /* Allocate/initialize the stream */
974e3884 264 if ((sp = calloc(1, sizeof(FTS))) == NULL)
34e8f829 265 return (NULL);
34e8f829
A
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
e9ce8d39 273static void
974e3884 274fts_load(FTS *sp, FTSENT *p)
e9ce8d39 275{
b061a43b 276 ssize_t len;
974e3884 277 char *cp;
e9ce8d39
A
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
297int
974e3884 298fts_close(FTS *sp)
e9ce8d39 299{
974e3884
A
300 FTSENT *freep, *p;
301 int rfd, error = 0;
e9ce8d39
A
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
974e3884
A
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){
e9ce8d39 322 fts_lfree(sp->fts_child);
e9ce8d39 323 }
974e3884
A
324 free(sp->fts_array); sp->fts_array = NULL;
325 free(sp->fts_path); sp->fts_path = NULL;
e9ce8d39 326
34e8f829
A
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
e9ce8d39
A
333 /* Free up the stream pointer. */
334 free(sp);
335
974e3884
A
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 }
e9ce8d39 347 errno = saved_errno;
e9ce8d39 348 }
974e3884
A
349
350 return (error);
e9ce8d39
A
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
361FTSENT *
974e3884 362fts_read(FTS *sp)
e9ce8d39 363{
974e3884
A
364 FTSENT *p, *tmp;
365 int instr;
366 char *t;
e9ce8d39
A
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) {
974e3884 382 p->fts_info = fts_stat(sp, p, 0, -1);
e9ce8d39
A
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)) {
974e3884 394 p->fts_info = fts_stat(sp, p, 1, -1);
6465356a 395 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
974e3884
A
396 if ((p->fts_symfd =
397 open(".", O_RDONLY | O_CLOEXEC)) < 0) {
e9ce8d39
A
398 p->fts_errno = errno;
399 p->fts_info = FTS_ERR;
6465356a 400 } else {
e9ce8d39 401 p->fts_flags |= FTS_SYMFOLLOW;
6465356a
A
402 }
403 }
e9ce8d39
A
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 ||
6465356a 411 (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
e9ce8d39
A
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);
6465356a 420 }
e9ce8d39
A
421
422 /* Rebuild if only read the names and now traversing. */
974e3884
A
423 if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
424 CLR(FTS_NAMEONLY);
e9ce8d39
A
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) {
974e3884 442 if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
e9ce8d39
A
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. */
460next: tmp = p;
6465356a 461 if ((p = p->fts_link)) {
974e3884
A
462 free(tmp);
463
e9ce8d39 464 /*
974e3884
A
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.
e9ce8d39
A
467 */
468 if (p->fts_level == FTS_ROOTLEVEL) {
974e3884 469 if (FCHDIR(sp, sp->fts_rfd)) {
e9ce8d39
A
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 */
1f2f436a 482 if (p->fts_instr == FTS_SKIP) {
e9ce8d39 483 goto next;
1f2f436a 484 }
e9ce8d39 485 if (p->fts_instr == FTS_FOLLOW) {
974e3884 486 p->fts_info = fts_stat(sp, p, 1, -1);
6465356a 487 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
e9ce8d39 488 if ((p->fts_symfd =
974e3884 489 open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
e9ce8d39
A
490 p->fts_errno = errno;
491 p->fts_info = FTS_ERR;
6465356a 492 } else {
e9ce8d39 493 p->fts_flags |= FTS_SYMFOLLOW;
6465356a
A
494 }
495 }
e9ce8d39
A
496 p->fts_instr = FTS_NOINSTR;
497 }
498
499name: 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;
974e3884 507 free(tmp);
e9ce8d39
A
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
974e3884 519 /* NUL terminate the pathname. */
e9ce8d39
A
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) {
974e3884 528 if (FCHDIR(sp, sp->fts_rfd)) {
e9ce8d39 529 SET(FTS_STOP);
974e3884 530 sp->fts_cur = p;
e9ce8d39
A
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);
974e3884 539 sp->fts_cur = p;
e9ce8d39
A
540 return (NULL);
541 }
542 (void)close(p->fts_symfd);
974e3884
A
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);
e9ce8d39
A
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 */
560int
974e3884 561fts_set(FTS __unused *sp, FTSENT *p, int instr)
e9ce8d39
A
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
572FTSENT *
974e3884 573fts_children(FTS *sp, int instr)
e9ce8d39 574{
974e3884 575 FTSENT *p;
e9ce8d39
A
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) {
974e3884 613 SET(FTS_NAMEONLY);
e9ce8d39 614 instr = BNAMES;
974e3884 615 } else
e9ce8d39
A
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
974e3884 629 if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
e9ce8d39
A
630 return (NULL);
631 sp->fts_child = fts_build(sp, instr);
974e3884
A
632 if (fchdir(fd)) {
633 (void)close(fd);
e9ce8d39 634 return (NULL);
974e3884 635 }
e9ce8d39
A
636 (void)close(fd);
637 return (sp->fts_child);
638}
639
b061a43b 640typedef struct __attribute__((packed)) __attribute__((__aligned__(4))) {
974e3884
A
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
666typedef 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
683typedef 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
702typedef 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
710static bool
711advance_directory(dir_handle *handle)
712{
713 if (handle->done) return true;
714
b061a43b 715 os_assert(handle->dirfd != -1);
974e3884
A
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
728error: {
729 int saved_errno = errno;
730 close(handle->dirfd);
731 handle->dirfd = -1;
732 errno = saved_errno;
733 return false;
734 }
735}
736
737static bool
738open_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
776fallback:
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
785static bool
786read_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);
b061a43b
A
838 os_assert((handle->cur_entry == handle->entry_count) ||
839 (void*)handle->curattr + handle->curattr->length <= (void*)handle->attrbuf + ATTR_BUF_SIZE);
974e3884 840
b061a43b 841 os_assert(curattr_stat->name.attr_length > 0);
974e3884
A
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;
b061a43b 845 os_assert((void*)entry->d_name + curattr_stat->name.attr_length <= (void*)handle->attrbuf + ATTR_BUF_SIZE);
974e3884
A
846 } else {
847 curattr_nostat = handle->curattr_nostat;
848 handle->cur_entry++;
849 handle->curattr_nostat = (attrListAttributes_nostat*)(((char*)curattr_nostat) + curattr_nostat->length);
b061a43b
A
850 os_assert((handle->cur_entry == handle->entry_count) ||
851 (void*)handle->curattr + handle->curattr->length <= (void*)handle->attrbuf + ATTR_BUF_SIZE);
974e3884 852
b061a43b 853 os_assert(curattr_nostat->name.attr_length > 0);
974e3884
A
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;
b061a43b 857 os_assert((void*)entry->d_name + curattr_nostat->name.attr_length <= (void*)handle->attrbuf + ATTR_BUF_SIZE);
974e3884
A
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
b061a43b
A
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
974e3884
A
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
957static int
958dir_fd(dir_handle *handle)
959{
960 return handle->dirp ? dirfd(handle->dirp) : handle->dirfd;
961}
962
963static void
964close_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
e9ce8d39
A
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
34e8f829
A
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.
e9ce8d39
A
987 */
988static FTSENT *
974e3884 989fts_build(FTS *sp, int type)
e9ce8d39 990{
974e3884
A
991 dir_entry de = { 0 };
992 dir_entry *dp = &de;
993 FTSENT *p, *head;
994 int nitems;
e9ce8d39 995 FTSENT *cur, *tail;
974e3884
A
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;
e9ce8d39
A
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 */
974e3884 1010 if (!open_directory(sp, &dirp, cur->fts_accpath)) {
e9ce8d39
A
1011 if (type == BREAD) {
1012 cur->fts_info = FTS_DNR;
1013 cur->fts_errno = errno;
1014 }
1015 return (NULL);
1016 }
1017
e9ce8d39 1018 if (type == BNAMES)
34e8f829 1019 dostat = F_NOSTAT;
6465356a
A
1020 else if (ISSET(FTS_NOSTAT_TYPE))
1021 dostat = ISSET(FTS_PHYSICAL) ? F_D_TYPE : F_D_TYPESYM;
34e8f829
A
1022 else if (ISSET(FTS_NOSTAT))
1023 dostat = ISSET(FTS_PHYSICAL) ? F_STATDIR : F_STATDIRSYM;
e9ce8d39 1024 else
34e8f829 1025 dostat = F_ALWAYSSTAT;
e9ce8d39
A
1026
1027#ifdef notdef
34e8f829 1028 (void)printf("dostat == %d\n", dostat);
e9ce8d39
A
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;
974e3884
A
1048 if (dostat || type == BREAD) {
1049 if (fts_safe_changedir(sp, cur, dir_fd(&dirp), NULL)) {
1050 if (dostat && type == BREAD) {
e9ce8d39 1051 cur->fts_errno = errno;
974e3884 1052 }
e9ce8d39
A
1053 cur->fts_flags |= FTS_DONTCHDIR;
1054 descend = 0;
1055 cderrno = errno;
974e3884
A
1056 close_directory(&dirp);
1057 } else {
e9ce8d39 1058 descend = 1;
974e3884
A
1059 }
1060 } else {
e9ce8d39 1061 descend = 0;
974e3884 1062 }
e9ce8d39
A
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 */
e9ce8d39
A
1074 len = NAPPEND(cur);
1075 if (ISSET(FTS_NOCHDIR)) {
1076 cp = sp->fts_path + len;
1077 *cp++ = '/';
1078 }
2be56ee9
A
1079 len++;
1080 maxlen = sp->fts_pathlen - len;
e9ce8d39 1081
974e3884
A
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++;
e9ce8d39
A
1089
1090 /* Read the directory, attaching each entry to the `link' pointer. */
974e3884
A
1091 doadjust = 0;
1092 for (head = tail = NULL, nitems = 0; read_dirent(&dirp, dp) ; ) {
e9ce8d39
A
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;
974e3884
A
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)) {
e9ce8d39
A
1101 /*
1102 * No more memory for path or structures. Save
1103 * errno, free up the current structure and the
1104 * structures already allocated.
1105 */
1106mem1: saved_errno = errno;
974e3884 1107 free(p);
e9ce8d39 1108 fts_lfree(head);
974e3884 1109 close_directory(&dirp);
e9ce8d39
A
1110 cur->fts_info = FTS_ERR;
1111 SET(FTS_STOP);
2be56ee9 1112 errno = saved_errno;
e9ce8d39
A
1113 return (NULL);
1114 }
974e3884
A
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;
e9ce8d39
A
1122 }
1123
974e3884
A
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 }
e9ce8d39 1139 p->fts_level = level;
2be56ee9
A
1140 p->fts_parent = sp->fts_cur;
1141 p->fts_pathlen = len + dp->d_namlen;
e9ce8d39 1142
e9ce8d39 1143 if (cderrno) {
34e8f829 1144 if (dostat) {
e9ce8d39
A
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;
e9ce8d39 1150 } else {
34e8f829
A
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):
6465356a
A
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):
34e8f829
A
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);
974e3884
A
1180 p->fts_info = fts_stat2(sp, p, 0, dir_fd(&dirp), dp->stat_valid ? &dp->sb : NULL);
1181 } else {
34e8f829 1182 p->fts_accpath = p->fts_name;
974e3884
A
1183 p->fts_info = fts_stat2(sp, p, 0, -1, dp->stat_valid ? &dp->sb : NULL);
1184 }
34e8f829 1185 break;
6465356a
A
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;
34e8f829
A
1207 default:
1208 /* No stat necessary */
6465356a
A
1209 p->fts_info = FTS_NSOK;
1210common_no_stat:
34e8f829
A
1211 p->fts_accpath =
1212 ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
34e8f829
A
1213 break;
1214 }
e9ce8d39
A
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 }
974e3884 1227 close_directory(&dirp);
e9ce8d39
A
1228
1229 /*
974e3884
A
1230 * If realloc() changed the address of the path, adjust the
1231 * addresses for the rest of the tree and the dir list.
e9ce8d39 1232 */
974e3884
A
1233 if (doadjust)
1234 fts_padjust(sp, head);
e9ce8d39
A
1235
1236 /*
1237 * If not changing directories, reset the path back to original
1238 * state.
1239 */
1240 if (ISSET(FTS_NOCHDIR)) {
974e3884 1241 if (len == sp->fts_pathlen || nitems == 0)
e9ce8d39
A
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) &&
974e3884
A
1254 (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) :
1255 fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
e9ce8d39
A
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
1274static u_short
974e3884
A
1275fts_stat(FTS *sp, FTSENT *p, int follow, int dfd)
1276{
1277 return fts_stat2(sp, p, follow, dfd, NULL);
1278}
1279
1280static u_short
1281fts_stat2(FTS *sp, FTSENT *p, int follow, int dfd, struct stat *insb)
e9ce8d39 1282{
974e3884
A
1283 FTSENT *t;
1284 dev_t dev;
1285 ino_t ino;
e9ce8d39
A
1286 struct stat *sbp, sb;
1287 int saved_errno;
974e3884
A
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 }
e9ce8d39
A
1296
1297 /* If user needs stat info, stat buffer already allocated. */
1298 sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
1299
e9ce8d39 1300 /*
974e3884
A
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.
e9ce8d39 1304 */
974e3884
A
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)) {
e9ce8d39 1320 saved_errno = errno;
974e3884 1321 if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
224c7076
A
1322 if (saved_errno == ELOOP)
1323 p->fts_errno = ELOOP;
e9ce8d39
A
1324 errno = 0;
1325 return (FTS_SLNONE);
974e3884 1326 }
e9ce8d39
A
1327 p->fts_errno = saved_errno;
1328 goto err;
1329 }
224c7076
A
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)) {
974e3884 1335 if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
224c7076
A
1336 p->fts_errno = errno;
1337 goto err;
1338 }
1339 }
974e3884 1340 } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {
e9ce8d39
A
1341 p->fts_errno = errno;
1342err: 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
1382static FTSENT *
974e3884 1383fts_sort(FTS *sp, FTSENT *head, int nitems)
e9ce8d39 1384{
974e3884 1385 FTSENT **ap, *p;
e9ce8d39
A
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) {
974e3884
A
1395 struct _ftsent **a;
1396
e9ce8d39 1397 sp->fts_nitems = nitems + 40;
974e3884
A
1398 if ((a = reallocarray(sp->fts_array,
1399 sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
1400 free(sp->fts_array);
1401 sp->fts_array = NULL;
e9ce8d39
A
1402 sp->fts_nitems = 0;
1403 return (head);
1404 }
974e3884 1405 sp->fts_array = a;
e9ce8d39
A
1406 }
1407 for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1408 *ap++ = p;
34e8f829
A
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);
e9ce8d39
A
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
1421static FTSENT *
b061a43b 1422fts_alloc(FTS *sp, char *name, ssize_t namelen)
e9ce8d39 1423{
974e3884 1424 FTSENT *p;
e9ce8d39
A
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;
974e3884 1438 if ((p = calloc(1, len)) == NULL)
e9ce8d39
A
1439 return (NULL);
1440
e9ce8d39 1441 p->fts_path = sp->fts_path;
974e3884 1442 p->fts_namelen = namelen;
e9ce8d39 1443 p->fts_instr = FTS_NOINSTR;
974e3884
A
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
e9ce8d39
A
1448 return (p);
1449}
1450
1451static void
974e3884 1452fts_lfree(FTSENT *head)
e9ce8d39 1453{
974e3884 1454 FTSENT *p;
e9ce8d39
A
1455
1456 /* Free a linked list of structures. */
6465356a 1457 while ((p = head)) {
e9ce8d39
A
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.
974e3884 1465 * Most systems will allow creation of paths much longer than PATH_MAX, even
e9ce8d39 1466 * though the kernel won't resolve them. Add the size (not just what's needed)
6465356a 1467 * plus 256 bytes so don't realloc the path 2 bytes at a time.
e9ce8d39
A
1468 */
1469static int
974e3884 1470fts_palloc(FTS *sp, size_t more)
e9ce8d39 1471{
974e3884
A
1472 char *p;
1473
e9ce8d39 1474 sp->fts_pathlen += more + 256;
974e3884
A
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);
e9ce8d39
A
1494}
1495
1496/*
1497 * When the path is realloc'd, have to fix all of the pointers in structures
1498 * already returned.
1499 */
1500static void
974e3884 1501fts_padjust(FTS *sp, FTSENT *head)
e9ce8d39
A
1502{
1503 FTSENT *p;
974e3884 1504 char *addr = sp->fts_path;
e9ce8d39 1505
974e3884 1506#define ADJUST(p) { \
2be56ee9
A
1507 if ((p)->fts_accpath != (p)->fts_name) { \
1508 (p)->fts_accpath = \
1509 (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
1510 } \
e9ce8d39 1511 (p)->fts_path = addr; \
974e3884 1512}
e9ce8d39
A
1513 /* Adjust the current set of children. */
1514 for (p = sp->fts_child; p; p = p->fts_link)
1515 ADJUST(p);
1516
974e3884
A
1517 /* Adjust the rest of the tree, including the current level. */
1518 for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
e9ce8d39
A
1519 ADJUST(p);
1520 p = p->fts_link ? p->fts_link : p->fts_parent;
1521 }
1522}
1523
1524static size_t
974e3884 1525fts_maxarglen(char * const *argv)
e9ce8d39
A
1526{
1527 size_t len, max;
1528
1529 for (max = 0; *argv; ++argv)
1530 if ((len = strlen(*argv)) > max)
1531 max = len;
2be56ee9 1532 return (max + 1);
e9ce8d39 1533}
974e3884
A
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 */
1540static int
1541fts_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 }
b061a43b
A
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
974e3884
A
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 }
b061a43b
A
1569#endif
1570
974e3884
A
1571 ret = fchdir(newfd);
1572bail:
1573 oerrno = errno;
1574 if (fd < 0)
1575 (void)close(newfd);
1576 errno = oerrno;
1577 return (ret);
1578}