Blame lib/fts.c

Packit 709fb3
/* Traverse a file hierarchy.
Packit 709fb3
Packit 709fb3
   Copyright (C) 2004-2017 Free Software Foundation, Inc.
Packit 709fb3
Packit 709fb3
   This program is free software: you can redistribute it and/or modify
Packit 709fb3
   it under the terms of the GNU General Public License as published by
Packit 709fb3
   the Free Software Foundation; either version 3 of the License, or
Packit 709fb3
   (at your option) any later version.
Packit 709fb3
Packit 709fb3
   This program is distributed in the hope that it will be useful,
Packit 709fb3
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 709fb3
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 709fb3
   GNU General Public License for more details.
Packit 709fb3
Packit 709fb3
   You should have received a copy of the GNU General Public License
Packit 709fb3
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 709fb3
Packit 709fb3
/*-
Packit 709fb3
 * Copyright (c) 1990, 1993, 1994
Packit 709fb3
 *      The Regents of the University of California.  All rights reserved.
Packit 709fb3
 *
Packit 709fb3
 * Redistribution and use in source and binary forms, with or without
Packit 709fb3
 * modification, are permitted provided that the following conditions
Packit 709fb3
 * are met:
Packit 709fb3
 * 1. Redistributions of source code must retain the above copyright
Packit 709fb3
 *    notice, this list of conditions and the following disclaimer.
Packit 709fb3
 * 2. Redistributions in binary form must reproduce the above copyright
Packit 709fb3
 *    notice, this list of conditions and the following disclaimer in the
Packit 709fb3
 *    documentation and/or other materials provided with the distribution.
Packit 709fb3
 * 4. Neither the name of the University nor the names of its contributors
Packit 709fb3
 *    may be used to endorse or promote products derived from this software
Packit 709fb3
 *    without specific prior written permission.
Packit 709fb3
 *
Packit 709fb3
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
Packit 709fb3
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit 709fb3
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit 709fb3
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
Packit 709fb3
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit 709fb3
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
Packit 709fb3
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
Packit 709fb3
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
Packit 709fb3
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
Packit 709fb3
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
Packit 709fb3
 * SUCH DAMAGE.
Packit 709fb3
 */
Packit 709fb3
Packit 709fb3
#include <config.h>
Packit 709fb3
Packit 709fb3
#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint
Packit 709fb3
static char sccsid[] = "@(#)fts.c       8.6 (Berkeley) 8/14/94";
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#include "fts_.h"
Packit 709fb3
Packit 709fb3
#if HAVE_SYS_PARAM_H || defined _LIBC
Packit 709fb3
# include <sys/param.h>
Packit 709fb3
#endif
Packit 709fb3
#ifdef _LIBC
Packit 709fb3
# include <include/sys/stat.h>
Packit 709fb3
#else
Packit 709fb3
# include <sys/stat.h>
Packit 709fb3
#endif
Packit 709fb3
#include <fcntl.h>
Packit 709fb3
#include <errno.h>
Packit 709fb3
#include <stdalign.h>
Packit 709fb3
#include <stdbool.h>
Packit 709fb3
#include <stddef.h>
Packit 709fb3
#include <stdlib.h>
Packit 709fb3
#include <string.h>
Packit 709fb3
#include <unistd.h>
Packit 709fb3
Packit 709fb3
#if ! _LIBC
Packit 709fb3
# include "fcntl--.h"
Packit 709fb3
# include "dirent--.h"
Packit 709fb3
# include "unistd--.h"
Packit 709fb3
/* FIXME - use fcntl(F_DUPFD_CLOEXEC)/openat(O_CLOEXEC) once they are
Packit 709fb3
   supported.  */
Packit 709fb3
# include "cloexec.h"
Packit 709fb3
# include "flexmember.h"
Packit 709fb3
# include "openat.h"
Packit 709fb3
# include "same-inode.h"
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#include <dirent.h>
Packit 709fb3
#ifndef _D_EXACT_NAMLEN
Packit 709fb3
# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#if HAVE_STRUCT_DIRENT_D_TYPE
Packit 709fb3
/* True if the type of the directory entry D is known.  */
Packit 709fb3
# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
Packit 709fb3
/* True if the type of the directory entry D must be T.  */
Packit 709fb3
# define DT_MUST_BE(d, t) ((d)->d_type == (t))
Packit 709fb3
# define D_TYPE(d) ((d)->d_type)
Packit 709fb3
#else
Packit 709fb3
# define DT_IS_KNOWN(d) false
Packit 709fb3
# define DT_MUST_BE(d, t) false
Packit 709fb3
# define D_TYPE(d) DT_UNKNOWN
Packit 709fb3
Packit 709fb3
# undef DT_UNKNOWN
Packit 709fb3
# define DT_UNKNOWN 0
Packit 709fb3
Packit 709fb3
/* Any nonzero values will do here, so long as they're distinct.
Packit 709fb3
   Undef any existing macros out of the way.  */
Packit 709fb3
# undef DT_BLK
Packit 709fb3
# undef DT_CHR
Packit 709fb3
# undef DT_DIR
Packit 709fb3
# undef DT_FIFO
Packit 709fb3
# undef DT_LNK
Packit 709fb3
# undef DT_REG
Packit 709fb3
# undef DT_SOCK
Packit 709fb3
# define DT_BLK 1
Packit 709fb3
# define DT_CHR 2
Packit 709fb3
# define DT_DIR 3
Packit 709fb3
# define DT_FIFO 4
Packit 709fb3
# define DT_LNK 5
Packit 709fb3
# define DT_REG 6
Packit 709fb3
# define DT_SOCK 7
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#ifndef S_IFLNK
Packit 709fb3
# define S_IFLNK 0
Packit 709fb3
#endif
Packit 709fb3
#ifndef S_IFSOCK
Packit 709fb3
# define S_IFSOCK 0
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
enum
Packit 709fb3
{
Packit 709fb3
  NOT_AN_INODE_NUMBER = 0
Packit 709fb3
};
Packit 709fb3
Packit 709fb3
#ifdef D_INO_IN_DIRENT
Packit 709fb3
# define D_INO(dp) (dp)->d_ino
Packit 709fb3
#else
Packit 709fb3
/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
Packit 709fb3
# define D_INO(dp) NOT_AN_INODE_NUMBER
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
/* If possible (see max_entries, below), read no more than this many directory
Packit 709fb3
   entries at a time.  Without this limit (i.e., when using non-NULL
Packit 709fb3
   fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
Packit 709fb3
   of memory, and handling 64M entries would require 16GiB of memory.  */
Packit 709fb3
#ifndef FTS_MAX_READDIR_ENTRIES
Packit 709fb3
# define FTS_MAX_READDIR_ENTRIES 100000
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
/* If there are more than this many entries in a directory,
Packit 709fb3
   and the conditions mentioned below are satisfied, then sort
Packit 709fb3
   the entries on inode number before any further processing.  */
Packit 709fb3
#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
Packit 709fb3
# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
enum
Packit 709fb3
{
Packit 709fb3
  _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
Packit 709fb3
};
Packit 709fb3
Packit 709fb3
enum Fts_stat
Packit 709fb3
{
Packit 709fb3
  FTS_NO_STAT_REQUIRED = 1,
Packit 709fb3
  FTS_STAT_REQUIRED = 2
Packit 709fb3
};
Packit 709fb3
Packit 709fb3
#ifdef _LIBC
Packit 709fb3
# undef close
Packit 709fb3
# define close __close
Packit 709fb3
# undef closedir
Packit 709fb3
# define closedir __closedir
Packit 709fb3
# undef fchdir
Packit 709fb3
# define fchdir __fchdir
Packit 709fb3
# undef open
Packit 709fb3
# define open __open
Packit 709fb3
# undef readdir
Packit 709fb3
# define readdir __readdir
Packit 709fb3
#else
Packit 709fb3
# undef internal_function
Packit 709fb3
# define internal_function /* empty */
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#ifndef __set_errno
Packit 709fb3
# define __set_errno(Val) errno = (Val)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
/* If this host provides the openat function, then we can avoid
Packit 709fb3
   attempting to open "." in some initialization code below.  */
Packit 709fb3
#ifdef HAVE_OPENAT
Packit 709fb3
# define HAVE_OPENAT_SUPPORT 1
Packit 709fb3
#else
Packit 709fb3
# define HAVE_OPENAT_SUPPORT 0
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#ifdef NDEBUG
Packit 709fb3
# define fts_assert(expr) ((void) (0 && (expr)))
Packit 709fb3
#else
Packit 709fb3
# define fts_assert(expr)       \
Packit 709fb3
    do                          \
Packit 709fb3
      {                         \
Packit 709fb3
        if (!(expr))            \
Packit 709fb3
          abort ();             \
Packit 709fb3
      }                         \
Packit 709fb3
    while (false)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#ifndef FALLTHROUGH
Packit 709fb3
# if __GNUC__ < 7
Packit 709fb3
#  define FALLTHROUGH ((void) 0)
Packit 709fb3
# else
Packit 709fb3
#  define FALLTHROUGH __attribute__ ((__fallthrough__))
Packit 709fb3
# endif
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
static FTSENT   *fts_alloc (FTS *, const char *, size_t) internal_function;
Packit 709fb3
static FTSENT   *fts_build (FTS *, int) internal_function;
Packit 709fb3
static void      fts_lfree (FTSENT *) internal_function;
Packit 709fb3
static void      fts_load (FTS *, FTSENT *) internal_function;
Packit 709fb3
static size_t    fts_maxarglen (char * const *) internal_function;
Packit 709fb3
static void      fts_padjust (FTS *, FTSENT *) internal_function;
Packit 709fb3
static bool      fts_palloc (FTS *, size_t) internal_function;
Packit 709fb3
static FTSENT   *fts_sort (FTS *, FTSENT *, size_t) internal_function;
Packit 709fb3
static unsigned short int fts_stat (FTS *, FTSENT *, bool) internal_function;
Packit 709fb3
static int      fts_safe_changedir (FTS *, FTSENT *, int, const char *)
Packit 709fb3
     internal_function;
Packit 709fb3
Packit 709fb3
#include "fts-cycle.c"
Packit 709fb3
Packit 709fb3
#ifndef MAX
Packit 709fb3
# define MAX(a,b) ((a) > (b) ? (a) : (b))
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#ifndef SIZE_MAX
Packit 709fb3
# define SIZE_MAX ((size_t) -1)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
Packit 709fb3
#define STREQ(a, b)     (strcmp (a, b) == 0)
Packit 709fb3
Packit 709fb3
#define CLR(opt)        (sp->fts_options &= ~(opt))
Packit 709fb3
#define ISSET(opt)      (sp->fts_options & (opt))
Packit 709fb3
#define SET(opt)        (sp->fts_options |= (opt))
Packit 709fb3
Packit 709fb3
/* FIXME: FTS_NOCHDIR is now misnamed.
Packit 709fb3
   Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
Packit 709fb3
#define FCHDIR(sp, fd)                                  \
Packit 709fb3
  (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \
Packit 709fb3
                           ? (cwd_advance_fd ((sp), (fd), true), 0) \
Packit 709fb3
                           : fchdir (fd)))
Packit 709fb3
Packit 709fb3
Packit 709fb3
/* fts_build flags */
Packit 709fb3
/* FIXME: make this an enum */
Packit 709fb3
#define BCHILD          1               /* fts_children */
Packit 709fb3
#define BNAMES          2               /* fts_children, names only */
Packit 709fb3
#define BREAD           3               /* fts_read */
Packit 709fb3
Packit 709fb3
#if FTS_DEBUG
Packit 709fb3
# include <inttypes.h>
Packit 709fb3
# include <stdint.h>
Packit 709fb3
# include <stdio.h>
Packit 709fb3
# include "getcwdat.h"
Packit 709fb3
bool fts_debug = false;
Packit 709fb3
# define Dprintf(x) do { if (fts_debug) printf x; } while (false)
Packit 709fb3
#else
Packit 709fb3
# define Dprintf(x)
Packit 709fb3
# define fd_ring_check(x)
Packit 709fb3
# define fd_ring_print(a, b, c)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#define LEAVE_DIR(Fts, Ent, Tag)                                \
Packit 709fb3
  do                                                            \
Packit 709fb3
    {                                                           \
Packit 709fb3
      Dprintf (("  %s-leaving: %s\n", Tag, (Ent)->fts_path));   \
Packit 709fb3
      leave_dir (Fts, Ent);                                     \
Packit 709fb3
      fd_ring_check (Fts);                                      \
Packit 709fb3
    }                                                           \
