io: Use gnulib fts implementation (BZ 22944, BZ 20331)

This patch synchronizes the glibc fts implementation with the latest
version from gnulib (as of 2026-02-16).

The primary motivation is to address limitations in the legacy glibc
implementation, most notably BZ 22944, where fts fails with an
ENAMETOOLONG error when traversing very long paths or deeply nested
directory trees.  The gnulib implementation dynamically reallocates
path buffers and uses openat/fchdir optimizations, effectively
lifting the MAXPATHLEN limitation.

The gnulib implementation also added extra features, which are
used by different GNU projects (coreutils, diffutils):

 * FTS_TIGHT_CYCLE_CHECK: used to enable a strict, immediate
   cycle-detection algorithm during a file system traversal.  This is
   done internally using a hash table: every time the traversal enters
   a directory, it records the directory's device and inode (dev/ino)
   pair in the hash table, and before entering any directory, fts
   checks the hash table.

 * FTS_CWDFD: instead of actually changing the process's current
   working directory, it maintains a virtual current working directory
   using file descriptors.  The file descriptor is store at the
   fts_cwd_fd field and all subsequent file operations are performed
   relative to this file descriptor using *at functions.

 * FTS_DEFER_STAT: performance-oriented flag that instructs the file
   tree traversal engine to delay fetching file metadata.  When the
   flag is used, fts skips the immediate stat call.  Instead, it marks
   the entry with a special internal state (FTS_NSOK and
   FTS_STAT_REQUIRED).  The actual stat call is pushed down the line
   and executed by fts_read right before the application actually
   accesses the entry.

 * FTS_VERBATIM: fts_open accept and use the path strings exactly as
   they were provided in the arguments array without slash trimming.

 * FTS_MOUNT: it restrict the file tree walk to a single file system.

Hopefully,it would allow some GNU projects to use the glibc
implementation instead of pulling the gnulib one.

It requires some changes to keep compatibility, compared to gnulib:

 * The new required fields are added at the end of FTS structure, and
   the new FTS flags are adjusted to avoid change FTS_NAMEONLY/FTS_STOP
   (even though they are marked as private).

 * The FTSENT uses a flexible array (fts_name), so two adjustments are
   required: the two new members (fts_fts and fts_dirp) are place
   *before* the struct and the fts_statp is now always allocated and
   accounted (the gnulib implementation uses an alwyas allocated member).

Checked on x86_64-linux-gnu and i686-linux-gnu.
This commit is contained in:
Adhemerval Zanella
2026-04-03 07:50:18 -03:00
parent 4adae8550a
commit 99303f3871
29 changed files with 4740 additions and 938 deletions
+16
View File
@@ -36,8 +36,22 @@ 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
@@ -47,7 +61,9 @@ 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
+2 -1
View File
@@ -23,7 +23,8 @@ __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), namelist, select, cmp);
return __scandir_tail (__opendirat (dfd, dir, 0, NULL), namelist, select,
cmp);
}
weak_alias (__scandirat, scandirat)
#endif
+2 -1
View File
@@ -24,7 +24,8 @@ 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), namelist, select, cmp);
return __scandir64_tail (__opendirat (dfd, dir, 0, NULL), namelist, select,
cmp);
}
#if _DIRENT_MATCHES_DIRENT64
+57
View File
@@ -0,0 +1,57 @@
/* 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
+2 -1
View File
@@ -16,7 +16,8 @@ struct scandir_cancel_struct
/* Now define the internal interfaces. */
extern DIR *__opendir (const char *__name) attribute_hidden;
extern DIR *__opendirat (int dfd, const char *__name) attribute_hidden;
extern DIR *__opendirat (int dfd, const char *__nam, int extra_flags,
int *pnew_fd) attribute_hidden;
extern DIR *__fdopendir (int __fd) attribute_hidden;
extern int __closedir (DIR *__dirp) attribute_hidden;
extern struct dirent *__readdir (DIR *__dirp) attribute_hidden;
+77
View File
@@ -0,0 +1,77 @@
/* 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]))
+7
View File
@@ -17,6 +17,13 @@ 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
+331
View File
@@ -0,0 +1,331 @@
/* 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
+47
View File
@@ -0,0 +1,47 @@
/* 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 */
+65
View File
@@ -0,0 +1,65 @@
/* 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_ */
+2
View File
@@ -197,7 +197,9 @@ 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 \
+85
View File
@@ -0,0 +1,85 @@
/* 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;
}
+70
View File
@@ -0,0 +1,70 @@
/* 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
+44
View File
@@ -0,0 +1,44 @@
/* 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
+162
View File
@@ -0,0 +1,162 @@
/* 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);
}
+1986 -916
View File
File diff suppressed because it is too large Load Diff
+125 -1
View File
@@ -52,7 +52,29 @@
#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 */
@@ -73,11 +95,106 @@ 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 */
#define FTS_OPTIONMASK 0x00ff /* valid user option mask */
/* 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_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
@@ -92,6 +209,13 @@ 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
+13 -12
View File
@@ -19,18 +19,19 @@
#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 STAT __stat64_time64
# define LSTAT __lstat64_time64
# define FSTAT __fstat64_time64
# 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
# include "fts.c"
#endif
+3 -2
View File
@@ -25,8 +25,9 @@
#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"
+72
View File
@@ -0,0 +1,72 @@
/* 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;
}
+106
View File
@@ -0,0 +1,106 @@
/* 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
+100
View File
@@ -0,0 +1,100 @@
/* 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>
+234
View File
@@ -0,0 +1,234 @@
/* 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>
+2
View File
@@ -192,6 +192,8 @@ 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)
+2
View File
@@ -121,6 +121,7 @@ routines := \
getusershell \
getxattr \
gtty \
hash \
hsearch \
hsearch_r \
ifunc-impl-list \
@@ -157,6 +158,7 @@ routines := \
munlock \
munlockall \
munmap \
next-prime \
preadv \
preadv2 \
preadv64 \
+1045
View File
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
/* 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;
}
+5 -2
View File
@@ -66,14 +66,15 @@ _hurd_fd_opendir (struct hurd_fd *d)
DIR *
__opendirat (int dfd, const char *name)
__opendirat (int dfd, const char *name, int extra_flags, int *pnew_fd)
{
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;
int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC
| extra_flags;
int fd;
#if IS_IN (rtld)
assert (dfd == AT_FDCWD);
@@ -88,6 +89,8 @@ __opendirat (int dfd, const char *name)
DIR *dirp = _hurd_fd_opendir (_hurd_fd_get (fd));
if (dirp == NULL)
__close (fd);
else if (pnew_fd != NULL)
*pnew_fd = fd;
return dirp;
}
+14 -2
View File
@@ -66,12 +66,24 @@ opendir_tail (int fd)
#if IS_IN (libc)
DIR *
__opendirat (int dfd, const char *name)
__opendirat (int dfd, const char *name, int extra_flags, int *pnew_fd)
{
if (__glibc_unlikely (invalid_name (name)))
return NULL;
return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags));
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;
}
#endif