source: rtems/cpukit/libmisc/shell/fts.c @ 6cdaa85

5
Last change on this file since 6cdaa85 was 6cdaa85, checked in by Sebastian Huber <sebastian.huber@…>, on 10/04/18 at 18:16:45

shell: Use #include "..." for local header files

Update #3375.

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