diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | io/Versions | 4 | ||||
-rw-r--r-- | io/ftw.c | 112 | ||||
-rw-r--r-- | io/ftw.h | 26 | ||||
-rw-r--r-- | io/ftw64.c | 2 | ||||
-rw-r--r-- | io/ftwtest-sh | 77 | ||||
-rw-r--r-- | io/ftwtest.c | 31 | ||||
-rw-r--r-- | manual/filesys.texi | 11 |
8 files changed, 260 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog index d9d6b555b5..ff63de9ffa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2003-11-07 Jakub Jelinek <jakub@redhat.com> + + * io/ftw.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define. + (ftw_dir, ftw_startup): Add __attribute ((noinline)). + (NFTW_OLD_NAME, NFTW_NEW_NAME): New functions. + (NFTW_NAME): Only define if !_LIBC, add versioned_symbol + and compat_symbol. + * io/ftw64.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define. + * io/Versions (libc): Export nftw@@GLIBC_2.3.3 + and nftw64@@GLIBC_2.3.3. + + * io/ftw.h (FTW_ACTIONRETVAL): New flag. + (FTW_CONTINUE, FTW_STOP, FTW_SKIP_SUBTREE, FTW_SKIP_SIBLINGS): New. + * io/ftw.c (ftw_dir): Add old_dir argument. + Clear result if it was FTW_SKIP_SIBLINGS after processing all + dir entries. Change cwd back if old_dir != NULL. + (process_entry): Adjust caller. Don't change cwd back here. + Change FTW_SKIP_SUBTREE result to 0. + (ftw_startup): Adjust ftw_dir caller. + Clear result if it was FTW_SKIP_SUBTREE or FTW_SKIP_SIBLINGS. + * io/ftwtest.c (skip_subtree, skip_siblings): New variables. + (options, main): Add --skip-subtree and --skip-siblings options. + (cb): Use return FTW_CONTINUE instead of return 0. + Handle --skip-subtree and --skip-siblings. + * io/ftwtest-sh: Add tests for FTW_ACTIONRETVAL. + * manual/filesys.texi: Document FTW_ACTIONRETVAL. + 2003-11-04 Jakub Jelinek <jakub@redhat.com> * io/ftw.c (ftw_dir): Close dir if callback with FTW_D type returns diff --git a/io/Versions b/io/Versions index 88f3a9ac04..9e650d482a 100644 --- a/io/Versions +++ b/io/Versions @@ -93,6 +93,10 @@ libc { # l* lchmod; } + GLIBC_2.3.3 { + # n* + nftw; nftw64; + } GLIBC_PRIVATE { # functions which have an additional interface since they are # cancelable. diff --git a/io/ftw.c b/io/ftw.c index 6d5cedf63e..c4d2157bd9 100644 --- a/io/ftw.c +++ b/io/ftw.c @@ -135,6 +135,8 @@ int rpl_lstat (const char *, struct stat *); #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 @@ -217,7 +219,8 @@ static const int ftw_arr[] = /* 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 @@ -415,43 +418,24 @@ process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, || (!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; - } - } - } + result = ftw_dir (data, &st, dir); } else result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], &data->ftw); } + 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 dirent64 *d; @@ -550,6 +534,9 @@ fail: __set_errno (save_err); } + 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; @@ -559,11 +546,37 @@ fail: 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; + + 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) @@ -683,7 +696,7 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, result = add_object (&data, &st); if (result == 0) - result = ftw_dir (&data, &st); + result = ftw_dir (&data, &st, NULL); } else { @@ -693,6 +706,10 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, &data.ftw); } } + + if ((flags & FTW_ACTIONRETVAL) + && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS)) + result = 0; } /* Return to the start directory (if necessary). */ @@ -726,6 +743,7 @@ FTW_NAME (path, func, descriptors) return ftw_startup (path, 0, func, descriptors, 0); } +#ifndef _LIBC int NFTW_NAME (path, func, descriptors, flags) const char *path; @@ -735,3 +753,43 @@ NFTW_NAME (path, func, descriptors, flags) { return ftw_startup (path, 1, func, descriptors, flags); } +#else + +#include <shlib-compat.h> + +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 (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 diff --git a/io/ftw.h b/io/ftw.h index 4939297b34..03973ccbbf 100644 --- a/io/ftw.h +++ b/io/ftw.h @@ -74,7 +74,33 @@ enum # define FTW_CHDIR FTW_CHDIR FTW_DEPTH = 8 /* Report files in directory before directory itself.*/ # define FTW_DEPTH FTW_DEPTH +# ifdef __USE_GNU + , + FTW_ACTIONRETVAL = 16 /* Assume callback to return FTW_* values instead of + zero to continue and non-zero to terminate. */ +# define FTW_ACTIONRETVAL FTW_ACTIONRETVAL +# endif +}; + +#ifdef __USE_GNU +/* Return values from callback functions. */ +enum +{ + FTW_CONTINUE = 0, /* Continue with next sibling or for FTW_D with the + first child. */ +# define FTW_CONTINUE FTW_CONTINUE + FTW_STOP = 1, /* Return from `ftw' or `nftw' with FTW_STOP as return + value. */ +# define FTW_STOP FTW_STOP + FTW_SKIP_SUBTREE = 2, /* Only meaningful for FTW_D: Don't walk through the + subtree, instead just continue with its next + sibling. */ +# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE + FTW_SKIP_SIBLINGS = 3,/* Continue with FTW_DP callback for current directory + (if FTW_DEPTH) and then its siblings. */ +# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS }; +#endif /* Structure used for fourth argument to callback function for `nftw'. */ struct FTW diff --git a/io/ftw64.c b/io/ftw64.c index 430ee365d6..7913b7af17 100644 --- a/io/ftw64.c +++ b/io/ftw64.c @@ -20,6 +20,8 @@ #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 #define STAT stat64 #define LXSTAT __lxstat64 diff --git a/io/ftwtest-sh b/io/ftwtest-sh index 76c1de7f3e..2fed2edcfa 100644 --- a/io/ftwtest-sh +++ b/io/ftwtest-sh @@ -202,6 +202,83 @@ succeeded EOF rm $testout +mkdir $tmpdir/foo/lvl1b +echo > $tmpdir/foo/lvl1b/file@1b +echo > $tmpdir/foo/lvl1b/file2@1b +echo > $tmpdir/foo/lvl1b/file3@1b + +LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-subtree=lvl1 $tmpdir | + sort > $testout + +cat <<EOF | diff -u $testout - || exit 1 +base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0 +base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1 +base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3 +EOF +rm $testout + +LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-siblings=lvl1 $tmpdir | + sort > $testout + +# The filesystem is not required to put lvl1 before lvl1b. +# If lvl1b comes after lvl1, it shouldn't be printed, while if it +# comes before, it should. +catcmd=cat +[ -n "`ls -U $tmpdir/foo/ | sed -n '/lvl1$/,${/lvl1b$/p}'`" ] \ + && catcmd="grep -v lvl1b" + +$catcmd <<EOF | diff -u $testout - || exit 1 +base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0 +base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1 +base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3 +EOF +rm $testout + +LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-siblings=file@1b $tmpdir | + sort > $testout + +# The filesystem is not required to put file2@1b and file3@1b after file@1b. +# If file[23]@1b come after file@1b, it shouldn't be printed, while if they +# come before, they should. +regexp=`echo $(ls -U /tmp/ftwtest.d/foo/lvl1b \ + | sed -n '/file@1b$/,${/file[23]@1b$/p}') | sed 's, ,|,'` +catcmd=cat +[ -n "$regexp" ] && catcmd="egrep -v $regexp" + +$catcmd <<EOF | diff -u $testout - || exit 1 +base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0 +base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1 +base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1 +base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2 +base = "/tmp/ftwtest.d/foo/lvl1/", file = "file@1", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1/", file = "link@1", flag = FTW_SLN, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1/", file = "lvl2", flag = FTW_D, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1/lvl2/", file = "file@2", flag = FTW_F, level = 4 +base = "/tmp/ftwtest.d/foo/lvl1/lvl2/", file = "lvl3", flag = FTW_D, level = 4 +base = "/tmp/ftwtest.d/foo/lvl1/lvl2/lvl3/", file = "file@3", flag = FTW_F, level = 5 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3 +base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3 +EOF +rm $testout + rm -fr $tmpdir exit 0 diff --git a/io/ftwtest.c b/io/ftwtest.c index 4f527997df..77debd2805 100644 --- a/io/ftwtest.c +++ b/io/ftwtest.c @@ -12,12 +12,16 @@ int do_depth; int do_chdir; int do_phys; int do_exit; +char *skip_subtree; +char *skip_siblings; struct option options[] = { { "depth", no_argument, &do_depth, 1 }, { "chdir", no_argument, &do_chdir, 1 }, { "phys", no_argument, &do_phys, 1 }, + { "skip-subtree", required_argument, NULL, 't' }, + { "skip-siblings", required_argument, NULL, 's' }, { "early-exit", no_argument, &do_exit, 1 }, { NULL, 0, NULL, 0 } }; @@ -38,7 +42,7 @@ static int cb (const char *name, const struct stat *st, int flag, struct FTW *f) { if (do_exit && strcmp (name + f->base, "file@2")) - return 0; + return FTW_CONTINUE; printf ("base = \"%.*s\", file = \"%s\", flag = %s", f->base, name, name + f->base, flag2name[flag]); @@ -49,7 +53,14 @@ cb (const char *name, const struct stat *st, int flag, struct FTW *f) free (cwd); } printf (", level = %d\n", f->level); - return do_exit ? 26 : 0; + + if (skip_siblings && strcmp (name + f->base, skip_siblings) == 0) + return FTW_SKIP_SIBLINGS; + + if (skip_subtree && strcmp (name + f->base, skip_subtree) == 0) + return FTW_SKIP_SUBTREE; + + return do_exit ? 26 : FTW_CONTINUE; } int @@ -61,7 +72,12 @@ main (int argc, char *argv[]) mtrace (); while ((opt = getopt_long_only (argc, argv, "", options, NULL)) != -1) - ; + { + if (opt == 't') + skip_subtree = optarg; + else if (opt == 's') + skip_siblings = optarg; + } if (do_chdir) flag |= FTW_CHDIR; @@ -69,6 +85,15 @@ main (int argc, char *argv[]) flag |= FTW_DEPTH; if (do_phys) flag |= FTW_PHYS; + if (skip_subtree || skip_siblings) + { + flag |= FTW_ACTIONRETVAL; + if (do_exit) + { + printf ("--early-exit cannot be used together with --skip-{siblings,subtree}"); + exit (1); + } + } char *cw1 = getcwd (NULL, 0); diff --git a/manual/filesys.texi b/manual/filesys.texi index c858e2b1fa..70889c2f85 100644 --- a/manual/filesys.texi +++ b/manual/filesys.texi @@ -918,6 +918,17 @@ If this option is specified then all subdirectories and files within them are processed before processing the top directory itself (depth-first processing). This also means the type flag given to the callback function is @code{FTW_DP} and not @code{FTW_D}. +@item FTW_ACTIONRETVAL +If this option is specified then return values from callbacks +are handled differently. If the callback returns @code{FTW_CONTINUE}, +walking continues normally. @code{FTW_STOP} means walking stops +and @code{FTW_STOP} is returned to the caller. If @code{FTW_SKIP_SUBTREE} +is returned by the callback with @code{FTW_D} argument, the subtree +is skipped and walking continues with next sibling of the directory. +If @code{FTW_SKIP_SIBLINGS} is returned by the callback, all siblings +of the current entry are skipped and walking continues in its parent. +No other return values should be returned from the callbacks if +this option is set. This option is a GNU extension. @end vtable The return value is computed in the same way as for @code{ftw}. |