[PATCH 2/3] io: Consolidate ftw implementation
Carlos O'Donell
carlos@redhat.com
Fri Apr 17 20:10:04 GMT 2026
On 4/17/26 1:31 PM, Adhemerval Zanella Netto wrote:
> This is a mechanical patch and I install if no one opposes it.
>
Likewise. Please push.
> On 17/04/26 10:24, Adhemerval Zanella wrote:
>> Remove wordsize-64 and arch-specific implementations, for ABIs when
>> off_t is the same as off64_t (__OFF_T_MATCHES_OFF64_T) the ftw64.c
>> will create the requires aliases.
>>
>> The ftw.c implementation is moved to ftw-common.c to simplify
>> the __OFF_T_MATCHES_OFF64_T usage.
>> ---
>> io/ftw-common.c | 969 ++++++++++++++++++
>> io/ftw.c | 958 +----------------
>> io/ftw64-time64.c | 2 +-
>> io/ftw64.c | 28 +-
>> sysdeps/unix/sysv/linux/mips/mips64/n64/ftw.c | 1 -
>> .../unix/sysv/linux/mips/mips64/n64/ftw64.c | 1 -
>> sysdeps/unix/sysv/linux/x86_64/x32/ftw.c | 1 -
>> sysdeps/unix/sysv/linux/x86_64/x32/ftw64.c | 1 -
>> sysdeps/wordsize-64/ftw.c | 16 -
>> sysdeps/wordsize-64/ftw64.c | 1 -
>> 10 files changed, 1003 insertions(+), 975 deletions(-)
>> create mode 100644 io/ftw-common.c
>> delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/ftw.c
>> delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/ftw64.c
>> delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/ftw.c
>> delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/ftw64.c
>> delete mode 100644 sysdeps/wordsize-64/ftw.c
>> delete mode 100644 sysdeps/wordsize-64/ftw64.c
>>
>> diff --git a/io/ftw-common.c b/io/ftw-common.c
>> new file mode 100644
>> index 0000000000..07df0ab25a
>> --- /dev/null
>> +++ b/io/ftw-common.c
>> @@ -0,0 +1,969 @@
>> +/* File tree walker functions.
>> + Copyright (C) 1996-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 <assert.h>
>> +#include <dirent.h>
>> +#include <fcntl.h>
>> +#include <ftw.h>
>> +#include <not-cancel.h>
>> +#include <search.h>
>> +#include <unistd.h>
>> +#include <sys/param.h>
>> +
>> +#define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
>> +
>> +/* Support for the LFS API version. */
>> +#ifndef FTW_NAME
>> +# define FTW_NAME ftw
>> +# define NFTW_NAME nftw
>> +# define NFTW_OLD_NAME __old_nftw
>> +# define NFTW_NEW_NAME __new_nftw
>> +# define INO_T ino_t
>> +# define STRUCT_STAT stat
>> +# define LSTAT __lstat
>> +# define STAT __stat
>> +# define FSTATAT __fstatat
>> +# define FTW_FUNC_T __ftw_func_t
>> +# define NFTW_FUNC_T __nftw_func_t
>> +#endif
>> +
>> +/* We define PATH_MAX if the system does not provide a definition.
>> + This does not artificially limit any operation. PATH_MAX is simply
>> + used as a guesstimate for the expected maximal path length.
>> + Buffers will be enlarged if necessary. */
>> +#ifndef PATH_MAX
>> +# define PATH_MAX 1024
>> +#endif
>> +
>> +struct dir_data
>> +{
>> + DIR *stream;
>> + int streamfd;
>> + char *content;
>> +};
>> +
>> +struct known_object
>> +{
>> + dev_t dev;
>> + INO_T ino;
>> +};
>> +
>> +/* Represents the execution state of a directory processing frame within the
>> + iterative file tree walk loop.
>> +
>> + Because the tree traversal is implemented iteratively using a custom stack
>> + rather than standard recursion, this state machine tracks the progress
>> + of each directory currently being visited. */
>> +enum ftw_frame_state
>> +{
>> + /* The initial state of a newly pushed directory frame. Attempts to open
>> + the directory stream. If successful, transitions to
>> + FTW_STATE_STREAM_LOOP. */
>> + FTW_STATE_INIT = 0,
>> +
>> + /* Iterating over the directory entries directly from the open DIR stream
>> + (using readdir). If a subdirectory is encountered and needs to be
>> + descended into, a new frame is added to the stack and execution pauses
>> + here. Transitions to FTW_STATE_CONTENT_LOOP if the stream was closed
>> + and cached to free up file descriptors, or FTW_STATE_CLEANUP when
>> + done. */
>> + FTW_STATE_STREAM_LOOP,
>> +
>> + /* Iterating over directory entries from a cached memory buffer. This state
>> + is used as a fallback when the original DIR stream had to be closed
>> + prematurely to prevent file descriptor exhaustion while descending into
>> + deeply nested child directories. Transitions to FTW_STATE_CLEANUP when
>> + all cached entries are processed. */
>> + FTW_STATE_CONTENT_LOOP,
>> +
>> + /* The final state, handles resource deallocation (closing remaining
>> + streams, freeing cached content buffers), triggering post-traversal
>> + callbacks (like FTW_DP for FTW_DEPTH walks), and restoring the
>> + previous working directory if FTW_CHDIR was used. */
>> + FTW_STATE_CLEANUP
>> +};
>> +
>> +/* Keep track of visited directories. */
>> +struct ftw_frame
>> +{
>> + struct dir_data dir;
>> + struct STRUCT_STAT st;
>> + int previous_base;
>> + char *runp;
>> + enum ftw_frame_state state;
>> +};
>> +
>> +struct ftw_stack
>> +{
>> + struct ftw_frame **stack;
>> + size_t num_blocks;
>> + ssize_t top;
>> +};
>> +
>> +typedef union
>> +{
>> + NFTW_FUNC_T nftw_func;
>> + FTW_FUNC_T ftw_func;
>> +} func_callback_t;
>> +
>> +struct ftw_data
>> +{
>> + /* Array with pointers to open directory streams. */
>> + struct dir_data **dirstreams;
>> + size_t actdir;
>> + size_t maxdir;
>> +
>> + /* Buffer containing name of currently processed object. */
>> + char *dirbuf;
>> + size_t dirbufsize;
>> +
>> + /* Passed as fourth argument to `nftw' callback. The `base' member
>> + tracks the content of the `dirbuf'. */
>> + struct FTW ftw;
>> +
>> + /* Flags passed to `nftw' function. 0 for `ftw'. */
>> + int flags;
>> +
>> + /* Conversion array for flag values. It is the identity mapping for
>> + `nftw' calls, otherwise it maps the values to those known by
>> + `ftw'. */
>> + const int *cvt_arr;
>> +
>> + /* Callback function. We always use the `nftw' form. */
>> + bool is_nftw;
>> + func_callback_t func;
>> +
>> + /* Device of starting point. Needed for FTW_MOUNT. */
>> + dev_t dev;
>> +
>> + /* Data structure for keeping fingerprints of already processed
>> + object. This is needed when not using FTW_PHYS. */
>> + void *known_objects;
>> +};
>> +#define CALL_FUNC(__ftw_data, __fp, __sb, __f, __ftw) \
>> + ((__ftw_data)->is_nftw ? (__ftw_data)->func.nftw_func (__fp, __sb, __f, __ftw) \
>> + : (__ftw_data)->func.ftw_func (__fp, __sb, __f))
>> +
>> +static bool
>> +ftw_allocate (struct ftw_data *data, size_t newsize)
>> +{
>> + void *newp = realloc (data->dirstreams, data->maxdir
>> + * sizeof (struct dir_data *)
>> + + newsize);
>> + if (newp == NULL)
>> + return false;
>> + data->dirstreams = newp;
>> + data->dirbufsize = newsize;
>> + data->dirbuf = (char *) data->dirstreams
>> + + data->maxdir * sizeof (struct dir_data *);
>> + return true;
>> +}
>> +
>> +/* Internally we use the FTW_* constants used for `nftw'. When invoked
>> + as `ftw', map each flag to the subset of values used by `ftw'. */
>> +static const int nftw_arr[] =
>> +{
>> + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
>> +};
>> +
>> +static const int ftw_arr[] =
>> +{
>> + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
>> +};
>> +
>> +
>> +static int
>> +object_compare (const void *p1, const void *p2)
>> +{
>> + /* We don't need a sophisticated and useful comparison. We are only
>> + interested in equality. However, we must be careful not to
>> + accidentally compare `holes' in the structure. */
>> + const struct known_object *kp1 = p1, *kp2 = p2;
>> + int cmp1;
>> + cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
>> + if (cmp1 != 0)
>> + return cmp1;
>> + return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
>> +}
>> +
>> +
>> +static int
>> +add_object (struct ftw_data *data, struct STRUCT_STAT *st)
>> +{
>> + struct known_object *newp = malloc (sizeof (struct known_object));
>> + if (newp == NULL)
>> + return -1;
>> + newp->dev = st->st_dev;
>> + newp->ino = st->st_ino;
>> + return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
>> +}
>> +
>> +
>> +static inline int
>> +find_object (struct ftw_data *data, struct STRUCT_STAT *st)
>> +{
>> + struct known_object obj;
>> + obj.dev = st->st_dev;
>> + obj.ino = st->st_ino;
>> + return __tfind (&obj, &data->known_objects, object_compare) != NULL;
>> +}
>> +
>> +
>> +static inline int
>> +open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
>> +{
>> + int result = 0;
>> +
>> + if (data->dirstreams[data->actdir] != NULL)
>> + {
>> + /* Oh, oh. We must close this stream. Get all remaining
>> + entries and store them as a list in the `content' member of
>> + the `struct dir_data' variable. */
>> + size_t bufsize = 1024;
>> + char *buf = malloc (bufsize);
>> +
>> + if (buf == NULL)
>> + result = -1;
>> + else
>> + {
>> + DIR *st = data->dirstreams[data->actdir]->stream;
>> + struct dirent64 *d;
>> + size_t actsize = 0;
>> +
>> + while ((d = __readdir64 (st)) != NULL)
>> + {
>> + size_t this_len = NAMLEN (d);
>> + if (actsize + this_len + 2 >= bufsize)
>> + {
>> + char *newp;
>> + bufsize += MAX (1024, 2 * this_len);
>> + newp = (char *) realloc (buf, bufsize);
>> + if (newp == NULL)
>> + {
>> + /* No more memory. */
>> + free (buf);
>> + return -1;
>> + }
>> + buf = newp;
>> + }
>> +
>> + *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
>> + = '\0';
>> + actsize += this_len + 1;
>> + }
>> +
>> + /* Terminate the list with an additional NUL byte. */
>> + buf[actsize++] = '\0';
>> +
>> + /* Shrink the buffer to what we actually need. */
>> + void *content = realloc (buf, actsize);
>> + data->dirstreams[data->actdir]->content = content;
>> + if (content == NULL)
>> + {
>> + free (buf);
>> + result = -1;
>> + }
>> + else
>> + {
>> + __closedir (st);
>> + data->dirstreams[data->actdir]->stream = NULL;
>> + data->dirstreams[data->actdir]->streamfd = -1;
>> + data->dirstreams[data->actdir] = NULL;
>> + }
>> + }
>> + }
>> +
>> + /* Open the new stream. */
>> + if (result == 0)
>> + {
>> + assert (data->dirstreams[data->actdir] == NULL);
>> +
>> + if (dfdp != NULL && *dfdp != -1)
>> + {
>> + int fd = __openat64_nocancel (*dfdp, data->dirbuf + data->ftw.base,
>> + O_RDONLY | O_DIRECTORY | O_NDELAY);
>> + dirp->stream = NULL;
>> + if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL)
>> + __close_nocancel_nostatus (fd);
>> + }
>> + else
>> + {
>> + const char *name;
>> +
>> + if (data->flags & FTW_CHDIR)
>> + {
>> + name = data->dirbuf + data->ftw.base;
>> + if (name[0] == '\0')
>> + name = ".";
>> + }
>> + else
>> + name = data->dirbuf;
>> +
>> + dirp->stream = __opendir (name);
>> + }
>> +
>> + if (dirp->stream == NULL)
>> + result = -1;
>> + else
>> + {
>> + dirp->streamfd = __dirfd (dirp->stream);
>> + dirp->content = NULL;
>> + data->dirstreams[data->actdir] = dirp;
>> +
>> + if (++data->actdir == data->maxdir)
>> + data->actdir = 0;
>> + }
>> + }
>> +
>> + return result;
>> +}
>> +
>> +
>> +static int
>> +process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
>> + size_t namlen, struct STRUCT_STAT *out_st, bool *descend)
>> +{
>> + struct STRUCT_STAT st;
>> + int result = 0;
>> + int flag = 0;
>> + size_t new_buflen;
>> +
>> + *descend = false;
>> +
>> + if (name[0] == '.' && (name[1] == '\0'
>> + || (name[1] == '.' && name[2] == '\0')))
>> + /* Don't process the "." and ".." entries. */
>> + return 0;
>> +
>> + new_buflen = data->ftw.base + namlen + 2;
>> + if (data->dirbufsize < new_buflen
>> + && !ftw_allocate (data, 2 * new_buflen))
>> + return -1;
>> +
>> + *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
>> +
>> + int statres;
>> + if (dir->streamfd != -1)
>> + statres = FSTATAT (dir->streamfd, name, &st,
>> + (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
>> + else
>> + {
>> + if ((data->flags & FTW_CHDIR) == 0)
>> + name = data->dirbuf;
>> +
>> + statres = ((data->flags & FTW_PHYS)
>> + ? LSTAT (name, &st)
>> + : STAT (name, &st));
>> + }
>> +
>> + if (statres < 0)
>> + {
>> + if (errno != EACCES && errno != ENOENT)
>> + result = -1;
>> + else if (data->flags & FTW_PHYS)
>> + flag = FTW_NS;
>> + else
>> + {
>> + /* Old code left ST undefined for dangling DT_LNK without
>> + FTW_PHYS set; a clarification at the POSIX level suggests
>> + it should contain information about the link (ala lstat).
>> + We do our best to fill in what data we can. */
>> + if (dir->streamfd != -1)
>> + statres = FSTATAT (dir->streamfd, name, &st,
>> + AT_SYMLINK_NOFOLLOW);
>> + else
>> + statres = LSTAT (name, &st);
>> + if (statres == 0 && S_ISLNK (st.st_mode))
>> + flag = FTW_SLN;
>> + else
>> + flag = FTW_NS;
>> + }
>> + }
>> + else
>> + {
>> + if (S_ISDIR (st.st_mode))
>> + flag = FTW_D;
>> + else if (S_ISLNK (st.st_mode))
>> + flag = FTW_SL;
>> + else
>> + flag = FTW_F;
>> + }
>> +
>> + if (result == 0
>> + && (flag == FTW_NS
>> + || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
>> + {
>> + if (flag == FTW_D)
>> + {
>> + if ((data->flags & FTW_PHYS)
>> + || (!find_object (data, &st)
>> + /* Remember the object. */
>> + && (result = add_object (data, &st)) == 0))
>> + {
>> + *out_st = st;
>> + *descend = true;
>> + }
>> + }
>> + else
>> + result = CALL_FUNC (data, data->dirbuf, &st, data->cvt_arr[flag],
>> + &data->ftw);
>> + }
>> +
>> + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
>> + result = 0;
>> +
>> + return result;
>> +}
>> +
>> +
>> +/* The ftw_frame are kept as chunked array to minimize the reallocation cost
>> + when the stack grows (since it contains STRUCT_STAT and extra metadata).
>> + New chunks of ftw_framw are allocated and only freed when ftw returns. */
>> +enum
>> +{
>> + FTW_STACK_CHUNK_BLOCKS = 1, /* Number of initial allocated chunks. */
>> + FTW_STACK_CHUNK_SIZE = 32 /* Number of stack frames allocated per
>> + chunk. */
>> +};
>> +
>> +static inline struct ftw_frame *
>> +frame_stack_get (struct ftw_stack *ftwst, int adj)
>> +{
>> + return &ftwst->stack[(ftwst->top + adj) / FTW_STACK_CHUNK_SIZE]
>> + [(ftwst->top + adj) % FTW_STACK_CHUNK_SIZE];
>> +}
>> +
>> +static inline void
>> +frame_stack_reset_top (struct ftw_stack *fwtst, const struct STRUCT_STAT *st)
>> +{
>> + struct ftw_frame *frame = frame_stack_get (fwtst, 0);
>> + frame->st = *st;
>> + frame->state = FTW_STATE_INIT;
>> + frame->dir.stream = NULL;
>> + frame->dir.content = NULL;
>> + frame->dir.streamfd = -1;
>> +}
>> +
>> +static bool
>> +frame_stack_init (struct ftw_stack *ftwst, const struct STRUCT_STAT *st)
>> +{
>> + ftwst->num_blocks = FTW_STACK_CHUNK_BLOCKS;
>> + ftwst->stack = malloc (FTW_STACK_CHUNK_BLOCKS * sizeof (*ftwst->stack));
>> + if (ftwst->stack == NULL)
>> + return false;
>> +
>> + ftwst->stack[0] = malloc (FTW_STACK_CHUNK_SIZE * sizeof (struct ftw_frame));
>> + if (ftwst->stack[0] == NULL)
>> + {
>> + free (ftwst->stack);
>> + return false;
>> + }
>> +
>> + ftwst->top = 0;
>> + frame_stack_reset_top (ftwst, st);
>> + return true;
>> +}
>> +
>> +static void
>> +frame_stack_free (struct ftw_stack *ftwst)
>> +{
>> + for (size_t i = 0; i < ftwst->num_blocks; i++)
>> + free (ftwst->stack[i]);
>> + free (ftwst->stack);
>> +}
>> +
>> +static bool
>> +frame_stack_add (struct ftw_stack *ftwst, const struct STRUCT_STAT *st)
>> +{
>> + if (ftwst->top + 1 >= ftwst->num_blocks * FTW_STACK_CHUNK_SIZE)
>> + {
>> + size_t new_blocks = ftwst->num_blocks + 1;
>> + struct ftw_frame **new_stack = realloc (
>> + ftwst->stack, new_blocks * sizeof (*ftwst->stack));
>> +
>> + if (new_stack == NULL)
>> + return false;
>> + ftwst->stack = new_stack;
>> + ftwst->stack[ftwst->num_blocks] = malloc (
>> + FTW_STACK_CHUNK_SIZE * sizeof (struct ftw_frame));
>> + if (ftwst->stack[ftwst->num_blocks] == NULL)
>> + return false;
>> + ftwst->num_blocks = new_blocks;
>> + }
>> + ftwst->top++;
>> + frame_stack_reset_top (ftwst, st);
>> + return true;
>> +}
>> +
>> +static void
>> +frame_closedir (struct ftw_data *data, struct ftw_frame *frame)
>> +{
>> + int save_err = errno;
>> + assert (frame->dir.content == NULL);
>> + __closedir (frame->dir.stream);
>> + frame->dir.streamfd = -1;
>> + __set_errno (save_err);
>> + if (data->actdir-- == 0)
>> + data->actdir = data->maxdir - 1;
>> + data->dirstreams[data->actdir] = NULL;
>> + frame->dir.stream = NULL;
>> +}
>> +
>> +static int
>> +ftw_dir (struct ftw_data *data, const struct STRUCT_STAT *st)
>> +{
>> + struct ftw_stack ftwst;
>> + if (!frame_stack_init (&ftwst, st))
>> + return -1;
>> +
>> + int result = 0;
>> +
>> + while (ftwst.top >= 0)
>> + {
>> + struct ftw_frame *frame = frame_stack_get (&ftwst, 0);
>> + struct dir_data *old_dir = (ftwst.top > 0)
>> + ? &frame_stack_get (&ftwst, -1)->dir : NULL;
>> +
>> + if (frame->state == FTW_STATE_INIT)
>> + {
>> + frame->previous_base = data->ftw.base;
>> + result = open_dir_stream (
>> + old_dir == NULL ? NULL : &old_dir->streamfd, data, &frame->dir);
>> + if (result != 0)
>> + {
>> + if (errno == EACCES)
>> + result = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_DNR,
>> + &data->ftw);
>> + ftwst.top--;
>> + /* Intercept FTW_SKIP_SUBTREE when popping frame */
>> + if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> + && result == FTW_SKIP_SUBTREE)
>> + result = 0;
>> + continue;
>> + }
>> +
>> + if (!(data->flags & FTW_DEPTH))
>> + {
>> + result = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_D,
>> + &data->ftw);
>> + if (result != 0)
>> + goto state0_fail;
>> + }
>> +
>> + if (data->flags & FTW_CHDIR)
>> + {
>> + if (__fchdir (__dirfd (frame->dir.stream)) < 0)
>> + {
>> + result = -1;
>> + state0_fail:
>> + frame_closedir (data, frame);
>> + ftwst.top--;
>> + /* Intercept FTW_SKIP_SUBTREE when popping frame. */
>> + if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> + && result == FTW_SKIP_SUBTREE)
>> + result = 0;
>> + continue;
>> + }
>> + }
>> +
>> + ++data->ftw.level;
>> + char *startp = strchr (data->dirbuf, '\0');
>> + assert (startp != data->dirbuf);
>> + if (startp[-1] != '/')
>> + *startp++ = '/';
>> + data->ftw.base = startp - data->dirbuf;
>> +
>> + frame->state = FTW_STATE_STREAM_LOOP;
>> + frame->runp = frame->dir.content;
>> + }
>> + else if (frame->state == FTW_STATE_STREAM_LOOP)
>> + {
>> + if (result != 0)
>> + {
>> + frame->state = FTW_STATE_CLEANUP;
>> + continue;
>> + }
>> +
>> + if (frame->dir.stream == NULL)
>> + {
>> + frame->state = FTW_STATE_CONTENT_LOOP;
>> + frame->runp = frame->dir.content;
>> + continue;
>> + }
>> +
>> + struct dirent64 *d = __readdir64 (frame->dir.stream);
>> + if (d != NULL)
>> + {
>> + struct STRUCT_STAT child_st;
>> + bool descend = false;
>> + result = process_entry (data, &frame->dir, d->d_name, NAMLEN (d),
>> + &child_st, &descend);
>> +
>> + if (result == 0 && descend)
>> + {
>> + if (!frame_stack_add (&ftwst, &child_st))
>> + {
>> + result = -1;
>> + frame->state = FTW_STATE_CLEANUP;
>> + }
>> + continue;
>> + }
>> + else if (result != 0)
>> + {
>> + frame->state = FTW_STATE_CLEANUP;
>> + continue;
>> + }
>> + }
>> + else
>> + frame->state = FTW_STATE_CLEANUP;
>> + }
>> + else if (frame->state == FTW_STATE_CONTENT_LOOP)
>> + {
>> + /* Check if we are safely positioned to process the starting path.
>> + The 'result' variable here comes from one of two places:
>> +
>> + 1. Initialization: defaults to 0 at the top of ftw_startup. If
>> + the FTW_CHDIR flag was NOT passed, it remains 0, meaning we
>> + are good to go.
>> +
>> + 2. Directory Change: If FTW_CHDIR WAS passed, 'result' holds the
>> + return value of the preceding __chdir() call (either moving
>> + to "/" or the parsed base directory).
>> +
>> + If 'result' is 0, the setup succeeded (or wasn't needed) and we
>> + can safely stat the initial object. Othewise, the chdir failed,
>> + so we skip processing and fall through to the cleanup phase. */
>> + if (result != 0)
>> + {
>> + frame->state = FTW_STATE_CLEANUP;
>> + continue;
>> + }
>> +
>> + if (frame->runp != NULL && *frame->runp != '\0')
>> + {
>> + char *endp = strchr (frame->runp, '\0');
>> + struct STRUCT_STAT child_st;
>> + bool descend = false;
>> +
>> + result = process_entry (data, &frame->dir, frame->runp,
>> + endp - frame->runp, &child_st,
>> + &descend);
>> + frame->runp = endp + 1;
>> +
>> + if (result == 0 && descend)
>> + {
>> + if (!frame_stack_add (&ftwst, &child_st))
>> + {
>> + result = -1;
>> + frame->state = FTW_STATE_CLEANUP;
>> + }
>> + continue;
>> + }
>> + else if (result != 0)
>> + {
>> + frame->state = FTW_STATE_CLEANUP;
>> + continue;
>> + }
>> + }
>> + else
>> + frame->state = FTW_STATE_CLEANUP;
>> + }
>> + else if (frame->state == FTW_STATE_CLEANUP)
>> + {
>> + if (frame->dir.stream != NULL)
>> + frame_closedir (data, frame);
>> + else if (frame->dir.content != NULL)
>> + {
>> + free (frame->dir.content);
>> + frame->dir.content = NULL;
>> + }
>> +
>> + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
>> + result = 0;
>> +
>> + data->dirbuf[data->ftw.base - 1] = '\0';
>> + --data->ftw.level;
>> + data->ftw.base = frame->previous_base;
>> +
>> + if (result == 0 && (data->flags & FTW_DEPTH))
>> + result
>> + = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_DP,
>> + &data->ftw);
>> +
>> + if (old_dir != NULL && (data->flags & FTW_CHDIR)
>> + && (result == 0
>> + || ((data->flags & FTW_ACTIONRETVAL)
>> + && (result != -1 && result != FTW_STOP))))
>> + {
>> + int done = 0;
>> + if (old_dir->stream != NULL)
>> + if (__fchdir (__dirfd (old_dir->stream)) == 0)
>> + done = 1;
>> +
>> + if (!done)
>> + {
>> + if (data->ftw.base == 1)
>> + {
>> + if (__chdir ("/") < 0)
>> + result = -1;
>> + }
>> + else if (__chdir ("..") < 0)
>> + result = -1;
>> + }
>> + }
>> +
>> + ftwst.top--;
>> + /* Intercept FTW_SKIP_SUBTREE when popping frame. */
>> + if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> + && result == FTW_SKIP_SUBTREE)
>> + result = 0;
>> + }
>> + }
>> +
>> + frame_stack_free (&ftwst);
>> +
>> + return result;
>> +}
>> +
>> +
>> +static int
>> +ftw_startup (const char *dir, bool is_nftw, func_callback_t func,
>> + int descriptors, int flags)
>> +{
>> + struct ftw_data data = { .dirstreams = NULL };
>> + struct STRUCT_STAT st;
>> + int result = 0;
>> + int cwdfd = -1;
>> + char *cwd = NULL;
>> + char *cp;
>> +
>> + /* First make sure the parameters are reasonable. */
>> + if (dir[0] == '\0')
>> + {
>> + __set_errno (ENOENT);
>> + return -1;
>> + }
>> +
>> + data.maxdir = descriptors < 1 ? 1 : descriptors;
>> + data.actdir = 0;
>> + /* PATH_MAX is always defined when we get here. */
>> + if (!ftw_allocate (&data, MAX (2 * strlen (dir), PATH_MAX)))
>> + return -1;
>> + memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
>> + cp = __stpcpy (data.dirbuf, dir);
>> + /* Strip trailing slashes. */
>> + while (cp > data.dirbuf + 1 && cp[-1] == '/')
>> + --cp;
>> + *cp = '\0';
>> +
>> + data.ftw.level = 0;
>> +
>> + /* Find basename. */
>> + while (cp > data.dirbuf && cp[-1] != '/')
>> + --cp;
>> + data.ftw.base = cp - data.dirbuf;
>> +
>> + data.flags = flags;
>> +
>> + data.is_nftw = is_nftw;
>> + data.func = func;
>> +
>> + /* Since we internally use the complete set of FTW_* values we need
>> + to reduce the value range before calling a `ftw' callback. */
>> + data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
>> +
>> + /* No object known so far. */
>> + data.known_objects = NULL;
>> +
>> + /* Now go to the directory containing the initial file/directory. */
>> + if (flags & FTW_CHDIR)
>> + {
>> + /* We have to be able to go back to the current working
>> + directory. The best way to do this is to use a file
>> + descriptor. */
>> + cwdfd = __open (".", O_RDONLY | O_DIRECTORY);
>> + if (cwdfd == -1)
>> + {
>> + /* Try getting the directory name. This can be needed if
>> + the current directory is executable but not readable. */
>> + if (errno == EACCES)
>> + /* GNU extension ahead. */
>> + cwd = __getcwd (NULL, 0);
>> +
>> + if (cwd == NULL)
>> + goto out_fail;
>> + }
>> + else if (data.maxdir > 1)
>> + /* Account for the file descriptor we use here. */
>> + --data.maxdir;
>> +
>> + if (data.ftw.base > 0)
>> + {
>> + /* Change to the directory the file is in. In data.dirbuf
>> + we have a writable copy of the file name. Just NUL
>> + terminate it for now and change the directory. */
>> + if (data.ftw.base == 1)
>> + /* I.e., the file is in the root directory. */
>> + result = __chdir ("/");
>> + else
>> + {
>> + char ch = data.dirbuf[data.ftw.base - 1];
>> + data.dirbuf[data.ftw.base - 1] = '\0';
>> + result = __chdir (data.dirbuf);
>> + data.dirbuf[data.ftw.base - 1] = ch;
>> + }
>> + }
>> + }
>> +
>> + /* Get stat info for start directory. */
>> + if (result == 0)
>> + {
>> + const char *name;
>> +
>> + if (data.flags & FTW_CHDIR)
>> + {
>> + name = data.dirbuf + data.ftw.base;
>> + if (name[0] == '\0')
>> + name = ".";
>> + }
>> + else
>> + name = data.dirbuf;
>> +
>> + if (((flags & FTW_PHYS)
>> + ? LSTAT (name, &st)
>> + : STAT (name, &st)) < 0)
>> + {
>> + if (!(flags & FTW_PHYS)
>> + && errno == ENOENT
>> + && LSTAT (name, &st) == 0
>> + && S_ISLNK (st.st_mode))
>> + result = CALL_FUNC (&data, data.dirbuf, &st, data.cvt_arr[FTW_SLN],
>> + &data.ftw);
>> + else
>> + /* No need to call the callback since we cannot say anything
>> + about the object. */
>> + result = -1;
>> + }
>> + else
>> + {
>> + if (S_ISDIR (st.st_mode))
>> + {
>> + /* Remember the device of the initial directory in case
>> + FTW_MOUNT is given. */
>> + data.dev = st.st_dev;
>> +
>> + /* We know this directory now. */
>> + if (!(flags & FTW_PHYS))
>> + result = add_object (&data, &st);
>> +
>> + if (result == 0)
>> + result = ftw_dir (&data, &st);
>> + }
>> + else
>> + {
>> + int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
>> +
>> + result = CALL_FUNC (&data, data.dirbuf, &st, data.cvt_arr[flag],
>> + &data.ftw);
>> + }
>> + }
>> +
>> + if ((flags & FTW_ACTIONRETVAL)
>> + && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
>> + result = 0;
>> + }
>> +
>> + /* Return to the start directory (if necessary). */
>> + if (cwdfd != -1)
>> + {
>> + int save_err = errno;
>> + __fchdir (cwdfd);
>> + __close_nocancel_nostatus (cwdfd);
>> + __set_errno (save_err);
>> + }
>> + else if (cwd != NULL)
>> + {
>> + int save_err = errno;
>> + __chdir (cwd);
>> + free (cwd);
>> + __set_errno (save_err);
>> + }
>> +
>> + /* Free all memory. */
>> + out_fail:
>> + __tdestroy (data.known_objects, free);
>> + free (data.dirstreams);
>> +
>> + return result;
>> +}
>> +
>> +
>> +
>> +/* Entry points. */
>> +
>> +int
>> +FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors)
>> +{
>> + return ftw_startup (path, false, (func_callback_t) { .ftw_func = func },
>> + descriptors, 0);
>> +}
>> +
>> +#ifndef NFTW_OLD_NAME
>> +int
>> +NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> +{
>> + return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> + descriptors, flags);
>> +}
>> +#else
>> +
>> +# include <shlib-compat.h>
>> +
>> +int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int);
>> +
>> +int
>> +NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> +{
>> + if (flags
>> + & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
>> + {
>> + __set_errno (EINVAL);
>> + return -1;
>> + }
>> + return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> + descriptors, flags);
>> +}
>> +
>> +# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> +
>> +/* Older nftw* version just ignored all unknown flags. */
>> +
>> +int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int);
>> +
>> +int
>> +attribute_compat_text_section
>> +NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> +{
>> + flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
>> + return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> + descriptors, flags);
>> +}
>> +
>> +# endif
>> +#endif /* NFTW_OLD_NAME */
>> diff --git a/io/ftw.c b/io/ftw.c
>> index 726c430eaf..ed0eeb3904 100644
>> --- a/io/ftw.c
>> +++ b/io/ftw.c
>> @@ -1,5 +1,5 @@
>> -/* File tree walker functions.
>> - Copyright (C) 1996-2026 Free Software Foundation, Inc.
>> +/* File tree traversal functions LFS version.
>> + 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
>> @@ -16,956 +16,14 @@
>> License along with the GNU C Library; if not, see
>> <https://www.gnu.org/licenses/>. */
>>
>> +#include <sys/types.h>
>>
>> -#include <assert.h>
>> -#include <dirent.h>
>> -#include <fcntl.h>
>> -#include <ftw.h>
>> -#include <not-cancel.h>
>> -#include <search.h>
>> -#include <unistd.h>
>> -#include <sys/param.h>
>> +#ifndef __OFF_T_MATCHES_OFF64_T
>> +# include "ftw-common.c"
>>
>> -#define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
>> -
>> -/* Support for the LFS API version. */
>> -#ifndef FTW_NAME
>> -# define FTW_NAME ftw
>> -# define NFTW_NAME nftw
>> -# define NFTW_OLD_NAME __old_nftw
>> -# define NFTW_NEW_NAME __new_nftw
>> -# define INO_T ino_t
>> -# define STRUCT_STAT stat
>> -# define LSTAT __lstat
>> -# define STAT __stat
>> -# define FSTATAT __fstatat
>> -# define FTW_FUNC_T __ftw_func_t
>> -# define NFTW_FUNC_T __nftw_func_t
>> +versioned_symbol (libc, __new_nftw, nftw, GLIBC_2_3_3);
>> +#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> +compat_symbol (libc, __old_nftw, nftw, GLIBC_2_1);
>> #endif
>>
>> -/* We define PATH_MAX if the system does not provide a definition.
>> - This does not artificially limit any operation. PATH_MAX is simply
>> - used as a guesstimate for the expected maximal path length.
>> - Buffers will be enlarged if necessary. */
>> -#ifndef PATH_MAX
>> -# define PATH_MAX 1024
>> #endif
>> -
>> -struct dir_data
>> -{
>> - DIR *stream;
>> - int streamfd;
>> - char *content;
>> -};
>> -
>> -struct known_object
>> -{
>> - dev_t dev;
>> - INO_T ino;
>> -};
>> -
>> -/* Represents the execution state of a directory processing frame within the
>> - iterative file tree walk loop.
>> -
>> - Because the tree traversal is implemented iteratively using a custom stack
>> - rather than standard recursion, this state machine tracks the progress
>> - of each directory currently being visited. */
>> -enum ftw_frame_state
>> -{
>> - /* The initial state of a newly pushed directory frame. Attempts to open
>> - the directory stream. If successful, transitions to
>> - FTW_STATE_STREAM_LOOP. */
>> - FTW_STATE_INIT = 0,
>> -
>> - /* Iterating over the directory entries directly from the open DIR stream
>> - (using readdir). If a subdirectory is encountered and needs to be
>> - descended into, a new frame is added to the stack and execution pauses
>> - here. Transitions to FTW_STATE_CONTENT_LOOP if the stream was closed
>> - and cached to free up file descriptors, or FTW_STATE_CLEANUP when
>> - done. */
>> - FTW_STATE_STREAM_LOOP,
>> -
>> - /* Iterating over directory entries from a cached memory buffer. This state
>> - is used as a fallback when the original DIR stream had to be closed
>> - prematurely to prevent file descriptor exhaustion while descending into
>> - deeply nested child directories. Transitions to FTW_STATE_CLEANUP when
>> - all cached entries are processed. */
>> - FTW_STATE_CONTENT_LOOP,
>> -
>> - /* The final state, handles resource deallocation (closing remaining
>> - streams, freeing cached content buffers), triggering post-traversal
>> - callbacks (like FTW_DP for FTW_DEPTH walks), and restoring the
>> - previous working directory if FTW_CHDIR was used. */
>> - FTW_STATE_CLEANUP
>> -};
>> -
>> -/* Keep track of visited directories. */
>> -struct ftw_frame
>> -{
>> - struct dir_data dir;
>> - struct STRUCT_STAT st;
>> - int previous_base;
>> - char *runp;
>> - enum ftw_frame_state state;
>> -};
>> -
>> -struct ftw_stack
>> -{
>> - struct ftw_frame **stack;
>> - size_t num_blocks;
>> - ssize_t top;
>> -};
>> -
>> -typedef union
>> -{
>> - NFTW_FUNC_T nftw_func;
>> - FTW_FUNC_T ftw_func;
>> -} func_callback_t;
>> -
>> -struct ftw_data
>> -{
>> - /* Array with pointers to open directory streams. */
>> - struct dir_data **dirstreams;
>> - size_t actdir;
>> - size_t maxdir;
>> -
>> - /* Buffer containing name of currently processed object. */
>> - char *dirbuf;
>> - size_t dirbufsize;
>> -
>> - /* Passed as fourth argument to `nftw' callback. The `base' member
>> - tracks the content of the `dirbuf'. */
>> - struct FTW ftw;
>> -
>> - /* Flags passed to `nftw' function. 0 for `ftw'. */
>> - int flags;
>> -
>> - /* Conversion array for flag values. It is the identity mapping for
>> - `nftw' calls, otherwise it maps the values to those known by
>> - `ftw'. */
>> - const int *cvt_arr;
>> -
>> - /* Callback function. We always use the `nftw' form. */
>> - bool is_nftw;
>> - func_callback_t func;
>> -
>> - /* Device of starting point. Needed for FTW_MOUNT. */
>> - dev_t dev;
>> -
>> - /* Data structure for keeping fingerprints of already processed
>> - object. This is needed when not using FTW_PHYS. */
>> - void *known_objects;
>> -};
>> -#define CALL_FUNC(__ftw_data, __fp, __sb, __f, __ftw) \
>> - ((__ftw_data)->is_nftw ? (__ftw_data)->func.nftw_func (__fp, __sb, __f, __ftw) \
>> - : (__ftw_data)->func.ftw_func (__fp, __sb, __f))
>> -
>> -static bool
>> -ftw_allocate (struct ftw_data *data, size_t newsize)
>> -{
>> - void *newp = realloc (data->dirstreams, data->maxdir
>> - * sizeof (struct dir_data *)
>> - + newsize);
>> - if (newp == NULL)
>> - return false;
>> - data->dirstreams = newp;
>> - data->dirbufsize = newsize;
>> - data->dirbuf = (char *) data->dirstreams
>> - + data->maxdir * sizeof (struct dir_data *);
>> - return true;
>> -}
>> -
>> -/* Internally we use the FTW_* constants used for `nftw'. When invoked
>> - as `ftw', map each flag to the subset of values used by `ftw'. */
>> -static const int nftw_arr[] =
>> -{
>> - FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
>> -};
>> -
>> -static const int ftw_arr[] =
>> -{
>> - FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
>> -};
>> -
>> -
>> -static int
>> -object_compare (const void *p1, const void *p2)
>> -{
>> - /* We don't need a sophisticated and useful comparison. We are only
>> - interested in equality. However, we must be careful not to
>> - accidentally compare `holes' in the structure. */
>> - const struct known_object *kp1 = p1, *kp2 = p2;
>> - int cmp1;
>> - cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
>> - if (cmp1 != 0)
>> - return cmp1;
>> - return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
>> -}
>> -
>> -
>> -static int
>> -add_object (struct ftw_data *data, struct STRUCT_STAT *st)
>> -{
>> - struct known_object *newp = malloc (sizeof (struct known_object));
>> - if (newp == NULL)
>> - return -1;
>> - newp->dev = st->st_dev;
>> - newp->ino = st->st_ino;
>> - return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
>> -}
>> -
>> -
>> -static inline int
>> -find_object (struct ftw_data *data, struct STRUCT_STAT *st)
>> -{
>> - struct known_object obj;
>> - obj.dev = st->st_dev;
>> - obj.ino = st->st_ino;
>> - return __tfind (&obj, &data->known_objects, object_compare) != NULL;
>> -}
>> -
>> -
>> -static inline int
>> -open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
>> -{
>> - int result = 0;
>> -
>> - if (data->dirstreams[data->actdir] != NULL)
>> - {
>> - /* Oh, oh. We must close this stream. Get all remaining
>> - entries and store them as a list in the `content' member of
>> - the `struct dir_data' variable. */
>> - size_t bufsize = 1024;
>> - char *buf = malloc (bufsize);
>> -
>> - if (buf == NULL)
>> - result = -1;
>> - else
>> - {
>> - DIR *st = data->dirstreams[data->actdir]->stream;
>> - struct dirent64 *d;
>> - size_t actsize = 0;
>> -
>> - while ((d = __readdir64 (st)) != NULL)
>> - {
>> - size_t this_len = NAMLEN (d);
>> - if (actsize + this_len + 2 >= bufsize)
>> - {
>> - char *newp;
>> - bufsize += MAX (1024, 2 * this_len);
>> - newp = (char *) realloc (buf, bufsize);
>> - if (newp == NULL)
>> - {
>> - /* No more memory. */
>> - free (buf);
>> - return -1;
>> - }
>> - buf = newp;
>> - }
>> -
>> - *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
>> - = '\0';
>> - actsize += this_len + 1;
>> - }
>> -
>> - /* Terminate the list with an additional NUL byte. */
>> - buf[actsize++] = '\0';
>> -
>> - /* Shrink the buffer to what we actually need. */
>> - void *content = realloc (buf, actsize);
>> - data->dirstreams[data->actdir]->content = content;
>> - if (content == NULL)
>> - {
>> - free (buf);
>> - result = -1;
>> - }
>> - else
>> - {
>> - __closedir (st);
>> - data->dirstreams[data->actdir]->stream = NULL;
>> - data->dirstreams[data->actdir]->streamfd = -1;
>> - data->dirstreams[data->actdir] = NULL;
>> - }
>> - }
>> - }
>> -
>> - /* Open the new stream. */
>> - if (result == 0)
>> - {
>> - assert (data->dirstreams[data->actdir] == NULL);
>> -
>> - if (dfdp != NULL && *dfdp != -1)
>> - {
>> - int fd = __openat64_nocancel (*dfdp, data->dirbuf + data->ftw.base,
>> - O_RDONLY | O_DIRECTORY | O_NDELAY);
>> - dirp->stream = NULL;
>> - if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL)
>> - __close_nocancel_nostatus (fd);
>> - }
>> - else
>> - {
>> - const char *name;
>> -
>> - if (data->flags & FTW_CHDIR)
>> - {
>> - name = data->dirbuf + data->ftw.base;
>> - if (name[0] == '\0')
>> - name = ".";
>> - }
>> - else
>> - name = data->dirbuf;
>> -
>> - dirp->stream = __opendir (name);
>> - }
>> -
>> - if (dirp->stream == NULL)
>> - result = -1;
>> - else
>> - {
>> - dirp->streamfd = __dirfd (dirp->stream);
>> - dirp->content = NULL;
>> - data->dirstreams[data->actdir] = dirp;
>> -
>> - if (++data->actdir == data->maxdir)
>> - data->actdir = 0;
>> - }
>> - }
>> -
>> - return result;
>> -}
>> -
>> -
>> -static int
>> -process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
>> - size_t namlen, struct STRUCT_STAT *out_st, bool *descend)
>> -{
>> - struct STRUCT_STAT st;
>> - int result = 0;
>> - int flag = 0;
>> - size_t new_buflen;
>> -
>> - *descend = false;
>> -
>> - if (name[0] == '.' && (name[1] == '\0'
>> - || (name[1] == '.' && name[2] == '\0')))
>> - /* Don't process the "." and ".." entries. */
>> - return 0;
>> -
>> - new_buflen = data->ftw.base + namlen + 2;
>> - if (data->dirbufsize < new_buflen
>> - && !ftw_allocate (data, 2 * new_buflen))
>> - return -1;
>> -
>> - *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
>> -
>> - int statres;
>> - if (dir->streamfd != -1)
>> - statres = FSTATAT (dir->streamfd, name, &st,
>> - (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
>> - else
>> - {
>> - if ((data->flags & FTW_CHDIR) == 0)
>> - name = data->dirbuf;
>> -
>> - statres = ((data->flags & FTW_PHYS)
>> - ? LSTAT (name, &st)
>> - : STAT (name, &st));
>> - }
>> -
>> - if (statres < 0)
>> - {
>> - if (errno != EACCES && errno != ENOENT)
>> - result = -1;
>> - else if (data->flags & FTW_PHYS)
>> - flag = FTW_NS;
>> - else
>> - {
>> - /* Old code left ST undefined for dangling DT_LNK without
>> - FTW_PHYS set; a clarification at the POSIX level suggests
>> - it should contain information about the link (ala lstat).
>> - We do our best to fill in what data we can. */
>> - if (dir->streamfd != -1)
>> - statres = FSTATAT (dir->streamfd, name, &st,
>> - AT_SYMLINK_NOFOLLOW);
>> - else
>> - statres = LSTAT (name, &st);
>> - if (statres == 0 && S_ISLNK (st.st_mode))
>> - flag = FTW_SLN;
>> - else
>> - flag = FTW_NS;
>> - }
>> - }
>> - else
>> - {
>> - if (S_ISDIR (st.st_mode))
>> - flag = FTW_D;
>> - else if (S_ISLNK (st.st_mode))
>> - flag = FTW_SL;
>> - else
>> - flag = FTW_F;
>> - }
>> -
>> - if (result == 0
>> - && (flag == FTW_NS
>> - || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
>> - {
>> - if (flag == FTW_D)
>> - {
>> - if ((data->flags & FTW_PHYS)
>> - || (!find_object (data, &st)
>> - /* Remember the object. */
>> - && (result = add_object (data, &st)) == 0))
>> - {
>> - *out_st = st;
>> - *descend = true;
>> - }
>> - }
>> - else
>> - result = CALL_FUNC (data, data->dirbuf, &st, data->cvt_arr[flag],
>> - &data->ftw);
>> - }
>> -
>> - if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
>> - result = 0;
>> -
>> - return result;
>> -}
>> -
>> -
>> -/* The ftw_frame are kept as chunked array to minimize the reallocation cost
>> - when the stack grows (since it contains STRUCT_STAT and extra metadata).
>> - New chunks of ftw_framw are allocated and only freed when ftw returns. */
>> -enum
>> -{
>> - FTW_STACK_CHUNK_BLOCKS = 1, /* Number of initial allocated chunks. */
>> - FTW_STACK_CHUNK_SIZE = 32 /* Number of stack frames allocated per
>> - chunk. */
>> -};
>> -
>> -static inline struct ftw_frame *
>> -frame_stack_get (struct ftw_stack *ftwst, int adj)
>> -{
>> - return &ftwst->stack[(ftwst->top + adj) / FTW_STACK_CHUNK_SIZE]
>> - [(ftwst->top + adj) % FTW_STACK_CHUNK_SIZE];
>> -}
>> -
>> -static inline void
>> -frame_stack_reset_top (struct ftw_stack *fwtst, const struct STRUCT_STAT *st)
>> -{
>> - struct ftw_frame *frame = frame_stack_get (fwtst, 0);
>> - frame->st = *st;
>> - frame->state = FTW_STATE_INIT;
>> - frame->dir.stream = NULL;
>> - frame->dir.content = NULL;
>> - frame->dir.streamfd = -1;
>> -}
>> -
>> -static bool
>> -frame_stack_init (struct ftw_stack *ftwst, const struct STRUCT_STAT *st)
>> -{
>> - ftwst->num_blocks = FTW_STACK_CHUNK_BLOCKS;
>> - ftwst->stack = malloc (FTW_STACK_CHUNK_BLOCKS * sizeof (*ftwst->stack));
>> - if (ftwst->stack == NULL)
>> - return false;
>> -
>> - ftwst->stack[0] = malloc (FTW_STACK_CHUNK_SIZE * sizeof (struct ftw_frame));
>> - if (ftwst->stack[0] == NULL)
>> - {
>> - free (ftwst->stack);
>> - return false;
>> - }
>> -
>> - ftwst->top = 0;
>> - frame_stack_reset_top (ftwst, st);
>> - return true;
>> -}
>> -
>> -static void
>> -frame_stack_free (struct ftw_stack *ftwst)
>> -{
>> - for (size_t i = 0; i < ftwst->num_blocks; i++)
>> - free (ftwst->stack[i]);
>> - free (ftwst->stack);
>> -}
>> -
>> -static bool
>> -frame_stack_add (struct ftw_stack *ftwst, const struct STRUCT_STAT *st)
>> -{
>> - if (ftwst->top + 1 >= ftwst->num_blocks * FTW_STACK_CHUNK_SIZE)
>> - {
>> - size_t new_blocks = ftwst->num_blocks + 1;
>> - struct ftw_frame **new_stack = realloc (
>> - ftwst->stack, new_blocks * sizeof (*ftwst->stack));
>> -
>> - if (new_stack == NULL)
>> - return false;
>> - ftwst->stack = new_stack;
>> - ftwst->stack[ftwst->num_blocks] = malloc (
>> - FTW_STACK_CHUNK_SIZE * sizeof (struct ftw_frame));
>> - if (ftwst->stack[ftwst->num_blocks] == NULL)
>> - return false;
>> - ftwst->num_blocks = new_blocks;
>> - }
>> - ftwst->top++;
>> - frame_stack_reset_top (ftwst, st);
>> - return true;
>> -}
>> -
>> -static void
>> -frame_closedir (struct ftw_data *data, struct ftw_frame *frame)
>> -{
>> - int save_err = errno;
>> - assert (frame->dir.content == NULL);
>> - __closedir (frame->dir.stream);
>> - frame->dir.streamfd = -1;
>> - __set_errno (save_err);
>> - if (data->actdir-- == 0)
>> - data->actdir = data->maxdir - 1;
>> - data->dirstreams[data->actdir] = NULL;
>> - frame->dir.stream = NULL;
>> -}
>> -
>> -static int
>> -ftw_dir (struct ftw_data *data, const struct STRUCT_STAT *st)
>> -{
>> - struct ftw_stack ftwst;
>> - if (!frame_stack_init (&ftwst, st))
>> - return -1;
>> -
>> - int result = 0;
>> -
>> - while (ftwst.top >= 0)
>> - {
>> - struct ftw_frame *frame = frame_stack_get (&ftwst, 0);
>> - struct dir_data *old_dir = (ftwst.top > 0)
>> - ? &frame_stack_get (&ftwst, -1)->dir : NULL;
>> -
>> - if (frame->state == FTW_STATE_INIT)
>> - {
>> - frame->previous_base = data->ftw.base;
>> - result = open_dir_stream (
>> - old_dir == NULL ? NULL : &old_dir->streamfd, data, &frame->dir);
>> - if (result != 0)
>> - {
>> - if (errno == EACCES)
>> - result = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_DNR,
>> - &data->ftw);
>> - ftwst.top--;
>> - /* Intercept FTW_SKIP_SUBTREE when popping frame */
>> - if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> - && result == FTW_SKIP_SUBTREE)
>> - result = 0;
>> - continue;
>> - }
>> -
>> - if (!(data->flags & FTW_DEPTH))
>> - {
>> - result = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_D,
>> - &data->ftw);
>> - if (result != 0)
>> - goto state0_fail;
>> - }
>> -
>> - if (data->flags & FTW_CHDIR)
>> - {
>> - if (__fchdir (__dirfd (frame->dir.stream)) < 0)
>> - {
>> - result = -1;
>> - state0_fail:
>> - frame_closedir (data, frame);
>> - ftwst.top--;
>> - /* Intercept FTW_SKIP_SUBTREE when popping frame. */
>> - if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> - && result == FTW_SKIP_SUBTREE)
>> - result = 0;
>> - continue;
>> - }
>> - }
>> -
>> - ++data->ftw.level;
>> - char *startp = strchr (data->dirbuf, '\0');
>> - assert (startp != data->dirbuf);
>> - if (startp[-1] != '/')
>> - *startp++ = '/';
>> - data->ftw.base = startp - data->dirbuf;
>> -
>> - frame->state = FTW_STATE_STREAM_LOOP;
>> - frame->runp = frame->dir.content;
>> - }
>> - else if (frame->state == FTW_STATE_STREAM_LOOP)
>> - {
>> - if (result != 0)
>> - {
>> - frame->state = FTW_STATE_CLEANUP;
>> - continue;
>> - }
>> -
>> - if (frame->dir.stream == NULL)
>> - {
>> - frame->state = FTW_STATE_CONTENT_LOOP;
>> - frame->runp = frame->dir.content;
>> - continue;
>> - }
>> -
>> - struct dirent64 *d = __readdir64 (frame->dir.stream);
>> - if (d != NULL)
>> - {
>> - struct STRUCT_STAT child_st;
>> - bool descend = false;
>> - result = process_entry (data, &frame->dir, d->d_name, NAMLEN (d),
>> - &child_st, &descend);
>> -
>> - if (result == 0 && descend)
>> - {
>> - if (!frame_stack_add (&ftwst, &child_st))
>> - {
>> - result = -1;
>> - frame->state = FTW_STATE_CLEANUP;
>> - }
>> - continue;
>> - }
>> - else if (result != 0)
>> - {
>> - frame->state = FTW_STATE_CLEANUP;
>> - continue;
>> - }
>> - }
>> - else
>> - frame->state = FTW_STATE_CLEANUP;
>> - }
>> - else if (frame->state == FTW_STATE_CONTENT_LOOP)
>> - {
>> - /* Check if we are safely positioned to process the starting path.
>> - The 'result' variable here comes from one of two places:
>> -
>> - 1. Initialization: defaults to 0 at the top of ftw_startup. If
>> - the FTW_CHDIR flag was NOT passed, it remains 0, meaning we
>> - are good to go.
>> -
>> - 2. Directory Change: If FTW_CHDIR WAS passed, 'result' holds the
>> - return value of the preceding __chdir() call (either moving
>> - to "/" or the parsed base directory).
>> -
>> - If 'result' is 0, the setup succeeded (or wasn't needed) and we
>> - can safely stat the initial object. Othewise, the chdir failed,
>> - so we skip processing and fall through to the cleanup phase. */
>> - if (result != 0)
>> - {
>> - frame->state = FTW_STATE_CLEANUP;
>> - continue;
>> - }
>> -
>> - if (frame->runp != NULL && *frame->runp != '\0')
>> - {
>> - char *endp = strchr (frame->runp, '\0');
>> - struct STRUCT_STAT child_st;
>> - bool descend = false;
>> -
>> - result = process_entry (data, &frame->dir, frame->runp,
>> - endp - frame->runp, &child_st,
>> - &descend);
>> - frame->runp = endp + 1;
>> -
>> - if (result == 0 && descend)
>> - {
>> - if (!frame_stack_add (&ftwst, &child_st))
>> - {
>> - result = -1;
>> - frame->state = FTW_STATE_CLEANUP;
>> - }
>> - continue;
>> - }
>> - else if (result != 0)
>> - {
>> - frame->state = FTW_STATE_CLEANUP;
>> - continue;
>> - }
>> - }
>> - else
>> - frame->state = FTW_STATE_CLEANUP;
>> - }
>> - else if (frame->state == FTW_STATE_CLEANUP)
>> - {
>> - if (frame->dir.stream != NULL)
>> - frame_closedir (data, frame);
>> - else if (frame->dir.content != NULL)
>> - {
>> - free (frame->dir.content);
>> - frame->dir.content = NULL;
>> - }
>> -
>> - if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
>> - result = 0;
>> -
>> - data->dirbuf[data->ftw.base - 1] = '\0';
>> - --data->ftw.level;
>> - data->ftw.base = frame->previous_base;
>> -
>> - if (result == 0 && (data->flags & FTW_DEPTH))
>> - result
>> - = CALL_FUNC (data, data->dirbuf, &frame->st, FTW_DP,
>> - &data->ftw);
>> -
>> - if (old_dir != NULL && (data->flags & FTW_CHDIR)
>> - && (result == 0
>> - || ((data->flags & FTW_ACTIONRETVAL)
>> - && (result != -1 && result != FTW_STOP))))
>> - {
>> - int done = 0;
>> - if (old_dir->stream != NULL)
>> - if (__fchdir (__dirfd (old_dir->stream)) == 0)
>> - done = 1;
>> -
>> - if (!done)
>> - {
>> - if (data->ftw.base == 1)
>> - {
>> - if (__chdir ("/") < 0)
>> - result = -1;
>> - }
>> - else if (__chdir ("..") < 0)
>> - result = -1;
>> - }
>> - }
>> -
>> - ftwst.top--;
>> - /* Intercept FTW_SKIP_SUBTREE when popping frame. */
>> - if (ftwst.top >= 0 && (data->flags & FTW_ACTIONRETVAL)
>> - && result == FTW_SKIP_SUBTREE)
>> - result = 0;
>> - }
>> - }
>> -
>> - frame_stack_free (&ftwst);
>> -
>> - return result;
>> -}
>> -
>> -
>> -static int
>> -ftw_startup (const char *dir, bool is_nftw, func_callback_t func,
>> - int descriptors, int flags)
>> -{
>> - struct ftw_data data = { .dirstreams = NULL };
>> - struct STRUCT_STAT st;
>> - int result = 0;
>> - int cwdfd = -1;
>> - char *cwd = NULL;
>> - char *cp;
>> -
>> - /* First make sure the parameters are reasonable. */
>> - if (dir[0] == '\0')
>> - {
>> - __set_errno (ENOENT);
>> - return -1;
>> - }
>> -
>> - data.maxdir = descriptors < 1 ? 1 : descriptors;
>> - data.actdir = 0;
>> - /* PATH_MAX is always defined when we get here. */
>> - if (!ftw_allocate (&data, MAX (2 * strlen (dir), PATH_MAX)))
>> - return -1;
>> - memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
>> - cp = __stpcpy (data.dirbuf, dir);
>> - /* Strip trailing slashes. */
>> - while (cp > data.dirbuf + 1 && cp[-1] == '/')
>> - --cp;
>> - *cp = '\0';
>> -
>> - data.ftw.level = 0;
>> -
>> - /* Find basename. */
>> - while (cp > data.dirbuf && cp[-1] != '/')
>> - --cp;
>> - data.ftw.base = cp - data.dirbuf;
>> -
>> - data.flags = flags;
>> -
>> - data.is_nftw = is_nftw;
>> - data.func = func;
>> -
>> - /* Since we internally use the complete set of FTW_* values we need
>> - to reduce the value range before calling a `ftw' callback. */
>> - data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
>> -
>> - /* No object known so far. */
>> - data.known_objects = NULL;
>> -
>> - /* Now go to the directory containing the initial file/directory. */
>> - if (flags & FTW_CHDIR)
>> - {
>> - /* We have to be able to go back to the current working
>> - directory. The best way to do this is to use a file
>> - descriptor. */
>> - cwdfd = __open (".", O_RDONLY | O_DIRECTORY);
>> - if (cwdfd == -1)
>> - {
>> - /* Try getting the directory name. This can be needed if
>> - the current directory is executable but not readable. */
>> - if (errno == EACCES)
>> - /* GNU extension ahead. */
>> - cwd = __getcwd (NULL, 0);
>> -
>> - if (cwd == NULL)
>> - goto out_fail;
>> - }
>> - else if (data.maxdir > 1)
>> - /* Account for the file descriptor we use here. */
>> - --data.maxdir;
>> -
>> - if (data.ftw.base > 0)
>> - {
>> - /* Change to the directory the file is in. In data.dirbuf
>> - we have a writable copy of the file name. Just NUL
>> - terminate it for now and change the directory. */
>> - if (data.ftw.base == 1)
>> - /* I.e., the file is in the root directory. */
>> - result = __chdir ("/");
>> - else
>> - {
>> - char ch = data.dirbuf[data.ftw.base - 1];
>> - data.dirbuf[data.ftw.base - 1] = '\0';
>> - result = __chdir (data.dirbuf);
>> - data.dirbuf[data.ftw.base - 1] = ch;
>> - }
>> - }
>> - }
>> -
>> - /* Get stat info for start directory. */
>> - if (result == 0)
>> - {
>> - const char *name;
>> -
>> - if (data.flags & FTW_CHDIR)
>> - {
>> - name = data.dirbuf + data.ftw.base;
>> - if (name[0] == '\0')
>> - name = ".";
>> - }
>> - else
>> - name = data.dirbuf;
>> -
>> - if (((flags & FTW_PHYS)
>> - ? LSTAT (name, &st)
>> - : STAT (name, &st)) < 0)
>> - {
>> - if (!(flags & FTW_PHYS)
>> - && errno == ENOENT
>> - && LSTAT (name, &st) == 0
>> - && S_ISLNK (st.st_mode))
>> - result = CALL_FUNC (&data, data.dirbuf, &st, data.cvt_arr[FTW_SLN],
>> - &data.ftw);
>> - else
>> - /* No need to call the callback since we cannot say anything
>> - about the object. */
>> - result = -1;
>> - }
>> - else
>> - {
>> - if (S_ISDIR (st.st_mode))
>> - {
>> - /* Remember the device of the initial directory in case
>> - FTW_MOUNT is given. */
>> - data.dev = st.st_dev;
>> -
>> - /* We know this directory now. */
>> - if (!(flags & FTW_PHYS))
>> - result = add_object (&data, &st);
>> -
>> - if (result == 0)
>> - result = ftw_dir (&data, &st);
>> - }
>> - else
>> - {
>> - int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
>> -
>> - result = CALL_FUNC (&data, data.dirbuf, &st, data.cvt_arr[flag],
>> - &data.ftw);
>> - }
>> - }
>> -
>> - if ((flags & FTW_ACTIONRETVAL)
>> - && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
>> - result = 0;
>> - }
>> -
>> - /* Return to the start directory (if necessary). */
>> - if (cwdfd != -1)
>> - {
>> - int save_err = errno;
>> - __fchdir (cwdfd);
>> - __close_nocancel_nostatus (cwdfd);
>> - __set_errno (save_err);
>> - }
>> - else if (cwd != NULL)
>> - {
>> - int save_err = errno;
>> - __chdir (cwd);
>> - free (cwd);
>> - __set_errno (save_err);
>> - }
>> -
>> - /* Free all memory. */
>> - out_fail:
>> - __tdestroy (data.known_objects, free);
>> - free (data.dirstreams);
>> -
>> - return result;
>> -}
>> -
>> -
>> -
>> -/* Entry points. */
>> -
>> -int
>> -FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors)
>> -{
>> - return ftw_startup (path, false, (func_callback_t) { .ftw_func = func },
>> - descriptors, 0);
>> -}
>> -
>> -#ifndef NFTW_OLD_NAME
>> -int
>> -NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> -{
>> - return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> - descriptors, flags);
>> -}
>> -#else
>> -
>> -# include <shlib-compat.h>
>> -
>> -int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int);
>> -
>> -int
>> -NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> -{
>> - if (flags
>> - & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
>> - {
>> - __set_errno (EINVAL);
>> - return -1;
>> - }
>> - return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> - descriptors, flags);
>> -}
>> -versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
>> -
>> -# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> -
>> -/* Older nftw* version just ignored all unknown flags. */
>> -
>> -int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int);
>> -
>> -int
>> -attribute_compat_text_section
>> -NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
>> -{
>> - flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
>> - return ftw_startup (path, true, (func_callback_t) { .nftw_func = func },
>> - descriptors, flags);
>> -}
>> -
>> -compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
>> -# endif
>> -#endif /* NFTW_OLD_NAME */
>> diff --git a/io/ftw64-time64.c b/io/ftw64-time64.c
>> index 2df871f802..88f58fd85c 100644
>> --- a/io/ftw64-time64.c
>> +++ b/io/ftw64-time64.c
>> @@ -29,5 +29,5 @@
>> # define FTW_FUNC_T __ftw64_time64_func_t
>> # define NFTW_FUNC_T __nftw64_time64_func_t
>>
>> -# include "ftw.c"
>> +# include "ftw-common.c"
>> #endif
>> diff --git a/io/ftw64.c b/io/ftw64.c
>> index 0d7cb30091..d3cd14c21a 100644
>> --- a/io/ftw64.c
>> +++ b/io/ftw64.c
>> @@ -16,8 +16,8 @@
>> License along with the GNU C Library; if not, see
>> <https://www.gnu.org/licenses/>. */
>>
>> -#define FTW_NAME ftw64
>> -#define NFTW_NAME nftw64
>> +#define FTW_NAME __ftw64
>> +#define NFTW_NAME __nftw64
>> #define NFTW_OLD_NAME __old_nftw64
>> #define NFTW_NEW_NAME __new_nftw64
>> #define INO_T ino64_t
>> @@ -28,4 +28,26 @@
>> #define FTW_FUNC_T __ftw64_func_t
>> #define NFTW_FUNC_T __nftw64_func_t
>>
>> -#include "ftw.c"
>> +#define ftw __rename_ftw
>> +#define nftw __rename_nftw
>> +
>> +#include <shlib-compat.h>
>> +#include "ftw-common.c"
>> +
>> +#undef ftw
>> +#undef nftw
>> +
>> +weak_alias (__ftw64, ftw64)
>> +versioned_symbol (libc, __new_nftw64, nftw64, GLIBC_2_3_3);
>> +
>> +#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> +compat_symbol (libc, __old_nftw64, nftw64, GLIBC_2_1);
>> +#endif
>> +
>> +#ifdef __OFF_T_MATCHES_OFF64_T
>> +weak_alias (__ftw64, ftw)
>> +versioned_symbol (libc, __new_nftw64, nftw, GLIBC_2_3_3);
>> +# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> +compat_symbol (libc, __old_nftw64, nftw, GLIBC_2_1);
>> +# endif
>> +#endif
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw.c
>> deleted file mode 100644
>> index 46389568b2..0000000000
>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw.c
>> +++ /dev/null
>> @@ -1 +0,0 @@
>> -#include <io/ftw.c>
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw64.c
>> deleted file mode 100644
>> index cb02172b3e..0000000000
>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/ftw64.c
>> +++ /dev/null
>> @@ -1 +0,0 @@
>> -#include <io/ftw64.c>
>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/ftw.c b/sysdeps/unix/sysv/linux/x86_64/x32/ftw.c
>> deleted file mode 100644
>> index a21dfe5690..0000000000
>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/ftw.c
>> +++ /dev/null
>> @@ -1 +0,0 @@
>> -#include <sysdeps/wordsize-64/ftw.c>
>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/ftw64.c b/sysdeps/unix/sysv/linux/x86_64/x32/ftw64.c
>> deleted file mode 100644
>> index 3c025b738a..0000000000
>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/ftw64.c
>> +++ /dev/null
>> @@ -1 +0,0 @@
>> -#include <sysdeps/wordsize-64/ftw64.c>
>> diff --git a/sysdeps/wordsize-64/ftw.c b/sysdeps/wordsize-64/ftw.c
>> deleted file mode 100644
>> index ca19903799..0000000000
>> --- a/sysdeps/wordsize-64/ftw.c
>> +++ /dev/null
>> @@ -1,16 +0,0 @@
>> -#define ftw64 __rename_ftw64
>> -#define nftw64 __rename_nftw64
>> -
>> -#include "../../io/ftw.c"
>> -
>> -#undef ftw64
>> -#undef nftw64
>> -
>> -weak_alias (ftw, ftw64)
>> -strong_alias (__new_nftw, __new_nftw64)
>> -versioned_symbol (libc, __new_nftw64, nftw64, GLIBC_2_3_3);
>> -
>> -#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
>> -strong_alias (__old_nftw, __old_nftw64)
>> -compat_symbol (libc, __old_nftw64, nftw64, GLIBC_2_1);
>> -#endif
>> diff --git a/sysdeps/wordsize-64/ftw64.c b/sysdeps/wordsize-64/ftw64.c
>> deleted file mode 100644
>> index 1cfcaadfd1..0000000000
>> --- a/sysdeps/wordsize-64/ftw64.c
>> +++ /dev/null
>> @@ -1 +0,0 @@
>> -/* Defined in ftw.c. */
>
--
Cheers,
Carlos.
More information about the Libc-alpha
mailing list