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