diff options
Diffstat (limited to 'libc/misc/ftw/ftw.c')
-rw-r--r-- | libc/misc/ftw/ftw.c | 1031 |
1 files changed, 596 insertions, 435 deletions
diff --git a/libc/misc/ftw/ftw.c b/libc/misc/ftw/ftw.c index 8a6db98d3..fdea1c208 100644 --- a/libc/misc/ftw/ftw.c +++ b/libc/misc/ftw/ftw.c @@ -1,5 +1,5 @@ /* File tree walker functions. - Copyright (C) 1996-2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1996-2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. @@ -18,67 +18,68 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -/* used by D_EXACT_NAMLEN */ -#define strlen __strlen +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif -#define mempcpy __mempcpy -#define stpcpy __stpcpy +#define _GNU_SOURCE +#define _XOPEN_SOURCE 500 +#include <features.h> +#ifdef __UCLIBC__ +#undef _LIBC +#define HAVE_DIRENT_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_DECL_STPCPY 1 +#define HAVE_MEMPCPY 1 +#define dirfd __dirfd #define tsearch __tsearch -#define tdestroy __tdestroy #define tfind __tfind -#define fchdir __fchdir -#define chdir __chdir -#define dirfd __dirfd +#define tdestroy __tdestroy #define getcwd __getcwd +#define chdir __chdir +#define fchdir __fchdir +#define mempcpy __mempcpy #define opendir __opendir #define closedir __closedir - -#define _GNU_SOURCE -#include <features.h> - -#if defined (__UCLIBC_HAS_LFS__) && defined L_ftw64 -#ifndef L_ftw -#define L_ftw +#define stpcpy __stpcpy #endif -/* If Large file support is enabled, transparently remap - * things to use the 64-bit interfaces */ -#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS != 64 -#undef _FILE_OFFSET_BITS -#define _FILE_OFFSET_BITS 64 -#endif -#ifndef __USE_LARGEFILE64 -# define __USE_LARGEFILE64 1 -#endif -#ifndef __USE_FILE_OFFSET64 -# define __USE_FILE_OFFSET64 1 +#if __GNUC__ +# define alloca __builtin_alloca +#else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX + # pragma alloca +# else +char *alloca (); +# endif +# endif #endif -#define FTW_NAME ftw64 -#define NFTW_NAME nftw64 -#define INO_T ino64_t -#define STAT stat64 -#define LSTAT __lstat64 -#define XSTAT __stat64 -#define FTW_FUNC_T __ftw64_func_t -#define NFTW_FUNC_T __nftw64_func_t -#define __readdir __readdir64 +#if defined _LIBC +# include <dirent.h> +# define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent) #else -#undef __stat -#undef __lstat -#define FTW_NAME ftw -#define NFTW_NAME nftw -#define INO_T ino_t -#define STAT stat -#define LSTAT __lstat -#define XSTAT __stat -#define FTW_FUNC_T __ftw_func_t -#define NFTW_FUNC_T __nftw_func_t +# if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) __strlen ((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +# endif #endif -#ifdef L_ftw - -#include <alloca.h> #include <errno.h> #include <ftw.h> #include <limits.h> @@ -86,14 +87,100 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <sys/param.h> -#include <sys/stat.h> +#if HAVE_SYS_PARAM_H || defined _LIBC +# include <sys/param.h> +#endif +#ifdef _LIBC +# include <include/sys/stat.h> +#else +# include <sys/stat.h> +#endif + +#if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy +char *stpcpy (); +#endif + +#if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy +/* Be CAREFUL that there are no side effects in N. */ +# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) +#endif + +/* #define NDEBUG 1 */ #include <assert.h> -#include <dirent.h> -#if 1 /*ndef L_ftw64*/ +#if !defined _LIBC && !defined __UCLIBC__ +# undef __chdir +# define __chdir chdir +# undef __closedir +# define __closedir closedir +# undef __fchdir +# define __fchdir fchdir +# ifndef __UCLIBC__ +# undef __getcwd +# define __getcwd(P, N) xgetcwd () +extern char *xgetcwd (void); +# endif +# undef __mempcpy +# define __mempcpy mempcpy +# undef __opendir +# define __opendir opendir +# undef __readdir64 +# define __readdir64 readdir +# undef __stpcpy +# define __stpcpy stpcpy +# undef __tdestroy +# define __tdestroy tdestroy +# undef __tfind +# define __tfind tfind +# undef __tsearch +# define __tsearch tsearch +# undef internal_function +# define internal_function /* empty */ +# undef dirent64 +# define dirent64 dirent +# undef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* Arrange to make lstat calls go through the wrapper function + on systems with an lstat function that does not dereference symlinks + that are specified with a trailing slash. */ +#if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK && !defined __UCLIBC__ +int rpl_lstat (const char *, struct stat *); +# undef lstat +# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf) +#endif + +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +/* 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 STAT stat +# ifdef _LIBC +# define LXSTAT __lxstat +# define XSTAT __xstat +# else +# ifdef __UCLIBC__ +# define LXSTAT(V,f,sb) __lstat(f,sb) +# define XSTAT(V,f,sb) __stat(f,sb) +# define __readdir64 __readdir +# define dirent64 dirent +# else +# define LXSTAT(V,f,sb) lstat (f,sb) +# define XSTAT(V,f,sb) stat (f,sb) +# endif +# endif +# define FTW_FUNC_T __ftw_func_t +# define NFTW_FUNC_T __nftw_func_t extern struct dirent *__readdir (DIR *__dirp) __nonnull ((1)) attribute_hidden; -#else +# else extern struct dirent64 *__readdir64 (DIR *__dirp) __nonnull ((1)) attribute_hidden; #endif @@ -107,48 +194,48 @@ extern struct dirent64 *__readdir64 (DIR *__dirp) __nonnull ((1)) attribute_hidd struct dir_data { - DIR *stream; - char *content; + DIR *stream; + char *content; }; struct known_object { - dev_t dev; - INO_T ino; + dev_t dev; + INO_T ino; }; struct ftw_data { - /* Array with pointers to open directory streams. */ - struct dir_data **dirstreams; - size_t actdir; - size_t maxdir; + /* 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; + /* 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; + /* 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; + /* 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; + /* 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. */ - NFTW_FUNC_T func; + /* Callback function. We always use the `nftw' form. */ + NFTW_FUNC_T func; - /* Device of starting point. Needed for FTW_MOUNT. */ - dev_t dev; + /* 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; + /* Data structure for keeping fingerprints of already processed + object. This is needed when not using FTW_PHYS. */ + void *known_objects; }; @@ -156,52 +243,54 @@ struct ftw_data 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 + 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 + FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS }; + /* Forward declarations of local functions. */ -static int ftw_dir (struct ftw_data *data, struct STAT *st) internal_function; +static int ftw_dir (struct ftw_data *data, struct STAT *st, + struct dir_data *old_dir) internal_function; 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); + /* 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 inline int add_object (struct ftw_data *data, 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; + 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 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; + struct known_object obj; + obj.dev = st->st_dev; + obj.ino = st->st_ino; + return __tfind (&obj, &data->known_objects, object_compare) != NULL; } @@ -209,465 +298,537 @@ static inline int __attribute ((always_inline)) open_dir_stream (struct ftw_data *data, struct dir_data *dirp) { - int result = 0; + int result = 0; - if (data->dirstreams[data->actdir] != NULL) + 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 + /* 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 dirent *d; - size_t actsize = 0; + DIR *st = data->dirstreams[data->actdir]->stream; + struct dirent64 *d; + size_t actsize = 0; - while ((d = __readdir (st)) != NULL) + while ((d = __readdir64 (st)) != NULL) { - size_t this_len = _D_EXACT_NAMLEN (d); - if (actsize + this_len + 2 >= bufsize) + 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) + char *newp; + bufsize += MAX (1024, 2 * this_len); + newp = (char *) realloc (buf, bufsize); + if (newp == NULL) { - /* No more memory. */ - int save_err = errno; - free (buf); - __set_errno (save_err); - result = -1; - break; + /* No more memory. */ + int save_err = errno; + free (buf); + __set_errno (save_err); + result = -1; + break; } - buf = newp; + buf = newp; } - *((char *) mempcpy (buf + actsize, d->d_name, this_len)) - = '\0'; - actsize += this_len + 1; + *((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'; + /* Terminate the list with an additional NUL byte. */ + buf[actsize++] = '\0'; - /* Shrink the buffer to what we actually need. */ - data->dirstreams[data->actdir]->content = realloc (buf, actsize); - if (data->dirstreams[data->actdir]->content == NULL) + /* Shrink the buffer to what we actually need. */ + data->dirstreams[data->actdir]->content = realloc (buf, actsize); + if (data->dirstreams[data->actdir]->content == NULL) { - int save_err = errno; - free (buf); - __set_errno (save_err); - result = -1; + int save_err = errno; + free (buf); + __set_errno (save_err); + result = -1; } - else + else { - closedir (st); - data->dirstreams[data->actdir]->stream = NULL; - data->dirstreams[data->actdir] = NULL; + __closedir (st); + data->dirstreams[data->actdir]->stream = NULL; + data->dirstreams[data->actdir] = NULL; } } } - /* Open the new stream. */ - if (result == 0) + /* Open the new stream. */ + if (result == 0) { - const char *name = ((data->flags & FTW_CHDIR) - ? data->dirbuf + data->ftw.base: data->dirbuf); - assert (data->dirstreams[data->actdir] == NULL); - - dirp->stream = opendir (name); - if (dirp->stream == NULL) - result = -1; - else + const char *name = ((data->flags & FTW_CHDIR) + ? data->dirbuf + data->ftw.base: data->dirbuf); + assert (data->dirstreams[data->actdir] == NULL); + + dirp->stream = __opendir (name); + if (dirp->stream == NULL) + result = -1; + else { - dirp->content = NULL; - data->dirstreams[data->actdir] = dirp; + dirp->content = NULL; + data->dirstreams[data->actdir] = dirp; - if (++data->actdir == data->maxdir) - data->actdir = 0; + if (++data->actdir == data->maxdir) + data->actdir = 0; } } - return result; + return result; } static int internal_function -process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, size_t namlen) +process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, + size_t namlen) { - struct STAT st; - int result = 0; - int flag = 0; - size_t new_buflen; - - 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) + struct STAT st; + int result = 0; + int flag = 0; + size_t new_buflen; + + 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) { - /* Enlarge the buffer. */ - char *newp; - - data->dirbufsize = 2 * new_buflen; - newp = (char *) realloc (data->dirbuf, data->dirbufsize); - if (newp == NULL) - return -1; - data->dirbuf = newp; + /* Enlarge the buffer. */ + char *newp; + + data->dirbufsize = 2 * new_buflen; + newp = (char *) realloc (data->dirbuf, data->dirbufsize); + if (newp == NULL) + return -1; + data->dirbuf = newp; } - *((char *) mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0'; + *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0'; - if ((data->flags & FTW_CHDIR) == 0) - name = data->dirbuf; + if ((data->flags & FTW_CHDIR) == 0) + name = data->dirbuf; - if (((data->flags & FTW_PHYS) - ? LSTAT (name, &st) - : XSTAT (name, &st)) < 0) + if (((data->flags & FTW_PHYS) + ? LXSTAT (_STAT_VER, name, &st) + : XSTAT (_STAT_VER, name, &st)) < 0) { - if (errno != EACCES && errno != ENOENT) - result = -1; - else if (!(data->flags & FTW_PHYS) - && LSTAT (name, &st) == 0 - && S_ISLNK (st.st_mode)) - flag = FTW_SLN; - else - flag = FTW_NS; + if (errno != EACCES && errno != ENOENT) + result = -1; + else if (!(data->flags & FTW_PHYS) + && LXSTAT (_STAT_VER, name, &st) == 0 + && S_ISLNK (st.st_mode)) + flag = FTW_SLN; + else + flag = FTW_NS; } - else + 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 (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 (result == 0 + && (flag == FTW_NS + || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) { - if (flag == FTW_D) + if (flag == FTW_D) { - if ((data->flags & FTW_PHYS) - || (!find_object (data, &st) - /* Remember the object. */ - && (result = add_object (data, &st)) == 0)) - { - result = ftw_dir (data, &st); - - if (result == 0 && (data->flags & FTW_CHDIR)) - { - /* Change back to the parent directory. */ - int done = 0; - if (dir->stream != NULL) - if (fchdir (dirfd (dir->stream)) == 0) - done = 1; - - if (!done) - { - if (data->ftw.base == 1) - { - if (chdir ("/") < 0) - result = -1; - } - else - if (chdir ("..") < 0) - result = -1; - } - } - } + if ((data->flags & FTW_PHYS) + || (!find_object (data, &st) + /* Remember the object. */ + && (result = add_object (data, &st)) == 0)) + result = ftw_dir (data, &st, dir); } - else - result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], - &data->ftw); + else + result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], + &data->ftw); } - return result; + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE) + result = 0; + + return result; } + static int +__attribute ((noinline)) internal_function -ftw_dir (struct ftw_data *data, struct STAT *st) +ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir) { - struct dir_data dir; - struct dirent *d; - int previous_base = data->ftw.base; - int result; - char *startp; - - /* Open the stream for this directory. This might require that - another stream has to be closed. */ - result = open_dir_stream (data, &dir); - if (result != 0) + struct dir_data dir; + struct dirent64 *d; + int previous_base = data->ftw.base; + int result; + char *startp; + + /* Open the stream for this directory. This might require that + another stream has to be closed. */ + result = open_dir_stream (data, &dir); + if (result != 0) { - if (errno == EACCES) - /* We cannot read the directory. Signal this with a special flag. */ - result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw); + if (errno == EACCES) + /* We cannot read the directory. Signal this with a special flag. */ + result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw); - return result; + return result; } - /* First, report the directory (if not depth-first). */ - if (!(data->flags & FTW_DEPTH)) + /* First, report the directory (if not depth-first). */ + if (!(data->flags & FTW_DEPTH)) { - result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); - if (result != 0) - return result; + result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); + if (result != 0) + { + int save_err; +fail: + save_err = errno; + __closedir (dir.stream); + __set_errno (save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; + return result; + } } - /* If necessary, change to this directory. */ - if (data->flags & FTW_CHDIR) + /* If necessary, change to this directory. */ + if (data->flags & FTW_CHDIR) { - if (fchdir (dirfd (dir.stream)) < 0) + if (__fchdir (dirfd (dir.stream)) < 0) { - int save_err = errno; - closedir (dir.stream); - __set_errno (save_err); - - if (data->actdir-- == 0) - data->actdir = data->maxdir - 1; - data->dirstreams[data->actdir] = NULL; - - return -1; + result = -1; + goto fail; } } - /* Next, update the `struct FTW' information. */ - ++data->ftw.level; - startp = __strchr (data->dirbuf, '\0'); - /* There always must be a directory name. */ - assert (startp != data->dirbuf); - if (startp[-1] != '/') - *startp++ = '/'; - data->ftw.base = startp - data->dirbuf; + /* Next, update the `struct FTW' information. */ + ++data->ftw.level; + startp = __strchr (data->dirbuf, '\0'); + /* There always must be a directory name. */ + assert (startp != data->dirbuf); + if (startp[-1] != '/') + *startp++ = '/'; + data->ftw.base = startp - data->dirbuf; - while (dir.stream != NULL && (d = __readdir (dir.stream)) != NULL) + while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL) { - result = process_entry (data, &dir, d->d_name, _D_EXACT_NAMLEN (d)); - if (result != 0) - break; + result = process_entry (data, &dir, d->d_name, NAMLEN (d)); + if (result != 0) + break; } - if (dir.stream != NULL) + if (dir.stream != NULL) { - /* The stream is still open. I.e., we did not need more - descriptors. Simply close the stream now. */ - int save_err = errno; + /* The stream is still open. I.e., we did not need more + descriptors. Simply close the stream now. */ + int save_err = errno; - assert (dir.content == NULL); + assert (dir.content == NULL); - closedir (dir.stream); - __set_errno (save_err); + __closedir (dir.stream); + __set_errno (save_err); - if (data->actdir-- == 0) - data->actdir = data->maxdir - 1; - data->dirstreams[data->actdir] = NULL; + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; } - else + else { - int save_err; - char *runp = dir.content; + int save_err; + char *runp = dir.content; - while (result == 0 && *runp != '\0') + while (result == 0 && *runp != '\0') { - char *endp = __strchr (runp, '\0'); + char *endp = __strchr (runp, '\0'); - result = process_entry (data, &dir, runp, endp - runp); + result = process_entry (data, &dir, runp, endp - runp); - runp = endp + 1; + runp = endp + 1; } - save_err = errno; - free (dir.content); - __set_errno (save_err); + save_err = errno; + free (dir.content); + __set_errno (save_err); } - /* Prepare the return, revert the `struct FTW' information. */ - data->dirbuf[data->ftw.base - 1] = '\0'; - --data->ftw.level; - data->ftw.base = previous_base; + if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS) + result = 0; + + /* Prepare the return, revert the `struct FTW' information. */ + data->dirbuf[data->ftw.base - 1] = '\0'; + --data->ftw.level; + data->ftw.base = previous_base; - /* Finally, if we process depth-first report the directory. */ - if (result == 0 && (data->flags & FTW_DEPTH)) - result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); + /* Finally, if we process depth-first report the directory. */ + if (result == 0 && (data->flags & FTW_DEPTH)) + result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); + + if (old_dir + && (data->flags & FTW_CHDIR) + && (result == 0 + || ((data->flags & FTW_ACTIONRETVAL) + && (result != -1 && result != FTW_STOP)))) + { + /* Change back to the parent directory. */ + int done = 0; + if (old_dir->stream != NULL) + if (__fchdir (dirfd (old_dir->stream)) == 0) + done = 1; - return result; + if (!done) + { + if (data->ftw.base == 1) + { + if (__chdir ("/") < 0) + result = -1; + } + else + if (__chdir ("..") < 0) + result = -1; + } + } + + return result; } static int +__attribute ((noinline)) internal_function -ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, int flags) +ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, + int flags) { - struct ftw_data data; - struct STAT st; - int result = 0; - int save_err; - char *cwd = NULL; - char *cp; - - /* First make sure the parameters are reasonable. */ - if (unlikely(dir==NULL || *dir=='\0')) { - __set_errno (ENOENT); - return -1; - } - if ((__strlen(dir)+1) > NAME_MAX) { - __set_errno(ENAMETOOLONG); - return -1; + struct ftw_data data; + struct STAT st; + int result = 0; + int save_err; + 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; - data.dirstreams = (struct dir_data **) alloca (data.maxdir - * sizeof (struct dir_data *)); - __memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); - - /* PATH_MAX is always defined when we get here. */ - data.dirbufsize = MAX (2 * __strlen (dir), PATH_MAX); - data.dirbuf = (char *) malloc (data.dirbufsize); - if (data.dirbuf == NULL) - return -1; - 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; - - /* This assignment might seem to be strange but it is what we want. - The trick is that the first three arguments to the `ftw' and - `nftw' callback functions are equal. Therefore we can call in - every case the callback using the format of the `nftw' version - and get the correct result since the stack layout for a function - call in C allows this. */ - data.func = (NFTW_FUNC_T) 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) + data.maxdir = descriptors < 1 ? 1 : descriptors; + data.actdir = 0; + data.dirstreams = (struct dir_data **) alloca (data.maxdir + * sizeof (struct dir_data *)); + __memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); + + /* PATH_MAX is always defined when we get here. */ + data.dirbufsize = MAX (2 * __strlen (dir), PATH_MAX); + data.dirbuf = (char *) malloc (data.dirbufsize); + if (data.dirbuf == NULL) + return -1; + 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; + + /* This assignment might seem to be strange but it is what we want. + The trick is that the first three arguments to the `ftw' and + `nftw' callback functions are equal. Therefore we can call in + every case the callback using the format of the `nftw' version + and get the correct result since the stack layout for a function + call in C allows this. */ + data.func = (NFTW_FUNC_T) 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) { - /* GNU extension ahead. */ - cwd = getcwd (NULL, 0); - if (cwd == NULL) - result = -1; - else if (data.ftw.base > 0) + /* GNU extension ahead. */ + cwd = __getcwd (NULL, 0); + if (cwd == NULL) + result = -1; + else 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 + /* 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; + 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) + /* Get stat info for start directory. */ + if (result == 0) { - const char *name = ((data.flags & FTW_CHDIR) - ? data.dirbuf + data.ftw.base - : data.dirbuf); + const char *name = ((data.flags & FTW_CHDIR) + ? data.dirbuf + data.ftw.base + : data.dirbuf); - if (((flags & FTW_PHYS) - ? LSTAT (name, &st) - : XSTAT (name, &st)) < 0) + if (((flags & FTW_PHYS) + ? LXSTAT (_STAT_VER, name, &st) + : XSTAT (_STAT_VER, name, &st)) < 0) { - if (!(flags & FTW_PHYS) - && errno == ENOENT - && LSTAT (name, &st) == 0 - && S_ISLNK (st.st_mode)) - result = (*data.func) (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; + if (!(flags & FTW_PHYS) + && errno == ENOENT + && LXSTAT (_STAT_VER, name, &st) == 0 + && S_ISLNK (st.st_mode)) + result = (*data.func) (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 + else { - if (S_ISDIR (st.st_mode)) + if (S_ISDIR (st.st_mode)) { - /* Remember the device of the initial directory in case - FTW_MOUNT is given. */ - data.dev = st.st_dev; + /* 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); + /* We know this directory now. */ + if (!(flags & FTW_PHYS)) + result = add_object (&data, &st); - if (result == 0) - result = ftw_dir (&data, &st); + if (result == 0) + result = ftw_dir (&data, &st, NULL); } - else + else { - int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F; + int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F; - result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], - &data.ftw); + result = (*data.func) (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 (cwd != NULL) + /* Return to the start directory (if necessary). */ + if (cwd != NULL) { - int save_err = errno; - chdir (cwd); - free (cwd); - __set_errno (save_err); + int save_err = errno; + __chdir (cwd); + free (cwd); + __set_errno (save_err); } - /* Free all memory. */ - save_err = errno; - tdestroy (data.known_objects, free); - free (data.dirbuf); - __set_errno (save_err); + /* Free all memory. */ + save_err = errno; + __tdestroy (data.known_objects, free); + free (data.dirbuf); + __set_errno (save_err); - return result; + return result; } /* Entry points. */ -int FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors) +int +FTW_NAME (path, func, descriptors) + const char *path; + FTW_FUNC_T func; + int descriptors; { - return ftw_startup (path, 0, func, descriptors, 0); + return ftw_startup (path, 0, func, descriptors, 0); } -int NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags) +#ifndef _LIBC +int +NFTW_NAME (path, func, descriptors, flags) + const char *path; + NFTW_FUNC_T func; + int descriptors; + int flags; { - return ftw_startup (path, 1, func, descriptors, flags); + return ftw_startup (path, 1, func, descriptors, flags); } +#else + +#include <shlib-compat.h> + +int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int); + +int +NFTW_NEW_NAME (path, func, descriptors, flags) + 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, 1, 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 (path, func, descriptors, flags) + const char *path; + NFTW_FUNC_T func; + int descriptors; + int flags; +{ + flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH); + return ftw_startup (path, 1, func, descriptors, flags); +} + +compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1); +#endif #endif |