public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
To: git-commits@fedoraproject.org
Subject: [rpms/glibc] rawhide: Auto-sync with upstream branch master
Date: Thu, 24 Sep 2026 11:48:57 GMT [thread overview]
Message-ID: <179025053754.1.17577788459617037943.rpms-glibc-52b122b0e8f4@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/glibc
Branch : rawhide
Commit : 52b122b0e8f460dc6c7fce7959d4bdc24f674588
Author : Frédéric Bérat <fberat@redhat.com>
Date : 2026-09-24T11:10:23+02:00
Stats : +116/-10817 in 4 file(s)
URL : https://src.fedoraproject.org/rpms/glibc/c/52b122b0e8f460dc6c7fce7959d4bdc24f674588?branch=rawhide
Log:
Auto-sync with upstream branch master
Upstream commit: 04a3995650ec181e98b768fef59f1ad228a87be5
- advisories: fix tag for CVE in SA-2026-0023 (Rolf Eike Beer)
- Update CVSSv3.1 score for GLIBC-SA-2026-0022 (Carlos O'Donell)
- Publish CVE-2026-95818 advisory (Carlos O'Donell)
- Publish CVE-2026-86805 advisory (Carlos O'Donell)
- elf: Fix ld.so SIGSEGV in trace mode with unresolved TLS symbols (BZ 34532) (Adhemerval Zanella)
- nptl: Retry SIGSETXID delivery on EAGAIN in setxid (bug 21108) (Adhemerval Zanella)
- math: Sync tgamma with CORE-MATH (Adhemerval Zanella)
- math: Sync exp10m1f with CORE-MATH (Adhemerval Zanella)
- math: Sync acosf with CORE-MATH (Adhemerval Zanella)
- math: Sync lgamma with CORE-MATH (Adhemerval Zanella)
- Fix d_t_fmt and date_fmt in en_SE [BZ #34635] (Mike FABIAN)
- stdlib: Don't call clearenv from __libc_setenv_freemem (Mark Wielaard)
- Publish CVE-2026-8674 advisory (Adhemerval Zanella)
- malloc: Improve calloc [BZ #34582] (Wilco Dijkstra)
- x86-64: Link tst-shstk-legacy-1{f,g} with -Wl,--no-as-needed (H.J. Lu)
- Revert "linux: Add __libc_procmaps_iterate" (Adhemerval Zanella)
- Revert "linux: Use __libc_procmaps_iterate on __readonly_area_fallback" (Adhemerval Zanella)
- Revert "nptl: Use __libc_procmaps_iterate on pthread_getattr_np" (Adhemerval Zanella)
- Revert "nptl: Fall back to AT_EXECFN on __pthread_main_stack without /proc" (Adhemerval Zanella)
- Revert "nptl: Account for the stack guard gap on pthread_getattr_np (BZ 34637)" (Adhemerval Zanella)
- Mark swapcontext returns_twice by default [BZ #23130] (Stian Halseth)
- libio: validate the wide vtable via a bounds-checked index (Alessandro Schino)
- resolv: Fix assertion failure on search list truncation [BZ 31026, CVE-2026-8674] (Adhemerval Zanella)
- nptl: Account for the stack guard gap on pthread_getattr_np (BZ 34637) (Adhemerval Zanella)
- nptl: Fall back to AT_EXECFN on __pthread_main_stack without /proc (Adhemerval Zanella)
- nptl: Use __libc_procmaps_iterate on pthread_getattr_np (Adhemerval Zanella)
- linux: Use __libc_procmaps_iterate on __readonly_area_fallback (Adhemerval Zanella)
- linux: Add __libc_procmaps_iterate (Adhemerval Zanella)
- linux: Add buffer argument and line truncation to __libc_procutils_read_file (Adhemerval Zanella)
- Publish CVE-2026-80489 advisory (Carlos O'Donell)
- Publish CVE-2026-77117 advisory (Carlos O'Donell)
- Publish CVE-2026-19542 advisory (Adhemerval Zanella)
- Publish CVE-2026-19499 advisory (Adhemerval Zanella)
- math: Update tanh from CORE-MATH (revision dbd377a9) (Adhemerval Zanella)
- io: Reject an empty path list on fts_open [BZ #25434] [BZ #34610] (Adhemerval Zanella)
- malloc: Reject the top chunk in mem2chunk_check (Magnus Lindholm)
- aarch64: Add MTE mode tunable (Yury Khrustalev)
- elf: Do not load cache extensions from an old-format ld.so.cache [BZ #34600] (Shamil Abdulaev)
- Publish CVE-2026-89092 advisory (Siddhesh Poyarekar)
- x86: Set AVX10 active only if XMM/YMM/ZMM are supported (H.J. Lu)
- time: handle ^ and # modifiers with %Z in wcsftime (bug 34611) (Andreas Schwab)
- x86: Detect APX_NCI_NDD_NF (H.J. Lu)
- math: let architectures signal underflow after narrowing (Matt Turner)
- locale: fix memory leaks in write_locales and write_charmaps (Ruslan Valiyev)
- math: Set errno to ERANGE for logb (+-0) [BZ #6793] (Shamil Abdulaev)
- alpha: Use a comdat group for the shared divide-by-zero handler [BZ #20543] (Matt Turner)
- nptl: Skip pretty-printer tests without python3 [BZ #34507] (Hemanth Kumar M D)
- Record CVE-2026-18374 fix (Florian Weimer)
- libio: Add test for fopen with an empty ", ccs=" value [BZ #34574] (Shamil Abdulaev)
- libio: Fix CVE-2026-18374 heap buffer overflow in ccs= handling (Dongkyun Son)
- getrusage: Add smoke test for getrusage (Ondrej Marek)
- powerpc: Restore NULL check on _rtld_global_ro in INIT_ARCH [BZ #34503] (Michael Pfeifroth)
- stdio-common: Skip the a and A conversions for the IBM extended format (Matt Turner)
- stdio-common: Handle subnormal values in the printf format tests (Matt Turner)
- stdio-common: Add printf format tests for the a and A conversions (Matt Turner)
- stdio-common: Add printf format tests for the b and B conversions (Matt Turner)
- stdio-common: Verify printf format tests with Python rather than AWK (Matt Turner)
- hurd: Make __file_name_lookup_at apply upmask for O_TMPFILE (BZ 34493) (Samuel Thibault)
- hurd: Make readlinkat check bogus length returned by translator (BZ 34504) (Samuel Thibault)
- hurd: Fix setreuid/setregid setting saved ID to new effective ID (BZ 34505) (Samuel Thibault)
- stdlib, wcsmbs: Add missing __nonnull to strto*/wcsto* [BZ #33053] (Shamil Abdulaev)
- elf: Open the normalized $ORIGIN rpath in AT_SECURE programs (BZ 34360) (Adhemerval Zanella)
- test: Fix tst-personality (Yury Khrustalev)
- manual: adjust reference to man-pages following proc(5) split-up (Collin Funk)
- Updates udp.h from Linux 6.19 to netinet/udp.h (Jiayuan Chen)
- nptl: Test case for bug 34546 (Florian Weimer)
- nptl: Use FAIL_UNSUPPORTED in init_tpp_test in tst-tpp.h (Florian Weimer)
- nptl: Revert TPP updates on pthread_mutex_*lock failure (bug 34546) (Florian Weimer)
- nptl: Fix test error reporting in CHECK_TPP_PRIORITY in tst-tpp.h (Florian Weimer)
- nptl: Revert robust list head on pthread_mutex_timedlock failure (bug 34542) (Florian Weimer)
- nptl: Treat negative times as timed out in PI futex locking (bug 34543) (Florian Weimer)
- stdio-common: Keep trailing zeros where %#g rounds into a new decade (BZ 34578) (Matt Turner)
- stdio-common: Iterate over the huge width for one printf function only (Matt Turner)
- elf: Remove dead l_need_tls_init static-TLS init path (Adhemerval Zanella)
- elf: Remove __chk_fail from dl-minimal.c (Adhemerval Zanella)
- elf: Allow RPATH/RUNPATH for static-pie (BZ 33326) (Adhemerval Zanella)
- Fix assert during static startup (BZ 33326) (Adhemerval Zanella)
- Use _dl_writev on __libc_message_impl (Adhemerval Zanella)
- nptl: Add __raise_direct (Adhemerval Zanella)
- tst-backtrace5: split long line (Samuel Thibault)
- tst-backtrace5: Fix on hurd (Samuel Thibault)
- fcntl: drop nonnull attribute for openat, openat2's path argument [BZ #34313] (Sam James)
- io: drop nonnull attribute for fchmodat, faccessat, fchownat's path argument [BZ #34313] (Sam James)
- hurd: Move SINGLE_THREAD_P / RTLD_SINGLE_THREAD_P to single-thread.h (Samuel Thibault)
- stdlib: merge some tests from gnulib (Collin Funk)
- nptl: Propagate EDEADLK from FUTEX_LOCK_PI for errror-checking mutexes (Moritz Klammler)
- advisories: fix typo in README (Rolf Eike Beer)
- Use __attribute_optimization_barrier__ for the remaining noclone users (Matt Turner)
- io: Do not alias fts and fts64 when inode types differ (Magnus Lindholm)
- Use FAIL_UNSUPPORTED more (Sam James)
- iconvdata: Test case for bug 34556, bug 34568 (Florian Weimer)
- iconvdata: EUC_JISX0213 decoding lacks pending character reset (CVE-2026-80489) (Florian Weimer)
- iconvdata: SHIFT_JISX0213 decoding lacks pending character reset (CVE-2026-77117) (Florian Weimer)
- localedata: bn_BD, bn_IN: use full Gregorian month names for abmon (Azharul Haque)
- manual: Fix some typos in the Low-Level Input/Output chapter (Collin Funk)
- x86: Update AVX10 support (H.J. Lu)
- Document CVE-2026-18374 and new CVSS tag (Siddhesh Poyarekar)
- aarch64: Add memory clobber to pkey_write (Yury Khrustalev)
- stdlib: Fix right-justification in strfmon (bug 34510, CVE-2026-19499) (Florian Weimer)
- hurd: Set _TIME_BITS=32 for installed programs on i386/Hurd (Samuel Thibault)
- Add check-symbol-version.awk (H.J. Lu)
- LoongArch: Add standard search paths for 32-bit (mengqinggang)
- LoongArch: Support pointer guard on LoongArch32 (mengqinggang)
- hurd: Warn about setting _TIME_BITS=64 on i386 GNU Hurd (Samuel Thibault)
- math: Update tanh from CORE-MATH (revision bb39e4fa) (Adhemerval Zanella)
- tests: elf: Fix flaky thp tests (Yury Khrustalev)
Remove patches already applied upstream:
- glibc-rh2457183-1.patch
- glibc-rh2457183-2.patch
---
diff --git a/glibc-rh2457183-1.patch b/glibc-rh2457183-1.patch
deleted file mode 100644
index aa38d71..0000000
--- a/glibc-rh2457183-1.patch
+++ /dev/null
@@ -1,4590 +0,0 @@
-Revert "io: Consolidate fts implementation"
-
-This reverts commit b9a2dea54abd1c5db9773a92c97dd3202bbaec62.
-
-Reason for revert: This change breaks dracut due to the way it uses
-fts: <https://bugzilla.redhat.com/show_bug.cgi?id=2457183>
-
-diff --git a/SHARED-FILES b/SHARED-FILES
-index 645040d7e10c1927..126aaf096d93371c 100644
---- a/SHARED-FILES
-+++ b/SHARED-FILES
-@@ -49,7 +49,7 @@ gnulib:
- io/cycle-check.h
- io/dev-ino.h
- io/fts-cycle.c
-- io/fts-common.c
-+ io/fts.c
- io/i-ring.c
- io/same-inode.h
- locale/programs/3level.h
-diff --git a/io/fts-common.c b/io/fts-common.c
-deleted file mode 100644
-index 3825f0aeb4eb1594..0000000000000000
---- a/io/fts-common.c
-+++ /dev/null
-@@ -1,2208 +0,0 @@
--/* Traverse a file hierarchy.
--
-- Copyright (C) 2004-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/*-
-- * Copyright (c) 1990, 1993, 1994
-- * The Regents of the University of California. All rights reserved.
-- *
-- * Redistribution and use in source and binary forms, with or without
-- * modification, are permitted provided that the following conditions
-- * are met:
-- * 1. Redistributions of source code must retain the above copyright
-- * notice, this list of conditions and the following disclaimer.
-- * 2. Redistributions in binary form must reproduce the above copyright
-- * notice, this list of conditions and the following disclaimer in the
-- * documentation and/or other materials provided with the distribution.
-- * 4. Neither the name of the University nor the names of its contributors
-- * may be used to endorse or promote products derived from this software
-- * without specific prior written permission.
-- *
-- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
-- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- * SUCH DAMAGE.
-- */
--
--#include <config.h>
--
--#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint
--static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
--#endif
--
--#if _LIBC
--# include <fts.h>
--#else
--# include "fts_.h"
--#endif
--#if _LIBC || HAVE_SYS_PARAM_H
--# include <sys/param.h>
--#endif
--#include <sys/stat.h>
--#include <fcntl.h>
--#include <errno.h>
--#include <stddef.h>
--#include <stdint.h>
--#include <stdlib.h>
--#include <string.h>
--#include <unistd.h>
--
--/* Support for the LFS API version. */
--#ifndef FTS_OPEN
--# define FTS_OPEN fts_open
--# define FTS_CLOSE fts_close
--# define FTS_READ fts_read
--# define FTS_SET fts_set
--# define FTS_CHILDREN fts_children
--# define FTSOBJ FTS
--# define FTSENTRY FTSENT
--# define INO_T ino_t
--# define STRUCT_STAT stat
--# define FSTAT __fstat
--# define FSTATAT __fstatat
--# define STRUCT_STATFS statfs
--# define FSTATFS __fstatfs
--#endif
--
--#if ! _LIBC
--# include "attribute.h"
--# include "fcntl--.h"
--# include "openat.h"
--# include "opendirat.h"
--# include "same-inode.h"
--# define OPENDIRAT opendirat
--# define FTSENT_WRAPPER(__p) __p
--# define FTS_COMPAR_CAST(__fn) __fn
--#else
--# include <stdbool.h>
--
--# define internal_function
--# define FALLTHROUGH ; [[fallthrough]]
--# define HAVE_STRUCT_DIRENT_D_TYPE 1
--# define GNULIB_FTS_DEBUG 0
--# ifdef O_PATH
--# define O_SEARCH O_PATH
--# else
--# define O_SEARCH O_RDONLY
--# endif
--# define HAVE_SYS_VFS_H 1
--# define HAVE_FSTATFS 1
--# define HAVE_STRUCT_STATFS_F_TYPE 1
--# define HAVE_OPENAT 1
--# define HAVE_WORKING_O_NOFOLLOW 1
--# define _GL_CMP(a, b) ((a) < (b) ? -1 : (a) > (b))
--# define OPENDIRAT __opendirat
--
--static inline bool openat_needs_fchdir (void)
--{
-- return false;
--}
--
--static inline bool streq (const char *s1, const char *s2)
--{
-- return strcmp (s1, s2) == 0;
--}
--# define reallocarray __libc_reallocarray
--# define fchdir __fchdir
--# define close __close
--# define closedir __closedir
--# define fcntl __fcntl
--# define readdir __readdir
--# ifndef dirfd
--# define dirfd __dirfd
--# endif
--# define open __open
--# define openat __openat
--
--# include "cycle-check.c"
--# include "i-ring.c"
--
--struct FTSENT_wrapper
--{
-- FTSOBJ *fts_fts; /* the file hierarchy itself */
-- DIR *fts_dirp; /* Dir pointer for any directory containing
-- more entries than we read at one time. */
-- struct STRUCT_STAT fts_stat;
--
-- FTSENTRY ent;
--};
--
--/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the
-- fts_open has a function point using 'FTSENT **' as argument. */
--# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)
--
--# define FTSENT_WRAPPER(p) \
-- ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))
--#endif
--#define FTSENT_FTS(p) (FTSENT_WRAPPER(p)->fts_fts)
--#define FTSENT_DIRP(p) (FTSENT_WRAPPER(p)->fts_dirp)
--
--#include "flexmember.h"
--
--#include <dirent.h>
--#ifndef _D_EXACT_NAMLEN
--# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)
--#endif
--
--#if HAVE_STRUCT_DIRENT_D_TYPE
--/* True if the type of the directory entry D is known. */
--# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
--/* True if the type of the directory entry D must be T. */
--# define DT_MUST_BE(d, t) ((d)->d_type == (t))
--# define D_TYPE(d) ((d)->d_type)
--#else
--# define DT_IS_KNOWN(d) false
--# define DT_MUST_BE(d, t) false
--# define D_TYPE(d) DT_UNKNOWN
--
--# undef DT_UNKNOWN
--# define DT_UNKNOWN 0
--
--/* Any nonzero values will do here, so long as they're distinct.
-- Undef any existing macros out of the way. */
--# undef DT_BLK
--# undef DT_CHR
--# undef DT_DIR
--# undef DT_FIFO
--# undef DT_LNK
--# undef DT_REG
--# undef DT_SOCK
--# define DT_BLK 1
--# define DT_CHR 2
--# define DT_DIR 3
--# define DT_FIFO 4
--# define DT_LNK 5
--# define DT_REG 6
--# define DT_SOCK 7
--#endif
--
--#ifndef S_IFBLK
--# define S_IFBLK 0
--#endif
--#ifndef S_IFLNK
--# define S_IFLNK 0
--#endif
--#ifndef S_IFSOCK
--# define S_IFSOCK 0
--#endif
--
--enum
--{
-- NOT_AN_INODE_NUMBER = 0
--};
--
--#ifdef D_INO_IN_DIRENT
--# define D_INO(dp) (dp)->d_ino
--#else
--/* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
--# define D_INO(dp) NOT_AN_INODE_NUMBER
--#endif
--
--/* If possible (see max_entries, below), read no more than this many directory
-- entries at a time. Without this limit (i.e., when using non-NULL
-- fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
-- of memory, and handling 64M entries would require 16GiB of memory. */
--#ifndef FTS_MAX_READDIR_ENTRIES
--# define FTS_MAX_READDIR_ENTRIES 100000
--#endif
--
--/* If there are more than this many entries in a directory,
-- and the conditions mentioned below are satisfied, then sort
-- the entries on inode number before any further processing. */
--#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
--# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000
--#endif
--
--enum
--{
-- _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
--};
--
--enum Fts_stat
--{
-- FTS_NO_STAT_REQUIRED = 1,
-- FTS_STAT_REQUIRED = 2
--};
--
--#ifndef __set_errno
--# define __set_errno(Val) errno = (Val)
--#endif
--
--/* If this host provides the openat function, then we can avoid
-- attempting to open "." in some initialization code below. */
--#ifdef HAVE_OPENAT
--# define HAVE_OPENAT_SUPPORT 1
--#else
--# define HAVE_OPENAT_SUPPORT 0
--#endif
--
--#ifdef NDEBUG
--# define fts_assert(expr) ((void) (0 && (expr)))
--#else
--# define fts_assert(expr) \
-- do \
-- { \
-- if (!(expr)) \
-- abort (); \
-- } \
-- while (false)
--#endif
--
--static FTSENTRY *fts_alloc (FTSOBJ *, const char *, size_t);
--static FTSENTRY *fts_build (FTSOBJ *, int);
--static void fts_lfree (FTSENTRY *);
--static void fts_load (FTSOBJ *, FTSENTRY *);
--static size_t fts_maxarglen (char * const *);
--static void fts_padjust (FTSOBJ *, FTSENTRY *);
--static bool fts_palloc (FTSOBJ *, size_t);
--static FTSENTRY *fts_sort (FTSOBJ *, FTSENTRY *, size_t);
--static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);
--static int fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);
--
--#include "fts-cycle.c"
--
--#ifndef MAX
--# define MAX(a,b) ((a) > (b) ? (a) : (b))
--#endif
--
--#ifndef SIZE_MAX
--# define SIZE_MAX ((size_t) -1)
--#endif
--
--#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
--
--#define CLR(opt) (sp->fts_options &= ~(opt))
--#define ISSET(opt) ((sp->fts_options & (opt)) != 0)
--#define SET(opt) (sp->fts_options |= (opt))
--
--/* FIXME: FTS_NOCHDIR is now misnamed.
-- Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
--#define FCHDIR(sp, fd) \
-- (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD) \
-- ? (cwd_advance_fd ((sp), (fd), true), 0) \
-- : fchdir (fd)))
--
--
--/* fts_build flags */
--/* FIXME: make this an enum */
--#define BCHILD 1 /* fts_children */
--#define BNAMES 2 /* fts_children, names only */
--#define BREAD 3 /* fts_read */
--
--#if GNULIB_FTS_DEBUG
--# include <inttypes.h>
--# include <stdio.h>
--bool fts_debug = false;
--# define Dprintf(x) do { if (fts_debug) printf x; } while (false)
--static void fd_ring_check (FTSOBJ const *);
--static void fd_ring_print (FTSOBJ const *, FILE *, char const *);
--#else
--# define Dprintf(x)
--# define fd_ring_check(x)
--# define fd_ring_print(a, b, c)
--#endif
--
--#define LEAVE_DIR(Fts, Ent, Tag) \
-- do \
-- { \
-- Dprintf ((" %s-leaving: %s\n", Tag, (Ent)->fts_path)); \
-- leave_dir (Fts, Ent); \
-- fd_ring_check (Fts); \
-- } \
-- while (false)
--
--static void
--fd_ring_clear (I_ring *fd_ring)
--{
-- while ( ! i_ring_empty (fd_ring))
-- {
-- int fd = i_ring_pop (fd_ring);
-- if (0 <= fd)
-- close (fd);
-- }
--}
--
--/* Overload the fts_statp->st_size member (otherwise unused, when
-- fts_info is FTS_NSOK) to indicate whether fts_read should stat
-- this entry or not. */
--static void
--fts_set_stat_required (FTSENTRY *p, bool required)
--{
-- fts_assert (p->fts_info == FTS_NSOK);
-- p->fts_statp->st_size = (required
-- ? FTS_STAT_REQUIRED
-- : FTS_NO_STAT_REQUIRED);
--}
--
--/* Virtual fchdir. Advance SP's working directory file descriptor,
-- SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
-- CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
-- open on sp->fts_cwd_fd; i.e., to move the working directory one level
-- down. */
--static void
--internal_function
--cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)
--{
-- int old = sp->fts_cwd_fd;
-- fts_assert (old != fd || old == AT_FDCWD);
--
-- if (chdir_down_one)
-- {
-- /* Push "old" onto the ring.
-- If the displaced file descriptor is non-negative, close it. */
-- int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
-- fd_ring_print (sp, stderr, "post-push");
-- if (0 <= prev_fd_in_slot)
-- close (prev_fd_in_slot); /* ignore any close failure */
-- }
-- else if ( ! ISSET (FTS_NOCHDIR))
-- {
-- if (0 <= old)
-- close (old); /* ignore any close failure */
-- }
--
-- sp->fts_cwd_fd = fd;
--}
--
--/* Restore the initial, pre-traversal, "working directory".
-- In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,
-- we may actually change the working directory.
-- Return 0 upon success. Upon failure, set errno and return nonzero. */
--static int
--restore_initial_cwd (FTSOBJ *sp)
--{
-- int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);
-- fd_ring_clear (&(sp->fts_fd_ring));
-- return fail;
--}
--
--/* Open the directory DIR if possible, and return a file
-- descriptor. Return -1 and set errno on failure. It doesn't matter
-- whether the file descriptor has read or write access. */
--
--static int
--internal_function
--diropen (FTSOBJ const *sp, char const *dir)
--{
-- int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
-- | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
--
-- int fd = (ISSET (FTS_CWDFD)
-- ? openat (sp->fts_cwd_fd, dir, open_flags)
-- : open (dir, open_flags));
-- return fd;
--}
--
--FTSOBJ *
--FTS_OPEN (char * const *argv,
-- register int options,
-- int (*compar) (const FTSENTRY **, const FTSENTRY **))
--{
-- /* Options check: glibc added other flags after FTS_NAMEONLY and
-- FTS_STOP, and they are assumed to be private. */
-- if (options & ~FTS_OPTIONMASK
--#if _LIBC
-- || options & (FTS_NAMEONLY | FTS_STOP)
--#endif
-- ) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
-- if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--#if !_LIBC
-- if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--#else
-- /* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or
-- FTS_LOGICAL is specified. */
-- if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))
-- options |= FTS_PHYSICAL;
--#endif
--
-- /* Allocate/initialize the stream */
-- register FTSOBJ *sp = calloc (1, sizeof *sp);
-- if (sp == NULL)
-- return (NULL);
-- sp->fts_compar = FTS_COMPAR_CAST(compar);
-- sp->fts_options = options;
--
-- /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
-- if (ISSET(FTS_LOGICAL)) {
-- SET(FTS_NOCHDIR);
-- CLR(FTS_CWDFD);
-- }
--
-- /* Initialize fts_cwd_fd. */
-- sp->fts_cwd_fd = AT_FDCWD;
-- if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
-- {
-- /* While it isn't technically necessary to open "." this
-- early, doing it here saves us the trouble of ensuring
-- later (where it'd be messier) that "." can in fact
-- be opened. If not, revert to FTS_NOCHDIR mode. */
-- int fd = open (".", O_SEARCH | O_CLOEXEC);
-- if (fd < 0)
-- {
-- /* Even if "." is unreadable, don't revert to FTS_NOCHDIR mode
-- on systems like Linux+PROC_FS, where our openat emulation
-- is good enough. Note: on a system that emulates
-- openat via /proc, this technique can still fail, but
-- only in extreme conditions, e.g., when the working
-- directory cannot be saved (i.e. save_cwd fails) --
-- and that happens on Linux only when "." is unreadable
-- and the CWD would be longer than PATH_MAX.
-- FIXME: once Linux kernel openat support is well established,
-- replace the above open call and this entire if/else block
-- with the body of the if-block below. */
-- if ( openat_needs_fchdir ())
-- {
-- SET(FTS_NOCHDIR);
-- CLR(FTS_CWDFD);
-- }
-- }
-- else
-- {
-- close (fd);
-- }
-- }
--
-- /*
-- * Start out with 1K of file name space, and enough, in any case,
-- * to hold the user's file names.
-- */
--#ifndef MAXPATHLEN
--# define MAXPATHLEN 1024
--#endif
-- {
-- size_t maxarglen = fts_maxarglen(argv);
-- if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
-- goto mem1;
-- }
--
-- /* Allocate/initialize root's parent. */
-- FTSENTRY *parent = NULL;
-- if (*argv != NULL) {
-- if ((parent = fts_alloc(sp, "", 0)) == NULL)
-- goto mem2;
-- parent->fts_level = FTS_ROOTPARENTLEVEL;
-- }
--
-- /* The classic fts implementation would call fts_stat with
-- a new entry for each iteration of the loop below.
-- If the comparison function is not specified or if the
-- FTS_DEFER_STAT option is in effect, don't stat any entry
-- in this loop. This is an attempt to minimize the interval
-- between the initial stat/lstat/fstatat and the point at which
-- a directory argument is first opened. This matters for any
-- directory command line argument that resides on a file system
-- without genuine i-nodes. If you specify FTS_DEFER_STAT along
-- with a comparison function, that function must not access any
-- data via the fts_statp pointer. */
-- bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
--
-- /* Allocate/initialize root(s). */
-- register FTSENTRY *root;
-- register size_t nitems;
-- FTSENTRY *tmp = NULL; /* pacify gcc */
-- for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
-- /* *Do* allow zero-length file names. */
-- size_t len = strlen(*argv);
--
-- if ( ! (options & FTS_VERBATIM))
-- {
-- /* If there are two or more trailing slashes, trim all but one,
-- but don't change "//" to "/", and do map "///" to "/". */
-- char const *v = *argv;
-- if (2 < len && v[len - 1] == '/')
-- while (1 < len && v[len - 2] == '/')
-- --len;
-- }
--
-- register FTSENTRY *p = fts_alloc(sp, *argv, len);
-- if (p == NULL)
-- goto mem3;
-- p->fts_level = FTS_ROOTLEVEL;
-- p->fts_parent = parent;
-- p->fts_accpath = p->fts_name;
-- /* Even when defer_stat is true, be sure to stat the first
-- command line argument, since fts_read (at least with
-- FTS_XDEV) requires that. */
-- if (defer_stat && root != NULL) {
-- p->fts_info = FTS_NSOK;
-- fts_set_stat_required(p, true);
-- } else {
-- p->fts_info = fts_stat(sp, p, false);
-- }
--
-- /*
-- * If comparison routine supplied, traverse in sorted
-- * order; otherwise traverse in the order specified.
-- */
-- if (compar) {
-- p->fts_link = root;
-- root = p;
-- } else {
-- p->fts_link = NULL;
-- if (root == NULL)
-- tmp = root = p;
-- else {
-- tmp->fts_link = p;
-- tmp = p;
-- }
-- }
-- }
-- if (compar && nitems > 1)
-- root = fts_sort(sp, root, nitems);
--
-- /*
-- * Allocate a dummy pointer and make fts_read think that we've just
-- * finished the node before the root(s); set p->fts_info to FTS_INIT
-- * so that everything about the "current" node is ignored.
-- */
-- if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
-- goto mem3;
-- sp->fts_cur->fts_link = root;
-- sp->fts_cur->fts_info = FTS_INIT;
-- sp->fts_cur->fts_level = 1;
-- if (! setup_dir (sp))
-- goto mem3;
--
-- /*
-- * If using chdir(2), grab a file descriptor pointing to dot to ensure
-- * that we can get back here; this could be avoided for some file names,
-- * but almost certainly not worth the effort. Slashes, symbolic links,
-- * and ".." are all fairly nasty problems. Note, if we can't get the
-- * descriptor we run anyway, just more slowly.
-- */
-- if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
-- && (sp->fts_rfd = diropen (sp, ".")) < 0)
-- SET(FTS_NOCHDIR);
--
-- i_ring_init (&sp->fts_fd_ring, -1);
-- return (sp);
--
--mem3: fts_lfree(root);
-- free(FTSENT_WRAPPER(parent));
--mem2: free(sp->fts_path);
--mem1: free(sp);
-- return (NULL);
--}
--
--static void
--internal_function
--fts_load (FTSOBJ *sp, register FTSENTRY *p)
--{
-- /*
-- * Load the stream structure for the next traversal. Since we don't
-- * actually enter the directory until after the preorder visit, set
-- * the fts_accpath field specially so the chdir gets done to the right
-- * place and the user can access the first node. From fts_open it's
-- * known that the file name will fit.
-- */
-- register size_t len = p->fts_pathlen = p->fts_namelen;
-- memmove(sp->fts_path, p->fts_name, len + 1);
-- register char *cp = strrchr(p->fts_name, '/');
-- if (cp && (cp != p->fts_name || cp[1])) {
-- len = strlen(++cp);
-- memmove(p->fts_name, cp, len + 1);
-- p->fts_namelen = len;
-- }
-- p->fts_accpath = p->fts_path = sp->fts_path;
--}
--
--int
--FTS_CLOSE (FTSOBJ *sp)
--{
-- /*
-- * This still works if we haven't read anything -- the dummy structure
-- * points to the root list, so we step through to the end of the root
-- * list which has a valid parent pointer.
-- */
-- if (sp->fts_cur) {
-- register FTSENTRY *p;
-- for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
-- register FTSENTRY *freep = p;
-- p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
-- free(FTSENT_WRAPPER(freep));
-- }
-- free(FTSENT_WRAPPER(p));
-- }
--
-- /* Free up child linked list, sort array, file name buffer. */
-- if (sp->fts_child)
-- fts_lfree(sp->fts_child);
-- free(sp->fts_array);
-- free(sp->fts_path);
--
-- int saved_errno = 0;
-- if (ISSET(FTS_CWDFD))
-- {
-- if (0 <= sp->fts_cwd_fd)
-- if (close (sp->fts_cwd_fd))
-- saved_errno = errno;
-- }
-- else if (!ISSET(FTS_NOCHDIR))
-- {
-- /* Return to original directory, save errno if necessary. */
-- if (fchdir(sp->fts_rfd))
-- saved_errno = errno;
--
-- /* If close fails, record errno only if saved_errno is zero,
-- so that we report the probably-more-meaningful fchdir errno. */
-- if (close (sp->fts_rfd))
-- if (saved_errno == 0)
-- saved_errno = errno;
-- }
--
-- fd_ring_clear (&sp->fts_fd_ring);
--
-- if (sp->fts_leaf_optimization_works_ht)
-- hash_free (sp->fts_leaf_optimization_works_ht);
--
-- free_dir (sp);
--
-- /* Free up the stream pointer. */
-- free(sp);
--
-- /* Set errno and return. */
-- if (saved_errno) {
-- __set_errno (saved_errno);
-- return (-1);
-- }
--
-- return (0);
--}
--
--/* Minimum link count of a traditional Unix directory. When leaf
-- optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,
-- then the directory has no subdirectories. */
--enum { MIN_DIR_NLINK = 2 };
--
--/* Whether leaf optimization is OK for a directory. */
--enum leaf_optimization
-- {
-- /* st_nlink is not reliable for this directory's subdirectories. */
-- NO_LEAF_OPTIMIZATION,
--
-- /* st_nlink == 2 means the directory lacks subdirectories. */
-- OK_LEAF_OPTIMIZATION
-- };
--
--#if (defined __linux__ || defined __ANDROID__) \
-- && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
--
--# include <sys/vfs.h>
--
--/* Linux-specific constants from coreutils' src/fs.h */
--# define S_MAGIC_AFS 0x5346414F
--# define S_MAGIC_CIFS 0xFF534D42
--# define S_MAGIC_LUSTRE 0x0BD00BD0
--# define S_MAGIC_NFS 0x6969
--# define S_MAGIC_PROC 0x9FA0
--# define S_MAGIC_TMPFS 0x1021994
--
--# ifdef HAVE___FSWORD_T
--typedef __fsword_t fsword;
--# else
--typedef long int fsword;
--# endif
--
--/* Map a stat.st_dev number to a file system type number f_ftype. */
--struct dev_type
--{
-- dev_t st_dev;
-- fsword f_type;
--};
--
--/* Use a tiny initial size. If a traversal encounters more than
-- a few devices, the cost of growing/rehashing this table will be
-- rendered negligible by the number of inodes processed. */
--enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };
--
--static size_t
--dev_type_hash (void const *x, size_t table_size)
--{
-- struct dev_type const *ax = x;
-- uintmax_t dev = ax->st_dev;
-- return dev % table_size;
--}
--
--static bool
--dev_type_compare (void const *x, void const *y)
--{
-- struct dev_type const *ax = x;
-- struct dev_type const *ay = y;
-- return ax->st_dev == ay->st_dev;
--}
--
--/* Return the file system type of P with file descriptor FD, or 0 if not known.
-- If FD is negative, P's file descriptor is unavailable.
-- Try to cache known values. */
--
--static fsword
--filesystem_type (FTSENTRY const *p, int fd)
--{
-- FTSOBJ *sp = FTSENT_FTS(p);
--
-- /* If we're not in CWDFD mode, don't bother with this optimization,
-- since the caller is not serious about performance. */
-- if (!ISSET (FTS_CWDFD))
-- return 0;
--
-- Hash_table *h = sp->fts_leaf_optimization_works_ht;
-- if (! h)
-- h = sp->fts_leaf_optimization_works_ht
-- = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,
-- dev_type_compare, free);
--
-- if (h)
-- {
-- struct dev_type tmp;
-- tmp.st_dev = p->fts_statp->st_dev;
-- struct dev_type *ent = hash_lookup (h, &tmp);
-- if (ent)
-- return ent->f_type;
-- }
--
-- /* Look-up failed. Query directly and cache the result. */
-- struct STRUCT_STATFS fs_buf;
-- if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)
-- return 0;
--
-- if (h)
-- {
-- struct dev_type *t2 = malloc (sizeof *t2);
-- if (t2)
-- {
-- t2->st_dev = p->fts_statp->st_dev;
-- t2->f_type = fs_buf.f_type;
--
-- struct dev_type *ent = hash_insert (h, t2);
-- if (ent)
-- fts_assert (ent == t2);
-- else
-- free (t2);
-- }
-- }
--
-- return fs_buf.f_type;
--}
--
--/* Return true if sorting dirents on inode numbers is known to improve
-- traversal performance for the directory P with descriptor DIR_FD.
-- Return false otherwise. When in doubt, return true.
-- DIR_FD is negative if unavailable. */
--static bool
--dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)
--{
-- /* Skip the sort only if we can determine efficiently
-- that skipping it is the right thing to do.
-- The cost of performing an unnecessary sort is negligible,
-- while the cost of *not* performing it can be O(N^2) with
-- a very large constant. */
--
-- switch (filesystem_type (p, dir_fd))
-- {
-- case S_MAGIC_LUSTRE:
-- /* On Lustre, sorting directory entries interferes with its ability to
-- prefetch file metadata (via statahead). This would make a command
-- like 'du' around 9 times slower. See
-- <https://bugs.gnu.org/80106>. */
-- case S_MAGIC_CIFS:
-- case S_MAGIC_NFS:
-- case S_MAGIC_TMPFS:
-- /* On a file system of any of these types, sorting
-- is unnecessary, and hence wasteful. */
-- return false;
--
-- default:
-- return true;
-- }
--}
--
--/* Given an FTS entry P for a directory with descriptor DIR_FD,
-- return whether it is valid to apply leaf optimization.
-- The optimization is valid if a directory's st_nlink value equal
-- to MIN_DIR_NLINK means the directory has no subdirectories.
-- DIR_FD is negative if unavailable. */
--static enum leaf_optimization
--leaf_optimization (FTSENTRY const *p, int dir_fd)
--{
-- switch (filesystem_type (p, dir_fd))
-- {
-- case 0:
-- /* Leaf optimization is unsafe if the file system type is unknown. */
-- FALLTHROUGH;
-- case S_MAGIC_AFS:
-- /* Although AFS mount points are not counted in st_nlink, they
-- act like directories. See <https://bugs.debian.org/143111>. */
-- FALLTHROUGH;
-- case S_MAGIC_CIFS:
-- /* Leaf optimization causes 'find' to abort. See
-- <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>. */
-- FALLTHROUGH;
-- case S_MAGIC_NFS:
-- /* NFS provides usable dirent.d_type but not necessarily for all entries
-- of large directories, so as per <https://bugzilla.redhat.com/1252549>
-- NFS should return true. However st_nlink values are not accurate on
-- all implementations as per <https://bugzilla.redhat.com/1299169>. */
-- FALLTHROUGH;
-- case S_MAGIC_PROC:
-- /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc
-- may have bogus stat.st_nlink values. */
-- return NO_LEAF_OPTIMIZATION;
--
-- default:
-- return OK_LEAF_OPTIMIZATION;
-- }
--}
--
--#else
--static bool
--dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,
-- _GL_UNUSED int dir_fd)
--{
-- return true;
--}
--static enum leaf_optimization
--leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)
--{
-- return NO_LEAF_OPTIMIZATION;
--}
--#endif
--
--/*
-- * Special case of "/" at the end of the file name so that slashes aren't
-- * appended which would cause file names to be written as "....//foo".
-- */
--#define NAPPEND(p) \
-- (p->fts_path[p->fts_pathlen - 1] == '/' \
-- ? p->fts_pathlen - 1 : p->fts_pathlen)
--
--FTSENTRY *
--FTS_READ (FTSOBJ *sp)
--{
-- /* If finished or unrecoverable error, return NULL. */
-- if (sp->fts_cur == NULL || ISSET(FTS_STOP))
-- return (NULL);
--
-- /* Set current node pointer. */
-- register FTSENTRY *p = sp->fts_cur;
--
-- /* Save and zero out user instructions. */
-- register unsigned short int instr = p->fts_instr;
-- p->fts_instr = FTS_NOINSTR;
--
-- /* Any type of file may be re-visited; re-stat and re-turn. */
-- if (instr == FTS_AGAIN) {
-- p->fts_info = fts_stat(sp, p, false);
-- return (p);
-- }
-- Dprintf (("fts_read: p=%s\n",
-- p->fts_info == FTS_INIT ? "" : p->fts_path));
--
-- /*
-- * Following a symlink -- SLNONE test allows application to see
-- * SLNONE and recover. If indirecting through a symlink, have
-- * keep a pointer to current location. If unable to get that
-- * pointer, follow fails.
-- */
-- if (instr == FTS_FOLLOW &&
-- (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
-- p->fts_info = fts_stat(sp, p, true);
-- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-- p->fts_errno = errno;
-- p->fts_info = FTS_ERR;
-- } else
-- p->fts_flags |= FTS_SYMFOLLOW;
-- }
-- goto check_for_dir;
-- }
--
-- /* Directory in pre-order. */
-- if (p->fts_info == FTS_D) {
-- /* If skipped or crossed mount point, do post-order visit. */
-- if (instr == FTS_SKIP ||
-- (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
-- if (p->fts_flags & FTS_SYMFOLLOW)
-- (void)close(p->fts_symfd);
-- if (sp->fts_child) {
-- fts_lfree(sp->fts_child);
-- sp->fts_child = NULL;
-- }
-- p->fts_info = FTS_DP;
-- LEAVE_DIR (sp, p, "1");
-- return (p);
-- }
--
-- /* Rebuild if only read the names and now traversing. */
-- if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
-- CLR(FTS_NAMEONLY);
-- fts_lfree(sp->fts_child);
-- sp->fts_child = NULL;
-- }
--
-- /*
-- * Cd to the subdirectory.
-- *
-- * If have already read and now fail to chdir, whack the list
-- * to make the names come out right, and set the parent errno
-- * so the application will eventually get an error condition.
-- * Set the FTS_DONTCHDIR flag so that when we logically change
-- * directories back to the parent we don't do a chdir.
-- *
-- * If haven't read do so. If the read fails, fts_build sets
-- * FTS_STOP or the fts_info field of the node.
-- */
-- if (sp->fts_child != NULL) {
-- if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
-- p->fts_errno = errno;
-- p->fts_flags |= FTS_DONTCHDIR;
-- for (p = sp->fts_child; p != NULL;
-- p = p->fts_link)
-- p->fts_accpath =
-- p->fts_parent->fts_accpath;
-- }
-- } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
-- if (ISSET(FTS_STOP))
-- return (NULL);
-- /* If fts_build's call to fts_safe_changedir failed
-- because it was not able to fchdir into a
-- subdirectory, tell the caller. */
-- if (p->fts_errno && p->fts_info != FTS_DNR)
-- p->fts_info = FTS_ERR;
-- LEAVE_DIR (sp, p, "2");
-- return (p);
-- }
-- p = sp->fts_child;
-- sp->fts_child = NULL;
-- goto name;
-- }
--
-- /* Move to the next node on this level. */
--next: ;
-- register FTSENTRY *tmp = p;
--
-- /* If we have so many directory entries that we're reading them
-- in batches, and we've reached the end of the current batch,
-- read in a new batch. */
-- if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))
-- {
-- p = tmp->fts_parent;
-- sp->fts_cur = p;
-- sp->fts_path[p->fts_pathlen] = '\0';
--
-- if ((p = fts_build (sp, BREAD)) == NULL)
-- {
-- if (ISSET(FTS_STOP))
-- return NULL;
-- goto cd_dot_dot;
-- }
--
-- free(FTSENT_WRAPPER(tmp));
-- goto name;
-- }
--
-- if ((p = p->fts_link) != NULL) {
-- sp->fts_cur = p;
-- free(FTSENT_WRAPPER(tmp));
--
-- /*
-- * If reached the top, return to the original directory (or
-- * the root of the tree), and load the file names for the next
-- * root.
-- */
-- if (p->fts_level == FTS_ROOTLEVEL) {
-- if (restore_initial_cwd(sp)) {
-- SET(FTS_STOP);
-- return (NULL);
-- }
-- free_dir(sp);
-- fts_load(sp, p);
-- if (! setup_dir(sp)) {
-- free_dir(sp);
-- return (NULL);
-- }
-- goto check_for_dir;
-- }
--
-- /*
-- * User may have called fts_set on the node. If skipped,
-- * ignore. If followed, get a file descriptor so we can
-- * get back if necessary.
-- */
-- if (p->fts_instr == FTS_SKIP)
-- goto next;
-- if (p->fts_instr == FTS_FOLLOW) {
-- p->fts_info = fts_stat(sp, p, true);
-- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-- p->fts_errno = errno;
-- p->fts_info = FTS_ERR;
-- } else
-- p->fts_flags |= FTS_SYMFOLLOW;
-- }
-- p->fts_instr = FTS_NOINSTR;
-- }
--
--name: {
-- register char *t = sp->fts_path + NAPPEND(p->fts_parent);
-- *t++ = '/';
-- memmove(t, p->fts_name, p->fts_namelen + 1);
-- }
--check_for_dir:
-- sp->fts_cur = p;
-- if (p->fts_info == FTS_NSOK)
-- {
-- if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
-- p->fts_info = fts_stat(sp, p, false);
-- else
-- fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
-- }
--
-- /* Skip files with different device numbers when FTS_MOUNT
-- is set. */
-- if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&
-- p->fts_level != FTS_ROOTLEVEL &&
-- p->fts_statp->st_dev != sp->fts_dev)
-- goto next;
--
-- if (p->fts_info == FTS_D)
-- {
-- /* Now that P->fts_statp is guaranteed to be valid, if
-- this is a command-line directory, record its device
-- number, to be used for FTS_MOUNT and FTS_XDEV. */
-- if (p->fts_level == FTS_ROOTLEVEL)
-- sp->fts_dev = p->fts_statp->st_dev;
-- Dprintf ((" entering: %s\n", p->fts_path));
-- if (! enter_dir (sp, p))
-- return NULL;
-- }
-- return p;
-- }
--cd_dot_dot:
--
-- /* Move up to the parent node. */
-- p = tmp->fts_parent;
-- sp->fts_cur = p;
-- free(FTSENT_WRAPPER(tmp));
--
-- if (p->fts_level == FTS_ROOTPARENTLEVEL) {
-- /*
-- * Done; free everything up and set errno to 0 so the user
-- * can distinguish between error and EOF.
-- */
-- free(FTSENT_WRAPPER(p));
-- __set_errno (0);
-- return (sp->fts_cur = NULL);
-- }
--
-- fts_assert (p->fts_info != FTS_NSOK);
--
-- /* NUL terminate the file name. */
-- sp->fts_path[p->fts_pathlen] = '\0';
--
-- /*
-- * Return to the parent directory. If at a root node, restore
-- * the initial working directory. If we came through a symlink,
-- * go back through the file descriptor. Otherwise, move up
-- * one level, via "..".
-- */
-- if (p->fts_level == FTS_ROOTLEVEL) {
-- if (restore_initial_cwd(sp)) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
-- } else if (p->fts_flags & FTS_SYMFOLLOW) {
-- if (FCHDIR(sp, p->fts_symfd)) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
-- (void)close(p->fts_symfd);
-- } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
-- fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
--
-- /* If the directory causes a cycle, preserve the FTS_DC flag and keep
-- the corresponding dev/ino pair in the hash table. It is going to be
-- removed when leaving the original directory. */
-- if (p->fts_info != FTS_DC) {
-- p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
-- if (p->fts_errno == 0)
-- LEAVE_DIR (sp, p, "3");
-- }
-- return ISSET(FTS_STOP) ? NULL : p;
--}
--
--/*
-- * Fts_set takes the stream as an argument although it's not used in this
-- * implementation; it would be necessary if anyone wanted to add global
-- * semantics to fts using fts_set. An error return is allowed for similar
-- * reasons.
-- */
--/* ARGSUSED */
--int
--FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)
--{
-- if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
-- instr != FTS_NOINSTR && instr != FTS_SKIP) {
-- __set_errno (EINVAL);
-- return (1);
-- }
-- p->fts_instr = instr;
-- return (0);
--}
--
--FTSENTRY *
--FTS_CHILDREN (FTSOBJ *sp, int instr)
--{
-- if (instr != 0 && instr != FTS_NAMEONLY) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--
-- /* Set current node pointer. */
-- register FTSENTRY *p = sp->fts_cur;
--
-- /*
-- * Errno set to 0 so user can distinguish empty directory from
-- * an error.
-- */
-- __set_errno (0);
--
-- /* Fatal errors stop here. */
-- if (ISSET(FTS_STOP))
-- return (NULL);
--
-- /* Return logical hierarchy of user's arguments. */
-- if (p->fts_info == FTS_INIT)
-- return (p->fts_link);
--
-- /*
-- * If not a directory being visited in pre-order, stop here. Could
-- * allow FTS_DNR, assuming the user has fixed the problem, but the
-- * same effect is available with FTS_AGAIN.
-- */
-- if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
-- return (NULL);
--
-- /* Free up any previous child list. */
-- if (sp->fts_child != NULL)
-- fts_lfree(sp->fts_child);
--
-- if (instr == FTS_NAMEONLY) {
-- SET(FTS_NAMEONLY);
-- instr = BNAMES;
-- } else
-- instr = BCHILD;
--
-- /*
-- * If using chdir on a relative file name and called BEFORE fts_read
-- * does its chdir to the root of a traversal, we can lose -- we need to
-- * chdir into the subdirectory, and we don't know where the current
-- * directory is, so we can't get back so that the upcoming chdir by
-- * fts_read will work.
-- */
-- if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
-- ISSET(FTS_NOCHDIR))
-- return (sp->fts_child = fts_build(sp, instr));
--
-- int fd = diropen (sp, ".");
-- if (fd < 0)
-- return (sp->fts_child = NULL);
-- sp->fts_child = fts_build(sp, instr);
-- if (ISSET(FTS_CWDFD))
-- {
-- cwd_advance_fd (sp, fd, true);
-- }
-- else
-- {
-- if (fchdir(fd))
-- {
-- int saved_errno = errno;
-- close (fd);
-- __set_errno (saved_errno);
-- return NULL;
-- }
-- close (fd);
-- }
-- return (sp->fts_child);
--}
--
--/* A comparison function to sort on increasing inode number.
-- For some file system types, sorting either way makes a huge
-- performance difference for a directory with very many entries,
-- but sorting on increasing values is slightly better than sorting
-- on decreasing values. The difference is in the 5% range. */
--static int
--fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)
--{
-- return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);
--}
--
--/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
-- S_IF* bit and set ST.st_mode, thus clearing all other bits in that field. */
--static void
--set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)
--{
-- mode_t type;
-- switch (dtype)
-- {
-- case DT_BLK:
-- type = S_IFBLK;
-- break;
-- case DT_CHR:
-- type = S_IFCHR;
-- break;
-- case DT_DIR:
-- type = S_IFDIR;
-- break;
-- case DT_FIFO:
-- type = S_IFIFO;
-- break;
-- case DT_LNK:
-- type = S_IFLNK;
-- break;
-- case DT_REG:
-- type = S_IFREG;
-- break;
-- case DT_SOCK:
-- type = S_IFSOCK;
-- break;
-- default:
-- type = 0;
-- }
-- st->st_mode = type;
--}
--
--#define closedir_and_clear(dirp) \
-- do \
-- { \
-- closedir (dirp); \
-- dirp = NULL; \
-- } \
-- while (0)
--
--#define fts_opendir(file, Pdir_fd) \
-- OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
-- ? sp->fts_cwd_fd : AT_FDCWD), \
-- file, \
-- (((ISSET(FTS_PHYSICAL) \
-- && ! (ISSET(FTS_COMFOLLOW) \
-- && cur->fts_level == FTS_ROOTLEVEL)) \
-- ? O_NOFOLLOW : 0)), \
-- Pdir_fd)
--
--/*
-- * This is the tricky part -- do not casually change *anything* in here. The
-- * idea is to build the linked list of entries that are used by fts_children
-- * and fts_read. There are lots of special cases.
-- *
-- * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
-- * set and it's a physical walk (so that symbolic links can't be directories),
-- * we can do things quickly. First, if it's a 4.4BSD file system, the type
-- * of the file is in the directory entry. Otherwise, we assume that the number
-- * of subdirectories in a node is equal to the number of links to the parent.
-- * The former skips all stat calls. The latter skips stat calls in any leaf
-- * directories and for any files after the subdirectories in the directory have
-- * been found, cutting the stat calls by about 2/3.
-- */
--static FTSENTRY *
--internal_function
--fts_build (register FTSOBJ *sp, int type)
--{
-- FTSENTRY *cur = sp->fts_cur;
-- bool continue_readdir = !!FTSENT_DIRP(cur);
--
-- /* When cur->fts_dirp is non-NULL, that means we should
-- continue calling readdir on that existing DIR* pointer
-- rather than opening a new one. */
-- int dir_fd;
-- if (continue_readdir)
-- {
-- DIR *dp = FTSENT_DIRP(cur);
-- dir_fd = dirfd (dp);
-- if (dir_fd < 0)
-- {
-- int dirfd_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- if (type == BREAD)
-- {
-- cur->fts_info = FTS_DNR;
-- cur->fts_errno = dirfd_errno;
-- }
-- return NULL;
-- }
-- }
-- else
-- {
-- /* Open the directory for reading. If this fails, we're done.
-- If being called from fts_read, set the fts_info field. */
-- if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)
-- {
-- if (type == BREAD)
-- {
-- cur->fts_info = FTS_DNR;
-- cur->fts_errno = errno;
-- }
-- return NULL;
-- }
-- /* Rather than calling fts_stat for each and every entry encountered
-- in the readdir loop (below), stat each directory only right after
-- opening it. */
-- bool stat_optimization = cur->fts_info == FTS_NSOK;
--
-- if (stat_optimization
-- /* Also read the stat info again after opening a directory to
-- reveal eventual changes caused by a submount triggered by
-- the traversal. But do it only for utilities which use
-- FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
-- benefit/suffer from this feature for now. */
-- || ISSET (FTS_TIGHT_CYCLE_CHECK))
-- {
-- if (!stat_optimization)
-- LEAVE_DIR (sp, cur, "4");
-- if (FSTAT (dir_fd, cur->fts_statp) != 0)
-- {
-- int fstat_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- if (type == BREAD)
-- {
-- cur->fts_errno = fstat_errno;
-- cur->fts_info = FTS_NS;
-- }
-- __set_errno (fstat_errno);
-- return NULL;
-- }
-- if (stat_optimization)
-- cur->fts_info = FTS_D;
-- else if (! enter_dir (sp, cur))
-- {
-- int saved_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- __set_errno (saved_errno);
-- return NULL;
-- }
-- }
-- }
--
-- /* Maximum number of readdir entries to read at one time. This
-- limitation is to avoid reading millions of entries into memory
-- at once. When an fts_compar function is specified, we have no
-- choice: we must read all entries into memory before calling that
-- function. But when no such function is specified, we can read
-- entries in batches that are large enough to help us with inode-
-- sorting, yet not so large that we risk exhausting memory. */
-- size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;
--
-- /*
-- * If we're going to need to stat anything or we want to descend
-- * and stay in the directory, chdir. If this fails we keep going,
-- * but set a flag so we don't chdir after the post-order visit.
-- * We won't be able to stat anything, but we can still return the
-- * names themselves. Note, that since fts_read won't be able to
-- * chdir into the directory, it will have to return different file
-- * names than before, i.e. "a/b" instead of "b". Since the node
-- * has already been visited in pre-order, have to wait until the
-- * post-order visit to return the error. There is a special case
-- * here, if there was nothing to stat then it's not an error to
-- * not be able to stat. This is all fairly nasty. If a program
-- * needed sorted entries or stat information, they had better be
-- * checking FTS_NS on the returned nodes.
-- */
-- bool descend;
-- if (continue_readdir)
-- {
-- /* When resuming a short readdir run, we already have
-- the required dirp and dir_fd. */
-- descend = true;
-- }
-- else
-- {
-- /* Try to descend unless it is a names-only fts_children,
-- or the directory is known to lack subdirectories. */
-- descend = (type != BNAMES
-- && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)
-- && ! ISSET (FTS_SEEDOT)
-- && cur->fts_statp->st_nlink == MIN_DIR_NLINK
-- && (leaf_optimization (cur, dir_fd)
-- != NO_LEAF_OPTIMIZATION)));
-- if (descend || type == BREAD)
-- {
-- if (ISSET(FTS_CWDFD))
-- dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-- if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
-- if (descend && type == BREAD)
-- cur->fts_errno = errno;
-- cur->fts_flags |= FTS_DONTCHDIR;
-- descend = false;
-- closedir_and_clear(FTSENT_DIRP(cur));
-- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
-- close (dir_fd);
-- FTSENT_DIRP(cur) = NULL;
-- } else
-- descend = true;
-- }
-- }
--
-- /*
-- * Figure out the max file name length that can be stored in the
-- * current buffer -- the inner loop allocates more space as necessary.
-- * We really wouldn't have to do the maxlen calculations here, we
-- * could do them in fts_read before returning the name, but it's a
-- * lot easier here since the length is part of the dirent structure.
-- *
-- * If not changing directories set a pointer so that can just append
-- * each new component into the file name.
-- */
-- size_t len = NAPPEND(cur);
-- char *cp;
-- if (ISSET(FTS_NOCHDIR)) {
-- cp = sp->fts_path + len;
-- *cp++ = '/';
-- } else {
-- /* GCC, you're too verbose. */
-- cp = NULL;
-- }
-- len++;
-- size_t maxlen = sp->fts_pathlen - len;
--
-- ptrdiff_t level = cur->fts_level + 1;
--
-- /* Read the directory, attaching each entry to the "link" pointer. */
-- bool doadjust = false;
-- register FTSENTRY *head = NULL;
-- FTSENTRY *tail = NULL;
-- register size_t nitems = 0;
-- bool sort_by_inode = false;
-- while (FTSENT_DIRP(cur)) {
-- __set_errno (0);
-- struct dirent *dp = readdir(FTSENT_DIRP(cur));
-- if (dp == NULL) {
-- /* Some readdir()s do not absorb ENOENT (dir
-- deleted but open). This bug was fixed in
-- glibc 2.3 (2002). */
--#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))
-- if (errno == ENOENT)
-- errno = 0;
--#endif
-- if (errno) {
-- cur->fts_errno = errno;
-- /* If we've not read any items yet, treat
-- the error as if we can't access the dir. */
-- cur->fts_info = (continue_readdir || nitems)
-- ? FTS_ERR : FTS_DNR;
-- }
-- closedir_and_clear(FTSENT_DIRP(cur));
-- break;
-- }
-- if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
-- continue;
--
-- size_t d_namelen = _D_EXACT_NAMLEN (dp);
-- register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);
-- if (!p)
-- goto mem1;
-- if (d_namelen >= maxlen) {
-- /* include space for NUL */
-- uintptr_t oldaddr = (uintptr_t) sp->fts_path;
-- if (! fts_palloc(sp, d_namelen + len + 1)) {
-- /*
-- * No more memory. Save
-- * errno, free up the current structure and the
-- * structures already allocated.
-- */
--mem1: ;
-- int saved_errno = errno;
-- free(FTSENT_WRAPPER(p));
-- fts_lfree(head);
-- closedir_and_clear(FTSENT_DIRP(cur));
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- __set_errno (saved_errno);
-- return (NULL);
-- }
-- /* Did realloc() change the pointer? */
-- if (oldaddr != (uintptr_t) sp->fts_path) {
-- doadjust = true;
-- if (ISSET(FTS_NOCHDIR))
-- cp = sp->fts_path + len;
-- }
-- maxlen = sp->fts_pathlen - len;
-- }
--
-- size_t new_len = len + d_namelen;
-- if (new_len < len) {
-- /*
-- * In the unlikely event that we would end up
-- * with a file name longer than SIZE_MAX, free up
-- * the current structure and the structures already
-- * allocated, then error out with ENAMETOOLONG.
-- */
-- free(FTSENT_WRAPPER(p));
-- fts_lfree(head);
-- closedir_and_clear(FTSENT_DIRP(cur));
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- __set_errno (ENAMETOOLONG);
-- return (NULL);
-- }
-- p->fts_level = level;
-- p->fts_parent = sp->fts_cur;
-- p->fts_pathlen = new_len;
--
-- /* Store dirent.d_ino, in case we need to sort
-- entries before processing them. */
-- p->fts_statp->st_ino = D_INO (dp);
--
-- /* Build a file name for fts_stat to stat. */
-- if (ISSET(FTS_NOCHDIR)) {
-- p->fts_accpath = p->fts_path;
-- memmove(cp, p->fts_name, p->fts_namelen + 1);
-- } else
-- p->fts_accpath = p->fts_name;
--
-- if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
-- /* Record what fts_read will have to do with this
-- entry. In many cases, it will simply fts_stat it,
-- but we can take advantage of any d_type information
-- to optimize away the unnecessary stat calls. I.e.,
-- if FTS_NOSTAT is in effect, we don't need device
-- numbers unconditionally (FTS_MOUNT) and we're not
-- following symlinks (FTS_PHYSICAL) and d_type
-- indicates this is *not* a directory, then we won't
-- have to stat it at all. If it *is* a directory,
-- then (currently) we stat it regardless, in order to
-- get device and inode numbers. Some day we might
-- optimize that away, too, for directories where
-- d_ino is known to be valid. */
-- bool skip_stat = (ISSET(FTS_NOSTAT)
-- && DT_IS_KNOWN(dp)
-- && ! DT_MUST_BE(dp, DT_DIR)
-- && (ISSET(FTS_PHYSICAL)
-- || ! DT_MUST_BE(dp, DT_LNK))
-- && ! ISSET(FTS_MOUNT));
-- p->fts_info = FTS_NSOK;
-- /* Propagate dirent.d_type information back
-- to caller, when possible. */
-- set_stat_type (p->fts_statp, D_TYPE (dp));
-- fts_set_stat_required(p, !skip_stat);
-- } else {
-- p->fts_info = fts_stat(sp, p, false);
-- }
--
-- /* We walk in directory order so "ls -f" doesn't get upset. */
-- p->fts_link = NULL;
-- if (head == NULL)
-- head = tail = p;
-- else {
-- tail->fts_link = p;
-- tail = p;
-- }
--
-- /* If there are many entries, no sorting function has been
-- specified, and this file system is of a type that may be
-- slow with a large number of entries, arrange to sort the
-- directory entries on increasing inode numbers.
--
-- The NITEMS comparison uses ==, not >, because the test
-- needs to be tried at most once once, and NITEMS will exceed
-- the threshold after it is incremented below. */
-- if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
-- && !sp->fts_compar)
-- sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);
--
-- ++nitems;
-- if (max_entries <= nitems) {
-- /* When there are too many dir entries, leave
-- fts_dirp open, so that a subsequent fts_read
-- can take up where we leave off. */
-- break;
-- }
-- }
--
-- /*
-- * If realloc() changed the address of the file name, adjust the
-- * addresses for the rest of the tree and the dir list.
-- */
-- if (doadjust)
-- fts_padjust(sp, head);
--
-- /*
-- * If not changing directories, reset the file name back to original
-- * state.
-- */
-- if (ISSET(FTS_NOCHDIR)) {
-- if (len == sp->fts_pathlen || nitems == 0)
-- --cp;
-- *cp = '\0';
-- }
--
-- /*
-- * If descended after called from fts_children or after called from
-- * fts_read and nothing found, get back. At the root level we use
-- * the saved fd; if one of fts_open()'s arguments is a relative name
-- * to an empty directory, we wind up here with no other way back. If
-- * can't get back, we're done.
-- */
-- if (!continue_readdir && descend && (type == BCHILD || !nitems) &&
-- (cur->fts_level == FTS_ROOTLEVEL
-- ? restore_initial_cwd(sp)
-- : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- fts_lfree(head);
-- return (NULL);
-- }
--
-- /* If didn't find anything, return NULL. */
-- if (!nitems) {
-- if (type == BREAD
-- && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
-- cur->fts_info = FTS_DP;
-- fts_lfree(head);
-- return (NULL);
-- }
--
-- if (sort_by_inode) {
-- sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);
-- head = fts_sort (sp, head, nitems);
-- sp->fts_compar = NULL;
-- }
--
-- /* Sort the entries. */
-- if (sp->fts_compar && nitems > 1)
-- head = fts_sort(sp, head, nitems);
-- return (head);
--}
--
--#if GNULIB_FTS_DEBUG
--
--struct devino {
-- intmax_t dev, ino;
--};
--#define PRINT_DEVINO "(%jd,%jd)"
--
--static struct devino
--getdevino (int fd)
--{
-- struct STRUCT_STAT st;
-- return (fd == AT_FDCWD
-- ? (struct devino) { -1, 0 }
-- : FSTAT (fd, &st) == 0
-- ? (struct devino) { st.st_dev, st.st_ino }
-- : (struct devino) { -1, errno });
--}
--
--/* Walk ->fts_parent links starting at E_CURR, until the root of the
-- current hierarchy. There should be a directory with dev/inode
-- matching those of AD. If not, print a lot of diagnostics. */
--static void
--find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)
--{
-- for (FTSENTRY const *ent = e_curr;
-- ent->fts_level >= FTS_ROOTLEVEL;
-- ent = ent->fts_parent)
-- {
-- if (ad->ino == ent->fts_statp->st_ino
-- && ad->dev == ent->fts_statp->st_dev)
-- return;
-- }
-- printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
-- printf ("active dirs:\n");
-- for (FTSENTRY const *ent = e_curr;
-- ent->fts_level >= FTS_ROOTLEVEL;
-- ent = ent->fts_parent)
-- printf (" %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
-- ad->fts_ent->fts_accpath,
-- (uintmax_t) ad->dev,
-- (uintmax_t) ad->ino,
-- ent->fts_accpath,
-- (uintmax_t) ent->fts_statp->st_dev,
-- (uintmax_t) ent->fts_statp->st_ino);
--}
--
--void
--fts_cross_check (FTSOBJ const *sp)
--{
-- if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))
-- return;
--
-- FTSENTRY const *ent = sp->fts_cur;
--
-- Dprintf (("fts-cross-check cur=%s\n", ent->fts_path));
-- /* Make sure every parent dir is in the tree. */
-- for (FTSENTRY const *t = ent->fts_parent;
-- t->fts_level >= FTS_ROOTLEVEL;
-- t = t->fts_parent)
-- {
-- struct Active_dir ad;
-- ad.ino = t->fts_statp->st_ino;
-- ad.dev = t->fts_statp->st_dev;
-- if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
-- printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
-- }
--
-- /* Make sure every dir in the tree is an active dir.
-- But ENT is not necessarily a directory. If so, just skip this part. */
-- if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
-- && (ent->fts_info == FTS_DP
-- || ent->fts_info == FTS_D))
-- for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);
-- ad != NULL;
-- ad = hash_get_next (sp->fts_cycle.ht, ad))
-- {
-- find_matching_ancestor (ent, ad);
-- }
--}
--
--static bool
--same_fd (int fd1, int fd2)
--{
-- struct STRUCT_STAT sb1, sb2;
-- return (FSTAT (fd1, &sb1) == 0
-- && FSTAT (fd2, &sb2) == 0
-- && psame_inode (&sb1, &sb2));
--}
--
--static void
--fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)
--{
-- if (!fts_debug)
-- return;
-- I_ring const *fd_ring = &sp->fts_fd_ring;
-- struct devino cwd = getdevino (sp->fts_cwd_fd);
-- fprintf (stream, "=== %s ========== "PRINT_DEVINO"\n", msg, cwd.dev, cwd.ino);
-- if (i_ring_empty (fd_ring))
-- return;
--
-- unsigned int i = fd_ring->ir_front;
-- while (true)
-- {
-- int fd = fd_ring->ir_data[i];
-- if (fd < 0)
-- fprintf (stream, "%u: %d:\n", i, fd);
-- else
-- {
-- struct devino wd = getdevino (fd);
-- fprintf (stream, "%u: %d: "PRINT_DEVINO"\n", i, fd, wd.dev, wd.ino);
-- }
-- if (i == fd_ring->ir_back)
-- break;
-- i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
-- }
--}
--
--/* Ensure that each file descriptor on the fd_ring matches a
-- parent, grandparent, etc. of the current working directory. */
--static void
--fd_ring_check (FTSOBJ const *sp)
--{
-- if (!fts_debug)
-- return;
--
-- /* Make a writable copy. */
-- I_ring fd_w = sp->fts_fd_ring;
--
-- int cwd_fd = sp->fts_cwd_fd;
-- cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-- struct devino dot = getdevino (cwd_fd);
-- fprintf (stderr, "===== check ===== cwd: "PRINT_DEVINO"\n",
-- dot.dev, dot.ino);
-- while ( ! i_ring_empty (&fd_w))
-- {
-- int fd = i_ring_pop (&fd_w);
-- if (0 <= fd)
-- {
-- int open_flags = O_SEARCH | O_CLOEXEC;
-- int parent_fd = openat (cwd_fd, "..", open_flags);
-- if (parent_fd < 0)
-- {
-- // Warn?
-- break;
-- }
-- if (!same_fd (fd, parent_fd))
-- {
-- struct devino cwd = getdevino (fd);
-- fprintf (stderr, "ring : "PRINT_DEVINO"\n", cwd.dev, cwd.ino);
-- struct devino c2 = getdevino (parent_fd);
-- fprintf (stderr, "parent: "PRINT_DEVINO"\n", c2.dev, c2.ino);
-- fts_assert (0);
-- }
-- close (cwd_fd);
-- cwd_fd = parent_fd;
-- }
-- }
-- close (cwd_fd);
--}
--#endif
--
--static unsigned short int
--internal_function
--fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)
--{
-- if (ISSET (FTS_LOGICAL)
-- || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))
-- follow = true;
--
-- struct STRUCT_STAT *sbp = p->fts_statp;
--
-- /*
-- * If doing a logical walk, or application requested FTS_FOLLOW, do
-- * a stat(2). If that fails, check for a nonexistent symlink. If
-- * fail, set the errno from the stat call.
-- */
-- int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
-- if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)
-- {
-- if (follow && errno == ENOENT
-- && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,
-- AT_SYMLINK_NOFOLLOW))
-- {
-- __set_errno (0);
-- return FTS_SLNONE;
-- }
--
-- p->fts_errno = errno;
-- memset (sbp, 0, sizeof *sbp);
-- return FTS_NS;
-- }
--
-- if (S_ISDIR(sbp->st_mode)) {
-- if (ISDOT(p->fts_name)) {
-- /* Command-line "." and ".." are real directories. */
-- return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
-- }
--
-- return (FTS_D);
-- }
-- if (S_ISLNK(sbp->st_mode))
-- return (FTS_SL);
-- if (S_ISREG(sbp->st_mode))
-- return (FTS_F);
-- return (FTS_DEFAULT);
--}
--
--static int
--fts_compar (void const *a, void const *b)
--{
-- /* Convert A and B to the correct types, to pacify the compiler, and
-- for portability to bizarre hosts where "void const *" and "FTSENT
-- const **" differ in runtime representation. The comparison
-- function cannot modify *a and *b, but there is no compile-time
-- check for this. */
-- FTSENTRY const **pa = (FTSENTRY const **) a;
-- FTSENTRY const **pb = (FTSENTRY const **) b;
-- return FTSENT_FTS(pa[0])->fts_compar (pa, pb);
--}
--
--static FTSENTRY *
--internal_function
--fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)
--{
-- register FTSENTRY **ap, *p;
--
-- /* On most modern hosts, void * and FTSENT ** have the same
-- run-time representation, and one can convert sp->fts_compar to
-- the type qsort expects without problem. Use the heuristic that
-- this is OK if the two pointer types are the same size, and if
-- converting FTSENT ** to uintptr_t is the same as converting
-- FTSENT ** to void * and then to uintptr_t. This heuristic isn't
-- valid in general but we don't know of any counterexamples. */
-- FTSENTRY *dummy;
-- int (*compare) (void const *, void const *) =
-- ((sizeof &dummy == sizeof (void *)
-- && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)
-- ? (int (*) (void const *, void const *)) sp->fts_compar
-- : fts_compar);
--
-- /*
-- * Construct an array of pointers to the structures and call qsort(3).
-- * Reassemble the array in the order returned by qsort. If unable to
-- * sort for memory reasons, return the directory entries in their
-- * current order. Allocate enough space for the current needs plus
-- * 40 so don't realloc one entry at a time.
-- */
-- if (nitems > sp->fts_nitems) {
-- sp->fts_nitems = nitems + 40;
-- FTSENTRY **a;
-- if (! (a = reallocarray (sp->fts_array,
-- sp->fts_nitems, sizeof *a))) {
-- free(sp->fts_array);
-- sp->fts_array = NULL;
-- sp->fts_nitems = 0;
-- return (head);
-- }
-- sp->fts_array = a;
-- }
-- for (ap = sp->fts_array, p = head; p; p = p->fts_link)
-- *ap++ = p;
-- qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);
-- for (head = *(ap = sp->fts_array); --nitems; ++ap)
-- ap[0]->fts_link = ap[1];
-- ap[0]->fts_link = NULL;
-- return (head);
--}
--
--static FTSENTRY *
--internal_function
--fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)
--{
-- /*
-- * The file name is a variable length array. Allocate the FTSENT
-- * structure and the file name in one chunk.
-- */
-- size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);
-- register FTSENTRY *p;
--#if !_LIBC
-- p = malloc(len);
-- if (p == NULL)
-- return (NULL);
--#else
-- /*
-- * For glibc, we use a wrapper struct to provide the extra required
-- * fields without changing the FSENT layout.
-- */
-- len += sizeof (struct FTSENT_wrapper);
-- struct FTSENT_wrapper *wrapper = malloc(len);
-- if (wrapper == NULL)
-- return (NULL);
-- p = &wrapper->ent;
-- p->fts_statp = &wrapper->fts_stat;
--#endif
--
-- /* Copy the name and guarantee NUL termination. */
-- memcpy(p->fts_name, name, namelen);
-- p->fts_name[namelen] = '\0';
--
-- p->fts_namelen = namelen;
-- FTSENT_FTS(p)= sp;
-- p->fts_path = sp->fts_path;
-- p->fts_errno = 0;
-- FTSENT_DIRP(p) = NULL;
-- p->fts_flags = 0;
-- p->fts_instr = FTS_NOINSTR;
-- p->fts_number = 0;
-- p->fts_pointer = NULL;
-- return (p);
--}
--
--static void
--internal_function
--fts_lfree (register FTSENTRY *head)
--{
-- int saved_errno = errno;
--
-- /* Free a linked list of structures. */
-- register FTSENTRY *p;
-- while ((p = head)) {
-- head = head->fts_link;
-- if (FTSENT_DIRP(p))
-- closedir (FTSENT_DIRP(p));
-- free(FTSENT_WRAPPER(p));
-- }
--
-- __set_errno (saved_errno);
--}
--
--/*
-- * Allow essentially unlimited file name lengths; find, rm, ls should
-- * all work on any tree. Most systems will allow creation of file
-- * names much longer than MAXPATHLEN, even though the kernel won't
-- * resolve them. Add the size (not just what's needed) plus 256 bytes
-- * so don't realloc the file name 2 bytes at a time.
-- */
--static bool
--internal_function
--fts_palloc (FTSOBJ *sp, size_t more)
--{
-- size_t new_len = sp->fts_pathlen + more + 256;
--
-- /*
-- * See if fts_pathlen would overflow.
-- */
-- if (new_len < sp->fts_pathlen) {
-- free(sp->fts_path);
-- sp->fts_path = NULL;
-- __set_errno (ENAMETOOLONG);
-- return false;
-- }
-- sp->fts_pathlen = new_len;
-- char *p = realloc(sp->fts_path, sp->fts_pathlen);
-- if (p == NULL) {
-- free(sp->fts_path);
-- sp->fts_path = NULL;
-- return false;
-- }
-- sp->fts_path = p;
-- return true;
--}
--
--/*
-- * When the file name is realloc'd, have to fix all of the pointers in
-- * structures already returned.
-- */
--static void
--internal_function
--fts_padjust (FTSOBJ *sp, FTSENTRY *head)
--{
-- char *addr = sp->fts_path;
--
-- /* This code looks at bit-patterns of freed pointers to
-- relocate them, so it relies on undefined behavior. If this
-- trick does not work on your platform, please report a bug. */
--
--#define ADJUST(p) do { \
-- uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath; \
-- if (old_accpath != (uintptr_t) (p)->fts_name) { \
-- (p)->fts_accpath = \
-- addr + (old_accpath - (uintptr_t) (p)->fts_path); \
-- } \
-- (p)->fts_path = addr; \
--} while (0)
-- /* Adjust the current set of children. */
-- for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)
-- ADJUST(p);
--
-- /* Adjust the rest of the tree, including the current level. */
-- for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {
-- ADJUST(p);
-- p = p->fts_link ? p->fts_link : p->fts_parent;
-- }
--}
--
--static size_t
--internal_function _GL_ATTRIBUTE_PURE
--fts_maxarglen (char * const *argv)
--{
-- size_t max;
--
-- for (max = 0; *argv; ++argv) {
-- size_t len = strlen(*argv);
-- if (len > max)
-- max = len;
-- }
-- return (max + 1);
--}
--
--/*
-- * Change to dir specified by fd or file name without getting
-- * tricked by someone changing the world out from underneath us.
-- * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.
-- * If FD is non-negative, expect it to be used after this function returns,
-- * and to be closed eventually. So don't pass e.g., 'dirfd(dirp)' and then
-- * do closedir(dirp), because that would invalidate the saved FD.
-- * Upon failure, close FD immediately and return nonzero.
-- */
--static int
--internal_function
--fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)
--{
-- fts_assert (0 <= fd || dir != NULL);
-- bool is_dotdot = dir && streq (dir, "..");
--
-- /* This clause handles the unusual case in which FTS_NOCHDIR
-- is specified, along with FTS_CWDFD. In that case, there is
-- no need to change even the virtual cwd file descriptor.
-- However, if FD is non-negative, we do close it here. */
-- if (ISSET (FTS_NOCHDIR))
-- {
-- if (ISSET (FTS_CWDFD) && 0 <= fd)
-- close (fd);
-- return 0;
-- }
--
-- if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
-- {
-- /* When possible, skip the diropen and subsequent fstat+dev/ino
-- comparison. I.e., when changing to parent directory
-- (chdir ("..")), use a file descriptor from the ring and
-- save the overhead of diropen+fstat, as well as avoiding
-- failure when we lack "x" access to the virtual cwd. */
-- if ( ! i_ring_empty (&sp->fts_fd_ring))
-- {
-- int parent_fd;
-- fd_ring_print (sp, stderr, "pre-pop");
-- parent_fd = i_ring_pop (&sp->fts_fd_ring);
-- if (0 <= parent_fd)
-- {
-- fd = parent_fd;
-- dir = NULL;
-- }
-- }
-- }
--
-- int newfd = fd;
-- if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
-- return -1;
--
-- /* The following dev/inode check is necessary if we're doing a
-- "logical" traversal (through symlinks, a la chown -L), if the
-- system lacks O_NOFOLLOW support, or if we're changing to ".."
-- (but not via a popped file descriptor). When changing to the
-- name "..", O_NOFOLLOW can't help. In general, when the target is
-- not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
-- follow a symlink, so we can avoid the expense of this fstat. */
-- int ret;
-- if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
-- || (dir && streq (dir, "..")))
-- {
-- struct STRUCT_STAT sb;
-- if (FSTAT (newfd, &sb))
-- {
-- ret = -1;
-- goto bail;
-- }
-- if (p->fts_statp->st_dev != sb.st_dev
-- || p->fts_statp->st_ino != sb.st_ino)
-- {
-- __set_errno (ENOENT); /* disinformation */
-- ret = -1;
-- goto bail;
-- }
-- }
--
-- if (ISSET(FTS_CWDFD))
-- {
-- cwd_advance_fd (sp, newfd, ! is_dotdot);
-- return 0;
-- }
--
-- ret = fchdir(newfd);
--bail:
-- if (fd < 0)
-- {
-- int oerrno = errno;
-- (void)close(newfd);
-- __set_errno (oerrno);
-- }
-- return ret;
--}
-diff --git a/io/fts.c b/io/fts.c
-index d3b8e5a100f3213f..3825f0aeb4eb1594 100644
---- a/io/fts.c
-+++ b/io/fts.c
-@@ -1,23 +1,2208 @@
--/* File tree traversal functions LFS version.
-- Copyright (C) 2026 Free Software Foundation, Inc.
-- This file is part of the GNU C Library.
-+/* Traverse a file hierarchy.
-
-- The GNU C Library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
-+ Copyright (C) 2004-2026 Free Software Foundation, Inc.
-
-- The GNU C Library is distributed in the hope that it will be useful,
-+ This file is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU Lesser General Public License as
-+ published by the Free Software Foundation; either version 2.1 of the
-+ License, or (at your option) any later version.
-+
-+ This file is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU Lesser General Public License for more details.
-+
-+ You should have received a copy of the GNU Lesser General Public License
-+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+
-+/*-
-+ * Copyright (c) 1990, 1993, 1994
-+ * The Regents of the University of California. All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions
-+ * are met:
-+ * 1. Redistributions of source code must retain the above copyright
-+ * notice, this list of conditions and the following disclaimer.
-+ * 2. Redistributions in binary form must reproduce the above copyright
-+ * notice, this list of conditions and the following disclaimer in the
-+ * documentation and/or other materials provided with the distribution.
-+ * 4. Neither the name of the University nor the names of its contributors
-+ * may be used to endorse or promote products derived from this software
-+ * without specific prior written permission.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
-+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-+ * SUCH DAMAGE.
-+ */
-+
-+#include <config.h>
-+
-+#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint
-+static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
-+#endif
-+
-+#if _LIBC
-+# include <fts.h>
-+#else
-+# include "fts_.h"
-+#endif
-+#if _LIBC || HAVE_SYS_PARAM_H
-+# include <sys/param.h>
-+#endif
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#include <errno.h>
-+#include <stddef.h>
-+#include <stdint.h>
-+#include <stdlib.h>
-+#include <string.h>
-+#include <unistd.h>
-+
-+/* Support for the LFS API version. */
-+#ifndef FTS_OPEN
-+# define FTS_OPEN fts_open
-+# define FTS_CLOSE fts_close
-+# define FTS_READ fts_read
-+# define FTS_SET fts_set
-+# define FTS_CHILDREN fts_children
-+# define FTSOBJ FTS
-+# define FTSENTRY FTSENT
-+# define INO_T ino_t
-+# define STRUCT_STAT stat
-+# define FSTAT __fstat
-+# define FSTATAT __fstatat
-+# define STRUCT_STATFS statfs
-+# define FSTATFS __fstatfs
-+#endif
-+
-+#if ! _LIBC
-+# include "attribute.h"
-+# include "fcntl--.h"
-+# include "openat.h"
-+# include "opendirat.h"
-+# include "same-inode.h"
-+# define OPENDIRAT opendirat
-+# define FTSENT_WRAPPER(__p) __p
-+# define FTS_COMPAR_CAST(__fn) __fn
-+#else
-+# include <stdbool.h>
-+
-+# define internal_function
-+# define FALLTHROUGH ; [[fallthrough]]
-+# define HAVE_STRUCT_DIRENT_D_TYPE 1
-+# define GNULIB_FTS_DEBUG 0
-+# ifdef O_PATH
-+# define O_SEARCH O_PATH
-+# else
-+# define O_SEARCH O_RDONLY
-+# endif
-+# define HAVE_SYS_VFS_H 1
-+# define HAVE_FSTATFS 1
-+# define HAVE_STRUCT_STATFS_F_TYPE 1
-+# define HAVE_OPENAT 1
-+# define HAVE_WORKING_O_NOFOLLOW 1
-+# define _GL_CMP(a, b) ((a) < (b) ? -1 : (a) > (b))
-+# define OPENDIRAT __opendirat
-+
-+static inline bool openat_needs_fchdir (void)
-+{
-+ return false;
-+}
-+
-+static inline bool streq (const char *s1, const char *s2)
-+{
-+ return strcmp (s1, s2) == 0;
-+}
-+# define reallocarray __libc_reallocarray
-+# define fchdir __fchdir
-+# define close __close
-+# define closedir __closedir
-+# define fcntl __fcntl
-+# define readdir __readdir
-+# ifndef dirfd
-+# define dirfd __dirfd
-+# endif
-+# define open __open
-+# define openat __openat
-
-- You should have received a copy of the GNU Lesser General Public
-- License along with the GNU C Library; if not, see
-- <https://www.gnu.org/licenses/>. */
-+# include "cycle-check.c"
-+# include "i-ring.c"
-
--#include <sys/types.h>
-+struct FTSENT_wrapper
-+{
-+ FTSOBJ *fts_fts; /* the file hierarchy itself */
-+ DIR *fts_dirp; /* Dir pointer for any directory containing
-+ more entries than we read at one time. */
-+ struct STRUCT_STAT fts_stat;
-
--#ifndef __OFF_T_MATCHES_OFF64_T
--# include "io/fts-common.c"
-+ FTSENTRY ent;
-+};
-+
-+/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the
-+ fts_open has a function point using 'FTSENT **' as argument. */
-+# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)
-+
-+# define FTSENT_WRAPPER(p) \
-+ ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))
-+#endif
-+#define FTSENT_FTS(p) (FTSENT_WRAPPER(p)->fts_fts)
-+#define FTSENT_DIRP(p) (FTSENT_WRAPPER(p)->fts_dirp)
-+
-+#include "flexmember.h"
-+
-+#include <dirent.h>
-+#ifndef _D_EXACT_NAMLEN
-+# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)
- #endif
-+
-+#if HAVE_STRUCT_DIRENT_D_TYPE
-+/* True if the type of the directory entry D is known. */
-+# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
-+/* True if the type of the directory entry D must be T. */
-+# define DT_MUST_BE(d, t) ((d)->d_type == (t))
-+# define D_TYPE(d) ((d)->d_type)
-+#else
-+# define DT_IS_KNOWN(d) false
-+# define DT_MUST_BE(d, t) false
-+# define D_TYPE(d) DT_UNKNOWN
-+
-+# undef DT_UNKNOWN
-+# define DT_UNKNOWN 0
-+
-+/* Any nonzero values will do here, so long as they're distinct.
-+ Undef any existing macros out of the way. */
-+# undef DT_BLK
-+# undef DT_CHR
-+# undef DT_DIR
-+# undef DT_FIFO
-+# undef DT_LNK
-+# undef DT_REG
-+# undef DT_SOCK
-+# define DT_BLK 1
-+# define DT_CHR 2
-+# define DT_DIR 3
-+# define DT_FIFO 4
-+# define DT_LNK 5
-+# define DT_REG 6
-+# define DT_SOCK 7
-+#endif
-+
-+#ifndef S_IFBLK
-+# define S_IFBLK 0
-+#endif
-+#ifndef S_IFLNK
-+# define S_IFLNK 0
-+#endif
-+#ifndef S_IFSOCK
-+# define S_IFSOCK 0
-+#endif
-+
-+enum
-+{
-+ NOT_AN_INODE_NUMBER = 0
-+};
-+
-+#ifdef D_INO_IN_DIRENT
-+# define D_INO(dp) (dp)->d_ino
-+#else
-+/* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
-+# define D_INO(dp) NOT_AN_INODE_NUMBER
-+#endif
-+
-+/* If possible (see max_entries, below), read no more than this many directory
-+ entries at a time. Without this limit (i.e., when using non-NULL
-+ fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
-+ of memory, and handling 64M entries would require 16GiB of memory. */
-+#ifndef FTS_MAX_READDIR_ENTRIES
-+# define FTS_MAX_READDIR_ENTRIES 100000
-+#endif
-+
-+/* If there are more than this many entries in a directory,
-+ and the conditions mentioned below are satisfied, then sort
-+ the entries on inode number before any further processing. */
-+#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
-+# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000
-+#endif
-+
-+enum
-+{
-+ _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
-+};
-+
-+enum Fts_stat
-+{
-+ FTS_NO_STAT_REQUIRED = 1,
-+ FTS_STAT_REQUIRED = 2
-+};
-+
-+#ifndef __set_errno
-+# define __set_errno(Val) errno = (Val)
-+#endif
-+
-+/* If this host provides the openat function, then we can avoid
-+ attempting to open "." in some initialization code below. */
-+#ifdef HAVE_OPENAT
-+# define HAVE_OPENAT_SUPPORT 1
-+#else
-+# define HAVE_OPENAT_SUPPORT 0
-+#endif
-+
-+#ifdef NDEBUG
-+# define fts_assert(expr) ((void) (0 && (expr)))
-+#else
-+# define fts_assert(expr) \
-+ do \
-+ { \
-+ if (!(expr)) \
-+ abort (); \
-+ } \
-+ while (false)
-+#endif
-+
-+static FTSENTRY *fts_alloc (FTSOBJ *, const char *, size_t);
-+static FTSENTRY *fts_build (FTSOBJ *, int);
-+static void fts_lfree (FTSENTRY *);
-+static void fts_load (FTSOBJ *, FTSENTRY *);
-+static size_t fts_maxarglen (char * const *);
-+static void fts_padjust (FTSOBJ *, FTSENTRY *);
-+static bool fts_palloc (FTSOBJ *, size_t);
-+static FTSENTRY *fts_sort (FTSOBJ *, FTSENTRY *, size_t);
-+static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);
-+static int fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);
-+
-+#include "fts-cycle.c"
-+
-+#ifndef MAX
-+# define MAX(a,b) ((a) > (b) ? (a) : (b))
-+#endif
-+
-+#ifndef SIZE_MAX
-+# define SIZE_MAX ((size_t) -1)
-+#endif
-+
-+#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-+
-+#define CLR(opt) (sp->fts_options &= ~(opt))
-+#define ISSET(opt) ((sp->fts_options & (opt)) != 0)
-+#define SET(opt) (sp->fts_options |= (opt))
-+
-+/* FIXME: FTS_NOCHDIR is now misnamed.
-+ Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
-+#define FCHDIR(sp, fd) \
-+ (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD) \
-+ ? (cwd_advance_fd ((sp), (fd), true), 0) \
-+ : fchdir (fd)))
-+
-+
-+/* fts_build flags */
-+/* FIXME: make this an enum */
-+#define BCHILD 1 /* fts_children */
-+#define BNAMES 2 /* fts_children, names only */
-+#define BREAD 3 /* fts_read */
-+
-+#if GNULIB_FTS_DEBUG
-+# include <inttypes.h>
-+# include <stdio.h>
-+bool fts_debug = false;
-+# define Dprintf(x) do { if (fts_debug) printf x; } while (false)
-+static void fd_ring_check (FTSOBJ const *);
-+static void fd_ring_print (FTSOBJ const *, FILE *, char const *);
-+#else
-+# define Dprintf(x)
-+# define fd_ring_check(x)
-+# define fd_ring_print(a, b, c)
-+#endif
-+
-+#define LEAVE_DIR(Fts, Ent, Tag) \
-+ do \
-+ { \
-+ Dprintf ((" %s-leaving: %s\n", Tag, (Ent)->fts_path)); \
-+ leave_dir (Fts, Ent); \
-+ fd_ring_check (Fts); \
-+ } \
-+ while (false)
-+
-+static void
-+fd_ring_clear (I_ring *fd_ring)
-+{
-+ while ( ! i_ring_empty (fd_ring))
-+ {
-+ int fd = i_ring_pop (fd_ring);
-+ if (0 <= fd)
-+ close (fd);
-+ }
-+}
-+
-+/* Overload the fts_statp->st_size member (otherwise unused, when
-+ fts_info is FTS_NSOK) to indicate whether fts_read should stat
-+ this entry or not. */
-+static void
-+fts_set_stat_required (FTSENTRY *p, bool required)
-+{
-+ fts_assert (p->fts_info == FTS_NSOK);
-+ p->fts_statp->st_size = (required
-+ ? FTS_STAT_REQUIRED
-+ : FTS_NO_STAT_REQUIRED);
-+}
-+
-+/* Virtual fchdir. Advance SP's working directory file descriptor,
-+ SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
-+ CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
-+ open on sp->fts_cwd_fd; i.e., to move the working directory one level
-+ down. */
-+static void
-+internal_function
-+cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)
-+{
-+ int old = sp->fts_cwd_fd;
-+ fts_assert (old != fd || old == AT_FDCWD);
-+
-+ if (chdir_down_one)
-+ {
-+ /* Push "old" onto the ring.
-+ If the displaced file descriptor is non-negative, close it. */
-+ int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
-+ fd_ring_print (sp, stderr, "post-push");
-+ if (0 <= prev_fd_in_slot)
-+ close (prev_fd_in_slot); /* ignore any close failure */
-+ }
-+ else if ( ! ISSET (FTS_NOCHDIR))
-+ {
-+ if (0 <= old)
-+ close (old); /* ignore any close failure */
-+ }
-+
-+ sp->fts_cwd_fd = fd;
-+}
-+
-+/* Restore the initial, pre-traversal, "working directory".
-+ In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,
-+ we may actually change the working directory.
-+ Return 0 upon success. Upon failure, set errno and return nonzero. */
-+static int
-+restore_initial_cwd (FTSOBJ *sp)
-+{
-+ int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);
-+ fd_ring_clear (&(sp->fts_fd_ring));
-+ return fail;
-+}
-+
-+/* Open the directory DIR if possible, and return a file
-+ descriptor. Return -1 and set errno on failure. It doesn't matter
-+ whether the file descriptor has read or write access. */
-+
-+static int
-+internal_function
-+diropen (FTSOBJ const *sp, char const *dir)
-+{
-+ int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
-+ | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
-+
-+ int fd = (ISSET (FTS_CWDFD)
-+ ? openat (sp->fts_cwd_fd, dir, open_flags)
-+ : open (dir, open_flags));
-+ return fd;
-+}
-+
-+FTSOBJ *
-+FTS_OPEN (char * const *argv,
-+ register int options,
-+ int (*compar) (const FTSENTRY **, const FTSENTRY **))
-+{
-+ /* Options check: glibc added other flags after FTS_NAMEONLY and
-+ FTS_STOP, and they are assumed to be private. */
-+ if (options & ~FTS_OPTIONMASK
-+#if _LIBC
-+ || options & (FTS_NAMEONLY | FTS_STOP)
-+#endif
-+ ) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-+ if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-+#if !_LIBC
-+ if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-+#else
-+ /* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or
-+ FTS_LOGICAL is specified. */
-+ if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))
-+ options |= FTS_PHYSICAL;
-+#endif
-+
-+ /* Allocate/initialize the stream */
-+ register FTSOBJ *sp = calloc (1, sizeof *sp);
-+ if (sp == NULL)
-+ return (NULL);
-+ sp->fts_compar = FTS_COMPAR_CAST(compar);
-+ sp->fts_options = options;
-+
-+ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
-+ if (ISSET(FTS_LOGICAL)) {
-+ SET(FTS_NOCHDIR);
-+ CLR(FTS_CWDFD);
-+ }
-+
-+ /* Initialize fts_cwd_fd. */
-+ sp->fts_cwd_fd = AT_FDCWD;
-+ if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
-+ {
-+ /* While it isn't technically necessary to open "." this
-+ early, doing it here saves us the trouble of ensuring
-+ later (where it'd be messier) that "." can in fact
-+ be opened. If not, revert to FTS_NOCHDIR mode. */
-+ int fd = open (".", O_SEARCH | O_CLOEXEC);
-+ if (fd < 0)
-+ {
-+ /* Even if "." is unreadable, don't revert to FTS_NOCHDIR mode
-+ on systems like Linux+PROC_FS, where our openat emulation
-+ is good enough. Note: on a system that emulates
-+ openat via /proc, this technique can still fail, but
-+ only in extreme conditions, e.g., when the working
-+ directory cannot be saved (i.e. save_cwd fails) --
-+ and that happens on Linux only when "." is unreadable
-+ and the CWD would be longer than PATH_MAX.
-+ FIXME: once Linux kernel openat support is well established,
-+ replace the above open call and this entire if/else block
-+ with the body of the if-block below. */
-+ if ( openat_needs_fchdir ())
-+ {
-+ SET(FTS_NOCHDIR);
-+ CLR(FTS_CWDFD);
-+ }
-+ }
-+ else
-+ {
-+ close (fd);
-+ }
-+ }
-+
-+ /*
-+ * Start out with 1K of file name space, and enough, in any case,
-+ * to hold the user's file names.
-+ */
-+#ifndef MAXPATHLEN
-+# define MAXPATHLEN 1024
-+#endif
-+ {
-+ size_t maxarglen = fts_maxarglen(argv);
-+ if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
-+ goto mem1;
-+ }
-+
-+ /* Allocate/initialize root's parent. */
-+ FTSENTRY *parent = NULL;
-+ if (*argv != NULL) {
-+ if ((parent = fts_alloc(sp, "", 0)) == NULL)
-+ goto mem2;
-+ parent->fts_level = FTS_ROOTPARENTLEVEL;
-+ }
-+
-+ /* The classic fts implementation would call fts_stat with
-+ a new entry for each iteration of the loop below.
-+ If the comparison function is not specified or if the
-+ FTS_DEFER_STAT option is in effect, don't stat any entry
-+ in this loop. This is an attempt to minimize the interval
-+ between the initial stat/lstat/fstatat and the point at which
-+ a directory argument is first opened. This matters for any
-+ directory command line argument that resides on a file system
-+ without genuine i-nodes. If you specify FTS_DEFER_STAT along
-+ with a comparison function, that function must not access any
-+ data via the fts_statp pointer. */
-+ bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
-+
-+ /* Allocate/initialize root(s). */
-+ register FTSENTRY *root;
-+ register size_t nitems;
-+ FTSENTRY *tmp = NULL; /* pacify gcc */
-+ for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
-+ /* *Do* allow zero-length file names. */
-+ size_t len = strlen(*argv);
-+
-+ if ( ! (options & FTS_VERBATIM))
-+ {
-+ /* If there are two or more trailing slashes, trim all but one,
-+ but don't change "//" to "/", and do map "///" to "/". */
-+ char const *v = *argv;
-+ if (2 < len && v[len - 1] == '/')
-+ while (1 < len && v[len - 2] == '/')
-+ --len;
-+ }
-+
-+ register FTSENTRY *p = fts_alloc(sp, *argv, len);
-+ if (p == NULL)
-+ goto mem3;
-+ p->fts_level = FTS_ROOTLEVEL;
-+ p->fts_parent = parent;
-+ p->fts_accpath = p->fts_name;
-+ /* Even when defer_stat is true, be sure to stat the first
-+ command line argument, since fts_read (at least with
-+ FTS_XDEV) requires that. */
-+ if (defer_stat && root != NULL) {
-+ p->fts_info = FTS_NSOK;
-+ fts_set_stat_required(p, true);
-+ } else {
-+ p->fts_info = fts_stat(sp, p, false);
-+ }
-+
-+ /*
-+ * If comparison routine supplied, traverse in sorted
-+ * order; otherwise traverse in the order specified.
-+ */
-+ if (compar) {
-+ p->fts_link = root;
-+ root = p;
-+ } else {
-+ p->fts_link = NULL;
-+ if (root == NULL)
-+ tmp = root = p;
-+ else {
-+ tmp->fts_link = p;
-+ tmp = p;
-+ }
-+ }
-+ }
-+ if (compar && nitems > 1)
-+ root = fts_sort(sp, root, nitems);
-+
-+ /*
-+ * Allocate a dummy pointer and make fts_read think that we've just
-+ * finished the node before the root(s); set p->fts_info to FTS_INIT
-+ * so that everything about the "current" node is ignored.
-+ */
-+ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
-+ goto mem3;
-+ sp->fts_cur->fts_link = root;
-+ sp->fts_cur->fts_info = FTS_INIT;
-+ sp->fts_cur->fts_level = 1;
-+ if (! setup_dir (sp))
-+ goto mem3;
-+
-+ /*
-+ * If using chdir(2), grab a file descriptor pointing to dot to ensure
-+ * that we can get back here; this could be avoided for some file names,
-+ * but almost certainly not worth the effort. Slashes, symbolic links,
-+ * and ".." are all fairly nasty problems. Note, if we can't get the
-+ * descriptor we run anyway, just more slowly.
-+ */
-+ if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
-+ && (sp->fts_rfd = diropen (sp, ".")) < 0)
-+ SET(FTS_NOCHDIR);
-+
-+ i_ring_init (&sp->fts_fd_ring, -1);
-+ return (sp);
-+
-+mem3: fts_lfree(root);
-+ free(FTSENT_WRAPPER(parent));
-+mem2: free(sp->fts_path);
-+mem1: free(sp);
-+ return (NULL);
-+}
-+
-+static void
-+internal_function
-+fts_load (FTSOBJ *sp, register FTSENTRY *p)
-+{
-+ /*
-+ * Load the stream structure for the next traversal. Since we don't
-+ * actually enter the directory until after the preorder visit, set
-+ * the fts_accpath field specially so the chdir gets done to the right
-+ * place and the user can access the first node. From fts_open it's
-+ * known that the file name will fit.
-+ */
-+ register size_t len = p->fts_pathlen = p->fts_namelen;
-+ memmove(sp->fts_path, p->fts_name, len + 1);
-+ register char *cp = strrchr(p->fts_name, '/');
-+ if (cp && (cp != p->fts_name || cp[1])) {
-+ len = strlen(++cp);
-+ memmove(p->fts_name, cp, len + 1);
-+ p->fts_namelen = len;
-+ }
-+ p->fts_accpath = p->fts_path = sp->fts_path;
-+}
-+
-+int
-+FTS_CLOSE (FTSOBJ *sp)
-+{
-+ /*
-+ * This still works if we haven't read anything -- the dummy structure
-+ * points to the root list, so we step through to the end of the root
-+ * list which has a valid parent pointer.
-+ */
-+ if (sp->fts_cur) {
-+ register FTSENTRY *p;
-+ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
-+ register FTSENTRY *freep = p;
-+ p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
-+ free(FTSENT_WRAPPER(freep));
-+ }
-+ free(FTSENT_WRAPPER(p));
-+ }
-+
-+ /* Free up child linked list, sort array, file name buffer. */
-+ if (sp->fts_child)
-+ fts_lfree(sp->fts_child);
-+ free(sp->fts_array);
-+ free(sp->fts_path);
-+
-+ int saved_errno = 0;
-+ if (ISSET(FTS_CWDFD))
-+ {
-+ if (0 <= sp->fts_cwd_fd)
-+ if (close (sp->fts_cwd_fd))
-+ saved_errno = errno;
-+ }
-+ else if (!ISSET(FTS_NOCHDIR))
-+ {
-+ /* Return to original directory, save errno if necessary. */
-+ if (fchdir(sp->fts_rfd))
-+ saved_errno = errno;
-+
-+ /* If close fails, record errno only if saved_errno is zero,
-+ so that we report the probably-more-meaningful fchdir errno. */
-+ if (close (sp->fts_rfd))
-+ if (saved_errno == 0)
-+ saved_errno = errno;
-+ }
-+
-+ fd_ring_clear (&sp->fts_fd_ring);
-+
-+ if (sp->fts_leaf_optimization_works_ht)
-+ hash_free (sp->fts_leaf_optimization_works_ht);
-+
-+ free_dir (sp);
-+
-+ /* Free up the stream pointer. */
-+ free(sp);
-+
-+ /* Set errno and return. */
-+ if (saved_errno) {
-+ __set_errno (saved_errno);
-+ return (-1);
-+ }
-+
-+ return (0);
-+}
-+
-+/* Minimum link count of a traditional Unix directory. When leaf
-+ optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,
-+ then the directory has no subdirectories. */
-+enum { MIN_DIR_NLINK = 2 };
-+
-+/* Whether leaf optimization is OK for a directory. */
-+enum leaf_optimization
-+ {
-+ /* st_nlink is not reliable for this directory's subdirectories. */
-+ NO_LEAF_OPTIMIZATION,
-+
-+ /* st_nlink == 2 means the directory lacks subdirectories. */
-+ OK_LEAF_OPTIMIZATION
-+ };
-+
-+#if (defined __linux__ || defined __ANDROID__) \
-+ && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
-+
-+# include <sys/vfs.h>
-+
-+/* Linux-specific constants from coreutils' src/fs.h */
-+# define S_MAGIC_AFS 0x5346414F
-+# define S_MAGIC_CIFS 0xFF534D42
-+# define S_MAGIC_LUSTRE 0x0BD00BD0
-+# define S_MAGIC_NFS 0x6969
-+# define S_MAGIC_PROC 0x9FA0
-+# define S_MAGIC_TMPFS 0x1021994
-+
-+# ifdef HAVE___FSWORD_T
-+typedef __fsword_t fsword;
-+# else
-+typedef long int fsword;
-+# endif
-+
-+/* Map a stat.st_dev number to a file system type number f_ftype. */
-+struct dev_type
-+{
-+ dev_t st_dev;
-+ fsword f_type;
-+};
-+
-+/* Use a tiny initial size. If a traversal encounters more than
-+ a few devices, the cost of growing/rehashing this table will be
-+ rendered negligible by the number of inodes processed. */
-+enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };
-+
-+static size_t
-+dev_type_hash (void const *x, size_t table_size)
-+{
-+ struct dev_type const *ax = x;
-+ uintmax_t dev = ax->st_dev;
-+ return dev % table_size;
-+}
-+
-+static bool
-+dev_type_compare (void const *x, void const *y)
-+{
-+ struct dev_type const *ax = x;
-+ struct dev_type const *ay = y;
-+ return ax->st_dev == ay->st_dev;
-+}
-+
-+/* Return the file system type of P with file descriptor FD, or 0 if not known.
-+ If FD is negative, P's file descriptor is unavailable.
-+ Try to cache known values. */
-+
-+static fsword
-+filesystem_type (FTSENTRY const *p, int fd)
-+{
-+ FTSOBJ *sp = FTSENT_FTS(p);
-+
-+ /* If we're not in CWDFD mode, don't bother with this optimization,
-+ since the caller is not serious about performance. */
-+ if (!ISSET (FTS_CWDFD))
-+ return 0;
-+
-+ Hash_table *h = sp->fts_leaf_optimization_works_ht;
-+ if (! h)
-+ h = sp->fts_leaf_optimization_works_ht
-+ = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,
-+ dev_type_compare, free);
-+
-+ if (h)
-+ {
-+ struct dev_type tmp;
-+ tmp.st_dev = p->fts_statp->st_dev;
-+ struct dev_type *ent = hash_lookup (h, &tmp);
-+ if (ent)
-+ return ent->f_type;
-+ }
-+
-+ /* Look-up failed. Query directly and cache the result. */
-+ struct STRUCT_STATFS fs_buf;
-+ if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)
-+ return 0;
-+
-+ if (h)
-+ {
-+ struct dev_type *t2 = malloc (sizeof *t2);
-+ if (t2)
-+ {
-+ t2->st_dev = p->fts_statp->st_dev;
-+ t2->f_type = fs_buf.f_type;
-+
-+ struct dev_type *ent = hash_insert (h, t2);
-+ if (ent)
-+ fts_assert (ent == t2);
-+ else
-+ free (t2);
-+ }
-+ }
-+
-+ return fs_buf.f_type;
-+}
-+
-+/* Return true if sorting dirents on inode numbers is known to improve
-+ traversal performance for the directory P with descriptor DIR_FD.
-+ Return false otherwise. When in doubt, return true.
-+ DIR_FD is negative if unavailable. */
-+static bool
-+dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)
-+{
-+ /* Skip the sort only if we can determine efficiently
-+ that skipping it is the right thing to do.
-+ The cost of performing an unnecessary sort is negligible,
-+ while the cost of *not* performing it can be O(N^2) with
-+ a very large constant. */
-+
-+ switch (filesystem_type (p, dir_fd))
-+ {
-+ case S_MAGIC_LUSTRE:
-+ /* On Lustre, sorting directory entries interferes with its ability to
-+ prefetch file metadata (via statahead). This would make a command
-+ like 'du' around 9 times slower. See
-+ <https://bugs.gnu.org/80106>. */
-+ case S_MAGIC_CIFS:
-+ case S_MAGIC_NFS:
-+ case S_MAGIC_TMPFS:
-+ /* On a file system of any of these types, sorting
-+ is unnecessary, and hence wasteful. */
-+ return false;
-+
-+ default:
-+ return true;
-+ }
-+}
-+
-+/* Given an FTS entry P for a directory with descriptor DIR_FD,
-+ return whether it is valid to apply leaf optimization.
-+ The optimization is valid if a directory's st_nlink value equal
-+ to MIN_DIR_NLINK means the directory has no subdirectories.
-+ DIR_FD is negative if unavailable. */
-+static enum leaf_optimization
-+leaf_optimization (FTSENTRY const *p, int dir_fd)
-+{
-+ switch (filesystem_type (p, dir_fd))
-+ {
-+ case 0:
-+ /* Leaf optimization is unsafe if the file system type is unknown. */
-+ FALLTHROUGH;
-+ case S_MAGIC_AFS:
-+ /* Although AFS mount points are not counted in st_nlink, they
-+ act like directories. See <https://bugs.debian.org/143111>. */
-+ FALLTHROUGH;
-+ case S_MAGIC_CIFS:
-+ /* Leaf optimization causes 'find' to abort. See
-+ <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>. */
-+ FALLTHROUGH;
-+ case S_MAGIC_NFS:
-+ /* NFS provides usable dirent.d_type but not necessarily for all entries
-+ of large directories, so as per <https://bugzilla.redhat.com/1252549>
-+ NFS should return true. However st_nlink values are not accurate on
-+ all implementations as per <https://bugzilla.redhat.com/1299169>. */
-+ FALLTHROUGH;
-+ case S_MAGIC_PROC:
-+ /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc
-+ may have bogus stat.st_nlink values. */
-+ return NO_LEAF_OPTIMIZATION;
-+
-+ default:
-+ return OK_LEAF_OPTIMIZATION;
-+ }
-+}
-+
-+#else
-+static bool
-+dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,
-+ _GL_UNUSED int dir_fd)
-+{
-+ return true;
-+}
-+static enum leaf_optimization
-+leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)
-+{
-+ return NO_LEAF_OPTIMIZATION;
-+}
-+#endif
-+
-+/*
-+ * Special case of "/" at the end of the file name so that slashes aren't
-+ * appended which would cause file names to be written as "....//foo".
-+ */
-+#define NAPPEND(p) \
-+ (p->fts_path[p->fts_pathlen - 1] == '/' \
-+ ? p->fts_pathlen - 1 : p->fts_pathlen)
-+
-+FTSENTRY *
-+FTS_READ (FTSOBJ *sp)
-+{
-+ /* If finished or unrecoverable error, return NULL. */
-+ if (sp->fts_cur == NULL || ISSET(FTS_STOP))
-+ return (NULL);
-+
-+ /* Set current node pointer. */
-+ register FTSENTRY *p = sp->fts_cur;
-+
-+ /* Save and zero out user instructions. */
-+ register unsigned short int instr = p->fts_instr;
-+ p->fts_instr = FTS_NOINSTR;
-+
-+ /* Any type of file may be re-visited; re-stat and re-turn. */
-+ if (instr == FTS_AGAIN) {
-+ p->fts_info = fts_stat(sp, p, false);
-+ return (p);
-+ }
-+ Dprintf (("fts_read: p=%s\n",
-+ p->fts_info == FTS_INIT ? "" : p->fts_path));
-+
-+ /*
-+ * Following a symlink -- SLNONE test allows application to see
-+ * SLNONE and recover. If indirecting through a symlink, have
-+ * keep a pointer to current location. If unable to get that
-+ * pointer, follow fails.
-+ */
-+ if (instr == FTS_FOLLOW &&
-+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
-+ p->fts_info = fts_stat(sp, p, true);
-+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-+ if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-+ p->fts_errno = errno;
-+ p->fts_info = FTS_ERR;
-+ } else
-+ p->fts_flags |= FTS_SYMFOLLOW;
-+ }
-+ goto check_for_dir;
-+ }
-+
-+ /* Directory in pre-order. */
-+ if (p->fts_info == FTS_D) {
-+ /* If skipped or crossed mount point, do post-order visit. */
-+ if (instr == FTS_SKIP ||
-+ (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
-+ if (p->fts_flags & FTS_SYMFOLLOW)
-+ (void)close(p->fts_symfd);
-+ if (sp->fts_child) {
-+ fts_lfree(sp->fts_child);
-+ sp->fts_child = NULL;
-+ }
-+ p->fts_info = FTS_DP;
-+ LEAVE_DIR (sp, p, "1");
-+ return (p);
-+ }
-+
-+ /* Rebuild if only read the names and now traversing. */
-+ if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
-+ CLR(FTS_NAMEONLY);
-+ fts_lfree(sp->fts_child);
-+ sp->fts_child = NULL;
-+ }
-+
-+ /*
-+ * Cd to the subdirectory.
-+ *
-+ * If have already read and now fail to chdir, whack the list
-+ * to make the names come out right, and set the parent errno
-+ * so the application will eventually get an error condition.
-+ * Set the FTS_DONTCHDIR flag so that when we logically change
-+ * directories back to the parent we don't do a chdir.
-+ *
-+ * If haven't read do so. If the read fails, fts_build sets
-+ * FTS_STOP or the fts_info field of the node.
-+ */
-+ if (sp->fts_child != NULL) {
-+ if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
-+ p->fts_errno = errno;
-+ p->fts_flags |= FTS_DONTCHDIR;
-+ for (p = sp->fts_child; p != NULL;
-+ p = p->fts_link)
-+ p->fts_accpath =
-+ p->fts_parent->fts_accpath;
-+ }
-+ } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
-+ if (ISSET(FTS_STOP))
-+ return (NULL);
-+ /* If fts_build's call to fts_safe_changedir failed
-+ because it was not able to fchdir into a
-+ subdirectory, tell the caller. */
-+ if (p->fts_errno && p->fts_info != FTS_DNR)
-+ p->fts_info = FTS_ERR;
-+ LEAVE_DIR (sp, p, "2");
-+ return (p);
-+ }
-+ p = sp->fts_child;
-+ sp->fts_child = NULL;
-+ goto name;
-+ }
-+
-+ /* Move to the next node on this level. */
-+next: ;
-+ register FTSENTRY *tmp = p;
-+
-+ /* If we have so many directory entries that we're reading them
-+ in batches, and we've reached the end of the current batch,
-+ read in a new batch. */
-+ if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))
-+ {
-+ p = tmp->fts_parent;
-+ sp->fts_cur = p;
-+ sp->fts_path[p->fts_pathlen] = '\0';
-+
-+ if ((p = fts_build (sp, BREAD)) == NULL)
-+ {
-+ if (ISSET(FTS_STOP))
-+ return NULL;
-+ goto cd_dot_dot;
-+ }
-+
-+ free(FTSENT_WRAPPER(tmp));
-+ goto name;
-+ }
-+
-+ if ((p = p->fts_link) != NULL) {
-+ sp->fts_cur = p;
-+ free(FTSENT_WRAPPER(tmp));
-+
-+ /*
-+ * If reached the top, return to the original directory (or
-+ * the root of the tree), and load the file names for the next
-+ * root.
-+ */
-+ if (p->fts_level == FTS_ROOTLEVEL) {
-+ if (restore_initial_cwd(sp)) {
-+ SET(FTS_STOP);
-+ return (NULL);
-+ }
-+ free_dir(sp);
-+ fts_load(sp, p);
-+ if (! setup_dir(sp)) {
-+ free_dir(sp);
-+ return (NULL);
-+ }
-+ goto check_for_dir;
-+ }
-+
-+ /*
-+ * User may have called fts_set on the node. If skipped,
-+ * ignore. If followed, get a file descriptor so we can
-+ * get back if necessary.
-+ */
-+ if (p->fts_instr == FTS_SKIP)
-+ goto next;
-+ if (p->fts_instr == FTS_FOLLOW) {
-+ p->fts_info = fts_stat(sp, p, true);
-+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-+ if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-+ p->fts_errno = errno;
-+ p->fts_info = FTS_ERR;
-+ } else
-+ p->fts_flags |= FTS_SYMFOLLOW;
-+ }
-+ p->fts_instr = FTS_NOINSTR;
-+ }
-+
-+name: {
-+ register char *t = sp->fts_path + NAPPEND(p->fts_parent);
-+ *t++ = '/';
-+ memmove(t, p->fts_name, p->fts_namelen + 1);
-+ }
-+check_for_dir:
-+ sp->fts_cur = p;
-+ if (p->fts_info == FTS_NSOK)
-+ {
-+ if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
-+ p->fts_info = fts_stat(sp, p, false);
-+ else
-+ fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
-+ }
-+
-+ /* Skip files with different device numbers when FTS_MOUNT
-+ is set. */
-+ if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&
-+ p->fts_level != FTS_ROOTLEVEL &&
-+ p->fts_statp->st_dev != sp->fts_dev)
-+ goto next;
-+
-+ if (p->fts_info == FTS_D)
-+ {
-+ /* Now that P->fts_statp is guaranteed to be valid, if
-+ this is a command-line directory, record its device
-+ number, to be used for FTS_MOUNT and FTS_XDEV. */
-+ if (p->fts_level == FTS_ROOTLEVEL)
-+ sp->fts_dev = p->fts_statp->st_dev;
-+ Dprintf ((" entering: %s\n", p->fts_path));
-+ if (! enter_dir (sp, p))
-+ return NULL;
-+ }
-+ return p;
-+ }
-+cd_dot_dot:
-+
-+ /* Move up to the parent node. */
-+ p = tmp->fts_parent;
-+ sp->fts_cur = p;
-+ free(FTSENT_WRAPPER(tmp));
-+
-+ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
-+ /*
-+ * Done; free everything up and set errno to 0 so the user
-+ * can distinguish between error and EOF.
-+ */
-+ free(FTSENT_WRAPPER(p));
-+ __set_errno (0);
-+ return (sp->fts_cur = NULL);
-+ }
-+
-+ fts_assert (p->fts_info != FTS_NSOK);
-+
-+ /* NUL terminate the file name. */
-+ sp->fts_path[p->fts_pathlen] = '\0';
-+
-+ /*
-+ * Return to the parent directory. If at a root node, restore
-+ * the initial working directory. If we came through a symlink,
-+ * go back through the file descriptor. Otherwise, move up
-+ * one level, via "..".
-+ */
-+ if (p->fts_level == FTS_ROOTLEVEL) {
-+ if (restore_initial_cwd(sp)) {
-+ p->fts_errno = errno;
-+ SET(FTS_STOP);
-+ }
-+ } else if (p->fts_flags & FTS_SYMFOLLOW) {
-+ if (FCHDIR(sp, p->fts_symfd)) {
-+ p->fts_errno = errno;
-+ SET(FTS_STOP);
-+ }
-+ (void)close(p->fts_symfd);
-+ } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
-+ fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
-+ p->fts_errno = errno;
-+ SET(FTS_STOP);
-+ }
-+
-+ /* If the directory causes a cycle, preserve the FTS_DC flag and keep
-+ the corresponding dev/ino pair in the hash table. It is going to be
-+ removed when leaving the original directory. */
-+ if (p->fts_info != FTS_DC) {
-+ p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
-+ if (p->fts_errno == 0)
-+ LEAVE_DIR (sp, p, "3");
-+ }
-+ return ISSET(FTS_STOP) ? NULL : p;
-+}
-+
-+/*
-+ * Fts_set takes the stream as an argument although it's not used in this
-+ * implementation; it would be necessary if anyone wanted to add global
-+ * semantics to fts using fts_set. An error return is allowed for similar
-+ * reasons.
-+ */
-+/* ARGSUSED */
-+int
-+FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)
-+{
-+ if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
-+ instr != FTS_NOINSTR && instr != FTS_SKIP) {
-+ __set_errno (EINVAL);
-+ return (1);
-+ }
-+ p->fts_instr = instr;
-+ return (0);
-+}
-+
-+FTSENTRY *
-+FTS_CHILDREN (FTSOBJ *sp, int instr)
-+{
-+ if (instr != 0 && instr != FTS_NAMEONLY) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-+
-+ /* Set current node pointer. */
-+ register FTSENTRY *p = sp->fts_cur;
-+
-+ /*
-+ * Errno set to 0 so user can distinguish empty directory from
-+ * an error.
-+ */
-+ __set_errno (0);
-+
-+ /* Fatal errors stop here. */
-+ if (ISSET(FTS_STOP))
-+ return (NULL);
-+
-+ /* Return logical hierarchy of user's arguments. */
-+ if (p->fts_info == FTS_INIT)
-+ return (p->fts_link);
-+
-+ /*
-+ * If not a directory being visited in pre-order, stop here. Could
-+ * allow FTS_DNR, assuming the user has fixed the problem, but the
-+ * same effect is available with FTS_AGAIN.
-+ */
-+ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
-+ return (NULL);
-+
-+ /* Free up any previous child list. */
-+ if (sp->fts_child != NULL)
-+ fts_lfree(sp->fts_child);
-+
-+ if (instr == FTS_NAMEONLY) {
-+ SET(FTS_NAMEONLY);
-+ instr = BNAMES;
-+ } else
-+ instr = BCHILD;
-+
-+ /*
-+ * If using chdir on a relative file name and called BEFORE fts_read
-+ * does its chdir to the root of a traversal, we can lose -- we need to
-+ * chdir into the subdirectory, and we don't know where the current
-+ * directory is, so we can't get back so that the upcoming chdir by
-+ * fts_read will work.
-+ */
-+ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
-+ ISSET(FTS_NOCHDIR))
-+ return (sp->fts_child = fts_build(sp, instr));
-+
-+ int fd = diropen (sp, ".");
-+ if (fd < 0)
-+ return (sp->fts_child = NULL);
-+ sp->fts_child = fts_build(sp, instr);
-+ if (ISSET(FTS_CWDFD))
-+ {
-+ cwd_advance_fd (sp, fd, true);
-+ }
-+ else
-+ {
-+ if (fchdir(fd))
-+ {
-+ int saved_errno = errno;
-+ close (fd);
-+ __set_errno (saved_errno);
-+ return NULL;
-+ }
-+ close (fd);
-+ }
-+ return (sp->fts_child);
-+}
-+
-+/* A comparison function to sort on increasing inode number.
-+ For some file system types, sorting either way makes a huge
-+ performance difference for a directory with very many entries,
-+ but sorting on increasing values is slightly better than sorting
-+ on decreasing values. The difference is in the 5% range. */
-+static int
-+fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)
-+{
-+ return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);
-+}
-+
-+/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
-+ S_IF* bit and set ST.st_mode, thus clearing all other bits in that field. */
-+static void
-+set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)
-+{
-+ mode_t type;
-+ switch (dtype)
-+ {
-+ case DT_BLK:
-+ type = S_IFBLK;
-+ break;
-+ case DT_CHR:
-+ type = S_IFCHR;
-+ break;
-+ case DT_DIR:
-+ type = S_IFDIR;
-+ break;
-+ case DT_FIFO:
-+ type = S_IFIFO;
-+ break;
-+ case DT_LNK:
-+ type = S_IFLNK;
-+ break;
-+ case DT_REG:
-+ type = S_IFREG;
-+ break;
-+ case DT_SOCK:
-+ type = S_IFSOCK;
-+ break;
-+ default:
-+ type = 0;
-+ }
-+ st->st_mode = type;
-+}
-+
-+#define closedir_and_clear(dirp) \
-+ do \
-+ { \
-+ closedir (dirp); \
-+ dirp = NULL; \
-+ } \
-+ while (0)
-+
-+#define fts_opendir(file, Pdir_fd) \
-+ OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
-+ ? sp->fts_cwd_fd : AT_FDCWD), \
-+ file, \
-+ (((ISSET(FTS_PHYSICAL) \
-+ && ! (ISSET(FTS_COMFOLLOW) \
-+ && cur->fts_level == FTS_ROOTLEVEL)) \
-+ ? O_NOFOLLOW : 0)), \
-+ Pdir_fd)
-+
-+/*
-+ * This is the tricky part -- do not casually change *anything* in here. The
-+ * idea is to build the linked list of entries that are used by fts_children
-+ * and fts_read. There are lots of special cases.
-+ *
-+ * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
-+ * set and it's a physical walk (so that symbolic links can't be directories),
-+ * we can do things quickly. First, if it's a 4.4BSD file system, the type
-+ * of the file is in the directory entry. Otherwise, we assume that the number
-+ * of subdirectories in a node is equal to the number of links to the parent.
-+ * The former skips all stat calls. The latter skips stat calls in any leaf
-+ * directories and for any files after the subdirectories in the directory have
-+ * been found, cutting the stat calls by about 2/3.
-+ */
-+static FTSENTRY *
-+internal_function
-+fts_build (register FTSOBJ *sp, int type)
-+{
-+ FTSENTRY *cur = sp->fts_cur;
-+ bool continue_readdir = !!FTSENT_DIRP(cur);
-+
-+ /* When cur->fts_dirp is non-NULL, that means we should
-+ continue calling readdir on that existing DIR* pointer
-+ rather than opening a new one. */
-+ int dir_fd;
-+ if (continue_readdir)
-+ {
-+ DIR *dp = FTSENT_DIRP(cur);
-+ dir_fd = dirfd (dp);
-+ if (dir_fd < 0)
-+ {
-+ int dirfd_errno = errno;
-+ closedir_and_clear (FTSENT_DIRP(cur));
-+ if (type == BREAD)
-+ {
-+ cur->fts_info = FTS_DNR;
-+ cur->fts_errno = dirfd_errno;
-+ }
-+ return NULL;
-+ }
-+ }
-+ else
-+ {
-+ /* Open the directory for reading. If this fails, we're done.
-+ If being called from fts_read, set the fts_info field. */
-+ if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)
-+ {
-+ if (type == BREAD)
-+ {
-+ cur->fts_info = FTS_DNR;
-+ cur->fts_errno = errno;
-+ }
-+ return NULL;
-+ }
-+ /* Rather than calling fts_stat for each and every entry encountered
-+ in the readdir loop (below), stat each directory only right after
-+ opening it. */
-+ bool stat_optimization = cur->fts_info == FTS_NSOK;
-+
-+ if (stat_optimization
-+ /* Also read the stat info again after opening a directory to
-+ reveal eventual changes caused by a submount triggered by
-+ the traversal. But do it only for utilities which use
-+ FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
-+ benefit/suffer from this feature for now. */
-+ || ISSET (FTS_TIGHT_CYCLE_CHECK))
-+ {
-+ if (!stat_optimization)
-+ LEAVE_DIR (sp, cur, "4");
-+ if (FSTAT (dir_fd, cur->fts_statp) != 0)
-+ {
-+ int fstat_errno = errno;
-+ closedir_and_clear (FTSENT_DIRP(cur));
-+ if (type == BREAD)
-+ {
-+ cur->fts_errno = fstat_errno;
-+ cur->fts_info = FTS_NS;
-+ }
-+ __set_errno (fstat_errno);
-+ return NULL;
-+ }
-+ if (stat_optimization)
-+ cur->fts_info = FTS_D;
-+ else if (! enter_dir (sp, cur))
-+ {
-+ int saved_errno = errno;
-+ closedir_and_clear (FTSENT_DIRP(cur));
-+ __set_errno (saved_errno);
-+ return NULL;
-+ }
-+ }
-+ }
-+
-+ /* Maximum number of readdir entries to read at one time. This
-+ limitation is to avoid reading millions of entries into memory
-+ at once. When an fts_compar function is specified, we have no
-+ choice: we must read all entries into memory before calling that
-+ function. But when no such function is specified, we can read
-+ entries in batches that are large enough to help us with inode-
-+ sorting, yet not so large that we risk exhausting memory. */
-+ size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;
-+
-+ /*
-+ * If we're going to need to stat anything or we want to descend
-+ * and stay in the directory, chdir. If this fails we keep going,
-+ * but set a flag so we don't chdir after the post-order visit.
-+ * We won't be able to stat anything, but we can still return the
-+ * names themselves. Note, that since fts_read won't be able to
-+ * chdir into the directory, it will have to return different file
-+ * names than before, i.e. "a/b" instead of "b". Since the node
-+ * has already been visited in pre-order, have to wait until the
-+ * post-order visit to return the error. There is a special case
-+ * here, if there was nothing to stat then it's not an error to
-+ * not be able to stat. This is all fairly nasty. If a program
-+ * needed sorted entries or stat information, they had better be
-+ * checking FTS_NS on the returned nodes.
-+ */
-+ bool descend;
-+ if (continue_readdir)
-+ {
-+ /* When resuming a short readdir run, we already have
-+ the required dirp and dir_fd. */
-+ descend = true;
-+ }
-+ else
-+ {
-+ /* Try to descend unless it is a names-only fts_children,
-+ or the directory is known to lack subdirectories. */
-+ descend = (type != BNAMES
-+ && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)
-+ && ! ISSET (FTS_SEEDOT)
-+ && cur->fts_statp->st_nlink == MIN_DIR_NLINK
-+ && (leaf_optimization (cur, dir_fd)
-+ != NO_LEAF_OPTIMIZATION)));
-+ if (descend || type == BREAD)
-+ {
-+ if (ISSET(FTS_CWDFD))
-+ dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-+ if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
-+ if (descend && type == BREAD)
-+ cur->fts_errno = errno;
-+ cur->fts_flags |= FTS_DONTCHDIR;
-+ descend = false;
-+ closedir_and_clear(FTSENT_DIRP(cur));
-+ if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
-+ close (dir_fd);
-+ FTSENT_DIRP(cur) = NULL;
-+ } else
-+ descend = true;
-+ }
-+ }
-+
-+ /*
-+ * Figure out the max file name length that can be stored in the
-+ * current buffer -- the inner loop allocates more space as necessary.
-+ * We really wouldn't have to do the maxlen calculations here, we
-+ * could do them in fts_read before returning the name, but it's a
-+ * lot easier here since the length is part of the dirent structure.
-+ *
-+ * If not changing directories set a pointer so that can just append
-+ * each new component into the file name.
-+ */
-+ size_t len = NAPPEND(cur);
-+ char *cp;
-+ if (ISSET(FTS_NOCHDIR)) {
-+ cp = sp->fts_path + len;
-+ *cp++ = '/';
-+ } else {
-+ /* GCC, you're too verbose. */
-+ cp = NULL;
-+ }
-+ len++;
-+ size_t maxlen = sp->fts_pathlen - len;
-+
-+ ptrdiff_t level = cur->fts_level + 1;
-+
-+ /* Read the directory, attaching each entry to the "link" pointer. */
-+ bool doadjust = false;
-+ register FTSENTRY *head = NULL;
-+ FTSENTRY *tail = NULL;
-+ register size_t nitems = 0;
-+ bool sort_by_inode = false;
-+ while (FTSENT_DIRP(cur)) {
-+ __set_errno (0);
-+ struct dirent *dp = readdir(FTSENT_DIRP(cur));
-+ if (dp == NULL) {
-+ /* Some readdir()s do not absorb ENOENT (dir
-+ deleted but open). This bug was fixed in
-+ glibc 2.3 (2002). */
-+#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))
-+ if (errno == ENOENT)
-+ errno = 0;
-+#endif
-+ if (errno) {
-+ cur->fts_errno = errno;
-+ /* If we've not read any items yet, treat
-+ the error as if we can't access the dir. */
-+ cur->fts_info = (continue_readdir || nitems)
-+ ? FTS_ERR : FTS_DNR;
-+ }
-+ closedir_and_clear(FTSENT_DIRP(cur));
-+ break;
-+ }
-+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
-+ continue;
-+
-+ size_t d_namelen = _D_EXACT_NAMLEN (dp);
-+ register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);
-+ if (!p)
-+ goto mem1;
-+ if (d_namelen >= maxlen) {
-+ /* include space for NUL */
-+ uintptr_t oldaddr = (uintptr_t) sp->fts_path;
-+ if (! fts_palloc(sp, d_namelen + len + 1)) {
-+ /*
-+ * No more memory. Save
-+ * errno, free up the current structure and the
-+ * structures already allocated.
-+ */
-+mem1: ;
-+ int saved_errno = errno;
-+ free(FTSENT_WRAPPER(p));
-+ fts_lfree(head);
-+ closedir_and_clear(FTSENT_DIRP(cur));
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ __set_errno (saved_errno);
-+ return (NULL);
-+ }
-+ /* Did realloc() change the pointer? */
-+ if (oldaddr != (uintptr_t) sp->fts_path) {
-+ doadjust = true;
-+ if (ISSET(FTS_NOCHDIR))
-+ cp = sp->fts_path + len;
-+ }
-+ maxlen = sp->fts_pathlen - len;
-+ }
-+
-+ size_t new_len = len + d_namelen;
-+ if (new_len < len) {
-+ /*
-+ * In the unlikely event that we would end up
-+ * with a file name longer than SIZE_MAX, free up
-+ * the current structure and the structures already
-+ * allocated, then error out with ENAMETOOLONG.
-+ */
-+ free(FTSENT_WRAPPER(p));
-+ fts_lfree(head);
-+ closedir_and_clear(FTSENT_DIRP(cur));
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ __set_errno (ENAMETOOLONG);
-+ return (NULL);
-+ }
-+ p->fts_level = level;
-+ p->fts_parent = sp->fts_cur;
-+ p->fts_pathlen = new_len;
-+
-+ /* Store dirent.d_ino, in case we need to sort
-+ entries before processing them. */
-+ p->fts_statp->st_ino = D_INO (dp);
-+
-+ /* Build a file name for fts_stat to stat. */
-+ if (ISSET(FTS_NOCHDIR)) {
-+ p->fts_accpath = p->fts_path;
-+ memmove(cp, p->fts_name, p->fts_namelen + 1);
-+ } else
-+ p->fts_accpath = p->fts_name;
-+
-+ if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
-+ /* Record what fts_read will have to do with this
-+ entry. In many cases, it will simply fts_stat it,
-+ but we can take advantage of any d_type information
-+ to optimize away the unnecessary stat calls. I.e.,
-+ if FTS_NOSTAT is in effect, we don't need device
-+ numbers unconditionally (FTS_MOUNT) and we're not
-+ following symlinks (FTS_PHYSICAL) and d_type
-+ indicates this is *not* a directory, then we won't
-+ have to stat it at all. If it *is* a directory,
-+ then (currently) we stat it regardless, in order to
-+ get device and inode numbers. Some day we might
-+ optimize that away, too, for directories where
-+ d_ino is known to be valid. */
-+ bool skip_stat = (ISSET(FTS_NOSTAT)
-+ && DT_IS_KNOWN(dp)
-+ && ! DT_MUST_BE(dp, DT_DIR)
-+ && (ISSET(FTS_PHYSICAL)
-+ || ! DT_MUST_BE(dp, DT_LNK))
-+ && ! ISSET(FTS_MOUNT));
-+ p->fts_info = FTS_NSOK;
-+ /* Propagate dirent.d_type information back
-+ to caller, when possible. */
-+ set_stat_type (p->fts_statp, D_TYPE (dp));
-+ fts_set_stat_required(p, !skip_stat);
-+ } else {
-+ p->fts_info = fts_stat(sp, p, false);
-+ }
-+
-+ /* We walk in directory order so "ls -f" doesn't get upset. */
-+ p->fts_link = NULL;
-+ if (head == NULL)
-+ head = tail = p;
-+ else {
-+ tail->fts_link = p;
-+ tail = p;
-+ }
-+
-+ /* If there are many entries, no sorting function has been
-+ specified, and this file system is of a type that may be
-+ slow with a large number of entries, arrange to sort the
-+ directory entries on increasing inode numbers.
-+
-+ The NITEMS comparison uses ==, not >, because the test
-+ needs to be tried at most once once, and NITEMS will exceed
-+ the threshold after it is incremented below. */
-+ if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
-+ && !sp->fts_compar)
-+ sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);
-+
-+ ++nitems;
-+ if (max_entries <= nitems) {
-+ /* When there are too many dir entries, leave
-+ fts_dirp open, so that a subsequent fts_read
-+ can take up where we leave off. */
-+ break;
-+ }
-+ }
-+
-+ /*
-+ * If realloc() changed the address of the file name, adjust the
-+ * addresses for the rest of the tree and the dir list.
-+ */
-+ if (doadjust)
-+ fts_padjust(sp, head);
-+
-+ /*
-+ * If not changing directories, reset the file name back to original
-+ * state.
-+ */
-+ if (ISSET(FTS_NOCHDIR)) {
-+ if (len == sp->fts_pathlen || nitems == 0)
-+ --cp;
-+ *cp = '\0';
-+ }
-+
-+ /*
-+ * If descended after called from fts_children or after called from
-+ * fts_read and nothing found, get back. At the root level we use
-+ * the saved fd; if one of fts_open()'s arguments is a relative name
-+ * to an empty directory, we wind up here with no other way back. If
-+ * can't get back, we're done.
-+ */
-+ if (!continue_readdir && descend && (type == BCHILD || !nitems) &&
-+ (cur->fts_level == FTS_ROOTLEVEL
-+ ? restore_initial_cwd(sp)
-+ : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ fts_lfree(head);
-+ return (NULL);
-+ }
-+
-+ /* If didn't find anything, return NULL. */
-+ if (!nitems) {
-+ if (type == BREAD
-+ && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
-+ cur->fts_info = FTS_DP;
-+ fts_lfree(head);
-+ return (NULL);
-+ }
-+
-+ if (sort_by_inode) {
-+ sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);
-+ head = fts_sort (sp, head, nitems);
-+ sp->fts_compar = NULL;
-+ }
-+
-+ /* Sort the entries. */
-+ if (sp->fts_compar && nitems > 1)
-+ head = fts_sort(sp, head, nitems);
-+ return (head);
-+}
-+
-+#if GNULIB_FTS_DEBUG
-+
-+struct devino {
-+ intmax_t dev, ino;
-+};
-+#define PRINT_DEVINO "(%jd,%jd)"
-+
-+static struct devino
-+getdevino (int fd)
-+{
-+ struct STRUCT_STAT st;
-+ return (fd == AT_FDCWD
-+ ? (struct devino) { -1, 0 }
-+ : FSTAT (fd, &st) == 0
-+ ? (struct devino) { st.st_dev, st.st_ino }
-+ : (struct devino) { -1, errno });
-+}
-+
-+/* Walk ->fts_parent links starting at E_CURR, until the root of the
-+ current hierarchy. There should be a directory with dev/inode
-+ matching those of AD. If not, print a lot of diagnostics. */
-+static void
-+find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)
-+{
-+ for (FTSENTRY const *ent = e_curr;
-+ ent->fts_level >= FTS_ROOTLEVEL;
-+ ent = ent->fts_parent)
-+ {
-+ if (ad->ino == ent->fts_statp->st_ino
-+ && ad->dev == ent->fts_statp->st_dev)
-+ return;
-+ }
-+ printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
-+ printf ("active dirs:\n");
-+ for (FTSENTRY const *ent = e_curr;
-+ ent->fts_level >= FTS_ROOTLEVEL;
-+ ent = ent->fts_parent)
-+ printf (" %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
-+ ad->fts_ent->fts_accpath,
-+ (uintmax_t) ad->dev,
-+ (uintmax_t) ad->ino,
-+ ent->fts_accpath,
-+ (uintmax_t) ent->fts_statp->st_dev,
-+ (uintmax_t) ent->fts_statp->st_ino);
-+}
-+
-+void
-+fts_cross_check (FTSOBJ const *sp)
-+{
-+ if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))
-+ return;
-+
-+ FTSENTRY const *ent = sp->fts_cur;
-+
-+ Dprintf (("fts-cross-check cur=%s\n", ent->fts_path));
-+ /* Make sure every parent dir is in the tree. */
-+ for (FTSENTRY const *t = ent->fts_parent;
-+ t->fts_level >= FTS_ROOTLEVEL;
-+ t = t->fts_parent)
-+ {
-+ struct Active_dir ad;
-+ ad.ino = t->fts_statp->st_ino;
-+ ad.dev = t->fts_statp->st_dev;
-+ if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
-+ printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
-+ }
-+
-+ /* Make sure every dir in the tree is an active dir.
-+ But ENT is not necessarily a directory. If so, just skip this part. */
-+ if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
-+ && (ent->fts_info == FTS_DP
-+ || ent->fts_info == FTS_D))
-+ for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);
-+ ad != NULL;
-+ ad = hash_get_next (sp->fts_cycle.ht, ad))
-+ {
-+ find_matching_ancestor (ent, ad);
-+ }
-+}
-+
-+static bool
-+same_fd (int fd1, int fd2)
-+{
-+ struct STRUCT_STAT sb1, sb2;
-+ return (FSTAT (fd1, &sb1) == 0
-+ && FSTAT (fd2, &sb2) == 0
-+ && psame_inode (&sb1, &sb2));
-+}
-+
-+static void
-+fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)
-+{
-+ if (!fts_debug)
-+ return;
-+ I_ring const *fd_ring = &sp->fts_fd_ring;
-+ struct devino cwd = getdevino (sp->fts_cwd_fd);
-+ fprintf (stream, "=== %s ========== "PRINT_DEVINO"\n", msg, cwd.dev, cwd.ino);
-+ if (i_ring_empty (fd_ring))
-+ return;
-+
-+ unsigned int i = fd_ring->ir_front;
-+ while (true)
-+ {
-+ int fd = fd_ring->ir_data[i];
-+ if (fd < 0)
-+ fprintf (stream, "%u: %d:\n", i, fd);
-+ else
-+ {
-+ struct devino wd = getdevino (fd);
-+ fprintf (stream, "%u: %d: "PRINT_DEVINO"\n", i, fd, wd.dev, wd.ino);
-+ }
-+ if (i == fd_ring->ir_back)
-+ break;
-+ i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
-+ }
-+}
-+
-+/* Ensure that each file descriptor on the fd_ring matches a
-+ parent, grandparent, etc. of the current working directory. */
-+static void
-+fd_ring_check (FTSOBJ const *sp)
-+{
-+ if (!fts_debug)
-+ return;
-+
-+ /* Make a writable copy. */
-+ I_ring fd_w = sp->fts_fd_ring;
-+
-+ int cwd_fd = sp->fts_cwd_fd;
-+ cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-+ struct devino dot = getdevino (cwd_fd);
-+ fprintf (stderr, "===== check ===== cwd: "PRINT_DEVINO"\n",
-+ dot.dev, dot.ino);
-+ while ( ! i_ring_empty (&fd_w))
-+ {
-+ int fd = i_ring_pop (&fd_w);
-+ if (0 <= fd)
-+ {
-+ int open_flags = O_SEARCH | O_CLOEXEC;
-+ int parent_fd = openat (cwd_fd, "..", open_flags);
-+ if (parent_fd < 0)
-+ {
-+ // Warn?
-+ break;
-+ }
-+ if (!same_fd (fd, parent_fd))
-+ {
-+ struct devino cwd = getdevino (fd);
-+ fprintf (stderr, "ring : "PRINT_DEVINO"\n", cwd.dev, cwd.ino);
-+ struct devino c2 = getdevino (parent_fd);
-+ fprintf (stderr, "parent: "PRINT_DEVINO"\n", c2.dev, c2.ino);
-+ fts_assert (0);
-+ }
-+ close (cwd_fd);
-+ cwd_fd = parent_fd;
-+ }
-+ }
-+ close (cwd_fd);
-+}
-+#endif
-+
-+static unsigned short int
-+internal_function
-+fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)
-+{
-+ if (ISSET (FTS_LOGICAL)
-+ || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))
-+ follow = true;
-+
-+ struct STRUCT_STAT *sbp = p->fts_statp;
-+
-+ /*
-+ * If doing a logical walk, or application requested FTS_FOLLOW, do
-+ * a stat(2). If that fails, check for a nonexistent symlink. If
-+ * fail, set the errno from the stat call.
-+ */
-+ int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
-+ if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)
-+ {
-+ if (follow && errno == ENOENT
-+ && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,
-+ AT_SYMLINK_NOFOLLOW))
-+ {
-+ __set_errno (0);
-+ return FTS_SLNONE;
-+ }
-+
-+ p->fts_errno = errno;
-+ memset (sbp, 0, sizeof *sbp);
-+ return FTS_NS;
-+ }
-+
-+ if (S_ISDIR(sbp->st_mode)) {
-+ if (ISDOT(p->fts_name)) {
-+ /* Command-line "." and ".." are real directories. */
-+ return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
-+ }
-+
-+ return (FTS_D);
-+ }
-+ if (S_ISLNK(sbp->st_mode))
-+ return (FTS_SL);
-+ if (S_ISREG(sbp->st_mode))
-+ return (FTS_F);
-+ return (FTS_DEFAULT);
-+}
-+
-+static int
-+fts_compar (void const *a, void const *b)
-+{
-+ /* Convert A and B to the correct types, to pacify the compiler, and
-+ for portability to bizarre hosts where "void const *" and "FTSENT
-+ const **" differ in runtime representation. The comparison
-+ function cannot modify *a and *b, but there is no compile-time
-+ check for this. */
-+ FTSENTRY const **pa = (FTSENTRY const **) a;
-+ FTSENTRY const **pb = (FTSENTRY const **) b;
-+ return FTSENT_FTS(pa[0])->fts_compar (pa, pb);
-+}
-+
-+static FTSENTRY *
-+internal_function
-+fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)
-+{
-+ register FTSENTRY **ap, *p;
-+
-+ /* On most modern hosts, void * and FTSENT ** have the same
-+ run-time representation, and one can convert sp->fts_compar to
-+ the type qsort expects without problem. Use the heuristic that
-+ this is OK if the two pointer types are the same size, and if
-+ converting FTSENT ** to uintptr_t is the same as converting
-+ FTSENT ** to void * and then to uintptr_t. This heuristic isn't
-+ valid in general but we don't know of any counterexamples. */
-+ FTSENTRY *dummy;
-+ int (*compare) (void const *, void const *) =
-+ ((sizeof &dummy == sizeof (void *)
-+ && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)
-+ ? (int (*) (void const *, void const *)) sp->fts_compar
-+ : fts_compar);
-+
-+ /*
-+ * Construct an array of pointers to the structures and call qsort(3).
-+ * Reassemble the array in the order returned by qsort. If unable to
-+ * sort for memory reasons, return the directory entries in their
-+ * current order. Allocate enough space for the current needs plus
-+ * 40 so don't realloc one entry at a time.
-+ */
-+ if (nitems > sp->fts_nitems) {
-+ sp->fts_nitems = nitems + 40;
-+ FTSENTRY **a;
-+ if (! (a = reallocarray (sp->fts_array,
-+ sp->fts_nitems, sizeof *a))) {
-+ free(sp->fts_array);
-+ sp->fts_array = NULL;
-+ sp->fts_nitems = 0;
-+ return (head);
-+ }
-+ sp->fts_array = a;
-+ }
-+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
-+ *ap++ = p;
-+ qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);
-+ for (head = *(ap = sp->fts_array); --nitems; ++ap)
-+ ap[0]->fts_link = ap[1];
-+ ap[0]->fts_link = NULL;
-+ return (head);
-+}
-+
-+static FTSENTRY *
-+internal_function
-+fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)
-+{
-+ /*
-+ * The file name is a variable length array. Allocate the FTSENT
-+ * structure and the file name in one chunk.
-+ */
-+ size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);
-+ register FTSENTRY *p;
-+#if !_LIBC
-+ p = malloc(len);
-+ if (p == NULL)
-+ return (NULL);
-+#else
-+ /*
-+ * For glibc, we use a wrapper struct to provide the extra required
-+ * fields without changing the FSENT layout.
-+ */
-+ len += sizeof (struct FTSENT_wrapper);
-+ struct FTSENT_wrapper *wrapper = malloc(len);
-+ if (wrapper == NULL)
-+ return (NULL);
-+ p = &wrapper->ent;
-+ p->fts_statp = &wrapper->fts_stat;
-+#endif
-+
-+ /* Copy the name and guarantee NUL termination. */
-+ memcpy(p->fts_name, name, namelen);
-+ p->fts_name[namelen] = '\0';
-+
-+ p->fts_namelen = namelen;
-+ FTSENT_FTS(p)= sp;
-+ p->fts_path = sp->fts_path;
-+ p->fts_errno = 0;
-+ FTSENT_DIRP(p) = NULL;
-+ p->fts_flags = 0;
-+ p->fts_instr = FTS_NOINSTR;
-+ p->fts_number = 0;
-+ p->fts_pointer = NULL;
-+ return (p);
-+}
-+
-+static void
-+internal_function
-+fts_lfree (register FTSENTRY *head)
-+{
-+ int saved_errno = errno;
-+
-+ /* Free a linked list of structures. */
-+ register FTSENTRY *p;
-+ while ((p = head)) {
-+ head = head->fts_link;
-+ if (FTSENT_DIRP(p))
-+ closedir (FTSENT_DIRP(p));
-+ free(FTSENT_WRAPPER(p));
-+ }
-+
-+ __set_errno (saved_errno);
-+}
-+
-+/*
-+ * Allow essentially unlimited file name lengths; find, rm, ls should
-+ * all work on any tree. Most systems will allow creation of file
-+ * names much longer than MAXPATHLEN, even though the kernel won't
-+ * resolve them. Add the size (not just what's needed) plus 256 bytes
-+ * so don't realloc the file name 2 bytes at a time.
-+ */
-+static bool
-+internal_function
-+fts_palloc (FTSOBJ *sp, size_t more)
-+{
-+ size_t new_len = sp->fts_pathlen + more + 256;
-+
-+ /*
-+ * See if fts_pathlen would overflow.
-+ */
-+ if (new_len < sp->fts_pathlen) {
-+ free(sp->fts_path);
-+ sp->fts_path = NULL;
-+ __set_errno (ENAMETOOLONG);
-+ return false;
-+ }
-+ sp->fts_pathlen = new_len;
-+ char *p = realloc(sp->fts_path, sp->fts_pathlen);
-+ if (p == NULL) {
-+ free(sp->fts_path);
-+ sp->fts_path = NULL;
-+ return false;
-+ }
-+ sp->fts_path = p;
-+ return true;
-+}
-+
-+/*
-+ * When the file name is realloc'd, have to fix all of the pointers in
-+ * structures already returned.
-+ */
-+static void
-+internal_function
-+fts_padjust (FTSOBJ *sp, FTSENTRY *head)
-+{
-+ char *addr = sp->fts_path;
-+
-+ /* This code looks at bit-patterns of freed pointers to
-+ relocate them, so it relies on undefined behavior. If this
-+ trick does not work on your platform, please report a bug. */
-+
-+#define ADJUST(p) do { \
-+ uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath; \
-+ if (old_accpath != (uintptr_t) (p)->fts_name) { \
-+ (p)->fts_accpath = \
-+ addr + (old_accpath - (uintptr_t) (p)->fts_path); \
-+ } \
-+ (p)->fts_path = addr; \
-+} while (0)
-+ /* Adjust the current set of children. */
-+ for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)
-+ ADJUST(p);
-+
-+ /* Adjust the rest of the tree, including the current level. */
-+ for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {
-+ ADJUST(p);
-+ p = p->fts_link ? p->fts_link : p->fts_parent;
-+ }
-+}
-+
-+static size_t
-+internal_function _GL_ATTRIBUTE_PURE
-+fts_maxarglen (char * const *argv)
-+{
-+ size_t max;
-+
-+ for (max = 0; *argv; ++argv) {
-+ size_t len = strlen(*argv);
-+ if (len > max)
-+ max = len;
-+ }
-+ return (max + 1);
-+}
-+
-+/*
-+ * Change to dir specified by fd or file name without getting
-+ * tricked by someone changing the world out from underneath us.
-+ * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.
-+ * If FD is non-negative, expect it to be used after this function returns,
-+ * and to be closed eventually. So don't pass e.g., 'dirfd(dirp)' and then
-+ * do closedir(dirp), because that would invalidate the saved FD.
-+ * Upon failure, close FD immediately and return nonzero.
-+ */
-+static int
-+internal_function
-+fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)
-+{
-+ fts_assert (0 <= fd || dir != NULL);
-+ bool is_dotdot = dir && streq (dir, "..");
-+
-+ /* This clause handles the unusual case in which FTS_NOCHDIR
-+ is specified, along with FTS_CWDFD. In that case, there is
-+ no need to change even the virtual cwd file descriptor.
-+ However, if FD is non-negative, we do close it here. */
-+ if (ISSET (FTS_NOCHDIR))
-+ {
-+ if (ISSET (FTS_CWDFD) && 0 <= fd)
-+ close (fd);
-+ return 0;
-+ }
-+
-+ if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
-+ {
-+ /* When possible, skip the diropen and subsequent fstat+dev/ino
-+ comparison. I.e., when changing to parent directory
-+ (chdir ("..")), use a file descriptor from the ring and
-+ save the overhead of diropen+fstat, as well as avoiding
-+ failure when we lack "x" access to the virtual cwd. */
-+ if ( ! i_ring_empty (&sp->fts_fd_ring))
-+ {
-+ int parent_fd;
-+ fd_ring_print (sp, stderr, "pre-pop");
-+ parent_fd = i_ring_pop (&sp->fts_fd_ring);
-+ if (0 <= parent_fd)
-+ {
-+ fd = parent_fd;
-+ dir = NULL;
-+ }
-+ }
-+ }
-+
-+ int newfd = fd;
-+ if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
-+ return -1;
-+
-+ /* The following dev/inode check is necessary if we're doing a
-+ "logical" traversal (through symlinks, a la chown -L), if the
-+ system lacks O_NOFOLLOW support, or if we're changing to ".."
-+ (but not via a popped file descriptor). When changing to the
-+ name "..", O_NOFOLLOW can't help. In general, when the target is
-+ not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
-+ follow a symlink, so we can avoid the expense of this fstat. */
-+ int ret;
-+ if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
-+ || (dir && streq (dir, "..")))
-+ {
-+ struct STRUCT_STAT sb;
-+ if (FSTAT (newfd, &sb))
-+ {
-+ ret = -1;
-+ goto bail;
-+ }
-+ if (p->fts_statp->st_dev != sb.st_dev
-+ || p->fts_statp->st_ino != sb.st_ino)
-+ {
-+ __set_errno (ENOENT); /* disinformation */
-+ ret = -1;
-+ goto bail;
-+ }
-+ }
-+
-+ if (ISSET(FTS_CWDFD))
-+ {
-+ cwd_advance_fd (sp, newfd, ! is_dotdot);
-+ return 0;
-+ }
-+
-+ ret = fchdir(newfd);
-+bail:
-+ if (fd < 0)
-+ {
-+ int oerrno = errno;
-+ (void)close(newfd);
-+ __set_errno (oerrno);
-+ }
-+ return ret;
-+}
-diff --git a/io/fts64-time64.c b/io/fts64-time64.c
-index 29f2dbc28cc7a11c..6a1053194eec51ba 100644
---- a/io/fts64-time64.c
-+++ b/io/fts64-time64.c
-@@ -33,5 +33,5 @@
- # define STRUCT_STATFS statfs64
- # define FSTATFS __fstatfs64
-
--# include "fts-common.c"
-+# include "fts.c"
- #endif
-diff --git a/io/fts64.c b/io/fts64.c
-index 1efa06ab3bef0643..a6f607a87383f4a7 100644
---- a/io/fts64.c
-+++ b/io/fts64.c
-@@ -16,11 +16,11 @@
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
-
--#define FTS_OPEN __fts64_open
--#define FTS_CLOSE __fts64_close
--#define FTS_READ __fts64_read
--#define FTS_SET __fts64_set
--#define FTS_CHILDREN __fts64_children
-+#define FTS_OPEN fts64_open
-+#define FTS_CLOSE fts64_close
-+#define FTS_READ fts64_read
-+#define FTS_SET fts64_set
-+#define FTS_CHILDREN fts64_children
- #define FTSOBJ FTS64
- #define FTSENTRY FTSENT64
- #define INO_T ino64_t
-@@ -30,30 +30,4 @@
- #define STRUCT_STATFS statfs64
- #define FSTATFS __fstatfs64
-
--#define fts_open __rename_fts_open
--#define fts_close __rename_fts_close
--#define fts_read __rename_fts_read
--#define fts_set __rename_fts_set
--#define fts_children __rename_fts_children
--
--#include "fts-common.c"
--
--#undef fts_open
--#undef fts_close
--#undef fts_read
--#undef fts_set
--#undef fts_children
--
--weak_alias (__fts64_open, fts64_open)
--weak_alias (__fts64_close, fts64_close)
--weak_alias (__fts64_read, fts64_read)
--weak_alias (__fts64_set, fts64_set)
--weak_alias (__fts64_children, fts64_children)
--
--#ifdef __OFF_T_MATCHES_OFF64_T
--weak_alias (__fts64_open, fts_open)
--weak_alias (__fts64_close, fts_close)
--weak_alias (__fts64_read, fts_read)
--weak_alias (__fts64_set, fts_set)
--weak_alias (__fts64_children, fts_children)
--#endif
-+#include "fts.c"
-diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c
-new file mode 100644
-index 0000000000000000..d0c62e6195eadc63
---- /dev/null
-+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c
-@@ -0,0 +1 @@
-+#include <io/fts.c>
-diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c
-new file mode 100644
-index 0000000000000000..2472f8bf75968b36
---- /dev/null
-+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c
-@@ -0,0 +1 @@
-+#include <io/fts64.c>
-diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts.c
-new file mode 100644
-index 0000000000000000..980573ed68928441
---- /dev/null
-+++ b/sysdeps/unix/sysv/linux/x86_64/x32/fts.c
-@@ -0,0 +1 @@
-+#include <sysdeps/wordsize-64/fts.c>
-diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c
-new file mode 100644
-index 0000000000000000..221d1b5608e8692f
---- /dev/null
-+++ b/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c
-@@ -0,0 +1 @@
-+#include <sysdeps/wordsize-64/fts64.c>
-diff --git a/sysdeps/wordsize-64/fts.c b/sysdeps/wordsize-64/fts.c
-new file mode 100644
-index 0000000000000000..159dc1febe4980f7
---- /dev/null
-+++ b/sysdeps/wordsize-64/fts.c
-@@ -0,0 +1,19 @@
-+#define fts64_open __rename_fts64_open
-+#define fts64_close __rename_fts64_close
-+#define fts64_read __rename_fts64_read
-+#define fts64_set __rename_fts64_set
-+#define fts64_children __rename_fts64_children
-+
-+#include "../../io/fts.c"
-+
-+#undef fts64_open
-+#undef fts64_close
-+#undef fts64_read
-+#undef fts64_set
-+#undef fts64_children
-+
-+weak_alias (fts_open, fts64_open)
-+weak_alias (fts_close, fts64_close)
-+weak_alias (fts_read, fts64_read)
-+weak_alias (fts_set, fts64_set)
-+weak_alias (fts_children, fts64_children)
-diff --git a/sysdeps/wordsize-64/fts64.c b/sysdeps/wordsize-64/fts64.c
-new file mode 100644
-index 0000000000000000..f2848fc3e4c08625
---- /dev/null
-+++ b/sysdeps/wordsize-64/fts64.c
-@@ -0,0 +1 @@
-+/* Defined in fts.c. */
---
-2.53.0
-
diff --git a/glibc-rh2457183-2.patch b/glibc-rh2457183-2.patch
deleted file mode 100644
index 53374bd..0000000
--- a/glibc-rh2457183-2.patch
+++ /dev/null
@@ -1,6222 +0,0 @@
-Revert "io: Use gnulib fts implementation (BZ 22944, BZ 20331)"
-
-This reverts commit 99303f387179332d64e5ee34a6b58e82a523bae5.
-
-Reason for revert: This change breaks dracut due to the way it uses
-fts: <https://bugzilla.redhat.com/show_bug.cgi?id=2457183>
-
-diff --git a/SHARED-FILES b/SHARED-FILES
-index 126aaf096d93371c..b9d9cdc9d04cab38 100644
---- a/SHARED-FILES
-+++ b/SHARED-FILES
-@@ -36,22 +36,8 @@ gnulib:
- # Merged from gnulib 2025-10-29 (gnulib commit 1790ef25d8)
- include/intprops.h
- include/intprops-internal.h
-- # Merged from gnulib 2026-02-16
-- include/assure.h
-- include/flexmember.h
-- include/hash.h
-- include/next-prime.h
-- include/xalloc-oversized.h
- # Merged from gnulib 2021-09-21
- include/regex.h
-- # Merged from gnulib 2026-02-16
-- io/cycle-check.c
-- io/cycle-check.h
-- io/dev-ino.h
-- io/fts-cycle.c
-- io/fts.c
-- io/i-ring.c
-- io/same-inode.h
- locale/programs/3level.h
- # Merged from gnulib 2014-6-23
- malloc/obstack.c
-@@ -61,9 +47,7 @@ gnulib:
- misc/error.c
- misc/error.h
- misc/getpass.c
-- misc/hash.c
- misc/mkdtemp.c
-- misc/next-prime.c
- # Merged from gnulib 2021-09-21
- misc/sys/cdefs.h
- posix/fnmatch_loop.c
-diff --git a/dirent/scandirat.c b/dirent/scandirat.c
-index d40c0828f19a9b2b..31ec9662b0b42173 100644
---- a/dirent/scandirat.c
-+++ b/dirent/scandirat.c
-@@ -23,8 +23,7 @@ __scandirat (int dfd, const char *dir, struct dirent ***namelist,
- int (*select) (const struct dirent *),
- int (*cmp) (const struct dirent **, const struct dirent **))
- {
-- return __scandir_tail (__opendirat (dfd, dir, 0, NULL), namelist, select,
-- cmp);
-+ return __scandir_tail (__opendirat (dfd, dir), namelist, select, cmp);
- }
- weak_alias (__scandirat, scandirat)
- #endif
-diff --git a/dirent/scandirat64.c b/dirent/scandirat64.c
-index cb1821174e693dce..fb010fac28f236c1 100644
---- a/dirent/scandirat64.c
-+++ b/dirent/scandirat64.c
-@@ -24,8 +24,7 @@ scandirat64 (int dfd, const char *dir, struct dirent64 ***namelist,
- int (*select) (const struct dirent64 *),
- int (*cmp) (const struct dirent64 **, const struct dirent64 **))
- {
-- return __scandir64_tail (__opendirat (dfd, dir, 0, NULL), namelist, select,
-- cmp);
-+ return __scandir64_tail (__opendirat (dfd, dir), namelist, select, cmp);
- }
-
- #if _DIRENT_MATCHES_DIRENT64
-diff --git a/include/assure.h b/include/assure.h
-deleted file mode 100644
-index edc72591d1283b4a..0000000000000000
---- a/include/assure.h
-+++ /dev/null
-@@ -1,57 +0,0 @@
--/* Run-time assert-like macros.
--
-- Copyright (C) 2014-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* Written by Paul Eggert. */
--
--#ifndef _GL_ASSURE_H
--#define _GL_ASSURE_H
--
--#include <assert.h>
--#include "verify.h"
--
--/* Evaluate an assertion E that is guaranteed to be true.
-- If NDEBUG is not defined, abort the program if E is false.
-- If NDEBUG is defined, the compiler can assume E and behavior is
-- undefined if E is false, fails to evaluate, or has side effects.
--
-- Unlike standard 'assert', this macro evaluates E even when NDEBUG
-- is defined, so as to catch typos, avoid some GCC warnings, and
-- improve performance when E is simple enough.
--
-- Also see the documentation for 'assume' in verify.h. */
--
--#ifdef NDEBUG
--# define affirm(E) assume (E)
--#else
--# define affirm(E) assert (E)
--#endif
--
--/* Check E's value at runtime, and report an error and abort if not.
-- However, do nothing if NDEBUG is defined.
--
-- Unlike standard 'assert', this macro compiles E even when NDEBUG
-- is defined, so as to catch typos and avoid some GCC warnings.
-- Unlike 'affirm', it is OK for E to use hard-to-optimize features,
-- since E is not executed if NDEBUG is defined. */
--
--#ifdef NDEBUG
--# define assure(E) ((void) (0 && (E)))
--#else
--# define assure(E) assert (E)
--#endif
--
--#endif
-diff --git a/include/dirent.h b/include/dirent.h
-index 9a4bd5a1e3796838..d7567f5e866e6ad3 100644
---- a/include/dirent.h
-+++ b/include/dirent.h
-@@ -16,8 +16,7 @@ struct scandir_cancel_struct
-
- /* Now define the internal interfaces. */
- extern DIR *__opendir (const char *__name) attribute_hidden;
--extern DIR *__opendirat (int dfd, const char *__nam, int extra_flags,
-- int *pnew_fd) attribute_hidden;
-+extern DIR *__opendirat (int dfd, const char *__name) attribute_hidden;
- extern DIR *__fdopendir (int __fd) attribute_hidden;
- extern int __closedir (DIR *__dirp) attribute_hidden;
- extern struct dirent *__readdir (DIR *__dirp) attribute_hidden;
-diff --git a/include/flexmember.h b/include/flexmember.h
-deleted file mode 100644
-index c6bb2842ec7febb1..0000000000000000
---- a/include/flexmember.h
-+++ /dev/null
-@@ -1,77 +0,0 @@
--/* Sizes of structs with flexible array members.
--
-- Copyright 2016-2026 Free Software Foundation, Inc.
--
-- This file is part of the GNU C Library.
--
-- The GNU C Library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- The GNU C Library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with the GNU C Library; if not, see
-- <https://www.gnu.org/licenses/>.
--
-- Written by Paul Eggert. */
--
--/* This file uses _Alignof. */
--#if !_LIBC && !_GL_CONFIG_H_INCLUDED
-- #error "Please include config.h first."
--#endif
--
--#include <stddef.h>
--
--/* Nonzero multiple of alignment of TYPE, suitable for FLEXSIZEOF below.
-- If _Alignof might not exist or might not work correctly on
-- structs with flexible array members, use a pessimistic bound that is
-- safe in practice even if FLEXIBLE_ARRAY_MEMBER is 1.
-- Otherwise, use _Alignof to get a tighter bound. */
--
--#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 || defined _Alignof
--# define FLEXALIGNOF(type) (sizeof (type) & ~ (sizeof (type) - 1))
--#else
--# define FLEXALIGNOF(type) _Alignof (type)
--#endif
--
--/* Yield a properly aligned upper bound on the size of a struct of
-- type TYPE with a flexible array member named MEMBER that is
-- followed by N bytes of other data. The result is suitable as an
-- argument to malloc. For example:
--
-- struct s { int a; char d[FLEXIBLE_ARRAY_MEMBER]; };
-- struct s *p = malloc (FLEXSIZEOF (struct s, d, n * sizeof (char)));
--
-- FLEXSIZEOF (TYPE, MEMBER, N) is not simply (sizeof (TYPE) + N),
-- since FLEXIBLE_ARRAY_MEMBER may be 1 on pre-C11 platforms. Nor is
-- it simply (offsetof (TYPE, MEMBER) + N), as that might yield a size
-- that causes malloc to yield a pointer that is not properly aligned
-- for TYPE; for example, if sizeof (int) == alignof (int) == 4,
-- malloc (offsetof (struct s, d) + 3 * sizeof (char)) is equivalent
-- to malloc (7) and might yield a pointer that is not a multiple of 4
-- (which means the pointer is not properly aligned for struct s),
-- whereas malloc (FLEXSIZEOF (struct s, d, 3 * sizeof (char))) is
-- equivalent to malloc (8) and must yield a pointer that is a
-- multiple of 4.
--
-- Yield a value less than N if and only if arithmetic overflow occurs. */
--
--#define FLEXSIZEOF(type, member, n) \
-- ((offsetof (type, member) + FLEXALIGNOF (type) - 1 + (n)) \
-- & ~ (FLEXALIGNOF (type) - 1))
--
--/* Yield a properly aligned upper bound on the size of a struct of
-- type TYPE with a flexible array member named MEMBER that has N
-- elements. The result is suitable as an argument to malloc.
-- For example:
--
-- struct s { int a; double d[FLEXIBLE_ARRAY_MEMBER]; };
-- struct s *p = malloc (FLEXNSIZEOF (struct s, d, n));
-- */
--#define FLEXNSIZEOF(type, member, n) \
-- FLEXSIZEOF (type, member, (n) * sizeof (((type *) 0)->member[0]))
-diff --git a/include/fts.h b/include/fts.h
-index 972884647bd5c2c1..ea36a9b9be29f88a 100644
---- a/include/fts.h
-+++ b/include/fts.h
-@@ -17,13 +17,6 @@ typedef struct
- int fts_nitems;
- int (*fts_compar) (const void *, const void *);
- int fts_options;
-- int fts_cwd_fd;
-- struct hash_table *fts_leaf_optimization_works_ht;
-- union {
-- struct hash_table *ht;
-- struct cycle_check_state *state;
-- } fts_cycle;
-- __I_ring fts_fd_ring;
- } FTS64_TIME64;
-
- typedef struct _ftsent64_time64
-diff --git a/include/hash.h b/include/hash.h
-deleted file mode 100644
-index d3c80aeedf32a75f..0000000000000000
---- a/include/hash.h
-+++ /dev/null
-@@ -1,331 +0,0 @@
--/* hash - hashing table processing.
-- Copyright (C) 1998-1999, 2001, 2003, 2009-2026 Free Software Foundation,
-- Inc.
-- Written by Jim Meyering <meyering@ascend.com>, 1998.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* A generic hash table package. */
--
--/* Make sure USE_OBSTACK is defined to 1 if you want the allocator to use
-- obstacks instead of malloc, and recompile 'hash.c' with same setting. */
--
--#ifndef HASH_H_
--# define HASH_H_
--
--/* This file uses _GL_ATTRIBUTE_DEALLOC, _GL_ATTRIBUTE_DEPRECATED,
-- _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_NODISCARD, _GL_ATTRIBUTE_PURE,
-- _GL_ATTRIBUTE_RETURNS_NONNULL. */
--#if !_LIBC && !_GL_CONFIG_H_INCLUDED
-- #error "Please include config.h first."
--#endif
--
--# include <stdbool.h>
--# include <stdio.h>
--
--# ifdef _LIBC
--# define _GL_ATTRIBUTE_NODISCARD __attribute_warn_unused_result__
--# define _GL_ATTRIBUTE_MALLOC __attribute_malloc__
--# define _GL_ATTRIBUTE_DEALLOC(__a, __b) __attr_dealloc (__a, __b)
--# define _GL_ATTRIBUTE_RETURNS_NONNULL __returns_nonnull
--# define GNULIB_HASHCODE_STRING1 0
--# define hash_get_n_buckets __hash_get_n_buckets
--# define hash_get_n_buckets_used __hash_get_n_buckets_used
--# define hash_get_n_entries __hash_get_n_entries
--# define hash_get_max_bucket_length __hash_get_max_bucket_length
--# define hash_table_ok __hash_table_ok
--# define hash_print_statistics __hash_print_statistics
--# define hash_lookup __hash_lookup
--# define hash_get_first __hash_get_first
--# define hash_get_next __hash_get_next
--# define hash_get_entries __hash_get_entries
--# define hash_do_for_each __hash_do_for_each
--# define hash_reset_tuning __hash_reset_tuning
--# define hash_free __hash_free
--# define hash_initialize __hash_initialize
--# define hash_xinitialize __hash_xinitialize
--# define hash_clear __hash_clear
--# define hash_rehash __hash_rehash
--# define hash_insert __hash_insert
--# define hash_xinsert __hash_xinsert
--# define hash_insert_if_absent __hash_insert_if_absent
--# define hash_remove __hash_remove
--# else
--# define attribute_hidden
--# endif
--
--# ifdef __cplusplus
--extern "C" {
--# endif
--
--struct hash_tuning
-- {
-- /* This structure is mainly used for 'hash_initialize', see the block
-- documentation of 'hash_reset_tuning' for more complete comments. */
--
-- float shrink_threshold; /* ratio of used buckets to trigger a shrink */
-- float shrink_factor; /* ratio of new smaller size to original size */
-- float growth_threshold; /* ratio of used buckets to trigger a growth */
-- float growth_factor; /* ratio of new bigger size to original size */
-- bool is_n_buckets; /* if CANDIDATE really means table size */
-- };
--
--typedef struct hash_tuning Hash_tuning;
--
--struct hash_table;
--
--typedef struct hash_table Hash_table;
--
--/*
-- * Information and lookup.
-- */
--
--/* The following few functions provide information about the overall hash
-- table organization: the number of entries, number of buckets and maximum
-- length of buckets. */
--
--/* Return the number of buckets in the hash table. The table size, the total
-- number of buckets (used plus unused), or the maximum number of slots, are
-- the same quantity. */
--extern size_t hash_get_n_buckets (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--/* Return the number of slots in use (non-empty buckets). */
--extern size_t hash_get_n_buckets_used (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--/* Return the number of active entries. */
--extern size_t hash_get_n_entries (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--/* Return the length of the longest chain (bucket). */
--extern size_t hash_get_max_bucket_length (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--/* Do a mild validation of a hash table, by traversing it and checking two
-- statistics. */
--extern bool hash_table_ok (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--extern void hash_print_statistics (const Hash_table *table, FILE *stream)
-- attribute_hidden;
--
--/* If ENTRY matches an entry already in the hash table, return the
-- entry from the table. Otherwise, return NULL. */
--extern void *hash_lookup (const Hash_table *table, const void *entry)
-- attribute_hidden;
--
--/*
-- * Walking.
-- */
--
--/* The functions in this page traverse the hash table and process the
-- contained entries. For the traversal to work properly, the hash table
-- should not be resized nor modified while any particular entry is being
-- processed. In particular, entries should not be added, and an entry
-- may be removed only if there is no shrink threshold and the entry being
-- removed has already been passed to hash_get_next. */
--
--/* Return the first data in the table, or NULL if the table is empty. */
--extern void *hash_get_first (const Hash_table *table)
-- _GL_ATTRIBUTE_PURE attribute_hidden;
--
--/* Return the user data for the entry following ENTRY, where ENTRY has been
-- returned by a previous call to either 'hash_get_first' or 'hash_get_next'.
-- Return NULL if there are no more entries. */
--extern void *hash_get_next (const Hash_table *table, const void *entry)
-- attribute_hidden;
--
--/* Fill BUFFER with pointers to active user entries in the hash table, then
-- return the number of pointers copied. Do not copy more than BUFFER_SIZE
-- pointers. */
--extern size_t hash_get_entries (const Hash_table *table, void **buffer,
-- size_t buffer_size)
-- attribute_hidden;
--
--typedef bool (*Hash_processor) (void *entry, void *processor_data);
--
--/* Call a PROCESSOR function for each entry of a hash table, and return the
-- number of entries for which the processor function returned success. A
-- pointer to some PROCESSOR_DATA which will be made available to each call to
-- the processor function. The PROCESSOR accepts two arguments: the first is
-- the user entry being walked into, the second is the value of PROCESSOR_DATA
-- as received. The walking continue for as long as the PROCESSOR function
-- returns nonzero. When it returns zero, the walking is interrupted. */
--extern size_t hash_do_for_each (const Hash_table *table,
-- Hash_processor processor, void *processor_data)
-- attribute_hidden;
--
--/* Return a hash code of ENTRY, in the range 0..TABLE_SIZE-1.
-- This hash code function must have the property that if the comparator of
-- ENTRY1 and ENTRY2 returns true, the hasher returns the same value for ENTRY1
-- and for ENTRY2.
-- The hash code function typically computes an unsigned integer and at the end
-- performs a % TABLE_SIZE modulo operation. This modulo operation is performed
-- as part of this hash code function, not by the caller, because in some cases
-- the unsigned integer will be a 'size_t', in other cases an 'uintmax_t' or
-- even larger. */
--typedef size_t (*Hash_hasher) (const void *entry, size_t table_size);
--
--/* Compare two entries, ENTRY1 (being looked up or being inserted) and
-- ENTRY2 (already in the table) for equality. Return true for equal,
-- false otherwise. */
--typedef bool (*Hash_comparator) (const void *entry1, const void *entry2);
--
--/* This function is invoked when an ENTRY is removed from the hash table. */
--typedef void (*Hash_data_freer) (void *entry);
--
--/*
-- * Allocation and clean-up.
-- */
--
--extern void hash_reset_tuning (Hash_tuning *tuning)
-- attribute_hidden;
--
--/* Reclaim all storage associated with a hash table. If a data_freer
-- function has been supplied by the user when the hash table was created,
-- this function applies it to the data of each entry before freeing that
-- entry. This function preserves errno, like 'free'. */
--extern void hash_free (Hash_table *table)
-- attribute_hidden;
--
--/* Allocate and return a new hash table, or NULL upon failure. The initial
-- number of buckets is automatically selected so as to _guarantee_ that you
-- may insert at least CANDIDATE different user entries before any growth of
-- the hash table size occurs. So, if have a reasonably tight a-priori upper
-- bound on the number of entries you intend to insert in the hash table, you
-- may save some table memory and insertion time, by specifying it here. If
-- the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE
-- argument has its meaning changed to the wanted number of buckets.
--
-- TUNING points to a structure of user-supplied values, in case some fine
-- tuning is wanted over the default behavior of the hasher. If TUNING is
-- NULL, the default tuning parameters are used instead. If TUNING is
-- provided but the values requested are out of bounds or might cause
-- rounding errors, return NULL.
--
-- The user-supplied HASHER function, when not NULL, accepts two
-- arguments ENTRY and TABLE_SIZE. It computes, by hashing ENTRY contents, a
-- slot number for that entry which should be in the range 0..TABLE_SIZE-1.
-- This slot number is then returned.
--
-- The user-supplied COMPARATOR function, when not NULL, accepts two
-- arguments pointing to user data, it then returns true for a pair of entries
-- that compare equal, or false otherwise. This function is internally called
-- on entries which are already known to hash to the same bucket index,
-- but which are distinct pointers.
--
-- The user-supplied DATA_FREER function, when not NULL, may be later called
-- with the user data as an argument, just before the entry containing the
-- data gets freed. This happens from within 'hash_free' or 'hash_clear'.
-- You should specify this function only if you want these functions to free
-- all of your 'data' data. This is typically the case when your data is
-- simply an auxiliary struct that you have malloc'd to aggregate several
-- values.
--
-- Set errno on failure; otherwise errno is unspecified. */
--_GL_ATTRIBUTE_NODISCARD
--extern Hash_table *hash_initialize (size_t candidate,
-- const Hash_tuning *tuning,
-- Hash_hasher hasher,
-- Hash_comparator comparator,
-- Hash_data_freer data_freer)
-- _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC (hash_free, 1)
-- attribute_hidden;
--
--/* Like hash_initialize, but invokes xalloc_die instead of returning NULL. */
--/* This function is defined by module 'xhash'. */
--_GL_ATTRIBUTE_NODISCARD
--extern Hash_table *hash_xinitialize (size_t candidate,
-- const Hash_tuning *tuning,
-- Hash_hasher hasher,
-- Hash_comparator comparator,
-- Hash_data_freer data_freer)
-- _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC (hash_free, 1)
-- _GL_ATTRIBUTE_RETURNS_NONNULL
-- attribute_hidden;
--
--/* Make all buckets empty, placing any chained entries on the free list.
-- Apply the user-specified function data_freer (if any) to the data of any
-- affected entries. */
--extern void hash_clear (Hash_table *table)
-- attribute_hidden;;
--
--/*
-- * Insertion and deletion.
-- */
--
--/* For an already existing hash table, change the number of buckets through
-- specifying CANDIDATE. The contents of the hash table are preserved. The
-- new number of buckets is automatically selected so as to _guarantee_ that
-- the table may receive at least CANDIDATE different user entries, including
-- those already in the table, before any other growth of the hash table size
-- occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the
-- exact number of buckets desired. Return true iff the rehash succeeded,
-- false (setting errno) otherwise. */
--_GL_ATTRIBUTE_NODISCARD
--extern bool hash_rehash (Hash_table *table, size_t candidate)
-- attribute_hidden;
--
--/* If ENTRY matches an entry already in the hash table, return the pointer
-- to the entry from the table. Otherwise, insert ENTRY and return ENTRY.
-- Return NULL (setting errno) if the storage required for insertion
-- cannot be allocated. This implementation does not support
-- duplicate entries or insertion of NULL. */
--_GL_ATTRIBUTE_NODISCARD
--extern void *hash_insert (Hash_table *table, const void *entry)
-- attribute_hidden;
--
--/* Same as hash_insert, but invokes xalloc_die instead of returning NULL. */
--/* This function is defined by module 'xhash'. */
--extern void *hash_xinsert (Hash_table *table, const void *entry)
-- attribute_hidden;
--
--/* Insert ENTRY into hash TABLE if there is not already a matching entry.
--
-- Return -1 (setting errno) upon memory allocation failure.
-- Return 1 if insertion succeeded.
-- Return 0 if there is already a matching entry in the table,
-- and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT
-- to that entry.
--
-- This interface is easier to use than hash_insert when you must
-- distinguish between the latter two cases. More importantly,
-- hash_insert is unusable for some types of ENTRY values. When using
-- hash_insert, the only way to distinguish those cases is to compare
-- the return value and ENTRY. That works only when you can have two
-- different ENTRY values that point to data that compares "equal". Thus,
-- when the ENTRY value is a simple scalar, you must use
-- hash_insert_if_absent. ENTRY must not be NULL. */
--extern int hash_insert_if_absent (Hash_table *table, const void *entry,
-- const void **matched_ent)
-- attribute_hidden;
--
--/* If ENTRY is already in the table, remove it and return the just-deleted
-- data (the user may want to deallocate its storage). If ENTRY is not in the
-- table, don't modify the table and return NULL. */
--extern void *hash_remove (Hash_table *table, const void *entry)
-- attribute_hidden;
--
--
--# if GNULIB_HASHCODE_STRING1
--/* Include declarations of module 'hashcode-string1'. */
--# include "hashcode-string1.h"
--# endif
--
--# ifdef __cplusplus
--}
--# endif
--
--#endif
-diff --git a/include/next-prime.h b/include/next-prime.h
-deleted file mode 100644
-index 2ee965982b071133..0000000000000000
---- a/include/next-prime.h
-+++ /dev/null
-@@ -1,47 +0,0 @@
--/* Finding the next prime >= a given small integer.
-- Copyright (C) 1995-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--#ifndef _GL_NEXT_PRIME_H
--#define _GL_NEXT_PRIME_H
--
--/* This file uses _GL_ATTRIBUTE_CONST. */
--#if !_LIBC && !_GL_CONFIG_H_INCLUDED
-- #error "Please include config.h first."
--#endif
--
--#include <stddef.h>
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--#ifdef _LIBC
--# define next_prime __next_prime
--#else
--# define attribute_hidden
--#endif
--
--/* Round a given CANDIDATE number up to the nearest prime, and return that
-- prime. Primes lower than 10 are merely skipped. */
--extern size_t _GL_ATTRIBUTE_CONST next_prime (size_t candidate)
-- attribute_hidden;;
--
--
--#ifdef __cplusplus
--}
--#endif
--
--#endif /* _GL_NEXT_PRIME_H */
-diff --git a/include/xalloc-oversized.h b/include/xalloc-oversized.h
-deleted file mode 100644
-index cecdaec51ef4e30b..0000000000000000
---- a/include/xalloc-oversized.h
-+++ /dev/null
-@@ -1,65 +0,0 @@
--/* xalloc-oversized.h -- memory allocation size checking
--
-- Copyright (C) 1990-2000, 2003-2004, 2006-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--#ifndef XALLOC_OVERSIZED_H_
--#define XALLOC_OVERSIZED_H_
--
--#include <stddef.h>
--#include <stdint.h>
--
--/* True if N * S does not fit into both ptrdiff_t and size_t.
-- N and S should be nonnegative and free of side effects.
-- This expands to a constant expression if N and S are both constants.
-- By gnulib convention, SIZE_MAX represents overflow in size_t
-- calculations, so the conservative size_t-based dividend to use here
-- is SIZE_MAX - 1. */
--#define __xalloc_oversized(n, s) \
-- ((s) != 0 \
-- && (PTRDIFF_MAX < SIZE_MAX ? PTRDIFF_MAX : SIZE_MAX - 1) / (s) < (n))
--
--/* Return 1 if and only if an array of N objects, each of size S,
-- cannot exist reliably because its total size in bytes would exceed
-- MIN (PTRDIFF_MAX, SIZE_MAX - 1).
--
-- N and S should be nonnegative and free of side effects.
--
-- Warning: (xalloc_oversized (N, S) ? NULL : malloc (N * S)) can
-- misbehave if N and S are both narrower than ptrdiff_t and size_t,
-- and can be rewritten as (xalloc_oversized (N, S) ? NULL
-- : malloc (N * (size_t) S)).
--
-- This is a macro, not a function, so that it works even if an
-- argument exceeds MAX (PTRDIFF_MAX, SIZE_MAX). */
--#if 7 <= __GNUC__ && !defined __clang__ && PTRDIFF_MAX < SIZE_MAX
--# define xalloc_oversized(n, s) \
-- __builtin_mul_overflow_p (n, s, (ptrdiff_t) 1)
--#elif 5 <= __GNUC__ && !defined __clang__ && !defined __ICC \
-- && PTRDIFF_MAX < SIZE_MAX
--# define xalloc_oversized(n, s) \
-- (__builtin_constant_p (n) && __builtin_constant_p (s) \
-- ? __xalloc_oversized (n, s) \
-- : __extension__ \
-- ({ ptrdiff_t __xalloc_count; \
-- __builtin_mul_overflow (n, s, &__xalloc_count); }))
--
--/* Other compilers use integer division; this may be slower but is
-- more portable. */
--#else
--# define xalloc_oversized(n, s) __xalloc_oversized (n, s)
--#endif
--
--#endif /* !XALLOC_OVERSIZED_H_ */
-diff --git a/io/Makefile b/io/Makefile
-index 2a5b4dcb654f3352..80e50578b29f4d80 100644
---- a/io/Makefile
-+++ b/io/Makefile
-@@ -197,9 +197,7 @@ tests := \
- tst-fcntl-lock-lfs \
- tst-fstatat \
- tst-fts \
-- tst-fts-bz22944 \
- tst-fts-lfs \
-- tst-fts-newflags \
- tst-ftw-bz26353 \
- tst-ftw-bz28126 \
- tst-ftw-lnk \
-diff --git a/io/cycle-check.c b/io/cycle-check.c
-deleted file mode 100644
-index 3a20d28aec897595..0000000000000000
---- a/io/cycle-check.c
-+++ /dev/null
-@@ -1,85 +0,0 @@
--
--/* help detect directory cycles efficiently
--
-- Copyright (C) 2003-2006, 2009-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* Written by Jim Meyering */
--
--#include <config.h>
--
--#include "cycle-check.h"
--
--#include <sys/types.h>
--#include <sys/stat.h>
--#include <stdio.h>
--#include <stdlib.h>
--
--#include "assure.h"
--
--#define CC_MAGIC 9827862
--
--/* Return true if I is a power of 2, or is zero. */
--
--static bool
--is_zero_or_power_of_two (uintmax_t i)
--{
-- return (i & (i - 1)) == 0;
--}
--
--INTERNAL_DEF void
--cycle_check_init (struct cycle_check_state *state)
--{
-- state->chdir_counter = 0;
-- state->magic = CC_MAGIC;
--}
--
--/* In traversing a directory hierarchy, call this function once for each
-- descending chdir call, with SB corresponding to the chdir operand.
-- If SB corresponds to a directory that has already been seen,
-- return true to indicate that there is a directory cycle.
-- Note that this is done "lazily", which means that some of
-- the directories in the cycle may be processed twice before
-- the cycle is detected. */
--
--INTERNAL_DEF bool
--cycle_check (struct cycle_check_state *state, struct STRUCT_STAT const *sb)
--{
-- assure (state->magic == CC_MAGIC);
--
-- /* If the current directory ever happens to be the same
-- as the one we last recorded for the cycle detection,
-- then it's obviously part of a cycle. */
-- if (state->chdir_counter && SAME_INODE (*sb, state->dev_ino))
-- return true;
--
-- /* If the number of "descending" chdir calls is a power of two,
-- record the dev/ino of the current directory. */
-- if (is_zero_or_power_of_two (++(state->chdir_counter)))
-- {
-- /* On all architectures that we know about, if the counter
-- overflows then there is a directory cycle here somewhere,
-- even if we haven't detected it yet. Typically this happens
-- only after the counter is incremented 2**64 times, so it's a
-- fairly theoretical point. */
-- if (state->chdir_counter == 0)
-- return true;
--
-- state->dev_ino.st_dev = sb->st_dev;
-- state->dev_ino.st_ino = sb->st_ino;
-- }
--
-- return false;
--}
-diff --git a/io/cycle-check.h b/io/cycle-check.h
-deleted file mode 100644
-index 0152992ced518125..0000000000000000
---- a/io/cycle-check.h
-+++ /dev/null
-@@ -1,70 +0,0 @@
--
--/* help detect directory cycles efficiently
--
-- Copyright (C) 2003-2004, 2006, 2009-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* Written by Jim Meyering */
--
--#ifndef CYCLE_CHECK_H
--#define CYCLE_CHECK_H 1
--
--#include <stdint.h>
--#include "dev-ino.h"
--#include "same-inode.h"
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--#if _LIBC
--# define cycle_check_init __cycle_check_init
--# define cycle_check __cycle_check
--# define INTERNAL_DEF static
--#else
--# define INTERNAL_DEF
--#endif
--
--struct cycle_check_state
--{
-- struct dev_ino dev_ino;
-- uintmax_t chdir_counter;
-- int magic;
--};
--
--INTERNAL_DEF void cycle_check_init (struct cycle_check_state *state);
--INTERNAL_DEF bool cycle_check (struct cycle_check_state *state,
-- struct STRUCT_STAT const *sb);
--
--#define CYCLE_CHECK_REFLECT_CHDIR_UP(State, SB_dir, SB_subdir) \
-- do \
-- { \
-- /* You must call cycle_check at least once before using this macro. */ \
-- if ((State)->chdir_counter == 0) \
-- abort (); \
-- if (SAME_INODE ((State)->dev_ino, SB_subdir)) \
-- { \
-- (State)->dev_ino.st_dev = (SB_dir).st_dev; \
-- (State)->dev_ino.st_ino = (SB_dir).st_ino; \
-- } \
-- } \
-- while (0)
--
--
--#ifdef __cplusplus
--}
--#endif
--
--#endif
-diff --git a/io/dev-ino.h b/io/dev-ino.h
-deleted file mode 100644
-index 9b1878c79f2eabaf..0000000000000000
---- a/io/dev-ino.h
-+++ /dev/null
-@@ -1,44 +0,0 @@
--/* A simple (device, inode) struct.
-- Copyright (C) 2003-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* Written by Jim Meyering, 2003. */
--
--#ifndef DEV_INO_H
--#define DEV_INO_H 1
--
--#include <sys/types.h>
--#include <sys/stat.h>
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--#ifndef INO_T
--#define INO_T ino_t
--#endif
--
--struct dev_ino
--{
-- INO_T st_ino;
-- dev_t st_dev;
--};
--
--
--#ifdef __cplusplus
--}
--#endif
--
--#endif
-diff --git a/io/fts-cycle.c b/io/fts-cycle.c
-deleted file mode 100644
-index 6bbc9f94460595cb..0000000000000000
---- a/io/fts-cycle.c
-+++ /dev/null
-@@ -1,162 +0,0 @@
--/* Detect cycles in file tree walks.
--
-- Copyright (C) 2003-2006, 2009-2026 Free Software Foundation, Inc.
--
-- Written by Jim Meyering.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--#include "cycle-check.h"
--#include "hash.h"
--
--#ifndef FTS_OPEN
--# define FTSOBJ FTS
--# define FTSENTRY FTSENT
--#endif
--
--/* Use each of these to map a device/inode pair to an FTSENT. */
--struct Active_dir
--{
-- dev_t dev;
-- INO_T ino;
-- FTSENTRY *fts_ent;
--};
--
--static bool
--AD_compare (void const *x, void const *y)
--{
-- struct Active_dir const *ax = x;
-- struct Active_dir const *ay = y;
-- return ax->ino == ay->ino
-- && ax->dev == ay->dev;
--}
--
--static size_t
--AD_hash (void const *x, size_t table_size)
--{
-- struct Active_dir const *ax = x;
-- return (uintmax_t) ax->ino % table_size;
--}
--
--/* Set up the cycle-detection machinery. */
--
--static bool
--setup_dir (FTSOBJ *fts)
--{
-- if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
-- {
-- enum { HT_INITIAL_SIZE = 31 };
-- fts->fts_cycle.ht = hash_initialize (HT_INITIAL_SIZE, NULL, AD_hash,
-- AD_compare, free);
-- if (! fts->fts_cycle.ht)
-- return false;
-- }
-- else
-- {
-- fts->fts_cycle.state = malloc (sizeof *fts->fts_cycle.state);
-- if (! fts->fts_cycle.state)
-- return false;
-- cycle_check_init (fts->fts_cycle.state);
-- }
--
-- return true;
--}
--
--/* Enter a directory during a file tree walk. */
--
--static bool
--enter_dir (FTSOBJ *fts, FTSENTRY *ent)
--{
-- if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
-- {
-- struct Active_dir *ad = malloc (sizeof *ad);
-- if (!ad)
-- return false;
--
-- struct STRUCT_STAT const *st = ent->fts_statp;
-- ad->dev = st->st_dev;
-- ad->ino = st->st_ino;
-- ad->fts_ent = ent;
--
-- /* See if we've already encountered this directory.
-- This can happen when following symlinks as well as
-- with a corrupted directory hierarchy. */
-- struct Active_dir *ad_from_table = hash_insert (fts->fts_cycle.ht, ad);
--
-- if (ad_from_table != ad)
-- {
-- free (ad);
-- if (!ad_from_table)
-- return false;
--
-- /* There was an entry with matching dev/inode already in the table.
-- Record the fact that we've found a cycle. */
-- ent->fts_cycle = ad_from_table->fts_ent;
-- ent->fts_info = FTS_DC;
-- }
-- }
-- else
-- {
-- if (cycle_check (fts->fts_cycle.state, ent->fts_statp))
-- {
-- /* FIXME: setting fts_cycle like this isn't proper.
-- To do what the documentation requires, we'd have to
-- go around the cycle again and find the right entry.
-- But no callers in coreutils use the fts_cycle member. */
-- ent->fts_cycle = ent;
-- ent->fts_info = FTS_DC;
-- }
-- }
--
-- return true;
--}
--
--/* Leave a directory during a file tree walk. */
--
--static void
--leave_dir (FTSOBJ *fts, FTSENTRY *ent)
--{
-- struct STRUCT_STAT const *st = ent->fts_statp;
-- if (fts->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
-- {
-- struct Active_dir obj;
-- obj.dev = st->st_dev;
-- obj.ino = st->st_ino;
-- void *found = hash_remove (fts->fts_cycle.ht, &obj);
-- if (!found)
-- abort ();
-- free (found);
-- }
-- else
-- {
-- FTSENTRY *parent = ent->fts_parent;
-- if (parent != NULL && 0 <= parent->fts_level)
-- CYCLE_CHECK_REFLECT_CHDIR_UP (fts->fts_cycle.state,
-- *(parent->fts_statp), *st);
-- }
--}
--
--/* Free any memory used for cycle detection. */
--
--static void
--free_dir (FTSOBJ *sp)
--{
-- if (sp->fts_options & (FTS_TIGHT_CYCLE_CHECK | FTS_LOGICAL))
-- {
-- if (sp->fts_cycle.ht)
-- hash_free (sp->fts_cycle.ht);
-- }
-- else
-- free (sp->fts_cycle.state);
--}
-diff --git a/io/fts.c b/io/fts.c
-index 3825f0aeb4eb1594..27a15b110431ce28 100644
---- a/io/fts.c
-+++ b/io/fts.c
-@@ -1,23 +1,24 @@
--/* Traverse a file hierarchy.
-+/* File tree traversal functions.
-+ Copyright (C) 1994-2026 Free Software Foundation, Inc.
-+ This file is part of the GNU C Library.
-
-- Copyright (C) 2004-2026 Free Software Foundation, Inc.
-+ The GNU C Library is free software; you can redistribute it and/or
-+ modify it under the terms of the GNU Lesser General Public
-+ License as published by the Free Software Foundation; either
-+ version 2.1 of the License, or (at your option) any later version.
-
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-+ The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ Lesser General Public License for more details.
-
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
-+ You should have received a copy of the GNU Lesser General Public
-+ License along with the GNU C Library; if not, see
-+ <https://www.gnu.org/licenses/>. */
-
- /*-
- * Copyright (c) 1990, 1993, 1994
-- * The Regents of the University of California. All rights reserved.
-+ * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
-@@ -31,7 +32,7 @@
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
-- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
-+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-@@ -44,1122 +45,457 @@
- * SUCH DAMAGE.
- */
-
--#include <config.h>
--
--#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint
--static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
--#endif
-+#if defined(LIBC_SCCS) && !defined(lint)
-+static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
-+#endif /* LIBC_SCCS and not lint */
-
--#if _LIBC
--# include <fts.h>
--#else
--# include "fts_.h"
--#endif
--#if _LIBC || HAVE_SYS_PARAM_H
--# include <sys/param.h>
--#endif
--#include <sys/stat.h>
-+#include <sys/param.h>
-+#include <include/sys/stat.h>
- #include <fcntl.h>
-+#include <dirent.h>
- #include <errno.h>
--#include <stddef.h>
-+#include <fts.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
-
-+
-+/* Largest alignment size needed, minus one.
-+ Usually long double is the worst case. */
-+#ifndef ALIGNBYTES
-+#define ALIGNBYTES (__alignof__ (long double) - 1)
-+#endif
-+/* Align P to that size. */
-+#ifndef ALIGN
-+#define ALIGN(p) (((uintptr_t) (p) + ALIGNBYTES) & ~ALIGNBYTES)
-+#endif
-+
-+
- /* Support for the LFS API version. */
- #ifndef FTS_OPEN
--# define FTS_OPEN fts_open
--# define FTS_CLOSE fts_close
--# define FTS_READ fts_read
--# define FTS_SET fts_set
--# define FTS_CHILDREN fts_children
-+#define FTS_OPEN fts_open
-+#define FTS_CLOSE fts_close
-+#define FTS_READ fts_read
-+#define FTS_SET fts_set
-+#define FTS_CHILDREN fts_children
- # define FTSOBJ FTS
- # define FTSENTRY FTSENT
- # define INO_T ino_t
- # define STRUCT_STAT stat
-+# define STAT __stat
-+# define LSTAT __lstat
- # define FSTAT __fstat
--# define FSTATAT __fstatat
--# define STRUCT_STATFS statfs
--# define FSTATFS __fstatfs
--#endif
--
--#if ! _LIBC
--# include "attribute.h"
--# include "fcntl--.h"
--# include "openat.h"
--# include "opendirat.h"
--# include "same-inode.h"
--# define OPENDIRAT opendirat
--# define FTSENT_WRAPPER(__p) __p
--# define FTS_COMPAR_CAST(__fn) __fn
--#else
--# include <stdbool.h>
--
--# define internal_function
--# define FALLTHROUGH ; [[fallthrough]]
--# define HAVE_STRUCT_DIRENT_D_TYPE 1
--# define GNULIB_FTS_DEBUG 0
--# ifdef O_PATH
--# define O_SEARCH O_PATH
--# else
--# define O_SEARCH O_RDONLY
--# endif
--# define HAVE_SYS_VFS_H 1
--# define HAVE_FSTATFS 1
--# define HAVE_STRUCT_STATFS_F_TYPE 1
--# define HAVE_OPENAT 1
--# define HAVE_WORKING_O_NOFOLLOW 1
--# define _GL_CMP(a, b) ((a) < (b) ? -1 : (a) > (b))
--# define OPENDIRAT __opendirat
--
--static inline bool openat_needs_fchdir (void)
--{
-- return false;
--}
--
--static inline bool streq (const char *s1, const char *s2)
--{
-- return strcmp (s1, s2) == 0;
--}
--# define reallocarray __libc_reallocarray
--# define fchdir __fchdir
--# define close __close
--# define closedir __closedir
--# define fcntl __fcntl
--# define readdir __readdir
--# ifndef dirfd
--# define dirfd __dirfd
--# endif
--# define open __open
--# define openat __openat
--
--# include "cycle-check.c"
--# include "i-ring.c"
--
--struct FTSENT_wrapper
--{
-- FTSOBJ *fts_fts; /* the file hierarchy itself */
-- DIR *fts_dirp; /* Dir pointer for any directory containing
-- more entries than we read at one time. */
-- struct STRUCT_STAT fts_stat;
--
-- FTSENTRY ent;
--};
--
--/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the
-- fts_open has a function point using 'FTSENT **' as argument. */
--# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)
--
--# define FTSENT_WRAPPER(p) \
-- ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))
--#endif
--#define FTSENT_FTS(p) (FTSENT_WRAPPER(p)->fts_fts)
--#define FTSENT_DIRP(p) (FTSENT_WRAPPER(p)->fts_dirp)
--
--#include "flexmember.h"
--
--#include <dirent.h>
--#ifndef _D_EXACT_NAMLEN
--# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)
--#endif
--
--#if HAVE_STRUCT_DIRENT_D_TYPE
--/* True if the type of the directory entry D is known. */
--# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)
--/* True if the type of the directory entry D must be T. */
--# define DT_MUST_BE(d, t) ((d)->d_type == (t))
--# define D_TYPE(d) ((d)->d_type)
--#else
--# define DT_IS_KNOWN(d) false
--# define DT_MUST_BE(d, t) false
--# define D_TYPE(d) DT_UNKNOWN
--
--# undef DT_UNKNOWN
--# define DT_UNKNOWN 0
--
--/* Any nonzero values will do here, so long as they're distinct.
-- Undef any existing macros out of the way. */
--# undef DT_BLK
--# undef DT_CHR
--# undef DT_DIR
--# undef DT_FIFO
--# undef DT_LNK
--# undef DT_REG
--# undef DT_SOCK
--# define DT_BLK 1
--# define DT_CHR 2
--# define DT_DIR 3
--# define DT_FIFO 4
--# define DT_LNK 5
--# define DT_REG 6
--# define DT_SOCK 7
--#endif
--
--#ifndef S_IFBLK
--# define S_IFBLK 0
--#endif
--#ifndef S_IFLNK
--# define S_IFLNK 0
--#endif
--#ifndef S_IFSOCK
--# define S_IFSOCK 0
--#endif
--
--enum
--{
-- NOT_AN_INODE_NUMBER = 0
--};
--
--#ifdef D_INO_IN_DIRENT
--# define D_INO(dp) (dp)->d_ino
--#else
--/* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
--# define D_INO(dp) NOT_AN_INODE_NUMBER
--#endif
--
--/* If possible (see max_entries, below), read no more than this many directory
-- entries at a time. Without this limit (i.e., when using non-NULL
-- fts_compar), processing a directory with 4,000,000 entries requires ~1GiB
-- of memory, and handling 64M entries would require 16GiB of memory. */
--#ifndef FTS_MAX_READDIR_ENTRIES
--# define FTS_MAX_READDIR_ENTRIES 100000
--#endif
--
--/* If there are more than this many entries in a directory,
-- and the conditions mentioned below are satisfied, then sort
-- the entries on inode number before any further processing. */
--#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
--# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000
--#endif
--
--enum
--{
-- _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
--};
--
--enum Fts_stat
--{
-- FTS_NO_STAT_REQUIRED = 1,
-- FTS_STAT_REQUIRED = 2
--};
--
--#ifndef __set_errno
--# define __set_errno(Val) errno = (Val)
--#endif
--
--/* If this host provides the openat function, then we can avoid
-- attempting to open "." in some initialization code below. */
--#ifdef HAVE_OPENAT
--# define HAVE_OPENAT_SUPPORT 1
--#else
--# define HAVE_OPENAT_SUPPORT 0
--#endif
--
--#ifdef NDEBUG
--# define fts_assert(expr) ((void) (0 && (expr)))
--#else
--# define fts_assert(expr) \
-- do \
-- { \
-- if (!(expr)) \
-- abort (); \
-- } \
-- while (false)
- #endif
-
--static FTSENTRY *fts_alloc (FTSOBJ *, const char *, size_t);
--static FTSENTRY *fts_build (FTSOBJ *, int);
--static void fts_lfree (FTSENTRY *);
--static void fts_load (FTSOBJ *, FTSENTRY *);
--static size_t fts_maxarglen (char * const *);
--static void fts_padjust (FTSOBJ *, FTSENTRY *);
--static bool fts_palloc (FTSOBJ *, size_t);
--static FTSENTRY *fts_sort (FTSOBJ *, FTSENTRY *, size_t);
--static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);
-+static FTSENTRY *fts_alloc (FTSOBJ *, const char *, size_t);
-+static FTSENTRY *fts_build (FTSOBJ *, int);
-+static void fts_lfree (FTSENTRY *);
-+static void fts_load (FTSOBJ *, FTSENTRY *);
-+static size_t fts_maxarglen (char * const *);
-+static void fts_padjust (FTSOBJ *, FTSENTRY *);
-+static int fts_palloc (FTSOBJ *, size_t);
-+static FTSENTRY *fts_sort (FTSOBJ *, FTSENTRY *, int);
-+static u_short fts_stat (FTSOBJ *, FTSENTRY *, int);
- static int fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);
-
--#include "fts-cycle.c"
--
- #ifndef MAX
--# define MAX(a,b) ((a) > (b) ? (a) : (b))
--#endif
--
--#ifndef SIZE_MAX
--# define SIZE_MAX ((size_t) -1)
-+#define MAX(a, b) ({ __typeof__ (a) _a = (a); \
-+ __typeof__ (b) _b = (b); \
-+ _a > _b ? _a : _b; })
- #endif
-
--#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-+#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-
--#define CLR(opt) (sp->fts_options &= ~(opt))
--#define ISSET(opt) ((sp->fts_options & (opt)) != 0)
--#define SET(opt) (sp->fts_options |= (opt))
--
--/* FIXME: FTS_NOCHDIR is now misnamed.
-- Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
--#define FCHDIR(sp, fd) \
-- (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD) \
-- ? (cwd_advance_fd ((sp), (fd), true), 0) \
-- : fchdir (fd)))
-+#define CLR(opt) (sp->fts_options &= ~(opt))
-+#define ISSET(opt) (sp->fts_options & (opt))
-+#define SET(opt) (sp->fts_options |= (opt))
-
-+#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && __fchdir(fd))
-
- /* fts_build flags */
--/* FIXME: make this an enum */
--#define BCHILD 1 /* fts_children */
--#define BNAMES 2 /* fts_children, names only */
--#define BREAD 3 /* fts_read */
--
--#if GNULIB_FTS_DEBUG
--# include <inttypes.h>
--# include <stdio.h>
--bool fts_debug = false;
--# define Dprintf(x) do { if (fts_debug) printf x; } while (false)
--static void fd_ring_check (FTSOBJ const *);
--static void fd_ring_print (FTSOBJ const *, FILE *, char const *);
--#else
--# define Dprintf(x)
--# define fd_ring_check(x)
--# define fd_ring_print(a, b, c)
--#endif
-+#define BCHILD 1 /* fts_children */
-+#define BNAMES 2 /* fts_children, names only */
-+#define BREAD 3 /* fts_read */
-
--#define LEAVE_DIR(Fts, Ent, Tag) \
-- do \
-- { \
-- Dprintf ((" %s-leaving: %s\n", Tag, (Ent)->fts_path)); \
-- leave_dir (Fts, Ent); \
-- fd_ring_check (Fts); \
-- } \
-- while (false)
--
--static void
--fd_ring_clear (I_ring *fd_ring)
--{
-- while ( ! i_ring_empty (fd_ring))
-- {
-- int fd = i_ring_pop (fd_ring);
-- if (0 <= fd)
-- close (fd);
-- }
--}
--
--/* Overload the fts_statp->st_size member (otherwise unused, when
-- fts_info is FTS_NSOK) to indicate whether fts_read should stat
-- this entry or not. */
--static void
--fts_set_stat_required (FTSENTRY *p, bool required)
-+FTSOBJ *
-+FTS_OPEN (char * const *argv, int options,
-+ int (*compar) (const FTSENTRY **, const FTSENTRY **))
- {
-- fts_assert (p->fts_info == FTS_NSOK);
-- p->fts_statp->st_size = (required
-- ? FTS_STAT_REQUIRED
-- : FTS_NO_STAT_REQUIRED);
--}
-+ FTSOBJ *sp;
-+ FTSENTRY *p, *root;
-+ int nitems;
-+ FTSENTRY *parent = NULL;
-+ FTSENTRY *tmp;
-+
-+ /* Options check. */
-+ if (options & ~FTS_OPTIONMASK) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-
--/* Virtual fchdir. Advance SP's working directory file descriptor,
-- SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.
-- CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory
-- open on sp->fts_cwd_fd; i.e., to move the working directory one level
-- down. */
--static void
--internal_function
--cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)
--{
-- int old = sp->fts_cwd_fd;
-- fts_assert (old != fd || old == AT_FDCWD);
--
-- if (chdir_down_one)
-- {
-- /* Push "old" onto the ring.
-- If the displaced file descriptor is non-negative, close it. */
-- int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
-- fd_ring_print (sp, stderr, "post-push");
-- if (0 <= prev_fd_in_slot)
-- close (prev_fd_in_slot); /* ignore any close failure */
-- }
-- else if ( ! ISSET (FTS_NOCHDIR))
-- {
-- if (0 <= old)
-- close (old); /* ignore any close failure */
-- }
--
-- sp->fts_cwd_fd = fd;
--}
-+ /* Allocate/initialize the stream */
-+ if ((sp = malloc((u_int)sizeof(FTSOBJ))) == NULL)
-+ return (NULL);
-+ memset(sp, 0, sizeof(FTSOBJ));
-+ sp->fts_compar = (int (*) (const void *, const void *)) compar;
-+ sp->fts_options = options;
-
--/* Restore the initial, pre-traversal, "working directory".
-- In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,
-- we may actually change the working directory.
-- Return 0 upon success. Upon failure, set errno and return nonzero. */
--static int
--restore_initial_cwd (FTSOBJ *sp)
--{
-- int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);
-- fd_ring_clear (&(sp->fts_fd_ring));
-- return fail;
--}
-+ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
-+ if (ISSET(FTS_LOGICAL))
-+ SET(FTS_NOCHDIR);
-
--/* Open the directory DIR if possible, and return a file
-- descriptor. Return -1 and set errno on failure. It doesn't matter
-- whether the file descriptor has read or write access. */
-+ /*
-+ * Start out with 1K of path space, and enough, in any case,
-+ * to hold the user's paths.
-+ */
-+#ifndef MAXPATHLEN
-+#define MAXPATHLEN 1024
-+#endif
-+ size_t maxarglen = fts_maxarglen(argv);
-+ if (fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
-+ goto mem1;
-+
-+ /* Allocate/initialize root's parent. */
-+ if (*argv != NULL) {
-+ if ((parent = fts_alloc(sp, "", 0)) == NULL)
-+ goto mem2;
-+ parent->fts_level = FTS_ROOTPARENTLEVEL;
-+ }
-+
-+ /* Allocate/initialize root(s). */
-+ for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
-+ /* Don't allow zero-length paths. */
-+ size_t len = strlen(*argv);
-+ if (len == 0) {
-+ __set_errno (ENOENT);
-+ goto mem3;
-+ }
-+
-+ p = fts_alloc(sp, *argv, len);
-+ p->fts_level = FTS_ROOTLEVEL;
-+ p->fts_parent = parent;
-+ p->fts_accpath = p->fts_name;
-+ p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
-+
-+ /* Command-line "." and ".." are real directories. */
-+ if (p->fts_info == FTS_DOT)
-+ p->fts_info = FTS_D;
-+
-+ /*
-+ * If comparison routine supplied, traverse in sorted
-+ * order; otherwise traverse in the order specified.
-+ */
-+ if (compar) {
-+ p->fts_link = root;
-+ root = p;
-+ } else {
-+ p->fts_link = NULL;
-+ if (root == NULL)
-+ tmp = root = p;
-+ else {
-+ tmp->fts_link = p;
-+ tmp = p;
-+ }
-+ }
-+ }
-+ if (compar && nitems > 1)
-+ root = fts_sort(sp, root, nitems);
-
--static int
--internal_function
--diropen (FTSOBJ const *sp, char const *dir)
--{
-- int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
-- | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
-+ /*
-+ * Allocate a dummy pointer and make fts_read think that we've just
-+ * finished the node before the root(s); set p->fts_info to FTS_INIT
-+ * so that everything about the "current" node is ignored.
-+ */
-+ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
-+ goto mem3;
-+ sp->fts_cur->fts_link = root;
-+ sp->fts_cur->fts_info = FTS_INIT;
-
-- int fd = (ISSET (FTS_CWDFD)
-- ? openat (sp->fts_cwd_fd, dir, open_flags)
-- : open (dir, open_flags));
-- return fd;
--}
-+ /*
-+ * If using chdir(2), grab a file descriptor pointing to dot to ensure
-+ * that we can get back here; this could be avoided for some paths,
-+ * but almost certainly not worth the effort. Slashes, symbolic links,
-+ * and ".." are all fairly nasty problems. Note, if we can't get the
-+ * descriptor we run anyway, just more slowly.
-+ */
-+ if (!ISSET(FTS_NOCHDIR)
-+ && (sp->fts_rfd = __open(".", O_RDONLY, 0)) < 0)
-+ SET(FTS_NOCHDIR);
-
--FTSOBJ *
--FTS_OPEN (char * const *argv,
-- register int options,
-- int (*compar) (const FTSENTRY **, const FTSENTRY **))
--{
-- /* Options check: glibc added other flags after FTS_NAMEONLY and
-- FTS_STOP, and they are assumed to be private. */
-- if (options & ~FTS_OPTIONMASK
--#if _LIBC
-- || options & (FTS_NAMEONLY | FTS_STOP)
--#endif
-- ) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
-- if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--#if !_LIBC
-- if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--#else
-- /* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or
-- FTS_LOGICAL is specified. */
-- if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))
-- options |= FTS_PHYSICAL;
--#endif
-+ return (sp);
-
-- /* Allocate/initialize the stream */
-- register FTSOBJ *sp = calloc (1, sizeof *sp);
-- if (sp == NULL)
-- return (NULL);
-- sp->fts_compar = FTS_COMPAR_CAST(compar);
-- sp->fts_options = options;
--
-- /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
-- if (ISSET(FTS_LOGICAL)) {
-- SET(FTS_NOCHDIR);
-- CLR(FTS_CWDFD);
-- }
--
-- /* Initialize fts_cwd_fd. */
-- sp->fts_cwd_fd = AT_FDCWD;
-- if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
-- {
-- /* While it isn't technically necessary to open "." this
-- early, doing it here saves us the trouble of ensuring
-- later (where it'd be messier) that "." can in fact
-- be opened. If not, revert to FTS_NOCHDIR mode. */
-- int fd = open (".", O_SEARCH | O_CLOEXEC);
-- if (fd < 0)
-- {
-- /* Even if "." is unreadable, don't revert to FTS_NOCHDIR mode
-- on systems like Linux+PROC_FS, where our openat emulation
-- is good enough. Note: on a system that emulates
-- openat via /proc, this technique can still fail, but
-- only in extreme conditions, e.g., when the working
-- directory cannot be saved (i.e. save_cwd fails) --
-- and that happens on Linux only when "." is unreadable
-- and the CWD would be longer than PATH_MAX.
-- FIXME: once Linux kernel openat support is well established,
-- replace the above open call and this entire if/else block
-- with the body of the if-block below. */
-- if ( openat_needs_fchdir ())
-- {
-- SET(FTS_NOCHDIR);
-- CLR(FTS_CWDFD);
-- }
-- }
-- else
-- {
-- close (fd);
-- }
-- }
--
-- /*
-- * Start out with 1K of file name space, and enough, in any case,
-- * to hold the user's file names.
-- */
--#ifndef MAXPATHLEN
--# define MAXPATHLEN 1024
--#endif
-- {
-- size_t maxarglen = fts_maxarglen(argv);
-- if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
-- goto mem1;
-- }
--
-- /* Allocate/initialize root's parent. */
-- FTSENTRY *parent = NULL;
-- if (*argv != NULL) {
-- if ((parent = fts_alloc(sp, "", 0)) == NULL)
-- goto mem2;
-- parent->fts_level = FTS_ROOTPARENTLEVEL;
-- }
--
-- /* The classic fts implementation would call fts_stat with
-- a new entry for each iteration of the loop below.
-- If the comparison function is not specified or if the
-- FTS_DEFER_STAT option is in effect, don't stat any entry
-- in this loop. This is an attempt to minimize the interval
-- between the initial stat/lstat/fstatat and the point at which
-- a directory argument is first opened. This matters for any
-- directory command line argument that resides on a file system
-- without genuine i-nodes. If you specify FTS_DEFER_STAT along
-- with a comparison function, that function must not access any
-- data via the fts_statp pointer. */
-- bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
--
-- /* Allocate/initialize root(s). */
-- register FTSENTRY *root;
-- register size_t nitems;
-- FTSENTRY *tmp = NULL; /* pacify gcc */
-- for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
-- /* *Do* allow zero-length file names. */
-- size_t len = strlen(*argv);
--
-- if ( ! (options & FTS_VERBATIM))
-- {
-- /* If there are two or more trailing slashes, trim all but one,
-- but don't change "//" to "/", and do map "///" to "/". */
-- char const *v = *argv;
-- if (2 < len && v[len - 1] == '/')
-- while (1 < len && v[len - 2] == '/')
-- --len;
-- }
--
-- register FTSENTRY *p = fts_alloc(sp, *argv, len);
-- if (p == NULL)
-- goto mem3;
-- p->fts_level = FTS_ROOTLEVEL;
-- p->fts_parent = parent;
-- p->fts_accpath = p->fts_name;
-- /* Even when defer_stat is true, be sure to stat the first
-- command line argument, since fts_read (at least with
-- FTS_XDEV) requires that. */
-- if (defer_stat && root != NULL) {
-- p->fts_info = FTS_NSOK;
-- fts_set_stat_required(p, true);
-- } else {
-- p->fts_info = fts_stat(sp, p, false);
-- }
--
-- /*
-- * If comparison routine supplied, traverse in sorted
-- * order; otherwise traverse in the order specified.
-- */
-- if (compar) {
-- p->fts_link = root;
-- root = p;
-- } else {
-- p->fts_link = NULL;
-- if (root == NULL)
-- tmp = root = p;
-- else {
-- tmp->fts_link = p;
-- tmp = p;
-- }
-- }
-- }
-- if (compar && nitems > 1)
-- root = fts_sort(sp, root, nitems);
--
-- /*
-- * Allocate a dummy pointer and make fts_read think that we've just
-- * finished the node before the root(s); set p->fts_info to FTS_INIT
-- * so that everything about the "current" node is ignored.
-- */
-- if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
-- goto mem3;
-- sp->fts_cur->fts_link = root;
-- sp->fts_cur->fts_info = FTS_INIT;
-- sp->fts_cur->fts_level = 1;
-- if (! setup_dir (sp))
-- goto mem3;
--
-- /*
-- * If using chdir(2), grab a file descriptor pointing to dot to ensure
-- * that we can get back here; this could be avoided for some file names,
-- * but almost certainly not worth the effort. Slashes, symbolic links,
-- * and ".." are all fairly nasty problems. Note, if we can't get the
-- * descriptor we run anyway, just more slowly.
-- */
-- if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
-- && (sp->fts_rfd = diropen (sp, ".")) < 0)
-- SET(FTS_NOCHDIR);
--
-- i_ring_init (&sp->fts_fd_ring, -1);
-- return (sp);
--
--mem3: fts_lfree(root);
-- free(FTSENT_WRAPPER(parent));
--mem2: free(sp->fts_path);
--mem1: free(sp);
-- return (NULL);
-+mem3: fts_lfree(root);
-+ free(parent);
-+mem2: free(sp->fts_path);
-+mem1: free(sp);
-+ return (NULL);
- }
-
- static void
--internal_function
--fts_load (FTSOBJ *sp, register FTSENTRY *p)
-+fts_load (FTSOBJ *sp, FTSENTRY *p)
- {
-- /*
-- * Load the stream structure for the next traversal. Since we don't
-- * actually enter the directory until after the preorder visit, set
-- * the fts_accpath field specially so the chdir gets done to the right
-- * place and the user can access the first node. From fts_open it's
-- * known that the file name will fit.
-- */
-- register size_t len = p->fts_pathlen = p->fts_namelen;
-- memmove(sp->fts_path, p->fts_name, len + 1);
-- register char *cp = strrchr(p->fts_name, '/');
-- if (cp && (cp != p->fts_name || cp[1])) {
-- len = strlen(++cp);
-- memmove(p->fts_name, cp, len + 1);
-- p->fts_namelen = len;
-- }
-- p->fts_accpath = p->fts_path = sp->fts_path;
-+ int len;
-+ char *cp;
-+
-+ /*
-+ * Load the stream structure for the next traversal. Since we don't
-+ * actually enter the directory until after the preorder visit, set
-+ * the fts_accpath field specially so the chdir gets done to the right
-+ * place and the user can access the first node. From fts_open it's
-+ * known that the path will fit.
-+ */
-+ len = p->fts_pathlen = p->fts_namelen;
-+ memmove(sp->fts_path, p->fts_name, len + 1);
-+ if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
-+ len = strlen(++cp);
-+ memmove(p->fts_name, cp, len + 1);
-+ p->fts_namelen = len;
-+ }
-+ p->fts_accpath = p->fts_path = sp->fts_path;
-+ sp->fts_dev = p->fts_dev;
- }
-
- int
- FTS_CLOSE (FTSOBJ *sp)
- {
-- /*
-- * This still works if we haven't read anything -- the dummy structure
-- * points to the root list, so we step through to the end of the root
-- * list which has a valid parent pointer.
-- */
-- if (sp->fts_cur) {
-- register FTSENTRY *p;
-- for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
-- register FTSENTRY *freep = p;
-- p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
-- free(FTSENT_WRAPPER(freep));
-- }
-- free(FTSENT_WRAPPER(p));
-- }
--
-- /* Free up child linked list, sort array, file name buffer. */
-- if (sp->fts_child)
-- fts_lfree(sp->fts_child);
-- free(sp->fts_array);
-- free(sp->fts_path);
--
-- int saved_errno = 0;
-- if (ISSET(FTS_CWDFD))
-- {
-- if (0 <= sp->fts_cwd_fd)
-- if (close (sp->fts_cwd_fd))
-- saved_errno = errno;
-- }
-- else if (!ISSET(FTS_NOCHDIR))
-- {
-- /* Return to original directory, save errno if necessary. */
-- if (fchdir(sp->fts_rfd))
-- saved_errno = errno;
--
-- /* If close fails, record errno only if saved_errno is zero,
-- so that we report the probably-more-meaningful fchdir errno. */
-- if (close (sp->fts_rfd))
-- if (saved_errno == 0)
-- saved_errno = errno;
-- }
--
-- fd_ring_clear (&sp->fts_fd_ring);
--
-- if (sp->fts_leaf_optimization_works_ht)
-- hash_free (sp->fts_leaf_optimization_works_ht);
--
-- free_dir (sp);
--
-- /* Free up the stream pointer. */
-- free(sp);
--
-- /* Set errno and return. */
-- if (saved_errno) {
-- __set_errno (saved_errno);
-- return (-1);
-- }
--
-- return (0);
--}
-+ FTSENTRY *freep, *p;
-+ int saved_errno;
-
--/* Minimum link count of a traditional Unix directory. When leaf
-- optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,
-- then the directory has no subdirectories. */
--enum { MIN_DIR_NLINK = 2 };
--
--/* Whether leaf optimization is OK for a directory. */
--enum leaf_optimization
-- {
-- /* st_nlink is not reliable for this directory's subdirectories. */
-- NO_LEAF_OPTIMIZATION,
--
-- /* st_nlink == 2 means the directory lacks subdirectories. */
-- OK_LEAF_OPTIMIZATION
-- };
--
--#if (defined __linux__ || defined __ANDROID__) \
-- && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE
--
--# include <sys/vfs.h>
--
--/* Linux-specific constants from coreutils' src/fs.h */
--# define S_MAGIC_AFS 0x5346414F
--# define S_MAGIC_CIFS 0xFF534D42
--# define S_MAGIC_LUSTRE 0x0BD00BD0
--# define S_MAGIC_NFS 0x6969
--# define S_MAGIC_PROC 0x9FA0
--# define S_MAGIC_TMPFS 0x1021994
--
--# ifdef HAVE___FSWORD_T
--typedef __fsword_t fsword;
--# else
--typedef long int fsword;
--# endif
--
--/* Map a stat.st_dev number to a file system type number f_ftype. */
--struct dev_type
--{
-- dev_t st_dev;
-- fsword f_type;
--};
--
--/* Use a tiny initial size. If a traversal encounters more than
-- a few devices, the cost of growing/rehashing this table will be
-- rendered negligible by the number of inodes processed. */
--enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };
--
--static size_t
--dev_type_hash (void const *x, size_t table_size)
--{
-- struct dev_type const *ax = x;
-- uintmax_t dev = ax->st_dev;
-- return dev % table_size;
--}
--
--static bool
--dev_type_compare (void const *x, void const *y)
--{
-- struct dev_type const *ax = x;
-- struct dev_type const *ay = y;
-- return ax->st_dev == ay->st_dev;
-+ /*
-+ * This still works if we haven't read anything -- the dummy structure
-+ * points to the root list, so we step through to the end of the root
-+ * list which has a valid parent pointer.
-+ */
-+ if (sp->fts_cur) {
-+ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
-+ freep = p;
-+ p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
-+ free(freep);
-+ }
-+ free(p);
-+ }
-+
-+ /* Free up child linked list, sort array, path buffer. */
-+ if (sp->fts_child)
-+ fts_lfree(sp->fts_child);
-+ free(sp->fts_array);
-+ free(sp->fts_path);
-+
-+ /* Return to original directory, save errno if necessary. */
-+ if (!ISSET(FTS_NOCHDIR)) {
-+ saved_errno = __fchdir(sp->fts_rfd) ? errno : 0;
-+ (void)__close(sp->fts_rfd);
-+
-+ /* Set errno and return. */
-+ if (saved_errno != 0) {
-+ /* Free up the stream pointer. */
-+ free(sp);
-+ __set_errno (saved_errno);
-+ return (-1);
-+ }
-+ }
-+
-+ /* Free up the stream pointer. */
-+ free(sp);
-+ return (0);
- }
-
--/* Return the file system type of P with file descriptor FD, or 0 if not known.
-- If FD is negative, P's file descriptor is unavailable.
-- Try to cache known values. */
-+/*
-+ * Special case of "/" at the end of the path so that slashes aren't
-+ * appended which would cause paths to be written as "....//foo".
-+ */
-+#define NAPPEND(p) \
-+ (p->fts_path[p->fts_pathlen - 1] == '/' \
-+ ? p->fts_pathlen - 1 : p->fts_pathlen)
-
--static fsword
--filesystem_type (FTSENTRY const *p, int fd)
-+FTSENTRY *
-+FTS_READ (FTSOBJ *sp)
- {
-- FTSOBJ *sp = FTSENT_FTS(p);
--
-- /* If we're not in CWDFD mode, don't bother with this optimization,
-- since the caller is not serious about performance. */
-- if (!ISSET (FTS_CWDFD))
-- return 0;
--
-- Hash_table *h = sp->fts_leaf_optimization_works_ht;
-- if (! h)
-- h = sp->fts_leaf_optimization_works_ht
-- = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,
-- dev_type_compare, free);
--
-- if (h)
-- {
-- struct dev_type tmp;
-- tmp.st_dev = p->fts_statp->st_dev;
-- struct dev_type *ent = hash_lookup (h, &tmp);
-- if (ent)
-- return ent->f_type;
-- }
--
-- /* Look-up failed. Query directly and cache the result. */
-- struct STRUCT_STATFS fs_buf;
-- if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)
-- return 0;
--
-- if (h)
-- {
-- struct dev_type *t2 = malloc (sizeof *t2);
-- if (t2)
-- {
-- t2->st_dev = p->fts_statp->st_dev;
-- t2->f_type = fs_buf.f_type;
--
-- struct dev_type *ent = hash_insert (h, t2);
-- if (ent)
-- fts_assert (ent == t2);
-- else
-- free (t2);
-- }
-- }
--
-- return fs_buf.f_type;
--}
-+ FTSENTRY *p, *tmp;
-+ int instr;
-+ char *t;
-+ int saved_errno;
-
--/* Return true if sorting dirents on inode numbers is known to improve
-- traversal performance for the directory P with descriptor DIR_FD.
-- Return false otherwise. When in doubt, return true.
-- DIR_FD is negative if unavailable. */
--static bool
--dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)
--{
-- /* Skip the sort only if we can determine efficiently
-- that skipping it is the right thing to do.
-- The cost of performing an unnecessary sort is negligible,
-- while the cost of *not* performing it can be O(N^2) with
-- a very large constant. */
--
-- switch (filesystem_type (p, dir_fd))
-- {
-- case S_MAGIC_LUSTRE:
-- /* On Lustre, sorting directory entries interferes with its ability to
-- prefetch file metadata (via statahead). This would make a command
-- like 'du' around 9 times slower. See
-- <https://bugs.gnu.org/80106>. */
-- case S_MAGIC_CIFS:
-- case S_MAGIC_NFS:
-- case S_MAGIC_TMPFS:
-- /* On a file system of any of these types, sorting
-- is unnecessary, and hence wasteful. */
-- return false;
--
-- default:
-- return true;
-- }
--}
-+ /* If finished or unrecoverable error, return NULL. */
-+ if (sp->fts_cur == NULL || ISSET(FTS_STOP))
-+ return (NULL);
-
--/* Given an FTS entry P for a directory with descriptor DIR_FD,
-- return whether it is valid to apply leaf optimization.
-- The optimization is valid if a directory's st_nlink value equal
-- to MIN_DIR_NLINK means the directory has no subdirectories.
-- DIR_FD is negative if unavailable. */
--static enum leaf_optimization
--leaf_optimization (FTSENTRY const *p, int dir_fd)
--{
-- switch (filesystem_type (p, dir_fd))
-- {
-- case 0:
-- /* Leaf optimization is unsafe if the file system type is unknown. */
-- FALLTHROUGH;
-- case S_MAGIC_AFS:
-- /* Although AFS mount points are not counted in st_nlink, they
-- act like directories. See <https://bugs.debian.org/143111>. */
-- FALLTHROUGH;
-- case S_MAGIC_CIFS:
-- /* Leaf optimization causes 'find' to abort. See
-- <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>. */
-- FALLTHROUGH;
-- case S_MAGIC_NFS:
-- /* NFS provides usable dirent.d_type but not necessarily for all entries
-- of large directories, so as per <https://bugzilla.redhat.com/1252549>
-- NFS should return true. However st_nlink values are not accurate on
-- all implementations as per <https://bugzilla.redhat.com/1299169>. */
-- FALLTHROUGH;
-- case S_MAGIC_PROC:
-- /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc
-- may have bogus stat.st_nlink values. */
-- return NO_LEAF_OPTIMIZATION;
--
-- default:
-- return OK_LEAF_OPTIMIZATION;
-- }
--}
-+ /* Set current node pointer. */
-+ p = sp->fts_cur;
-
--#else
--static bool
--dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,
-- _GL_UNUSED int dir_fd)
--{
-- return true;
--}
--static enum leaf_optimization
--leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)
--{
-- return NO_LEAF_OPTIMIZATION;
--}
--#endif
-+ /* Save and zero out user instructions. */
-+ instr = p->fts_instr;
-+ p->fts_instr = FTS_NOINSTR;
-
--/*
-- * Special case of "/" at the end of the file name so that slashes aren't
-- * appended which would cause file names to be written as "....//foo".
-- */
--#define NAPPEND(p) \
-- (p->fts_path[p->fts_pathlen - 1] == '/' \
-- ? p->fts_pathlen - 1 : p->fts_pathlen)
-+ /* Any type of file may be re-visited; re-stat and re-turn. */
-+ if (instr == FTS_AGAIN) {
-+ p->fts_info = fts_stat(sp, p, 0);
-+ return (p);
-+ }
-
--FTSENTRY *
--FTS_READ (FTSOBJ *sp)
--{
-- /* If finished or unrecoverable error, return NULL. */
-- if (sp->fts_cur == NULL || ISSET(FTS_STOP))
-- return (NULL);
--
-- /* Set current node pointer. */
-- register FTSENTRY *p = sp->fts_cur;
--
-- /* Save and zero out user instructions. */
-- register unsigned short int instr = p->fts_instr;
-- p->fts_instr = FTS_NOINSTR;
--
-- /* Any type of file may be re-visited; re-stat and re-turn. */
-- if (instr == FTS_AGAIN) {
-- p->fts_info = fts_stat(sp, p, false);
-- return (p);
-- }
-- Dprintf (("fts_read: p=%s\n",
-- p->fts_info == FTS_INIT ? "" : p->fts_path));
--
-- /*
-- * Following a symlink -- SLNONE test allows application to see
-- * SLNONE and recover. If indirecting through a symlink, have
-- * keep a pointer to current location. If unable to get that
-- * pointer, follow fails.
-- */
-- if (instr == FTS_FOLLOW &&
-- (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
-- p->fts_info = fts_stat(sp, p, true);
-- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-- p->fts_errno = errno;
-- p->fts_info = FTS_ERR;
-- } else
-- p->fts_flags |= FTS_SYMFOLLOW;
-- }
-- goto check_for_dir;
-- }
--
-- /* Directory in pre-order. */
-- if (p->fts_info == FTS_D) {
-- /* If skipped or crossed mount point, do post-order visit. */
-- if (instr == FTS_SKIP ||
-- (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
-- if (p->fts_flags & FTS_SYMFOLLOW)
-- (void)close(p->fts_symfd);
-- if (sp->fts_child) {
-- fts_lfree(sp->fts_child);
-- sp->fts_child = NULL;
-- }
-- p->fts_info = FTS_DP;
-- LEAVE_DIR (sp, p, "1");
-- return (p);
-- }
--
-- /* Rebuild if only read the names and now traversing. */
-- if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
-- CLR(FTS_NAMEONLY);
-- fts_lfree(sp->fts_child);
-- sp->fts_child = NULL;
-- }
--
-- /*
-- * Cd to the subdirectory.
-- *
-- * If have already read and now fail to chdir, whack the list
-- * to make the names come out right, and set the parent errno
-- * so the application will eventually get an error condition.
-- * Set the FTS_DONTCHDIR flag so that when we logically change
-- * directories back to the parent we don't do a chdir.
-- *
-- * If haven't read do so. If the read fails, fts_build sets
-- * FTS_STOP or the fts_info field of the node.
-- */
-- if (sp->fts_child != NULL) {
-- if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
-- p->fts_errno = errno;
-- p->fts_flags |= FTS_DONTCHDIR;
-- for (p = sp->fts_child; p != NULL;
-- p = p->fts_link)
-- p->fts_accpath =
-- p->fts_parent->fts_accpath;
-- }
-- } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
-- if (ISSET(FTS_STOP))
-- return (NULL);
-- /* If fts_build's call to fts_safe_changedir failed
-- because it was not able to fchdir into a
-- subdirectory, tell the caller. */
-- if (p->fts_errno && p->fts_info != FTS_DNR)
-- p->fts_info = FTS_ERR;
-- LEAVE_DIR (sp, p, "2");
-- return (p);
-- }
-- p = sp->fts_child;
-- sp->fts_child = NULL;
-- goto name;
-- }
--
-- /* Move to the next node on this level. */
--next: ;
-- register FTSENTRY *tmp = p;
--
-- /* If we have so many directory entries that we're reading them
-- in batches, and we've reached the end of the current batch,
-- read in a new batch. */
-- if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))
-- {
-- p = tmp->fts_parent;
-- sp->fts_cur = p;
-- sp->fts_path[p->fts_pathlen] = '\0';
--
-- if ((p = fts_build (sp, BREAD)) == NULL)
-- {
-- if (ISSET(FTS_STOP))
-- return NULL;
-- goto cd_dot_dot;
-- }
--
-- free(FTSENT_WRAPPER(tmp));
-- goto name;
-- }
--
-- if ((p = p->fts_link) != NULL) {
-- sp->fts_cur = p;
-- free(FTSENT_WRAPPER(tmp));
--
-- /*
-- * If reached the top, return to the original directory (or
-- * the root of the tree), and load the file names for the next
-- * root.
-- */
-- if (p->fts_level == FTS_ROOTLEVEL) {
-- if (restore_initial_cwd(sp)) {
-- SET(FTS_STOP);
-- return (NULL);
-- }
-- free_dir(sp);
-- fts_load(sp, p);
-- if (! setup_dir(sp)) {
-- free_dir(sp);
-- return (NULL);
-- }
-- goto check_for_dir;
-- }
--
-- /*
-- * User may have called fts_set on the node. If skipped,
-- * ignore. If followed, get a file descriptor so we can
-- * get back if necessary.
-- */
-- if (p->fts_instr == FTS_SKIP)
-- goto next;
-- if (p->fts_instr == FTS_FOLLOW) {
-- p->fts_info = fts_stat(sp, p, true);
-- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
-- p->fts_errno = errno;
-- p->fts_info = FTS_ERR;
-- } else
-- p->fts_flags |= FTS_SYMFOLLOW;
-- }
-- p->fts_instr = FTS_NOINSTR;
-- }
--
--name: {
-- register char *t = sp->fts_path + NAPPEND(p->fts_parent);
-- *t++ = '/';
-- memmove(t, p->fts_name, p->fts_namelen + 1);
-- }
--check_for_dir:
-- sp->fts_cur = p;
-- if (p->fts_info == FTS_NSOK)
-- {
-- if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
-- p->fts_info = fts_stat(sp, p, false);
-- else
-- fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
-- }
--
-- /* Skip files with different device numbers when FTS_MOUNT
-- is set. */
-- if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&
-- p->fts_level != FTS_ROOTLEVEL &&
-- p->fts_statp->st_dev != sp->fts_dev)
-- goto next;
--
-- if (p->fts_info == FTS_D)
-- {
-- /* Now that P->fts_statp is guaranteed to be valid, if
-- this is a command-line directory, record its device
-- number, to be used for FTS_MOUNT and FTS_XDEV. */
-- if (p->fts_level == FTS_ROOTLEVEL)
-- sp->fts_dev = p->fts_statp->st_dev;
-- Dprintf ((" entering: %s\n", p->fts_path));
-- if (! enter_dir (sp, p))
-- return NULL;
-- }
-- return p;
-- }
--cd_dot_dot:
--
-- /* Move up to the parent node. */
-- p = tmp->fts_parent;
-- sp->fts_cur = p;
-- free(FTSENT_WRAPPER(tmp));
--
-- if (p->fts_level == FTS_ROOTPARENTLEVEL) {
-- /*
-- * Done; free everything up and set errno to 0 so the user
-- * can distinguish between error and EOF.
-- */
-- free(FTSENT_WRAPPER(p));
-- __set_errno (0);
-- return (sp->fts_cur = NULL);
-- }
--
-- fts_assert (p->fts_info != FTS_NSOK);
--
-- /* NUL terminate the file name. */
-- sp->fts_path[p->fts_pathlen] = '\0';
--
-- /*
-- * Return to the parent directory. If at a root node, restore
-- * the initial working directory. If we came through a symlink,
-- * go back through the file descriptor. Otherwise, move up
-- * one level, via "..".
-- */
-- if (p->fts_level == FTS_ROOTLEVEL) {
-- if (restore_initial_cwd(sp)) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
-- } else if (p->fts_flags & FTS_SYMFOLLOW) {
-- if (FCHDIR(sp, p->fts_symfd)) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
-- (void)close(p->fts_symfd);
-- } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
-- fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
-- p->fts_errno = errno;
-- SET(FTS_STOP);
-- }
--
-- /* If the directory causes a cycle, preserve the FTS_DC flag and keep
-- the corresponding dev/ino pair in the hash table. It is going to be
-- removed when leaving the original directory. */
-- if (p->fts_info != FTS_DC) {
-- p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
-- if (p->fts_errno == 0)
-- LEAVE_DIR (sp, p, "3");
-- }
-- return ISSET(FTS_STOP) ? NULL : p;
-+ /*
-+ * Following a symlink -- SLNONE test allows application to see
-+ * SLNONE and recover. If indirecting through a symlink, have
-+ * keep a pointer to current location. If unable to get that
-+ * pointer, follow fails.
-+ */
-+ if (instr == FTS_FOLLOW &&
-+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
-+ p->fts_info = fts_stat(sp, p, 1);
-+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-+ if ((p->fts_symfd = __open(".", O_RDONLY, 0)) < 0) {
-+ p->fts_errno = errno;
-+ p->fts_info = FTS_ERR;
-+ } else
-+ p->fts_flags |= FTS_SYMFOLLOW;
-+ }
-+ return (p);
-+ }
-+
-+ /* Directory in pre-order. */
-+ if (p->fts_info == FTS_D) {
-+ /* If skipped or crossed mount point, do post-order visit. */
-+ if (instr == FTS_SKIP ||
-+ (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
-+ if (p->fts_flags & FTS_SYMFOLLOW)
-+ (void)__close(p->fts_symfd);
-+ if (sp->fts_child) {
-+ fts_lfree(sp->fts_child);
-+ sp->fts_child = NULL;
-+ }
-+ p->fts_info = FTS_DP;
-+ return (p);
-+ }
-+
-+ /* Rebuild if only read the names and now traversing. */
-+ if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
-+ CLR(FTS_NAMEONLY);
-+ fts_lfree(sp->fts_child);
-+ sp->fts_child = NULL;
-+ }
-+
-+ /*
-+ * Cd to the subdirectory.
-+ *
-+ * If have already read and now fail to chdir, whack the list
-+ * to make the names come out right, and set the parent errno
-+ * so the application will eventually get an error condition.
-+ * Set the FTS_DONTCHDIR flag so that when we logically change
-+ * directories back to the parent we don't do a chdir.
-+ *
-+ * If haven't read do so. If the read fails, fts_build sets
-+ * FTS_STOP or the fts_info field of the node.
-+ */
-+ if (sp->fts_child != NULL) {
-+ if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
-+ p->fts_errno = errno;
-+ p->fts_flags |= FTS_DONTCHDIR;
-+ for (p = sp->fts_child; p != NULL;
-+ p = p->fts_link)
-+ p->fts_accpath =
-+ p->fts_parent->fts_accpath;
-+ }
-+ } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
-+ if (ISSET(FTS_STOP))
-+ return (NULL);
-+ return (p);
-+ }
-+ p = sp->fts_child;
-+ sp->fts_child = NULL;
-+ sp->fts_cur = p;
-+ goto name;
-+ }
-+
-+ /* Move to the next node on this level. */
-+next: tmp = p;
-+ if ((p = p->fts_link) != NULL) {
-+ sp->fts_cur = p;
-+ free(tmp);
-+
-+ /*
-+ * If reached the top, return to the original directory (or
-+ * the root of the tree), and load the paths for the next root.
-+ */
-+ if (p->fts_level == FTS_ROOTLEVEL) {
-+ if (FCHDIR(sp, sp->fts_rfd)) {
-+ SET(FTS_STOP);
-+ return (NULL);
-+ }
-+ fts_load(sp, p);
-+ return p;
-+ }
-+
-+ /*
-+ * User may have called fts_set on the node. If skipped,
-+ * ignore. If followed, get a file descriptor so we can
-+ * get back if necessary.
-+ */
-+ if (p->fts_instr == FTS_SKIP)
-+ goto next;
-+ if (p->fts_instr == FTS_FOLLOW) {
-+ p->fts_info = fts_stat(sp, p, 1);
-+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-+ if ((p->fts_symfd =
-+ __open(".", O_RDONLY, 0)) < 0) {
-+ p->fts_errno = errno;
-+ p->fts_info = FTS_ERR;
-+ } else
-+ p->fts_flags |= FTS_SYMFOLLOW;
-+ }
-+ p->fts_instr = FTS_NOINSTR;
-+ }
-+
-+name: t = sp->fts_path + NAPPEND(p->fts_parent);
-+ *t++ = '/';
-+ memmove(t, p->fts_name, p->fts_namelen + 1);
-+ return p;
-+ }
-+
-+ /* Move up to the parent node. */
-+ p = tmp->fts_parent;
-+ sp->fts_cur = p;
-+ free(tmp);
-+
-+ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
-+ /*
-+ * Done; free everything up and set errno to 0 so the user
-+ * can distinguish between error and EOF.
-+ */
-+ free(p);
-+ __set_errno (0);
-+ return (sp->fts_cur = NULL);
-+ }
-+
-+ /* NUL terminate the pathname. */
-+ sp->fts_path[p->fts_pathlen] = '\0';
-+
-+ /*
-+ * Return to the parent directory. If at a root node or came through
-+ * a symlink, go back through the file descriptor. Otherwise, cd up
-+ * one directory.
-+ */
-+ if (p->fts_level == FTS_ROOTLEVEL) {
-+ if (FCHDIR(sp, sp->fts_rfd)) {
-+ SET(FTS_STOP);
-+ return (NULL);
-+ }
-+ } else if (p->fts_flags & FTS_SYMFOLLOW) {
-+ if (FCHDIR(sp, p->fts_symfd)) {
-+ saved_errno = errno;
-+ (void)__close(p->fts_symfd);
-+ __set_errno (saved_errno);
-+ SET(FTS_STOP);
-+ return (NULL);
-+ }
-+ (void)__close(p->fts_symfd);
-+ } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
-+ fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
-+ SET(FTS_STOP);
-+ return (NULL);
-+ }
-+ p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
-+ return p;
- }
-
- /*
-@@ -1170,157 +506,93 @@ cd_dot_dot:
- */
- /* ARGSUSED */
- int
--FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)
-+FTS_SET (FTSOBJ *sp, FTSENTRY *p, int instr)
- {
-- if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
-- instr != FTS_NOINSTR && instr != FTS_SKIP) {
-- __set_errno (EINVAL);
-- return (1);
-- }
-- p->fts_instr = instr;
-- return (0);
-+ if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
-+ instr != FTS_NOINSTR && instr != FTS_SKIP) {
-+ __set_errno (EINVAL);
-+ return (1);
-+ }
-+ p->fts_instr = instr;
-+ return (0);
- }
-
- FTSENTRY *
--FTS_CHILDREN (FTSOBJ *sp, int instr)
-+FTS_CHILDREN(FTSOBJ *sp, int instr)
- {
-- if (instr != 0 && instr != FTS_NAMEONLY) {
-- __set_errno (EINVAL);
-- return (NULL);
-- }
--
-- /* Set current node pointer. */
-- register FTSENTRY *p = sp->fts_cur;
--
-- /*
-- * Errno set to 0 so user can distinguish empty directory from
-- * an error.
-- */
-- __set_errno (0);
--
-- /* Fatal errors stop here. */
-- if (ISSET(FTS_STOP))
-- return (NULL);
--
-- /* Return logical hierarchy of user's arguments. */
-- if (p->fts_info == FTS_INIT)
-- return (p->fts_link);
--
-- /*
-- * If not a directory being visited in pre-order, stop here. Could
-- * allow FTS_DNR, assuming the user has fixed the problem, but the
-- * same effect is available with FTS_AGAIN.
-- */
-- if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
-- return (NULL);
--
-- /* Free up any previous child list. */
-- if (sp->fts_child != NULL)
-- fts_lfree(sp->fts_child);
--
-- if (instr == FTS_NAMEONLY) {
-- SET(FTS_NAMEONLY);
-- instr = BNAMES;
-- } else
-- instr = BCHILD;
--
-- /*
-- * If using chdir on a relative file name and called BEFORE fts_read
-- * does its chdir to the root of a traversal, we can lose -- we need to
-- * chdir into the subdirectory, and we don't know where the current
-- * directory is, so we can't get back so that the upcoming chdir by
-- * fts_read will work.
-- */
-- if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
-- ISSET(FTS_NOCHDIR))
-- return (sp->fts_child = fts_build(sp, instr));
--
-- int fd = diropen (sp, ".");
-- if (fd < 0)
-- return (sp->fts_child = NULL);
-- sp->fts_child = fts_build(sp, instr);
-- if (ISSET(FTS_CWDFD))
-- {
-- cwd_advance_fd (sp, fd, true);
-- }
-- else
-- {
-- if (fchdir(fd))
-- {
-- int saved_errno = errno;
-- close (fd);
-- __set_errno (saved_errno);
-- return NULL;
-- }
-- close (fd);
-- }
-- return (sp->fts_child);
--}
-+ FTSENTRY *p;
-+ int fd;
-
--/* A comparison function to sort on increasing inode number.
-- For some file system types, sorting either way makes a huge
-- performance difference for a directory with very many entries,
-- but sorting on increasing values is slightly better than sorting
-- on decreasing values. The difference is in the 5% range. */
--static int
--fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)
--{
-- return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);
-+ if (instr != 0 && instr != FTS_NAMEONLY) {
-+ __set_errno (EINVAL);
-+ return (NULL);
-+ }
-+
-+ /* Set current node pointer. */
-+ p = sp->fts_cur;
-+
-+ /*
-+ * Errno set to 0 so user can distinguish empty directory from
-+ * an error.
-+ */
-+ __set_errno (0);
-+
-+ /* Fatal errors stop here. */
-+ if (ISSET(FTS_STOP))
-+ return (NULL);
-+
-+ /* Return logical hierarchy of user's arguments. */
-+ if (p->fts_info == FTS_INIT)
-+ return (p->fts_link);
-+
-+ /*
-+ * If not a directory being visited in pre-order, stop here. Could
-+ * allow FTS_DNR, assuming the user has fixed the problem, but the
-+ * same effect is available with FTS_AGAIN.
-+ */
-+ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
-+ return (NULL);
-+
-+ /* Free up any previous child list. */
-+ if (sp->fts_child != NULL)
-+ fts_lfree(sp->fts_child);
-+
-+ if (instr == FTS_NAMEONLY) {
-+ SET(FTS_NAMEONLY);
-+ instr = BNAMES;
-+ } else
-+ instr = BCHILD;
-+
-+ /*
-+ * If using chdir on a relative path and called BEFORE fts_read does
-+ * its chdir to the root of a traversal, we can lose -- we need to
-+ * chdir into the subdirectory, and we don't know where the current
-+ * directory is, so we can't get back so that the upcoming chdir by
-+ * fts_read will work.
-+ */
-+ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
-+ ISSET(FTS_NOCHDIR))
-+ return (sp->fts_child = fts_build(sp, instr));
-+
-+ if ((fd = __open(".", O_RDONLY, 0)) < 0)
-+ return (NULL);
-+ sp->fts_child = fts_build(sp, instr);
-+ if (__fchdir(fd))
-+ return (NULL);
-+ (void)__close(fd);
-+ return (sp->fts_child);
- }
-
--/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
-- S_IF* bit and set ST.st_mode, thus clearing all other bits in that field. */
--static void
--set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)
-+static inline int
-+dirent_not_directory(const struct dirent *dp)
- {
-- mode_t type;
-- switch (dtype)
-- {
-- case DT_BLK:
-- type = S_IFBLK;
-- break;
-- case DT_CHR:
-- type = S_IFCHR;
-- break;
-- case DT_DIR:
-- type = S_IFDIR;
-- break;
-- case DT_FIFO:
-- type = S_IFIFO;
-- break;
-- case DT_LNK:
-- type = S_IFLNK;
-- break;
-- case DT_REG:
-- type = S_IFREG;
-- break;
-- case DT_SOCK:
-- type = S_IFSOCK;
-- break;
-- default:
-- type = 0;
-- }
-- st->st_mode = type;
-+#if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE
-+ return dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN;
-+#else
-+ return 0;
-+#endif
- }
-
--#define closedir_and_clear(dirp) \
-- do \
-- { \
-- closedir (dirp); \
-- dirp = NULL; \
-- } \
-- while (0)
--
--#define fts_opendir(file, Pdir_fd) \
-- OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
-- ? sp->fts_cwd_fd : AT_FDCWD), \
-- file, \
-- (((ISSET(FTS_PHYSICAL) \
-- && ! (ISSET(FTS_COMFOLLOW) \
-- && cur->fts_level == FTS_ROOTLEVEL)) \
-- ? O_NOFOLLOW : 0)), \
-- Pdir_fd)
--
- /*
- * This is the tricky part -- do not casually change *anything* in here. The
- * idea is to build the linked list of entries that are used by fts_children
-@@ -1336,873 +608,531 @@ set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)
- * been found, cutting the stat calls by about 2/3.
- */
- static FTSENTRY *
--internal_function
--fts_build (register FTSOBJ *sp, int type)
-+fts_build (FTSOBJ *sp, int type)
- {
-- FTSENTRY *cur = sp->fts_cur;
-- bool continue_readdir = !!FTSENT_DIRP(cur);
--
-- /* When cur->fts_dirp is non-NULL, that means we should
-- continue calling readdir on that existing DIR* pointer
-- rather than opening a new one. */
-- int dir_fd;
-- if (continue_readdir)
-- {
-- DIR *dp = FTSENT_DIRP(cur);
-- dir_fd = dirfd (dp);
-- if (dir_fd < 0)
-- {
-- int dirfd_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- if (type == BREAD)
-- {
-- cur->fts_info = FTS_DNR;
-- cur->fts_errno = dirfd_errno;
-- }
-- return NULL;
-- }
-- }
-- else
-- {
-- /* Open the directory for reading. If this fails, we're done.
-- If being called from fts_read, set the fts_info field. */
-- if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)
-- {
-- if (type == BREAD)
-- {
-- cur->fts_info = FTS_DNR;
-- cur->fts_errno = errno;
-- }
-- return NULL;
-- }
-- /* Rather than calling fts_stat for each and every entry encountered
-- in the readdir loop (below), stat each directory only right after
-- opening it. */
-- bool stat_optimization = cur->fts_info == FTS_NSOK;
--
-- if (stat_optimization
-- /* Also read the stat info again after opening a directory to
-- reveal eventual changes caused by a submount triggered by
-- the traversal. But do it only for utilities which use
-- FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
-- benefit/suffer from this feature for now. */
-- || ISSET (FTS_TIGHT_CYCLE_CHECK))
-- {
-- if (!stat_optimization)
-- LEAVE_DIR (sp, cur, "4");
-- if (FSTAT (dir_fd, cur->fts_statp) != 0)
-- {
-- int fstat_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- if (type == BREAD)
-- {
-- cur->fts_errno = fstat_errno;
-- cur->fts_info = FTS_NS;
-- }
-- __set_errno (fstat_errno);
-- return NULL;
-- }
-- if (stat_optimization)
-- cur->fts_info = FTS_D;
-- else if (! enter_dir (sp, cur))
-- {
-- int saved_errno = errno;
-- closedir_and_clear (FTSENT_DIRP(cur));
-- __set_errno (saved_errno);
-- return NULL;
-- }
-- }
-- }
--
-- /* Maximum number of readdir entries to read at one time. This
-- limitation is to avoid reading millions of entries into memory
-- at once. When an fts_compar function is specified, we have no
-- choice: we must read all entries into memory before calling that
-- function. But when no such function is specified, we can read
-- entries in batches that are large enough to help us with inode-
-- sorting, yet not so large that we risk exhausting memory. */
-- size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;
--
-- /*
-- * If we're going to need to stat anything or we want to descend
-- * and stay in the directory, chdir. If this fails we keep going,
-- * but set a flag so we don't chdir after the post-order visit.
-- * We won't be able to stat anything, but we can still return the
-- * names themselves. Note, that since fts_read won't be able to
-- * chdir into the directory, it will have to return different file
-- * names than before, i.e. "a/b" instead of "b". Since the node
-- * has already been visited in pre-order, have to wait until the
-- * post-order visit to return the error. There is a special case
-- * here, if there was nothing to stat then it's not an error to
-- * not be able to stat. This is all fairly nasty. If a program
-- * needed sorted entries or stat information, they had better be
-- * checking FTS_NS on the returned nodes.
-- */
-- bool descend;
-- if (continue_readdir)
-- {
-- /* When resuming a short readdir run, we already have
-- the required dirp and dir_fd. */
-- descend = true;
-- }
-- else
-- {
-- /* Try to descend unless it is a names-only fts_children,
-- or the directory is known to lack subdirectories. */
-- descend = (type != BNAMES
-- && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)
-- && ! ISSET (FTS_SEEDOT)
-- && cur->fts_statp->st_nlink == MIN_DIR_NLINK
-- && (leaf_optimization (cur, dir_fd)
-- != NO_LEAF_OPTIMIZATION)));
-- if (descend || type == BREAD)
-- {
-- if (ISSET(FTS_CWDFD))
-- dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-- if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
-- if (descend && type == BREAD)
-- cur->fts_errno = errno;
-- cur->fts_flags |= FTS_DONTCHDIR;
-- descend = false;
-- closedir_and_clear(FTSENT_DIRP(cur));
-- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
-- close (dir_fd);
-- FTSENT_DIRP(cur) = NULL;
-- } else
-- descend = true;
-- }
-- }
--
-- /*
-- * Figure out the max file name length that can be stored in the
-- * current buffer -- the inner loop allocates more space as necessary.
-- * We really wouldn't have to do the maxlen calculations here, we
-- * could do them in fts_read before returning the name, but it's a
-- * lot easier here since the length is part of the dirent structure.
-- *
-- * If not changing directories set a pointer so that can just append
-- * each new component into the file name.
-- */
-- size_t len = NAPPEND(cur);
-- char *cp;
-- if (ISSET(FTS_NOCHDIR)) {
-- cp = sp->fts_path + len;
-- *cp++ = '/';
-- } else {
-- /* GCC, you're too verbose. */
-- cp = NULL;
-- }
-- len++;
-- size_t maxlen = sp->fts_pathlen - len;
--
-- ptrdiff_t level = cur->fts_level + 1;
--
-- /* Read the directory, attaching each entry to the "link" pointer. */
-- bool doadjust = false;
-- register FTSENTRY *head = NULL;
-- FTSENTRY *tail = NULL;
-- register size_t nitems = 0;
-- bool sort_by_inode = false;
-- while (FTSENT_DIRP(cur)) {
-- __set_errno (0);
-- struct dirent *dp = readdir(FTSENT_DIRP(cur));
-- if (dp == NULL) {
-- /* Some readdir()s do not absorb ENOENT (dir
-- deleted but open). This bug was fixed in
-- glibc 2.3 (2002). */
--#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))
-- if (errno == ENOENT)
-- errno = 0;
-+ struct dirent *dp;
-+ FTSENTRY *p, *head;
-+ int nitems;
-+ FTSENTRY *cur, *tail;
-+ DIR *dirp;
-+ void *oldaddr;
-+ int cderrno, descend, len, level, nlinks, saved_errno,
-+ nostat, doadjust;
-+ size_t maxlen;
-+ char *cp;
-+
-+ /* Set current node pointer. */
-+ cur = sp->fts_cur;
-+
-+ /*
-+ * Open the directory for reading. If this fails, we're done.
-+ * If being called from fts_read, set the fts_info field.
-+ */
-+#if defined FTS_WHITEOUT && 0
-+ if (ISSET(FTS_WHITEOUT))
-+ oflag = DTF_NODUP|DTF_REWIND;
-+ else
-+ oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
-+#else
-+# define __opendir2(path, flag) __opendir(path)
- #endif
-- if (errno) {
-- cur->fts_errno = errno;
-- /* If we've not read any items yet, treat
-- the error as if we can't access the dir. */
-- cur->fts_info = (continue_readdir || nitems)
-- ? FTS_ERR : FTS_DNR;
-- }
-- closedir_and_clear(FTSENT_DIRP(cur));
-- break;
-- }
-- if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
-- continue;
--
-- size_t d_namelen = _D_EXACT_NAMLEN (dp);
-- register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);
-- if (!p)
-- goto mem1;
-- if (d_namelen >= maxlen) {
-- /* include space for NUL */
-- uintptr_t oldaddr = (uintptr_t) sp->fts_path;
-- if (! fts_palloc(sp, d_namelen + len + 1)) {
-- /*
-- * No more memory. Save
-- * errno, free up the current structure and the
-- * structures already allocated.
-- */
--mem1: ;
-- int saved_errno = errno;
-- free(FTSENT_WRAPPER(p));
-- fts_lfree(head);
-- closedir_and_clear(FTSENT_DIRP(cur));
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- __set_errno (saved_errno);
-- return (NULL);
-- }
-- /* Did realloc() change the pointer? */
-- if (oldaddr != (uintptr_t) sp->fts_path) {
-- doadjust = true;
-- if (ISSET(FTS_NOCHDIR))
-- cp = sp->fts_path + len;
-- }
-- maxlen = sp->fts_pathlen - len;
-- }
--
-- size_t new_len = len + d_namelen;
-- if (new_len < len) {
-- /*
-- * In the unlikely event that we would end up
-- * with a file name longer than SIZE_MAX, free up
-- * the current structure and the structures already
-- * allocated, then error out with ENAMETOOLONG.
-- */
-- free(FTSENT_WRAPPER(p));
-- fts_lfree(head);
-- closedir_and_clear(FTSENT_DIRP(cur));
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- __set_errno (ENAMETOOLONG);
-- return (NULL);
-- }
-- p->fts_level = level;
-- p->fts_parent = sp->fts_cur;
-- p->fts_pathlen = new_len;
--
-- /* Store dirent.d_ino, in case we need to sort
-- entries before processing them. */
-- p->fts_statp->st_ino = D_INO (dp);
--
-- /* Build a file name for fts_stat to stat. */
-- if (ISSET(FTS_NOCHDIR)) {
-- p->fts_accpath = p->fts_path;
-- memmove(cp, p->fts_name, p->fts_namelen + 1);
-- } else
-- p->fts_accpath = p->fts_name;
--
-- if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
-- /* Record what fts_read will have to do with this
-- entry. In many cases, it will simply fts_stat it,
-- but we can take advantage of any d_type information
-- to optimize away the unnecessary stat calls. I.e.,
-- if FTS_NOSTAT is in effect, we don't need device
-- numbers unconditionally (FTS_MOUNT) and we're not
-- following symlinks (FTS_PHYSICAL) and d_type
-- indicates this is *not* a directory, then we won't
-- have to stat it at all. If it *is* a directory,
-- then (currently) we stat it regardless, in order to
-- get device and inode numbers. Some day we might
-- optimize that away, too, for directories where
-- d_ino is known to be valid. */
-- bool skip_stat = (ISSET(FTS_NOSTAT)
-- && DT_IS_KNOWN(dp)
-- && ! DT_MUST_BE(dp, DT_DIR)
-- && (ISSET(FTS_PHYSICAL)
-- || ! DT_MUST_BE(dp, DT_LNK))
-- && ! ISSET(FTS_MOUNT));
-- p->fts_info = FTS_NSOK;
-- /* Propagate dirent.d_type information back
-- to caller, when possible. */
-- set_stat_type (p->fts_statp, D_TYPE (dp));
-- fts_set_stat_required(p, !skip_stat);
-- } else {
-- p->fts_info = fts_stat(sp, p, false);
-- }
--
-- /* We walk in directory order so "ls -f" doesn't get upset. */
-- p->fts_link = NULL;
-- if (head == NULL)
-- head = tail = p;
-- else {
-- tail->fts_link = p;
-- tail = p;
-- }
--
-- /* If there are many entries, no sorting function has been
-- specified, and this file system is of a type that may be
-- slow with a large number of entries, arrange to sort the
-- directory entries on increasing inode numbers.
--
-- The NITEMS comparison uses ==, not >, because the test
-- needs to be tried at most once once, and NITEMS will exceed
-- the threshold after it is incremented below. */
-- if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
-- && !sp->fts_compar)
-- sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);
--
-- ++nitems;
-- if (max_entries <= nitems) {
-- /* When there are too many dir entries, leave
-- fts_dirp open, so that a subsequent fts_read
-- can take up where we leave off. */
-- break;
-- }
-- }
--
-- /*
-- * If realloc() changed the address of the file name, adjust the
-- * addresses for the rest of the tree and the dir list.
-- */
-- if (doadjust)
-- fts_padjust(sp, head);
--
-- /*
-- * If not changing directories, reset the file name back to original
-- * state.
-- */
-- if (ISSET(FTS_NOCHDIR)) {
-- if (len == sp->fts_pathlen || nitems == 0)
-- --cp;
-- *cp = '\0';
-- }
--
-- /*
-- * If descended after called from fts_children or after called from
-- * fts_read and nothing found, get back. At the root level we use
-- * the saved fd; if one of fts_open()'s arguments is a relative name
-- * to an empty directory, we wind up here with no other way back. If
-- * can't get back, we're done.
-- */
-- if (!continue_readdir && descend && (type == BCHILD || !nitems) &&
-- (cur->fts_level == FTS_ROOTLEVEL
-- ? restore_initial_cwd(sp)
-- : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
-- cur->fts_info = FTS_ERR;
-- SET(FTS_STOP);
-- fts_lfree(head);
-- return (NULL);
-- }
--
-- /* If didn't find anything, return NULL. */
-- if (!nitems) {
-- if (type == BREAD
-- && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
-- cur->fts_info = FTS_DP;
-- fts_lfree(head);
-- return (NULL);
-- }
--
-- if (sort_by_inode) {
-- sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);
-- head = fts_sort (sp, head, nitems);
-- sp->fts_compar = NULL;
-- }
--
-- /* Sort the entries. */
-- if (sp->fts_compar && nitems > 1)
-- head = fts_sort(sp, head, nitems);
-- return (head);
--}
-+ if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
-+ if (type == BREAD) {
-+ cur->fts_info = FTS_DNR;
-+ cur->fts_errno = errno;
-+ }
-+ return (NULL);
-+ }
-
--#if GNULIB_FTS_DEBUG
-+ /*
-+ * Nlinks is the number of possible entries of type directory in the
-+ * directory if we're cheating on stat calls, 0 if we're not doing
-+ * any stat calls at all, -1 if we're doing stats on everything.
-+ */
-+ if (type == BNAMES) {
-+ nlinks = 0;
-+ /* Be quiet about nostat, GCC. */
-+ nostat = 0;
-+ } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
-+ nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
-+ nostat = 1;
-+ } else {
-+ nlinks = -1;
-+ nostat = 0;
-+ }
-+
-+#ifdef notdef
-+ (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
-+ (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
-+ ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
-+#endif
-+ /*
-+ * If we're going to need to stat anything or we want to descend
-+ * and stay in the directory, chdir. If this fails we keep going,
-+ * but set a flag so we don't chdir after the post-order visit.
-+ * We won't be able to stat anything, but we can still return the
-+ * names themselves. Note, that since fts_read won't be able to
-+ * chdir into the directory, it will have to return different path
-+ * names than before, i.e. "a/b" instead of "b". Since the node
-+ * has already been visited in pre-order, have to wait until the
-+ * post-order visit to return the error. There is a special case
-+ * here, if there was nothing to stat then it's not an error to
-+ * not be able to stat. This is all fairly nasty. If a program
-+ * needed sorted entries or stat information, they had better be
-+ * checking FTS_NS on the returned nodes.
-+ */
-+ cderrno = 0;
-+ if (nlinks || type == BREAD) {
-+ if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
-+ if (nlinks && type == BREAD)
-+ cur->fts_errno = errno;
-+ cur->fts_flags |= FTS_DONTCHDIR;
-+ descend = 0;
-+ cderrno = errno;
-+ (void)__closedir(dirp);
-+ dirp = NULL;
-+ } else
-+ descend = 1;
-+ } else
-+ descend = 0;
-
--struct devino {
-- intmax_t dev, ino;
--};
--#define PRINT_DEVINO "(%jd,%jd)"
-+ /*
-+ * Figure out the max file name length that can be stored in the
-+ * current path -- the inner loop allocates more path as necessary.
-+ * We really wouldn't have to do the maxlen calculations here, we
-+ * could do them in fts_read before returning the path, but it's a
-+ * lot easier here since the length is part of the dirent structure.
-+ *
-+ * If not changing directories set a pointer so that can just append
-+ * each new name into the path.
-+ */
-+ len = NAPPEND(cur);
-+ if (ISSET(FTS_NOCHDIR)) {
-+ cp = sp->fts_path + len;
-+ *cp++ = '/';
-+ } else {
-+ /* GCC, you're too verbose. */
-+ cp = NULL;
-+ }
-+ len++;
-+ maxlen = sp->fts_pathlen - len;
-+
-+ level = cur->fts_level + 1;
-+
-+ /* Read the directory, attaching each entry to the `link' pointer. */
-+ doadjust = 0;
-+ for (head = tail = NULL, nitems = 0; dirp && (dp = __readdir(dirp));) {
-+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
-+ continue;
-+
-+ if ((p = fts_alloc(sp, dp->d_name, _D_EXACT_NAMLEN (dp))) == NULL)
-+ goto mem1;
-+ if (_D_EXACT_NAMLEN (dp) >= maxlen) {/* include space for NUL */
-+ oldaddr = sp->fts_path;
-+ if (fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
-+ /*
-+ * No more memory for path or structures. Save
-+ * errno, free up the current structure and the
-+ * structures already allocated.
-+ */
-+mem1: saved_errno = errno;
-+ free(p);
-+ fts_lfree(head);
-+ (void)__closedir(dirp);
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ __set_errno (saved_errno);
-+ return (NULL);
-+ }
-+ /* Did realloc() change the pointer? */
-+ if (oldaddr != sp->fts_path) {
-+ doadjust = 1;
-+ if (ISSET(FTS_NOCHDIR))
-+ cp = sp->fts_path + len;
-+ }
-+ maxlen = sp->fts_pathlen - len;
-+ }
-+
-+ if (len + _D_EXACT_NAMLEN (dp) >= USHRT_MAX) {
-+ /*
-+ * In an FTSENT, fts_pathlen is a u_short so it is
-+ * possible to wraparound here. If we do, free up
-+ * the current structure and the structures already
-+ * allocated, then error out with ENAMETOOLONG.
-+ */
-+ free(p);
-+ fts_lfree(head);
-+ (void)__closedir(dirp);
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ __set_errno (ENAMETOOLONG);
-+ return (NULL);
-+ }
-+ p->fts_level = level;
-+ p->fts_parent = sp->fts_cur;
-+ p->fts_pathlen = len + _D_EXACT_NAMLEN (dp);
-+
-+#if defined FTS_WHITEOUT && 0
-+ if (dp->d_type == DT_WHT)
-+ p->fts_flags |= FTS_ISW;
-+#endif
-
--static struct devino
--getdevino (int fd)
--{
-- struct STRUCT_STAT st;
-- return (fd == AT_FDCWD
-- ? (struct devino) { -1, 0 }
-- : FSTAT (fd, &st) == 0
-- ? (struct devino) { st.st_dev, st.st_ino }
-- : (struct devino) { -1, errno });
--}
-+ /* Unreachable code. cderrno is only ever set to a nonnull
-+ value if dirp is closed at the same time. But then we
-+ cannot enter this loop. */
-+ if (0 && cderrno) {
-+ if (nlinks) {
-+ p->fts_info = FTS_NS;
-+ p->fts_errno = cderrno;
-+ } else
-+ p->fts_info = FTS_NSOK;
-+ p->fts_accpath = cur->fts_accpath;
-+ } else if (nlinks == 0
-+ || (nostat && dirent_not_directory(dp))) {
-+ p->fts_accpath =
-+ ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
-+ p->fts_info = FTS_NSOK;
-+ } else {
-+ /* Build a file name for fts_stat to stat. */
-+ if (ISSET(FTS_NOCHDIR)) {
-+ p->fts_accpath = p->fts_path;
-+ memmove(cp, p->fts_name, p->fts_namelen + 1);
-+ } else
-+ p->fts_accpath = p->fts_name;
-+ /* Stat it. */
-+ p->fts_info = fts_stat(sp, p, 0);
-+
-+ /* Decrement link count if applicable. */
-+ if (nlinks > 0 && (p->fts_info == FTS_D ||
-+ p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
-+ --nlinks;
-+ }
-+
-+ /* We walk in directory order so "ls -f" doesn't get upset. */
-+ p->fts_link = NULL;
-+ if (head == NULL)
-+ head = tail = p;
-+ else {
-+ tail->fts_link = p;
-+ tail = p;
-+ }
-+ ++nitems;
-+ }
-+ if (dirp)
-+ (void)__closedir(dirp);
-
--/* Walk ->fts_parent links starting at E_CURR, until the root of the
-- current hierarchy. There should be a directory with dev/inode
-- matching those of AD. If not, print a lot of diagnostics. */
--static void
--find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)
--{
-- for (FTSENTRY const *ent = e_curr;
-- ent->fts_level >= FTS_ROOTLEVEL;
-- ent = ent->fts_parent)
-- {
-- if (ad->ino == ent->fts_statp->st_ino
-- && ad->dev == ent->fts_statp->st_dev)
-- return;
-- }
-- printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
-- printf ("active dirs:\n");
-- for (FTSENTRY const *ent = e_curr;
-- ent->fts_level >= FTS_ROOTLEVEL;
-- ent = ent->fts_parent)
-- printf (" %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
-- ad->fts_ent->fts_accpath,
-- (uintmax_t) ad->dev,
-- (uintmax_t) ad->ino,
-- ent->fts_accpath,
-- (uintmax_t) ent->fts_statp->st_dev,
-- (uintmax_t) ent->fts_statp->st_ino);
--}
-+ /*
-+ * If realloc() changed the address of the path, adjust the
-+ * addresses for the rest of the tree and the dir list.
-+ */
-+ if (doadjust)
-+ fts_padjust(sp, head);
-
--void
--fts_cross_check (FTSOBJ const *sp)
--{
-- if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))
-- return;
--
-- FTSENTRY const *ent = sp->fts_cur;
--
-- Dprintf (("fts-cross-check cur=%s\n", ent->fts_path));
-- /* Make sure every parent dir is in the tree. */
-- for (FTSENTRY const *t = ent->fts_parent;
-- t->fts_level >= FTS_ROOTLEVEL;
-- t = t->fts_parent)
-- {
-- struct Active_dir ad;
-- ad.ino = t->fts_statp->st_ino;
-- ad.dev = t->fts_statp->st_dev;
-- if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
-- printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
-- }
--
-- /* Make sure every dir in the tree is an active dir.
-- But ENT is not necessarily a directory. If so, just skip this part. */
-- if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
-- && (ent->fts_info == FTS_DP
-- || ent->fts_info == FTS_D))
-- for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);
-- ad != NULL;
-- ad = hash_get_next (sp->fts_cycle.ht, ad))
-- {
-- find_matching_ancestor (ent, ad);
-- }
--}
-+ /*
-+ * If not changing directories, reset the path back to original
-+ * state.
-+ */
-+ if (ISSET(FTS_NOCHDIR)) {
-+ if (len == sp->fts_pathlen || nitems == 0)
-+ --cp;
-+ *cp = '\0';
-+ }
-
--static bool
--same_fd (int fd1, int fd2)
--{
-- struct STRUCT_STAT sb1, sb2;
-- return (FSTAT (fd1, &sb1) == 0
-- && FSTAT (fd2, &sb2) == 0
-- && psame_inode (&sb1, &sb2));
--}
-+ /*
-+ * If descended after called from fts_children or after called from
-+ * fts_read and nothing found, get back. At the root level we use
-+ * the saved fd; if one of fts_open()'s arguments is a relative path
-+ * to an empty directory, we wind up here with no other way back. If
-+ * can't get back, we're done.
-+ */
-+ if (descend && (type == BCHILD || !nitems) &&
-+ (cur->fts_level == FTS_ROOTLEVEL ?
-+ FCHDIR(sp, sp->fts_rfd) :
-+ fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
-+ cur->fts_info = FTS_ERR;
-+ SET(FTS_STOP);
-+ fts_lfree(head);
-+ return (NULL);
-+ }
-
--static void
--fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)
--{
-- if (!fts_debug)
-- return;
-- I_ring const *fd_ring = &sp->fts_fd_ring;
-- struct devino cwd = getdevino (sp->fts_cwd_fd);
-- fprintf (stream, "=== %s ========== "PRINT_DEVINO"\n", msg, cwd.dev, cwd.ino);
-- if (i_ring_empty (fd_ring))
-- return;
--
-- unsigned int i = fd_ring->ir_front;
-- while (true)
-- {
-- int fd = fd_ring->ir_data[i];
-- if (fd < 0)
-- fprintf (stream, "%u: %d:\n", i, fd);
-- else
-- {
-- struct devino wd = getdevino (fd);
-- fprintf (stream, "%u: %d: "PRINT_DEVINO"\n", i, fd, wd.dev, wd.ino);
-- }
-- if (i == fd_ring->ir_back)
-- break;
-- i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
-- }
--}
-+ /* If didn't find anything, return NULL. */
-+ if (!nitems) {
-+ if (type == BREAD)
-+ cur->fts_info = FTS_DP;
-+ fts_lfree(head);
-+ return (NULL);
-+ }
-
--/* Ensure that each file descriptor on the fd_ring matches a
-- parent, grandparent, etc. of the current working directory. */
--static void
--fd_ring_check (FTSOBJ const *sp)
--{
-- if (!fts_debug)
-- return;
--
-- /* Make a writable copy. */
-- I_ring fd_w = sp->fts_fd_ring;
--
-- int cwd_fd = sp->fts_cwd_fd;
-- cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-- struct devino dot = getdevino (cwd_fd);
-- fprintf (stderr, "===== check ===== cwd: "PRINT_DEVINO"\n",
-- dot.dev, dot.ino);
-- while ( ! i_ring_empty (&fd_w))
-- {
-- int fd = i_ring_pop (&fd_w);
-- if (0 <= fd)
-- {
-- int open_flags = O_SEARCH | O_CLOEXEC;
-- int parent_fd = openat (cwd_fd, "..", open_flags);
-- if (parent_fd < 0)
-- {
-- // Warn?
-- break;
-- }
-- if (!same_fd (fd, parent_fd))
-- {
-- struct devino cwd = getdevino (fd);
-- fprintf (stderr, "ring : "PRINT_DEVINO"\n", cwd.dev, cwd.ino);
-- struct devino c2 = getdevino (parent_fd);
-- fprintf (stderr, "parent: "PRINT_DEVINO"\n", c2.dev, c2.ino);
-- fts_assert (0);
-- }
-- close (cwd_fd);
-- cwd_fd = parent_fd;
-- }
-- }
-- close (cwd_fd);
-+ /* Sort the entries. */
-+ if (sp->fts_compar && nitems > 1)
-+ head = fts_sort(sp, head, nitems);
-+ return (head);
- }
--#endif
-
--static unsigned short int
--internal_function
--fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)
-+static u_short
-+fts_stat (FTSOBJ *sp, FTSENTRY *p, int follow)
- {
-- if (ISSET (FTS_LOGICAL)
-- || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))
-- follow = true;
--
-- struct STRUCT_STAT *sbp = p->fts_statp;
--
-- /*
-- * If doing a logical walk, or application requested FTS_FOLLOW, do
-- * a stat(2). If that fails, check for a nonexistent symlink. If
-- * fail, set the errno from the stat call.
-- */
-- int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
-- if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)
-- {
-- if (follow && errno == ENOENT
-- && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,
-- AT_SYMLINK_NOFOLLOW))
-- {
-- __set_errno (0);
-- return FTS_SLNONE;
-- }
--
-- p->fts_errno = errno;
-- memset (sbp, 0, sizeof *sbp);
-- return FTS_NS;
-- }
--
-- if (S_ISDIR(sbp->st_mode)) {
-- if (ISDOT(p->fts_name)) {
-- /* Command-line "." and ".." are real directories. */
-- return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
-- }
--
-- return (FTS_D);
-- }
-- if (S_ISLNK(sbp->st_mode))
-- return (FTS_SL);
-- if (S_ISREG(sbp->st_mode))
-- return (FTS_F);
-- return (FTS_DEFAULT);
--}
-+ FTSENTRY *t;
-+ dev_t dev;
-+ INO_T ino;
-+ struct STRUCT_STAT *sbp, sb;
-+ int saved_errno;
-+
-+ /* If user needs stat info, stat buffer already allocated. */
-+ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
-+
-+#if defined FTS_WHITEOUT && 0
-+ /* check for whiteout */
-+ if (p->fts_flags & FTS_ISW) {
-+ if (sbp != &sb) {
-+ memset(sbp, '\0', sizeof (*sbp));
-+ sbp->st_mode = S_IFWHT;
-+ }
-+ return (FTS_W);
-+ }
-+#endif
-
--static int
--fts_compar (void const *a, void const *b)
--{
-- /* Convert A and B to the correct types, to pacify the compiler, and
-- for portability to bizarre hosts where "void const *" and "FTSENT
-- const **" differ in runtime representation. The comparison
-- function cannot modify *a and *b, but there is no compile-time
-- check for this. */
-- FTSENTRY const **pa = (FTSENTRY const **) a;
-- FTSENTRY const **pb = (FTSENTRY const **) b;
-- return FTSENT_FTS(pa[0])->fts_compar (pa, pb);
-+ /*
-+ * If doing a logical walk, or application requested FTS_FOLLOW, do
-+ * a stat(2). If that fails, check for a non-existent symlink. If
-+ * fail, set the errno from the stat call.
-+ */
-+ if (ISSET(FTS_LOGICAL) || follow) {
-+ if (STAT(p->fts_accpath, sbp)) {
-+ saved_errno = errno;
-+ if (!LSTAT(p->fts_accpath, sbp)) {
-+ __set_errno (0);
-+ return (FTS_SLNONE);
-+ }
-+ p->fts_errno = saved_errno;
-+ goto err;
-+ }
-+ } else if (LSTAT(p->fts_accpath, sbp)) {
-+ p->fts_errno = errno;
-+err: memset(sbp, 0, sizeof(struct STRUCT_STAT));
-+ return (FTS_NS);
-+ }
-+
-+ if (S_ISDIR(sbp->st_mode)) {
-+ /*
-+ * Set the device/inode. Used to find cycles and check for
-+ * crossing mount points. Also remember the link count, used
-+ * in fts_build to limit the number of stat calls. It is
-+ * understood that these fields are only referenced if fts_info
-+ * is set to FTS_D.
-+ */
-+ dev = p->fts_dev = sbp->st_dev;
-+ ino = p->fts_ino = sbp->st_ino;
-+ p->fts_nlink = sbp->st_nlink;
-+
-+ if (ISDOT(p->fts_name))
-+ return (FTS_DOT);
-+
-+ /*
-+ * Cycle detection is done by brute force when the directory
-+ * is first encountered. If the tree gets deep enough or the
-+ * number of symbolic links to directories is high enough,
-+ * something faster might be worthwhile.
-+ */
-+ for (t = p->fts_parent;
-+ t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
-+ if (ino == t->fts_ino && dev == t->fts_dev) {
-+ p->fts_cycle = t;
-+ return (FTS_DC);
-+ }
-+ return (FTS_D);
-+ }
-+ if (S_ISLNK(sbp->st_mode))
-+ return (FTS_SL);
-+ if (S_ISREG(sbp->st_mode))
-+ return (FTS_F);
-+ return (FTS_DEFAULT);
- }
-
- static FTSENTRY *
--internal_function
--fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)
-+fts_sort (FTSOBJ *sp, FTSENTRY *head, int nitems)
- {
-- register FTSENTRY **ap, *p;
--
-- /* On most modern hosts, void * and FTSENT ** have the same
-- run-time representation, and one can convert sp->fts_compar to
-- the type qsort expects without problem. Use the heuristic that
-- this is OK if the two pointer types are the same size, and if
-- converting FTSENT ** to uintptr_t is the same as converting
-- FTSENT ** to void * and then to uintptr_t. This heuristic isn't
-- valid in general but we don't know of any counterexamples. */
-- FTSENTRY *dummy;
-- int (*compare) (void const *, void const *) =
-- ((sizeof &dummy == sizeof (void *)
-- && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)
-- ? (int (*) (void const *, void const *)) sp->fts_compar
-- : fts_compar);
--
-- /*
-- * Construct an array of pointers to the structures and call qsort(3).
-- * Reassemble the array in the order returned by qsort. If unable to
-- * sort for memory reasons, return the directory entries in their
-- * current order. Allocate enough space for the current needs plus
-- * 40 so don't realloc one entry at a time.
-- */
-- if (nitems > sp->fts_nitems) {
-- sp->fts_nitems = nitems + 40;
-- FTSENTRY **a;
-- if (! (a = reallocarray (sp->fts_array,
-- sp->fts_nitems, sizeof *a))) {
-- free(sp->fts_array);
-- sp->fts_array = NULL;
-- sp->fts_nitems = 0;
-- return (head);
-- }
-- sp->fts_array = a;
-- }
-- for (ap = sp->fts_array, p = head; p; p = p->fts_link)
-- *ap++ = p;
-- qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);
-- for (head = *(ap = sp->fts_array); --nitems; ++ap)
-- ap[0]->fts_link = ap[1];
-- ap[0]->fts_link = NULL;
-- return (head);
-+ FTSENTRY **ap, *p;
-+
-+ /*
-+ * Construct an array of pointers to the structures and call qsort(3).
-+ * Reassemble the array in the order returned by qsort. If unable to
-+ * sort for memory reasons, return the directory entries in their
-+ * current order. Allocate enough space for the current needs plus
-+ * 40 so don't realloc one entry at a time.
-+ */
-+ if (nitems > sp->fts_nitems) {
-+ FTSENTRY **a;
-+
-+ sp->fts_nitems = nitems + 40;
-+ if ((a = realloc(sp->fts_array,
-+ (size_t)(sp->fts_nitems * sizeof(FTSENTRY *)))) == NULL) {
-+ free(sp->fts_array);
-+ sp->fts_array = NULL;
-+ sp->fts_nitems = 0;
-+ return (head);
-+ }
-+ sp->fts_array = a;
-+ }
-+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
-+ *ap++ = p;
-+ qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), sp->fts_compar);
-+ for (head = *(ap = sp->fts_array); --nitems; ++ap)
-+ ap[0]->fts_link = ap[1];
-+ ap[0]->fts_link = NULL;
-+ return (head);
- }
-
- static FTSENTRY *
--internal_function
--fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)
-+fts_alloc (FTSOBJ *sp, const char *name, size_t namelen)
- {
-- /*
-- * The file name is a variable length array. Allocate the FTSENT
-- * structure and the file name in one chunk.
-- */
-- size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);
-- register FTSENTRY *p;
--#if !_LIBC
-- p = malloc(len);
-- if (p == NULL)
-- return (NULL);
--#else
-+ FTSENTRY *p;
-+ size_t len;
-+
- /*
-- * For glibc, we use a wrapper struct to provide the extra required
-- * fields without changing the FSENT layout.
-+ * The file name is a variable length array and no stat structure is
-+ * necessary if the user has set the nostat bit. Allocate the FTSENT
-+ * structure, the file name and the stat structure in one chunk, but
-+ * be careful that the stat structure is reasonably aligned. Since the
-+ * fts_name field is declared to be of size 1, the fts_name pointer is
-+ * namelen + 2 before the first possible address of the stat structure.
- */
-- len += sizeof (struct FTSENT_wrapper);
-- struct FTSENT_wrapper *wrapper = malloc(len);
-- if (wrapper == NULL)
-+ len = sizeof(FTSENTRY) + namelen;
-+ if (!ISSET(FTS_NOSTAT))
-+ len += sizeof(struct STRUCT_STAT) + ALIGNBYTES;
-+ if ((p = malloc(len)) == NULL)
- return (NULL);
-- p = &wrapper->ent;
-- p->fts_statp = &wrapper->fts_stat;
--#endif
-
-- /* Copy the name and guarantee NUL termination. */
-- memcpy(p->fts_name, name, namelen);
-- p->fts_name[namelen] = '\0';
--
-- p->fts_namelen = namelen;
-- FTSENT_FTS(p)= sp;
-- p->fts_path = sp->fts_path;
-- p->fts_errno = 0;
-- FTSENT_DIRP(p) = NULL;
-- p->fts_flags = 0;
-- p->fts_instr = FTS_NOINSTR;
-- p->fts_number = 0;
-- p->fts_pointer = NULL;
-- return (p);
-+ /* Copy the name and guarantee NUL termination. */
-+ memmove(p->fts_name, name, namelen);
-+ p->fts_name[namelen] = '\0';
-+
-+ if (!ISSET(FTS_NOSTAT))
-+ p->fts_statp = (struct STRUCT_STAT *)ALIGN(p->fts_name + namelen + 2);
-+ p->fts_namelen = namelen;
-+ p->fts_path = sp->fts_path;
-+ p->fts_errno = 0;
-+ p->fts_flags = 0;
-+ p->fts_instr = FTS_NOINSTR;
-+ p->fts_number = 0;
-+ p->fts_pointer = NULL;
-+ return (p);
- }
-
- static void
--internal_function
--fts_lfree (register FTSENTRY *head)
-+fts_lfree (FTSENTRY *head)
- {
-- int saved_errno = errno;
--
-- /* Free a linked list of structures. */
-- register FTSENTRY *p;
-- while ((p = head)) {
-- head = head->fts_link;
-- if (FTSENT_DIRP(p))
-- closedir (FTSENT_DIRP(p));
-- free(FTSENT_WRAPPER(p));
-- }
--
-- __set_errno (saved_errno);
-+ FTSENTRY *p;
-+
-+ /* Free a linked list of structures. */
-+ while ((p = head)) {
-+ head = head->fts_link;
-+ free(p);
-+ }
- }
-
- /*
-- * Allow essentially unlimited file name lengths; find, rm, ls should
-- * all work on any tree. Most systems will allow creation of file
-- * names much longer than MAXPATHLEN, even though the kernel won't
-- * resolve them. Add the size (not just what's needed) plus 256 bytes
-- * so don't realloc the file name 2 bytes at a time.
-+ * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
-+ * Most systems will allow creation of paths much longer than MAXPATHLEN, even
-+ * though the kernel won't resolve them. Add the size (not just what's needed)
-+ * plus 256 bytes so don't realloc the path 2 bytes at a time.
- */
--static bool
--internal_function
-+static int
- fts_palloc (FTSOBJ *sp, size_t more)
- {
-- size_t new_len = sp->fts_pathlen + more + 256;
--
-- /*
-- * See if fts_pathlen would overflow.
-- */
-- if (new_len < sp->fts_pathlen) {
-- free(sp->fts_path);
-- sp->fts_path = NULL;
-- __set_errno (ENAMETOOLONG);
-- return false;
-- }
-- sp->fts_pathlen = new_len;
-- char *p = realloc(sp->fts_path, sp->fts_pathlen);
-- if (p == NULL) {
-- free(sp->fts_path);
-- sp->fts_path = NULL;
-- return false;
-- }
-- sp->fts_path = p;
-- return true;
-+ char *p;
-+
-+ sp->fts_pathlen += more + 256;
-+ /*
-+ * Check for possible wraparound. In an FTS, fts_pathlen is
-+ * a signed int but in an FTSENT it is an unsigned short.
-+ * We limit fts_pathlen to USHRT_MAX to be safe in both cases.
-+ */
-+ if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) {
-+ free(sp->fts_path);
-+ sp->fts_path = NULL;
-+ __set_errno (ENAMETOOLONG);
-+ return (1);
-+ }
-+ p = realloc(sp->fts_path, sp->fts_pathlen);
-+ if (p == NULL) {
-+ free(sp->fts_path);
-+ sp->fts_path = NULL;
-+ return 1;
-+ }
-+ sp->fts_path = p;
-+ return 0;
- }
-
- /*
-- * When the file name is realloc'd, have to fix all of the pointers in
-- * structures already returned.
-+ * When the path is realloc'd, have to fix all of the pointers in structures
-+ * already returned.
- */
- static void
--internal_function
- fts_padjust (FTSOBJ *sp, FTSENTRY *head)
- {
-- char *addr = sp->fts_path;
--
-- /* This code looks at bit-patterns of freed pointers to
-- relocate them, so it relies on undefined behavior. If this
-- trick does not work on your platform, please report a bug. */
--
--#define ADJUST(p) do { \
-- uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath; \
-- if (old_accpath != (uintptr_t) (p)->fts_name) { \
-- (p)->fts_accpath = \
-- addr + (old_accpath - (uintptr_t) (p)->fts_path); \
-- } \
-- (p)->fts_path = addr; \
-+ FTSENTRY *p;
-+ char *addr = sp->fts_path;
-+
-+#define ADJUST(p) do { \
-+ if ((p)->fts_accpath != (p)->fts_name) { \
-+ (p)->fts_accpath = \
-+ (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
-+ } \
-+ (p)->fts_path = addr; \
- } while (0)
-- /* Adjust the current set of children. */
-- for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)
-- ADJUST(p);
--
-- /* Adjust the rest of the tree, including the current level. */
-- for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {
-- ADJUST(p);
-- p = p->fts_link ? p->fts_link : p->fts_parent;
-- }
-+ /* Adjust the current set of children. */
-+ for (p = sp->fts_child; p; p = p->fts_link)
-+ ADJUST(p);
-+
-+ /* Adjust the rest of the tree, including the current level. */
-+ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
-+ ADJUST(p);
-+ p = p->fts_link ? p->fts_link : p->fts_parent;
-+ }
- }
-
- static size_t
--internal_function _GL_ATTRIBUTE_PURE
- fts_maxarglen (char * const *argv)
- {
-- size_t max;
--
-- for (max = 0; *argv; ++argv) {
-- size_t len = strlen(*argv);
-- if (len > max)
-- max = len;
-- }
-- return (max + 1);
-+ size_t len, max;
-+
-+ for (max = 0; *argv; ++argv)
-+ if ((len = strlen(*argv)) > max)
-+ max = len;
-+ return (max + 1);
- }
-
- /*
-- * Change to dir specified by fd or file name without getting
-+ * Change to dir specified by fd or p->fts_accpath without getting
- * tricked by someone changing the world out from underneath us.
-- * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.
-- * If FD is non-negative, expect it to be used after this function returns,
-- * and to be closed eventually. So don't pass e.g., 'dirfd(dirp)' and then
-- * do closedir(dirp), because that would invalidate the saved FD.
-- * Upon failure, close FD immediately and return nonzero.
-+ * Assumes p->fts_dev and p->fts_ino are filled in.
- */
- static int
--internal_function
--fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)
-+fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, const char *path)
- {
-- fts_assert (0 <= fd || dir != NULL);
-- bool is_dotdot = dir && streq (dir, "..");
--
-- /* This clause handles the unusual case in which FTS_NOCHDIR
-- is specified, along with FTS_CWDFD. In that case, there is
-- no need to change even the virtual cwd file descriptor.
-- However, if FD is non-negative, we do close it here. */
-- if (ISSET (FTS_NOCHDIR))
-- {
-- if (ISSET (FTS_CWDFD) && 0 <= fd)
-- close (fd);
-- return 0;
-- }
--
-- if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
-- {
-- /* When possible, skip the diropen and subsequent fstat+dev/ino
-- comparison. I.e., when changing to parent directory
-- (chdir ("..")), use a file descriptor from the ring and
-- save the overhead of diropen+fstat, as well as avoiding
-- failure when we lack "x" access to the virtual cwd. */
-- if ( ! i_ring_empty (&sp->fts_fd_ring))
-- {
-- int parent_fd;
-- fd_ring_print (sp, stderr, "pre-pop");
-- parent_fd = i_ring_pop (&sp->fts_fd_ring);
-- if (0 <= parent_fd)
-- {
-- fd = parent_fd;
-- dir = NULL;
-- }
-- }
-- }
--
-- int newfd = fd;
-- if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
-- return -1;
--
-- /* The following dev/inode check is necessary if we're doing a
-- "logical" traversal (through symlinks, a la chown -L), if the
-- system lacks O_NOFOLLOW support, or if we're changing to ".."
-- (but not via a popped file descriptor). When changing to the
-- name "..", O_NOFOLLOW can't help. In general, when the target is
-- not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
-- follow a symlink, so we can avoid the expense of this fstat. */
-- int ret;
-- if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
-- || (dir && streq (dir, "..")))
-- {
-- struct STRUCT_STAT sb;
-- if (FSTAT (newfd, &sb))
-- {
-- ret = -1;
-- goto bail;
-- }
-- if (p->fts_statp->st_dev != sb.st_dev
-- || p->fts_statp->st_ino != sb.st_ino)
-- {
-- __set_errno (ENOENT); /* disinformation */
-- ret = -1;
-- goto bail;
-- }
-- }
--
-- if (ISSET(FTS_CWDFD))
-- {
-- cwd_advance_fd (sp, newfd, ! is_dotdot);
-- return 0;
-- }
--
-- ret = fchdir(newfd);
-+ int ret, oerrno, newfd;
-+ struct STRUCT_STAT sb;
-+
-+ newfd = fd;
-+ if (ISSET(FTS_NOCHDIR))
-+ return (0);
-+ if (fd < 0 && (newfd = __open(path, O_RDONLY, 0)) < 0)
-+ return (-1);
-+ if (FSTAT (newfd, &sb)) {
-+ ret = -1;
-+ goto bail;
-+ }
-+ if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
-+ __set_errno (ENOENT); /* disinformation */
-+ ret = -1;
-+ goto bail;
-+ }
-+ ret = __fchdir(newfd);
- bail:
-- if (fd < 0)
-- {
-- int oerrno = errno;
-- (void)close(newfd);
-- __set_errno (oerrno);
-- }
-- return ret;
-+ oerrno = errno;
-+ if (fd < 0)
-+ (void)__close(newfd);
-+ __set_errno (oerrno);
-+ return (ret);
- }
-diff --git a/io/fts.h b/io/fts.h
-index 50636e6c87a3a48d..b496e75ceeab0c3e 100644
---- a/io/fts.h
-+++ b/io/fts.h
-@@ -52,29 +52,7 @@
-
- #include <features.h>
- #include <sys/types.h>
--#include <sys/stat.h>
--#include <dirent.h>
--#include <stdbool.h>
-
--enum { __I_RING_SIZE = 4 };
--
--/* When ir_empty is true, the ring is empty.
-- Otherwise, ir_data[B..F] are defined, where B..F is the contiguous
-- range of indices, modulo I_RING_SIZE, from back to front, inclusive.
-- Undefined elements of ir_data are always set to ir_default_val.
-- Popping from an empty ring aborts.
-- Pushing onto a full ring returns the displaced value.
-- An empty ring has F==B and ir_empty == true.
-- A ring with one entry still has F==B, but now ir_empty == false. */
--struct __I_ring
--{
-- int ir_data[__I_RING_SIZE];
-- int ir_default_val;
-- unsigned int ir_front;
-- unsigned int ir_back;
-- bool ir_empty;
--};
--typedef struct __I_ring __I_ring;
-
- typedef struct {
- struct _ftsent *fts_cur; /* current node */
-@@ -95,106 +73,11 @@ typedef struct {
- #define FTS_SEEDOT 0x0020 /* return dot and dot-dot */
- #define FTS_XDEV 0x0040 /* don't cross devices */
- #define FTS_WHITEOUT 0x0080 /* return whiteout information */
--
-- /* There are two ways to detect cycles.
-- The lazy way (which works only with FTS_PHYSICAL),
-- with which one may process a directory that is a
-- part of the cycle several times before detecting the cycle.
-- The "tight" way, whereby fts uses more memory (proportional
-- to number of "active" directories, aka distance from root
-- of current tree to current directory -- see active_dir_ht)
-- to detect any cycle right away. For example, du must use
-- this option to avoid counting disk space in a cycle multiple
-- times, but chown -R need not.
-- The default is to use the constant-memory lazy way, when possible
-- (see below).
--
-- However, with FTS_LOGICAL (when following symlinks, e.g., chown -L)
-- using lazy cycle detection is inadequate. For example, traversing
-- a directory containing a symbolic link to a peer directory, it is
-- possible to encounter the same directory twice even though there
-- is no cycle:
-- dir
-- ...
-- slink -> dir
-- So, when FTS_LOGICAL is selected, we have to use a different
-- mode of cycle detection: FTS_TIGHT_CYCLE_CHECK. */
--#define FTS_TIGHT_CYCLE_CHECK 0x0400
--
-- /* Use this flag to enable semantics with which the parent
-- application may be made both more efficient and more robust.
-- Whereas the default is to visit each directory in a recursive
-- traversal (via chdir), using this flag makes it so the initial
-- working directory is never changed. Instead, these functions
-- perform the traversal via a virtual working directory, maintained
-- through the file descriptor member, fts_cwd_fd. */
--# define FTS_CWDFD 0x0800
--
-- /* Historically, for each directory that fts initially encounters, it would
-- open it, read all entries, and stat each entry, storing the results, and
-- then it would process the first entry. But that behavior is bad for
-- locality of reference, and also causes trouble with inode-simulating
-- file systems like FAT, CIFS, FUSE-based ones, etc., when entries from
-- their name/inode cache are flushed too early.
-- Use this flag to make fts_open and fts_read defer the stat/lstat/fststat
-- of each entry until it is actually processed. However, note that if you
-- use this option and also specify a comparison function, that function may
-- not examine any data via fts_statp. However, when fts_statp->st_mode is
-- nonzero, the S_IFMT type bits are valid, with mapped dirent.d_type data.
-- Of course, that happens only on file systems that provide useful
-- dirent.d_type data. */
--#define FTS_DEFER_STAT 0x1000
--
-- /* Use this flag to disable stripping of trailing slashes
-- from input path names during fts_open initialization. */
--#define FTS_VERBATIM 0x2000
--
--#define FTS_MOUNT 0x4000 /* skip other devices */
--#define FTS_OPTIONMASK 0x7fff /* valid user option mask */
-+#define FTS_OPTIONMASK 0x00ff /* valid user option mask */
-
- #define FTS_NAMEONLY 0x0100 /* (private) child names only */
- #define FTS_STOP 0x0200 /* (private) unrecoverable error */
--
- int fts_options; /* fts_open options, global flags */
--
-- int fts_cwd_fd; /* the file descriptor on which the
-- virtual cwd is open, or AT_FDCWD */
--
-- /* Map a directory's device number to a boolean. The boolean is
-- true if for that file system (type determined by a single fstatfs
-- call per FS) st_nlink can be used to calculate the number of
-- sub-directory entries in a directory.
-- Using this table is an optimization that permits us to look up
-- file system type on a per-inode basis at the minimal cost of
-- calling fstatfs only once per traversed device. */
-- struct hash_table *fts_leaf_optimization_works_ht;
--
-- union {
-- /* This data structure is used if FTS_TIGHT_CYCLE_CHECK is
-- specified. It records the directories between a starting
-- point and the current directory. I.e., a directory is
-- recorded here IFF we have visited it once, but we have not
-- yet completed processing of all its entries. Every time we
-- visit a new directory, we add that directory to this set.
-- When we finish with a directory (usually by visiting it a
-- second time), we remove it from this set. Each entry in
-- this data structure is a device/inode pair. This data
-- structure is used to detect directory cycles efficiently and
-- promptly even when the depth of a hierarchy is in the tens
-- of thousands. */
-- struct hash_table *ht;
--
-- /* FIXME: rename these two members to have the fts_ prefix */
-- /* This data structure uses a lazy cycle-detection algorithm,
-- as done by rm via cycle-check.c. It's the default,
-- but it's not appropriate for programs like du. */
-- struct cycle_check_state *state;
-- } fts_cycle;
--
-- /* A stack of the file descriptors corresponding to the
-- most-recently traversed parent directories.
-- Currently used only in FTS_CWDFD mode. */
-- __I_ring fts_fd_ring;
- } FTS;
-
- #ifdef __USE_LARGEFILE64
-@@ -209,13 +92,6 @@ typedef struct {
- int fts_nitems; /* elements in the sort array */
- int (*fts_compar) (const void *, const void *); /* compare fn */
- int fts_options; /* fts_open options, global flags */
-- int fts_cwd_fd;
-- struct hash_table *fts_leaf_optimization_works_ht;
-- union {
-- struct hash_table *ht;
-- struct cycle_check_state *state;
-- } fts_cycle;
-- __I_ring fts_fd_ring;
- } FTS64;
- #endif
-
-diff --git a/io/fts64-time64.c b/io/fts64-time64.c
-index 6a1053194eec51ba..f9bad60572605dcb 100644
---- a/io/fts64-time64.c
-+++ b/io/fts64-time64.c
-@@ -19,19 +19,18 @@
- #include <time.h>
-
- #if __TIMESIZE != 64
--# define FTS_OPEN __fts64_open_time64
--# define FTS_CLOSE __fts64_close_time64
--# define FTS_READ __fts64_read_time64
--# define FTS_SET __fts64_set_time64
--# define FTS_CHILDREN __fts64_children_time64
--# define FTSOBJ FTS64_TIME64
--# define FTSENTRY FSTENT64_TIME64
--# define INO_T ino64_t
--# define STRUCT_STAT __stat64_t64
--# define FSTAT __fstat64_time64
--# define FSTATAT __fstatat64_time64
--# define STRUCT_STATFS statfs64
--# define FSTATFS __fstatfs64
-+# define FTS_OPEN __fts64_open_time64
-+# define FTS_CLOSE __fts64_close_time64
-+# define FTS_READ __fts64_read_time64
-+# define FTS_SET __fts64_set_time64
-+# define FTS_CHILDREN __fts64_children_time64
-+# define FTSOBJ FTS64_TIME64
-+# define FTSENTRY FSTENT64_TIME64
-+# define INO_T ino64_t
-+# define STRUCT_STAT __stat64_t64
-+# define STAT __stat64_time64
-+# define LSTAT __lstat64_time64
-+# define FSTAT __fstat64_time64
-
- # include "fts.c"
- #endif
-diff --git a/io/fts64.c b/io/fts64.c
-index a6f607a87383f4a7..152910018e261049 100644
---- a/io/fts64.c
-+++ b/io/fts64.c
-@@ -25,9 +25,8 @@
- #define FTSENTRY FTSENT64
- #define INO_T ino64_t
- #define STRUCT_STAT stat64
-+#define STAT __stat64
-+#define LSTAT __lstat64
- #define FSTAT __fstat64
--#define FSTATAT __fstatat64
--#define STRUCT_STATFS statfs64
--#define FSTATFS __fstatfs64
-
- #include "fts.c"
-diff --git a/io/i-ring.c b/io/i-ring.c
-deleted file mode 100644
-index b4e47904dd1af4e5..0000000000000000
---- a/io/i-ring.c
-+++ /dev/null
-@@ -1,72 +0,0 @@
--/* a simple ring buffer
-- Copyright (C) 2006, 2009-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* written by Jim Meyering */
--
--#include <config.h>
--#if !_LIBC
--# include "i-ring.h"
--# define INTERNAL_DEF
--#else
--# define I_ring __I_ring
--# define I_RING_SIZE __I_RING_SIZE
--#endif
--
--#include <stdlib.h>
--
--INTERNAL_DEF void
--i_ring_init (I_ring *ir, int default_val)
--{
-- ir->ir_empty = true;
-- ir->ir_front = 0;
-- ir->ir_back = 0;
-- for (int i = 0; i < I_RING_SIZE; i++)
-- ir->ir_data[i] = default_val;
-- ir->ir_default_val = default_val;
--}
--
--INTERNAL_DEF bool
--i_ring_empty (I_ring const *ir)
--{
-- return ir->ir_empty;
--}
--
--INTERNAL_DEF int
--i_ring_push (I_ring *ir, int val)
--{
-- unsigned int dest_idx = (ir->ir_front + !ir->ir_empty) % I_RING_SIZE;
-- int old_val = ir->ir_data[dest_idx];
-- ir->ir_data[dest_idx] = val;
-- ir->ir_front = dest_idx;
-- if (dest_idx == ir->ir_back)
-- ir->ir_back = (ir->ir_back + !ir->ir_empty) % I_RING_SIZE;
-- ir->ir_empty = false;
-- return old_val;
--}
--
--INTERNAL_DEF int
--i_ring_pop (I_ring *ir)
--{
-- if (i_ring_empty (ir))
-- abort ();
-- int top_val = ir->ir_data[ir->ir_front];
-- ir->ir_data[ir->ir_front] = ir->ir_default_val;
-- if (ir->ir_front == ir->ir_back)
-- ir->ir_empty = true;
-- else
-- ir->ir_front = ((ir->ir_front + I_RING_SIZE - 1) % I_RING_SIZE);
-- return top_val;
--}
-diff --git a/io/same-inode.h b/io/same-inode.h
-deleted file mode 100644
-index 44b6f3ed84e42e8c..0000000000000000
---- a/io/same-inode.h
-+++ /dev/null
-@@ -1,106 +0,0 @@
--/* Determine whether two stat buffers are known to refer to the same file.
--
-- Copyright (C) 2006, 2009-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--#ifndef SAME_INODE_H
--#define SAME_INODE_H 1
--
--/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE. */
--#if !_LIBC && !_GL_CONFIG_H_INCLUDED
-- #error "Please include config.h first."
--#endif
--
--#include <sys/stat.h>
--#if _LIBC
--# define _GL_INLINE_HEADER_BEGIN
--# define _GL_INLINE_HEADER_END
--# define SAME_INODE_INLINE static inline
--# define S_TYPEISTMO(p) 0
--#endif
--
--_GL_INLINE_HEADER_BEGIN
--#ifndef SAME_INODE_INLINE
--# define SAME_INODE_INLINE _GL_INLINE
--#endif
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--
--/* True if A and B point to structs with st_dev and st_ino members
-- that are known to represent the same file.
--
-- Use | and ^ to shorten generated code, and to lessen the
-- probability of screwups if st_ino is an array. */
--
--#if defined __VMS && __CRTL_VER < 80200000
--# define PSAME_INODE(a, b) (! (((a)->st_dev ^ (b)->st_dev) \
-- | ((a)->st_ino[0] ^ (b)->st_ino[0]) \
-- | ((a)->st_ino[1] ^ (b)->st_ino[1]) \
-- | ((a)->st_ino[2] ^ (b)->st_ino[2])))
--#elif defined _WIN32 && ! defined __CYGWIN__
-- /* Native Windows. */
--# if _GL_WINDOWS_STAT_INODES
-- /* stat() and fstat() set st_dev and st_ino to 0 if information about
-- the inode is not available. */
--# if _GL_WINDOWS_STAT_INODES == 2
--# define PSAME_INODE(a, b) \
-- (! (! ((a)->st_dev | (a)->st_ino._gl_ino[0] | (a)->st_ino._gl_ino[1]) \
-- | ((a)->st_dev ^ (b)->st_dev) \
-- | ((a)->st_ino._gl_ino[0] ^ (b)->st_ino._gl_ino[0]) \
-- | ((a)->st_ino._gl_ino[1] ^ (b)->st_ino._gl_ino[1])))
--# else
--# define PSAME_INODE(a, b) (! (! ((a)->st_dev | (a)->st_ino) \
-- | ((a)->st_dev ^ (b)->st_dev) \
-- | ((a)->st_ino ^ (b)->st_ino)))
--# endif
--# else
-- /* stat() and fstat() set st_ino to 0 always. */
--# define PSAME_INODE(a, b) 0
--# endif
--#else
-- /* POSIX. */
--# define PSAME_INODE(a, b) (! (((a)->st_dev ^ (b)->st_dev) \
-- | ((a)->st_ino ^ (b)->st_ino)))
--#endif
--
--/* True if struct objects A and B are known to represent the same file. */
--
--#define SAME_INODE(a, b) PSAME_INODE (&(a), &(b))
--
--/* True if *A and *B represent the same file. Unlike PSAME_INODE,
-- args are evaluated once and must point to struct stat,
-- and this function works even on POSIX platforms where fstat etc. do
-- not return useful st_dev and st_ino values for shared memory
-- objects and typed memory objects. */
--
--SAME_INODE_INLINE bool
--psame_inode (struct stat const *a, struct stat const *b)
--{
-- return (! (S_TYPEISSHM (a) | S_TYPEISTMO (a)
-- | S_TYPEISSHM (b) | S_TYPEISTMO (b))
-- && PSAME_INODE (a, b));
--}
--
--
--#ifdef __cplusplus
--}
--#endif
--
--_GL_INLINE_HEADER_END
--
--#endif
-diff --git a/io/tst-fts-bz22944.c b/io/tst-fts-bz22944.c
-deleted file mode 100644
-index 7f860452349b3c9e..0000000000000000
---- a/io/tst-fts-bz22944.c
-+++ /dev/null
-@@ -1,100 +0,0 @@
--/* Check that fts does not fail with very long paths.
-- Copyright (C) 2026 Free Software Foundation, Inc.
-- This file is part of the GNU C Library.
--
-- The GNU C Library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- The GNU C Library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with the GNU C Library; if not, see
-- <https://www.gnu.org/licenses/>. */
--
--#include <fts.h>
--#include <errno.h>
--#include <stdio.h>
--#include <support/check.h>
--#include <support/temp_file.h>
--#include <support/xunistd.h>
--
--#define BASENAME "tst-fts-bz22944."
--
--enum { nested_depth = 150 };
--static const char dir_name[] = { [0 ... 254] = 'A', '\0' };
--
--static void
--do_cleanup (void)
--{
-- xchdir ("..");
-- for (int i = 0; i < nested_depth; i++)
-- {
-- remove (dir_name);
-- xchdir ("..");
-- }
-- remove (dir_name);
--}
--#define CLEANUP_HANDLER do_cleanup
--
--static void
--check_mkdir (const char *path)
--{
-- int r = mkdir (path, 0700);
-- /* Some filesystem such as overlayfs does not support larger path required
-- to trigger the internal buffer reallocation. */
-- if (r != 0)
-- {
-- if (errno == ENAMETOOLONG)
-- FAIL_UNSUPPORTED ("the filesystem does not support the required"
-- "large path");
-- else
-- FAIL_EXIT1 ("mkdir (\"%s\", 0%o): %m", path, 0700);
-- }
--}
--
--int
--do_test (void)
--{
-- char *tempdir = support_create_temp_directory (BASENAME);
-- xchdir (tempdir);
-- for (int i = 0; i < nested_depth; i++)
-- {
-- check_mkdir (dir_name);
-- xchdir (dir_name);
-- }
--
-- char *paths[] = { tempdir , 0 };
-- FTS *ftsp = fts_open (paths, FTS_XDEV | FTS_COMFOLLOW | FTS_PHYSICAL, 0);
-- TEST_VERIFY_EXIT (ftsp != NULL);
--
-- int num_dirs = 0;
--
-- FTSENT *ent;
-- while ((ent = fts_read(ftsp)) != 0)
-- {
-- switch (ent->fts_info)
-- {
-- case FTS_D:
-- num_dirs++;
-- break;
-- default:
-- break;
-- }
-- }
--
-- TEST_COMPARE (num_dirs, nested_depth + 1);
-- TEST_COMPARE (errno, 0);
--
-- fts_close (ftsp);
--
-- do_cleanup ();
--
-- return 0;
--}
--
--#include <support/test-driver.c>
-diff --git a/io/tst-fts-newflags.c b/io/tst-fts-newflags.c
-deleted file mode 100644
-index a40561268bb55914..0000000000000000
---- a/io/tst-fts-newflags.c
-+++ /dev/null
-@@ -1,234 +0,0 @@
--/* Simple tests for some gnulib imported fts features.
-- Copyright (C) 2026 Free Software Foundation, Inc.
-- This file is part of the GNU C Library.
--
-- The GNU C Library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- The GNU C Library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with the GNU C Library; if not, see
-- <https://www.gnu.org/licenses/>. */
--
--#include <errno.h>
--#include <fts.h>
--#include <stdbool.h>
--#include <stdio.h>
--#include <stdlib.h>
--#include <fcntl.h>
--#include <string.h>
--#include <support/check.h>
--#include <support/support.h>
--#include <support/temp_file.h>
--#include <support/xunistd.h>
--
--static char *tempdir;
--
--static void
--do_prepare (int argc, char **argv)
--{
-- tempdir = support_create_temp_directory ("tst-fts-newflags");
--
-- /* Create directory tree:
-- tempdir/dir
-- tempdir/dir/file
-- tempdir/dir/symlink -> tempdir (cycle) */
-- char *path;
--
-- path = xasprintf ("%s/dir", tempdir);
-- xmkdir (path, 0777);
-- add_temp_file (path);
-- free (path);
--
-- path = xasprintf ("%s/dir/file", tempdir);
-- int fd = xopen (path, O_CREAT | O_RDWR, 0666);
-- xclose (fd);
-- add_temp_file (path);
-- free (path);
--
-- path = xasprintf ("%s/dir/symlink", tempdir);
-- xsymlink (tempdir, path);
-- add_temp_file (path);
-- free (path);
--}
--#define PREPARE do_prepare
--
--/* FTS_TIGHT_CYCLE_CHECK: we use FTS_LOGICAL to follow the symlink we
-- created, causing a loop. The tight cycle checker should catch this
-- immediately and emit FTS_DC. */
--static void
--test_tight_cycle (void)
--{
-- char *paths[] = { tempdir, NULL };
-- FTS *fts = fts_open (paths, FTS_LOGICAL | FTS_TIGHT_CYCLE_CHECK, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
--
-- FTSENT *ent;
-- bool found_cycle = false;
-- while ((ent = fts_read (fts)) != NULL)
-- {
-- if (ent->fts_info == FTS_DC)
-- {
-- found_cycle = true;
-- break;
-- }
-- }
-- TEST_VERIFY (found_cycle);
-- fts_close (fts);
--}
--
--/* FTS_CWDFD: ensures that fts uses virtual file descriptors and does not
-- actually change the process's global working directory. */
--static void
--test_cwdfd (void)
--{
-- char *paths[] = { tempdir, NULL };
-- FTS *fts = fts_open (paths, FTS_PHYSICAL | FTS_CWDFD, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
--
-- char *cwd_before = getcwd (NULL, 0);
-- TEST_VERIFY_EXIT (cwd_before != NULL);
--
-- FTSENT *ent;
-- while ((ent = fts_read (fts)) != NULL)
-- {
-- }
--
-- char *cwd_after = getcwd (NULL, 0);
-- TEST_VERIFY_EXIT (cwd_after != NULL);
--
-- /* If FTS_CWDFD works, the global CWD remains untouched. */
-- TEST_COMPARE_STRING (cwd_before, cwd_after);
--
-- free (cwd_before);
-- free (cwd_after);
-- fts_close (fts);
--}
--
--/* FTS_DEFER_STAT: verifies that deferring the stat calls does not break
-- standard physical tree traversals. */
--static void
--test_defer_stat (void)
--{
-- char *paths[] = { tempdir, NULL };
-- FTS *fts = fts_open (paths, FTS_PHYSICAL | FTS_DEFER_STAT, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
--
-- FTSENT *ent;
-- int count = 0;
-- while ((ent = fts_read (fts)) != NULL)
-- {
-- count++;
-- }
--
-- /* Expects:
--
-- 1: $tmpdir
-- 2: $tmpdir/dir
-- 3: $tmpdir/dir/file
-- 4: $tmpdir/dir/symlink
-- 5: $tmpdir/dir/
-- 6: $tmpdir/ */
-- TEST_COMPARE (count, 6);
-- fts_close (fts);
--}
--
--/* FTS_MOUNT: validates that the traversal refuses to cross into a different
-- file system when the flag is set. Assumes /proc is on a different mount. */
--static void
--test_mount (void)
--{
-- /* Create a symlink to /proc, which resides on a different mount point. */
-- char *link_path = xasprintf ("%s/mnt_link", tempdir);
-- xsymlink ("/proc", link_path);
--
-- /* Traverse with FTS_LOGICAL so fts resolves the symlink and sees the
-- /proc device ID, but add FTS_MOUNT to enforce the boundary. */
-- char *paths[] = { tempdir, NULL };
-- FTS *fts = fts_open (paths, FTS_LOGICAL | FTS_MOUNT, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
--
-- FTSENT *ent;
-- bool found_mnt_link = false;
-- while ((ent = fts_read (fts)) != NULL)
-- {
-- if (strcmp (ent->fts_name, "mnt_link") == 0)
-- found_mnt_link = true;
-- }
--
-- /* Because the device ID of /proc differs from the root traversal
-- device ID, FTS_MOUNT forces fts to skip the entry entirely. */
-- TEST_VERIFY (!found_mnt_link);
--
-- fts_close (fts);
-- unlink (link_path);
-- free (link_path);
--}
--
--/* FTS_VERBATIM: Ensures paths are passed through without having trailing
-- slashes stripped. */
--static void
--test_verbatim (void)
--{
-- char *path_with_slashes = xasprintf ("%s///", tempdir);
-- char *paths[] = { path_with_slashes, NULL };
--
-- FTS *fts = fts_open (paths, FTS_PHYSICAL | FTS_VERBATIM, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
--
-- FTSENT *ent = fts_read (fts);
-- TEST_VERIFY_EXIT (ent != NULL);
--
-- /* The returned root path should retain the exact slashes passed to it. */
-- TEST_COMPARE_STRING (ent->fts_path, path_with_slashes);
--
-- fts_close (fts);
-- free (path_with_slashes);
--}
--
--/* Main test driver */
--static int
--do_test (void)
--{
-- support_need_proc ("FTS_MOUNT test requires /proc");
--
-- {
-- /* Check if fts_open does not fail if neither FTS_LOGICAL nor FTS_PHYSICAL
-- are specified. */
-- char *paths[] = { tempdir, NULL };
-- FTS *fts = fts_open (paths, 0, NULL);
-- TEST_VERIFY_EXIT (fts != NULL);
-- fts_close (fts);
-- }
--
-- {
-- FTS *fts;
-- char *paths[] = { tempdir, NULL };
--
-- /* There are internal only flags. */
-- fts = fts_open (paths, FTS_NAMEONLY, NULL);
-- TEST_VERIFY_EXIT (fts == NULL);
-- TEST_COMPARE (errno, EINVAL);
--
-- fts = fts_open (paths, FTS_STOP, NULL);
-- TEST_VERIFY_EXIT (fts == NULL);
-- TEST_COMPARE (errno, EINVAL);
-- }
--
-- test_tight_cycle ();
-- test_cwdfd ();
-- test_defer_stat ();
-- test_mount ();
-- test_verbatim ();
--
-- return 0;
--}
--
--/* Includes the glibc test framework driver */
--#include <support/test-driver.c>
-diff --git a/io/tst-fts.c b/io/tst-fts.c
-index ca82377d20c26efb..494eb29c9a7fdd95 100644
---- a/io/tst-fts.c
-+++ b/io/tst-fts.c
-@@ -192,8 +192,6 @@ do_test (void)
- don't use FTS_LOGICAL. */
- #ifndef TST_FTS_Y2038
- flags |= FTS_LOGICAL;
--#else
-- flags |= FTS_PHYSICAL;
- #endif
- fts = fts_open (paths, flags, &compare_ents);
- if (fts == NULL)
-diff --git a/misc/Makefile b/misc/Makefile
-index 4395366d74c12fcc..fc44de99348f5ceb 100644
---- a/misc/Makefile
-+++ b/misc/Makefile
-@@ -121,7 +121,6 @@ routines := \
- getusershell \
- getxattr \
- gtty \
-- hash \
- hsearch \
- hsearch_r \
- ifunc-impl-list \
-@@ -158,7 +157,6 @@ routines := \
- munlock \
- munlockall \
- munmap \
-- next-prime \
- preadv \
- preadv2 \
- preadv64 \
-diff --git a/misc/hash.c b/misc/hash.c
-deleted file mode 100644
-index f722713a8fb01796..0000000000000000
---- a/misc/hash.c
-+++ /dev/null
-@@ -1,1045 +0,0 @@
--/* hash - hashing table processing.
--
-- Copyright (C) 1998-2004, 2006-2007, 2009-2026 Free Software Foundation, Inc.
--
-- Written by Jim Meyering, 1992.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--/* A generic hash table package. */
--
--/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead
-- of malloc. If you change USE_OBSTACK, you have to recompile! */
--
--#include <config.h>
--#ifdef _LIBC
--# define USE_OBSTACK 0
--# define TESTING 0
--# include <stdbit.h>
--# define rotr_sz(__x, __n) stdc_rotate_right(__x, __n)
--#else
--# include "bitrotate.h"
--#endif
--
--#include "hash.h"
--
--#include "next-prime.h"
--#include "xalloc-oversized.h"
--
--#include <errno.h>
--#include <stdint.h>
--#include <stdio.h>
--#include <stdlib.h>
--
--#if USE_OBSTACK
--# include "obstack.h"
--# ifndef obstack_chunk_alloc
--# define obstack_chunk_alloc malloc
--# endif
--# ifndef obstack_chunk_free
--# define obstack_chunk_free free
--# endif
--#endif
--
--struct hash_entry
-- {
-- void *data;
-- struct hash_entry *next;
-- /* Invariant: If DATA is NULL, this entry is an unused bucket head,
-- and therefore NEXT is NULL as well. */
-- };
--
--struct hash_table
-- {
-- /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1,
-- for a possibility of N_BUCKETS. Among those, N_BUCKETS_USED buckets
-- are not empty, there are N_ENTRIES active entries in the table. */
-- struct hash_entry *bucket;
-- struct hash_entry const *bucket_limit;
-- size_t n_buckets;
-- size_t n_buckets_used;
-- size_t n_entries;
--
-- /* Tuning arguments, kept in a physically separate structure. */
-- const Hash_tuning *tuning;
--
-- /* Three functions are given to 'hash_initialize', see the documentation
-- block for this function. In a word, HASHER randomizes a user entry
-- into a number up from 0 up to some maximum minus 1; COMPARATOR returns
-- true if two user entries compare equally; and DATA_FREER is the cleanup
-- function for a user entry. */
-- Hash_hasher hasher;
-- Hash_comparator comparator;
-- Hash_data_freer data_freer;
--
-- /* A linked list of freed struct hash_entry structs. */
-- struct hash_entry *free_entry_list;
--
--#if USE_OBSTACK
-- /* Whenever obstacks are used, it is possible to allocate all overflowed
-- entries into a single stack, so they all can be freed in a single
-- operation. It is not clear if the speedup is worth the trouble. */
-- struct obstack entry_stack;
--#endif
-- };
--
--/* A hash table contains many internal entries, each holding a pointer to
-- some user-provided data (also called a user entry). An entry indistinctly
-- refers to both the internal entry and its associated user entry. A user
-- entry contents may be hashed by a randomization function (the hashing
-- function, or just "hasher" for short) into a number (or "slot") between 0
-- and the current table size. At each slot position in the hash table,
-- starts a linked chain of entries for which the user data all hash to this
-- slot. A bucket is the collection of all entries hashing to the same slot.
--
-- A good "hasher" function will distribute entries rather evenly in buckets.
-- In the ideal case, the length of each bucket is roughly the number of
-- entries divided by the table size. Finding the slot for a data is usually
-- done in constant time by the "hasher", and the later finding of a precise
-- entry is linear in time with the size of the bucket. Consequently, a
-- larger hash table size (that is, a larger number of buckets) is prone to
-- yielding shorter chains, *given* the "hasher" function behaves properly.
--
-- Long buckets slow down the lookup algorithm. One might use big hash table
-- sizes in hope to reduce the average length of buckets, but this might
-- become inordinate, as unused slots in the hash table take some space. The
-- best bet is to make sure you are using a good "hasher" function (beware
-- that those are not that easy to write! :-), and to use a table size
-- larger than the actual number of entries. */
--
--/* If an insertion makes the ratio of nonempty buckets to table size larger
-- than the growth threshold (a number between 0.0 and 1.0), then increase
-- the table size by multiplying by the growth factor (a number greater than
-- 1.0). The growth threshold defaults to 0.8, and the growth factor
-- defaults to 1.414, meaning that the table will have doubled its size
-- every second time 80% of the buckets get used. */
--#define DEFAULT_GROWTH_THRESHOLD 0.8f
--#define DEFAULT_GROWTH_FACTOR 1.414f
--
--/* If a deletion empties a bucket and causes the ratio of used buckets to
-- table size to become smaller than the shrink threshold (a number between
-- 0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a
-- number greater than the shrink threshold but smaller than 1.0). The shrink
-- threshold and factor default to 0.0 and 1.0, meaning that the table never
-- shrinks. */
--#define DEFAULT_SHRINK_THRESHOLD 0.0f
--#define DEFAULT_SHRINK_FACTOR 1.0f
--
--/* Use this to initialize or reset a TUNING structure to
-- some sensible values. */
--static const Hash_tuning default_tuning =
-- {
-- DEFAULT_SHRINK_THRESHOLD,
-- DEFAULT_SHRINK_FACTOR,
-- DEFAULT_GROWTH_THRESHOLD,
-- DEFAULT_GROWTH_FACTOR,
-- false
-- };
--
--/* Information and lookup. */
--
--#if !_LIBC
--size_t
--hash_get_n_buckets (const Hash_table *table)
--{
-- return table->n_buckets;
--}
--
--size_t
--hash_get_n_buckets_used (const Hash_table *table)
--{
-- return table->n_buckets_used;
--}
--
--size_t
--hash_get_n_entries (const Hash_table *table)
--{
-- return table->n_entries;
--}
--
--size_t
--hash_get_max_bucket_length (const Hash_table *table)
--{
-- size_t max_bucket_length = 0;
--
-- for (struct hash_entry const *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- struct hash_entry const *cursor = bucket;
-- size_t bucket_length = 1;
--
-- while (cursor = cursor->next, cursor)
-- bucket_length++;
--
-- if (bucket_length > max_bucket_length)
-- max_bucket_length = bucket_length;
-- }
-- }
--
-- return max_bucket_length;
--}
--
--bool
--hash_table_ok (const Hash_table *table)
--{
-- size_t n_buckets_used = 0;
-- size_t n_entries = 0;
--
-- for (struct hash_entry const *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- struct hash_entry const *cursor = bucket;
--
-- /* Count bucket head. */
-- n_buckets_used++;
-- n_entries++;
--
-- /* Count bucket overflow. */
-- while (cursor = cursor->next, cursor)
-- n_entries++;
-- }
-- }
--
-- if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries)
-- return true;
--
-- return false;
--}
--
--void
--hash_print_statistics (const Hash_table *table, FILE *stream)
--{
-- size_t n_entries = hash_get_n_entries (table);
-- size_t n_buckets = hash_get_n_buckets (table);
-- size_t n_buckets_used = hash_get_n_buckets_used (table);
-- size_t max_bucket_length = hash_get_max_bucket_length (table);
--
-- fprintf (stream, "# entries: %lu\n", (unsigned long int) n_entries);
-- fprintf (stream, "# buckets: %lu\n", (unsigned long int) n_buckets);
-- fprintf (stream, "# buckets used: %lu (%.2f%%)\n",
-- (unsigned long int) n_buckets_used,
-- (100.0 * n_buckets_used) / n_buckets);
-- fprintf (stream, "max bucket length: %lu\n",
-- (unsigned long int) max_bucket_length);
--}
--#endif
--
--/* Hash KEY and return a pointer to the selected bucket.
-- If TABLE->hasher misbehaves, abort. */
--static struct hash_entry *
--safe_hasher (const Hash_table *table, const void *key)
--{
-- size_t n = table->hasher (key, table->n_buckets);
-- if (! (n < table->n_buckets))
-- abort ();
-- return table->bucket + n;
--}
--
--void *
--hash_lookup (const Hash_table *table, const void *entry)
--{
-- struct hash_entry const *bucket = safe_hasher (table, entry);
--
-- if (bucket->data == NULL)
-- return NULL;
--
-- for (struct hash_entry const *cursor = bucket; cursor; cursor = cursor->next)
-- if (entry == cursor->data || table->comparator (entry, cursor->data))
-- return cursor->data;
--
-- return NULL;
--}
--
--#if !_LIBC
--/* Walking. */
--
--void *
--hash_get_first (const Hash_table *table)
--{
-- if (table->n_entries == 0)
-- return NULL;
--
-- for (struct hash_entry const *bucket = table->bucket; ; bucket++)
-- if (! (bucket < table->bucket_limit))
-- abort ();
-- else if (bucket->data)
-- return bucket->data;
--}
--
--void *
--hash_get_next (const Hash_table *table, const void *entry)
--{
-- struct hash_entry const *bucket = safe_hasher (table, entry);
--
-- /* Find next entry in the same bucket. */
-- {
-- struct hash_entry const *cursor = bucket;
-- do
-- {
-- if (cursor->data == entry && cursor->next)
-- return cursor->next->data;
-- cursor = cursor->next;
-- }
-- while (cursor != NULL);
-- }
--
-- /* Find first entry in any subsequent bucket. */
-- while (++bucket < table->bucket_limit)
-- if (bucket->data)
-- return bucket->data;
--
-- /* None found. */
-- return NULL;
--}
--
--size_t
--hash_get_entries (const Hash_table *table, void **buffer,
-- size_t buffer_size)
--{
-- size_t counter = 0;
--
-- for (struct hash_entry const *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- for (struct hash_entry const *cursor = bucket; cursor; cursor = cursor->next)
-- {
-- if (counter >= buffer_size)
-- return counter;
-- buffer[counter++] = cursor->data;
-- }
-- }
-- }
--
-- return counter;
--}
--
--size_t
--hash_do_for_each (const Hash_table *table, Hash_processor processor,
-- void *processor_data)
--{
-- size_t counter = 0;
--
-- for (struct hash_entry const *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- for (struct hash_entry const *cursor = bucket; cursor; cursor = cursor->next)
-- {
-- if (! processor (cursor->data, processor_data))
-- return counter;
-- counter++;
-- }
-- }
-- }
--
-- return counter;
--}
--#endif
--
--
--/* If the user passes a NULL hasher, we hash the raw pointer. */
--static size_t
--raw_hasher (const void *data, size_t n)
--{
-- /* When hashing unique pointers, it is often the case that they were
-- generated by malloc and thus have the property that the low-order
-- bits are 0. As this tends to give poorer performance with small
-- tables, we rotate the pointer value before performing division,
-- in an attempt to improve hash quality. */
-- size_t val = rotr_sz ((size_t) data, 3);
-- return val % n;
--}
--
--/* If the user passes a NULL comparator, we use pointer comparison. */
--static bool
--raw_comparator (const void *a, const void *b)
--{
-- return a == b;
--}
--
--
--#if !_LIBC
--/* Allocation and clean-up. */
--
--void
--hash_reset_tuning (Hash_tuning *tuning)
--{
-- *tuning = default_tuning;
--}
--#endif
--
--/* For the given hash TABLE, check the user supplied tuning structure for
-- reasonable values, and return true if there is no gross error with it.
-- Otherwise, definitively reset the TUNING field to some acceptable default
-- in the hash table (that is, the user loses the right of further modifying
-- tuning arguments), and return false. */
--
--static bool
--check_tuning (Hash_table *table)
--{
-- const Hash_tuning *tuning = table->tuning;
-- if (tuning == &default_tuning)
-- return true;
--
-- /* Be a bit stricter than mathematics would require, so that
-- rounding errors in size calculations do not cause allocations to
-- fail to grow or shrink as they should. The smallest allocation
-- is 11 (due to next_prime's algorithm), so an epsilon of 0.1
-- should be good enough. */
-- float epsilon = 0.1f;
--
-- if (epsilon < tuning->growth_threshold
-- && tuning->growth_threshold < 1 - epsilon
-- && 1 + epsilon < tuning->growth_factor
-- && 0 <= tuning->shrink_threshold
-- && tuning->shrink_threshold + epsilon < tuning->shrink_factor
-- && tuning->shrink_factor <= 1
-- && tuning->shrink_threshold + epsilon < tuning->growth_threshold)
-- return true;
--
-- table->tuning = &default_tuning;
-- return false;
--}
--
--/* Compute the size of the bucket array for the given CANDIDATE and
-- TUNING, or return 0 if there is no possible way to allocate that
-- many entries. */
--
--static size_t _GL_ATTRIBUTE_PURE
--compute_bucket_size (size_t candidate, const Hash_tuning *tuning)
--{
-- if (!tuning->is_n_buckets)
-- {
-- float new_candidate = candidate / tuning->growth_threshold;
-- if ((float) SIZE_MAX <= new_candidate)
-- goto nomem;
-- candidate = new_candidate;
-- }
-- candidate = next_prime (candidate);
-- if (xalloc_oversized (candidate, sizeof (struct hash_entry *)))
-- goto nomem;
-- return candidate;
--
-- nomem:
-- errno = ENOMEM;
-- return 0;
--}
--
--Hash_table *
--hash_initialize (size_t candidate, const Hash_tuning *tuning,
-- Hash_hasher hasher, Hash_comparator comparator,
-- Hash_data_freer data_freer)
--{
-- if (hasher == NULL)
-- hasher = raw_hasher;
-- if (comparator == NULL)
-- comparator = raw_comparator;
--
-- Hash_table *table = malloc (sizeof *table);
-- if (table == NULL)
-- return NULL;
--
-- if (!tuning)
-- tuning = &default_tuning;
-- table->tuning = tuning;
-- if (!check_tuning (table))
-- {
-- /* Fail if the tuning options are invalid. This is the only occasion
-- when the user gets some feedback about it. Once the table is created,
-- if the user provides invalid tuning options, we silently revert to
-- using the defaults, and ignore further request to change the tuning
-- options. */
-- errno = EINVAL;
-- goto fail;
-- }
--
-- table->n_buckets = compute_bucket_size (candidate, tuning);
-- if (!table->n_buckets)
-- goto fail;
--
-- table->bucket = calloc (table->n_buckets, sizeof *table->bucket);
-- if (table->bucket == NULL)
-- goto fail;
-- table->bucket_limit = table->bucket + table->n_buckets;
-- table->n_buckets_used = 0;
-- table->n_entries = 0;
--
-- table->hasher = hasher;
-- table->comparator = comparator;
-- table->data_freer = data_freer;
--
-- table->free_entry_list = NULL;
--#if USE_OBSTACK
-- obstack_init (&table->entry_stack);
--#endif
-- return table;
--
-- fail:
-- free (table);
-- return NULL;
--}
--
--#if !_LIBC
--void
--hash_clear (Hash_table *table)
--{
-- for (struct hash_entry *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- /* Free the bucket overflow. */
-- for (struct hash_entry *cursor = bucket->next; cursor; )
-- {
-- if (table->data_freer)
-- table->data_freer (cursor->data);
-- cursor->data = NULL;
--
-- struct hash_entry *next = cursor->next;
-- /* Relinking is done one entry at a time, as it is to be expected
-- that overflows are either rare or short. */
-- cursor->next = table->free_entry_list;
-- table->free_entry_list = cursor;
--
-- cursor = next;
-- }
--
-- /* Free the bucket head. */
-- if (table->data_freer)
-- table->data_freer (bucket->data);
-- bucket->data = NULL;
-- bucket->next = NULL;
-- }
-- }
--
-- table->n_buckets_used = 0;
-- table->n_entries = 0;
--}
--#endif
--
--void
--hash_free (Hash_table *table)
--{
-- int saved_errno = errno;
--
-- /* Call the user data_freer function. */
-- if (table->data_freer && table->n_entries)
-- {
-- for (struct hash_entry *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket->data)
-- {
-- for (struct hash_entry *cursor = bucket; cursor; cursor = cursor->next)
-- table->data_freer (cursor->data);
-- }
-- }
-- }
--
--#if USE_OBSTACK
--
-- obstack_free (&table->entry_stack, NULL);
--
--#else
--
-- /* Free all bucket overflowed entries. */
-- for (struct hash_entry *bucket = table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- for (struct hash_entry *cursor = bucket->next; cursor; )
-- {
-- struct hash_entry *next = cursor->next;
-- free (cursor);
-- cursor = next;
-- }
-- }
--
-- /* Also reclaim the internal list of previously freed entries. */
-- for (struct hash_entry *cursor = table->free_entry_list; cursor; )
-- {
-- struct hash_entry *next = cursor->next;
-- free (cursor);
-- cursor = next;
-- }
--
--#endif
--
-- /* Free the remainder of the hash table structure. */
-- free (table->bucket);
-- free (table);
--
-- errno = saved_errno;
--}
--
--/* Insertion and deletion. */
--
--/* Get a new hash entry for a bucket overflow, possibly by recycling a
-- previously freed one. If this is not possible, allocate a new one. */
--
--static struct hash_entry *
--allocate_entry (Hash_table *table)
--{
-- struct hash_entry *new;
--
-- if (table->free_entry_list)
-- {
-- new = table->free_entry_list;
-- table->free_entry_list = new->next;
-- }
-- else
-- {
--#if USE_OBSTACK
-- new = obstack_alloc (&table->entry_stack, sizeof *new);
--#else
-- new = malloc (sizeof *new);
--#endif
-- }
--
-- return new;
--}
--
--/* Free a hash entry which was part of some bucket overflow,
-- saving it for later recycling. */
--
--static void
--free_entry (Hash_table *table, struct hash_entry *entry)
--{
-- entry->data = NULL;
-- entry->next = table->free_entry_list;
-- table->free_entry_list = entry;
--}
--
--/* This private function is used to help with insertion and deletion. When
-- ENTRY matches an entry in the table, return a pointer to the corresponding
-- user data and set *BUCKET_HEAD to the head of the selected bucket.
-- Otherwise, return NULL. When DELETE is true and ENTRY matches an entry in
-- the table, unlink the matching entry. */
--
--static void *
--find_entry (Hash_table *table, const void *entry,
-- struct hash_entry **bucket_head, bool delete)
--{
-- struct hash_entry *bucket = safe_hasher (table, entry);
--
-- *bucket_head = bucket;
--
-- /* Test for empty bucket. */
-- if (bucket->data == NULL)
-- return NULL;
--
-- /* See if the entry is the first in the bucket. */
-- if (entry == bucket->data || table->comparator (entry, bucket->data))
-- {
-- void *data = bucket->data;
--
-- if (delete)
-- {
-- if (bucket->next)
-- {
-- struct hash_entry *next = bucket->next;
--
-- /* Bump the first overflow entry into the bucket head, then save
-- the previous first overflow entry for later recycling. */
-- *bucket = *next;
-- free_entry (table, next);
-- }
-- else
-- {
-- bucket->data = NULL;
-- }
-- }
--
-- return data;
-- }
--
-- /* Scan the bucket overflow. */
-- for (struct hash_entry *cursor = bucket; cursor->next; cursor = cursor->next)
-- {
-- if (entry == cursor->next->data
-- || table->comparator (entry, cursor->next->data))
-- {
-- void *data = cursor->next->data;
--
-- if (delete)
-- {
-- struct hash_entry *next = cursor->next;
--
-- /* Unlink the entry to delete, then save the freed entry for later
-- recycling. */
-- cursor->next = next->next;
-- free_entry (table, next);
-- }
--
-- return data;
-- }
-- }
--
-- /* No entry found. */
-- return NULL;
--}
--
--/* Internal helper, to move entries from SRC to DST. Both tables must
-- share the same free entry list. If SAFE, only move overflow
-- entries, saving bucket heads for later, so that no allocations will
-- occur. Return false (setting errno) if the free entry list is
-- exhausted and an allocation fails. */
--
--static bool
--transfer_entries (Hash_table *dst, Hash_table *src, bool safe)
--{
-- for (struct hash_entry *bucket = src->bucket; bucket < src->bucket_limit; bucket++)
-- if (bucket->data)
-- {
-- void *data;
-- struct hash_entry *new_bucket;
--
-- /* Within each bucket, transfer overflow entries first and
-- then the bucket head, to minimize memory pressure. After
-- all, the only time we might allocate is when moving the
-- bucket head, but moving overflow entries first may create
-- free entries that can be recycled by the time we finally
-- get to the bucket head. */
-- for (struct hash_entry *cursor = bucket->next; cursor; )
-- {
-- data = cursor->data;
-- new_bucket = safe_hasher (dst, data);
--
-- struct hash_entry *next = cursor->next;
--
-- if (new_bucket->data)
-- {
-- /* Merely relink an existing entry, when moving from a
-- bucket overflow into a bucket overflow. */
-- cursor->next = new_bucket->next;
-- new_bucket->next = cursor;
-- }
-- else
-- {
-- /* Free an existing entry, when moving from a bucket
-- overflow into a bucket header. */
-- new_bucket->data = data;
-- dst->n_buckets_used++;
-- free_entry (dst, cursor);
-- }
--
-- cursor = next;
-- }
-- /* Now move the bucket head. Be sure that if we fail due to
-- allocation failure that the src table is in a consistent
-- state. */
-- data = bucket->data;
-- bucket->next = NULL;
-- if (!safe)
-- {
-- new_bucket = safe_hasher (dst, data);
--
-- if (new_bucket->data)
-- {
-- /* Allocate or recycle an entry, when moving from a bucket
-- header into a bucket overflow. */
-- struct hash_entry *new_entry = allocate_entry (dst);
--
-- if (new_entry == NULL)
-- return false;
--
-- new_entry->data = data;
-- new_entry->next = new_bucket->next;
-- new_bucket->next = new_entry;
-- }
-- else
-- {
-- /* Move from one bucket header to another. */
-- new_bucket->data = data;
-- dst->n_buckets_used++;
-- }
-- bucket->data = NULL;
-- src->n_buckets_used--;
-- }
-- }
-- return true;
--}
--
--bool
--hash_rehash (Hash_table *table, size_t candidate)
--{
-- size_t new_size = compute_bucket_size (candidate, table->tuning);
--
-- if (!new_size)
-- return false;
-- if (new_size == table->n_buckets)
-- return true;
--
-- Hash_table storage;
-- Hash_table *new_table = &storage;
-- new_table->bucket = calloc (new_size, sizeof *new_table->bucket);
-- if (new_table->bucket == NULL)
-- return false;
-- new_table->n_buckets = new_size;
-- new_table->bucket_limit = new_table->bucket + new_size;
-- new_table->n_buckets_used = 0;
-- new_table->n_entries = 0;
-- new_table->tuning = table->tuning;
-- new_table->hasher = table->hasher;
-- new_table->comparator = table->comparator;
-- new_table->data_freer = table->data_freer;
--
-- /* In order for the transfer to successfully complete, we need
-- additional overflow entries when distinct buckets in the old
-- table collide into a common bucket in the new table. The worst
-- case possible is a hasher that gives a good spread with the old
-- size, but returns a constant with the new size; if we were to
-- guarantee table->n_buckets_used-1 free entries in advance, then
-- the transfer would be guaranteed to not allocate memory.
-- However, for large tables, a guarantee of no further allocation
-- introduces a lot of extra memory pressure, all for an unlikely
-- corner case (most rehashes reduce, rather than increase, the
-- number of overflow entries needed). So, we instead ensure that
-- the transfer process can be reversed if we hit a memory
-- allocation failure mid-transfer. */
--
-- /* Merely reuse the extra old space into the new table. */
--#if USE_OBSTACK
-- new_table->entry_stack = table->entry_stack;
--#endif
-- new_table->free_entry_list = table->free_entry_list;
--
-- if (transfer_entries (new_table, table, false))
-- {
-- /* Entries transferred successfully; tie up the loose ends. */
-- free (table->bucket);
-- table->bucket = new_table->bucket;
-- table->bucket_limit = new_table->bucket_limit;
-- table->n_buckets = new_table->n_buckets;
-- table->n_buckets_used = new_table->n_buckets_used;
-- table->free_entry_list = new_table->free_entry_list;
-- /* table->n_entries and table->entry_stack already hold their value. */
-- return true;
-- }
--
-- /* We've allocated new_table->bucket (and possibly some entries),
-- exhausted the free list, and moved some but not all entries into
-- new_table. We must undo the partial move before returning
-- failure. The only way to get into this situation is if new_table
-- uses fewer buckets than the old table, so we will reclaim some
-- free entries as overflows in the new table are put back into
-- distinct buckets in the old table.
--
-- There are some pathological cases where a single pass through the
-- table requires more intermediate overflow entries than using two
-- passes. Two passes give worse cache performance and takes
-- longer, but at this point, we're already out of memory, so slow
-- and safe is better than failure. */
-- int saved_errno = errno;
-- table->free_entry_list = new_table->free_entry_list;
-- if (! (transfer_entries (table, new_table, true)
-- && transfer_entries (table, new_table, false)))
-- abort ();
-- /* table->n_entries already holds its value. */
-- free (new_table->bucket);
-- errno = saved_errno;
-- return false;
--}
--
--int
--hash_insert_if_absent (Hash_table *table, void const *entry,
-- void const **matched_ent)
--{
--
-- /* The caller cannot insert a NULL entry, since hash_lookup returns NULL
-- to indicate "not found", and find_entry uses "bucket->data == NULL"
-- to indicate an empty bucket. */
-- if (! entry)
-- abort ();
--
-- /* If there's a matching entry already in the table, return that. */
-- struct hash_entry *bucket;
-- void *data = find_entry (table, entry, &bucket, false);
-- if (data != NULL)
-- {
-- if (matched_ent)
-- *matched_ent = data;
-- return 0;
-- }
--
-- /* If the growth threshold of the buckets in use has been reached, increase
-- the table size and rehash. There's no point in checking the number of
-- entries: if the hashing function is ill-conditioned, rehashing is not
-- likely to improve it. */
--
-- if (table->n_buckets_used
-- > table->tuning->growth_threshold * table->n_buckets)
-- {
-- /* Check more fully, before starting real work. If tuning arguments
-- became invalid, the second check will rely on proper defaults. */
-- check_tuning (table);
-- if (table->n_buckets_used
-- > table->tuning->growth_threshold * table->n_buckets)
-- {
-- const Hash_tuning *tuning = table->tuning;
-- float candidate =
-- (tuning->is_n_buckets
-- ? (table->n_buckets * tuning->growth_factor)
-- : (table->n_buckets * tuning->growth_factor
-- * tuning->growth_threshold));
--
-- if ((float) SIZE_MAX <= candidate)
-- {
-- errno = ENOMEM;
-- return -1;
-- }
--
-- /* If the rehash fails, arrange to return NULL. */
-- if (!hash_rehash (table, candidate))
-- return -1;
--
-- /* Update the bucket we are interested in. */
-- if (find_entry (table, entry, &bucket, false) != NULL)
-- abort ();
-- }
-- }
--
-- /* ENTRY is not matched, it should be inserted. */
--
-- if (bucket->data)
-- {
-- struct hash_entry *new_entry = allocate_entry (table);
--
-- if (new_entry == NULL)
-- return -1;
--
-- /* Add ENTRY in the overflow of the bucket. */
--
-- new_entry->data = (void *) entry;
-- new_entry->next = bucket->next;
-- bucket->next = new_entry;
-- table->n_entries++;
-- return 1;
-- }
--
-- /* Add ENTRY right in the bucket head. */
--
-- bucket->data = (void *) entry;
-- table->n_entries++;
-- table->n_buckets_used++;
--
-- return 1;
--}
--
--void *
--hash_insert (Hash_table *table, void const *entry)
--{
-- void const *matched_ent;
-- int err = hash_insert_if_absent (table, entry, &matched_ent);
-- return (err == -1
-- ? NULL
-- : (void *) (err == 0 ? matched_ent : entry));
--}
--
--void *
--hash_remove (Hash_table *table, const void *entry)
--{
-- struct hash_entry *bucket;
-- void *data = find_entry (table, entry, &bucket, true);
-- if (!data)
-- return NULL;
--
-- table->n_entries--;
-- if (!bucket->data)
-- {
-- table->n_buckets_used--;
--
-- /* If the shrink threshold of the buckets in use has been reached,
-- rehash into a smaller table. */
--
-- if (table->n_buckets_used
-- < table->tuning->shrink_threshold * table->n_buckets)
-- {
-- /* Check more fully, before starting real work. If tuning arguments
-- became invalid, the second check will rely on proper defaults. */
-- check_tuning (table);
-- if (table->n_buckets_used
-- < table->tuning->shrink_threshold * table->n_buckets)
-- {
-- const Hash_tuning *tuning = table->tuning;
-- size_t candidate =
-- (tuning->is_n_buckets
-- ? table->n_buckets * tuning->shrink_factor
-- : (table->n_buckets * tuning->shrink_factor
-- * tuning->growth_threshold));
--
-- if (!hash_rehash (table, candidate))
-- {
-- /* Failure to allocate memory in an attempt to
-- shrink the table is not fatal. But since memory
-- is low, we can at least be kind and free any
-- spare entries, rather than keeping them tied up
-- in the free entry list. */
--#if ! USE_OBSTACK
-- struct hash_entry *cursor = table->free_entry_list;
-- struct hash_entry *next;
-- while (cursor)
-- {
-- next = cursor->next;
-- free (cursor);
-- cursor = next;
-- }
-- table->free_entry_list = NULL;
--#endif
-- }
-- }
-- }
-- }
--
-- return data;
--}
--
--/* Testing. */
--
--#if TESTING
--
--void
--hash_print (const Hash_table *table)
--{
-- for (struct hash_entry *bucket = (struct hash_entry *) table->bucket;
-- bucket < table->bucket_limit;
-- bucket++)
-- {
-- if (bucket)
-- printf ("%lu:\n", (unsigned long int) (bucket - table->bucket));
--
-- for (struct hash_entry *cursor = bucket; cursor; cursor = cursor->next)
-- {
-- char const *s = cursor->data;
-- /* FIXME */
-- if (s)
-- printf (" %s\n", s);
-- }
-- }
--}
--
--#endif /* TESTING */
-diff --git a/misc/next-prime.c b/misc/next-prime.c
-deleted file mode 100644
-index 62fbf94a47caf174..0000000000000000
---- a/misc/next-prime.c
-+++ /dev/null
-@@ -1,64 +0,0 @@
--/* Finding the next prime >= a given small integer.
-- Copyright (C) 1995-2026 Free Software Foundation, Inc.
--
-- This file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as
-- published by the Free Software Foundation; either version 2.1 of the
-- License, or (at your option) any later version.
--
-- This file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with this program. If not, see <https://www.gnu.org/licenses/>. */
--
--#include <config.h>
--#ifdef _LIBC
--# include <stdbool.h>
--#endif
--
--/* Specification. */
--#include "next-prime.h"
--
--#include <stdint.h> /* for SIZE_MAX */
--
--/* Return true if CANDIDATE is a prime number or 1.
-- CANDIDATE should be an odd number >= 1. */
--static bool _GL_ATTRIBUTE_CONST
--is_prime (size_t candidate)
--{
-- size_t divisor = 3;
-- size_t square = divisor * divisor;
--
-- for (;;)
-- {
-- if (square > candidate)
-- return true;
-- if ((candidate % divisor) == 0)
-- return false;
-- /* Increment divisor by 2. */
-- divisor++;
-- square += 4 * divisor;
-- divisor++;
-- }
--}
--
--size_t _GL_ATTRIBUTE_CONST
--next_prime (size_t candidate)
--{
--#if !defined IN_LIBGETTEXTLIB
-- /* Skip small primes. */
-- if (candidate < 10)
-- candidate = 10;
--#endif
--
-- /* Make it definitely odd. */
-- candidate |= 1;
--
-- while (SIZE_MAX != candidate && !is_prime (candidate))
-- candidate += 2;
--
-- return candidate;
--}
-diff --git a/sysdeps/mach/hurd/opendir.c b/sysdeps/mach/hurd/opendir.c
-index e610236a583d6547..ee005cc37169f41f 100644
---- a/sysdeps/mach/hurd/opendir.c
-+++ b/sysdeps/mach/hurd/opendir.c
-@@ -66,15 +66,14 @@ _hurd_fd_opendir (struct hurd_fd *d)
-
-
- DIR *
--__opendirat (int dfd, const char *name, int extra_flags, int *pnew_fd)
-+__opendirat (int dfd, const char *name)
- {
- if (name[0] == '\0')
- /* POSIX.1-1990 says an empty name gets ENOENT;
- but `open' might like it fine. */
- return __hurd_fail (ENOENT), NULL;
-
-- int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC
-- | extra_flags;
-+ int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC;
- int fd;
- #if IS_IN (rtld)
- assert (dfd == AT_FDCWD);
-@@ -89,8 +88,6 @@ __opendirat (int dfd, const char *name, int extra_flags, int *pnew_fd)
- DIR *dirp = _hurd_fd_opendir (_hurd_fd_get (fd));
- if (dirp == NULL)
- __close (fd);
-- else if (pnew_fd != NULL)
-- *pnew_fd = fd;
-
- return dirp;
- }
-diff --git a/sysdeps/unix/sysv/linux/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
-index 9c41a8ff3e78ba57..ceaaefe2bbbd74a7 100644
---- a/sysdeps/unix/sysv/linux/opendir.c
-+++ b/sysdeps/unix/sysv/linux/opendir.c
-@@ -66,24 +66,12 @@ opendir_tail (int fd)
-
- #if IS_IN (libc)
- DIR *
--__opendirat (int dfd, const char *name, int extra_flags, int *pnew_fd)
-+__opendirat (int dfd, const char *name)
- {
- if (__glibc_unlikely (invalid_name (name)))
- return NULL;
-
-- int open_oflags = opendir_oflags | extra_flags;
-- int new_fd = __openat_nocancel (dfd, name, open_oflags);
-- if (new_fd < 0)
-- return NULL;
-- DIR *dirp = opendir_tail (new_fd);
-- if (dirp == NULL)
-- {
-- __close_nocancel_nostatus (new_fd);
-- return NULL;
-- }
-- else if (pnew_fd != NULL)
-- *pnew_fd = new_fd;
-- return dirp;
-+ return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags));
- }
- #endif
-
---
-2.53.0
-
diff --git a/glibc.spec b/glibc.spec
index 326c809..771c679 100644
--- a/glibc.spec
+++ b/glibc.spec
@@ -1,4 +1,4 @@
-%global glibcsrcdir glibc-2.44.9000-95-g53e5f02a6b
+%global glibcsrcdir glibc-2.44.9000-201-g04a3995650
%global glibcversion 2.44.9000
# Pre-release tarballs are pulled in from git using a command that is
# effectively:
@@ -152,7 +152,7 @@ Version: %{glibcversion}
# - It allows using the Release number without the %%dist tag in the dependency
# generator to make the generated requires interchangeable between Rawhide
# and ELN (.elnYY < .fcXX).
-%global baserelease 2
+%global baserelease 3
Release: %{baserelease}%{?dist}
# Licenses:
@@ -401,8 +401,6 @@ rpm.define("__debug_install_post bash " .. wrapper
Patch13: glibc-fedora-localedata-rh61908.patch
Patch17: glibc-cs-path.patch
Patch18: glibc-rh2426825.patch
-Patch19: glibc-rh2457183-1.patch
-Patch20: glibc-rh2457183-2.patch
##############################################################################
# Continued list of core "glibc" package information:
@@ -2479,6 +2477,119 @@ update_gconv_modules_cache ()
%endif
%changelog
+* Thu Sep 24 2026 Frédéric Bérat <fberat@redhat.com> - 2.44.9000-3
+- Auto-sync with upstream branch master,
+ commit 04a3995650ec181e98b768fef59f1ad228a87be5:
+- advisories: fix tag for CVE in SA-2026-0023 (Rolf Eike Beer)
+- Update CVSSv3.1 score for GLIBC-SA-2026-0022 (Carlos O'Donell)
+- Publish CVE-2026-95818 advisory (Carlos O'Donell)
+- Publish CVE-2026-86805 advisory (Carlos O'Donell)
+- elf: Fix ld.so SIGSEGV in trace mode with unresolved TLS symbols (BZ 34532) (Adhemerval Zanella)
+- nptl: Retry SIGSETXID delivery on EAGAIN in setxid (bug 21108) (Adhemerval Zanella)
+- math: Sync tgamma with CORE-MATH (Adhemerval Zanella)
+- math: Sync exp10m1f with CORE-MATH (Adhemerval Zanella)
+- math: Sync acosf with CORE-MATH (Adhemerval Zanella)
+- math: Sync lgamma with CORE-MATH (Adhemerval Zanella)
+- Fix d_t_fmt and date_fmt in en_SE [BZ #34635] (Mike FABIAN)
+- stdlib: Don't call clearenv from __libc_setenv_freemem (Mark Wielaard)
+- Publish CVE-2026-8674 advisory (Adhemerval Zanella)
+- malloc: Improve calloc [BZ #34582] (Wilco Dijkstra)
+- x86-64: Link tst-shstk-legacy-1{f,g} with -Wl,--no-as-needed (H.J. Lu)
+- Revert "linux: Add __libc_procmaps_iterate" (Adhemerval Zanella)
+- Revert "linux: Use __libc_procmaps_iterate on __readonly_area_fallback" (Adhemerval Zanella)
+- Revert "nptl: Use __libc_procmaps_iterate on pthread_getattr_np" (Adhemerval Zanella)
+- Revert "nptl: Fall back to AT_EXECFN on __pthread_main_stack without /proc" (Adhemerval Zanella)
+- Revert "nptl: Account for the stack guard gap on pthread_getattr_np (BZ 34637)" (Adhemerval Zanella)
+- Mark swapcontext returns_twice by default [BZ #23130] (Stian Halseth)
+- libio: validate the wide vtable via a bounds-checked index (Alessandro Schino)
+- resolv: Fix assertion failure on search list truncation [BZ 31026, CVE-2026-8674] (Adhemerval Zanella)
+- nptl: Account for the stack guard gap on pthread_getattr_np (BZ 34637) (Adhemerval Zanella)
+- nptl: Fall back to AT_EXECFN on __pthread_main_stack without /proc (Adhemerval Zanella)
+- nptl: Use __libc_procmaps_iterate on pthread_getattr_np (Adhemerval Zanella)
+- linux: Use __libc_procmaps_iterate on __readonly_area_fallback (Adhemerval Zanella)
+- linux: Add __libc_procmaps_iterate (Adhemerval Zanella)
+- linux: Add buffer argument and line truncation to __libc_procutils_read_file (Adhemerval Zanella)
+- Publish CVE-2026-80489 advisory (Carlos O'Donell)
+- Publish CVE-2026-77117 advisory (Carlos O'Donell)
+- Publish CVE-2026-19542 advisory (Adhemerval Zanella)
+- Publish CVE-2026-19499 advisory (Adhemerval Zanella)
+- math: Update tanh from CORE-MATH (revision dbd377a9) (Adhemerval Zanella)
+- io: Reject an empty path list on fts_open [BZ #25434] [BZ #34610] (Adhemerval Zanella)
+- malloc: Reject the top chunk in mem2chunk_check (Magnus Lindholm)
+- aarch64: Add MTE mode tunable (Yury Khrustalev)
+- elf: Do not load cache extensions from an old-format ld.so.cache [BZ #34600] (Shamil Abdulaev)
+- Publish CVE-2026-89092 advisory (Siddhesh Poyarekar)
+- x86: Set AVX10 active only if XMM/YMM/ZMM are supported (H.J. Lu)
+- time: handle ^ and # modifiers with %Z in wcsftime (bug 34611) (Andreas Schwab)
+- x86: Detect APX_NCI_NDD_NF (H.J. Lu)
+- math: let architectures signal underflow after narrowing (Matt Turner)
+- locale: fix memory leaks in write_locales and write_charmaps (Ruslan Valiyev)
+- math: Set errno to ERANGE for logb (+-0) [BZ #6793] (Shamil Abdulaev)
+- alpha: Use a comdat group for the shared divide-by-zero handler [BZ #20543] (Matt Turner)
+- nptl: Skip pretty-printer tests without python3 [BZ #34507] (Hemanth Kumar M D)
+- Record CVE-2026-18374 fix (Florian Weimer)
+- libio: Add test for fopen with an empty ", ccs=" value [BZ #34574] (Shamil Abdulaev)
+- libio: Fix CVE-2026-18374 heap buffer overflow in ccs= handling (Dongkyun Son)
+- getrusage: Add smoke test for getrusage (Ondrej Marek)
+- powerpc: Restore NULL check on _rtld_global_ro in INIT_ARCH [BZ #34503] (Michael Pfeifroth)
+- stdio-common: Skip the a and A conversions for the IBM extended format (Matt Turner)
+- stdio-common: Handle subnormal values in the printf format tests (Matt Turner)
+- stdio-common: Add printf format tests for the a and A conversions (Matt Turner)
+- stdio-common: Add printf format tests for the b and B conversions (Matt Turner)
+- stdio-common: Verify printf format tests with Python rather than AWK (Matt Turner)
+- hurd: Make __file_name_lookup_at apply upmask for O_TMPFILE (BZ 34493) (Samuel Thibault)
+- hurd: Make readlinkat check bogus length returned by translator (BZ 34504) (Samuel Thibault)
+- hurd: Fix setreuid/setregid setting saved ID to new effective ID (BZ 34505) (Samuel Thibault)
+- stdlib, wcsmbs: Add missing __nonnull to strto*/wcsto* [BZ #33053] (Shamil Abdulaev)
+- elf: Open the normalized $ORIGIN rpath in AT_SECURE programs (BZ 34360) (Adhemerval Zanella)
+- test: Fix tst-personality (Yury Khrustalev)
+- manual: adjust reference to man-pages following proc(5) split-up (Collin Funk)
+- Updates udp.h from Linux 6.19 to netinet/udp.h (Jiayuan Chen)
+- nptl: Test case for bug 34546 (Florian Weimer)
+- nptl: Use FAIL_UNSUPPORTED in init_tpp_test in tst-tpp.h (Florian Weimer)
+- nptl: Revert TPP updates on pthread_mutex_*lock failure (bug 34546) (Florian Weimer)
+- nptl: Fix test error reporting in CHECK_TPP_PRIORITY in tst-tpp.h (Florian Weimer)
+- nptl: Revert robust list head on pthread_mutex_timedlock failure (bug 34542) (Florian Weimer)
+- nptl: Treat negative times as timed out in PI futex locking (bug 34543) (Florian Weimer)
+- stdio-common: Keep trailing zeros where %#g rounds into a new decade (BZ 34578) (Matt Turner)
+- stdio-common: Iterate over the huge width for one printf function only (Matt Turner)
+- elf: Remove dead l_need_tls_init static-TLS init path (Adhemerval Zanella)
+- elf: Remove __chk_fail from dl-minimal.c (Adhemerval Zanella)
+- elf: Allow RPATH/RUNPATH for static-pie (BZ 33326) (Adhemerval Zanella)
+- Fix assert during static startup (BZ 33326) (Adhemerval Zanella)
+- Use _dl_writev on __libc_message_impl (Adhemerval Zanella)
+- nptl: Add __raise_direct (Adhemerval Zanella)
+- tst-backtrace5: split long line (Samuel Thibault)
+- tst-backtrace5: Fix on hurd (Samuel Thibault)
+- fcntl: drop nonnull attribute for openat, openat2's path argument [BZ #34313] (Sam James)
+- io: drop nonnull attribute for fchmodat, faccessat, fchownat's path argument [BZ #34313] (Sam James)
+- hurd: Move SINGLE_THREAD_P / RTLD_SINGLE_THREAD_P to single-thread.h (Samuel Thibault)
+- stdlib: merge some tests from gnulib (Collin Funk)
+- nptl: Propagate EDEADLK from FUTEX_LOCK_PI for errror-checking mutexes (Moritz Klammler)
+- advisories: fix typo in README (Rolf Eike Beer)
+- Use __attribute_optimization_barrier__ for the remaining noclone users (Matt Turner)
+- io: Do not alias fts and fts64 when inode types differ (Magnus Lindholm)
+- Use FAIL_UNSUPPORTED more (Sam James)
+- iconvdata: Test case for bug 34556, bug 34568 (Florian Weimer)
+- iconvdata: EUC_JISX0213 decoding lacks pending character reset (CVE-2026-80489) (Florian Weimer)
+- iconvdata: SHIFT_JISX0213 decoding lacks pending character reset (CVE-2026-77117) (Florian Weimer)
+- localedata: bn_BD, bn_IN: use full Gregorian month names for abmon (Azharul Haque)
+- manual: Fix some typos in the Low-Level Input/Output chapter (Collin Funk)
+- x86: Update AVX10 support (H.J. Lu)
+- Document CVE-2026-18374 and new CVSS tag (Siddhesh Poyarekar)
+- aarch64: Add memory clobber to pkey_write (Yury Khrustalev)
+- stdlib: Fix right-justification in strfmon (bug 34510, CVE-2026-19499) (Florian Weimer)
+- hurd: Set _TIME_BITS=32 for installed programs on i386/Hurd (Samuel Thibault)
+- Add check-symbol-version.awk (H.J. Lu)
+- LoongArch: Add standard search paths for 32-bit (mengqinggang)
+- LoongArch: Support pointer guard on LoongArch32 (mengqinggang)
+- hurd: Warn about setting _TIME_BITS=64 on i386 GNU Hurd (Samuel Thibault)
+- math: Update tanh from CORE-MATH (revision bb39e4fa) (Adhemerval Zanella)
+- tests: elf: Fix flaky thp tests (Yury Khrustalev)
+- Remove applied or redundant patches:
+ - glibc-rh2457183-1.patch
+ - glibc-rh2457183-2.patch
+
* Tue Sep 01 2026 Florian Weimer <fweimer@redhat.com> - 2.44.9000-2
- Fail the build if there are unexpected test failures (#2449608)
diff --git a/sources b/sources
index 0a5e87d..e31878f 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (glibc-2.44.9000-95-g53e5f02a6b.tar.xz) = b6fa43c7f372cd7efa813880b7619818e1fb5102a0bf7dc879f4d60be9d1e9e1abca4f42ecb709902329ca8b0ac0a8e076d89f7bb2ea771d6861eb0d541c582d
+SHA512 (glibc-2.44.9000-201-g04a3995650.tar.xz) = bb4d25275c6071be9a0163500bb78e27ea103fa8704660f51815d3d1e1b0dd98f80d6920c751b732d7ed0a7a0842668a75ccebb0e37703a17a3b8071957e1b16
next reply other threads:[~2026-09-24 11:48 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-24 11:48 [this message]
-- strict thread matches above, loose matches on Subject: below --
2026-07-21 17:07 [rpms/glibc] rawhide: Auto-sync with upstream branch master
2026-07-07 13:52
2026-06-23 17:32
2026-06-16 13:09
2026-06-10 15:23
2026-06-02 7:31
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179025053754.1.17577788459617037943.rpms-glibc-52b122b0e8f4@fedoraproject.org \
--to=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox