[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