(root)/
glibc-2.38/
io/
ftw.c
       1  /* File tree walker functions.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #ifdef HAVE_CONFIG_H
      20  # include <config.h>
      21  #endif
      22  
      23  #if __GNUC__
      24  # define alloca __builtin_alloca
      25  #else
      26  # if HAVE_ALLOCA_H
      27  #  include <alloca.h>
      28  # else
      29  #  ifdef _AIX
      30   #  pragma alloca
      31  #  else
      32  char *alloca ();
      33  #  endif
      34  # endif
      35  #endif
      36  
      37  #ifdef _LIBC
      38  # include <dirent.h>
      39  # define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
      40  #else
      41  # if HAVE_DIRENT_H
      42  #  include <dirent.h>
      43  #  define NAMLEN(dirent) strlen ((dirent)->d_name)
      44  # else
      45  #  define dirent direct
      46  #  define NAMLEN(dirent) (dirent)->d_namlen
      47  #  if HAVE_SYS_NDIR_H
      48  #   include <sys/ndir.h>
      49  #  endif
      50  #  if HAVE_SYS_DIR_H
      51  #   include <sys/dir.h>
      52  #  endif
      53  #  if HAVE_NDIR_H
      54  #   include <ndir.h>
      55  #  endif
      56  # endif
      57  #endif
      58  
      59  #include <errno.h>
      60  #include <fcntl.h>
      61  #include <ftw.h>
      62  #include <limits.h>
      63  #include <search.h>
      64  #include <stdlib.h>
      65  #include <string.h>
      66  #include <unistd.h>
      67  #include <not-cancel.h>
      68  #include <sys/param.h>
      69  #ifdef _LIBC
      70  # include <include/sys/stat.h>
      71  #else
      72  # include <sys/stat.h>
      73  #endif
      74  
      75  #if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy
      76  char *stpcpy ();
      77  #endif
      78  
      79  #if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy
      80  /* Be CAREFUL that there are no side effects in N.  */
      81  # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
      82  #endif
      83  
      84  /* #define NDEBUG 1 */
      85  #include <assert.h>
      86  
      87  #ifndef _LIBC
      88  # undef __chdir
      89  # define __chdir chdir
      90  # undef __closedir
      91  # define __closedir closedir
      92  # undef __fchdir
      93  # define __fchdir fchdir
      94  # undef __getcwd
      95  # define __getcwd(P, N) xgetcwd ()
      96  extern char *xgetcwd (void);
      97  # undef __mempcpy
      98  # define __mempcpy mempcpy
      99  # undef __opendir
     100  # define __opendir opendir
     101  # undef __readdir64
     102  # define __readdir64 readdir
     103  # undef __stpcpy
     104  # define __stpcpy stpcpy
     105  # undef __tdestroy
     106  # define __tdestroy tdestroy
     107  # undef __tfind
     108  # define __tfind tfind
     109  # undef __tsearch
     110  # define __tsearch tsearch
     111  # undef dirent64
     112  # define dirent64 dirent
     113  # undef MAX
     114  # define MAX(a, b) ((a) > (b) ? (a) : (b))
     115  #endif
     116  
     117  /* Arrange to make lstat calls go through the wrapper function
     118     on systems with an lstat function that does not dereference symlinks
     119     that are specified with a trailing slash.  */
     120  #if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK
     121  int rpl_lstat (const char *, struct stat *);
     122  # undef lstat
     123  # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
     124  #endif
     125  
     126  #ifndef __set_errno
     127  # define __set_errno(Val) errno = (Val)
     128  #endif
     129  
     130  /* Support for the LFS API version.  */
     131  #ifndef FTW_NAME
     132  # define FTW_NAME ftw
     133  # define NFTW_NAME nftw
     134  # define NFTW_OLD_NAME __old_nftw
     135  # define NFTW_NEW_NAME __new_nftw
     136  # define INO_T ino_t
     137  # define STRUCT_STAT stat
     138  # ifdef _LIBC
     139  #  define LSTAT __lstat
     140  #  define STAT __stat
     141  #  define FSTATAT __fstatat
     142  # else
     143  #  define LSTAT lstat
     144  #  define XTAT stat
     145  #  define FSTATAT fstatat
     146  # endif
     147  # define FTW_FUNC_T __ftw_func_t
     148  # define NFTW_FUNC_T __nftw_func_t
     149  #endif
     150  
     151  /* We define PATH_MAX if the system does not provide a definition.
     152     This does not artificially limit any operation.  PATH_MAX is simply
     153     used as a guesstimate for the expected maximal path length.
     154     Buffers will be enlarged if necessary.  */
     155  #ifndef PATH_MAX
     156  # define PATH_MAX 1024
     157  #endif
     158  
     159  struct dir_data
     160  {
     161    DIR *stream;
     162    int streamfd;
     163    char *content;
     164  };
     165  
     166  struct known_object
     167  {
     168    dev_t dev;
     169    INO_T ino;
     170  };
     171  
     172  struct ftw_data
     173  {
     174    /* Array with pointers to open directory streams.  */
     175    struct dir_data **dirstreams;
     176    size_t actdir;
     177    size_t maxdir;
     178  
     179    /* Buffer containing name of currently processed object.  */
     180    char *dirbuf;
     181    size_t dirbufsize;
     182  
     183    /* Passed as fourth argument to `nftw' callback.  The `base' member
     184       tracks the content of the `dirbuf'.  */
     185    struct FTW ftw;
     186  
     187    /* Flags passed to `nftw' function.  0 for `ftw'.  */
     188    int flags;
     189  
     190    /* Conversion array for flag values.  It is the identity mapping for
     191       `nftw' calls, otherwise it maps the values to those known by
     192       `ftw'.  */
     193    const int *cvt_arr;
     194  
     195    /* Callback function.  We always use the `nftw' form.  */
     196    NFTW_FUNC_T func;
     197  
     198    /* Device of starting point.  Needed for FTW_MOUNT.  */
     199    dev_t dev;
     200  
     201    /* Data structure for keeping fingerprints of already processed
     202       object.  This is needed when not using FTW_PHYS.  */
     203    void *known_objects;
     204  };
     205  
     206  static bool
     207  ftw_allocate (struct ftw_data *data, size_t newsize)
     208  {
     209    void *newp = realloc (data->dirstreams, data->maxdir
     210  					  * sizeof (struct dir_data *)
     211  					  + newsize);
     212    if (newp == NULL)
     213      return false;
     214    data->dirstreams = newp;
     215    data->dirbufsize = newsize;
     216    data->dirbuf = (char *) data->dirstreams
     217  		 + data->maxdir * sizeof (struct dir_data *);
     218    return true;
     219  }
     220  
     221  /* Internally we use the FTW_* constants used for `nftw'.  When invoked
     222     as `ftw', map each flag to the subset of values used by `ftw'.  */
     223  static const int nftw_arr[] =
     224  {
     225    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
     226  };
     227  
     228  static const int ftw_arr[] =
     229  {
     230    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
     231  };
     232  
     233  
     234  /* Forward declarations of local functions.  */
     235  static int ftw_dir (struct ftw_data *data, struct STRUCT_STAT *st,
     236  		    struct dir_data *old_dir);
     237  
     238  
     239  static int
     240  object_compare (const void *p1, const void *p2)
     241  {
     242    /* We don't need a sophisticated and useful comparison.  We are only
     243       interested in equality.  However, we must be careful not to
     244       accidentally compare `holes' in the structure.  */
     245    const struct known_object *kp1 = p1, *kp2 = p2;
     246    int cmp1;
     247    cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
     248    if (cmp1 != 0)
     249      return cmp1;
     250    return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
     251  }
     252  
     253  
     254  static int
     255  add_object (struct ftw_data *data, struct STRUCT_STAT *st)
     256  {
     257    struct known_object *newp = malloc (sizeof (struct known_object));
     258    if (newp == NULL)
     259      return -1;
     260    newp->dev = st->st_dev;
     261    newp->ino = st->st_ino;
     262    return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
     263  }
     264  
     265  
     266  static inline int
     267  find_object (struct ftw_data *data, struct STRUCT_STAT *st)
     268  {
     269    struct known_object obj;
     270    obj.dev = st->st_dev;
     271    obj.ino = st->st_ino;
     272    return __tfind (&obj, &data->known_objects, object_compare) != NULL;
     273  }
     274  
     275  
     276  static inline int
     277  __attribute ((always_inline))
     278  open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
     279  {
     280    int result = 0;
     281  
     282    if (data->dirstreams[data->actdir] != NULL)
     283      {
     284        /* Oh, oh.  We must close this stream.  Get all remaining
     285  	 entries and store them as a list in the `content' member of
     286  	 the `struct dir_data' variable.  */
     287        size_t bufsize = 1024;
     288        char *buf = malloc (bufsize);
     289  
     290        if (buf == NULL)
     291  	result = -1;
     292        else
     293  	{
     294  	  DIR *st = data->dirstreams[data->actdir]->stream;
     295  	  struct dirent64 *d;
     296  	  size_t actsize = 0;
     297  
     298  	  while ((d = __readdir64 (st)) != NULL)
     299  	    {
     300  	      size_t this_len = NAMLEN (d);
     301  	      if (actsize + this_len + 2 >= bufsize)
     302  		{
     303  		  char *newp;
     304  		  bufsize += MAX (1024, 2 * this_len);
     305  		  newp = (char *) realloc (buf, bufsize);
     306  		  if (newp == NULL)
     307  		    {
     308  		      /* No more memory.  */
     309  		      int save_err = errno;
     310  		      free (buf);
     311  		      __set_errno (save_err);
     312  		      return -1;
     313  		    }
     314  		  buf = newp;
     315  		}
     316  
     317  	      *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
     318  		= '\0';
     319  	      actsize += this_len + 1;
     320  	    }
     321  
     322  	  /* Terminate the list with an additional NUL byte.  */
     323  	  buf[actsize++] = '\0';
     324  
     325  	  /* Shrink the buffer to what we actually need.  */
     326  	  void *content = realloc (buf, actsize);
     327  	  data->dirstreams[data->actdir]->content = content;
     328  	  if (content == NULL)
     329  	    {
     330  	      int save_err = errno;
     331  	      free (buf);
     332  	      __set_errno (save_err);
     333  	      result = -1;
     334  	    }
     335  	  else
     336  	    {
     337  	      __closedir (st);
     338  	      data->dirstreams[data->actdir]->stream = NULL;
     339  	      data->dirstreams[data->actdir]->streamfd = -1;
     340  	      data->dirstreams[data->actdir] = NULL;
     341  	    }
     342  	}
     343      }
     344  
     345    /* Open the new stream.  */
     346    if (result == 0)
     347      {
     348        assert (data->dirstreams[data->actdir] == NULL);
     349  
     350        if (dfdp != NULL && *dfdp != -1)
     351  	{
     352  	  int fd = __openat64_nocancel (*dfdp, data->dirbuf + data->ftw.base,
     353  					O_RDONLY | O_DIRECTORY | O_NDELAY);
     354  	  dirp->stream = NULL;
     355  	  if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL)
     356  	    __close_nocancel_nostatus (fd);
     357  	}
     358        else
     359  	{
     360  	  const char *name;
     361  
     362  	  if (data->flags & FTW_CHDIR)
     363  	    {
     364  	      name = data->dirbuf + data->ftw.base;
     365  	      if (name[0] == '\0')
     366  		name = ".";
     367  	    }
     368  	  else
     369  	    name = data->dirbuf;
     370  
     371  	  dirp->stream = __opendir (name);
     372  	}
     373  
     374        if (dirp->stream == NULL)
     375  	result = -1;
     376        else
     377  	{
     378  	  dirp->streamfd = __dirfd (dirp->stream);
     379  	  dirp->content = NULL;
     380  	  data->dirstreams[data->actdir] = dirp;
     381  
     382  	  if (++data->actdir == data->maxdir)
     383  	    data->actdir = 0;
     384  	}
     385      }
     386  
     387    return result;
     388  }
     389  
     390  
     391  static int
     392  process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
     393  	       size_t namlen, int d_type)
     394  {
     395    struct STRUCT_STAT st;
     396    int result = 0;
     397    int flag = 0;
     398    size_t new_buflen;
     399  
     400    if (name[0] == '.' && (name[1] == '\0'
     401  			 || (name[1] == '.' && name[2] == '\0')))
     402      /* Don't process the "." and ".." entries.  */
     403      return 0;
     404  
     405    new_buflen = data->ftw.base + namlen + 2;
     406    if (data->dirbufsize < new_buflen
     407        && !ftw_allocate (data, 2 * new_buflen))
     408      return -1;
     409  
     410    *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
     411  
     412    int statres;
     413    if (dir->streamfd != -1)
     414      statres = FSTATAT (dir->streamfd, name, &st,
     415  		       (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
     416    else
     417      {
     418        if ((data->flags & FTW_CHDIR) == 0)
     419  	name = data->dirbuf;
     420  
     421        statres = ((data->flags & FTW_PHYS)
     422  		 ? LSTAT (name, &st)
     423  		 : STAT (name, &st));
     424      }
     425  
     426    if (statres < 0)
     427      {
     428        if (errno != EACCES && errno != ENOENT)
     429  	result = -1;
     430        else if (data->flags & FTW_PHYS)
     431  	flag = FTW_NS;
     432        else
     433  	{
     434  	  /* Old code left ST undefined for dangling DT_LNK without
     435  	     FTW_PHYS set; a clarification at the POSIX level suggests
     436  	     it should contain information about the link (ala lstat).
     437  	     We do our best to fill in what data we can.  */
     438  	  if (dir->streamfd != -1)
     439  	    statres = FSTATAT (dir->streamfd, name, &st,
     440  			       AT_SYMLINK_NOFOLLOW);
     441  	  else
     442  	    statres = LSTAT (name, &st);
     443  	  if (statres == 0 && S_ISLNK (st.st_mode))
     444  	    flag = FTW_SLN;
     445  	  else
     446  	    flag = FTW_NS;
     447  	}
     448      }
     449    else
     450      {
     451        if (S_ISDIR (st.st_mode))
     452  	flag = FTW_D;
     453        else if (S_ISLNK (st.st_mode))
     454  	flag = FTW_SL;
     455        else
     456  	flag = FTW_F;
     457      }
     458  
     459    if (result == 0
     460        && (flag == FTW_NS
     461  	  || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
     462      {
     463        if (flag == FTW_D)
     464  	{
     465  	  if ((data->flags & FTW_PHYS)
     466  	      || (!find_object (data, &st)
     467  		  /* Remember the object.  */
     468  		  && (result = add_object (data, &st)) == 0))
     469  	    result = ftw_dir (data, &st, dir);
     470  	}
     471        else
     472  	result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
     473  				&data->ftw);
     474      }
     475  
     476    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
     477      result = 0;
     478  
     479    return result;
     480  }
     481  
     482  
     483  static int
     484  __attribute ((noinline))
     485  ftw_dir (struct ftw_data *data, struct STRUCT_STAT *st, struct dir_data *old_dir)
     486  {
     487    struct dir_data dir;
     488    struct dirent64 *d;
     489    int previous_base = data->ftw.base;
     490    int result;
     491    char *startp;
     492  
     493    /* Open the stream for this directory.  This might require that
     494       another stream has to be closed.  */
     495    result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
     496  			    data, &dir);
     497    if (result != 0)
     498      {
     499        if (errno == EACCES)
     500  	/* We cannot read the directory.  Signal this with a special flag.  */
     501  	result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
     502  
     503        return result;
     504      }
     505  
     506    /* First, report the directory (if not depth-first).  */
     507    if (!(data->flags & FTW_DEPTH))
     508      {
     509        result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
     510        if (result != 0)
     511  	{
     512  	  int save_err;
     513  fail:
     514  	  save_err = errno;
     515  	  __closedir (dir.stream);
     516  	  dir.streamfd = -1;
     517  	  __set_errno (save_err);
     518  
     519  	  if (data->actdir-- == 0)
     520  	    data->actdir = data->maxdir - 1;
     521  	  data->dirstreams[data->actdir] = NULL;
     522  	  return result;
     523  	}
     524      }
     525  
     526    /* If necessary, change to this directory.  */
     527    if (data->flags & FTW_CHDIR)
     528      {
     529        if (__fchdir (__dirfd (dir.stream)) < 0)
     530  	{
     531  	  result = -1;
     532  	  goto fail;
     533  	}
     534      }
     535  
     536    /* Next, update the `struct FTW' information.  */
     537    ++data->ftw.level;
     538    startp = strchr (data->dirbuf, '\0');
     539    /* There always must be a directory name.  */
     540    assert (startp != data->dirbuf);
     541    if (startp[-1] != '/')
     542      *startp++ = '/';
     543    data->ftw.base = startp - data->dirbuf;
     544  
     545    while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
     546      {
     547        int d_type = DT_UNKNOWN;
     548  #ifdef _DIRENT_HAVE_D_TYPE
     549        d_type = d->d_type;
     550  #endif
     551        result = process_entry (data, &dir, d->d_name, NAMLEN (d), d_type);
     552        if (result != 0)
     553  	break;
     554      }
     555  
     556    if (dir.stream != NULL)
     557      {
     558        /* The stream is still open.  I.e., we did not need more
     559  	 descriptors.  Simply close the stream now.  */
     560        int save_err = errno;
     561  
     562        assert (dir.content == NULL);
     563  
     564        __closedir (dir.stream);
     565        dir.streamfd = -1;
     566        __set_errno (save_err);
     567  
     568        if (data->actdir-- == 0)
     569  	data->actdir = data->maxdir - 1;
     570        data->dirstreams[data->actdir] = NULL;
     571      }
     572    else
     573      {
     574        int save_err;
     575        char *runp = dir.content;
     576  
     577        while (result == 0 && *runp != '\0')
     578  	{
     579  	  char *endp = strchr (runp, '\0');
     580  
     581  	  // XXX Should store the d_type values as well?!
     582  	  result = process_entry (data, &dir, runp, endp - runp, DT_UNKNOWN);
     583  
     584  	  runp = endp + 1;
     585  	}
     586  
     587        save_err = errno;
     588        free (dir.content);
     589        __set_errno (save_err);
     590      }
     591  
     592    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
     593      result = 0;
     594  
     595    /* Prepare the return, revert the `struct FTW' information.  */
     596    data->dirbuf[data->ftw.base - 1] = '\0';
     597    --data->ftw.level;
     598    data->ftw.base = previous_base;
     599  
     600    /* Finally, if we process depth-first report the directory.  */
     601    if (result == 0 && (data->flags & FTW_DEPTH))
     602      result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
     603  
     604    if (old_dir
     605        && (data->flags & FTW_CHDIR)
     606        && (result == 0
     607  	  || ((data->flags & FTW_ACTIONRETVAL)
     608  	      && (result != -1 && result != FTW_STOP))))
     609      {
     610        /* Change back to the parent directory.  */
     611        int done = 0;
     612        if (old_dir->stream != NULL)
     613  	if (__fchdir (__dirfd (old_dir->stream)) == 0)
     614  	  done = 1;
     615  
     616        if (!done)
     617  	{
     618  	  if (data->ftw.base == 1)
     619  	    {
     620  	      if (__chdir ("/") < 0)
     621  		result = -1;
     622  	    }
     623  	  else
     624  	    if (__chdir ("..") < 0)
     625  	      result = -1;
     626  	}
     627      }
     628  
     629    return result;
     630  }
     631  
     632  
     633  static int
     634  __attribute ((noinline))
     635  ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
     636  	     int flags)
     637  {
     638    struct ftw_data data = { .dirstreams = NULL };
     639    struct STRUCT_STAT st;
     640    int result = 0;
     641    int save_err;
     642    int cwdfd = -1;
     643    char *cwd = NULL;
     644    char *cp;
     645  
     646    /* First make sure the parameters are reasonable.  */
     647    if (dir[0] == '\0')
     648      {
     649        __set_errno (ENOENT);
     650        return -1;
     651      }
     652  
     653    data.maxdir = descriptors < 1 ? 1 : descriptors;
     654    data.actdir = 0;
     655    /* PATH_MAX is always defined when we get here.  */
     656    if (!ftw_allocate (&data, MAX (2 * strlen (dir), PATH_MAX)))
     657      return -1;
     658    memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
     659    cp = __stpcpy (data.dirbuf, dir);
     660    /* Strip trailing slashes.  */
     661    while (cp > data.dirbuf + 1 && cp[-1] == '/')
     662      --cp;
     663    *cp = '\0';
     664  
     665    data.ftw.level = 0;
     666  
     667    /* Find basename.  */
     668    while (cp > data.dirbuf && cp[-1] != '/')
     669      --cp;
     670    data.ftw.base = cp - data.dirbuf;
     671  
     672    data.flags = flags;
     673  
     674    /* This assignment might seem to be strange but it is what we want.
     675       The trick is that the first three arguments to the `ftw' and
     676       `nftw' callback functions are equal.  Therefore we can call in
     677       every case the callback using the format of the `nftw' version
     678       and get the correct result since the stack layout for a function
     679       call in C allows this.  */
     680    data.func = (NFTW_FUNC_T) func;
     681  
     682    /* Since we internally use the complete set of FTW_* values we need
     683       to reduce the value range before calling a `ftw' callback.  */
     684    data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
     685  
     686    /* No object known so far.  */
     687    data.known_objects = NULL;
     688  
     689    /* Now go to the directory containing the initial file/directory.  */
     690    if (flags & FTW_CHDIR)
     691      {
     692        /* We have to be able to go back to the current working
     693  	 directory.  The best way to do this is to use a file
     694  	 descriptor.  */
     695        cwdfd = __open (".", O_RDONLY | O_DIRECTORY);
     696        if (cwdfd == -1)
     697  	{
     698  	  /* Try getting the directory name.  This can be needed if
     699  	     the current directory is executable but not readable.  */
     700  	  if (errno == EACCES)
     701  	    /* GNU extension ahead.  */
     702  	    cwd =  __getcwd (NULL, 0);
     703  
     704  	  if (cwd == NULL)
     705  	    goto out_fail;
     706  	}
     707        else if (data.maxdir > 1)
     708  	/* Account for the file descriptor we use here.  */
     709  	--data.maxdir;
     710  
     711        if (data.ftw.base > 0)
     712  	{
     713  	  /* Change to the directory the file is in.  In data.dirbuf
     714  	     we have a writable copy of the file name.  Just NUL
     715  	     terminate it for now and change the directory.  */
     716  	  if (data.ftw.base == 1)
     717  	    /* I.e., the file is in the root directory.  */
     718  	    result = __chdir ("/");
     719  	  else
     720  	    {
     721  	      char ch = data.dirbuf[data.ftw.base - 1];
     722  	      data.dirbuf[data.ftw.base - 1] = '\0';
     723  	      result = __chdir (data.dirbuf);
     724  	      data.dirbuf[data.ftw.base - 1] = ch;
     725  	    }
     726  	}
     727      }
     728  
     729    /* Get stat info for start directory.  */
     730    if (result == 0)
     731      {
     732        const char *name;
     733  
     734        if (data.flags & FTW_CHDIR)
     735  	{
     736  	  name = data.dirbuf + data.ftw.base;
     737  	  if (name[0] == '\0')
     738  	    name = ".";
     739  	}
     740        else
     741  	name = data.dirbuf;
     742  
     743        if (((flags & FTW_PHYS)
     744  	   ? LSTAT (name, &st)
     745  	   : STAT (name, &st)) < 0)
     746  	{
     747  	  if (!(flags & FTW_PHYS)
     748  	      && errno == ENOENT
     749  	      && LSTAT (name, &st) == 0
     750  	      && S_ISLNK (st.st_mode))
     751  	    result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
     752  				   &data.ftw);
     753  	  else
     754  	    /* No need to call the callback since we cannot say anything
     755  	       about the object.  */
     756  	    result = -1;
     757  	}
     758        else
     759  	{
     760  	  if (S_ISDIR (st.st_mode))
     761  	    {
     762  	      /* Remember the device of the initial directory in case
     763  		 FTW_MOUNT is given.  */
     764  	      data.dev = st.st_dev;
     765  
     766  	      /* We know this directory now.  */
     767  	      if (!(flags & FTW_PHYS))
     768  		result = add_object (&data, &st);
     769  
     770  	      if (result == 0)
     771  		result = ftw_dir (&data, &st, NULL);
     772  	    }
     773  	  else
     774  	    {
     775  	      int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
     776  
     777  	      result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
     778  				     &data.ftw);
     779  	    }
     780  	}
     781  
     782        if ((flags & FTW_ACTIONRETVAL)
     783  	  && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
     784  	result = 0;
     785      }
     786  
     787    /* Return to the start directory (if necessary).  */
     788    if (cwdfd != -1)
     789      {
     790        int save_err = errno;
     791        __fchdir (cwdfd);
     792        __close_nocancel_nostatus (cwdfd);
     793        __set_errno (save_err);
     794      }
     795    else if (cwd != NULL)
     796      {
     797        int save_err = errno;
     798        __chdir (cwd);
     799        free (cwd);
     800        __set_errno (save_err);
     801      }
     802  
     803    /* Free all memory.  */
     804   out_fail:
     805    save_err = errno;
     806    __tdestroy (data.known_objects, free);
     807    free (data.dirstreams);
     808    __set_errno (save_err);
     809  
     810    return result;
     811  }
     812  
     813  
     814  
     815  /* Entry points.  */
     816  
     817  int
     818  FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors)
     819  {
     820    return ftw_startup (path, 0, func, descriptors, 0);
     821  }
     822  
     823  #ifndef NFTW_OLD_NAME
     824  int
     825  NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
     826  {
     827    return ftw_startup (path, 1, func, descriptors, flags);
     828  }
     829  #else
     830  
     831  # include <shlib-compat.h>
     832  
     833  int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int);
     834  
     835  int
     836  NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
     837  {
     838    if (flags
     839        & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
     840      {
     841        __set_errno (EINVAL);
     842        return -1;
     843      }
     844    return ftw_startup (path, 1, func, descriptors, flags);
     845  }
     846  versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
     847  
     848  # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
     849  
     850  /* Older nftw* version just ignored all unknown flags.  */
     851  
     852  int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int);
     853  
     854  int
     855  attribute_compat_text_section
     856  NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags)
     857  {
     858    flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
     859    return ftw_startup (path, 1, func, descriptors, flags);
     860  }
     861  
     862  compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
     863  # endif
     864  #endif /* NFTW_OLD_NAME  */