Packit 709fb3
  while (false)
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
fd_ring_clear (I_ring *fd_ring)
Packit 709fb3
{
Packit 709fb3
  while ( ! i_ring_empty (fd_ring))
Packit 709fb3
    {
Packit 709fb3
      int fd = i_ring_pop (fd_ring);
Packit 709fb3
      if (0 <= fd)
Packit 709fb3
        close (fd);
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Overload the fts_statp->st_size member (otherwise unused, when
Packit 709fb3
   fts_info is FTS_NSOK) to indicate whether fts_read should stat
Packit 709fb3
   this entry or not.  */
Packit 709fb3
static void
Packit 709fb3
fts_set_stat_required (FTSENT *p, bool required)
Packit 709fb3
{
Packit 709fb3
  fts_assert (p->fts_info == FTS_NSOK);
Packit 709fb3
  p->fts_statp->st_size = (required
Packit 709fb3
                           ? FTS_STAT_REQUIRED
Packit 709fb3
                           : FTS_NO_STAT_REQUIRED);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* file-descriptor-relative opendir.  */
Packit 709fb3
/* FIXME: if others need this function, move it into lib/openat.c */
Packit 709fb3
static DIR *
Packit 709fb3
internal_function
Packit 709fb3
opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd)
Packit 709fb3
{
Packit 709fb3
  int new_fd = openat (fd, dir,
Packit 709fb3
                       (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
Packit 709fb3
                        | extra_flags));
Packit 709fb3
  DIR *dirp;
Packit 709fb3
Packit 709fb3
  if (new_fd < 0)
Packit 709fb3
    return NULL;
Packit 709fb3
  set_cloexec_flag (new_fd, true);
Packit 709fb3
  dirp = fdopendir (new_fd);
Packit 709fb3
  if (dirp)
Packit 709fb3
    *pdir_fd = new_fd;
Packit 709fb3
  else
Packit 709fb3
    {
Packit 709fb3
      int saved_errno = errno;
Packit 709fb3
      close (new_fd);
Packit 709fb3
      errno = saved_errno;
Packit 709fb3
    }
Packit 709fb3
  return dirp;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Virtual fchdir.  Advance SP's working directory file descriptor,
Packit 709fb3
   SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
Packit 709fb3
   CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
Packit 709fb3
   open on sp->fts_cwd_fd; i.e., to move the working directory one level
Packit 709fb3
   down.  */
Packit 709fb3
static void
Packit 709fb3
internal_function
Packit 709fb3
cwd_advance_fd (FTS *sp, int fd, bool chdir_down_one)
Packit 709fb3
{
Packit 709fb3
  int old = sp->fts_cwd_fd;
Packit 709fb3
  fts_assert (old != fd || old == AT_FDCWD);
Packit 709fb3
Packit 709fb3
  if (chdir_down_one)
Packit 709fb3
    {
Packit 709fb3
      /* Push "old" onto the ring.
Packit 709fb3
         If the displaced file descriptor is non-negative, close it.  */
Packit 709fb3
      int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
Packit 709fb3
      fd_ring_print (sp, stderr, "post-push");
Packit 709fb3
      if (0 <= prev_fd_in_slot)
Packit 709fb3
        close (prev_fd_in_slot); /* ignore any close failure */
Packit 709fb3
    }
Packit 709fb3
  else if ( ! ISSET (FTS_NOCHDIR))
Packit 709fb3
    {
Packit 709fb3
      if (0 <= old)
Packit 709fb3
        close (old); /* ignore any close failure */
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  sp->fts_cwd_fd = fd;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Restore the initial, pre-traversal, "working directory".
Packit 709fb3
   In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,
Packit 709fb3
   we may actually change the working directory.
Packit 709fb3
   Return 0 upon success. Upon failure, set errno and return nonzero.  */
Packit 709fb3
static int
Packit 709fb3
restore_initial_cwd (FTS *sp)
Packit 709fb3
{
Packit 709fb3
  int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);
Packit 709fb3
  fd_ring_clear (&(sp->fts_fd_ring));
Packit 709fb3
  return fail;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Open the directory DIR if possible, and return a file
Packit 709fb3
   descriptor.  Return -1 and set errno on failure.  It doesn't matter
Packit 709fb3
   whether the file descriptor has read or write access.  */
Packit 709fb3
Packit 709fb3
static int
Packit 709fb3
internal_function
Packit 709fb3
diropen (FTS const *sp, char const *dir)
Packit 709fb3
{
Packit 709fb3
  int open_flags = (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
Packit 709fb3
                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0)
Packit 709fb3
                    | (ISSET (FTS_NOATIME) ? O_NOATIME : 0));
Packit 709fb3
Packit 709fb3
  int fd = (ISSET (FTS_CWDFD)
Packit 709fb3
            ? openat (sp->fts_cwd_fd, dir, open_flags)
Packit 709fb3
            : open (dir, open_flags));
Packit 709fb3
  if (0 <= fd)
Packit 709fb3
    set_cloexec_flag (fd, true);
Packit 709fb3
  return fd;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
FTS *
Packit 709fb3
fts_open (char * const *argv,
Packit 709fb3
          register int options,
Packit 709fb3
          int (*compar) (FTSENT const **, FTSENT const **))
Packit 709fb3
{
Packit 709fb3
        register FTS *sp;
Packit 709fb3
        register FTSENT *p, *root;
Packit 709fb3
        register size_t nitems;
Packit 709fb3
        FTSENT *parent = NULL;
Packit 709fb3
        FTSENT *tmp = NULL;     /* pacify gcc */
Packit 709fb3
        bool defer_stat;
Packit 709fb3
Packit 709fb3
        /* Options check. */
Packit 709fb3
        if (options & ~FTS_OPTIONMASK) {
Packit 709fb3
                __set_errno (EINVAL);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
        if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
Packit 709fb3
                __set_errno (EINVAL);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
        if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
Packit 709fb3
                __set_errno (EINVAL);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Allocate/initialize the stream */
Packit 709fb3
        if ((sp = malloc(sizeof(FTS))) == NULL)
Packit 709fb3
                return (NULL);
Packit 709fb3
        memset(sp, 0, sizeof(FTS));
Packit 709fb3
        sp->fts_compar = compar;
Packit 709fb3
        sp->fts_options = options;
Packit 709fb3
Packit 709fb3
        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
Packit 709fb3
        if (ISSET(FTS_LOGICAL)) {
Packit 709fb3
                SET(FTS_NOCHDIR);
Packit 709fb3
                CLR(FTS_CWDFD);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Initialize fts_cwd_fd.  */
Packit 709fb3
        sp->fts_cwd_fd = AT_FDCWD;
Packit 709fb3
        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
Packit 709fb3
          {
Packit 709fb3
            /* While it isn't technically necessary to open "." this
Packit 709fb3
               early, doing it here saves us the trouble of ensuring
Packit 709fb3
               later (where it'd be messier) that "." can in fact
Packit 709fb3
               be opened.  If not, revert to FTS_NOCHDIR mode.  */
Packit 709fb3
            int fd = open (".",
Packit 709fb3
                           O_SEARCH | (ISSET (FTS_NOATIME) ? O_NOATIME : 0));
Packit 709fb3
            if (fd < 0)
Packit 709fb3
              {
Packit 709fb3
                /* Even if "." is unreadable, don't revert to FTS_NOCHDIR mode
Packit 709fb3
                   on systems like Linux+PROC_FS, where our openat emulation
Packit 709fb3
                   is good enough.  Note: on a system that emulates
Packit 709fb3
                   openat via /proc, this technique can still fail, but
Packit 709fb3
                   only in extreme conditions, e.g., when the working
Packit 709fb3
                   directory cannot be saved (i.e. save_cwd fails) --
Packit 709fb3
                   and that happens on Linux only when "." is unreadable
Packit 709fb3
                   and the CWD would be longer than PATH_MAX.
Packit 709fb3
                   FIXME: once Linux kernel openat support is well established,
Packit 709fb3
                   replace the above open call and this entire if/else block
Packit 709fb3
                   with the body of the if-block below.  */
Packit 709fb3
                if ( openat_needs_fchdir ())
Packit 709fb3
                  {
Packit 709fb3
                    SET(FTS_NOCHDIR);
Packit 709fb3
                    CLR(FTS_CWDFD);
Packit 709fb3
                  }
Packit 709fb3
              }
Packit 709fb3
            else
Packit 709fb3
              {
Packit 709fb3
                close (fd);
Packit 709fb3
              }
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Start out with 1K of file name space, and enough, in any case,
Packit 709fb3
         * to hold the user's file names.
Packit 709fb3
         */
Packit 709fb3
#ifndef MAXPATHLEN
Packit 709fb3
# define MAXPATHLEN 1024
Packit 709fb3
#endif
Packit 709fb3
        {
Packit 709fb3
          size_t maxarglen = fts_maxarglen(argv);
Packit 709fb3
          if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
Packit 709fb3
                  goto mem1;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Allocate/initialize root's parent. */
Packit 709fb3
        if (*argv != NULL) {
Packit 709fb3
                if ((parent = fts_alloc(sp, "", 0)) == NULL)
Packit 709fb3
                        goto mem2;
Packit 709fb3
                parent->fts_level = FTS_ROOTPARENTLEVEL;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        /* The classic fts implementation would call fts_stat with
Packit 709fb3
           a new entry for each iteration of the loop below.
Packit 709fb3
           If the comparison function is not specified or if the
Packit 709fb3
           FTS_DEFER_STAT option is in effect, don't stat any entry
Packit 709fb3
           in this loop.  This is an attempt to minimize the interval
Packit 709fb3
           between the initial stat/lstat/fstatat and the point at which
Packit 709fb3
           a directory argument is first opened.  This matters for any
Packit 709fb3
           directory command line argument that resides on a file system
Packit 709fb3
           without genuine i-nodes.  If you specify FTS_DEFER_STAT along
Packit 709fb3
           with a comparison function, that function must not access any
Packit 709fb3
           data via the fts_statp pointer.  */
Packit 709fb3
        defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
Packit 709fb3
Packit 709fb3
        /* Allocate/initialize root(s). */
Packit 709fb3
        for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
Packit 709fb3
                /* *Do* allow zero-length file names. */
Packit 709fb3
                size_t len = strlen(*argv);
Packit 709fb3
Packit 709fb3
                if ( ! (options & FTS_VERBATIM))
Packit 709fb3
                  {
Packit 709fb3
                    /* If there are two or more trailing slashes, trim all but one,
Packit 709fb3
                       but don't change "//" to "/", and do map "///" to "/".  */
Packit 709fb3
                    char const *v = *argv;
Packit 709fb3
                    if (2 < len && v[len - 1] == '/')
Packit 709fb3
                      while (1 < len && v[len - 2] == '/')
Packit 709fb3
                        --len;
Packit 709fb3
                  }
Packit 709fb3
Packit 709fb3
                if ((p = fts_alloc(sp, *argv, len)) == NULL)
Packit 709fb3
                        goto mem3;
Packit 709fb3
                p->fts_level = FTS_ROOTLEVEL;
Packit 709fb3
                p->fts_parent = parent;
Packit 709fb3
                p->fts_accpath = p->fts_name;
Packit 709fb3
                /* Even when defer_stat is true, be sure to stat the first
Packit 709fb3
                   command line argument, since fts_read (at least with
Packit 709fb3
                   FTS_XDEV) requires that.  */
Packit 709fb3
                if (defer_stat && root != NULL) {
Packit 709fb3
                        p->fts_info = FTS_NSOK;
Packit 709fb3
                        fts_set_stat_required(p, true);
Packit 709fb3
                } else {
Packit 709fb3
                        p->fts_info = fts_stat(sp, p, false);
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                /*
Packit 709fb3
                 * If comparison routine supplied, traverse in sorted
Packit 709fb3
                 * order; otherwise traverse in the order specified.
Packit 709fb3
                 */
Packit 709fb3
                if (compar) {
Packit 709fb3
                        p->fts_link = root;
Packit 709fb3
                        root = p;
Packit 709fb3
                } else {
Packit 709fb3
                        p->fts_link = NULL;
Packit 709fb3
                        if (root == NULL)
Packit 709fb3
                                tmp = root = p;
Packit 709fb3
                        else {
Packit 709fb3
                                tmp->fts_link = p;
Packit 709fb3
                                tmp = p;
Packit 709fb3
                        }
Packit 709fb3
                }
Packit 709fb3
        }
Packit 709fb3
        if (compar && nitems > 1)
Packit 709fb3
                root = fts_sort(sp, root, nitems);
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Allocate a dummy pointer and make fts_read think that we've just
Packit 709fb3
         * finished the node before the root(s); set p->fts_info to FTS_INIT
Packit 709fb3
         * so that everything about the "current" node is ignored.
Packit 709fb3
         */
Packit 709fb3
        if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
Packit 709fb3
                goto mem3;
Packit 709fb3
        sp->fts_cur->fts_link = root;
Packit 709fb3
        sp->fts_cur->fts_info = FTS_INIT;
Packit 709fb3
        if (! setup_dir (sp))
Packit 709fb3
                goto mem3;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If using chdir(2), grab a file descriptor pointing to dot to ensure
Packit 709fb3
         * that we can get back here; this could be avoided for some file names,
Packit 709fb3
         * but almost certainly not worth the effort.  Slashes, symbolic links,
Packit 709fb3
         * and ".." are all fairly nasty problems.  Note, if we can't get the
Packit 709fb3
         * descriptor we run anyway, just more slowly.
Packit 709fb3
         */
Packit 709fb3
        if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
Packit 709fb3
            && (sp->fts_rfd = diropen (sp, ".")) < 0)
Packit 709fb3
                SET(FTS_NOCHDIR);
Packit 709fb3
Packit 709fb3
        i_ring_init (&sp->fts_fd_ring, -1);
Packit 709fb3
        return (sp);
Packit 709fb3
Packit 709fb3
mem3:   fts_lfree(root);
Packit 709fb3
        free(parent);
Packit 709fb3
mem2:   free(sp->fts_path);
Packit 709fb3
mem1:   free(sp);
Packit 709fb3
        return (NULL);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
internal_function
Packit 709fb3
fts_load (FTS *sp, register FTSENT *p)
Packit 709fb3
{
Packit 709fb3
        register size_t len;
Packit 709fb3
        register char *cp;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Load the stream structure for the next traversal.  Since we don't
Packit 709fb3
         * actually enter the directory until after the preorder visit, set
Packit 709fb3
         * the fts_accpath field specially so the chdir gets done to the right
Packit 709fb3
         * place and the user can access the first node.  From fts_open it's
Packit 709fb3
         * known that the file name will fit.
Packit 709fb3
         */
Packit 709fb3
        len = p->fts_pathlen = p->fts_namelen;
Packit 709fb3
        memmove(sp->fts_path, p->fts_name, len + 1);
Packit 709fb3
        if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
Packit 709fb3
                len = strlen(++cp);
Packit 709fb3
                memmove(p->fts_name, cp, len + 1);
Packit 709fb3
                p->fts_namelen = len;
Packit 709fb3
        }
Packit 709fb3
        p->fts_accpath = p->fts_path = sp->fts_path;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
int
Packit 709fb3
fts_close (FTS *sp)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *freep, *p;
Packit 709fb3
        int saved_errno = 0;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * This still works if we haven't read anything -- the dummy structure
Packit 709fb3
         * points to the root list, so we step through to the end of the root
Packit 709fb3
         * list which has a valid parent pointer.
Packit 709fb3
         */
Packit 709fb3
        if (sp->fts_cur) {
Packit 709fb3
                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
Packit 709fb3
                        freep = p;
Packit 709fb3
                        p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
Packit 709fb3
                        free(freep);
Packit 709fb3
                }
Packit 709fb3
                free(p);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Free up child linked list, sort array, file name buffer. */
Packit 709fb3
        if (sp->fts_child)
Packit 709fb3
                fts_lfree(sp->fts_child);
Packit 709fb3
        free(sp->fts_array);
Packit 709fb3
        free(sp->fts_path);
Packit 709fb3
Packit 709fb3
        if (ISSET(FTS_CWDFD))
Packit 709fb3
          {
Packit 709fb3
            if (0 <= sp->fts_cwd_fd)
Packit 709fb3
              if (close (sp->fts_cwd_fd))
Packit 709fb3
                saved_errno = errno;
Packit 709fb3
          }
Packit 709fb3
        else if (!ISSET(FTS_NOCHDIR))
Packit 709fb3
          {
Packit 709fb3
            /* Return to original directory, save errno if necessary. */
Packit 709fb3
            if (fchdir(sp->fts_rfd))
Packit 709fb3
              saved_errno = errno;
Packit 709fb3
Packit 709fb3
            /* If close fails, record errno only if saved_errno is zero,
Packit 709fb3
               so that we report the probably-more-meaningful fchdir errno.  */
Packit 709fb3
            if (close (sp->fts_rfd))
Packit 709fb3
              if (saved_errno == 0)
Packit 709fb3
                saved_errno = errno;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        fd_ring_clear (&sp->fts_fd_ring);
Packit 709fb3
Packit 709fb3
        if (sp->fts_leaf_optimization_works_ht)
Packit 709fb3
          hash_free (sp->fts_leaf_optimization_works_ht);
Packit 709fb3
Packit 709fb3
        free_dir (sp);
Packit 709fb3
Packit 709fb3
        /* Free up the stream pointer. */
Packit 709fb3
        free(sp);
Packit 709fb3
Packit 709fb3
        /* Set errno and return. */
Packit 709fb3
        if (saved_errno) {
Packit 709fb3
                __set_errno (saved_errno);
Packit 709fb3
                return (-1);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        return (0);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
#if defined __linux__ \
Packit 709fb3
  && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
Packit 709fb3
Packit 709fb3
# include <sys/vfs.h>
Packit 709fb3
Packit 709fb3
/* Linux-specific constants from coreutils' src/fs.h */
Packit 709fb3
# define S_MAGIC_TMPFS 0x1021994
Packit 709fb3
# define S_MAGIC_NFS 0x6969
Packit 709fb3
# define S_MAGIC_REISERFS 0x52654973
Packit 709fb3
# define S_MAGIC_XFS 0x58465342
Packit 709fb3
# define S_MAGIC_PROC 0x9FA0
Packit 709fb3
Packit 709fb3
/* Return false if it is easy to determine the file system type of
Packit 709fb3
   the directory on which DIR_FD is open, and sorting dirents on
Packit 709fb3
   inode numbers is known not to improve traversal performance with
Packit 709fb3
   that type of file system.  Otherwise, return true.  */
Packit 709fb3
static bool
Packit 709fb3
dirent_inode_sort_may_be_useful (int dir_fd)
Packit 709fb3
{
Packit 709fb3
  /* Skip the sort only if we can determine efficiently
Packit 709fb3
     that skipping it is the right thing to do.
Packit 709fb3
     The cost of performing an unnecessary sort is negligible,
Packit 709fb3
     while the cost of *not* performing it can be O(N^2) with
Packit 709fb3
     a very large constant.  */
Packit 709fb3
  struct statfs fs_buf;
Packit 709fb3
Packit 709fb3
  /* If fstatfs fails, assume sorting would be useful.  */
Packit 709fb3
  if (fstatfs (dir_fd, &fs_buf) != 0)
Packit 709fb3
    return true;
Packit 709fb3
Packit 709fb3
  /* FIXME: what about when f_type is not an integral type?
Packit 709fb3
     deal with that if/when it's encountered.  */
Packit 709fb3
  switch (fs_buf.f_type)
Packit 709fb3
    {
Packit 709fb3
    case S_MAGIC_TMPFS:
Packit 709fb3
    case S_MAGIC_NFS:
Packit 709fb3
      /* On a file system of any of these types, sorting
Packit 709fb3
         is unnecessary, and hence wasteful.  */
Packit 709fb3
      return false;
Packit 709fb3
Packit 709fb3
    default:
Packit 709fb3
      return true;
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Given a file descriptor DIR_FD open on a directory D,
Packit 709fb3
   return true if it is valid to apply the leaf-optimization
Packit 709fb3
   technique of counting directories in D via stat.st_nlink.  */
Packit 709fb3
static bool
Packit 709fb3
leaf_optimization_applies (int dir_fd)
Packit 709fb3
{
Packit 709fb3
  struct statfs fs_buf;
Packit 709fb3
Packit 709fb3
  /* If fstatfs fails, assume we can't use the optimization.  */
Packit 709fb3
  if (fstatfs (dir_fd, &fs_buf) != 0)
Packit 709fb3
    return false;
Packit 709fb3
Packit 709fb3
  /* FIXME: do we need to detect AFS mount points?  I doubt it,
Packit 709fb3
     unless fstatfs can report S_MAGIC_REISERFS for such a directory.  */
Packit 709fb3
Packit 709fb3
  switch (fs_buf.f_type)
Packit 709fb3
    {
Packit 709fb3
      /* List here the file system types that lack usable dirent.d_type
Packit 709fb3
         info, yet for which the optimization does apply.  */
Packit 709fb3
    case S_MAGIC_REISERFS:
Packit 709fb3
    case S_MAGIC_XFS:
Packit 709fb3
      return true;
Packit 709fb3
Packit 709fb3
      /* Explicitly list here any other file system type for which the
Packit 709fb3
         optimization is not applicable, but need documentation.  */
Packit 709fb3
    case S_MAGIC_NFS:
Packit 709fb3
      /* NFS provides usable dirent.d_type but not necessarily for all entries
Packit 709fb3
         of large directories, so as per <https://bugzilla.redhat.com/1252549>
Packit 709fb3
         NFS should return true.  However st_nlink values are not accurate on
Packit 709fb3
         all implementations as per <https://bugzilla.redhat.com/1299169>.  */
Packit 709fb3
      FALLTHROUGH;
Packit 709fb3
    case S_MAGIC_PROC:
Packit 709fb3
      /* Per <http://bugs.debian.org/143111> /proc may have
Packit 709fb3
         bogus stat.st_nlink values.  */
Packit 709fb3
      FALLTHROUGH;
Packit 709fb3
    default:
Packit 709fb3
      return false;
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
#else
Packit 709fb3
static bool
Packit 709fb3
dirent_inode_sort_may_be_useful (int dir_fd _GL_UNUSED) { return true; }
Packit 709fb3
static bool
Packit 709fb3
leaf_optimization_applies (int dir_fd _GL_UNUSED) { return false; }
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
/* link-count-optimization entry:
Packit 709fb3
   map a stat.st_dev number to a boolean: leaf_optimization_works */
Packit 709fb3
struct LCO_ent
Packit 709fb3
{
Packit 709fb3
  dev_t st_dev;
Packit 709fb3
  bool opt_ok;
Packit 709fb3
};
Packit 709fb3
Packit 709fb3
/* Use a tiny initial size.  If a traversal encounters more than
Packit 709fb3
   a few devices, the cost of growing/rehashing this table will be
Packit 709fb3
   rendered negligible by the number of inodes processed.  */
Packit 709fb3
enum { LCO_HT_INITIAL_SIZE = 13 };
Packit 709fb3
Packit 709fb3
static size_t
Packit 709fb3
LCO_hash (void const *x, size_t table_size)
Packit 709fb3
{
Packit 709fb3
  struct LCO_ent const *ax = x;
Packit 709fb3
  return (uintmax_t) ax->st_dev % table_size;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
LCO_compare (void const *x, void const *y)
Packit 709fb3
{
Packit 709fb3
  struct LCO_ent const *ax = x;
Packit 709fb3
  struct LCO_ent const *ay = y;
Packit 709fb3
  return ax->st_dev == ay->st_dev;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Ask the same question as leaf_optimization_applies, but query
Packit 709fb3
   the cache first (FTS.fts_leaf_optimization_works_ht), and if necessary,
Packit 709fb3
   update that cache.  */
Packit 709fb3
static bool
Packit 709fb3
link_count_optimize_ok (FTSENT const *p)
Packit 709fb3
{
Packit 709fb3
  FTS *sp = p->fts_fts;
Packit 709fb3
  Hash_table *h = sp->fts_leaf_optimization_works_ht;
Packit 709fb3
  struct LCO_ent tmp;
Packit 709fb3
  struct LCO_ent *ent;
Packit 709fb3
  bool opt_ok;
Packit 709fb3
  struct LCO_ent *t2;
Packit 709fb3
Packit 709fb3
  /* If we're not in CWDFD mode, don't bother with this optimization,
Packit 709fb3
     since the caller is not serious about performance. */
Packit 709fb3
  if (!ISSET(FTS_CWDFD))
Packit 709fb3
    return false;
Packit 709fb3
Packit 709fb3
  /* map st_dev to the boolean, leaf_optimization_works */
Packit 709fb3
  if (h == NULL)
Packit 709fb3
    {
Packit 709fb3
      h = sp->fts_leaf_optimization_works_ht
Packit 709fb3
        = hash_initialize (LCO_HT_INITIAL_SIZE, NULL, LCO_hash,
Packit 709fb3
                           LCO_compare, free);
Packit 709fb3
      if (h == NULL)
Packit 709fb3
        return false;
Packit 709fb3
    }
Packit 709fb3
  tmp.st_dev = p->fts_statp->st_dev;
Packit 709fb3
  ent = hash_lookup (h, &tmp);
Packit 709fb3
  if (ent)
Packit 709fb3
    return ent->opt_ok;
Packit 709fb3
Packit 709fb3
  /* Look-up failed.  Query directly and cache the result.  */
Packit 709fb3
  t2 = malloc (sizeof *t2);
Packit 709fb3
  if (t2 == NULL)
Packit 709fb3
    return false;
Packit 709fb3
Packit 709fb3
  /* Is it ok to perform the optimization in the dir, FTS_CWD_FD?  */
Packit 709fb3
  opt_ok = leaf_optimization_applies (sp->fts_cwd_fd);
Packit 709fb3
  t2->opt_ok = opt_ok;
Packit 709fb3
  t2->st_dev = p->fts_statp->st_dev;
Packit 709fb3
Packit 709fb3
  ent = hash_insert (h, t2);
Packit 709fb3
  if (ent == NULL)
Packit 709fb3
    {
Packit 709fb3
      /* insertion failed */
Packit 709fb3
      free (t2);
Packit 709fb3
      return false;
Packit 709fb3
    }
Packit 709fb3
  fts_assert (ent == t2);
Packit 709fb3
Packit 709fb3
  return opt_ok;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * Special case of "/" at the end of the file name so that slashes aren't
Packit 709fb3
 * appended which would cause file names to be written as "....//foo".
Packit 709fb3
 */
Packit 709fb3
#define NAPPEND(p)                                                      \
Packit 709fb3
        (p->fts_path[p->fts_pathlen - 1] == '/'                         \
Packit 709fb3
            ? p->fts_pathlen - 1 : p->fts_pathlen)
Packit 709fb3
Packit 709fb3
FTSENT *
Packit 709fb3
fts_read (register FTS *sp)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *p, *tmp;
Packit 709fb3
        register unsigned short int instr;
Packit 709fb3
        register char *t;
Packit 709fb3
Packit 709fb3
        /* If finished or unrecoverable error, return NULL. */
Packit 709fb3
        if (sp->fts_cur == NULL || ISSET(FTS_STOP))
Packit 709fb3
                return (NULL);
Packit 709fb3
Packit 709fb3
        /* Set current node pointer. */
Packit 709fb3
        p = sp->fts_cur;
Packit 709fb3
Packit 709fb3
        /* Save and zero out user instructions. */
Packit 709fb3
        instr = p->fts_instr;
Packit 709fb3
        p->fts_instr = FTS_NOINSTR;
Packit 709fb3
Packit 709fb3
        /* Any type of file may be re-visited; re-stat and re-turn. */
Packit 709fb3
        if (instr == FTS_AGAIN) {
Packit 709fb3
                p->fts_info = fts_stat(sp, p, false);
Packit 709fb3
                return (p);
Packit 709fb3
        }
Packit 709fb3
        Dprintf (("fts_read: p=%s\n",
Packit 709fb3
                  p->fts_info == FTS_INIT ? "" : p->fts_path));
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Following a symlink -- SLNONE test allows application to see
Packit 709fb3
         * SLNONE and recover.  If indirecting through a symlink, have
Packit 709fb3
         * keep a pointer to current location.  If unable to get that
Packit 709fb3
         * pointer, follow fails.
Packit 709fb3
         */
Packit 709fb3
        if (instr == FTS_FOLLOW &&
Packit 709fb3
            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
Packit 709fb3
                p->fts_info = fts_stat(sp, p, true);
Packit 709fb3
                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
Packit 709fb3
                        if ((p->fts_symfd = diropen (sp, ".")) < 0) {
Packit 709fb3
                                p->fts_errno = errno;
Packit 709fb3
                                p->fts_info = FTS_ERR;
Packit 709fb3
                        } else
Packit 709fb3
                                p->fts_flags |= FTS_SYMFOLLOW;
Packit 709fb3
                }
Packit 709fb3
                goto check_for_dir;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Directory in pre-order. */
Packit 709fb3
        if (p->fts_info == FTS_D) {
Packit 709fb3
                /* If skipped or crossed mount point, do post-order visit. */
Packit 709fb3
                if (instr == FTS_SKIP ||
Packit 709fb3
                    (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
Packit 709fb3
                        if (p->fts_flags & FTS_SYMFOLLOW)
Packit 709fb3
                                (void)close(p->fts_symfd);
Packit 709fb3
                        if (sp->fts_child) {
Packit 709fb3
                                fts_lfree(sp->fts_child);
Packit 709fb3
                                sp->fts_child = NULL;
Packit 709fb3
                        }
Packit 709fb3
                        p->fts_info = FTS_DP;
Packit 709fb3
                        LEAVE_DIR (sp, p, "1");
Packit 709fb3
                        return (p);
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                /* Rebuild if only read the names and now traversing. */
Packit 709fb3
                if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
Packit 709fb3
                        CLR(FTS_NAMEONLY);
Packit 709fb3
                        fts_lfree(sp->fts_child);
Packit 709fb3
                        sp->fts_child = NULL;
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                /*
Packit 709fb3
                 * Cd to the subdirectory.
Packit 709fb3
                 *
Packit 709fb3
                 * If have already read and now fail to chdir, whack the list
Packit 709fb3
                 * to make the names come out right, and set the parent errno
Packit 709fb3
                 * so the application will eventually get an error condition.
Packit 709fb3
                 * Set the FTS_DONTCHDIR flag so that when we logically change
Packit 709fb3
                 * directories back to the parent we don't do a chdir.
Packit 709fb3
                 *
Packit 709fb3
                 * If haven't read do so.  If the read fails, fts_build sets
Packit 709fb3
                 * FTS_STOP or the fts_info field of the node.
Packit 709fb3
                 */
Packit 709fb3
                if (sp->fts_child != NULL) {
Packit 709fb3
                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
Packit 709fb3
                                p->fts_errno = errno;
Packit 709fb3
                                p->fts_flags |= FTS_DONTCHDIR;
Packit 709fb3
                                for (p = sp->fts_child; p != NULL;
Packit 709fb3
                                     p = p->fts_link)
Packit 709fb3
                                        p->fts_accpath =
Packit 709fb3
                                            p->fts_parent->fts_accpath;
Packit 709fb3
                        }
Packit 709fb3
                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
Packit 709fb3
                        if (ISSET(FTS_STOP))
Packit 709fb3
                                return (NULL);
Packit 709fb3
                        /* If fts_build's call to fts_safe_changedir failed
Packit 709fb3
                           because it was not able to fchdir into a
Packit 709fb3
                           subdirectory, tell the caller.  */
Packit 709fb3
                        if (p->fts_errno && p->fts_info != FTS_DNR)
Packit 709fb3
                                p->fts_info = FTS_ERR;
Packit 709fb3
                        LEAVE_DIR (sp, p, "2");
Packit 709fb3
                        return (p);
Packit 709fb3
                }
Packit 709fb3
                p = sp->fts_child;
Packit 709fb3
                sp->fts_child = NULL;
Packit 709fb3
                goto name;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Move to the next node on this level. */
Packit 709fb3
next:   tmp = p;
Packit 709fb3
Packit 709fb3
        /* If we have so many directory entries that we're reading them
Packit 709fb3
           in batches, and we've reached the end of the current batch,
Packit 709fb3
           read in a new batch.  */
Packit 709fb3
        if (p->fts_link == NULL && p->fts_parent->fts_dirp)
Packit 709fb3
          {
Packit 709fb3
            p = tmp->fts_parent;
Packit 709fb3
            sp->fts_cur = p;
Packit 709fb3
            sp->fts_path[p->fts_pathlen] = '\0';
Packit 709fb3
Packit 709fb3
            if ((p = fts_build (sp, BREAD)) == NULL)
Packit 709fb3
              {
Packit 709fb3
                if (ISSET(FTS_STOP))
Packit 709fb3
                  return NULL;
Packit 709fb3
                goto cd_dot_dot;
Packit 709fb3
              }
Packit 709fb3
Packit 709fb3
            free(tmp);
Packit 709fb3
            goto name;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        if ((p = p->fts_link) != NULL) {
Packit 709fb3
                sp->fts_cur = p;
Packit 709fb3
                free(tmp);
Packit 709fb3
Packit 709fb3
                /*
Packit 709fb3
                 * If reached the top, return to the original directory (or
Packit 709fb3
                 * the root of the tree), and load the file names for the next
Packit 709fb3
                 * root.
Packit 709fb3
                 */
Packit 709fb3
                if (p->fts_level == FTS_ROOTLEVEL) {
Packit 709fb3
                        if (restore_initial_cwd(sp)) {
Packit 709fb3
                                SET(FTS_STOP);
Packit 709fb3
                                return (NULL);
Packit 709fb3
                        }
Packit 709fb3
                        free_dir(sp);
Packit 709fb3
                        fts_load(sp, p);
Packit 709fb3
                        setup_dir(sp);
Packit 709fb3
                        goto check_for_dir;
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                /*
Packit 709fb3
                 * User may have called fts_set on the node.  If skipped,
Packit 709fb3
                 * ignore.  If followed, get a file descriptor so we can
Packit 709fb3
                 * get back if necessary.
Packit 709fb3
                 */
Packit 709fb3
                if (p->fts_instr == FTS_SKIP)
Packit 709fb3
                        goto next;
Packit 709fb3
                if (p->fts_instr == FTS_FOLLOW) {
Packit 709fb3
                        p->fts_info = fts_stat(sp, p, true);
Packit 709fb3
                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
Packit 709fb3
                                if ((p->fts_symfd = diropen (sp, ".")) < 0) {
Packit 709fb3
                                        p->fts_errno = errno;
Packit 709fb3
                                        p->fts_info = FTS_ERR;
Packit 709fb3
                                } else
Packit 709fb3
                                        p->fts_flags |= FTS_SYMFOLLOW;
Packit 709fb3
                        }
Packit 709fb3
                        p->fts_instr = FTS_NOINSTR;
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
name:           t = sp->fts_path + NAPPEND(p->fts_parent);
Packit 709fb3
                *t++ = '/';
Packit 709fb3
                memmove(t, p->fts_name, p->fts_namelen + 1);
Packit 709fb3
check_for_dir:
Packit 709fb3
                sp->fts_cur = p;
Packit 709fb3
                if (p->fts_info == FTS_NSOK)
Packit 709fb3
                  {
Packit 709fb3
                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
Packit 709fb3
                      {
Packit 709fb3
                        FTSENT *parent = p->fts_parent;
Packit 709fb3
                        if (FTS_ROOTLEVEL < p->fts_level
Packit 709fb3
                            /* ->fts_n_dirs_remaining is not valid
Packit 709fb3
                               for command-line-specified names.  */
Packit 709fb3
                            && parent->fts_n_dirs_remaining == 0
Packit 709fb3
                            && ISSET(FTS_NOSTAT)
Packit 709fb3
                            && ISSET(FTS_PHYSICAL)
Packit 709fb3
                            && link_count_optimize_ok (parent))
Packit 709fb3
                          {
Packit 709fb3
                            /* nothing more needed */
Packit 709fb3
                          }
Packit 709fb3
                        else
Packit 709fb3
                          {
Packit 709fb3
                            p->fts_info = fts_stat(sp, p, false);
Packit 709fb3
                            if (S_ISDIR(p->fts_statp->st_mode)
Packit 709fb3
                                && p->fts_level != FTS_ROOTLEVEL
Packit 709fb3
                                && parent->fts_n_dirs_remaining)
Packit 709fb3
                                  parent->fts_n_dirs_remaining--;
Packit 709fb3
                          }
Packit 709fb3
                      }
Packit 709fb3
                    else
Packit 709fb3
                      fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
Packit 709fb3
                  }
Packit 709fb3
Packit 709fb3
                if (p->fts_info == FTS_D)
Packit 709fb3
                  {
Packit 709fb3
                    /* Now that P->fts_statp is guaranteed to be valid,
Packit 709fb3
                       if this is a command-line directory, record its
Packit 709fb3
                       device number, to be used for FTS_XDEV.  */
Packit 709fb3
                    if (p->fts_level == FTS_ROOTLEVEL)
Packit 709fb3
                      sp->fts_dev = p->fts_statp->st_dev;
Packit 709fb3
                    Dprintf (("  entering: %s\n", p->fts_path));
Packit 709fb3
                    if (! enter_dir (sp, p))
Packit 709fb3
                      {
Packit 709fb3
                        __set_errno (ENOMEM);
Packit 709fb3
                        return NULL;
Packit 709fb3
                      }
Packit 709fb3
                  }
Packit 709fb3
                return p;
Packit 709fb3
        }
Packit 709fb3
cd_dot_dot:
Packit 709fb3
Packit 709fb3
        /* Move up to the parent node. */
Packit 709fb3
        p = tmp->fts_parent;
Packit 709fb3
        sp->fts_cur = p;
Packit 709fb3
        free(tmp);
Packit 709fb3
Packit 709fb3
        if (p->fts_level == FTS_ROOTPARENTLEVEL) {
Packit 709fb3
                /*
Packit 709fb3
                 * Done; free everything up and set errno to 0 so the user
Packit 709fb3
                 * can distinguish between error and EOF.
Packit 709fb3
                 */
Packit 709fb3
                free(p);
Packit 709fb3
                __set_errno (0);
Packit 709fb3
                return (sp->fts_cur = NULL);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        fts_assert (p->fts_info != FTS_NSOK);
Packit 709fb3
Packit 709fb3
        /* NUL terminate the file name.  */
Packit 709fb3
        sp->fts_path[p->fts_pathlen] = '\0';
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Return to the parent directory.  If at a root node, restore
Packit 709fb3
         * the initial working directory.  If we came through a symlink,
Packit 709fb3
         * go back through the file descriptor.  Otherwise, move up
Packit 709fb3
         * one level, via "..".
Packit 709fb3
         */
Packit 709fb3
        if (p->fts_level == FTS_ROOTLEVEL) {
Packit 709fb3
                if (restore_initial_cwd(sp)) {
Packit 709fb3
                        p->fts_errno = errno;
Packit 709fb3
                        SET(FTS_STOP);
Packit 709fb3
                }
Packit 709fb3
        } else if (p->fts_flags & FTS_SYMFOLLOW) {
Packit 709fb3
                if (FCHDIR(sp, p->fts_symfd)) {
Packit 709fb3
                        p->fts_errno = errno;
Packit 709fb3
                        SET(FTS_STOP);
Packit 709fb3
                }
Packit 709fb3
                (void)close(p->fts_symfd);
Packit 709fb3
        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
Packit 709fb3
                   fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
Packit 709fb3
                p->fts_errno = errno;
Packit 709fb3
                SET(FTS_STOP);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* If the directory causes a cycle, preserve the FTS_DC flag and keep
Packit 709fb3
           the corresponding dev/ino pair in the hash table.  It is going to be
Packit 709fb3
           removed when leaving the original directory.  */
Packit 709fb3
        if (p->fts_info != FTS_DC) {
Packit 709fb3
                p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
Packit 709fb3
                if (p->fts_errno == 0)
Packit 709fb3
                        LEAVE_DIR (sp, p, "3");
Packit 709fb3
        }
Packit 709fb3
        return ISSET(FTS_STOP) ? NULL : p;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * Fts_set takes the stream as an argument although it's not used in this
Packit 709fb3
 * implementation; it would be necessary if anyone wanted to add global
Packit 709fb3
 * semantics to fts using fts_set.  An error return is allowed for similar
Packit 709fb3
 * reasons.
Packit 709fb3
 */
Packit 709fb3
/* ARGSUSED */
Packit 709fb3
int
Packit 709fb3
fts_set(FTS *sp _GL_UNUSED, FTSENT *p, int instr)
Packit 709fb3
{
Packit 709fb3
        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
Packit 709fb3
            instr != FTS_NOINSTR && instr != FTS_SKIP) {
Packit 709fb3
                __set_errno (EINVAL);
Packit 709fb3
                return (1);
Packit 709fb3
        }
Packit 709fb3
        p->fts_instr = instr;
Packit 709fb3
        return (0);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
FTSENT *
Packit 709fb3
fts_children (register FTS *sp, int instr)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *p;
Packit 709fb3
        int fd;
Packit 709fb3
Packit 709fb3
        if (instr != 0 && instr != FTS_NAMEONLY) {
Packit 709fb3
                __set_errno (EINVAL);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Set current node pointer. */
Packit 709fb3
        p = sp->fts_cur;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Errno set to 0 so user can distinguish empty directory from
Packit 709fb3
         * an error.
Packit 709fb3
         */
Packit 709fb3
        __set_errno (0);
Packit 709fb3
Packit 709fb3
        /* Fatal errors stop here. */
Packit 709fb3
        if (ISSET(FTS_STOP))
Packit 709fb3
                return (NULL);
Packit 709fb3
Packit 709fb3
        /* Return logical hierarchy of user's arguments. */
Packit 709fb3
        if (p->fts_info == FTS_INIT)
Packit 709fb3
                return (p->fts_link);
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If not a directory being visited in pre-order, stop here.  Could
Packit 709fb3
         * allow FTS_DNR, assuming the user has fixed the problem, but the
Packit 709fb3
         * same effect is available with FTS_AGAIN.
Packit 709fb3
         */
Packit 709fb3
        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
Packit 709fb3
                return (NULL);
Packit 709fb3
Packit 709fb3
        /* Free up any previous child list. */
Packit 709fb3
        if (sp->fts_child != NULL)
Packit 709fb3
                fts_lfree(sp->fts_child);
Packit 709fb3
Packit 709fb3
        if (instr == FTS_NAMEONLY) {
Packit 709fb3
                SET(FTS_NAMEONLY);
Packit 709fb3
                instr = BNAMES;
Packit 709fb3
        } else
Packit 709fb3
                instr = BCHILD;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If using chdir on a relative file name and called BEFORE fts_read
Packit 709fb3
         * does its chdir to the root of a traversal, we can lose -- we need to
Packit 709fb3
         * chdir into the subdirectory, and we don't know where the current
Packit 709fb3
         * directory is, so we can't get back so that the upcoming chdir by
Packit 709fb3
         * fts_read will work.
Packit 709fb3
         */
Packit 709fb3
        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
Packit 709fb3
            ISSET(FTS_NOCHDIR))
Packit 709fb3
                return (sp->fts_child = fts_build(sp, instr));
Packit 709fb3
Packit 709fb3
        if ((fd = diropen (sp, ".")) < 0)
Packit 709fb3
                return (sp->fts_child = NULL);
Packit 709fb3
        sp->fts_child = fts_build(sp, instr);
Packit 709fb3
        if (ISSET(FTS_CWDFD))
Packit 709fb3
          {
Packit 709fb3
            cwd_advance_fd (sp, fd, true);
Packit 709fb3
          }
Packit 709fb3
        else
Packit 709fb3
          {
Packit 709fb3
            if (fchdir(fd))
Packit 709fb3
              {
Packit 709fb3
                int saved_errno = errno;
Packit 709fb3
                close (fd);
Packit 709fb3
                __set_errno (saved_errno);
Packit 709fb3
                return NULL;
Packit 709fb3
              }
Packit 709fb3
            close (fd);
Packit 709fb3
          }
Packit 709fb3
        return (sp->fts_child);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* A comparison function to sort on increasing inode number.
Packit 709fb3
   For some file system types, sorting either way makes a huge
Packit 709fb3
   performance difference for a directory with very many entries,
Packit 709fb3
   but sorting on increasing values is slightly better than sorting
Packit 709fb3
   on decreasing values.  The difference is in the 5% range.  */
Packit 709fb3
static int
Packit 709fb3
fts_compare_ino (struct _ftsent const **a, struct _ftsent const **b)
Packit 709fb3
{
Packit 709fb3
  return (a[0]->fts_statp->st_ino < b[0]->fts_statp->st_ino ? -1
Packit 709fb3
          : b[0]->fts_statp->st_ino < a[0]->fts_statp->st_ino ? 1 : 0);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
Packit 709fb3
   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */
Packit 709fb3
static void
Packit 709fb3
set_stat_type (struct stat *st, unsigned int dtype)
Packit 709fb3
{
Packit 709fb3
  mode_t type;
Packit 709fb3
  switch (dtype)
Packit 709fb3
    {
Packit 709fb3
    case DT_BLK:
Packit 709fb3
      type = S_IFBLK;
Packit 709fb3
      break;
Packit 709fb3
    case DT_CHR:
Packit 709fb3
      type = S_IFCHR;
Packit 709fb3
      break;
Packit 709fb3
    case DT_DIR:
Packit 709fb3
      type = S_IFDIR;
Packit 709fb3
      break;
Packit 709fb3
    case DT_FIFO:
Packit 709fb3
      type = S_IFIFO;
Packit 709fb3
      break;
Packit 709fb3
    case DT_LNK:
Packit 709fb3
      type = S_IFLNK;
Packit 709fb3
      break;
Packit 709fb3
    case DT_REG:
Packit 709fb3
      type = S_IFREG;
Packit 709fb3
      break;
Packit 709fb3
    case DT_SOCK:
Packit 709fb3
      type = S_IFSOCK;
Packit 709fb3
      break;
Packit 709fb3
    default:
Packit 709fb3
      type = 0;
Packit 709fb3
    }
Packit 709fb3
  st->st_mode = type;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
#define closedir_and_clear(dirp)                \
Packit 709fb3
  do                                            \
Packit 709fb3
    {                                           \
Packit 709fb3
      closedir (dirp);                          \
Packit 709fb3
      dirp = NULL;                              \
Packit 709fb3
    }                                           \
Packit 709fb3
  while (0)
Packit 709fb3
Packit 709fb3
#define fts_opendir(file, Pdir_fd)                              \
Packit 709fb3
        opendirat((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \
Packit 709fb3
                   ? sp->fts_cwd_fd : AT_FDCWD),                \
Packit 709fb3
                  file,                                         \
Packit 709fb3
                  (((ISSET(FTS_PHYSICAL)                        \
Packit 709fb3
                     && ! (ISSET(FTS_COMFOLLOW)                 \
Packit 709fb3
                           && cur->fts_level == FTS_ROOTLEVEL)) \
Packit 709fb3
                    ? O_NOFOLLOW : 0)                           \
Packit 709fb3
                   | (ISSET (FTS_NOATIME) ? O_NOATIME : 0)),    \
Packit 709fb3
                  Pdir_fd)
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * This is the tricky part -- do not casually change *anything* in here.  The
Packit 709fb3
 * idea is to build the linked list of entries that are used by fts_children
Packit 709fb3
 * and fts_read.  There are lots of special cases.
Packit 709fb3
 *
Packit 709fb3
 * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
Packit 709fb3
 * set and it's a physical walk (so that symbolic links can't be directories),
Packit 709fb3
 * we can do things quickly.  First, if it's a 4.4BSD file system, the type
Packit 709fb3
 * of the file is in the directory entry.  Otherwise, we assume that the number
Packit 709fb3
 * of subdirectories in a node is equal to the number of links to the parent.
Packit 709fb3
 * The former skips all stat calls.  The latter skips stat calls in any leaf
Packit 709fb3
 * directories and for any files after the subdirectories in the directory have
Packit 709fb3
 * been found, cutting the stat calls by about 2/3.
Packit 709fb3
 */
Packit 709fb3
static FTSENT *
Packit 709fb3
internal_function
Packit 709fb3
fts_build (register FTS *sp, int type)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *p, *head;
Packit 709fb3
        register size_t nitems;
Packit 709fb3
        FTSENT *tail;
Packit 709fb3
        void *oldaddr;
Packit 709fb3
        int saved_errno;
Packit 709fb3
        bool descend;
Packit 709fb3
        bool doadjust;
Packit 709fb3
        ptrdiff_t level;
Packit 709fb3
        nlink_t nlinks;
Packit 709fb3
        bool nostat;
Packit 709fb3
        size_t len, maxlen, new_len;
Packit 709fb3
        char *cp;
Packit 709fb3
        int dir_fd;
Packit 709fb3
        FTSENT *cur = sp->fts_cur;
Packit 709fb3
        bool continue_readdir = !!cur->fts_dirp;
Packit 709fb3
        size_t max_entries;
Packit 709fb3
Packit 709fb3
        /* When cur->fts_dirp is non-NULL, that means we should
Packit 709fb3
           continue calling readdir on that existing DIR* pointer
Packit 709fb3
           rather than opening a new one.  */
Packit 709fb3
        if (continue_readdir)
Packit 709fb3
          {
Packit 709fb3
            DIR *dp = cur->fts_dirp;
Packit 709fb3
            dir_fd = dirfd (dp);
Packit 709fb3
            if (dir_fd < 0)
Packit 709fb3
              {
Packit 709fb3
                closedir_and_clear (cur->fts_dirp);
Packit 709fb3
                if (type == BREAD)
Packit 709fb3
                  {
Packit 709fb3
                    cur->fts_info = FTS_DNR;
Packit 709fb3
                    cur->fts_errno = errno;
Packit 709fb3
                  }
Packit 709fb3
                return NULL;
Packit 709fb3
              }
Packit 709fb3
          }
Packit 709fb3
        else
Packit 709fb3
          {
Packit 709fb3
            /* Open the directory for reading.  If this fails, we're done.
Packit 709fb3
               If being called from fts_read, set the fts_info field. */
Packit 709fb3
            if ((cur->fts_dirp = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)
Packit 709fb3
              {
Packit 709fb3
                if (type == BREAD)
Packit 709fb3
                  {
Packit 709fb3
                    cur->fts_info = FTS_DNR;
Packit 709fb3
                    cur->fts_errno = errno;
Packit 709fb3
                  }
Packit 709fb3
                return NULL;
Packit 709fb3
              }
Packit 709fb3
            /* Rather than calling fts_stat for each and every entry encountered
Packit 709fb3
               in the readdir loop (below), stat each directory only right after
Packit 709fb3
               opening it.  */
Packit 709fb3
            if (cur->fts_info == FTS_NSOK)
Packit 709fb3
              cur->fts_info = fts_stat(sp, cur, false);
Packit 709fb3
            else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK)
Packit 709fb3
              {
Packit 709fb3
                /* Now read the stat info again after opening a directory to
Packit 709fb3
                   reveal eventual changes caused by a submount triggered by
Packit 709fb3
                   the traversal.  But do it only for utilities which use
Packit 709fb3
                   FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du
Packit 709fb3
                   benefit/suffer from this feature for now.  */
Packit 709fb3
                LEAVE_DIR (sp, cur, "4");
Packit 709fb3
                fts_stat (sp, cur, false);
Packit 709fb3
                if (! enter_dir (sp, cur))
Packit 709fb3
                  {
Packit 709fb3
                    __set_errno (ENOMEM);
Packit 709fb3
                    return NULL;
Packit 709fb3
                  }
Packit 709fb3
              }
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        /* Maximum number of readdir entries to read at one time.  This
Packit 709fb3
           limitation is to avoid reading millions of entries into memory
Packit 709fb3
           at once.  When an fts_compar function is specified, we have no
Packit 709fb3
           choice: we must read all entries into memory before calling that
Packit 709fb3
           function.  But when no such function is specified, we can read
Packit 709fb3
           entries in batches that are large enough to help us with inode-
Packit 709fb3
           sorting, yet not so large that we risk exhausting memory.  */
Packit 709fb3
        max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Nlinks is the number of possible entries of type directory in the
Packit 709fb3
         * directory if we're cheating on stat calls, 0 if we're not doing
Packit 709fb3
         * any stat calls at all, (nlink_t) -1 if we're statting everything.
Packit 709fb3
         */
Packit 709fb3
        if (type == BNAMES) {
Packit 709fb3
                nlinks = 0;
Packit 709fb3
                /* Be quiet about nostat, GCC. */
Packit 709fb3
                nostat = false;
Packit 709fb3
        } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
Packit 709fb3
                nlinks = (cur->fts_statp->st_nlink
Packit 709fb3
                          - (ISSET(FTS_SEEDOT) ? 0 : 2));
Packit 709fb3
                nostat = true;
Packit 709fb3
        } else {
Packit 709fb3
                nlinks = -1;
Packit 709fb3
                nostat = false;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If we're going to need to stat anything or we want to descend
Packit 709fb3
         * and stay in the directory, chdir.  If this fails we keep going,
Packit 709fb3
         * but set a flag so we don't chdir after the post-order visit.
Packit 709fb3
         * We won't be able to stat anything, but we can still return the
Packit 709fb3
         * names themselves.  Note, that since fts_read won't be able to
Packit 709fb3
         * chdir into the directory, it will have to return different file
Packit 709fb3
         * names than before, i.e. "a/b" instead of "b".  Since the node
Packit 709fb3
         * has already been visited in pre-order, have to wait until the
Packit 709fb3
         * post-order visit to return the error.  There is a special case
Packit 709fb3
         * here, if there was nothing to stat then it's not an error to
Packit 709fb3
         * not be able to stat.  This is all fairly nasty.  If a program
Packit 709fb3
         * needed sorted entries or stat information, they had better be
Packit 709fb3
         * checking FTS_NS on the returned nodes.
Packit 709fb3
         */
Packit 709fb3
        if (continue_readdir)
Packit 709fb3
          {
Packit 709fb3
            /* When resuming a short readdir run, we already have
Packit 709fb3
               the required dirp and dir_fd.  */
Packit 709fb3
            descend = true;
Packit 709fb3
          }
Packit 709fb3
        else if (nlinks || type == BREAD) {
Packit 709fb3
                if (ISSET(FTS_CWDFD))
Packit 709fb3
                  {
Packit 709fb3
                    dir_fd = dup (dir_fd);
Packit 709fb3
                    if (0 <= dir_fd)
Packit 709fb3
                      set_cloexec_flag (dir_fd, true);
Packit 709fb3
                  }
Packit 709fb3
                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
Packit 709fb3
                        if (nlinks && type == BREAD)
Packit 709fb3
                                cur->fts_errno = errno;
Packit 709fb3
                        cur->fts_flags |= FTS_DONTCHDIR;
Packit 709fb3
                        descend = false;
Packit 709fb3
                        closedir_and_clear(cur->fts_dirp);
Packit 709fb3
                        if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
Packit 709fb3
                                close (dir_fd);
Packit 709fb3
                        cur->fts_dirp = NULL;
Packit 709fb3
                } else
Packit 709fb3
                        descend = true;
Packit 709fb3
        } else
Packit 709fb3
                descend = false;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Figure out the max file name length that can be stored in the
Packit 709fb3
         * current buffer -- the inner loop allocates more space as necessary.
Packit 709fb3
         * We really wouldn't have to do the maxlen calculations here, we
Packit 709fb3
         * could do them in fts_read before returning the name, but it's a
Packit 709fb3
         * lot easier here since the length is part of the dirent structure.
Packit 709fb3
         *
Packit 709fb3
         * If not changing directories set a pointer so that can just append
Packit 709fb3
         * each new component into the file name.
Packit 709fb3
         */
Packit 709fb3
        len = NAPPEND(cur);
Packit 709fb3
        if (ISSET(FTS_NOCHDIR)) {
Packit 709fb3
                cp = sp->fts_path + len;
Packit 709fb3
                *cp++ = '/';
Packit 709fb3
        } else {
Packit 709fb3
                /* GCC, you're too verbose. */
Packit 709fb3
                cp = NULL;
Packit 709fb3
        }
Packit 709fb3
        len++;
Packit 709fb3
        maxlen = sp->fts_pathlen - len;
Packit 709fb3
Packit 709fb3
        level = cur->fts_level + 1;
Packit 709fb3
Packit 709fb3
        /* Read the directory, attaching each entry to the "link" pointer. */
Packit 709fb3
        doadjust = false;
Packit 709fb3
        head = NULL;
Packit 709fb3
        tail = NULL;
Packit 709fb3
        nitems = 0;
Packit 709fb3
        while (cur->fts_dirp) {
Packit 709fb3
                bool is_dir;
Packit 709fb3
                size_t d_namelen;
Packit 709fb3
                __set_errno (0);
Packit 709fb3
                struct dirent *dp = readdir(cur->fts_dirp);
Packit 709fb3
                if (dp == NULL) {
Packit 709fb3
                        if (errno) {
Packit 709fb3
                                cur->fts_errno = errno;
Packit 709fb3
                                /* If we've not read any items yet, treat
Packit 709fb3
                                   the error as if we can't access the dir.  */
Packit 709fb3
                                cur->fts_info = (continue_readdir || nitems)
Packit 709fb3
                                                ? FTS_ERR : FTS_DNR;
Packit 709fb3
                        }
Packit 709fb3
                        break;
Packit 709fb3
                }
Packit 709fb3
                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
Packit 709fb3
                        continue;
Packit 709fb3
Packit 709fb3
                d_namelen = _D_EXACT_NAMLEN (dp);
Packit 709fb3
                p = fts_alloc (sp, dp->d_name, d_namelen);
Packit 709fb3
                if (!p)
Packit 709fb3
                        goto mem1;
Packit 709fb3
                if (d_namelen >= maxlen) {
Packit 709fb3
                        /* include space for NUL */
Packit 709fb3
                        oldaddr = sp->fts_path;
Packit 709fb3
                        if (! fts_palloc(sp, d_namelen + len + 1)) {
Packit 709fb3
                                /*
Packit 709fb3
                                 * No more memory.  Save
Packit 709fb3
                                 * errno, free up the current structure and the
Packit 709fb3
                                 * structures already allocated.
Packit 709fb3
                                 */
Packit 709fb3
mem1:                           saved_errno = errno;
Packit 709fb3
                                free(p);
Packit 709fb3
                                fts_lfree(head);
Packit 709fb3
                                closedir_and_clear(cur->fts_dirp);
Packit 709fb3
                                cur->fts_info = FTS_ERR;
Packit 709fb3
                                SET(FTS_STOP);
Packit 709fb3
                                __set_errno (saved_errno);
Packit 709fb3
                                return (NULL);
Packit 709fb3
                        }
Packit 709fb3
                        /* Did realloc() change the pointer? */
Packit 709fb3
                        if (oldaddr != sp->fts_path) {
Packit 709fb3
                                doadjust = true;
Packit 709fb3
                                if (ISSET(FTS_NOCHDIR))
Packit 709fb3
                                        cp = sp->fts_path + len;
Packit 709fb3
                        }
Packit 709fb3
                        maxlen = sp->fts_pathlen - len;
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                new_len = len + d_namelen;
Packit 709fb3
                if (new_len < len) {
Packit 709fb3
                        /*
Packit 709fb3
                         * In the unlikely event that we would end up
Packit 709fb3
                         * with a file name longer than SIZE_MAX, free up
Packit 709fb3
                         * the current structure and the structures already
Packit 709fb3
                         * allocated, then error out with ENAMETOOLONG.
Packit 709fb3
                         */
Packit 709fb3
                        free(p);
Packit 709fb3
                        fts_lfree(head);
Packit 709fb3
                        closedir_and_clear(cur->fts_dirp);
Packit 709fb3
                        cur->fts_info = FTS_ERR;
Packit 709fb3
                        SET(FTS_STOP);
Packit 709fb3
                        __set_errno (ENAMETOOLONG);
Packit 709fb3
                        return (NULL);
Packit 709fb3
                }
Packit 709fb3
                p->fts_level = level;
Packit 709fb3
                p->fts_parent = sp->fts_cur;
Packit 709fb3
                p->fts_pathlen = new_len;
Packit 709fb3
Packit 709fb3
                /* Store dirent.d_ino, in case we need to sort
Packit 709fb3
                   entries before processing them.  */
Packit 709fb3
                p->fts_statp->st_ino = D_INO (dp);
Packit 709fb3
Packit 709fb3
                /* Build a file name for fts_stat to stat. */
Packit 709fb3
                if (ISSET(FTS_NOCHDIR)) {
Packit 709fb3
                        p->fts_accpath = p->fts_path;
Packit 709fb3
                        memmove(cp, p->fts_name, p->fts_namelen + 1);
Packit 709fb3
                } else
Packit 709fb3
                        p->fts_accpath = p->fts_name;
Packit 709fb3
Packit 709fb3
                if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
Packit 709fb3
                        /* Record what fts_read will have to do with this
Packit 709fb3
                           entry. In many cases, it will simply fts_stat it,
Packit 709fb3
                           but we can take advantage of any d_type information
Packit 709fb3
                           to optimize away the unnecessary stat calls.  I.e.,
Packit 709fb3
                           if FTS_NOSTAT is in effect and we're not following
Packit 709fb3
                           symlinks (FTS_PHYSICAL) and d_type indicates this
Packit 709fb3
                           is *not* a directory, then we won't have to stat it
Packit 709fb3
                           at all.  If it *is* a directory, then (currently)
Packit 709fb3
                           we stat it regardless, in order to get device and
Packit 709fb3
                           inode numbers.  Some day we might optimize that
Packit 709fb3
                           away, too, for directories where d_ino is known to
Packit 709fb3
                           be valid.  */
Packit 709fb3
                        bool skip_stat = (ISSET(FTS_PHYSICAL)
Packit 709fb3
                                          && ISSET(FTS_NOSTAT)
Packit 709fb3
                                          && DT_IS_KNOWN(dp)
Packit 709fb3
                                          && ! DT_MUST_BE(dp, DT_DIR));
Packit 709fb3
                        p->fts_info = FTS_NSOK;
Packit 709fb3
                        /* Propagate dirent.d_type information back
Packit 709fb3
                           to caller, when possible.  */
Packit 709fb3
                        set_stat_type (p->fts_statp, D_TYPE (dp));
Packit 709fb3
                        fts_set_stat_required(p, !skip_stat);
Packit 709fb3
                        is_dir = (ISSET(FTS_PHYSICAL)
Packit 709fb3
                                  && DT_MUST_BE(dp, DT_DIR));
Packit 709fb3
                } else {
Packit 709fb3
                        p->fts_info = fts_stat(sp, p, false);
Packit 709fb3
                        is_dir = (p->fts_info == FTS_D
Packit 709fb3
                                  || p->fts_info == FTS_DC
Packit 709fb3
                                  || p->fts_info == FTS_DOT);
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                /* Decrement link count if applicable. */
Packit 709fb3
                if (nlinks > 0 && is_dir)
Packit 709fb3
                        nlinks -= nostat;
Packit 709fb3
Packit 709fb3
                /* We walk in directory order so "ls -f" doesn't get upset. */
Packit 709fb3
                p->fts_link = NULL;
Packit 709fb3
                if (head == NULL)
Packit 709fb3
                        head = tail = p;
Packit 709fb3
                else {
Packit 709fb3
                        tail->fts_link = p;
Packit 709fb3
                        tail = p;
Packit 709fb3
                }
Packit 709fb3
                ++nitems;
Packit 709fb3
                if (max_entries <= nitems) {
Packit 709fb3
                        /* When there are too many dir entries, leave
Packit 709fb3
                           fts_dirp open, so that a subsequent fts_read
Packit 709fb3
                           can take up where we leave off.  */
Packit 709fb3
                        goto break_without_closedir;
Packit 709fb3
                }
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        if (cur->fts_dirp)
Packit 709fb3
                closedir_and_clear(cur->fts_dirp);
Packit 709fb3
Packit 709fb3
 break_without_closedir:
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If realloc() changed the address of the file name, adjust the
Packit 709fb3
         * addresses for the rest of the tree and the dir list.
Packit 709fb3
         */
Packit 709fb3
        if (doadjust)
Packit 709fb3
                fts_padjust(sp, head);
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If not changing directories, reset the file name back to original
Packit 709fb3
         * state.
Packit 709fb3
         */
Packit 709fb3
        if (ISSET(FTS_NOCHDIR)) {
Packit 709fb3
                if (len == sp->fts_pathlen || nitems == 0)
Packit 709fb3
                        --cp;
Packit 709fb3
                *cp = '\0';
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If descended after called from fts_children or after called from
Packit 709fb3
         * fts_read and nothing found, get back.  At the root level we use
Packit 709fb3
         * the saved fd; if one of fts_open()'s arguments is a relative name
Packit 709fb3
         * to an empty directory, we wind up here with no other way back.  If
Packit 709fb3
         * can't get back, we're done.
Packit 709fb3
         */
Packit 709fb3
        if (!continue_readdir && descend && (type == BCHILD || !nitems) &&
Packit 709fb3
            (cur->fts_level == FTS_ROOTLEVEL
Packit 709fb3
             ? restore_initial_cwd(sp)
Packit 709fb3
             : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
Packit 709fb3
                cur->fts_info = FTS_ERR;
Packit 709fb3
                SET(FTS_STOP);
Packit 709fb3
                fts_lfree(head);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* If didn't find anything, return NULL. */
Packit 709fb3
        if (!nitems) {
Packit 709fb3
                if (type == BREAD
Packit 709fb3
                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
Packit 709fb3
                        cur->fts_info = FTS_DP;
Packit 709fb3
                fts_lfree(head);
Packit 709fb3
                return (NULL);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* If there are many entries, no sorting function has been specified,
Packit 709fb3
           and this file system is of a type that may be slow with a large
Packit 709fb3
           number of entries, then sort the directory entries on increasing
Packit 709fb3
           inode numbers.  */
Packit 709fb3
        if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
Packit 709fb3
            && !sp->fts_compar
Packit 709fb3
            && ISSET (FTS_CWDFD)
Packit 709fb3
            && dirent_inode_sort_may_be_useful (sp->fts_cwd_fd)) {
Packit 709fb3
                sp->fts_compar = fts_compare_ino;
Packit 709fb3
                head = fts_sort (sp, head, nitems);
Packit 709fb3
                sp->fts_compar = NULL;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        /* Sort the entries. */
Packit 709fb3
        if (sp->fts_compar && nitems > 1)
Packit 709fb3
                head = fts_sort(sp, head, nitems);
Packit 709fb3
        return (head);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
#if FTS_DEBUG
Packit 709fb3
Packit 709fb3
/* Walk ->fts_parent links starting at E_CURR, until the root of the
Packit 709fb3
   current hierarchy.  There should be a directory with dev/inode
Packit 709fb3
   matching those of AD.  If not, print a lot of diagnostics.  */
Packit 709fb3
static void
Packit 709fb3
find_matching_ancestor (FTSENT const *e_curr, struct Active_dir const *ad)
Packit 709fb3
{
Packit 709fb3
  FTSENT const *ent;
Packit 709fb3
  for (ent = e_curr; ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
Packit 709fb3
    {
Packit 709fb3
      if (ad->ino == ent->fts_statp->st_ino
Packit 709fb3
          && ad->dev == ent->fts_statp->st_dev)
Packit 709fb3
        return;
Packit 709fb3
    }
Packit 709fb3
  printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
Packit 709fb3
  printf ("active dirs:\n");
Packit 709fb3
  for (ent = e_curr;
Packit 709fb3
       ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
Packit 709fb3
    printf ("  %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
Packit 709fb3
            ad->fts_ent->fts_accpath,
Packit 709fb3
            (uintmax_t) ad->dev,
Packit 709fb3
            (uintmax_t) ad->ino,
Packit 709fb3
            ent->fts_accpath,
Packit 709fb3
            (uintmax_t) ent->fts_statp->st_dev,
Packit 709fb3
            (uintmax_t) ent->fts_statp->st_ino);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
void
Packit 709fb3
fts_cross_check (FTS const *sp)
Packit 709fb3
{
Packit 709fb3
  FTSENT const *ent = sp->fts_cur;
Packit 709fb3
  FTSENT const *t;
Packit 709fb3
  if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))
Packit 709fb3
    return;
Packit 709fb3
Packit 709fb3
  Dprintf (("fts-cross-check cur=%s\n", ent->fts_path));
Packit 709fb3
  /* Make sure every parent dir is in the tree.  */
Packit 709fb3
  for (t = ent->fts_parent; t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
Packit 709fb3
    {
Packit 709fb3
      struct Active_dir ad;
Packit 709fb3
      ad.ino = t->fts_statp->st_ino;
Packit 709fb3
      ad.dev = t->fts_statp->st_dev;
Packit 709fb3
      if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
Packit 709fb3
        printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  /* Make sure every dir in the tree is an active dir.
Packit 709fb3
     But ENT is not necessarily a directory.  If so, just skip this part. */
Packit 709fb3
  if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
Packit 709fb3
      && (ent->fts_info == FTS_DP
Packit 709fb3
          || ent->fts_info == FTS_D))
Packit 709fb3
    {
Packit 709fb3
      struct Active_dir *ad;
Packit 709fb3
      for (ad = hash_get_first (sp->fts_cycle.ht); ad != NULL;
Packit 709fb3
           ad = hash_get_next (sp->fts_cycle.ht, ad))
Packit 709fb3
        {
Packit 709fb3
          find_matching_ancestor (ent, ad);
Packit 709fb3
        }
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static bool
Packit 709fb3
same_fd (int fd1, int fd2)
Packit 709fb3
{
Packit 709fb3
  struct stat sb1, sb2;
Packit 709fb3
  return (fstat (fd1, &sb1) == 0
Packit 709fb3
          && fstat (fd2, &sb2) == 0
Packit 709fb3
          && SAME_INODE (sb1, sb2));
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
fd_ring_print (FTS const *sp, FILE *stream, char const *msg)
Packit 709fb3
{
Packit 709fb3
  I_ring const *fd_ring = &sp->fts_fd_ring;
Packit 709fb3
  unsigned int i = fd_ring->fts_front;
Packit 709fb3
  char *cwd = getcwdat (sp->fts_cwd_fd, NULL, 0);
Packit 709fb3
  fprintf (stream, "=== %s ========== %s\n", msg, cwd);
Packit 709fb3
  free (cwd);
Packit 709fb3
  if (i_ring_empty (fd_ring))
Packit 709fb3
    return;
Packit 709fb3
Packit 709fb3
  while (true)
Packit 709fb3
    {
Packit 709fb3
      int fd = fd_ring->fts_fd_ring[i];
Packit 709fb3
      if (fd < 0)
Packit 709fb3
        fprintf (stream, "%d: %d:\n", i, fd);
Packit 709fb3
      else
Packit 709fb3
        {
Packit 709fb3
          char *wd = getcwdat (fd, NULL, 0);
Packit 709fb3
          fprintf (stream, "%d: %d: %s\n", i, fd, wd);
Packit 709fb3
          free (wd);
Packit 709fb3
        }
Packit 709fb3
      if (i == fd_ring->fts_back)
Packit 709fb3
        break;
Packit 709fb3
      i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
Packit 709fb3
    }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* Ensure that each file descriptor on the fd_ring matches a
Packit 709fb3
   parent, grandparent, etc. of the current working directory.  */
Packit 709fb3
static void
Packit 709fb3
fd_ring_check (FTS const *sp)
Packit 709fb3
{
Packit 709fb3
  if (!fts_debug)
Packit 709fb3
    return;
Packit 709fb3
Packit 709fb3
  /* Make a writable copy.  */
Packit 709fb3
  I_ring fd_w = sp->fts_fd_ring;
Packit 709fb3
Packit 709fb3
  int cwd_fd = sp->fts_cwd_fd;
Packit 709fb3
  cwd_fd = dup (cwd_fd);
Packit 709fb3
  char *dot = getcwdat (cwd_fd, NULL, 0);
Packit 709fb3
  error (0, 0, "===== check ===== cwd: %s", dot);
Packit 709fb3
  free (dot);
Packit 709fb3
  while ( ! i_ring_empty (&fd_w))
Packit 709fb3
    {
Packit 709fb3
      int fd = i_ring_pop (&fd_w);
Packit 709fb3
      if (0 <= fd)
Packit 709fb3
        {
Packit 709fb3
          int parent_fd = openat (cwd_fd, "..", O_SEARCH | O_NOATIME);
Packit 709fb3
          if (parent_fd < 0)
Packit 709fb3
            {
Packit 709fb3
              // Warn?
Packit 709fb3
              break;
Packit 709fb3
            }
Packit 709fb3
          if (!same_fd (fd, parent_fd))
Packit 709fb3
            {
Packit 709fb3
              char *cwd = getcwdat (fd, NULL, 0);
Packit 709fb3
              error (0, errno, "ring  : %s", cwd);
Packit 709fb3
              char *c2 = getcwdat (parent_fd, NULL, 0);
Packit 709fb3
              error (0, errno, "parent: %s", c2);
Packit 709fb3
              free (cwd);
Packit 709fb3
              free (c2);
Packit 709fb3
              fts_assert (0);
Packit 709fb3
            }
Packit 709fb3
          close (cwd_fd);
Packit 709fb3
          cwd_fd = parent_fd;
Packit 709fb3
        }
Packit 709fb3
    }
Packit 709fb3
  close (cwd_fd);
Packit 709fb3
}
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
static unsigned short int
Packit 709fb3
internal_function
Packit 709fb3
fts_stat(FTS *sp, register FTSENT *p, bool follow)
Packit 709fb3
{
Packit 709fb3
        struct stat *sbp = p->fts_statp;
Packit 709fb3
        int saved_errno;
Packit 709fb3
Packit 709fb3
        if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW))
Packit 709fb3
                follow = true;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * If doing a logical walk, or application requested FTS_FOLLOW, do
Packit 709fb3
         * a stat(2).  If that fails, check for a non-existent symlink.  If
Packit 709fb3
         * fail, set the errno from the stat call.
Packit 709fb3
         */
Packit 709fb3
        if (ISSET(FTS_LOGICAL) || follow) {
Packit 709fb3
                if (stat(p->fts_accpath, sbp)) {
Packit 709fb3
                        saved_errno = errno;
Packit 709fb3
                        if (errno == ENOENT
Packit 709fb3
                            && lstat(p->fts_accpath, sbp) == 0) {
Packit 709fb3
                                __set_errno (0);
Packit 709fb3
                                return (FTS_SLNONE);
Packit 709fb3
                        }
Packit 709fb3
                        p->fts_errno = saved_errno;
Packit 709fb3
                        goto err;
Packit 709fb3
                }
Packit 709fb3
        } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp,
Packit 709fb3
                           AT_SYMLINK_NOFOLLOW)) {
Packit 709fb3
                p->fts_errno = errno;
Packit 709fb3
err:            memset(sbp, 0, sizeof(struct stat));
Packit 709fb3
                return (FTS_NS);
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
        if (S_ISDIR(sbp->st_mode)) {
Packit 709fb3
                p->fts_n_dirs_remaining = (sbp->st_nlink
Packit 709fb3
                                           - (ISSET(FTS_SEEDOT) ? 0 : 2));
Packit 709fb3
                if (ISDOT(p->fts_name)) {
Packit 709fb3
                        /* Command-line "." and ".." are real directories. */
Packit 709fb3
                        return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
Packit 709fb3
                }
Packit 709fb3
Packit 709fb3
                return (FTS_D);
Packit 709fb3
        }
Packit 709fb3
        if (S_ISLNK(sbp->st_mode))
Packit 709fb3
                return (FTS_SL);
Packit 709fb3
        if (S_ISREG(sbp->st_mode))
Packit 709fb3
                return (FTS_F);
Packit 709fb3
        return (FTS_DEFAULT);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static int
Packit 709fb3
fts_compar (void const *a, void const *b)
Packit 709fb3
{
Packit 709fb3
  /* Convert A and B to the correct types, to pacify the compiler, and
Packit 709fb3
     for portability to bizarre hosts where "void const *" and "FTSENT
Packit 709fb3
     const **" differ in runtime representation.  The comparison
Packit 709fb3
     function cannot modify *a and *b, but there is no compile-time
Packit 709fb3
     check for this.  */
Packit 709fb3
  FTSENT const **pa = (FTSENT const **) a;
Packit 709fb3
  FTSENT const **pb = (FTSENT const **) b;
Packit 709fb3
  return pa[0]->fts_fts->fts_compar (pa, pb);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static FTSENT *
Packit 709fb3
internal_function
Packit 709fb3
fts_sort (FTS *sp, FTSENT *head, register size_t nitems)
Packit 709fb3
{
Packit 709fb3
        register FTSENT **ap, *p;
Packit 709fb3
Packit 709fb3
        /* On most modern hosts, void * and FTSENT ** have the same
Packit 709fb3
           run-time representation, and one can convert sp->fts_compar to
Packit 709fb3
           the type qsort expects without problem.  Use the heuristic that
Packit 709fb3
           this is OK if the two pointer types are the same size, and if
Packit 709fb3
           converting FTSENT ** to long int is the same as converting
Packit 709fb3
           FTSENT ** to void * and then to long int.  This heuristic isn't
Packit 709fb3
           valid in general but we don't know of any counterexamples.  */
Packit 709fb3
        FTSENT *dummy;
Packit 709fb3
        int (*compare) (void const *, void const *) =
Packit 709fb3
          ((sizeof &dummy == sizeof (void *)
Packit 709fb3
            && (long int) &dummy == (long int) (void *) &dummy)
Packit 709fb3
           ? (int (*) (void const *, void const *)) sp->fts_compar
Packit 709fb3
           : fts_compar);
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * Construct an array of pointers to the structures and call qsort(3).
Packit 709fb3
         * Reassemble the array in the order returned by qsort.  If unable to
Packit 709fb3
         * sort for memory reasons, return the directory entries in their
Packit 709fb3
         * current order.  Allocate enough space for the current needs plus
Packit 709fb3
         * 40 so don't realloc one entry at a time.
Packit 709fb3
         */
Packit 709fb3
        if (nitems > sp->fts_nitems) {
Packit 709fb3
                FTSENT **a;
Packit 709fb3
Packit 709fb3
                sp->fts_nitems = nitems + 40;
Packit 709fb3
                if (SIZE_MAX / sizeof *a < sp->fts_nitems
Packit 709fb3
                    || ! (a = realloc (sp->fts_array,
Packit 709fb3
                                       sp->fts_nitems * sizeof *a))) {
Packit 709fb3
                        free(sp->fts_array);
Packit 709fb3
                        sp->fts_array = NULL;
Packit 709fb3
                        sp->fts_nitems = 0;
Packit 709fb3
                        return (head);
Packit 709fb3
                }
Packit 709fb3
                sp->fts_array = a;
Packit 709fb3
        }
Packit 709fb3
        for (ap = sp->fts_array, p = head; p; p = p->fts_link)
Packit 709fb3
                *ap++ = p;
Packit 709fb3
        qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), compare);
Packit 709fb3
        for (head = *(ap = sp->fts_array); --nitems; ++ap)
Packit 709fb3
                ap[0]->fts_link = ap[1];
Packit 709fb3
        ap[0]->fts_link = NULL;
Packit 709fb3
        return (head);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static FTSENT *
Packit 709fb3
internal_function
Packit 709fb3
fts_alloc (FTS *sp, const char *name, register size_t namelen)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *p;
Packit 709fb3
        size_t len;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * The file name is a variable length array.  Allocate the FTSENT
Packit 709fb3
         * structure and the file name in one chunk.
Packit 709fb3
         */
Packit 709fb3
        len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);
Packit 709fb3
        if ((p = malloc(len)) == NULL)
Packit 709fb3
                return (NULL);
Packit 709fb3
Packit 709fb3
        /* Copy the name and guarantee NUL termination. */
Packit 709fb3
        memcpy(p->fts_name, name, namelen);
Packit 709fb3
        p->fts_name[namelen] = '\0';
Packit 709fb3
Packit 709fb3
        p->fts_namelen = namelen;
Packit 709fb3
        p->fts_fts = sp;
Packit 709fb3
        p->fts_path = sp->fts_path;
Packit 709fb3
        p->fts_errno = 0;
Packit 709fb3
        p->fts_dirp = NULL;
Packit 709fb3
        p->fts_flags = 0;
Packit 709fb3
        p->fts_instr = FTS_NOINSTR;
Packit 709fb3
        p->fts_number = 0;
Packit 709fb3
        p->fts_pointer = NULL;
Packit 709fb3
        return (p);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static void
Packit 709fb3
internal_function
Packit 709fb3
fts_lfree (register FTSENT *head)
Packit 709fb3
{
Packit 709fb3
        register FTSENT *p;
Packit 709fb3
Packit 709fb3
        /* Free a linked list of structures. */
Packit 709fb3
        while ((p = head)) {
Packit 709fb3
                head = head->fts_link;
Packit 709fb3
                if (p->fts_dirp)
Packit 709fb3
                        closedir (p->fts_dirp);
Packit 709fb3
                free(p);
Packit 709fb3
        }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * Allow essentially unlimited file name lengths; find, rm, ls should
Packit 709fb3
 * all work on any tree.  Most systems will allow creation of file
Packit 709fb3
 * names much longer than MAXPATHLEN, even though the kernel won't
Packit 709fb3
 * resolve them.  Add the size (not just what's needed) plus 256 bytes
Packit 709fb3
 * so don't realloc the file name 2 bytes at a time.
Packit 709fb3
 */
Packit 709fb3
static bool
Packit 709fb3
internal_function
Packit 709fb3
fts_palloc (FTS *sp, size_t more)
Packit 709fb3
{
Packit 709fb3
        char *p;
Packit 709fb3
        size_t new_len = sp->fts_pathlen + more + 256;
Packit 709fb3
Packit 709fb3
        /*
Packit 709fb3
         * See if fts_pathlen would overflow.
Packit 709fb3
         */
Packit 709fb3
        if (new_len < sp->fts_pathlen) {
Packit 709fb3
                free(sp->fts_path);
Packit 709fb3
                sp->fts_path = NULL;
Packit 709fb3
                __set_errno (ENAMETOOLONG);
Packit 709fb3
                return false;
Packit 709fb3
        }
Packit 709fb3
        sp->fts_pathlen = new_len;
Packit 709fb3
        p = realloc(sp->fts_path, sp->fts_pathlen);
Packit 709fb3
        if (p == NULL) {
Packit 709fb3
                free(sp->fts_path);
Packit 709fb3
                sp->fts_path = NULL;
Packit 709fb3
                return false;
Packit 709fb3
        }
Packit 709fb3
        sp->fts_path = p;
Packit 709fb3
        return true;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * When the file name is realloc'd, have to fix all of the pointers in
Packit 709fb3
 *  structures already returned.
Packit 709fb3
 */
Packit 709fb3
static void
Packit 709fb3
internal_function
Packit 709fb3
fts_padjust (FTS *sp, FTSENT *head)
Packit 709fb3
{
Packit 709fb3
        FTSENT *p;
Packit 709fb3
        char *addr = sp->fts_path;
Packit 709fb3
Packit 709fb3
#define ADJUST(p) do {                                                  \
Packit 709fb3
        if ((p)->fts_accpath != (p)->fts_name) {                        \
Packit 709fb3
                (p)->fts_accpath =                                      \
Packit 709fb3
                    (char *)addr + ((p)->fts_accpath - (p)->fts_path);  \
Packit 709fb3
        }                                                               \
Packit 709fb3
        (p)->fts_path = addr;                                           \
Packit 709fb3
} while (0)
Packit 709fb3
        /* Adjust the current set of children. */
Packit 709fb3
        for (p = sp->fts_child; p; p = p->fts_link)
Packit 709fb3
                ADJUST(p);
Packit 709fb3
Packit 709fb3
        /* Adjust the rest of the tree, including the current level. */
Packit 709fb3
        for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
Packit 709fb3
                ADJUST(p);
Packit 709fb3
                p = p->fts_link ? p->fts_link : p->fts_parent;
Packit 709fb3
        }
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static size_t
Packit 709fb3
internal_function _GL_ATTRIBUTE_PURE
Packit 709fb3
fts_maxarglen (char * const *argv)
Packit 709fb3
{
Packit 709fb3
        size_t len, max;
Packit 709fb3
Packit 709fb3
        for (max = 0; *argv; ++argv)
Packit 709fb3
                if ((len = strlen(*argv)) > max)
Packit 709fb3
                        max = len;
Packit 709fb3
        return (max + 1);
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/*
Packit 709fb3
 * Change to dir specified by fd or file name without getting
Packit 709fb3
 * tricked by someone changing the world out from underneath us.
Packit 709fb3
 * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.
Packit 709fb3
 * If FD is non-negative, expect it to be used after this function returns,
Packit 709fb3
 * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then
Packit 709fb3
 * do closedir(dirp), because that would invalidate the saved FD.
Packit 709fb3
 * Upon failure, close FD immediately and return nonzero.
Packit 709fb3
 */
Packit 709fb3
static int
Packit 709fb3
internal_function
Packit 709fb3
fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir)
Packit 709fb3
{
Packit 709fb3
        int ret;
Packit 709fb3
        bool is_dotdot = dir && STREQ (dir, "..");
Packit 709fb3
        int newfd;
Packit 709fb3
Packit 709fb3
        /* This clause handles the unusual case in which FTS_NOCHDIR
Packit 709fb3
           is specified, along with FTS_CWDFD.  In that case, there is
Packit 709fb3
           no need to change even the virtual cwd file descriptor.
Packit 709fb3
           However, if FD is non-negative, we do close it here.  */
Packit 709fb3
        if (ISSET (FTS_NOCHDIR))
Packit 709fb3
          {
Packit 709fb3
            if (ISSET (FTS_CWDFD) && 0 <= fd)
Packit 709fb3
              close (fd);
Packit 709fb3
            return 0;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
Packit 709fb3
          {
Packit 709fb3
            /* When possible, skip the diropen and subsequent fstat+dev/ino
Packit 709fb3
               comparison.  I.e., when changing to parent directory
Packit 709fb3
               (chdir ("..")), use a file descriptor from the ring and
Packit 709fb3
               save the overhead of diropen+fstat, as well as avoiding
Packit 709fb3
               failure when we lack "x" access to the virtual cwd.  */
Packit 709fb3
            if ( ! i_ring_empty (&sp->fts_fd_ring))
Packit 709fb3
              {
Packit 709fb3
                int parent_fd;
Packit 709fb3
                fd_ring_print (sp, stderr, "pre-pop");
Packit 709fb3
                parent_fd = i_ring_pop (&sp->fts_fd_ring);
Packit 709fb3
                is_dotdot = true;
Packit 709fb3
                if (0 <= parent_fd)
Packit 709fb3
                  {
Packit 709fb3
                    fd = parent_fd;
Packit 709fb3
                    dir = NULL;
Packit 709fb3
                  }
Packit 709fb3
              }
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        newfd = fd;
Packit 709fb3
        if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
Packit 709fb3
          return -1;
Packit 709fb3
Packit 709fb3
        /* The following dev/inode check is necessary if we're doing a
Packit 709fb3
           "logical" traversal (through symlinks, a la chown -L), if the
Packit 709fb3
           system lacks O_NOFOLLOW support, or if we're changing to ".."
Packit 709fb3
           (but not via a popped file descriptor).  When changing to the
Packit 709fb3
           name "..", O_NOFOLLOW can't help.  In general, when the target is
Packit 709fb3
           not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
Packit 709fb3
           follow a symlink, so we can avoid the expense of this fstat.  */
Packit 709fb3
        if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
Packit 709fb3
            || (dir && STREQ (dir, "..")))
Packit 709fb3
          {
Packit 709fb3
            struct stat sb;
Packit 709fb3
            if (fstat(newfd, &sb))
Packit 709fb3
              {
Packit 709fb3
                ret = -1;
Packit 709fb3
                goto bail;
Packit 709fb3
              }
Packit 709fb3
            if (p->fts_statp->st_dev != sb.st_dev
Packit 709fb3
                || p->fts_statp->st_ino != sb.st_ino)
Packit 709fb3
              {
Packit 709fb3
                __set_errno (ENOENT);           /* disinformation */
Packit 709fb3
                ret = -1;
Packit 709fb3
                goto bail;
Packit 709fb3
              }
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        if (ISSET(FTS_CWDFD))
Packit 709fb3
          {
Packit 709fb3
            cwd_advance_fd (sp, newfd, ! is_dotdot);
Packit 709fb3
            return 0;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        ret = fchdir(newfd);
Packit 709fb3
bail:
Packit 709fb3
        if (fd < 0)
Packit 709fb3
          {
Packit 709fb3
            int oerrno = errno;
Packit 709fb3
            (void)close(newfd);
Packit 709fb3
            __set_errno (oerrno);
Packit 709fb3
          }
Packit 709fb3
        return ret;
Packit 709fb3
}