xref: /dflybsd-src/contrib/grep/lib/fts.c (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
1cf28ed85SJohn Marino /* Traverse a file hierarchy.
2cf28ed85SJohn Marino 
3*09d4459fSDaniel Fojt    Copyright (C) 2004-2020 Free Software Foundation, Inc.
4cf28ed85SJohn Marino 
5cf28ed85SJohn Marino    This program is free software: you can redistribute it and/or modify
6cf28ed85SJohn Marino    it under the terms of the GNU General Public License as published by
7cf28ed85SJohn Marino    the Free Software Foundation; either version 3 of the License, or
8cf28ed85SJohn Marino    (at your option) any later version.
9cf28ed85SJohn Marino 
10cf28ed85SJohn Marino    This program is distributed in the hope that it will be useful,
11cf28ed85SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
12cf28ed85SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13cf28ed85SJohn Marino    GNU General Public License for more details.
14cf28ed85SJohn Marino 
15cf28ed85SJohn Marino    You should have received a copy of the GNU General Public License
16*09d4459fSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17cf28ed85SJohn Marino 
18cf28ed85SJohn Marino /*-
19cf28ed85SJohn Marino  * Copyright (c) 1990, 1993, 1994
20cf28ed85SJohn Marino  *      The Regents of the University of California.  All rights reserved.
21cf28ed85SJohn Marino  *
22cf28ed85SJohn Marino  * Redistribution and use in source and binary forms, with or without
23cf28ed85SJohn Marino  * modification, are permitted provided that the following conditions
24cf28ed85SJohn Marino  * are met:
25cf28ed85SJohn Marino  * 1. Redistributions of source code must retain the above copyright
26cf28ed85SJohn Marino  *    notice, this list of conditions and the following disclaimer.
27cf28ed85SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
28cf28ed85SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
29cf28ed85SJohn Marino  *    documentation and/or other materials provided with the distribution.
30cf28ed85SJohn Marino  * 4. Neither the name of the University nor the names of its contributors
31cf28ed85SJohn Marino  *    may be used to endorse or promote products derived from this software
32cf28ed85SJohn Marino  *    without specific prior written permission.
33cf28ed85SJohn Marino  *
34cf28ed85SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
35cf28ed85SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36cf28ed85SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37cf28ed85SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38cf28ed85SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39cf28ed85SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40cf28ed85SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41cf28ed85SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42cf28ed85SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43cf28ed85SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44cf28ed85SJohn Marino  * SUCH DAMAGE.
45cf28ed85SJohn Marino  */
46cf28ed85SJohn Marino 
47cf28ed85SJohn Marino #include <config.h>
48cf28ed85SJohn Marino 
49*09d4459fSDaniel Fojt #if defined LIBC_SCCS && !defined GCC_LINT && !defined lint
50cf28ed85SJohn Marino static char sccsid[] = "@(#)fts.c       8.6 (Berkeley) 8/14/94";
51*09d4459fSDaniel Fojt #endif
52cf28ed85SJohn Marino 
53cf28ed85SJohn Marino #include "fts_.h"
54cf28ed85SJohn Marino 
55cf28ed85SJohn Marino #if HAVE_SYS_PARAM_H || defined _LIBC
56cf28ed85SJohn Marino # include <sys/param.h>
57cf28ed85SJohn Marino #endif
58cf28ed85SJohn Marino #ifdef _LIBC
59cf28ed85SJohn Marino # include <include/sys/stat.h>
60cf28ed85SJohn Marino #else
61cf28ed85SJohn Marino # include <sys/stat.h>
62cf28ed85SJohn Marino #endif
63cf28ed85SJohn Marino #include <fcntl.h>
64cf28ed85SJohn Marino #include <errno.h>
65dc7c36e4SJohn Marino #include <stdalign.h>
66cf28ed85SJohn Marino #include <stdbool.h>
67dc7c36e4SJohn Marino #include <stddef.h>
68cf28ed85SJohn Marino #include <stdlib.h>
69cf28ed85SJohn Marino #include <string.h>
70cf28ed85SJohn Marino #include <unistd.h>
71cf28ed85SJohn Marino 
72cf28ed85SJohn Marino #if ! _LIBC
73cf28ed85SJohn Marino # include "fcntl--.h"
74*09d4459fSDaniel Fojt # include "flexmember.h"
75cf28ed85SJohn Marino # include "openat.h"
76*09d4459fSDaniel Fojt # include "opendirat.h"
77cf28ed85SJohn Marino # include "same-inode.h"
78cf28ed85SJohn Marino #endif
79cf28ed85SJohn Marino 
80cf28ed85SJohn Marino #include <dirent.h>
81cf28ed85SJohn Marino #ifndef _D_EXACT_NAMLEN
82cf28ed85SJohn Marino # define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)
83cf28ed85SJohn Marino #endif
84cf28ed85SJohn Marino 
85cf28ed85SJohn Marino #if HAVE_STRUCT_DIRENT_D_TYPE
86cf28ed85SJohn Marino /* True if the type of the directory entry D is known.  */
87cf28ed85SJohn Marino # define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
88cf28ed85SJohn Marino /* True if the type of the directory entry D must be T.  */
89cf28ed85SJohn Marino # define DT_MUST_BE(d, t) ((d)->d_type == (t))
90cf28ed85SJohn Marino # define D_TYPE(d) ((d)->d_type)
91cf28ed85SJohn Marino #else
92cf28ed85SJohn Marino # define DT_IS_KNOWN(d) false
93cf28ed85SJohn Marino # define DT_MUST_BE(d, t) false
94cf28ed85SJohn Marino # define D_TYPE(d) DT_UNKNOWN
95cf28ed85SJohn Marino 
96cf28ed85SJohn Marino # undef DT_UNKNOWN
97cf28ed85SJohn Marino # define DT_UNKNOWN 0
98cf28ed85SJohn Marino 
99cf28ed85SJohn Marino /* Any nonzero values will do here, so long as they're distinct.
100cf28ed85SJohn Marino    Undef any existing macros out of the way.  */
101cf28ed85SJohn Marino # undef DT_BLK
102cf28ed85SJohn Marino # undef DT_CHR
103cf28ed85SJohn Marino # undef DT_DIR
104cf28ed85SJohn Marino # undef DT_FIFO
105cf28ed85SJohn Marino # undef DT_LNK
106cf28ed85SJohn Marino # undef DT_REG
107cf28ed85SJohn Marino # undef DT_SOCK
108cf28ed85SJohn Marino # define DT_BLK 1
109cf28ed85SJohn Marino # define DT_CHR 2
110cf28ed85SJohn Marino # define DT_DIR 3
111cf28ed85SJohn Marino # define DT_FIFO 4
112cf28ed85SJohn Marino # define DT_LNK 5
113cf28ed85SJohn Marino # define DT_REG 6
114cf28ed85SJohn Marino # define DT_SOCK 7
115cf28ed85SJohn Marino #endif
116cf28ed85SJohn Marino 
117cf28ed85SJohn Marino #ifndef S_IFLNK
118cf28ed85SJohn Marino # define S_IFLNK 0
119cf28ed85SJohn Marino #endif
120cf28ed85SJohn Marino #ifndef S_IFSOCK
121cf28ed85SJohn Marino # define S_IFSOCK 0
122cf28ed85SJohn Marino #endif
123cf28ed85SJohn Marino 
124cf28ed85SJohn Marino enum
125cf28ed85SJohn Marino {
126cf28ed85SJohn Marino   NOT_AN_INODE_NUMBER = 0
127cf28ed85SJohn Marino };
128cf28ed85SJohn Marino 
129cf28ed85SJohn Marino #ifdef D_INO_IN_DIRENT
130cf28ed85SJohn Marino # define D_INO(dp) (dp)->d_ino
131cf28ed85SJohn Marino #else
132cf28ed85SJohn Marino /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
133cf28ed85SJohn Marino # define D_INO(dp) NOT_AN_INODE_NUMBER
134cf28ed85SJohn Marino #endif
135cf28ed85SJohn Marino 
136cf28ed85SJohn Marino /* If possible (see max_entries, below), read no more than this many directory
137cf28ed85SJohn Marino    entries at a time.  Without this limit (i.e., when using non-NULL
138cf28ed85SJohn Marino    fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
139cf28ed85SJohn Marino    of memory, and handling 64M entries would require 16GiB of memory.  */
140cf28ed85SJohn Marino #ifndef FTS_MAX_READDIR_ENTRIES
141cf28ed85SJohn Marino # define FTS_MAX_READDIR_ENTRIES 100000
142cf28ed85SJohn Marino #endif
143cf28ed85SJohn Marino 
144cf28ed85SJohn Marino /* If there are more than this many entries in a directory,
145cf28ed85SJohn Marino    and the conditions mentioned below are satisfied, then sort
146cf28ed85SJohn Marino    the entries on inode number before any further processing.  */
147cf28ed85SJohn Marino #ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
148cf28ed85SJohn Marino # define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000
149cf28ed85SJohn Marino #endif
150cf28ed85SJohn Marino 
151cf28ed85SJohn Marino enum
152cf28ed85SJohn Marino {
153cf28ed85SJohn Marino   _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
154cf28ed85SJohn Marino };
155cf28ed85SJohn Marino 
156cf28ed85SJohn Marino enum Fts_stat
157cf28ed85SJohn Marino {
158cf28ed85SJohn Marino   FTS_NO_STAT_REQUIRED = 1,
159cf28ed85SJohn Marino   FTS_STAT_REQUIRED = 2
160cf28ed85SJohn Marino };
161cf28ed85SJohn Marino 
162cf28ed85SJohn Marino #ifdef _LIBC
163cf28ed85SJohn Marino # undef close
164cf28ed85SJohn Marino # define close __close
165cf28ed85SJohn Marino # undef closedir
166cf28ed85SJohn Marino # define closedir __closedir
167cf28ed85SJohn Marino # undef fchdir
168cf28ed85SJohn Marino # define fchdir __fchdir
169cf28ed85SJohn Marino # undef open
170cf28ed85SJohn Marino # define open __open
171cf28ed85SJohn Marino # undef readdir
172cf28ed85SJohn Marino # define readdir __readdir
173cf28ed85SJohn Marino #else
174cf28ed85SJohn Marino # undef internal_function
175cf28ed85SJohn Marino # define internal_function /* empty */
176cf28ed85SJohn Marino #endif
177cf28ed85SJohn Marino 
178cf28ed85SJohn Marino #ifndef __set_errno
179cf28ed85SJohn Marino # define __set_errno(Val) errno = (Val)
180cf28ed85SJohn Marino #endif
181cf28ed85SJohn Marino 
182cf28ed85SJohn Marino /* If this host provides the openat function, then we can avoid
183cf28ed85SJohn Marino    attempting to open "." in some initialization code below.  */
184cf28ed85SJohn Marino #ifdef HAVE_OPENAT
185cf28ed85SJohn Marino # define HAVE_OPENAT_SUPPORT 1
186cf28ed85SJohn Marino #else
187cf28ed85SJohn Marino # define HAVE_OPENAT_SUPPORT 0
188cf28ed85SJohn Marino #endif
189cf28ed85SJohn Marino 
190cf28ed85SJohn Marino #ifdef NDEBUG
191dc7c36e4SJohn Marino # define fts_assert(expr) ((void) (0 && (expr)))
192cf28ed85SJohn Marino #else
193cf28ed85SJohn Marino # define fts_assert(expr)       \
194cf28ed85SJohn Marino     do                          \
195cf28ed85SJohn Marino       {                         \
196cf28ed85SJohn Marino         if (!(expr))            \
197cf28ed85SJohn Marino           abort ();             \
198cf28ed85SJohn Marino       }                         \
199cf28ed85SJohn Marino     while (false)
200cf28ed85SJohn Marino #endif
201cf28ed85SJohn Marino 
202*09d4459fSDaniel Fojt #ifndef FALLTHROUGH
203*09d4459fSDaniel Fojt # if __GNUC__ < 7
204*09d4459fSDaniel Fojt #  define FALLTHROUGH ((void) 0)
205*09d4459fSDaniel Fojt # else
206*09d4459fSDaniel Fojt #  define FALLTHROUGH __attribute__ ((__fallthrough__))
207*09d4459fSDaniel Fojt # endif
208*09d4459fSDaniel Fojt #endif
209*09d4459fSDaniel Fojt 
210cf28ed85SJohn Marino static FTSENT   *fts_alloc (FTS *, const char *, size_t) internal_function;
211cf28ed85SJohn Marino static FTSENT   *fts_build (FTS *, int) internal_function;
212cf28ed85SJohn Marino static void      fts_lfree (FTSENT *) internal_function;
213cf28ed85SJohn Marino static void      fts_load (FTS *, FTSENT *) internal_function;
214cf28ed85SJohn Marino static size_t    fts_maxarglen (char * const *) internal_function;
215cf28ed85SJohn Marino static void      fts_padjust (FTS *, FTSENT *) internal_function;
216cf28ed85SJohn Marino static bool      fts_palloc (FTS *, size_t) internal_function;
217cf28ed85SJohn Marino static FTSENT   *fts_sort (FTS *, FTSENT *, size_t) internal_function;
218cf28ed85SJohn Marino static unsigned short int fts_stat (FTS *, FTSENT *, bool) internal_function;
219cf28ed85SJohn Marino static int      fts_safe_changedir (FTS *, FTSENT *, int, const char *)
220cf28ed85SJohn Marino      internal_function;
221cf28ed85SJohn Marino 
222cf28ed85SJohn Marino #include "fts-cycle.c"
223cf28ed85SJohn Marino 
224cf28ed85SJohn Marino #ifndef MAX
225cf28ed85SJohn Marino # define MAX(a,b) ((a) > (b) ? (a) : (b))
226cf28ed85SJohn Marino #endif
227cf28ed85SJohn Marino 
228cf28ed85SJohn Marino #ifndef SIZE_MAX
229cf28ed85SJohn Marino # define SIZE_MAX ((size_t) -1)
230cf28ed85SJohn Marino #endif
231cf28ed85SJohn Marino 
232cf28ed85SJohn Marino #define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
233cf28ed85SJohn Marino #define STREQ(a, b)     (strcmp (a, b) == 0)
234cf28ed85SJohn Marino 
235cf28ed85SJohn Marino #define CLR(opt)        (sp->fts_options &= ~(opt))
236cf28ed85SJohn Marino #define ISSET(opt)      (sp->fts_options & (opt))
237cf28ed85SJohn Marino #define SET(opt)        (sp->fts_options |= (opt))
238cf28ed85SJohn Marino 
239cf28ed85SJohn Marino /* FIXME: FTS_NOCHDIR is now misnamed.
240cf28ed85SJohn Marino    Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
241cf28ed85SJohn Marino #define FCHDIR(sp, fd)                                  \
242cf28ed85SJohn Marino   (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \
243cf28ed85SJohn Marino                            ? (cwd_advance_fd ((sp), (fd), true), 0) \
244cf28ed85SJohn Marino                            : fchdir (fd)))
245cf28ed85SJohn Marino 
246cf28ed85SJohn Marino 
247cf28ed85SJohn Marino /* fts_build flags */
248cf28ed85SJohn Marino /* FIXME: make this an enum */
249cf28ed85SJohn Marino #define BCHILD          1               /* fts_children */
250cf28ed85SJohn Marino #define BNAMES          2               /* fts_children, names only */
251cf28ed85SJohn Marino #define BREAD           3               /* fts_read */
252cf28ed85SJohn Marino 
253cf28ed85SJohn Marino #if FTS_DEBUG
254cf28ed85SJohn Marino # include <inttypes.h>
255cf28ed85SJohn Marino # include <stdint.h>
256cf28ed85SJohn Marino # include <stdio.h>
257cf28ed85SJohn Marino # include "getcwdat.h"
258cf28ed85SJohn Marino bool fts_debug = false;
259cf28ed85SJohn Marino # define Dprintf(x) do { if (fts_debug) printf x; } while (false)
260cf28ed85SJohn Marino #else
261cf28ed85SJohn Marino # define Dprintf(x)
262cf28ed85SJohn Marino # define fd_ring_check(x)
263cf28ed85SJohn Marino # define fd_ring_print(a, b, c)
264cf28ed85SJohn Marino #endif
265cf28ed85SJohn Marino 
266cf28ed85SJohn Marino #define LEAVE_DIR(Fts, Ent, Tag)                                \
267cf28ed85SJohn Marino   do                                                            \
268cf28ed85SJohn Marino     {                                                           \
269cf28ed85SJohn Marino       Dprintf (("  %s-leaving: %s\n", Tag, (Ent)->fts_path));   \
270cf28ed85SJohn Marino       leave_dir (Fts, Ent);                                     \
271cf28ed85SJohn Marino       fd_ring_check (Fts);                                      \
272cf28ed85SJohn Marino     }                                                           \
273cf28ed85SJohn Marino   while (false)
274cf28ed85SJohn Marino 
275cf28ed85SJohn Marino static void
fd_ring_clear(I_ring * fd_ring)276cf28ed85SJohn Marino fd_ring_clear (I_ring *fd_ring)
277cf28ed85SJohn Marino {
278cf28ed85SJohn Marino   while ( ! i_ring_empty (fd_ring))
279cf28ed85SJohn Marino     {
280cf28ed85SJohn Marino       int fd = i_ring_pop (fd_ring);
281cf28ed85SJohn Marino       if (0 <= fd)
282cf28ed85SJohn Marino         close (fd);
283cf28ed85SJohn Marino     }
284cf28ed85SJohn Marino }
285cf28ed85SJohn Marino 
286cf28ed85SJohn Marino /* Overload the fts_statp->st_size member (otherwise unused, when
287cf28ed85SJohn Marino    fts_info is FTS_NSOK) to indicate whether fts_read should stat
288cf28ed85SJohn Marino    this entry or not.  */
289cf28ed85SJohn Marino static void
fts_set_stat_required(FTSENT * p,bool required)290cf28ed85SJohn Marino fts_set_stat_required (FTSENT *p, bool required)
291cf28ed85SJohn Marino {
292cf28ed85SJohn Marino   fts_assert (p->fts_info == FTS_NSOK);
293cf28ed85SJohn Marino   p->fts_statp->st_size = (required
294cf28ed85SJohn Marino                            ? FTS_STAT_REQUIRED
295cf28ed85SJohn Marino                            : FTS_NO_STAT_REQUIRED);
296cf28ed85SJohn Marino }
297cf28ed85SJohn Marino 
298cf28ed85SJohn Marino /* Virtual fchdir.  Advance SP's working directory file descriptor,
299cf28ed85SJohn Marino    SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
300cf28ed85SJohn Marino    CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
301cf28ed85SJohn Marino    open on sp->fts_cwd_fd; i.e., to move the working directory one level
302cf28ed85SJohn Marino    down.  */
303cf28ed85SJohn Marino static void
304cf28ed85SJohn Marino internal_function
cwd_advance_fd(FTS * sp,int fd,bool chdir_down_one)305cf28ed85SJohn Marino cwd_advance_fd (FTS *sp, int fd, bool chdir_down_one)
306cf28ed85SJohn Marino {
307cf28ed85SJohn Marino   int old = sp->fts_cwd_fd;
308cf28ed85SJohn Marino   fts_assert (old != fd || old == AT_FDCWD);
309cf28ed85SJohn Marino 
310cf28ed85SJohn Marino   if (chdir_down_one)
311cf28ed85SJohn Marino     {
312cf28ed85SJohn Marino       /* Push "old" onto the ring.
313cf28ed85SJohn Marino          If the displaced file descriptor is non-negative, close it.  */
314cf28ed85SJohn Marino       int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
315cf28ed85SJohn Marino       fd_ring_print (sp, stderr, "post-push");
316cf28ed85SJohn Marino       if (0 <= prev_fd_in_slot)
317cf28ed85SJohn Marino         close (prev_fd_in_slot); /* ignore any close failure */
318cf28ed85SJohn Marino     }
319cf28ed85SJohn Marino   else if ( ! ISSET (FTS_NOCHDIR))
320cf28ed85SJohn Marino     {
321cf28ed85SJohn Marino       if (0 <= old)
322cf28ed85SJohn Marino         close (old); /* ignore any close failure */
323cf28ed85SJohn Marino     }
324cf28ed85SJohn Marino 
325cf28ed85SJohn Marino   sp->fts_cwd_fd = fd;
326cf28ed85SJohn Marino }
327cf28ed85SJohn Marino 
328cf28ed85SJohn Marino /* Restore the initial, pre-traversal, "working directory".
329cf28ed85SJohn Marino    In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,
330cf28ed85SJohn Marino    we may actually change the working directory.
331cf28ed85SJohn Marino    Return 0 upon success. Upon failure, set errno and return nonzero.  */
332cf28ed85SJohn Marino static int
restore_initial_cwd(FTS * sp)333cf28ed85SJohn Marino restore_initial_cwd (FTS *sp)
334cf28ed85SJohn Marino {
335cf28ed85SJohn Marino   int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);
336cf28ed85SJohn Marino   fd_ring_clear (&(sp->fts_fd_ring));
337cf28ed85SJohn Marino   return fail;
338cf28ed85SJohn Marino }
339cf28ed85SJohn Marino 
340cf28ed85SJohn Marino /* Open the directory DIR if possible, and return a file
341cf28ed85SJohn Marino    descriptor.  Return -1 and set errno on failure.  It doesn't matter
342cf28ed85SJohn Marino    whether the file descriptor has read or write access.  */
343cf28ed85SJohn Marino 
344680a9cb8SJohn Marino static int
345cf28ed85SJohn Marino internal_function
diropen(FTS const * sp,char const * dir)346cf28ed85SJohn Marino diropen (FTS const *sp, char const *dir)
347cf28ed85SJohn Marino {
348*09d4459fSDaniel Fojt   int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
349*09d4459fSDaniel Fojt                     | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
350cf28ed85SJohn Marino 
351cf28ed85SJohn Marino   int fd = (ISSET (FTS_CWDFD)
352cf28ed85SJohn Marino             ? openat (sp->fts_cwd_fd, dir, open_flags)
353cf28ed85SJohn Marino             : open (dir, open_flags));
354cf28ed85SJohn Marino   return fd;
355cf28ed85SJohn Marino }
356cf28ed85SJohn Marino 
357cf28ed85SJohn Marino FTS *
fts_open(char * const * argv,register int options,int (* compar)(FTSENT const **,FTSENT const **))358cf28ed85SJohn Marino fts_open (char * const *argv,
359cf28ed85SJohn Marino           register int options,
360cf28ed85SJohn Marino           int (*compar) (FTSENT const **, FTSENT const **))
361cf28ed85SJohn Marino {
362cf28ed85SJohn Marino         register FTS *sp;
363cf28ed85SJohn Marino         register FTSENT *p, *root;
364cf28ed85SJohn Marino         register size_t nitems;
365cf28ed85SJohn Marino         FTSENT *parent = NULL;
366cf28ed85SJohn Marino         FTSENT *tmp = NULL;     /* pacify gcc */
367cf28ed85SJohn Marino         bool defer_stat;
368cf28ed85SJohn Marino 
369cf28ed85SJohn Marino         /* Options check. */
370cf28ed85SJohn Marino         if (options & ~FTS_OPTIONMASK) {
371cf28ed85SJohn Marino                 __set_errno (EINVAL);
372cf28ed85SJohn Marino                 return (NULL);
373cf28ed85SJohn Marino         }
374cf28ed85SJohn Marino         if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
375cf28ed85SJohn Marino                 __set_errno (EINVAL);
376cf28ed85SJohn Marino                 return (NULL);
377cf28ed85SJohn Marino         }
378cf28ed85SJohn Marino         if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
379cf28ed85SJohn Marino                 __set_errno (EINVAL);
380cf28ed85SJohn Marino                 return (NULL);
381cf28ed85SJohn Marino         }
382cf28ed85SJohn Marino 
383cf28ed85SJohn Marino         /* Allocate/initialize the stream */
384*09d4459fSDaniel Fojt         sp = calloc (1, sizeof *sp);
385*09d4459fSDaniel Fojt         if (sp == NULL)
386cf28ed85SJohn Marino                 return (NULL);
387cf28ed85SJohn Marino         sp->fts_compar = compar;
388cf28ed85SJohn Marino         sp->fts_options = options;
389cf28ed85SJohn Marino 
390cf28ed85SJohn Marino         /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
391cf28ed85SJohn Marino         if (ISSET(FTS_LOGICAL)) {
392cf28ed85SJohn Marino                 SET(FTS_NOCHDIR);
393cf28ed85SJohn Marino                 CLR(FTS_CWDFD);
394cf28ed85SJohn Marino         }
395cf28ed85SJohn Marino 
396cf28ed85SJohn Marino         /* Initialize fts_cwd_fd.  */
397cf28ed85SJohn Marino         sp->fts_cwd_fd = AT_FDCWD;
398cf28ed85SJohn Marino         if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
399cf28ed85SJohn Marino           {
400cf28ed85SJohn Marino             /* While it isn't technically necessary to open "." this
401cf28ed85SJohn Marino                early, doing it here saves us the trouble of ensuring
402cf28ed85SJohn Marino                later (where it'd be messier) that "." can in fact
403cf28ed85SJohn Marino                be opened.  If not, revert to FTS_NOCHDIR mode.  */
404*09d4459fSDaniel Fojt             int fd = open (".", O_SEARCH);
405cf28ed85SJohn Marino             if (fd < 0)
406cf28ed85SJohn Marino               {
407cf28ed85SJohn Marino                 /* Even if "." is unreadable, don't revert to FTS_NOCHDIR mode
408cf28ed85SJohn Marino                    on systems like Linux+PROC_FS, where our openat emulation
409cf28ed85SJohn Marino                    is good enough.  Note: on a system that emulates
410cf28ed85SJohn Marino                    openat via /proc, this technique can still fail, but
411cf28ed85SJohn Marino                    only in extreme conditions, e.g., when the working
412cf28ed85SJohn Marino                    directory cannot be saved (i.e. save_cwd fails) --
413cf28ed85SJohn Marino                    and that happens on Linux only when "." is unreadable
414cf28ed85SJohn Marino                    and the CWD would be longer than PATH_MAX.
415cf28ed85SJohn Marino                    FIXME: once Linux kernel openat support is well established,
416cf28ed85SJohn Marino                    replace the above open call and this entire if/else block
417cf28ed85SJohn Marino                    with the body of the if-block below.  */
418cf28ed85SJohn Marino                 if ( openat_needs_fchdir ())
419cf28ed85SJohn Marino                   {
420cf28ed85SJohn Marino                     SET(FTS_NOCHDIR);
421cf28ed85SJohn Marino                     CLR(FTS_CWDFD);
422cf28ed85SJohn Marino                   }
423cf28ed85SJohn Marino               }
424cf28ed85SJohn Marino             else
425cf28ed85SJohn Marino               {
426cf28ed85SJohn Marino                 close (fd);
427cf28ed85SJohn Marino               }
428cf28ed85SJohn Marino           }
429cf28ed85SJohn Marino 
430cf28ed85SJohn Marino         /*
431cf28ed85SJohn Marino          * Start out with 1K of file name space, and enough, in any case,
432cf28ed85SJohn Marino          * to hold the user's file names.
433cf28ed85SJohn Marino          */
434cf28ed85SJohn Marino #ifndef MAXPATHLEN
435cf28ed85SJohn Marino # define MAXPATHLEN 1024
436cf28ed85SJohn Marino #endif
437cf28ed85SJohn Marino         {
438cf28ed85SJohn Marino           size_t maxarglen = fts_maxarglen(argv);
439cf28ed85SJohn Marino           if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
440cf28ed85SJohn Marino                   goto mem1;
441cf28ed85SJohn Marino         }
442cf28ed85SJohn Marino 
443cf28ed85SJohn Marino         /* Allocate/initialize root's parent. */
444cf28ed85SJohn Marino         if (*argv != NULL) {
445cf28ed85SJohn Marino                 if ((parent = fts_alloc(sp, "", 0)) == NULL)
446cf28ed85SJohn Marino                         goto mem2;
447cf28ed85SJohn Marino                 parent->fts_level = FTS_ROOTPARENTLEVEL;
448*09d4459fSDaniel Fojt                 parent->fts_n_dirs_remaining = -1;
449cf28ed85SJohn Marino           }
450cf28ed85SJohn Marino 
451cf28ed85SJohn Marino         /* The classic fts implementation would call fts_stat with
452cf28ed85SJohn Marino            a new entry for each iteration of the loop below.
453cf28ed85SJohn Marino            If the comparison function is not specified or if the
454cf28ed85SJohn Marino            FTS_DEFER_STAT option is in effect, don't stat any entry
455cf28ed85SJohn Marino            in this loop.  This is an attempt to minimize the interval
456cf28ed85SJohn Marino            between the initial stat/lstat/fstatat and the point at which
457cf28ed85SJohn Marino            a directory argument is first opened.  This matters for any
458cf28ed85SJohn Marino            directory command line argument that resides on a file system
459cf28ed85SJohn Marino            without genuine i-nodes.  If you specify FTS_DEFER_STAT along
460cf28ed85SJohn Marino            with a comparison function, that function must not access any
461cf28ed85SJohn Marino            data via the fts_statp pointer.  */
462cf28ed85SJohn Marino         defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
463cf28ed85SJohn Marino 
464cf28ed85SJohn Marino         /* Allocate/initialize root(s). */
465cf28ed85SJohn Marino         for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
466cf28ed85SJohn Marino                 /* *Do* allow zero-length file names. */
467cf28ed85SJohn Marino                 size_t len = strlen(*argv);
468680a9cb8SJohn Marino 
469680a9cb8SJohn Marino                 if ( ! (options & FTS_VERBATIM))
470680a9cb8SJohn Marino                   {
471680a9cb8SJohn Marino                     /* If there are two or more trailing slashes, trim all but one,
472680a9cb8SJohn Marino                        but don't change "//" to "/", and do map "///" to "/".  */
473680a9cb8SJohn Marino                     char const *v = *argv;
474680a9cb8SJohn Marino                     if (2 < len && v[len - 1] == '/')
475680a9cb8SJohn Marino                       while (1 < len && v[len - 2] == '/')
476680a9cb8SJohn Marino                         --len;
477680a9cb8SJohn Marino                   }
478680a9cb8SJohn Marino 
479cf28ed85SJohn Marino                 if ((p = fts_alloc(sp, *argv, len)) == NULL)
480cf28ed85SJohn Marino                         goto mem3;
481cf28ed85SJohn Marino                 p->fts_level = FTS_ROOTLEVEL;
482cf28ed85SJohn Marino                 p->fts_parent = parent;
483cf28ed85SJohn Marino                 p->fts_accpath = p->fts_name;
484cf28ed85SJohn Marino                 /* Even when defer_stat is true, be sure to stat the first
485cf28ed85SJohn Marino                    command line argument, since fts_read (at least with
486cf28ed85SJohn Marino                    FTS_XDEV) requires that.  */
487cf28ed85SJohn Marino                 if (defer_stat && root != NULL) {
488cf28ed85SJohn Marino                         p->fts_info = FTS_NSOK;
489cf28ed85SJohn Marino                         fts_set_stat_required(p, true);
490cf28ed85SJohn Marino                 } else {
491cf28ed85SJohn Marino                         p->fts_info = fts_stat(sp, p, false);
492cf28ed85SJohn Marino                 }
493cf28ed85SJohn Marino 
494cf28ed85SJohn Marino                 /*
495cf28ed85SJohn Marino                  * If comparison routine supplied, traverse in sorted
496cf28ed85SJohn Marino                  * order; otherwise traverse in the order specified.
497cf28ed85SJohn Marino                  */
498cf28ed85SJohn Marino                 if (compar) {
499cf28ed85SJohn Marino                         p->fts_link = root;
500cf28ed85SJohn Marino                         root = p;
501cf28ed85SJohn Marino                 } else {
502cf28ed85SJohn Marino                         p->fts_link = NULL;
503cf28ed85SJohn Marino                         if (root == NULL)
504cf28ed85SJohn Marino                                 tmp = root = p;
505cf28ed85SJohn Marino                         else {
506cf28ed85SJohn Marino                                 tmp->fts_link = p;
507cf28ed85SJohn Marino                                 tmp = p;
508cf28ed85SJohn Marino                         }
509cf28ed85SJohn Marino                 }
510cf28ed85SJohn Marino         }
511cf28ed85SJohn Marino         if (compar && nitems > 1)
512cf28ed85SJohn Marino                 root = fts_sort(sp, root, nitems);
513cf28ed85SJohn Marino 
514cf28ed85SJohn Marino         /*
515cf28ed85SJohn Marino          * Allocate a dummy pointer and make fts_read think that we've just
516cf28ed85SJohn Marino          * finished the node before the root(s); set p->fts_info to FTS_INIT
517cf28ed85SJohn Marino          * so that everything about the "current" node is ignored.
518cf28ed85SJohn Marino          */
519cf28ed85SJohn Marino         if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
520cf28ed85SJohn Marino                 goto mem3;
521cf28ed85SJohn Marino         sp->fts_cur->fts_link = root;
522cf28ed85SJohn Marino         sp->fts_cur->fts_info = FTS_INIT;
523*09d4459fSDaniel Fojt         sp->fts_cur->fts_level = 1;
524cf28ed85SJohn Marino         if (! setup_dir (sp))
525cf28ed85SJohn Marino                 goto mem3;
526cf28ed85SJohn Marino 
527cf28ed85SJohn Marino         /*
528cf28ed85SJohn Marino          * If using chdir(2), grab a file descriptor pointing to dot to ensure
529cf28ed85SJohn Marino          * that we can get back here; this could be avoided for some file names,
530cf28ed85SJohn Marino          * but almost certainly not worth the effort.  Slashes, symbolic links,
531cf28ed85SJohn Marino          * and ".." are all fairly nasty problems.  Note, if we can't get the
532cf28ed85SJohn Marino          * descriptor we run anyway, just more slowly.
533cf28ed85SJohn Marino          */
534cf28ed85SJohn Marino         if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
535cf28ed85SJohn Marino             && (sp->fts_rfd = diropen (sp, ".")) < 0)
536cf28ed85SJohn Marino                 SET(FTS_NOCHDIR);
537cf28ed85SJohn Marino 
538cf28ed85SJohn Marino         i_ring_init (&sp->fts_fd_ring, -1);
539cf28ed85SJohn Marino         return (sp);
540cf28ed85SJohn Marino 
541cf28ed85SJohn Marino mem3:   fts_lfree(root);
542cf28ed85SJohn Marino         free(parent);
543cf28ed85SJohn Marino mem2:   free(sp->fts_path);
544cf28ed85SJohn Marino mem1:   free(sp);
545cf28ed85SJohn Marino         return (NULL);
546cf28ed85SJohn Marino }
547cf28ed85SJohn Marino 
548cf28ed85SJohn Marino static void
549cf28ed85SJohn Marino internal_function
fts_load(FTS * sp,register FTSENT * p)550cf28ed85SJohn Marino fts_load (FTS *sp, register FTSENT *p)
551cf28ed85SJohn Marino {
552cf28ed85SJohn Marino         register size_t len;
553cf28ed85SJohn Marino         register char *cp;
554cf28ed85SJohn Marino 
555cf28ed85SJohn Marino         /*
556cf28ed85SJohn Marino          * Load the stream structure for the next traversal.  Since we don't
557cf28ed85SJohn Marino          * actually enter the directory until after the preorder visit, set
558cf28ed85SJohn Marino          * the fts_accpath field specially so the chdir gets done to the right
559cf28ed85SJohn Marino          * place and the user can access the first node.  From fts_open it's
560cf28ed85SJohn Marino          * known that the file name will fit.
561cf28ed85SJohn Marino          */
562cf28ed85SJohn Marino         len = p->fts_pathlen = p->fts_namelen;
563cf28ed85SJohn Marino         memmove(sp->fts_path, p->fts_name, len + 1);
564cf28ed85SJohn Marino         if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
565cf28ed85SJohn Marino                 len = strlen(++cp);
566cf28ed85SJohn Marino                 memmove(p->fts_name, cp, len + 1);
567cf28ed85SJohn Marino                 p->fts_namelen = len;
568cf28ed85SJohn Marino         }
569cf28ed85SJohn Marino         p->fts_accpath = p->fts_path = sp->fts_path;
570cf28ed85SJohn Marino }
571cf28ed85SJohn Marino 
572cf28ed85SJohn Marino int
fts_close(FTS * sp)573cf28ed85SJohn Marino fts_close (FTS *sp)
574cf28ed85SJohn Marino {
575cf28ed85SJohn Marino         register FTSENT *freep, *p;
576cf28ed85SJohn Marino         int saved_errno = 0;
577cf28ed85SJohn Marino 
578cf28ed85SJohn Marino         /*
579cf28ed85SJohn Marino          * This still works if we haven't read anything -- the dummy structure
580cf28ed85SJohn Marino          * points to the root list, so we step through to the end of the root
581cf28ed85SJohn Marino          * list which has a valid parent pointer.
582cf28ed85SJohn Marino          */
583cf28ed85SJohn Marino         if (sp->fts_cur) {
584cf28ed85SJohn Marino                 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
585cf28ed85SJohn Marino                         freep = p;
586cf28ed85SJohn Marino                         p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
587cf28ed85SJohn Marino                         free(freep);
588cf28ed85SJohn Marino                 }
589cf28ed85SJohn Marino                 free(p);
590cf28ed85SJohn Marino         }
591cf28ed85SJohn Marino 
592cf28ed85SJohn Marino         /* Free up child linked list, sort array, file name buffer. */
593cf28ed85SJohn Marino         if (sp->fts_child)
594cf28ed85SJohn Marino                 fts_lfree(sp->fts_child);
595cf28ed85SJohn Marino         free(sp->fts_array);
596cf28ed85SJohn Marino         free(sp->fts_path);
597cf28ed85SJohn Marino 
598cf28ed85SJohn Marino         if (ISSET(FTS_CWDFD))
599cf28ed85SJohn Marino           {
600cf28ed85SJohn Marino             if (0 <= sp->fts_cwd_fd)
601cf28ed85SJohn Marino               if (close (sp->fts_cwd_fd))
602cf28ed85SJohn Marino                 saved_errno = errno;
603cf28ed85SJohn Marino           }
604cf28ed85SJohn Marino         else if (!ISSET(FTS_NOCHDIR))
605cf28ed85SJohn Marino           {
606cf28ed85SJohn Marino             /* Return to original directory, save errno if necessary. */
607cf28ed85SJohn Marino             if (fchdir(sp->fts_rfd))
608cf28ed85SJohn Marino               saved_errno = errno;
609cf28ed85SJohn Marino 
610cf28ed85SJohn Marino             /* If close fails, record errno only if saved_errno is zero,
611cf28ed85SJohn Marino                so that we report the probably-more-meaningful fchdir errno.  */
612cf28ed85SJohn Marino             if (close (sp->fts_rfd))
613cf28ed85SJohn Marino               if (saved_errno == 0)
614cf28ed85SJohn Marino                 saved_errno = errno;
615cf28ed85SJohn Marino           }
616cf28ed85SJohn Marino 
617cf28ed85SJohn Marino         fd_ring_clear (&sp->fts_fd_ring);
618cf28ed85SJohn Marino 
619cf28ed85SJohn Marino         if (sp->fts_leaf_optimization_works_ht)
620cf28ed85SJohn Marino           hash_free (sp->fts_leaf_optimization_works_ht);
621cf28ed85SJohn Marino 
622cf28ed85SJohn Marino         free_dir (sp);
623cf28ed85SJohn Marino 
624cf28ed85SJohn Marino         /* Free up the stream pointer. */
625cf28ed85SJohn Marino         free(sp);
626cf28ed85SJohn Marino 
627cf28ed85SJohn Marino         /* Set errno and return. */
628cf28ed85SJohn Marino         if (saved_errno) {
629cf28ed85SJohn Marino                 __set_errno (saved_errno);
630cf28ed85SJohn Marino                 return (-1);
631cf28ed85SJohn Marino         }
632cf28ed85SJohn Marino 
633cf28ed85SJohn Marino         return (0);
634cf28ed85SJohn Marino }
635cf28ed85SJohn Marino 
636*09d4459fSDaniel Fojt /* Minimum link count of a traditional Unix directory.  When leaf
637*09d4459fSDaniel Fojt    optimization is OK and MIN_DIR_NLINK <= st_nlink, then st_nlink is
638*09d4459fSDaniel Fojt    an upper bound on the number of subdirectories (counting "." and
639*09d4459fSDaniel Fojt    "..").  */
640*09d4459fSDaniel Fojt enum { MIN_DIR_NLINK = 2 };
641*09d4459fSDaniel Fojt 
642*09d4459fSDaniel Fojt /* Whether leaf optimization is OK for a directory.  */
643*09d4459fSDaniel Fojt enum leaf_optimization
644*09d4459fSDaniel Fojt   {
645*09d4459fSDaniel Fojt     /* st_nlink is not reliable for this directory's subdirectories.  */
646*09d4459fSDaniel Fojt     NO_LEAF_OPTIMIZATION,
647*09d4459fSDaniel Fojt 
648*09d4459fSDaniel Fojt     /* Leaf optimization is OK, but is not useful for avoiding stat calls.  */
649*09d4459fSDaniel Fojt     OK_LEAF_OPTIMIZATION,
650*09d4459fSDaniel Fojt 
651*09d4459fSDaniel Fojt     /* Leaf optimization is not only OK: it is useful for avoiding
652*09d4459fSDaniel Fojt        stat calls, because dirent.d_type does not work.  */
653*09d4459fSDaniel Fojt     NOSTAT_LEAF_OPTIMIZATION
654*09d4459fSDaniel Fojt   };
655*09d4459fSDaniel Fojt 
656*09d4459fSDaniel Fojt #if (defined __linux__ || defined __ANDROID__) \
657cf28ed85SJohn Marino   && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
658cf28ed85SJohn Marino 
659cf28ed85SJohn Marino # include <sys/vfs.h>
660cf28ed85SJohn Marino 
661cf28ed85SJohn Marino /* Linux-specific constants from coreutils' src/fs.h */
662*09d4459fSDaniel Fojt # define S_MAGIC_AFS 0x5346414F
663*09d4459fSDaniel Fojt # define S_MAGIC_CIFS 0xFF534D42
664cf28ed85SJohn Marino # define S_MAGIC_NFS 0x6969
665cf28ed85SJohn Marino # define S_MAGIC_PROC 0x9FA0
666*09d4459fSDaniel Fojt # define S_MAGIC_REISERFS 0x52654973
667*09d4459fSDaniel Fojt # define S_MAGIC_TMPFS 0x1021994
668*09d4459fSDaniel Fojt # define S_MAGIC_XFS 0x58465342
669cf28ed85SJohn Marino 
670*09d4459fSDaniel Fojt # ifdef HAVE___FSWORD_T
671*09d4459fSDaniel Fojt typedef __fsword_t fsword;
672*09d4459fSDaniel Fojt # else
673*09d4459fSDaniel Fojt typedef long int fsword;
674*09d4459fSDaniel Fojt # endif
675*09d4459fSDaniel Fojt 
676*09d4459fSDaniel Fojt /* Map a stat.st_dev number to a file system type number f_ftype.  */
677*09d4459fSDaniel Fojt struct dev_type
678*09d4459fSDaniel Fojt {
679*09d4459fSDaniel Fojt   dev_t st_dev;
680*09d4459fSDaniel Fojt   fsword f_type;
681*09d4459fSDaniel Fojt };
682*09d4459fSDaniel Fojt 
683*09d4459fSDaniel Fojt /* Use a tiny initial size.  If a traversal encounters more than
684*09d4459fSDaniel Fojt    a few devices, the cost of growing/rehashing this table will be
685*09d4459fSDaniel Fojt    rendered negligible by the number of inodes processed.  */
686*09d4459fSDaniel Fojt enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };
687*09d4459fSDaniel Fojt 
688*09d4459fSDaniel Fojt static size_t
dev_type_hash(void const * x,size_t table_size)689*09d4459fSDaniel Fojt dev_type_hash (void const *x, size_t table_size)
690*09d4459fSDaniel Fojt {
691*09d4459fSDaniel Fojt   struct dev_type const *ax = x;
692*09d4459fSDaniel Fojt   uintmax_t dev = ax->st_dev;
693*09d4459fSDaniel Fojt   return dev % table_size;
694*09d4459fSDaniel Fojt }
695*09d4459fSDaniel Fojt 
696cf28ed85SJohn Marino static bool
dev_type_compare(void const * x,void const * y)697*09d4459fSDaniel Fojt dev_type_compare (void const *x, void const *y)
698*09d4459fSDaniel Fojt {
699*09d4459fSDaniel Fojt   struct dev_type const *ax = x;
700*09d4459fSDaniel Fojt   struct dev_type const *ay = y;
701*09d4459fSDaniel Fojt   return ax->st_dev == ay->st_dev;
702*09d4459fSDaniel Fojt }
703*09d4459fSDaniel Fojt 
704*09d4459fSDaniel Fojt /* Return the file system type of P with file descriptor FD, or 0 if not known.
705*09d4459fSDaniel Fojt    If FD is negative, P's file descriptor is unavailable.
706*09d4459fSDaniel Fojt    Try to cache known values.  */
707*09d4459fSDaniel Fojt 
708*09d4459fSDaniel Fojt static fsword
filesystem_type(FTSENT const * p,int fd)709*09d4459fSDaniel Fojt filesystem_type (FTSENT const *p, int fd)
710*09d4459fSDaniel Fojt {
711*09d4459fSDaniel Fojt   FTS *sp = p->fts_fts;
712*09d4459fSDaniel Fojt   Hash_table *h = sp->fts_leaf_optimization_works_ht;
713*09d4459fSDaniel Fojt   struct dev_type *ent;
714*09d4459fSDaniel Fojt   struct statfs fs_buf;
715*09d4459fSDaniel Fojt 
716*09d4459fSDaniel Fojt   /* If we're not in CWDFD mode, don't bother with this optimization,
717*09d4459fSDaniel Fojt      since the caller is not serious about performance.  */
718*09d4459fSDaniel Fojt   if (!ISSET (FTS_CWDFD))
719*09d4459fSDaniel Fojt     return 0;
720*09d4459fSDaniel Fojt 
721*09d4459fSDaniel Fojt   if (! h)
722*09d4459fSDaniel Fojt     h = sp->fts_leaf_optimization_works_ht
723*09d4459fSDaniel Fojt       = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,
724*09d4459fSDaniel Fojt                          dev_type_compare, free);
725*09d4459fSDaniel Fojt   if (h)
726*09d4459fSDaniel Fojt     {
727*09d4459fSDaniel Fojt       struct dev_type tmp;
728*09d4459fSDaniel Fojt       tmp.st_dev = p->fts_statp->st_dev;
729*09d4459fSDaniel Fojt       ent = hash_lookup (h, &tmp);
730*09d4459fSDaniel Fojt       if (ent)
731*09d4459fSDaniel Fojt         return ent->f_type;
732*09d4459fSDaniel Fojt     }
733*09d4459fSDaniel Fojt 
734*09d4459fSDaniel Fojt   /* Look-up failed.  Query directly and cache the result.  */
735*09d4459fSDaniel Fojt   if (fd < 0 || fstatfs (fd, &fs_buf) != 0)
736*09d4459fSDaniel Fojt     return 0;
737*09d4459fSDaniel Fojt 
738*09d4459fSDaniel Fojt   if (h)
739*09d4459fSDaniel Fojt     {
740*09d4459fSDaniel Fojt       struct dev_type *t2 = malloc (sizeof *t2);
741*09d4459fSDaniel Fojt       if (t2)
742*09d4459fSDaniel Fojt         {
743*09d4459fSDaniel Fojt           t2->st_dev = p->fts_statp->st_dev;
744*09d4459fSDaniel Fojt           t2->f_type = fs_buf.f_type;
745*09d4459fSDaniel Fojt 
746*09d4459fSDaniel Fojt           ent = hash_insert (h, t2);
747*09d4459fSDaniel Fojt           if (ent)
748*09d4459fSDaniel Fojt             fts_assert (ent == t2);
749*09d4459fSDaniel Fojt           else
750*09d4459fSDaniel Fojt             free (t2);
751*09d4459fSDaniel Fojt         }
752*09d4459fSDaniel Fojt     }
753*09d4459fSDaniel Fojt 
754*09d4459fSDaniel Fojt   return fs_buf.f_type;
755*09d4459fSDaniel Fojt }
756*09d4459fSDaniel Fojt 
757*09d4459fSDaniel Fojt /* Return true if sorting dirents on inode numbers is known to improve
758*09d4459fSDaniel Fojt    traversal performance for the directory P with descriptor DIR_FD.
759*09d4459fSDaniel Fojt    Return false otherwise.  When in doubt, return true.
760*09d4459fSDaniel Fojt    DIR_FD is negative if unavailable.  */
761*09d4459fSDaniel Fojt static bool
dirent_inode_sort_may_be_useful(FTSENT const * p,int dir_fd)762*09d4459fSDaniel Fojt dirent_inode_sort_may_be_useful (FTSENT const *p, int dir_fd)
763cf28ed85SJohn Marino {
764cf28ed85SJohn Marino   /* Skip the sort only if we can determine efficiently
765cf28ed85SJohn Marino      that skipping it is the right thing to do.
766cf28ed85SJohn Marino      The cost of performing an unnecessary sort is negligible,
767cf28ed85SJohn Marino      while the cost of *not* performing it can be O(N^2) with
768cf28ed85SJohn Marino      a very large constant.  */
769cf28ed85SJohn Marino 
770*09d4459fSDaniel Fojt   switch (filesystem_type (p, dir_fd))
771cf28ed85SJohn Marino     {
772*09d4459fSDaniel Fojt     case S_MAGIC_CIFS:
773cf28ed85SJohn Marino     case S_MAGIC_NFS:
774*09d4459fSDaniel Fojt     case S_MAGIC_TMPFS:
775cf28ed85SJohn Marino       /* On a file system of any of these types, sorting
776cf28ed85SJohn Marino          is unnecessary, and hence wasteful.  */
777cf28ed85SJohn Marino       return false;
778cf28ed85SJohn Marino 
779cf28ed85SJohn Marino     default:
780cf28ed85SJohn Marino       return true;
781cf28ed85SJohn Marino     }
782cf28ed85SJohn Marino }
783cf28ed85SJohn Marino 
784*09d4459fSDaniel Fojt /* Given an FTS entry P for a directory with descriptor DIR_FD,
785*09d4459fSDaniel Fojt    return true if it is both useful and valid to apply leaf optimization.
786*09d4459fSDaniel Fojt    The optimization is useful only for file systems that lack usable
787*09d4459fSDaniel Fojt    dirent.d_type info.  The optimization is valid if an st_nlink value
788*09d4459fSDaniel Fojt    of at least MIN_DIR_NLINK is an upper bound on the number of
789*09d4459fSDaniel Fojt    subdirectories of D, counting "." and ".."  as subdirectories.
790*09d4459fSDaniel Fojt    DIR_FD is negative if unavailable.  */
791*09d4459fSDaniel Fojt static enum leaf_optimization
leaf_optimization(FTSENT const * p,int dir_fd)792*09d4459fSDaniel Fojt leaf_optimization (FTSENT const *p, int dir_fd)
793cf28ed85SJohn Marino {
794*09d4459fSDaniel Fojt   switch (filesystem_type (p, dir_fd))
795cf28ed85SJohn Marino     {
796*09d4459fSDaniel Fojt       /* List here the file system types that may lack usable dirent.d_type
797cf28ed85SJohn Marino          info, yet for which the optimization does apply.  */
798cf28ed85SJohn Marino     case S_MAGIC_REISERFS:
799*09d4459fSDaniel Fojt     case S_MAGIC_XFS: /* XFS lacked it until 2013-08-22 commit.  */
800*09d4459fSDaniel Fojt       return NOSTAT_LEAF_OPTIMIZATION;
801cf28ed85SJohn Marino 
802*09d4459fSDaniel Fojt     case 0:
803*09d4459fSDaniel Fojt       /* Leaf optimization is unsafe if the file system type is unknown.  */
804*09d4459fSDaniel Fojt       FALLTHROUGH;
805*09d4459fSDaniel Fojt     case S_MAGIC_AFS:
806*09d4459fSDaniel Fojt       /* Although AFS mount points are not counted in st_nlink, they
807*09d4459fSDaniel Fojt          act like directories.  See <https://bugs.debian.org/143111>.  */
808*09d4459fSDaniel Fojt       FALLTHROUGH;
809*09d4459fSDaniel Fojt     case S_MAGIC_CIFS:
810*09d4459fSDaniel Fojt       /* Leaf optimization causes 'find' to abort.  See
811*09d4459fSDaniel Fojt          <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>.  */
812*09d4459fSDaniel Fojt       FALLTHROUGH;
813*09d4459fSDaniel Fojt     case S_MAGIC_NFS:
814*09d4459fSDaniel Fojt       /* NFS provides usable dirent.d_type but not necessarily for all entries
815*09d4459fSDaniel Fojt          of large directories, so as per <https://bugzilla.redhat.com/1252549>
816*09d4459fSDaniel Fojt          NFS should return true.  However st_nlink values are not accurate on
817*09d4459fSDaniel Fojt          all implementations as per <https://bugzilla.redhat.com/1299169>.  */
818*09d4459fSDaniel Fojt       FALLTHROUGH;
819cf28ed85SJohn Marino     case S_MAGIC_PROC:
820*09d4459fSDaniel Fojt       /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc
821*09d4459fSDaniel Fojt          may have bogus stat.st_nlink values.  */
822*09d4459fSDaniel Fojt       return NO_LEAF_OPTIMIZATION;
823*09d4459fSDaniel Fojt 
824cf28ed85SJohn Marino     default:
825*09d4459fSDaniel Fojt       return OK_LEAF_OPTIMIZATION;
826cf28ed85SJohn Marino     }
827cf28ed85SJohn Marino }
828cf28ed85SJohn Marino 
829cf28ed85SJohn Marino #else
830cf28ed85SJohn Marino static bool
dirent_inode_sort_may_be_useful(FTSENT const * p _GL_UNUSED,int dir_fd _GL_UNUSED)831*09d4459fSDaniel Fojt dirent_inode_sort_may_be_useful (FTSENT const *p _GL_UNUSED,
832*09d4459fSDaniel Fojt                                  int dir_fd _GL_UNUSED)
833*09d4459fSDaniel Fojt {
834*09d4459fSDaniel Fojt   return true;
835*09d4459fSDaniel Fojt }
836*09d4459fSDaniel Fojt static enum leaf_optimization
leaf_optimization(FTSENT const * p _GL_UNUSED,int dir_fd _GL_UNUSED)837*09d4459fSDaniel Fojt leaf_optimization (FTSENT const *p _GL_UNUSED, int dir_fd _GL_UNUSED)
838*09d4459fSDaniel Fojt {
839*09d4459fSDaniel Fojt   return NO_LEAF_OPTIMIZATION;
840*09d4459fSDaniel Fojt }
841cf28ed85SJohn Marino #endif
842cf28ed85SJohn Marino 
843cf28ed85SJohn Marino /*
844cf28ed85SJohn Marino  * Special case of "/" at the end of the file name so that slashes aren't
845cf28ed85SJohn Marino  * appended which would cause file names to be written as "....//foo".
846cf28ed85SJohn Marino  */
847cf28ed85SJohn Marino #define NAPPEND(p)                                                      \
848cf28ed85SJohn Marino         (p->fts_path[p->fts_pathlen - 1] == '/'                         \
849cf28ed85SJohn Marino             ? p->fts_pathlen - 1 : p->fts_pathlen)
850cf28ed85SJohn Marino 
851cf28ed85SJohn Marino FTSENT *
fts_read(register FTS * sp)852cf28ed85SJohn Marino fts_read (register FTS *sp)
853cf28ed85SJohn Marino {
854cf28ed85SJohn Marino         register FTSENT *p, *tmp;
855cf28ed85SJohn Marino         register unsigned short int instr;
856cf28ed85SJohn Marino         register char *t;
857cf28ed85SJohn Marino 
858cf28ed85SJohn Marino         /* If finished or unrecoverable error, return NULL. */
859cf28ed85SJohn Marino         if (sp->fts_cur == NULL || ISSET(FTS_STOP))
860cf28ed85SJohn Marino                 return (NULL);
861cf28ed85SJohn Marino 
862cf28ed85SJohn Marino         /* Set current node pointer. */
863cf28ed85SJohn Marino         p = sp->fts_cur;
864cf28ed85SJohn Marino 
865cf28ed85SJohn Marino         /* Save and zero out user instructions. */
866cf28ed85SJohn Marino         instr = p->fts_instr;
867cf28ed85SJohn Marino         p->fts_instr = FTS_NOINSTR;
868cf28ed85SJohn Marino 
869cf28ed85SJohn Marino         /* Any type of file may be re-visited; re-stat and re-turn. */
870cf28ed85SJohn Marino         if (instr == FTS_AGAIN) {
871cf28ed85SJohn Marino                 p->fts_info = fts_stat(sp, p, false);
872cf28ed85SJohn Marino                 return (p);
873cf28ed85SJohn Marino         }
874cf28ed85SJohn Marino         Dprintf (("fts_read: p=%s\n",
875cf28ed85SJohn Marino                   p->fts_info == FTS_INIT ? "" : p->fts_path));
876cf28ed85SJohn Marino 
877cf28ed85SJohn Marino         /*
878cf28ed85SJohn Marino          * Following a symlink -- SLNONE test allows application to see
879cf28ed85SJohn Marino          * SLNONE and recover.  If indirecting through a symlink, have
880cf28ed85SJohn Marino          * keep a pointer to current location.  If unable to get that
881cf28ed85SJohn Marino          * pointer, follow fails.
882cf28ed85SJohn Marino          */
883cf28ed85SJohn Marino         if (instr == FTS_FOLLOW &&
884cf28ed85SJohn Marino             (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
885cf28ed85SJohn Marino                 p->fts_info = fts_stat(sp, p, true);
886cf28ed85SJohn Marino                 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
887cf28ed85SJohn Marino                         if ((p->fts_symfd = diropen (sp, ".")) < 0) {
888cf28ed85SJohn Marino                                 p->fts_errno = errno;
889cf28ed85SJohn Marino                                 p->fts_info = FTS_ERR;
890cf28ed85SJohn Marino                         } else
891cf28ed85SJohn Marino                                 p->fts_flags |= FTS_SYMFOLLOW;
892cf28ed85SJohn Marino                 }
893cf28ed85SJohn Marino                 goto check_for_dir;
894cf28ed85SJohn Marino         }
895cf28ed85SJohn Marino 
896cf28ed85SJohn Marino         /* Directory in pre-order. */
897cf28ed85SJohn Marino         if (p->fts_info == FTS_D) {
898cf28ed85SJohn Marino                 /* If skipped or crossed mount point, do post-order visit. */
899cf28ed85SJohn Marino                 if (instr == FTS_SKIP ||
900cf28ed85SJohn Marino                     (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
901cf28ed85SJohn Marino                         if (p->fts_flags & FTS_SYMFOLLOW)
902cf28ed85SJohn Marino                                 (void)close(p->fts_symfd);
903cf28ed85SJohn Marino                         if (sp->fts_child) {
904cf28ed85SJohn Marino                                 fts_lfree(sp->fts_child);
905cf28ed85SJohn Marino                                 sp->fts_child = NULL;
906cf28ed85SJohn Marino                         }
907cf28ed85SJohn Marino                         p->fts_info = FTS_DP;
908cf28ed85SJohn Marino                         LEAVE_DIR (sp, p, "1");
909cf28ed85SJohn Marino                         return (p);
910cf28ed85SJohn Marino                 }
911cf28ed85SJohn Marino 
912cf28ed85SJohn Marino                 /* Rebuild if only read the names and now traversing. */
913cf28ed85SJohn Marino                 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
914cf28ed85SJohn Marino                         CLR(FTS_NAMEONLY);
915cf28ed85SJohn Marino                         fts_lfree(sp->fts_child);
916cf28ed85SJohn Marino                         sp->fts_child = NULL;
917cf28ed85SJohn Marino                 }
918cf28ed85SJohn Marino 
919cf28ed85SJohn Marino                 /*
920cf28ed85SJohn Marino                  * Cd to the subdirectory.
921cf28ed85SJohn Marino                  *
922cf28ed85SJohn Marino                  * If have already read and now fail to chdir, whack the list
923cf28ed85SJohn Marino                  * to make the names come out right, and set the parent errno
924cf28ed85SJohn Marino                  * so the application will eventually get an error condition.
925cf28ed85SJohn Marino                  * Set the FTS_DONTCHDIR flag so that when we logically change
926cf28ed85SJohn Marino                  * directories back to the parent we don't do a chdir.
927cf28ed85SJohn Marino                  *
928cf28ed85SJohn Marino                  * If haven't read do so.  If the read fails, fts_build sets
929cf28ed85SJohn Marino                  * FTS_STOP or the fts_info field of the node.
930cf28ed85SJohn Marino                  */
931cf28ed85SJohn Marino                 if (sp->fts_child != NULL) {
932cf28ed85SJohn Marino                         if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
933cf28ed85SJohn Marino                                 p->fts_errno = errno;
934cf28ed85SJohn Marino                                 p->fts_flags |= FTS_DONTCHDIR;
935cf28ed85SJohn Marino                                 for (p = sp->fts_child; p != NULL;
936cf28ed85SJohn Marino                                      p = p->fts_link)
937cf28ed85SJohn Marino                                         p->fts_accpath =
938cf28ed85SJohn Marino                                             p->fts_parent->fts_accpath;
939cf28ed85SJohn Marino                         }
940cf28ed85SJohn Marino                 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
941cf28ed85SJohn Marino                         if (ISSET(FTS_STOP))
942cf28ed85SJohn Marino                                 return (NULL);
943cf28ed85SJohn Marino                         /* If fts_build's call to fts_safe_changedir failed
944cf28ed85SJohn Marino                            because it was not able to fchdir into a
945cf28ed85SJohn Marino                            subdirectory, tell the caller.  */
946cf28ed85SJohn Marino                         if (p->fts_errno && p->fts_info != FTS_DNR)
947cf28ed85SJohn Marino                                 p->fts_info = FTS_ERR;
948cf28ed85SJohn Marino                         LEAVE_DIR (sp, p, "2");
949cf28ed85SJohn Marino                         return (p);
950cf28ed85SJohn Marino                 }
951cf28ed85SJohn Marino                 p = sp->fts_child;
952cf28ed85SJohn Marino                 sp->fts_child = NULL;
953cf28ed85SJohn Marino                 goto name;
954cf28ed85SJohn Marino         }
955cf28ed85SJohn Marino 
956cf28ed85SJohn Marino         /* Move to the next node on this level. */
957cf28ed85SJohn Marino next:   tmp = p;
958cf28ed85SJohn Marino 
959cf28ed85SJohn Marino         /* If we have so many directory entries that we're reading them
960cf28ed85SJohn Marino            in batches, and we've reached the end of the current batch,
961cf28ed85SJohn Marino            read in a new batch.  */
962cf28ed85SJohn Marino         if (p->fts_link == NULL && p->fts_parent->fts_dirp)
963cf28ed85SJohn Marino           {
964cf28ed85SJohn Marino             p = tmp->fts_parent;
965cf28ed85SJohn Marino             sp->fts_cur = p;
966cf28ed85SJohn Marino             sp->fts_path[p->fts_pathlen] = '\0';
967cf28ed85SJohn Marino 
968cf28ed85SJohn Marino             if ((p = fts_build (sp, BREAD)) == NULL)
969cf28ed85SJohn Marino               {
970cf28ed85SJohn Marino                 if (ISSET(FTS_STOP))
971cf28ed85SJohn Marino                   return NULL;
972cf28ed85SJohn Marino                 goto cd_dot_dot;
973cf28ed85SJohn Marino               }
974cf28ed85SJohn Marino 
975cf28ed85SJohn Marino             free(tmp);
976cf28ed85SJohn Marino             goto name;
977cf28ed85SJohn Marino           }
978cf28ed85SJohn Marino 
979cf28ed85SJohn Marino         if ((p = p->fts_link) != NULL) {
980cf28ed85SJohn Marino                 sp->fts_cur = p;
981cf28ed85SJohn Marino                 free(tmp);
982cf28ed85SJohn Marino 
983cf28ed85SJohn Marino                 /*
984cf28ed85SJohn Marino                  * If reached the top, return to the original directory (or
985cf28ed85SJohn Marino                  * the root of the tree), and load the file names for the next
986cf28ed85SJohn Marino                  * root.
987cf28ed85SJohn Marino                  */
988cf28ed85SJohn Marino                 if (p->fts_level == FTS_ROOTLEVEL) {
989cf28ed85SJohn Marino                         if (restore_initial_cwd(sp)) {
990cf28ed85SJohn Marino                                 SET(FTS_STOP);
991cf28ed85SJohn Marino                                 return (NULL);
992cf28ed85SJohn Marino                         }
993cf28ed85SJohn Marino                         free_dir(sp);
994cf28ed85SJohn Marino                         fts_load(sp, p);
995cf28ed85SJohn Marino                         setup_dir(sp);
996cf28ed85SJohn Marino                         goto check_for_dir;
997cf28ed85SJohn Marino                 }
998cf28ed85SJohn Marino 
999cf28ed85SJohn Marino                 /*
1000cf28ed85SJohn Marino                  * User may have called fts_set on the node.  If skipped,
1001cf28ed85SJohn Marino                  * ignore.  If followed, get a file descriptor so we can
1002cf28ed85SJohn Marino                  * get back if necessary.
1003cf28ed85SJohn Marino                  */
1004cf28ed85SJohn Marino                 if (p->fts_instr == FTS_SKIP)
1005cf28ed85SJohn Marino                         goto next;
1006cf28ed85SJohn Marino                 if (p->fts_instr == FTS_FOLLOW) {
1007cf28ed85SJohn Marino                         p->fts_info = fts_stat(sp, p, true);
1008cf28ed85SJohn Marino                         if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
1009cf28ed85SJohn Marino                                 if ((p->fts_symfd = diropen (sp, ".")) < 0) {
1010cf28ed85SJohn Marino                                         p->fts_errno = errno;
1011cf28ed85SJohn Marino                                         p->fts_info = FTS_ERR;
1012cf28ed85SJohn Marino                                 } else
1013cf28ed85SJohn Marino                                         p->fts_flags |= FTS_SYMFOLLOW;
1014cf28ed85SJohn Marino                         }
1015cf28ed85SJohn Marino                         p->fts_instr = FTS_NOINSTR;
1016cf28ed85SJohn Marino                 }
1017cf28ed85SJohn Marino 
1018cf28ed85SJohn Marino name:           t = sp->fts_path + NAPPEND(p->fts_parent);
1019cf28ed85SJohn Marino                 *t++ = '/';
1020cf28ed85SJohn Marino                 memmove(t, p->fts_name, p->fts_namelen + 1);
1021cf28ed85SJohn Marino check_for_dir:
1022cf28ed85SJohn Marino                 sp->fts_cur = p;
1023cf28ed85SJohn Marino                 if (p->fts_info == FTS_NSOK)
1024cf28ed85SJohn Marino                   {
1025cf28ed85SJohn Marino                     if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
1026cf28ed85SJohn Marino                       {
1027cf28ed85SJohn Marino                         FTSENT *parent = p->fts_parent;
1028*09d4459fSDaniel Fojt                         if (parent->fts_n_dirs_remaining == 0
1029cf28ed85SJohn Marino                             && ISSET(FTS_NOSTAT)
1030cf28ed85SJohn Marino                             && ISSET(FTS_PHYSICAL)
1031*09d4459fSDaniel Fojt                             && (leaf_optimization (parent, sp->fts_cwd_fd)
1032*09d4459fSDaniel Fojt                                 == NOSTAT_LEAF_OPTIMIZATION))
1033cf28ed85SJohn Marino                           {
1034cf28ed85SJohn Marino                             /* nothing more needed */
1035cf28ed85SJohn Marino                           }
1036cf28ed85SJohn Marino                         else
1037cf28ed85SJohn Marino                           {
1038cf28ed85SJohn Marino                             p->fts_info = fts_stat(sp, p, false);
1039cf28ed85SJohn Marino                             if (S_ISDIR(p->fts_statp->st_mode)
1040cf28ed85SJohn Marino                                 && p->fts_level != FTS_ROOTLEVEL
1041*09d4459fSDaniel Fojt                                 && 0 < parent->fts_n_dirs_remaining
1042*09d4459fSDaniel Fojt                                 && parent->fts_n_dirs_remaining != (nlink_t) -1)
1043cf28ed85SJohn Marino                                   parent->fts_n_dirs_remaining--;
1044cf28ed85SJohn Marino                           }
1045cf28ed85SJohn Marino                       }
1046cf28ed85SJohn Marino                     else
1047cf28ed85SJohn Marino                       fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
1048cf28ed85SJohn Marino                   }
1049cf28ed85SJohn Marino 
1050cf28ed85SJohn Marino                 if (p->fts_info == FTS_D)
1051cf28ed85SJohn Marino                   {
1052cf28ed85SJohn Marino                     /* Now that P->fts_statp is guaranteed to be valid,
1053cf28ed85SJohn Marino                        if this is a command-line directory, record its
1054cf28ed85SJohn Marino                        device number, to be used for FTS_XDEV.  */
1055cf28ed85SJohn Marino                     if (p->fts_level == FTS_ROOTLEVEL)
1056cf28ed85SJohn Marino                       sp->fts_dev = p->fts_statp->st_dev;
1057cf28ed85SJohn Marino                     Dprintf (("  entering: %s\n", p->fts_path));
1058cf28ed85SJohn Marino                     if (! enter_dir (sp, p))
1059cf28ed85SJohn Marino                       {
1060cf28ed85SJohn Marino                         __set_errno (ENOMEM);
1061cf28ed85SJohn Marino                         return NULL;
1062cf28ed85SJohn Marino                       }
1063cf28ed85SJohn Marino                   }
1064cf28ed85SJohn Marino                 return p;
1065cf28ed85SJohn Marino         }
1066cf28ed85SJohn Marino cd_dot_dot:
1067cf28ed85SJohn Marino 
1068cf28ed85SJohn Marino         /* Move up to the parent node. */
1069cf28ed85SJohn Marino         p = tmp->fts_parent;
1070cf28ed85SJohn Marino         sp->fts_cur = p;
1071cf28ed85SJohn Marino         free(tmp);
1072cf28ed85SJohn Marino 
1073cf28ed85SJohn Marino         if (p->fts_level == FTS_ROOTPARENTLEVEL) {
1074cf28ed85SJohn Marino                 /*
1075cf28ed85SJohn Marino                  * Done; free everything up and set errno to 0 so the user
1076cf28ed85SJohn Marino                  * can distinguish between error and EOF.
1077cf28ed85SJohn Marino                  */
1078cf28ed85SJohn Marino                 free(p);
1079cf28ed85SJohn Marino                 __set_errno (0);
1080cf28ed85SJohn Marino                 return (sp->fts_cur = NULL);
1081cf28ed85SJohn Marino         }
1082cf28ed85SJohn Marino 
1083cf28ed85SJohn Marino         fts_assert (p->fts_info != FTS_NSOK);
1084cf28ed85SJohn Marino 
1085cf28ed85SJohn Marino         /* NUL terminate the file name.  */
1086cf28ed85SJohn Marino         sp->fts_path[p->fts_pathlen] = '\0';
1087cf28ed85SJohn Marino 
1088cf28ed85SJohn Marino         /*
1089cf28ed85SJohn Marino          * Return to the parent directory.  If at a root node, restore
1090cf28ed85SJohn Marino          * the initial working directory.  If we came through a symlink,
1091cf28ed85SJohn Marino          * go back through the file descriptor.  Otherwise, move up
1092cf28ed85SJohn Marino          * one level, via "..".
1093cf28ed85SJohn Marino          */
1094cf28ed85SJohn Marino         if (p->fts_level == FTS_ROOTLEVEL) {
1095cf28ed85SJohn Marino                 if (restore_initial_cwd(sp)) {
1096cf28ed85SJohn Marino                         p->fts_errno = errno;
1097cf28ed85SJohn Marino                         SET(FTS_STOP);
1098cf28ed85SJohn Marino                 }
1099cf28ed85SJohn Marino         } else if (p->fts_flags & FTS_SYMFOLLOW) {
1100cf28ed85SJohn Marino                 if (FCHDIR(sp, p->fts_symfd)) {
1101cf28ed85SJohn Marino                         p->fts_errno = errno;
1102cf28ed85SJohn Marino                         SET(FTS_STOP);
1103cf28ed85SJohn Marino                 }
1104cf28ed85SJohn Marino                 (void)close(p->fts_symfd);
1105cf28ed85SJohn Marino         } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
1106cf28ed85SJohn Marino                    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
1107cf28ed85SJohn Marino                 p->fts_errno = errno;
1108cf28ed85SJohn Marino                 SET(FTS_STOP);
1109cf28ed85SJohn Marino         }
1110dc7c36e4SJohn Marino 
1111dc7c36e4SJohn Marino         /* If the directory causes a cycle, preserve the FTS_DC flag and keep
1112dc7c36e4SJohn Marino            the corresponding dev/ino pair in the hash table.  It is going to be
1113dc7c36e4SJohn Marino            removed when leaving the original directory.  */
1114dc7c36e4SJohn Marino         if (p->fts_info != FTS_DC) {
1115cf28ed85SJohn Marino                 p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
1116cf28ed85SJohn Marino                 if (p->fts_errno == 0)
1117cf28ed85SJohn Marino                         LEAVE_DIR (sp, p, "3");
1118dc7c36e4SJohn Marino         }
1119cf28ed85SJohn Marino         return ISSET(FTS_STOP) ? NULL : p;
1120cf28ed85SJohn Marino }
1121cf28ed85SJohn Marino 
1122cf28ed85SJohn Marino /*
1123cf28ed85SJohn Marino  * Fts_set takes the stream as an argument although it's not used in this
1124cf28ed85SJohn Marino  * implementation; it would be necessary if anyone wanted to add global
1125cf28ed85SJohn Marino  * semantics to fts using fts_set.  An error return is allowed for similar
1126cf28ed85SJohn Marino  * reasons.
1127cf28ed85SJohn Marino  */
1128cf28ed85SJohn Marino /* ARGSUSED */
1129cf28ed85SJohn Marino int
fts_set(FTS * sp _GL_UNUSED,FTSENT * p,int instr)1130cf28ed85SJohn Marino fts_set(FTS *sp _GL_UNUSED, FTSENT *p, int instr)
1131cf28ed85SJohn Marino {
1132cf28ed85SJohn Marino         if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
1133cf28ed85SJohn Marino             instr != FTS_NOINSTR && instr != FTS_SKIP) {
1134cf28ed85SJohn Marino                 __set_errno (EINVAL);
1135cf28ed85SJohn Marino                 return (1);
1136cf28ed85SJohn Marino         }
1137cf28ed85SJohn Marino         p->fts_instr = instr;
1138cf28ed85SJohn Marino         return (0);
1139cf28ed85SJohn Marino }
1140cf28ed85SJohn Marino 
1141cf28ed85SJohn Marino FTSENT *
fts_children(register FTS * sp,int instr)1142cf28ed85SJohn Marino fts_children (register FTS *sp, int instr)
1143cf28ed85SJohn Marino {
1144cf28ed85SJohn Marino         register FTSENT *p;
1145cf28ed85SJohn Marino         int fd;
1146cf28ed85SJohn Marino 
1147cf28ed85SJohn Marino         if (instr != 0 && instr != FTS_NAMEONLY) {
1148cf28ed85SJohn Marino                 __set_errno (EINVAL);
1149cf28ed85SJohn Marino                 return (NULL);
1150cf28ed85SJohn Marino         }
1151cf28ed85SJohn Marino 
1152cf28ed85SJohn Marino         /* Set current node pointer. */
1153cf28ed85SJohn Marino         p = sp->fts_cur;
1154cf28ed85SJohn Marino 
1155cf28ed85SJohn Marino         /*
1156cf28ed85SJohn Marino          * Errno set to 0 so user can distinguish empty directory from
1157cf28ed85SJohn Marino          * an error.
1158cf28ed85SJohn Marino          */
1159cf28ed85SJohn Marino         __set_errno (0);
1160cf28ed85SJohn Marino 
1161cf28ed85SJohn Marino         /* Fatal errors stop here. */
1162cf28ed85SJohn Marino         if (ISSET(FTS_STOP))
1163cf28ed85SJohn Marino                 return (NULL);
1164cf28ed85SJohn Marino 
1165cf28ed85SJohn Marino         /* Return logical hierarchy of user's arguments. */
1166cf28ed85SJohn Marino         if (p->fts_info == FTS_INIT)
1167cf28ed85SJohn Marino                 return (p->fts_link);
1168cf28ed85SJohn Marino 
1169cf28ed85SJohn Marino         /*
1170cf28ed85SJohn Marino          * If not a directory being visited in pre-order, stop here.  Could
1171cf28ed85SJohn Marino          * allow FTS_DNR, assuming the user has fixed the problem, but the
1172cf28ed85SJohn Marino          * same effect is available with FTS_AGAIN.
1173cf28ed85SJohn Marino          */
1174cf28ed85SJohn Marino         if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
1175cf28ed85SJohn Marino                 return (NULL);
1176cf28ed85SJohn Marino 
1177cf28ed85SJohn Marino         /* Free up any previous child list. */
1178cf28ed85SJohn Marino         if (sp->fts_child != NULL)
1179cf28ed85SJohn Marino                 fts_lfree(sp->fts_child);
1180cf28ed85SJohn Marino 
1181cf28ed85SJohn Marino         if (instr == FTS_NAMEONLY) {
1182cf28ed85SJohn Marino                 SET(FTS_NAMEONLY);
1183cf28ed85SJohn Marino                 instr = BNAMES;
1184cf28ed85SJohn Marino         } else
1185cf28ed85SJohn Marino                 instr = BCHILD;
1186cf28ed85SJohn Marino 
1187cf28ed85SJohn Marino         /*
1188cf28ed85SJohn Marino          * If using chdir on a relative file name and called BEFORE fts_read
1189cf28ed85SJohn Marino          * does its chdir to the root of a traversal, we can lose -- we need to
1190cf28ed85SJohn Marino          * chdir into the subdirectory, and we don't know where the current
1191cf28ed85SJohn Marino          * directory is, so we can't get back so that the upcoming chdir by
1192cf28ed85SJohn Marino          * fts_read will work.
1193cf28ed85SJohn Marino          */
1194cf28ed85SJohn Marino         if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
1195cf28ed85SJohn Marino             ISSET(FTS_NOCHDIR))
1196cf28ed85SJohn Marino                 return (sp->fts_child = fts_build(sp, instr));
1197cf28ed85SJohn Marino 
1198cf28ed85SJohn Marino         if ((fd = diropen (sp, ".")) < 0)
1199cf28ed85SJohn Marino                 return (sp->fts_child = NULL);
1200cf28ed85SJohn Marino         sp->fts_child = fts_build(sp, instr);
1201cf28ed85SJohn Marino         if (ISSET(FTS_CWDFD))
1202cf28ed85SJohn Marino           {
1203cf28ed85SJohn Marino             cwd_advance_fd (sp, fd, true);
1204cf28ed85SJohn Marino           }
1205cf28ed85SJohn Marino         else
1206cf28ed85SJohn Marino           {
1207cf28ed85SJohn Marino             if (fchdir(fd))
1208cf28ed85SJohn Marino               {
1209cf28ed85SJohn Marino                 int saved_errno = errno;
1210cf28ed85SJohn Marino                 close (fd);
1211cf28ed85SJohn Marino                 __set_errno (saved_errno);
1212cf28ed85SJohn Marino                 return NULL;
1213cf28ed85SJohn Marino               }
1214cf28ed85SJohn Marino             close (fd);
1215cf28ed85SJohn Marino           }
1216cf28ed85SJohn Marino         return (sp->fts_child);
1217cf28ed85SJohn Marino }
1218cf28ed85SJohn Marino 
1219cf28ed85SJohn Marino /* A comparison function to sort on increasing inode number.
1220cf28ed85SJohn Marino    For some file system types, sorting either way makes a huge
1221cf28ed85SJohn Marino    performance difference for a directory with very many entries,
1222cf28ed85SJohn Marino    but sorting on increasing values is slightly better than sorting
1223cf28ed85SJohn Marino    on decreasing values.  The difference is in the 5% range.  */
1224cf28ed85SJohn Marino static int
fts_compare_ino(struct _ftsent const ** a,struct _ftsent const ** b)1225cf28ed85SJohn Marino fts_compare_ino (struct _ftsent const **a, struct _ftsent const **b)
1226cf28ed85SJohn Marino {
1227cf28ed85SJohn Marino   return (a[0]->fts_statp->st_ino < b[0]->fts_statp->st_ino ? -1
1228cf28ed85SJohn Marino           : b[0]->fts_statp->st_ino < a[0]->fts_statp->st_ino ? 1 : 0);
1229cf28ed85SJohn Marino }
1230cf28ed85SJohn Marino 
1231cf28ed85SJohn Marino /* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
1232cf28ed85SJohn Marino    S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */
1233cf28ed85SJohn Marino static void
set_stat_type(struct stat * st,unsigned int dtype)1234cf28ed85SJohn Marino set_stat_type (struct stat *st, unsigned int dtype)
1235cf28ed85SJohn Marino {
1236cf28ed85SJohn Marino   mode_t type;
1237cf28ed85SJohn Marino   switch (dtype)
1238cf28ed85SJohn Marino     {
1239cf28ed85SJohn Marino     case DT_BLK:
1240cf28ed85SJohn Marino       type = S_IFBLK;
1241cf28ed85SJohn Marino       break;
1242cf28ed85SJohn Marino     case DT_CHR:
1243cf28ed85SJohn Marino       type = S_IFCHR;
1244cf28ed85SJohn Marino       break;
1245cf28ed85SJohn Marino     case DT_DIR:
1246cf28ed85SJohn Marino       type = S_IFDIR;
1247cf28ed85SJohn Marino       break;
1248cf28ed85SJohn Marino     case DT_FIFO:
1249cf28ed85SJohn Marino       type = S_IFIFO;
1250cf28ed85SJohn Marino       break;
1251cf28ed85SJohn Marino     case DT_LNK:
1252cf28ed85SJohn Marino       type = S_IFLNK;
1253cf28ed85SJohn Marino       break;
1254cf28ed85SJohn Marino     case DT_REG:
1255cf28ed85SJohn Marino       type = S_IFREG;
1256cf28ed85SJohn Marino       break;
1257cf28ed85SJohn Marino     case DT_SOCK:
1258cf28ed85SJohn Marino       type = S_IFSOCK;
1259cf28ed85SJohn Marino       break;
1260cf28ed85SJohn Marino     default:
1261cf28ed85SJohn Marino       type = 0;
1262cf28ed85SJohn Marino     }
1263cf28ed85SJohn Marino   st->st_mode = type;
1264cf28ed85SJohn Marino }
1265cf28ed85SJohn Marino 
1266cf28ed85SJohn Marino #define closedir_and_clear(dirp)                \
1267cf28ed85SJohn Marino   do                                            \
1268cf28ed85SJohn Marino     {                                           \
1269cf28ed85SJohn Marino       closedir (dirp);                          \
1270cf28ed85SJohn Marino       dirp = NULL;                              \
1271cf28ed85SJohn Marino     }                                           \
1272cf28ed85SJohn Marino   while (0)
1273cf28ed85SJohn Marino 
1274cf28ed85SJohn Marino #define fts_opendir(file, Pdir_fd)                              \
1275cf28ed85SJohn Marino         opendirat((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \
1276cf28ed85SJohn Marino                    ? sp->fts_cwd_fd : AT_FDCWD),                \
1277cf28ed85SJohn Marino                   file,                                         \
1278cf28ed85SJohn Marino                   (((ISSET(FTS_PHYSICAL)                        \
1279cf28ed85SJohn Marino                      && ! (ISSET(FTS_COMFOLLOW)                 \
1280cf28ed85SJohn Marino                            && cur->fts_level == FTS_ROOTLEVEL)) \
1281*09d4459fSDaniel Fojt                     ? O_NOFOLLOW : 0)),                         \
1282cf28ed85SJohn Marino                   Pdir_fd)
1283cf28ed85SJohn Marino 
1284cf28ed85SJohn Marino /*
1285cf28ed85SJohn Marino  * This is the tricky part -- do not casually change *anything* in here.  The
1286cf28ed85SJohn Marino  * idea is to build the linked list of entries that are used by fts_children
1287cf28ed85SJohn Marino  * and fts_read.  There are lots of special cases.
1288cf28ed85SJohn Marino  *
1289cf28ed85SJohn Marino  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
1290cf28ed85SJohn Marino  * set and it's a physical walk (so that symbolic links can't be directories),
1291cf28ed85SJohn Marino  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
1292cf28ed85SJohn Marino  * of the file is in the directory entry.  Otherwise, we assume that the number
1293cf28ed85SJohn Marino  * of subdirectories in a node is equal to the number of links to the parent.
1294cf28ed85SJohn Marino  * The former skips all stat calls.  The latter skips stat calls in any leaf
1295cf28ed85SJohn Marino  * directories and for any files after the subdirectories in the directory have
1296cf28ed85SJohn Marino  * been found, cutting the stat calls by about 2/3.
1297cf28ed85SJohn Marino  */
1298cf28ed85SJohn Marino static FTSENT *
1299cf28ed85SJohn Marino internal_function
fts_build(register FTS * sp,int type)1300cf28ed85SJohn Marino fts_build (register FTS *sp, int type)
1301cf28ed85SJohn Marino {
1302cf28ed85SJohn Marino         register FTSENT *p, *head;
1303cf28ed85SJohn Marino         register size_t nitems;
1304cf28ed85SJohn Marino         FTSENT *tail;
1305cf28ed85SJohn Marino         void *oldaddr;
1306cf28ed85SJohn Marino         int saved_errno;
1307cf28ed85SJohn Marino         bool descend;
1308cf28ed85SJohn Marino         bool doadjust;
1309cf28ed85SJohn Marino         ptrdiff_t level;
1310cf28ed85SJohn Marino         size_t len, maxlen, new_len;
1311cf28ed85SJohn Marino         char *cp;
1312cf28ed85SJohn Marino         int dir_fd;
1313cf28ed85SJohn Marino         FTSENT *cur = sp->fts_cur;
1314cf28ed85SJohn Marino         bool continue_readdir = !!cur->fts_dirp;
1315*09d4459fSDaniel Fojt         bool sort_by_inode = false;
1316dc7c36e4SJohn Marino         size_t max_entries;
1317cf28ed85SJohn Marino 
1318cf28ed85SJohn Marino         /* When cur->fts_dirp is non-NULL, that means we should
1319cf28ed85SJohn Marino            continue calling readdir on that existing DIR* pointer
1320cf28ed85SJohn Marino            rather than opening a new one.  */
1321cf28ed85SJohn Marino         if (continue_readdir)
1322cf28ed85SJohn Marino           {
1323cf28ed85SJohn Marino             DIR *dp = cur->fts_dirp;
1324cf28ed85SJohn Marino             dir_fd = dirfd (dp);
1325cf28ed85SJohn Marino             if (dir_fd < 0)
1326cf28ed85SJohn Marino               {
1327cf28ed85SJohn Marino                 closedir_and_clear (cur->fts_dirp);
1328cf28ed85SJohn Marino                 if (type == BREAD)
1329cf28ed85SJohn Marino                   {
1330cf28ed85SJohn Marino                     cur->fts_info = FTS_DNR;
1331cf28ed85SJohn Marino                     cur->fts_errno = errno;
1332cf28ed85SJohn Marino                   }
1333cf28ed85SJohn Marino                 return NULL;
1334cf28ed85SJohn Marino               }
1335cf28ed85SJohn Marino           }
1336cf28ed85SJohn Marino         else
1337cf28ed85SJohn Marino           {
1338cf28ed85SJohn Marino             /* Open the directory for reading.  If this fails, we're done.
1339cf28ed85SJohn Marino                If being called from fts_read, set the fts_info field. */
1340cf28ed85SJohn Marino             if ((cur->fts_dirp = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)
1341cf28ed85SJohn Marino               {
1342cf28ed85SJohn Marino                 if (type == BREAD)
1343cf28ed85SJohn Marino                   {
1344cf28ed85SJohn Marino                     cur->fts_info = FTS_DNR;
1345cf28ed85SJohn Marino                     cur->fts_errno = errno;
1346cf28ed85SJohn Marino                   }
1347cf28ed85SJohn Marino                 return NULL;
1348cf28ed85SJohn Marino               }
1349cf28ed85SJohn Marino             /* Rather than calling fts_stat for each and every entry encountered
1350cf28ed85SJohn Marino                in the readdir loop (below), stat each directory only right after
1351cf28ed85SJohn Marino                opening it.  */
1352cf28ed85SJohn Marino             if (cur->fts_info == FTS_NSOK)
1353cf28ed85SJohn Marino               cur->fts_info = fts_stat(sp, cur, false);
1354cf28ed85SJohn Marino             else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK)
1355cf28ed85SJohn Marino               {
1356cf28ed85SJohn Marino                 /* Now read the stat info again after opening a directory to
1357cf28ed85SJohn Marino                    reveal eventual changes caused by a submount triggered by
1358cf28ed85SJohn Marino                    the traversal.  But do it only for utilities which use
1359cf28ed85SJohn Marino                    FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du
1360cf28ed85SJohn Marino                    benefit/suffer from this feature for now.  */
1361cf28ed85SJohn Marino                 LEAVE_DIR (sp, cur, "4");
1362cf28ed85SJohn Marino                 fts_stat (sp, cur, false);
1363cf28ed85SJohn Marino                 if (! enter_dir (sp, cur))
1364cf28ed85SJohn Marino                   {
1365cf28ed85SJohn Marino                     __set_errno (ENOMEM);
1366cf28ed85SJohn Marino                     return NULL;
1367cf28ed85SJohn Marino                   }
1368cf28ed85SJohn Marino               }
1369cf28ed85SJohn Marino           }
1370cf28ed85SJohn Marino 
1371cf28ed85SJohn Marino         /* Maximum number of readdir entries to read at one time.  This
1372cf28ed85SJohn Marino            limitation is to avoid reading millions of entries into memory
1373cf28ed85SJohn Marino            at once.  When an fts_compar function is specified, we have no
1374cf28ed85SJohn Marino            choice: we must read all entries into memory before calling that
1375cf28ed85SJohn Marino            function.  But when no such function is specified, we can read
1376cf28ed85SJohn Marino            entries in batches that are large enough to help us with inode-
1377cf28ed85SJohn Marino            sorting, yet not so large that we risk exhausting memory.  */
1378dc7c36e4SJohn Marino         max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;
1379cf28ed85SJohn Marino 
1380cf28ed85SJohn Marino         /*
1381cf28ed85SJohn Marino          * If we're going to need to stat anything or we want to descend
1382cf28ed85SJohn Marino          * and stay in the directory, chdir.  If this fails we keep going,
1383cf28ed85SJohn Marino          * but set a flag so we don't chdir after the post-order visit.
1384cf28ed85SJohn Marino          * We won't be able to stat anything, but we can still return the
1385cf28ed85SJohn Marino          * names themselves.  Note, that since fts_read won't be able to
1386cf28ed85SJohn Marino          * chdir into the directory, it will have to return different file
1387cf28ed85SJohn Marino          * names than before, i.e. "a/b" instead of "b".  Since the node
1388cf28ed85SJohn Marino          * has already been visited in pre-order, have to wait until the
1389cf28ed85SJohn Marino          * post-order visit to return the error.  There is a special case
1390cf28ed85SJohn Marino          * here, if there was nothing to stat then it's not an error to
1391cf28ed85SJohn Marino          * not be able to stat.  This is all fairly nasty.  If a program
1392cf28ed85SJohn Marino          * needed sorted entries or stat information, they had better be
1393cf28ed85SJohn Marino          * checking FTS_NS on the returned nodes.
1394cf28ed85SJohn Marino          */
1395cf28ed85SJohn Marino         if (continue_readdir)
1396cf28ed85SJohn Marino           {
1397cf28ed85SJohn Marino             /* When resuming a short readdir run, we already have
1398cf28ed85SJohn Marino                the required dirp and dir_fd.  */
1399cf28ed85SJohn Marino             descend = true;
1400cf28ed85SJohn Marino           }
1401*09d4459fSDaniel Fojt         else
1402cf28ed85SJohn Marino           {
1403*09d4459fSDaniel Fojt             /* Try to descend unless it is a names-only fts_children,
1404*09d4459fSDaniel Fojt                or the directory is known to lack subdirectories.  */
1405*09d4459fSDaniel Fojt             descend = (type != BNAMES
1406*09d4459fSDaniel Fojt                        && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)
1407*09d4459fSDaniel Fojt                              && ! ISSET (FTS_SEEDOT)
1408*09d4459fSDaniel Fojt                              && cur->fts_statp->st_nlink == MIN_DIR_NLINK
1409*09d4459fSDaniel Fojt                              && (leaf_optimization (cur, dir_fd)
1410*09d4459fSDaniel Fojt                                  != NO_LEAF_OPTIMIZATION)));
1411*09d4459fSDaniel Fojt             if (descend || type == BREAD)
1412*09d4459fSDaniel Fojt               {
1413*09d4459fSDaniel Fojt                 if (ISSET(FTS_CWDFD))
1414*09d4459fSDaniel Fojt                   dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
1415cf28ed85SJohn Marino                 if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
1416*09d4459fSDaniel Fojt                         if (descend && type == BREAD)
1417cf28ed85SJohn Marino                                 cur->fts_errno = errno;
1418cf28ed85SJohn Marino                         cur->fts_flags |= FTS_DONTCHDIR;
1419cf28ed85SJohn Marino                         descend = false;
1420cf28ed85SJohn Marino                         closedir_and_clear(cur->fts_dirp);
1421cf28ed85SJohn Marino                         if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
1422cf28ed85SJohn Marino                                 close (dir_fd);
1423cf28ed85SJohn Marino                         cur->fts_dirp = NULL;
1424cf28ed85SJohn Marino                 } else
1425cf28ed85SJohn Marino                         descend = true;
1426*09d4459fSDaniel Fojt               }
1427*09d4459fSDaniel Fojt           }
1428cf28ed85SJohn Marino 
1429cf28ed85SJohn Marino         /*
1430cf28ed85SJohn Marino          * Figure out the max file name length that can be stored in the
1431cf28ed85SJohn Marino          * current buffer -- the inner loop allocates more space as necessary.
1432cf28ed85SJohn Marino          * We really wouldn't have to do the maxlen calculations here, we
1433cf28ed85SJohn Marino          * could do them in fts_read before returning the name, but it's a
1434cf28ed85SJohn Marino          * lot easier here since the length is part of the dirent structure.
1435cf28ed85SJohn Marino          *
1436cf28ed85SJohn Marino          * If not changing directories set a pointer so that can just append
1437cf28ed85SJohn Marino          * each new component into the file name.
1438cf28ed85SJohn Marino          */
1439cf28ed85SJohn Marino         len = NAPPEND(cur);
1440cf28ed85SJohn Marino         if (ISSET(FTS_NOCHDIR)) {
1441cf28ed85SJohn Marino                 cp = sp->fts_path + len;
1442cf28ed85SJohn Marino                 *cp++ = '/';
1443cf28ed85SJohn Marino         } else {
1444cf28ed85SJohn Marino                 /* GCC, you're too verbose. */
1445cf28ed85SJohn Marino                 cp = NULL;
1446cf28ed85SJohn Marino         }
1447cf28ed85SJohn Marino         len++;
1448cf28ed85SJohn Marino         maxlen = sp->fts_pathlen - len;
1449cf28ed85SJohn Marino 
1450cf28ed85SJohn Marino         level = cur->fts_level + 1;
1451cf28ed85SJohn Marino 
1452cf28ed85SJohn Marino         /* Read the directory, attaching each entry to the "link" pointer. */
1453cf28ed85SJohn Marino         doadjust = false;
1454cf28ed85SJohn Marino         head = NULL;
1455cf28ed85SJohn Marino         tail = NULL;
1456cf28ed85SJohn Marino         nitems = 0;
1457cf28ed85SJohn Marino         while (cur->fts_dirp) {
1458680a9cb8SJohn Marino                 size_t d_namelen;
1459*09d4459fSDaniel Fojt                 __set_errno (0);
1460cf28ed85SJohn Marino                 struct dirent *dp = readdir(cur->fts_dirp);
1461*09d4459fSDaniel Fojt                 if (dp == NULL) {
1462*09d4459fSDaniel Fojt                         if (errno) {
1463*09d4459fSDaniel Fojt                                 cur->fts_errno = errno;
1464*09d4459fSDaniel Fojt                                 /* If we've not read any items yet, treat
1465*09d4459fSDaniel Fojt                                    the error as if we can't access the dir.  */
1466*09d4459fSDaniel Fojt                                 cur->fts_info = (continue_readdir || nitems)
1467*09d4459fSDaniel Fojt                                                 ? FTS_ERR : FTS_DNR;
1468*09d4459fSDaniel Fojt                         }
1469cf28ed85SJohn Marino                         break;
1470*09d4459fSDaniel Fojt                 }
1471cf28ed85SJohn Marino                 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
1472cf28ed85SJohn Marino                         continue;
1473cf28ed85SJohn Marino 
1474680a9cb8SJohn Marino                 d_namelen = _D_EXACT_NAMLEN (dp);
1475680a9cb8SJohn Marino                 p = fts_alloc (sp, dp->d_name, d_namelen);
1476680a9cb8SJohn Marino                 if (!p)
1477cf28ed85SJohn Marino                         goto mem1;
1478680a9cb8SJohn Marino                 if (d_namelen >= maxlen) {
1479cf28ed85SJohn Marino                         /* include space for NUL */
1480cf28ed85SJohn Marino                         oldaddr = sp->fts_path;
1481680a9cb8SJohn Marino                         if (! fts_palloc(sp, d_namelen + len + 1)) {
1482cf28ed85SJohn Marino                                 /*
1483cf28ed85SJohn Marino                                  * No more memory.  Save
1484cf28ed85SJohn Marino                                  * errno, free up the current structure and the
1485cf28ed85SJohn Marino                                  * structures already allocated.
1486cf28ed85SJohn Marino                                  */
1487cf28ed85SJohn Marino mem1:                           saved_errno = errno;
1488cf28ed85SJohn Marino                                 free(p);
1489cf28ed85SJohn Marino                                 fts_lfree(head);
1490cf28ed85SJohn Marino                                 closedir_and_clear(cur->fts_dirp);
1491cf28ed85SJohn Marino                                 cur->fts_info = FTS_ERR;
1492cf28ed85SJohn Marino                                 SET(FTS_STOP);
1493cf28ed85SJohn Marino                                 __set_errno (saved_errno);
1494cf28ed85SJohn Marino                                 return (NULL);
1495cf28ed85SJohn Marino                         }
1496cf28ed85SJohn Marino                         /* Did realloc() change the pointer? */
1497cf28ed85SJohn Marino                         if (oldaddr != sp->fts_path) {
1498cf28ed85SJohn Marino                                 doadjust = true;
1499cf28ed85SJohn Marino                                 if (ISSET(FTS_NOCHDIR))
1500cf28ed85SJohn Marino                                         cp = sp->fts_path + len;
1501cf28ed85SJohn Marino                         }
1502cf28ed85SJohn Marino                         maxlen = sp->fts_pathlen - len;
1503cf28ed85SJohn Marino                 }
1504cf28ed85SJohn Marino 
1505680a9cb8SJohn Marino                 new_len = len + d_namelen;
1506cf28ed85SJohn Marino                 if (new_len < len) {
1507cf28ed85SJohn Marino                         /*
1508cf28ed85SJohn Marino                          * In the unlikely event that we would end up
1509cf28ed85SJohn Marino                          * with a file name longer than SIZE_MAX, free up
1510cf28ed85SJohn Marino                          * the current structure and the structures already
1511cf28ed85SJohn Marino                          * allocated, then error out with ENAMETOOLONG.
1512cf28ed85SJohn Marino                          */
1513cf28ed85SJohn Marino                         free(p);
1514cf28ed85SJohn Marino                         fts_lfree(head);
1515cf28ed85SJohn Marino                         closedir_and_clear(cur->fts_dirp);
1516cf28ed85SJohn Marino                         cur->fts_info = FTS_ERR;
1517cf28ed85SJohn Marino                         SET(FTS_STOP);
1518cf28ed85SJohn Marino                         __set_errno (ENAMETOOLONG);
1519cf28ed85SJohn Marino                         return (NULL);
1520cf28ed85SJohn Marino                 }
1521cf28ed85SJohn Marino                 p->fts_level = level;
1522cf28ed85SJohn Marino                 p->fts_parent = sp->fts_cur;
1523cf28ed85SJohn Marino                 p->fts_pathlen = new_len;
1524cf28ed85SJohn Marino 
1525cf28ed85SJohn Marino                 /* Store dirent.d_ino, in case we need to sort
1526cf28ed85SJohn Marino                    entries before processing them.  */
1527cf28ed85SJohn Marino                 p->fts_statp->st_ino = D_INO (dp);
1528cf28ed85SJohn Marino 
1529cf28ed85SJohn Marino                 /* Build a file name for fts_stat to stat. */
1530cf28ed85SJohn Marino                 if (ISSET(FTS_NOCHDIR)) {
1531cf28ed85SJohn Marino                         p->fts_accpath = p->fts_path;
1532cf28ed85SJohn Marino                         memmove(cp, p->fts_name, p->fts_namelen + 1);
1533cf28ed85SJohn Marino                 } else
1534cf28ed85SJohn Marino                         p->fts_accpath = p->fts_name;
1535cf28ed85SJohn Marino 
1536cf28ed85SJohn Marino                 if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
1537cf28ed85SJohn Marino                         /* Record what fts_read will have to do with this
1538cf28ed85SJohn Marino                            entry. In many cases, it will simply fts_stat it,
1539cf28ed85SJohn Marino                            but we can take advantage of any d_type information
1540cf28ed85SJohn Marino                            to optimize away the unnecessary stat calls.  I.e.,
1541cf28ed85SJohn Marino                            if FTS_NOSTAT is in effect and we're not following
1542cf28ed85SJohn Marino                            symlinks (FTS_PHYSICAL) and d_type indicates this
1543cf28ed85SJohn Marino                            is *not* a directory, then we won't have to stat it
1544cf28ed85SJohn Marino                            at all.  If it *is* a directory, then (currently)
1545cf28ed85SJohn Marino                            we stat it regardless, in order to get device and
1546cf28ed85SJohn Marino                            inode numbers.  Some day we might optimize that
1547cf28ed85SJohn Marino                            away, too, for directories where d_ino is known to
1548cf28ed85SJohn Marino                            be valid.  */
1549cf28ed85SJohn Marino                         bool skip_stat = (ISSET(FTS_PHYSICAL)
1550cf28ed85SJohn Marino                                           && ISSET(FTS_NOSTAT)
1551cf28ed85SJohn Marino                                           && DT_IS_KNOWN(dp)
1552cf28ed85SJohn Marino                                           && ! DT_MUST_BE(dp, DT_DIR));
1553cf28ed85SJohn Marino                         p->fts_info = FTS_NSOK;
1554cf28ed85SJohn Marino                         /* Propagate dirent.d_type information back
1555cf28ed85SJohn Marino                            to caller, when possible.  */
1556cf28ed85SJohn Marino                         set_stat_type (p->fts_statp, D_TYPE (dp));
1557cf28ed85SJohn Marino                         fts_set_stat_required(p, !skip_stat);
1558cf28ed85SJohn Marino                 } else {
1559cf28ed85SJohn Marino                         p->fts_info = fts_stat(sp, p, false);
1560cf28ed85SJohn Marino                 }
1561cf28ed85SJohn Marino 
1562cf28ed85SJohn Marino                 /* We walk in directory order so "ls -f" doesn't get upset. */
1563cf28ed85SJohn Marino                 p->fts_link = NULL;
1564cf28ed85SJohn Marino                 if (head == NULL)
1565cf28ed85SJohn Marino                         head = tail = p;
1566cf28ed85SJohn Marino                 else {
1567cf28ed85SJohn Marino                         tail->fts_link = p;
1568cf28ed85SJohn Marino                         tail = p;
1569cf28ed85SJohn Marino                 }
1570*09d4459fSDaniel Fojt 
1571*09d4459fSDaniel Fojt                 /* If there are many entries, no sorting function has been
1572*09d4459fSDaniel Fojt                    specified, and this file system is of a type that may be
1573*09d4459fSDaniel Fojt                    slow with a large number of entries, arrange to sort the
1574*09d4459fSDaniel Fojt                    directory entries on increasing inode numbers.
1575*09d4459fSDaniel Fojt 
1576*09d4459fSDaniel Fojt                    The NITEMS comparison uses ==, not >, because the test
1577*09d4459fSDaniel Fojt                    needs to be tried at most once once, and NITEMS will exceed
1578*09d4459fSDaniel Fojt                    the threshold after it is incremented below.  */
1579*09d4459fSDaniel Fojt                 if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
1580*09d4459fSDaniel Fojt                     && !sp->fts_compar)
1581*09d4459fSDaniel Fojt                   sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);
1582*09d4459fSDaniel Fojt 
1583cf28ed85SJohn Marino                 ++nitems;
1584cf28ed85SJohn Marino                 if (max_entries <= nitems) {
1585cf28ed85SJohn Marino                         /* When there are too many dir entries, leave
1586cf28ed85SJohn Marino                            fts_dirp open, so that a subsequent fts_read
1587cf28ed85SJohn Marino                            can take up where we leave off.  */
1588cf28ed85SJohn Marino                         goto break_without_closedir;
1589cf28ed85SJohn Marino                 }
1590cf28ed85SJohn Marino         }
1591cf28ed85SJohn Marino 
1592cf28ed85SJohn Marino         if (cur->fts_dirp)
1593cf28ed85SJohn Marino                 closedir_and_clear(cur->fts_dirp);
1594cf28ed85SJohn Marino 
1595cf28ed85SJohn Marino  break_without_closedir:
1596cf28ed85SJohn Marino 
1597cf28ed85SJohn Marino         /*
1598cf28ed85SJohn Marino          * If realloc() changed the address of the file name, adjust the
1599cf28ed85SJohn Marino          * addresses for the rest of the tree and the dir list.
1600cf28ed85SJohn Marino          */
1601cf28ed85SJohn Marino         if (doadjust)
1602cf28ed85SJohn Marino                 fts_padjust(sp, head);
1603cf28ed85SJohn Marino 
1604cf28ed85SJohn Marino         /*
1605cf28ed85SJohn Marino          * If not changing directories, reset the file name back to original
1606cf28ed85SJohn Marino          * state.
1607cf28ed85SJohn Marino          */
1608cf28ed85SJohn Marino         if (ISSET(FTS_NOCHDIR)) {
1609cf28ed85SJohn Marino                 if (len == sp->fts_pathlen || nitems == 0)
1610cf28ed85SJohn Marino                         --cp;
1611cf28ed85SJohn Marino                 *cp = '\0';
1612cf28ed85SJohn Marino         }
1613cf28ed85SJohn Marino 
1614cf28ed85SJohn Marino         /*
1615cf28ed85SJohn Marino          * If descended after called from fts_children or after called from
1616cf28ed85SJohn Marino          * fts_read and nothing found, get back.  At the root level we use
1617cf28ed85SJohn Marino          * the saved fd; if one of fts_open()'s arguments is a relative name
1618cf28ed85SJohn Marino          * to an empty directory, we wind up here with no other way back.  If
1619cf28ed85SJohn Marino          * can't get back, we're done.
1620cf28ed85SJohn Marino          */
1621cf28ed85SJohn Marino         if (!continue_readdir && descend && (type == BCHILD || !nitems) &&
1622cf28ed85SJohn Marino             (cur->fts_level == FTS_ROOTLEVEL
1623cf28ed85SJohn Marino              ? restore_initial_cwd(sp)
1624cf28ed85SJohn Marino              : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
1625cf28ed85SJohn Marino                 cur->fts_info = FTS_ERR;
1626cf28ed85SJohn Marino                 SET(FTS_STOP);
1627cf28ed85SJohn Marino                 fts_lfree(head);
1628cf28ed85SJohn Marino                 return (NULL);
1629cf28ed85SJohn Marino         }
1630cf28ed85SJohn Marino 
1631cf28ed85SJohn Marino         /* If didn't find anything, return NULL. */
1632cf28ed85SJohn Marino         if (!nitems) {
1633*09d4459fSDaniel Fojt                 if (type == BREAD
1634*09d4459fSDaniel Fojt                     && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
1635cf28ed85SJohn Marino                         cur->fts_info = FTS_DP;
1636cf28ed85SJohn Marino                 fts_lfree(head);
1637cf28ed85SJohn Marino                 return (NULL);
1638cf28ed85SJohn Marino         }
1639cf28ed85SJohn Marino 
1640*09d4459fSDaniel Fojt         if (sort_by_inode) {
1641cf28ed85SJohn Marino                 sp->fts_compar = fts_compare_ino;
1642cf28ed85SJohn Marino                 head = fts_sort (sp, head, nitems);
1643cf28ed85SJohn Marino                 sp->fts_compar = NULL;
1644cf28ed85SJohn Marino         }
1645cf28ed85SJohn Marino 
1646cf28ed85SJohn Marino         /* Sort the entries. */
1647cf28ed85SJohn Marino         if (sp->fts_compar && nitems > 1)
1648cf28ed85SJohn Marino                 head = fts_sort(sp, head, nitems);
1649cf28ed85SJohn Marino         return (head);
1650cf28ed85SJohn Marino }
1651cf28ed85SJohn Marino 
1652cf28ed85SJohn Marino #if FTS_DEBUG
1653cf28ed85SJohn Marino 
1654cf28ed85SJohn Marino /* Walk ->fts_parent links starting at E_CURR, until the root of the
1655cf28ed85SJohn Marino    current hierarchy.  There should be a directory with dev/inode
1656cf28ed85SJohn Marino    matching those of AD.  If not, print a lot of diagnostics.  */
1657cf28ed85SJohn Marino static void
find_matching_ancestor(FTSENT const * e_curr,struct Active_dir const * ad)1658cf28ed85SJohn Marino find_matching_ancestor (FTSENT const *e_curr, struct Active_dir const *ad)
1659cf28ed85SJohn Marino {
1660cf28ed85SJohn Marino   FTSENT const *ent;
1661cf28ed85SJohn Marino   for (ent = e_curr; ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
1662cf28ed85SJohn Marino     {
1663cf28ed85SJohn Marino       if (ad->ino == ent->fts_statp->st_ino
1664cf28ed85SJohn Marino           && ad->dev == ent->fts_statp->st_dev)
1665cf28ed85SJohn Marino         return;
1666cf28ed85SJohn Marino     }
1667cf28ed85SJohn Marino   printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
1668cf28ed85SJohn Marino   printf ("active dirs:\n");
1669cf28ed85SJohn Marino   for (ent = e_curr;
1670cf28ed85SJohn Marino        ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
1671cf28ed85SJohn Marino     printf ("  %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
1672cf28ed85SJohn Marino             ad->fts_ent->fts_accpath,
1673cf28ed85SJohn Marino             (uintmax_t) ad->dev,
1674cf28ed85SJohn Marino             (uintmax_t) ad->ino,
1675cf28ed85SJohn Marino             ent->fts_accpath,
1676cf28ed85SJohn Marino             (uintmax_t) ent->fts_statp->st_dev,
1677cf28ed85SJohn Marino             (uintmax_t) ent->fts_statp->st_ino);
1678cf28ed85SJohn Marino }
1679cf28ed85SJohn Marino 
1680cf28ed85SJohn Marino void
fts_cross_check(FTS const * sp)1681cf28ed85SJohn Marino fts_cross_check (FTS const *sp)
1682cf28ed85SJohn Marino {
1683cf28ed85SJohn Marino   FTSENT const *ent = sp->fts_cur;
1684cf28ed85SJohn Marino   FTSENT const *t;
1685cf28ed85SJohn Marino   if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))
1686cf28ed85SJohn Marino     return;
1687cf28ed85SJohn Marino 
1688cf28ed85SJohn Marino   Dprintf (("fts-cross-check cur=%s\n", ent->fts_path));
1689cf28ed85SJohn Marino   /* Make sure every parent dir is in the tree.  */
1690cf28ed85SJohn Marino   for (t = ent->fts_parent; t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
1691cf28ed85SJohn Marino     {
1692cf28ed85SJohn Marino       struct Active_dir ad;
1693cf28ed85SJohn Marino       ad.ino = t->fts_statp->st_ino;
1694cf28ed85SJohn Marino       ad.dev = t->fts_statp->st_dev;
1695cf28ed85SJohn Marino       if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
1696cf28ed85SJohn Marino         printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
1697cf28ed85SJohn Marino     }
1698cf28ed85SJohn Marino 
1699cf28ed85SJohn Marino   /* Make sure every dir in the tree is an active dir.
1700cf28ed85SJohn Marino      But ENT is not necessarily a directory.  If so, just skip this part. */
1701cf28ed85SJohn Marino   if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
1702cf28ed85SJohn Marino       && (ent->fts_info == FTS_DP
1703cf28ed85SJohn Marino           || ent->fts_info == FTS_D))
1704cf28ed85SJohn Marino     {
1705cf28ed85SJohn Marino       struct Active_dir *ad;
1706cf28ed85SJohn Marino       for (ad = hash_get_first (sp->fts_cycle.ht); ad != NULL;
1707cf28ed85SJohn Marino            ad = hash_get_next (sp->fts_cycle.ht, ad))
1708cf28ed85SJohn Marino         {
1709cf28ed85SJohn Marino           find_matching_ancestor (ent, ad);
1710cf28ed85SJohn Marino         }
1711cf28ed85SJohn Marino     }
1712cf28ed85SJohn Marino }
1713cf28ed85SJohn Marino 
1714cf28ed85SJohn Marino static bool
same_fd(int fd1,int fd2)1715cf28ed85SJohn Marino same_fd (int fd1, int fd2)
1716cf28ed85SJohn Marino {
1717cf28ed85SJohn Marino   struct stat sb1, sb2;
1718cf28ed85SJohn Marino   return (fstat (fd1, &sb1) == 0
1719cf28ed85SJohn Marino           && fstat (fd2, &sb2) == 0
1720cf28ed85SJohn Marino           && SAME_INODE (sb1, sb2));
1721cf28ed85SJohn Marino }
1722cf28ed85SJohn Marino 
1723cf28ed85SJohn Marino static void
fd_ring_print(FTS const * sp,FILE * stream,char const * msg)1724cf28ed85SJohn Marino fd_ring_print (FTS const *sp, FILE *stream, char const *msg)
1725cf28ed85SJohn Marino {
1726cf28ed85SJohn Marino   I_ring const *fd_ring = &sp->fts_fd_ring;
1727cf28ed85SJohn Marino   unsigned int i = fd_ring->fts_front;
1728cf28ed85SJohn Marino   char *cwd = getcwdat (sp->fts_cwd_fd, NULL, 0);
1729cf28ed85SJohn Marino   fprintf (stream, "=== %s ========== %s\n", msg, cwd);
1730cf28ed85SJohn Marino   free (cwd);
1731cf28ed85SJohn Marino   if (i_ring_empty (fd_ring))
1732cf28ed85SJohn Marino     return;
1733cf28ed85SJohn Marino 
1734cf28ed85SJohn Marino   while (true)
1735cf28ed85SJohn Marino     {
1736cf28ed85SJohn Marino       int fd = fd_ring->fts_fd_ring[i];
1737cf28ed85SJohn Marino       if (fd < 0)
1738cf28ed85SJohn Marino         fprintf (stream, "%d: %d:\n", i, fd);
1739cf28ed85SJohn Marino       else
1740cf28ed85SJohn Marino         {
1741cf28ed85SJohn Marino           char *wd = getcwdat (fd, NULL, 0);
1742cf28ed85SJohn Marino           fprintf (stream, "%d: %d: %s\n", i, fd, wd);
1743cf28ed85SJohn Marino           free (wd);
1744cf28ed85SJohn Marino         }
1745cf28ed85SJohn Marino       if (i == fd_ring->fts_back)
1746cf28ed85SJohn Marino         break;
1747cf28ed85SJohn Marino       i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
1748cf28ed85SJohn Marino     }
1749cf28ed85SJohn Marino }
1750cf28ed85SJohn Marino 
1751cf28ed85SJohn Marino /* Ensure that each file descriptor on the fd_ring matches a
1752cf28ed85SJohn Marino    parent, grandparent, etc. of the current working directory.  */
1753cf28ed85SJohn Marino static void
fd_ring_check(FTS const * sp)1754cf28ed85SJohn Marino fd_ring_check (FTS const *sp)
1755cf28ed85SJohn Marino {
1756cf28ed85SJohn Marino   if (!fts_debug)
1757cf28ed85SJohn Marino     return;
1758cf28ed85SJohn Marino 
1759cf28ed85SJohn Marino   /* Make a writable copy.  */
1760cf28ed85SJohn Marino   I_ring fd_w = sp->fts_fd_ring;
1761cf28ed85SJohn Marino 
1762cf28ed85SJohn Marino   int cwd_fd = sp->fts_cwd_fd;
1763*09d4459fSDaniel Fojt   cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
1764cf28ed85SJohn Marino   char *dot = getcwdat (cwd_fd, NULL, 0);
1765cf28ed85SJohn Marino   error (0, 0, "===== check ===== cwd: %s", dot);
1766cf28ed85SJohn Marino   free (dot);
1767cf28ed85SJohn Marino   while ( ! i_ring_empty (&fd_w))
1768cf28ed85SJohn Marino     {
1769cf28ed85SJohn Marino       int fd = i_ring_pop (&fd_w);
1770cf28ed85SJohn Marino       if (0 <= fd)
1771cf28ed85SJohn Marino         {
1772*09d4459fSDaniel Fojt           int open_flags = O_SEARCH | O_CLOEXEC;
1773*09d4459fSDaniel Fojt           int parent_fd = openat (cwd_fd, "..", open_flags);
1774cf28ed85SJohn Marino           if (parent_fd < 0)
1775cf28ed85SJohn Marino             {
1776cf28ed85SJohn Marino               // Warn?
1777cf28ed85SJohn Marino               break;
1778cf28ed85SJohn Marino             }
1779cf28ed85SJohn Marino           if (!same_fd (fd, parent_fd))
1780cf28ed85SJohn Marino             {
1781cf28ed85SJohn Marino               char *cwd = getcwdat (fd, NULL, 0);
1782cf28ed85SJohn Marino               error (0, errno, "ring  : %s", cwd);
1783cf28ed85SJohn Marino               char *c2 = getcwdat (parent_fd, NULL, 0);
1784cf28ed85SJohn Marino               error (0, errno, "parent: %s", c2);
1785cf28ed85SJohn Marino               free (cwd);
1786cf28ed85SJohn Marino               free (c2);
1787cf28ed85SJohn Marino               fts_assert (0);
1788cf28ed85SJohn Marino             }
1789cf28ed85SJohn Marino           close (cwd_fd);
1790cf28ed85SJohn Marino           cwd_fd = parent_fd;
1791cf28ed85SJohn Marino         }
1792cf28ed85SJohn Marino     }
1793cf28ed85SJohn Marino   close (cwd_fd);
1794cf28ed85SJohn Marino }
1795cf28ed85SJohn Marino #endif
1796cf28ed85SJohn Marino 
1797cf28ed85SJohn Marino static unsigned short int
1798cf28ed85SJohn Marino internal_function
fts_stat(FTS * sp,register FTSENT * p,bool follow)1799cf28ed85SJohn Marino fts_stat(FTS *sp, register FTSENT *p, bool follow)
1800cf28ed85SJohn Marino {
1801cf28ed85SJohn Marino         struct stat *sbp = p->fts_statp;
1802cf28ed85SJohn Marino 
1803cf28ed85SJohn Marino         if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW))
1804cf28ed85SJohn Marino                 follow = true;
1805cf28ed85SJohn Marino 
1806cf28ed85SJohn Marino         /*
1807cf28ed85SJohn Marino          * If doing a logical walk, or application requested FTS_FOLLOW, do
1808cf28ed85SJohn Marino          * a stat(2).  If that fails, check for a non-existent symlink.  If
1809cf28ed85SJohn Marino          * fail, set the errno from the stat call.
1810cf28ed85SJohn Marino          */
1811cf28ed85SJohn Marino         if (ISSET(FTS_LOGICAL) || follow) {
1812cf28ed85SJohn Marino                 if (stat(p->fts_accpath, sbp)) {
1813cf28ed85SJohn Marino                         if (errno == ENOENT
1814cf28ed85SJohn Marino                             && lstat(p->fts_accpath, sbp) == 0) {
1815cf28ed85SJohn Marino                                 __set_errno (0);
1816cf28ed85SJohn Marino                                 return (FTS_SLNONE);
1817cf28ed85SJohn Marino                         }
1818*09d4459fSDaniel Fojt                         p->fts_errno = errno;
1819cf28ed85SJohn Marino                         goto err;
1820cf28ed85SJohn Marino                 }
1821cf28ed85SJohn Marino         } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp,
1822cf28ed85SJohn Marino                            AT_SYMLINK_NOFOLLOW)) {
1823cf28ed85SJohn Marino                 p->fts_errno = errno;
1824cf28ed85SJohn Marino err:            memset(sbp, 0, sizeof(struct stat));
1825cf28ed85SJohn Marino                 return (FTS_NS);
1826cf28ed85SJohn Marino         }
1827cf28ed85SJohn Marino 
1828cf28ed85SJohn Marino         if (S_ISDIR(sbp->st_mode)) {
1829*09d4459fSDaniel Fojt                 p->fts_n_dirs_remaining
1830*09d4459fSDaniel Fojt                   = ((sbp->st_nlink < MIN_DIR_NLINK
1831*09d4459fSDaniel Fojt                       || p->fts_level <= FTS_ROOTLEVEL)
1832*09d4459fSDaniel Fojt                      ? -1
1833*09d4459fSDaniel Fojt                      : sbp->st_nlink - (ISSET (FTS_SEEDOT) ? 0 : MIN_DIR_NLINK));
1834cf28ed85SJohn Marino                 if (ISDOT(p->fts_name)) {
1835cf28ed85SJohn Marino                         /* Command-line "." and ".." are real directories. */
1836cf28ed85SJohn Marino                         return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
1837cf28ed85SJohn Marino                 }
1838cf28ed85SJohn Marino 
1839cf28ed85SJohn Marino                 return (FTS_D);
1840cf28ed85SJohn Marino         }
1841cf28ed85SJohn Marino         if (S_ISLNK(sbp->st_mode))
1842cf28ed85SJohn Marino                 return (FTS_SL);
1843cf28ed85SJohn Marino         if (S_ISREG(sbp->st_mode))
1844cf28ed85SJohn Marino                 return (FTS_F);
1845cf28ed85SJohn Marino         return (FTS_DEFAULT);
1846cf28ed85SJohn Marino }
1847cf28ed85SJohn Marino 
1848cf28ed85SJohn Marino static int
fts_compar(void const * a,void const * b)1849cf28ed85SJohn Marino fts_compar (void const *a, void const *b)
1850cf28ed85SJohn Marino {
1851cf28ed85SJohn Marino   /* Convert A and B to the correct types, to pacify the compiler, and
1852cf28ed85SJohn Marino      for portability to bizarre hosts where "void const *" and "FTSENT
1853cf28ed85SJohn Marino      const **" differ in runtime representation.  The comparison
1854cf28ed85SJohn Marino      function cannot modify *a and *b, but there is no compile-time
1855cf28ed85SJohn Marino      check for this.  */
1856cf28ed85SJohn Marino   FTSENT const **pa = (FTSENT const **) a;
1857cf28ed85SJohn Marino   FTSENT const **pb = (FTSENT const **) b;
1858cf28ed85SJohn Marino   return pa[0]->fts_fts->fts_compar (pa, pb);
1859cf28ed85SJohn Marino }
1860cf28ed85SJohn Marino 
1861cf28ed85SJohn Marino static FTSENT *
1862cf28ed85SJohn Marino internal_function
fts_sort(FTS * sp,FTSENT * head,register size_t nitems)1863cf28ed85SJohn Marino fts_sort (FTS *sp, FTSENT *head, register size_t nitems)
1864cf28ed85SJohn Marino {
1865cf28ed85SJohn Marino         register FTSENT **ap, *p;
1866cf28ed85SJohn Marino 
1867cf28ed85SJohn Marino         /* On most modern hosts, void * and FTSENT ** have the same
1868cf28ed85SJohn Marino            run-time representation, and one can convert sp->fts_compar to
1869cf28ed85SJohn Marino            the type qsort expects without problem.  Use the heuristic that
1870cf28ed85SJohn Marino            this is OK if the two pointer types are the same size, and if
1871cf28ed85SJohn Marino            converting FTSENT ** to long int is the same as converting
1872cf28ed85SJohn Marino            FTSENT ** to void * and then to long int.  This heuristic isn't
1873cf28ed85SJohn Marino            valid in general but we don't know of any counterexamples.  */
1874cf28ed85SJohn Marino         FTSENT *dummy;
1875cf28ed85SJohn Marino         int (*compare) (void const *, void const *) =
1876cf28ed85SJohn Marino           ((sizeof &dummy == sizeof (void *)
1877cf28ed85SJohn Marino             && (long int) &dummy == (long int) (void *) &dummy)
1878cf28ed85SJohn Marino            ? (int (*) (void const *, void const *)) sp->fts_compar
1879cf28ed85SJohn Marino            : fts_compar);
1880cf28ed85SJohn Marino 
1881cf28ed85SJohn Marino         /*
1882cf28ed85SJohn Marino          * Construct an array of pointers to the structures and call qsort(3).
1883cf28ed85SJohn Marino          * Reassemble the array in the order returned by qsort.  If unable to
1884cf28ed85SJohn Marino          * sort for memory reasons, return the directory entries in their
1885cf28ed85SJohn Marino          * current order.  Allocate enough space for the current needs plus
1886cf28ed85SJohn Marino          * 40 so don't realloc one entry at a time.
1887cf28ed85SJohn Marino          */
1888cf28ed85SJohn Marino         if (nitems > sp->fts_nitems) {
1889cf28ed85SJohn Marino                 FTSENT **a;
1890cf28ed85SJohn Marino 
1891cf28ed85SJohn Marino                 sp->fts_nitems = nitems + 40;
1892cf28ed85SJohn Marino                 if (SIZE_MAX / sizeof *a < sp->fts_nitems
1893cf28ed85SJohn Marino                     || ! (a = realloc (sp->fts_array,
1894cf28ed85SJohn Marino                                        sp->fts_nitems * sizeof *a))) {
1895cf28ed85SJohn Marino                         free(sp->fts_array);
1896cf28ed85SJohn Marino                         sp->fts_array = NULL;
1897cf28ed85SJohn Marino                         sp->fts_nitems = 0;
1898cf28ed85SJohn Marino                         return (head);
1899cf28ed85SJohn Marino                 }
1900cf28ed85SJohn Marino                 sp->fts_array = a;
1901cf28ed85SJohn Marino         }
1902cf28ed85SJohn Marino         for (ap = sp->fts_array, p = head; p; p = p->fts_link)
1903cf28ed85SJohn Marino                 *ap++ = p;
1904cf28ed85SJohn Marino         qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), compare);
1905cf28ed85SJohn Marino         for (head = *(ap = sp->fts_array); --nitems; ++ap)
1906cf28ed85SJohn Marino                 ap[0]->fts_link = ap[1];
1907cf28ed85SJohn Marino         ap[0]->fts_link = NULL;
1908cf28ed85SJohn Marino         return (head);
1909cf28ed85SJohn Marino }
1910cf28ed85SJohn Marino 
1911cf28ed85SJohn Marino static FTSENT *
1912cf28ed85SJohn Marino internal_function
fts_alloc(FTS * sp,const char * name,register size_t namelen)1913cf28ed85SJohn Marino fts_alloc (FTS *sp, const char *name, register size_t namelen)
1914cf28ed85SJohn Marino {
1915cf28ed85SJohn Marino         register FTSENT *p;
1916cf28ed85SJohn Marino         size_t len;
1917cf28ed85SJohn Marino 
1918cf28ed85SJohn Marino         /*
1919cf28ed85SJohn Marino          * The file name is a variable length array.  Allocate the FTSENT
1920cf28ed85SJohn Marino          * structure and the file name in one chunk.
1921cf28ed85SJohn Marino          */
1922*09d4459fSDaniel Fojt         len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);
1923cf28ed85SJohn Marino         if ((p = malloc(len)) == NULL)
1924cf28ed85SJohn Marino                 return (NULL);
1925cf28ed85SJohn Marino 
1926cf28ed85SJohn Marino         /* Copy the name and guarantee NUL termination. */
1927680a9cb8SJohn Marino         memcpy(p->fts_name, name, namelen);
1928cf28ed85SJohn Marino         p->fts_name[namelen] = '\0';
1929cf28ed85SJohn Marino 
1930cf28ed85SJohn Marino         p->fts_namelen = namelen;
1931cf28ed85SJohn Marino         p->fts_fts = sp;
1932cf28ed85SJohn Marino         p->fts_path = sp->fts_path;
1933cf28ed85SJohn Marino         p->fts_errno = 0;
1934cf28ed85SJohn Marino         p->fts_dirp = NULL;
1935cf28ed85SJohn Marino         p->fts_flags = 0;
1936cf28ed85SJohn Marino         p->fts_instr = FTS_NOINSTR;
1937cf28ed85SJohn Marino         p->fts_number = 0;
1938cf28ed85SJohn Marino         p->fts_pointer = NULL;
1939cf28ed85SJohn Marino         return (p);
1940cf28ed85SJohn Marino }
1941cf28ed85SJohn Marino 
1942cf28ed85SJohn Marino static void
1943cf28ed85SJohn Marino internal_function
fts_lfree(register FTSENT * head)1944cf28ed85SJohn Marino fts_lfree (register FTSENT *head)
1945cf28ed85SJohn Marino {
1946cf28ed85SJohn Marino         register FTSENT *p;
1947cf28ed85SJohn Marino 
1948cf28ed85SJohn Marino         /* Free a linked list of structures. */
1949cf28ed85SJohn Marino         while ((p = head)) {
1950cf28ed85SJohn Marino                 head = head->fts_link;
1951cf28ed85SJohn Marino                 if (p->fts_dirp)
1952cf28ed85SJohn Marino                         closedir (p->fts_dirp);
1953cf28ed85SJohn Marino                 free(p);
1954cf28ed85SJohn Marino         }
1955cf28ed85SJohn Marino }
1956cf28ed85SJohn Marino 
1957cf28ed85SJohn Marino /*
1958cf28ed85SJohn Marino  * Allow essentially unlimited file name lengths; find, rm, ls should
1959cf28ed85SJohn Marino  * all work on any tree.  Most systems will allow creation of file
1960cf28ed85SJohn Marino  * names much longer than MAXPATHLEN, even though the kernel won't
1961cf28ed85SJohn Marino  * resolve them.  Add the size (not just what's needed) plus 256 bytes
1962cf28ed85SJohn Marino  * so don't realloc the file name 2 bytes at a time.
1963cf28ed85SJohn Marino  */
1964cf28ed85SJohn Marino static bool
1965cf28ed85SJohn Marino internal_function
fts_palloc(FTS * sp,size_t more)1966cf28ed85SJohn Marino fts_palloc (FTS *sp, size_t more)
1967cf28ed85SJohn Marino {
1968cf28ed85SJohn Marino         char *p;
1969cf28ed85SJohn Marino         size_t new_len = sp->fts_pathlen + more + 256;
1970cf28ed85SJohn Marino 
1971cf28ed85SJohn Marino         /*
1972cf28ed85SJohn Marino          * See if fts_pathlen would overflow.
1973cf28ed85SJohn Marino          */
1974cf28ed85SJohn Marino         if (new_len < sp->fts_pathlen) {
1975cf28ed85SJohn Marino                 free(sp->fts_path);
1976cf28ed85SJohn Marino                 sp->fts_path = NULL;
1977cf28ed85SJohn Marino                 __set_errno (ENAMETOOLONG);
1978cf28ed85SJohn Marino                 return false;
1979cf28ed85SJohn Marino         }
1980cf28ed85SJohn Marino         sp->fts_pathlen = new_len;
1981cf28ed85SJohn Marino         p = realloc(sp->fts_path, sp->fts_pathlen);
1982cf28ed85SJohn Marino         if (p == NULL) {
1983cf28ed85SJohn Marino                 free(sp->fts_path);
1984cf28ed85SJohn Marino                 sp->fts_path = NULL;
1985cf28ed85SJohn Marino                 return false;
1986cf28ed85SJohn Marino         }
1987cf28ed85SJohn Marino         sp->fts_path = p;
1988cf28ed85SJohn Marino         return true;
1989cf28ed85SJohn Marino }
1990cf28ed85SJohn Marino 
1991cf28ed85SJohn Marino /*
1992cf28ed85SJohn Marino  * When the file name is realloc'd, have to fix all of the pointers in
1993cf28ed85SJohn Marino  *  structures already returned.
1994cf28ed85SJohn Marino  */
1995cf28ed85SJohn Marino static void
1996cf28ed85SJohn Marino internal_function
fts_padjust(FTS * sp,FTSENT * head)1997cf28ed85SJohn Marino fts_padjust (FTS *sp, FTSENT *head)
1998cf28ed85SJohn Marino {
1999cf28ed85SJohn Marino         FTSENT *p;
2000cf28ed85SJohn Marino         char *addr = sp->fts_path;
2001cf28ed85SJohn Marino 
2002cf28ed85SJohn Marino #define ADJUST(p) do {                                                  \
2003cf28ed85SJohn Marino         if ((p)->fts_accpath != (p)->fts_name) {                        \
2004cf28ed85SJohn Marino                 (p)->fts_accpath =                                      \
2005cf28ed85SJohn Marino                     (char *)addr + ((p)->fts_accpath - (p)->fts_path);  \
2006cf28ed85SJohn Marino         }                                                               \
2007cf28ed85SJohn Marino         (p)->fts_path = addr;                                           \
2008cf28ed85SJohn Marino } while (0)
2009cf28ed85SJohn Marino         /* Adjust the current set of children. */
2010cf28ed85SJohn Marino         for (p = sp->fts_child; p; p = p->fts_link)
2011cf28ed85SJohn Marino                 ADJUST(p);
2012cf28ed85SJohn Marino 
2013cf28ed85SJohn Marino         /* Adjust the rest of the tree, including the current level. */
2014cf28ed85SJohn Marino         for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
2015cf28ed85SJohn Marino                 ADJUST(p);
2016cf28ed85SJohn Marino                 p = p->fts_link ? p->fts_link : p->fts_parent;
2017cf28ed85SJohn Marino         }
2018cf28ed85SJohn Marino }
2019cf28ed85SJohn Marino 
2020cf28ed85SJohn Marino static size_t
2021cf28ed85SJohn Marino internal_function _GL_ATTRIBUTE_PURE
fts_maxarglen(char * const * argv)2022cf28ed85SJohn Marino fts_maxarglen (char * const *argv)
2023cf28ed85SJohn Marino {
2024cf28ed85SJohn Marino         size_t len, max;
2025cf28ed85SJohn Marino 
2026cf28ed85SJohn Marino         for (max = 0; *argv; ++argv)
2027cf28ed85SJohn Marino                 if ((len = strlen(*argv)) > max)
2028cf28ed85SJohn Marino                         max = len;
2029cf28ed85SJohn Marino         return (max + 1);
2030cf28ed85SJohn Marino }
2031cf28ed85SJohn Marino 
2032cf28ed85SJohn Marino /*
2033cf28ed85SJohn Marino  * Change to dir specified by fd or file name without getting
2034cf28ed85SJohn Marino  * tricked by someone changing the world out from underneath us.
2035cf28ed85SJohn Marino  * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.
2036cf28ed85SJohn Marino  * If FD is non-negative, expect it to be used after this function returns,
2037cf28ed85SJohn Marino  * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then
2038cf28ed85SJohn Marino  * do closedir(dirp), because that would invalidate the saved FD.
2039cf28ed85SJohn Marino  * Upon failure, close FD immediately and return nonzero.
2040cf28ed85SJohn Marino  */
2041cf28ed85SJohn Marino static int
2042cf28ed85SJohn Marino internal_function
fts_safe_changedir(FTS * sp,FTSENT * p,int fd,char const * dir)2043cf28ed85SJohn Marino fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir)
2044cf28ed85SJohn Marino {
2045cf28ed85SJohn Marino         int ret;
2046cf28ed85SJohn Marino         bool is_dotdot = dir && STREQ (dir, "..");
2047cf28ed85SJohn Marino         int newfd;
2048cf28ed85SJohn Marino 
2049cf28ed85SJohn Marino         /* This clause handles the unusual case in which FTS_NOCHDIR
2050cf28ed85SJohn Marino            is specified, along with FTS_CWDFD.  In that case, there is
2051cf28ed85SJohn Marino            no need to change even the virtual cwd file descriptor.
2052cf28ed85SJohn Marino            However, if FD is non-negative, we do close it here.  */
2053cf28ed85SJohn Marino         if (ISSET (FTS_NOCHDIR))
2054cf28ed85SJohn Marino           {
2055cf28ed85SJohn Marino             if (ISSET (FTS_CWDFD) && 0 <= fd)
2056cf28ed85SJohn Marino               close (fd);
2057cf28ed85SJohn Marino             return 0;
2058cf28ed85SJohn Marino           }
2059cf28ed85SJohn Marino 
2060cf28ed85SJohn Marino         if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
2061cf28ed85SJohn Marino           {
2062cf28ed85SJohn Marino             /* When possible, skip the diropen and subsequent fstat+dev/ino
2063cf28ed85SJohn Marino                comparison.  I.e., when changing to parent directory
2064cf28ed85SJohn Marino                (chdir ("..")), use a file descriptor from the ring and
2065cf28ed85SJohn Marino                save the overhead of diropen+fstat, as well as avoiding
2066cf28ed85SJohn Marino                failure when we lack "x" access to the virtual cwd.  */
2067cf28ed85SJohn Marino             if ( ! i_ring_empty (&sp->fts_fd_ring))
2068cf28ed85SJohn Marino               {
2069cf28ed85SJohn Marino                 int parent_fd;
2070cf28ed85SJohn Marino                 fd_ring_print (sp, stderr, "pre-pop");
2071cf28ed85SJohn Marino                 parent_fd = i_ring_pop (&sp->fts_fd_ring);
2072cf28ed85SJohn Marino                 if (0 <= parent_fd)
2073cf28ed85SJohn Marino                   {
2074cf28ed85SJohn Marino                     fd = parent_fd;
2075cf28ed85SJohn Marino                     dir = NULL;
2076cf28ed85SJohn Marino                   }
2077cf28ed85SJohn Marino               }
2078cf28ed85SJohn Marino           }
2079cf28ed85SJohn Marino 
2080cf28ed85SJohn Marino         newfd = fd;
2081cf28ed85SJohn Marino         if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
2082cf28ed85SJohn Marino           return -1;
2083cf28ed85SJohn Marino 
2084cf28ed85SJohn Marino         /* The following dev/inode check is necessary if we're doing a
2085cf28ed85SJohn Marino            "logical" traversal (through symlinks, a la chown -L), if the
2086cf28ed85SJohn Marino            system lacks O_NOFOLLOW support, or if we're changing to ".."
2087cf28ed85SJohn Marino            (but not via a popped file descriptor).  When changing to the
2088cf28ed85SJohn Marino            name "..", O_NOFOLLOW can't help.  In general, when the target is
2089cf28ed85SJohn Marino            not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
2090cf28ed85SJohn Marino            follow a symlink, so we can avoid the expense of this fstat.  */
2091cf28ed85SJohn Marino         if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
2092cf28ed85SJohn Marino             || (dir && STREQ (dir, "..")))
2093cf28ed85SJohn Marino           {
2094cf28ed85SJohn Marino             struct stat sb;
2095cf28ed85SJohn Marino             if (fstat(newfd, &sb))
2096cf28ed85SJohn Marino               {
2097cf28ed85SJohn Marino                 ret = -1;
2098cf28ed85SJohn Marino                 goto bail;
2099cf28ed85SJohn Marino               }
2100cf28ed85SJohn Marino             if (p->fts_statp->st_dev != sb.st_dev
2101cf28ed85SJohn Marino                 || p->fts_statp->st_ino != sb.st_ino)
2102cf28ed85SJohn Marino               {
2103cf28ed85SJohn Marino                 __set_errno (ENOENT);           /* disinformation */
2104cf28ed85SJohn Marino                 ret = -1;
2105cf28ed85SJohn Marino                 goto bail;
2106cf28ed85SJohn Marino               }
2107cf28ed85SJohn Marino           }
2108cf28ed85SJohn Marino 
2109cf28ed85SJohn Marino         if (ISSET(FTS_CWDFD))
2110cf28ed85SJohn Marino           {
2111cf28ed85SJohn Marino             cwd_advance_fd (sp, newfd, ! is_dotdot);
2112cf28ed85SJohn Marino             return 0;
2113cf28ed85SJohn Marino           }
2114cf28ed85SJohn Marino 
2115cf28ed85SJohn Marino         ret = fchdir(newfd);
2116cf28ed85SJohn Marino bail:
2117cf28ed85SJohn Marino         if (fd < 0)
2118cf28ed85SJohn Marino           {
2119cf28ed85SJohn Marino             int oerrno = errno;
2120cf28ed85SJohn Marino             (void)close(newfd);
2121cf28ed85SJohn Marino             __set_errno (oerrno);
2122cf28ed85SJohn Marino           }
2123cf28ed85SJohn Marino         return ret;
2124cf28ed85SJohn Marino }
2125