Back to index

glibc  2.9
ftw.c
Go to the documentation of this file.
00001 /* File tree walker functions.
00002    Copyright (C) 1996-2004, 2006, 2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 # include <config.h>
00023 #endif
00024 
00025 #if __GNUC__
00026 # define alloca __builtin_alloca
00027 #else
00028 # if HAVE_ALLOCA_H
00029 #  include <alloca.h>
00030 # else
00031 #  ifdef _AIX
00032  #  pragma alloca
00033 #  else
00034 char *alloca ();
00035 #  endif
00036 # endif
00037 #endif
00038 
00039 #ifdef _LIBC
00040 # include <dirent.h>
00041 # define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
00042 #else
00043 # if HAVE_DIRENT_H
00044 #  include <dirent.h>
00045 #  define NAMLEN(dirent) strlen ((dirent)->d_name)
00046 # else
00047 #  define dirent direct
00048 #  define NAMLEN(dirent) (dirent)->d_namlen
00049 #  if HAVE_SYS_NDIR_H
00050 #   include <sys/ndir.h>
00051 #  endif
00052 #  if HAVE_SYS_DIR_H
00053 #   include <sys/dir.h>
00054 #  endif
00055 #  if HAVE_NDIR_H
00056 #   include <ndir.h>
00057 #  endif
00058 # endif
00059 #endif
00060 
00061 #include <errno.h>
00062 #include <fcntl.h>
00063 #include <ftw.h>
00064 #include <limits.h>
00065 #include <search.h>
00066 #include <stdlib.h>
00067 #include <string.h>
00068 #include <unistd.h>
00069 #include <not-cancel.h>
00070 #if HAVE_SYS_PARAM_H || defined _LIBC
00071 # include <sys/param.h>
00072 #endif
00073 #ifdef _LIBC
00074 # include <include/sys/stat.h>
00075 #else
00076 # include <sys/stat.h>
00077 #endif
00078 
00079 #if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy
00080 char *stpcpy ();
00081 #endif
00082 
00083 #if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy
00084 /* Be CAREFUL that there are no side effects in N.  */
00085 # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
00086 #endif
00087 
00088 /* #define NDEBUG 1 */
00089 #include <assert.h>
00090 
00091 #ifndef _LIBC
00092 # undef __chdir
00093 # define __chdir chdir
00094 # undef __closedir
00095 # define __closedir closedir
00096 # undef __fchdir
00097 # define __fchdir fchdir
00098 # undef __getcwd
00099 # define __getcwd(P, N) xgetcwd ()
00100 extern char *xgetcwd (void);
00101 # undef __mempcpy
00102 # define __mempcpy mempcpy
00103 # undef __opendir
00104 # define __opendir opendir
00105 # undef __readdir64
00106 # define __readdir64 readdir
00107 # undef __stpcpy
00108 # define __stpcpy stpcpy
00109 # undef __tdestroy
00110 # define __tdestroy tdestroy
00111 # undef __tfind
00112 # define __tfind tfind
00113 # undef __tsearch
00114 # define __tsearch tsearch
00115 # undef internal_function
00116 # define internal_function /* empty */
00117 # undef dirent64
00118 # define dirent64 dirent
00119 # undef MAX
00120 # define MAX(a, b) ((a) > (b) ? (a) : (b))
00121 #endif
00122 
00123 /* Arrange to make lstat calls go through the wrapper function
00124    on systems with an lstat function that does not dereference symlinks
00125    that are specified with a trailing slash.  */
00126 #if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK
00127 int rpl_lstat (const char *, struct stat *);
00128 # undef lstat
00129 # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
00130 #endif
00131 
00132 #ifndef __set_errno
00133 # define __set_errno(Val) errno = (Val)
00134 #endif
00135 
00136 /* Support for the LFS API version.  */
00137 #ifndef FTW_NAME
00138 # define FTW_NAME ftw
00139 # define NFTW_NAME nftw
00140 # define NFTW_OLD_NAME __old_nftw
00141 # define NFTW_NEW_NAME __new_nftw
00142 # define INO_T ino_t
00143 # define STAT stat
00144 # ifdef _LIBC
00145 #  define LXSTAT __lxstat
00146 #  define XSTAT __xstat
00147 #  define FXSTATAT __fxstatat
00148 # else
00149 #  define LXSTAT(V,f,sb) lstat (f,sb)
00150 #  define XSTAT(V,f,sb) stat (f,sb)
00151 #  define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m)
00152 # endif
00153 # define FTW_FUNC_T __ftw_func_t
00154 # define NFTW_FUNC_T __nftw_func_t
00155 #endif
00156 
00157 /* We define PATH_MAX if the system does not provide a definition.
00158    This does not artificially limit any operation.  PATH_MAX is simply
00159    used as a guesstimate for the expected maximal path length.
00160    Buffers will be enlarged if necessary.  */
00161 #ifndef PATH_MAX
00162 # define PATH_MAX 1024
00163 #endif
00164 
00165 struct dir_data
00166 {
00167   DIR *stream;
00168   int streamfd;
00169   char *content;
00170 };
00171 
00172 struct known_object
00173 {
00174   dev_t dev;
00175   INO_T ino;
00176 };
00177 
00178 struct ftw_data
00179 {
00180   /* Array with pointers to open directory streams.  */
00181   struct dir_data **dirstreams;
00182   size_t actdir;
00183   size_t maxdir;
00184 
00185   /* Buffer containing name of currently processed object.  */
00186   char *dirbuf;
00187   size_t dirbufsize;
00188 
00189   /* Passed as fourth argument to `nftw' callback.  The `base' member
00190      tracks the content of the `dirbuf'.  */
00191   struct FTW ftw;
00192 
00193   /* Flags passed to `nftw' function.  0 for `ftw'.  */
00194   int flags;
00195 
00196   /* Conversion array for flag values.  It is the identity mapping for
00197      `nftw' calls, otherwise it maps the values to those known by
00198      `ftw'.  */
00199   const int *cvt_arr;
00200 
00201   /* Callback function.  We always use the `nftw' form.  */
00202   NFTW_FUNC_T func;
00203 
00204   /* Device of starting point.  Needed for FTW_MOUNT.  */
00205   dev_t dev;
00206 
00207   /* Data structure for keeping fingerprints of already processed
00208      object.  This is needed when not using FTW_PHYS.  */
00209   void *known_objects;
00210 };
00211 
00212 
00213 /* Internally we use the FTW_* constants used for `nftw'.  When invoked
00214    as `ftw', map each flag to the subset of values used by `ftw'.  */
00215 static const int nftw_arr[] =
00216 {
00217   FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
00218 };
00219 
00220 static const int ftw_arr[] =
00221 {
00222   FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
00223 };
00224 
00225 
00226 /* Forward declarations of local functions.  */
00227 static int ftw_dir (struct ftw_data *data, struct STAT *st,
00228                   struct dir_data *old_dir) internal_function;
00229 
00230 
00231 static int
00232 object_compare (const void *p1, const void *p2)
00233 {
00234   /* We don't need a sophisticated and useful comparison.  We are only
00235      interested in equality.  However, we must be careful not to
00236      accidentally compare `holes' in the structure.  */
00237   const struct known_object *kp1 = p1, *kp2 = p2;
00238   int cmp1;
00239   cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
00240   if (cmp1 != 0)
00241     return cmp1;
00242   return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
00243 }
00244 
00245 
00246 static int
00247 add_object (struct ftw_data *data, struct STAT *st)
00248 {
00249   struct known_object *newp = malloc (sizeof (struct known_object));
00250   if (newp == NULL)
00251     return -1;
00252   newp->dev = st->st_dev;
00253   newp->ino = st->st_ino;
00254   return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
00255 }
00256 
00257 
00258 static inline int
00259 find_object (struct ftw_data *data, struct STAT *st)
00260 {
00261   struct known_object obj;
00262   obj.dev = st->st_dev;
00263   obj.ino = st->st_ino;
00264   return __tfind (&obj, &data->known_objects, object_compare) != NULL;
00265 }
00266 
00267 
00268 static inline int
00269 __attribute ((always_inline))
00270 open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
00271 {
00272   int result = 0;
00273 
00274   if (data->dirstreams[data->actdir] != NULL)
00275     {
00276       /* Oh, oh.  We must close this stream.  Get all remaining
00277         entries and store them as a list in the `content' member of
00278         the `struct dir_data' variable.  */
00279       size_t bufsize = 1024;
00280       char *buf = malloc (bufsize);
00281 
00282       if (buf == NULL)
00283        result = -1;
00284       else
00285        {
00286          DIR *st = data->dirstreams[data->actdir]->stream;
00287          struct dirent64 *d;
00288          size_t actsize = 0;
00289 
00290          while ((d = __readdir64 (st)) != NULL)
00291            {
00292              size_t this_len = NAMLEN (d);
00293              if (actsize + this_len + 2 >= bufsize)
00294               {
00295                 char *newp;
00296                 bufsize += MAX (1024, 2 * this_len);
00297                 newp = (char *) realloc (buf, bufsize);
00298                 if (newp == NULL)
00299                   {
00300                     /* No more memory.  */
00301                     int save_err = errno;
00302                     free (buf);
00303                     __set_errno (save_err);
00304                     return -1;
00305                   }
00306                 buf = newp;
00307               }
00308 
00309              *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
00310               = '\0';
00311              actsize += this_len + 1;
00312            }
00313 
00314          /* Terminate the list with an additional NUL byte.  */
00315          buf[actsize++] = '\0';
00316 
00317          /* Shrink the buffer to what we actually need.  */
00318          data->dirstreams[data->actdir]->content = realloc (buf, actsize);
00319          if (data->dirstreams[data->actdir]->content == NULL)
00320            {
00321              int save_err = errno;
00322              free (buf);
00323              __set_errno (save_err);
00324              result = -1;
00325            }
00326          else
00327            {
00328              __closedir (st);
00329              data->dirstreams[data->actdir]->stream = NULL;
00330              data->dirstreams[data->actdir]->streamfd = -1;
00331              data->dirstreams[data->actdir] = NULL;
00332            }
00333        }
00334     }
00335 
00336   /* Open the new stream.  */
00337   if (result == 0)
00338     {
00339       assert (data->dirstreams[data->actdir] == NULL);
00340 
00341       if (dfdp != NULL && *dfdp != -1)
00342        {
00343          int fd = openat64_not_cancel_3 (*dfdp, data->dirbuf + data->ftw.base,
00344                                      O_RDONLY | O_DIRECTORY | O_NDELAY);
00345          dirp->stream = NULL;
00346          if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL)
00347            close_not_cancel_no_status (fd);
00348        }
00349       else
00350        {
00351          const char *name;
00352 
00353          if (data->flags & FTW_CHDIR)
00354            {
00355              name = data->dirbuf + data->ftw.base;
00356              if (name[0] == '\0')
00357               name = ".";
00358            }
00359          else
00360            name = data->dirbuf;
00361 
00362          dirp->stream = __opendir (name);
00363        }
00364 
00365       if (dirp->stream == NULL)
00366        result = -1;
00367       else
00368        {
00369          dirp->streamfd = dirfd (dirp->stream);
00370          dirp->content = NULL;
00371          data->dirstreams[data->actdir] = dirp;
00372 
00373          if (++data->actdir == data->maxdir)
00374            data->actdir = 0;
00375        }
00376     }
00377 
00378   return result;
00379 }
00380 
00381 
00382 static int
00383 internal_function
00384 process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
00385               size_t namlen, int d_type)
00386 {
00387   struct STAT st;
00388   int result = 0;
00389   int flag = 0;
00390   size_t new_buflen;
00391 
00392   if (name[0] == '.' && (name[1] == '\0'
00393                       || (name[1] == '.' && name[2] == '\0')))
00394     /* Don't process the "." and ".." entries.  */
00395     return 0;
00396 
00397   new_buflen = data->ftw.base + namlen + 2;
00398   if (data->dirbufsize < new_buflen)
00399     {
00400       /* Enlarge the buffer.  */
00401       char *newp;
00402 
00403       data->dirbufsize = 2 * new_buflen;
00404       newp = (char *) realloc (data->dirbuf, data->dirbufsize);
00405       if (newp == NULL)
00406        return -1;
00407       data->dirbuf = newp;
00408     }
00409 
00410   *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
00411 
00412   int statres;
00413   if (dir->streamfd != -1)
00414     statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
00415                      (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
00416   else
00417     {
00418       if ((data->flags & FTW_CHDIR) == 0)
00419        name = data->dirbuf;
00420 
00421       statres = ((data->flags & FTW_PHYS)
00422                ? LXSTAT (_STAT_VER, name, &st)
00423                : XSTAT (_STAT_VER, name, &st));
00424     }
00425 
00426   if (statres < 0)
00427     {
00428       if (errno != EACCES && errno != ENOENT)
00429        result = -1;
00430       else if (data->flags & FTW_PHYS)
00431        flag = FTW_NS;
00432       else if (d_type == DT_LNK)
00433        flag = FTW_SLN;
00434       else
00435        {
00436          if (dir->streamfd != -1)
00437            statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
00438                             AT_SYMLINK_NOFOLLOW);
00439          else
00440            statres = LXSTAT (_STAT_VER, name, &st);
00441          if (statres == 0 && S_ISLNK (st.st_mode))
00442            flag = FTW_SLN;
00443          else
00444            flag = FTW_NS;
00445        }
00446     }
00447   else
00448     {
00449       if (S_ISDIR (st.st_mode))
00450        flag = FTW_D;
00451       else if (S_ISLNK (st.st_mode))
00452        flag = FTW_SL;
00453       else
00454        flag = FTW_F;
00455     }
00456 
00457   if (result == 0
00458       && (flag == FTW_NS
00459          || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
00460     {
00461       if (flag == FTW_D)
00462        {
00463          if ((data->flags & FTW_PHYS)
00464              || (!find_object (data, &st)
00465                 /* Remember the object.  */
00466                 && (result = add_object (data, &st)) == 0))
00467            result = ftw_dir (data, &st, dir);
00468        }
00469       else
00470        result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
00471                             &data->ftw);
00472     }
00473 
00474   if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
00475     result = 0;
00476 
00477   return result;
00478 }
00479 
00480 
00481 static int
00482 __attribute ((noinline))
00483 internal_function
00484 ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
00485 {
00486   struct dir_data dir;
00487   struct dirent64 *d;
00488   int previous_base = data->ftw.base;
00489   int result;
00490   char *startp;
00491 
00492   /* Open the stream for this directory.  This might require that
00493      another stream has to be closed.  */
00494   result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
00495                          data, &dir);
00496   if (result != 0)
00497     {
00498       if (errno == EACCES)
00499        /* We cannot read the directory.  Signal this with a special flag.  */
00500        result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
00501 
00502       return result;
00503     }
00504 
00505   /* First, report the directory (if not depth-first).  */
00506   if (!(data->flags & FTW_DEPTH))
00507     {
00508       result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
00509       if (result != 0)
00510        {
00511          int save_err;
00512 fail:
00513          save_err = errno;
00514          __closedir (dir.stream);
00515          dir.streamfd = -1;
00516          __set_errno (save_err);
00517 
00518          if (data->actdir-- == 0)
00519            data->actdir = data->maxdir - 1;
00520          data->dirstreams[data->actdir] = NULL;
00521          return result;
00522        }
00523     }
00524 
00525   /* If necessary, change to this directory.  */
00526   if (data->flags & FTW_CHDIR)
00527     {
00528       if (__fchdir (dirfd (dir.stream)) < 0)
00529        {
00530          result = -1;
00531          goto fail;
00532        }
00533     }
00534 
00535   /* Next, update the `struct FTW' information.  */
00536   ++data->ftw.level;
00537   startp = __rawmemchr (data->dirbuf, '\0');
00538   /* There always must be a directory name.  */
00539   assert (startp != data->dirbuf);
00540   if (startp[-1] != '/')
00541     *startp++ = '/';
00542   data->ftw.base = startp - data->dirbuf;
00543 
00544   while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
00545     {
00546       result = process_entry (data, &dir, d->d_name, NAMLEN (d), d->d_type);
00547       if (result != 0)
00548        break;
00549     }
00550 
00551   if (dir.stream != NULL)
00552     {
00553       /* The stream is still open.  I.e., we did not need more
00554         descriptors.  Simply close the stream now.  */
00555       int save_err = errno;
00556 
00557       assert (dir.content == NULL);
00558 
00559       __closedir (dir.stream);
00560       dir.streamfd = -1;
00561       __set_errno (save_err);
00562 
00563       if (data->actdir-- == 0)
00564        data->actdir = data->maxdir - 1;
00565       data->dirstreams[data->actdir] = NULL;
00566     }
00567   else
00568     {
00569       int save_err;
00570       char *runp = dir.content;
00571 
00572       while (result == 0 && *runp != '\0')
00573        {
00574          char *endp = strchr (runp, '\0');
00575 
00576          // XXX Should store the d_type values as well?!
00577          result = process_entry (data, &dir, runp, endp - runp, DT_UNKNOWN);
00578 
00579          runp = endp + 1;
00580        }
00581 
00582       save_err = errno;
00583       free (dir.content);
00584       __set_errno (save_err);
00585     }
00586 
00587   if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
00588     result = 0;
00589 
00590   /* Prepare the return, revert the `struct FTW' information.  */
00591   data->dirbuf[data->ftw.base - 1] = '\0';
00592   --data->ftw.level;
00593   data->ftw.base = previous_base;
00594 
00595   /* Finally, if we process depth-first report the directory.  */
00596   if (result == 0 && (data->flags & FTW_DEPTH))
00597     result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
00598 
00599   if (old_dir
00600       && (data->flags & FTW_CHDIR)
00601       && (result == 0
00602          || ((data->flags & FTW_ACTIONRETVAL)
00603              && (result != -1 && result != FTW_STOP))))
00604     {
00605       /* Change back to the parent directory.  */
00606       int done = 0;
00607       if (old_dir->stream != NULL)
00608        if (__fchdir (dirfd (old_dir->stream)) == 0)
00609          done = 1;
00610 
00611       if (!done)
00612        {
00613          if (data->ftw.base == 1)
00614            {
00615              if (__chdir ("/") < 0)
00616               result = -1;
00617            }
00618          else
00619            if (__chdir ("..") < 0)
00620              result = -1;
00621        }
00622     }
00623 
00624   return result;
00625 }
00626 
00627 
00628 static int
00629 __attribute ((noinline))
00630 internal_function
00631 ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
00632             int flags)
00633 {
00634   struct ftw_data data;
00635   struct STAT st;
00636   int result = 0;
00637   int save_err;
00638   int cwdfd = -1;
00639   char *cwd = NULL;
00640   char *cp;
00641 
00642   /* First make sure the parameters are reasonable.  */
00643   if (dir[0] == '\0')
00644     {
00645       __set_errno (ENOENT);
00646       return -1;
00647     }
00648 
00649   data.maxdir = descriptors < 1 ? 1 : descriptors;
00650   data.actdir = 0;
00651   data.dirstreams = (struct dir_data **) alloca (data.maxdir
00652                                            * sizeof (struct dir_data *));
00653   memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
00654 
00655   /* PATH_MAX is always defined when we get here.  */
00656   data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX);
00657   data.dirbuf = (char *) malloc (data.dirbufsize);
00658   if (data.dirbuf == NULL)
00659     return -1;
00660   cp = __stpcpy (data.dirbuf, dir);
00661   /* Strip trailing slashes.  */
00662   while (cp > data.dirbuf + 1 && cp[-1] == '/')
00663     --cp;
00664   *cp = '\0';
00665 
00666   data.ftw.level = 0;
00667 
00668   /* Find basename.  */
00669   while (cp > data.dirbuf && cp[-1] != '/')
00670     --cp;
00671   data.ftw.base = cp - data.dirbuf;
00672 
00673   data.flags = flags;
00674 
00675   /* This assignment might seem to be strange but it is what we want.
00676      The trick is that the first three arguments to the `ftw' and
00677      `nftw' callback functions are equal.  Therefore we can call in
00678      every case the callback using the format of the `nftw' version
00679      and get the correct result since the stack layout for a function
00680      call in C allows this.  */
00681   data.func = (NFTW_FUNC_T) func;
00682 
00683   /* Since we internally use the complete set of FTW_* values we need
00684      to reduce the value range before calling a `ftw' callback.  */
00685   data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
00686 
00687   /* No object known so far.  */
00688   data.known_objects = NULL;
00689 
00690   /* Now go to the directory containing the initial file/directory.  */
00691   if (flags & FTW_CHDIR)
00692     {
00693       /* We have to be able to go back to the current working
00694         directory.  The best way to do this is to use a file
00695         descriptor.  */
00696       cwdfd = __open (".", O_RDONLY | O_DIRECTORY);
00697       if (cwdfd == -1)
00698        {
00699          /* Try getting the directory name.  This can be needed if
00700             the current directory is executable but not readable.  */
00701          if (errno == EACCES)
00702            /* GNU extension ahead.  */
00703            cwd =  __getcwd (NULL, 0);
00704 
00705          if (cwd == NULL)
00706            goto out_fail;
00707        }
00708       else if (data.maxdir > 1)
00709        /* Account for the file descriptor we use here.  */
00710        --data.maxdir;
00711 
00712       if (data.ftw.base > 0)
00713        {
00714          /* Change to the directory the file is in.  In data.dirbuf
00715             we have a writable copy of the file name.  Just NUL
00716             terminate it for now and change the directory.  */
00717          if (data.ftw.base == 1)
00718            /* I.e., the file is in the root directory.  */
00719            result = __chdir ("/");
00720          else
00721            {
00722              char ch = data.dirbuf[data.ftw.base - 1];
00723              data.dirbuf[data.ftw.base - 1] = '\0';
00724              result = __chdir (data.dirbuf);
00725              data.dirbuf[data.ftw.base - 1] = ch;
00726            }
00727        }
00728     }
00729 
00730   /* Get stat info for start directory.  */
00731   if (result == 0)
00732     {
00733       const char *name;
00734 
00735       if (data.flags & FTW_CHDIR)
00736        {
00737          name = data.dirbuf + data.ftw.base;
00738          if (name[0] == '\0')
00739            name = ".";
00740        }
00741       else
00742        name = data.dirbuf;
00743 
00744       if (((flags & FTW_PHYS)
00745           ? LXSTAT (_STAT_VER, name, &st)
00746           : XSTAT (_STAT_VER, name, &st)) < 0)
00747        {
00748          if (!(flags & FTW_PHYS)
00749              && errno == ENOENT
00750              && LXSTAT (_STAT_VER, name, &st) == 0
00751              && S_ISLNK (st.st_mode))
00752            result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
00753                                &data.ftw);
00754          else
00755            /* No need to call the callback since we cannot say anything
00756               about the object.  */
00757            result = -1;
00758        }
00759       else
00760        {
00761          if (S_ISDIR (st.st_mode))
00762            {
00763              /* Remember the device of the initial directory in case
00764                FTW_MOUNT is given.  */
00765              data.dev = st.st_dev;
00766 
00767              /* We know this directory now.  */
00768              if (!(flags & FTW_PHYS))
00769               result = add_object (&data, &st);
00770 
00771              if (result == 0)
00772               result = ftw_dir (&data, &st, NULL);
00773            }
00774          else
00775            {
00776              int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
00777 
00778              result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
00779                                  &data.ftw);
00780            }
00781        }
00782 
00783       if ((flags & FTW_ACTIONRETVAL)
00784          && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
00785        result = 0;
00786     }
00787 
00788   /* Return to the start directory (if necessary).  */
00789   if (cwdfd != -1)
00790     {
00791       int save_err = errno;
00792       __fchdir (cwdfd);
00793       __set_errno (save_err);
00794     }
00795   else if (cwd != NULL)
00796     {
00797       int save_err = errno;
00798       __chdir (cwd);
00799       free (cwd);
00800       __set_errno (save_err);
00801     }
00802 
00803   /* Free all memory.  */
00804  out_fail:
00805   save_err = errno;
00806   __tdestroy (data.known_objects, free);
00807   free (data.dirbuf);
00808   __set_errno (save_err);
00809 
00810   return result;
00811 }
00812 
00813 
00814 
00815 /* Entry points.  */
00816 
00817 int
00818 FTW_NAME (path, func, descriptors)
00819      const char *path;
00820      FTW_FUNC_T func;
00821      int descriptors;
00822 {
00823   return ftw_startup (path, 0, func, descriptors, 0);
00824 }
00825 
00826 #ifndef _LIBC
00827 int
00828 NFTW_NAME (path, func, descriptors, flags)
00829      const char *path;
00830      NFTW_FUNC_T func;
00831      int descriptors;
00832      int flags;
00833 {
00834   return ftw_startup (path, 1, func, descriptors, flags);
00835 }
00836 #else
00837 
00838 # include <shlib-compat.h>
00839 
00840 int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int);
00841 
00842 int
00843 NFTW_NEW_NAME (path, func, descriptors, flags)
00844      const char *path;
00845      NFTW_FUNC_T func;
00846      int descriptors;
00847      int flags;
00848 {
00849   if (flags
00850       & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
00851     {
00852       __set_errno (EINVAL);
00853       return -1;
00854     }
00855   return ftw_startup (path, 1, func, descriptors, flags);
00856 }
00857 
00858 versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
00859 
00860 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
00861 
00862 /* Older nftw* version just ignored all unknown flags.  */
00863 
00864 int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int);
00865 
00866 int
00867 attribute_compat_text_section
00868 NFTW_OLD_NAME (path, func, descriptors, flags)
00869      const char *path;
00870      NFTW_FUNC_T func;
00871      int descriptors;
00872      int flags;
00873 {
00874   flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
00875   return ftw_startup (path, 1, func, descriptors, flags);
00876 }
00877 
00878 compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
00879 # endif
00880 #endif