From 7c9da61ea1a8b47ba142172ebfb150d02cd4eb68 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 12 Feb 2018 18:38:31 +0100 Subject: [PATCH] Resolves: #1544429 - import gnulib's FTS module from upstream commit 281b825e --- findutils-4.5.15-leaf-opt.patch | 141 ------ findutils-4.6.0-fts-update.patch | 761 +++++++++++++++++++++++++++++++ findutils-4.6.0-leaf-opt.patch | 83 ++++ findutils.spec | 14 +- 4 files changed, 854 insertions(+), 145 deletions(-) delete mode 100644 findutils-4.5.15-leaf-opt.patch create mode 100644 findutils-4.6.0-fts-update.patch create mode 100644 findutils-4.6.0-leaf-opt.patch diff --git a/findutils-4.5.15-leaf-opt.patch b/findutils-4.5.15-leaf-opt.patch deleted file mode 100644 index 64817fb..0000000 --- a/findutils-4.5.15-leaf-opt.patch +++ /dev/null @@ -1,141 +0,0 @@ -From 1328926a705fdb4728c1f255dd368de928736d39 Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Fri, 25 Sep 2015 16:09:39 +0200 -Subject: [PATCH 1/3] fts: introduce the FTS_NOLEAF flag - -The flag is needed to implement the -noleaf option of find. -* lib/fts.c (link_count_optimize_ok): Implement the FTS_NOLEAF flag. -* lib/fts_.h (FTS_NOLEAF): New macro, shifted conflicting constants. ---- - gl/lib/fts.c | 4 ++++ - gl/lib/fts_.h | 12 +++++++++--- - 2 files changed, 13 insertions(+), 3 deletions(-) - -diff --git a/gl/lib/fts.c b/gl/lib/fts.c -index d2d404f..808466f 100644 ---- a/gl/lib/fts.c -+++ b/gl/lib/fts.c -@@ -788,6 +788,10 @@ link_count_optimize_ok (FTSENT const *p) - bool opt_ok; - struct LCO_ent *t2; - -+ if (ISSET(FTS_NOLEAF)) -+ /* leaf optimization explicitly disabled by the FTS_NOLEAF flag */ -+ return false; -+ - /* If we're not in CWDFD mode, don't bother with this optimization, - since the caller is not serious about performance. */ - if (!ISSET(FTS_CWDFD)) -diff --git a/gl/lib/fts_.h b/gl/lib/fts_.h -index 63d4b74..f1d519b 100644 ---- a/gl/lib/fts_.h -+++ b/gl/lib/fts_.h -@@ -155,10 +155,16 @@ typedef struct { - from input path names during fts_open initialization. */ - # define FTS_VERBATIM 0x1000 - --# define FTS_OPTIONMASK 0x1fff /* valid user option mask */ -+ /* Disable leaf optimization (which eliminates stat() calls during traversal, -+ based on the count of nested directories stored in stat.st_nlink of each -+ directory). Note that the optimization is by default enabled only for -+ selected file systems, and only if the FTS_CWDFD flag is set. */ -+# define FTS_NOLEAF 0x2000 - --# define FTS_NAMEONLY 0x2000 /* (private) child names only */ --# define FTS_STOP 0x4000 /* (private) unrecoverable error */ -+# define FTS_OPTIONMASK 0x3fff /* valid user option mask */ -+ -+# define FTS_NAMEONLY 0x4000 /* (private) child names only */ -+# define FTS_STOP 0x8000 /* (private) unrecoverable error */ - int fts_options; /* fts_open options, global flags */ - - /* Map a directory's device number to a boolean. The boolean is --- -2.5.0 - - -From c186934e6e37ddadf7511abb9b1045192757618e Mon Sep 17 00:00:00 2001 -From: Kamil Dudka -Date: Fri, 25 Sep 2015 19:13:15 +0200 -Subject: [PATCH 2/3] ftsfind: propagate the -noleaf option to FTS - -* find/ftsfind.c (find): Propagate the -noleaf option to FTS. ---- - find/ftsfind.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/find/ftsfind.c b/find/ftsfind.c -index 5159470..e34b672 100644 ---- a/find/ftsfind.c -+++ b/find/ftsfind.c -@@ -559,6 +559,9 @@ find (char *arg) - if (options.stay_on_filesystem) - ftsoptions |= FTS_XDEV; - -+ if (options.no_leaf_check) -+ ftsoptions |= FTS_NOLEAF; -+ - p = fts_open (arglist, ftsoptions, NULL); - if (NULL == p) - { --- -2.5.0 - - -From 2edb6204bab0acb0ef8cdde7499396afd9c66131 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?P=C3=A1draig=20Brady?= -Date: Mon, 18 Jan 2016 17:29:28 +0000 -Subject: [PATCH 3/3] fts: don't unconditionally use leaf optimization for NFS - -NFS st_nlink are not accurate on all implementations, -leading to aborts() if that assumption is made. -See -* lib/fts.c (leaf_optimization_applies): Remove NFS from -the white list, and document the issue. - -Upstream-commit: 85717b68b03bf85016c5079fbbf0c8aa2b182ba6 -Signed-off-by: Kamil Dudka ---- - gl/lib/fts.c | 19 ++++++++++--------- - 1 file changed, 10 insertions(+), 9 deletions(-) - -diff --git a/gl/lib/fts.c b/gl/lib/fts.c -index 55cd554..6e1eaf5 100644 ---- a/gl/lib/fts.c -+++ b/gl/lib/fts.c -@@ -718,22 +718,23 @@ leaf_optimization_applies (int dir_fd) - - switch (fs_buf.f_type) - { -- case S_MAGIC_NFS: -- /* NFS provides usable dirent.d_type but not necessarily for all entries -- of large directories. See . */ -- return true; -- - /* List here the file system types that lack usable dirent.d_type - info, yet for which the optimization does apply. */ - case S_MAGIC_REISERFS: - case S_MAGIC_XFS: - return true; - -+ /* Explicitly list here any other file system type for which the -+ optimization is not applicable, but need documentation. */ -+ case S_MAGIC_NFS: -+ /* NFS provides usable dirent.d_type but not necessarily for all entries -+ of large directories, so as per -+ NFS should return true. However st_nlink values are not accurate on -+ all implementations as per . */ -+ /* fall through */ - case S_MAGIC_PROC: -- /* Explicitly listing this or any other file system type for which -- the optimization is not applicable is not necessary, but we leave -- it here to document the risk. Per http://bugs.debian.org/143111, -- /proc may have bogus stat.st_nlink values. */ -+ /* Per /proc may have -+ bogus stat.st_nlink values. */ - /* fall through */ - default: - return false; --- -2.5.5 - diff --git a/findutils-4.6.0-fts-update.patch b/findutils-4.6.0-fts-update.patch new file mode 100644 index 0000000..ad6289b --- /dev/null +++ b/findutils-4.6.0-fts-update.patch @@ -0,0 +1,761 @@ +From f3337786e55909538aacfd7c29b1cf58ff444fbf Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 12 Feb 2018 12:45:36 +0100 +Subject: [PATCH 1/2] import gnulib's FTS module from upstream commit 281b825e + +--- + gl/lib/fts.c | 424 +++++++++++++++++++++++++++++----------------------------- + gl/lib/fts_.h | 10 +- + 2 files changed, 221 insertions(+), 213 deletions(-) + +diff --git a/gl/lib/fts.c b/gl/lib/fts.c +index c91d7a1..bfa73e3 100644 +--- a/gl/lib/fts.c ++++ b/gl/lib/fts.c +@@ -1,6 +1,6 @@ + /* Traverse a file hierarchy. + +- Copyright (C) 2004-2015 Free Software Foundation, Inc. ++ Copyright (C) 2004-2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -13,7 +13,7 @@ + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +- along with this program. If not, see . */ ++ along with this program. If not, see . */ + + /*- + * Copyright (c) 1990, 1993, 1994 +@@ -46,9 +46,9 @@ + + #include + +-#if defined(LIBC_SCCS) && !defined(lint) ++#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint + static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; +-#endif /* LIBC_SCCS and not lint */ ++#endif + + #include "fts_.h" + +@@ -71,11 +71,7 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; + + #if ! _LIBC + # include "fcntl--.h" +-# include "dirent--.h" +-# include "unistd--.h" +-/* FIXME - use fcntl(F_DUPFD_CLOEXEC)/openat(O_CLOEXEC) once they are +- supported. */ +-# include "cloexec.h" ++# include "flexmember.h" + # include "openat.h" + # include "same-inode.h" + #endif +@@ -202,6 +198,14 @@ enum Fts_stat + while (false) + #endif + ++#ifndef FALLTHROUGH ++# if __GNUC__ < 7 ++# define FALLTHROUGH ((void) 0) ++# else ++# define FALLTHROUGH __attribute__ ((__fallthrough__)) ++# endif ++#endif ++ + static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function; + static FTSENT *fts_build (FTS *, int) internal_function; + static void fts_lfree (FTSENT *) internal_function; +@@ -296,14 +300,13 @@ static DIR * + internal_function + opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd) + { +- int new_fd = openat (fd, dir, +- (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK +- | extra_flags)); ++ int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY ++ | O_NONBLOCK | extra_flags); ++ int new_fd = openat (fd, dir, open_flags); + DIR *dirp; + + if (new_fd < 0) + return NULL; +- set_cloexec_flag (new_fd, true); + dirp = fdopendir (new_fd); + if (dirp) + *pdir_fd = new_fd; +@@ -366,15 +369,13 @@ static int + internal_function + diropen (FTS const *sp, char const *dir) + { +- int open_flags = (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK ++ int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK + | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0) + | (ISSET (FTS_NOATIME) ? O_NOATIME : 0)); + + int fd = (ISSET (FTS_CWDFD) + ? openat (sp->fts_cwd_fd, dir, open_flags) + : open (dir, open_flags)); +- if (0 <= fd) +- set_cloexec_flag (fd, true); + return fd; + } + +@@ -470,6 +471,7 @@ fts_open (char * const *argv, + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; ++ parent->fts_n_dirs_remaining = -1; + } + + /* The classic fts implementation would call fts_stat with +@@ -656,39 +658,139 @@ fts_close (FTS *sp) + return (0); + } + ++/* Minimum link count of a traditional Unix directory. When leaf ++ optimization is OK and MIN_DIR_NLINK <= st_nlink, then st_nlink is ++ an upper bound on the number of subdirectories (counting "." and ++ ".."). */ ++enum { MIN_DIR_NLINK = 2 }; ++ ++/* Whether leaf optimization is OK for a directory. */ ++enum leaf_optimization ++ { ++ /* st_nlink is not reliable for this directory's subdirectories. */ ++ NO_LEAF_OPTIMIZATION, ++ ++ /* Leaf optimization is OK, but is not useful for avoiding stat calls. */ ++ OK_LEAF_OPTIMIZATION, ++ ++ /* Leaf optimization is not only OK: it is useful for avoiding ++ stat calls, because dirent.d_type does not work. */ ++ NOSTAT_LEAF_OPTIMIZATION ++ }; ++ + #if defined __linux__ \ + && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE + + # include + + /* Linux-specific constants from coreutils' src/fs.h */ +-# define S_MAGIC_TMPFS 0x1021994 ++# define S_MAGIC_AFS 0x5346414F + # define S_MAGIC_NFS 0x6969 ++# define S_MAGIC_PROC 0x9FA0 + # define S_MAGIC_REISERFS 0x52654973 ++# define S_MAGIC_TMPFS 0x1021994 + # define S_MAGIC_XFS 0x58465342 +-# define S_MAGIC_PROC 0x9FA0 + +-/* Return false if it is easy to determine the file system type of +- the directory on which DIR_FD is open, and sorting dirents on +- inode numbers is known not to improve traversal performance with +- that type of file system. Otherwise, return true. */ ++# ifdef HAVE___FSWORD_T ++typedef __fsword_t fsword; ++# else ++typedef long int fsword; ++# endif ++ ++/* Map a stat.st_dev number to a file system type number f_ftype. */ ++struct dev_type ++{ ++ dev_t st_dev; ++ fsword f_type; ++}; ++ ++/* Use a tiny initial size. If a traversal encounters more than ++ a few devices, the cost of growing/rehashing this table will be ++ rendered negligible by the number of inodes processed. */ ++enum { DEV_TYPE_HT_INITIAL_SIZE = 13 }; ++ ++static size_t ++dev_type_hash (void const *x, size_t table_size) ++{ ++ struct dev_type const *ax = x; ++ uintmax_t dev = ax->st_dev; ++ return dev % table_size; ++} ++ + static bool +-dirent_inode_sort_may_be_useful (int dir_fd) ++dev_type_compare (void const *x, void const *y) ++{ ++ struct dev_type const *ax = x; ++ struct dev_type const *ay = y; ++ return ax->st_dev == ay->st_dev; ++} ++ ++/* Return the file system type of P, or 0 if not known. ++ Try to cache known values. */ ++ ++static fsword ++filesystem_type (FTSENT const *p) ++{ ++ FTS *sp = p->fts_fts; ++ Hash_table *h = sp->fts_leaf_optimization_works_ht; ++ struct dev_type *ent; ++ struct statfs fs_buf; ++ ++ /* If we're not in CWDFD mode, don't bother with this optimization, ++ since the caller is not serious about performance. */ ++ if (!ISSET (FTS_CWDFD)) ++ return 0; ++ ++ if (! h) ++ h = sp->fts_leaf_optimization_works_ht ++ = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash, ++ dev_type_compare, free); ++ if (h) ++ { ++ struct dev_type tmp; ++ tmp.st_dev = p->fts_statp->st_dev; ++ ent = hash_lookup (h, &tmp); ++ if (ent) ++ return ent->f_type; ++ } ++ ++ /* Look-up failed. Query directly and cache the result. */ ++ if (fstatfs (p->fts_fts->fts_cwd_fd, &fs_buf) != 0) ++ return 0; ++ ++ if (h) ++ { ++ struct dev_type *t2 = malloc (sizeof *t2); ++ if (t2) ++ { ++ t2->st_dev = p->fts_statp->st_dev; ++ t2->f_type = fs_buf.f_type; ++ ++ ent = hash_insert (h, t2); ++ if (ent) ++ fts_assert (ent == t2); ++ else ++ free (t2); ++ } ++ } ++ ++ return fs_buf.f_type; ++} ++ ++/* Return false if it is easy to determine the file system type of the ++ directory P, and sorting dirents on inode numbers is known not to ++ improve traversal performance with that type of file system. ++ Otherwise, return true. */ ++static bool ++dirent_inode_sort_may_be_useful (FTSENT const *p) + { + /* Skip the sort only if we can determine efficiently + that skipping it is the right thing to do. + The cost of performing an unnecessary sort is negligible, + while the cost of *not* performing it can be O(N^2) with + a very large constant. */ +- struct statfs fs_buf; +- +- /* If fstatfs fails, assume sorting would be useful. */ +- if (fstatfs (dir_fd, &fs_buf) != 0) +- return true; + +- /* FIXME: what about when f_type is not an integral type? +- deal with that if/when it's encountered. */ +- switch (fs_buf.f_type) ++ switch (filesystem_type (p)) + { + case S_MAGIC_TMPFS: + case S_MAGIC_NFS: +@@ -701,133 +803,58 @@ dirent_inode_sort_may_be_useful (int dir_fd) + } + } + +-/* Given a file descriptor DIR_FD open on a directory D, +- return true if it is valid to apply the leaf-optimization +- technique of counting directories in D via stat.st_nlink. */ +-static bool +-leaf_optimization_applies (int dir_fd) ++/* Given an FTS entry P for a directory D, ++ return true if it is both useful and valid to apply leaf optimization. ++ The optimization is useful only for file systems that lack usable ++ dirent.d_type info. The optimization is valid if an st_nlink value ++ of at least MIN_DIR_NLINK is an upper bound on the number of ++ subdirectories of D, counting "." and ".." as subdirectories. */ ++static enum leaf_optimization ++leaf_optimization (FTSENT const *p) + { +- struct statfs fs_buf; +- +- /* If fstatfs fails, assume we can't use the optimization. */ +- if (fstatfs (dir_fd, &fs_buf) != 0) +- return false; +- +- /* FIXME: do we need to detect AFS mount points? I doubt it, +- unless fstatfs can report S_MAGIC_REISERFS for such a directory. */ +- +- switch (fs_buf.f_type) ++ switch (filesystem_type (p)) + { +- case S_MAGIC_NFS: +- /* NFS provides usable dirent.d_type but not necessarily for all entries +- of large directories. See . */ +- return true; +- +- /* List here the file system types that lack usable dirent.d_type ++ /* List here the file system types that may lack usable dirent.d_type + info, yet for which the optimization does apply. */ + case S_MAGIC_REISERFS: +- case S_MAGIC_XFS: +- return true; +- ++ case S_MAGIC_XFS: /* XFS lacked it until 2013-08-22 commit. */ ++ return NOSTAT_LEAF_OPTIMIZATION; ++ ++ case 0: ++ /* Leaf optimization is unsafe if the file system type is unknown. */ ++ FALLTHROUGH; ++ case S_MAGIC_AFS: ++ /* Although AFS mount points are not counted in st_nlink, they ++ act like directories. See . */ ++ FALLTHROUGH; ++ case S_MAGIC_NFS: ++ /* NFS provides usable dirent.d_type but not necessarily for all entries ++ of large directories, so as per ++ NFS should return true. However st_nlink values are not accurate on ++ all implementations as per . */ ++ FALLTHROUGH; + case S_MAGIC_PROC: +- /* Explicitly listing this or any other file system type for which +- the optimization is not applicable is not necessary, but we leave +- it here to document the risk. Per http://bugs.debian.org/143111, +- /proc may have bogus stat.st_nlink values. */ +- /* fall through */ ++ /* Per /proc ++ may have bogus stat.st_nlink values. */ ++ return NO_LEAF_OPTIMIZATION; ++ + default: +- return false; ++ return OK_LEAF_OPTIMIZATION; + } + } + + #else + static bool +-dirent_inode_sort_may_be_useful (int dir_fd _GL_UNUSED) { return true; } +-static bool +-leaf_optimization_applies (int dir_fd _GL_UNUSED) { return false; } +-#endif +- +-/* link-count-optimization entry: +- map a stat.st_dev number to a boolean: leaf_optimization_works */ +-struct LCO_ent +-{ +- dev_t st_dev; +- bool opt_ok; +-}; +- +-/* Use a tiny initial size. If a traversal encounters more than +- a few devices, the cost of growing/rehashing this table will be +- rendered negligible by the number of inodes processed. */ +-enum { LCO_HT_INITIAL_SIZE = 13 }; +- +-static size_t +-LCO_hash (void const *x, size_t table_size) +-{ +- struct LCO_ent const *ax = x; +- return (uintmax_t) ax->st_dev % table_size; +-} +- +-static bool +-LCO_compare (void const *x, void const *y) ++dirent_inode_sort_may_be_useful (FTSENT const *p _GL_UNUSED) + { +- struct LCO_ent const *ax = x; +- struct LCO_ent const *ay = y; +- return ax->st_dev == ay->st_dev; ++ return true; + } +- +-/* Ask the same question as leaf_optimization_applies, but query +- the cache first (FTS.fts_leaf_optimization_works_ht), and if necessary, +- update that cache. */ +-static bool +-link_count_optimize_ok (FTSENT const *p) ++static enum leaf_optimization ++leaf_optimization (FTSENT const *p _GL_UNUSED) + { +- FTS *sp = p->fts_fts; +- Hash_table *h = sp->fts_leaf_optimization_works_ht; +- struct LCO_ent tmp; +- struct LCO_ent *ent; +- bool opt_ok; +- struct LCO_ent *t2; +- +- /* If we're not in CWDFD mode, don't bother with this optimization, +- since the caller is not serious about performance. */ +- if (!ISSET(FTS_CWDFD)) +- return false; +- +- /* map st_dev to the boolean, leaf_optimization_works */ +- if (h == NULL) +- { +- h = sp->fts_leaf_optimization_works_ht +- = hash_initialize (LCO_HT_INITIAL_SIZE, NULL, LCO_hash, +- LCO_compare, free); +- if (h == NULL) +- return false; +- } +- tmp.st_dev = p->fts_statp->st_dev; +- ent = hash_lookup (h, &tmp); +- if (ent) +- return ent->opt_ok; +- +- /* Look-up failed. Query directly and cache the result. */ +- t2 = malloc (sizeof *t2); +- if (t2 == NULL) +- return false; +- +- /* Is it ok to perform the optimization in the dir, FTS_CWD_FD? */ +- opt_ok = leaf_optimization_applies (sp->fts_cwd_fd); +- t2->opt_ok = opt_ok; +- t2->st_dev = p->fts_statp->st_dev; +- +- ent = hash_insert (h, t2); +- if (ent == NULL) +- { +- /* insertion failed */ +- free (t2); +- return false; +- } +- fts_assert (ent == t2); +- +- return opt_ok; ++ return NO_LEAF_OPTIMIZATION; + } ++#endif + + /* + * Special case of "/" at the end of the file name so that slashes aren't +@@ -1014,13 +1041,11 @@ check_for_dir: + if (p->fts_statp->st_size == FTS_STAT_REQUIRED) + { + FTSENT *parent = p->fts_parent; +- if (FTS_ROOTLEVEL < p->fts_level +- /* ->fts_n_dirs_remaining is not valid +- for command-line-specified names. */ +- && parent->fts_n_dirs_remaining == 0 ++ if (parent->fts_n_dirs_remaining == 0 + && ISSET(FTS_NOSTAT) + && ISSET(FTS_PHYSICAL) +- && link_count_optimize_ok (parent)) ++ && (leaf_optimization (parent) ++ == NOSTAT_LEAF_OPTIMIZATION)) + { + /* nothing more needed */ + } +@@ -1029,7 +1054,8 @@ check_for_dir: + p->fts_info = fts_stat(sp, p, false); + if (S_ISDIR(p->fts_statp->st_mode) + && p->fts_level != FTS_ROOTLEVEL +- && parent->fts_n_dirs_remaining) ++ && 0 < parent->fts_n_dirs_remaining ++ && parent->fts_n_dirs_remaining != (nlink_t) -1) + parent->fts_n_dirs_remaining--; + } + } +@@ -1298,8 +1324,6 @@ fts_build (register FTS *sp, int type) + bool descend; + bool doadjust; + ptrdiff_t level; +- nlink_t nlinks; +- bool nostat; + size_t len, maxlen, new_len; + char *cp; + int dir_fd; +@@ -1370,24 +1394,6 @@ fts_build (register FTS *sp, int type) + max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES; + + /* +- * Nlinks is the number of possible entries of type directory in the +- * directory if we're cheating on stat calls, 0 if we're not doing +- * any stat calls at all, (nlink_t) -1 if we're statting everything. +- */ +- if (type == BNAMES) { +- nlinks = 0; +- /* Be quiet about nostat, GCC. */ +- nostat = false; +- } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { +- nlinks = (cur->fts_statp->st_nlink +- - (ISSET(FTS_SEEDOT) ? 0 : 2)); +- nostat = true; +- } else { +- nlinks = -1; +- nostat = false; +- } +- +- /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. +@@ -1408,15 +1414,22 @@ fts_build (register FTS *sp, int type) + the required dirp and dir_fd. */ + descend = true; + } +- else if (nlinks || type == BREAD) { ++ else ++ { ++ /* Try to descend unless it is a names-only fts_children, ++ or the directory is known to lack subdirectories. */ ++ descend = (type != BNAMES ++ && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL) ++ && ! ISSET (FTS_SEEDOT) ++ && cur->fts_statp->st_nlink == MIN_DIR_NLINK ++ && (leaf_optimization (cur) ++ != NO_LEAF_OPTIMIZATION))); ++ if (descend || type == BREAD) ++ { + if (ISSET(FTS_CWDFD)) +- { +- dir_fd = dup (dir_fd); +- if (0 <= dir_fd) +- set_cloexec_flag (dir_fd, true); +- } ++ dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); + if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) { +- if (nlinks && type == BREAD) ++ if (descend && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = false; +@@ -1426,8 +1439,8 @@ fts_build (register FTS *sp, int type) + cur->fts_dirp = NULL; + } else + descend = true; +- } else +- descend = false; ++ } ++ } + + /* + * Figure out the max file name length that can be stored in the +@@ -1458,11 +1471,19 @@ fts_build (register FTS *sp, int type) + tail = NULL; + nitems = 0; + while (cur->fts_dirp) { +- bool is_dir; + size_t d_namelen; ++ __set_errno (0); + struct dirent *dp = readdir(cur->fts_dirp); +- if (dp == NULL) ++ if (dp == NULL) { ++ if (errno) { ++ cur->fts_errno = errno; ++ /* If we've not read any items yet, treat ++ the error as if we can't access the dir. */ ++ cur->fts_info = (continue_readdir || nitems) ++ ? FTS_ERR : FTS_DNR; ++ } + break; ++ } + if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) + continue; + +@@ -1550,19 +1571,10 @@ mem1: saved_errno = errno; + to caller, when possible. */ + set_stat_type (p->fts_statp, D_TYPE (dp)); + fts_set_stat_required(p, !skip_stat); +- is_dir = (ISSET(FTS_PHYSICAL) +- && DT_MUST_BE(dp, DT_DIR)); + } else { + p->fts_info = fts_stat(sp, p, false); +- is_dir = (p->fts_info == FTS_D +- || p->fts_info == FTS_DC +- || p->fts_info == FTS_DOT); + } + +- /* Decrement link count if applicable. */ +- if (nlinks > 0 && is_dir) +- nlinks -= nostat; +- + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) +@@ -1621,7 +1633,8 @@ mem1: saved_errno = errno; + + /* If didn't find anything, return NULL. */ + if (!nitems) { +- if (type == BREAD) ++ if (type == BREAD ++ && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR) + cur->fts_info = FTS_DP; + fts_lfree(head); + return (NULL); +@@ -1633,8 +1646,7 @@ mem1: saved_errno = errno; + inode numbers. */ + if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD + && !sp->fts_compar +- && ISSET (FTS_CWDFD) +- && dirent_inode_sort_may_be_useful (sp->fts_cwd_fd)) { ++ && dirent_inode_sort_may_be_useful (cur)) { + sp->fts_compar = fts_compare_ino; + head = fts_sort (sp, head, nitems); + sp->fts_compar = NULL; +@@ -1757,7 +1769,7 @@ fd_ring_check (FTS const *sp) + I_ring fd_w = sp->fts_fd_ring; + + int cwd_fd = sp->fts_cwd_fd; +- cwd_fd = dup (cwd_fd); ++ cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); + char *dot = getcwdat (cwd_fd, NULL, 0); + error (0, 0, "===== check ===== cwd: %s", dot); + free (dot); +@@ -1766,7 +1778,8 @@ fd_ring_check (FTS const *sp) + int fd = i_ring_pop (&fd_w); + if (0 <= fd) + { +- int parent_fd = openat (cwd_fd, "..", O_SEARCH | O_NOATIME); ++ int open_flags = O_SEARCH | O_CLOEXEC | O_NOATIME; ++ int parent_fd = openat (cwd_fd, "..", open_flags); + if (parent_fd < 0) + { + // Warn? +@@ -1795,7 +1808,6 @@ internal_function + fts_stat(FTS *sp, register FTSENT *p, bool follow) + { + struct stat *sbp = p->fts_statp; +- int saved_errno; + + if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW)) + follow = true; +@@ -1807,13 +1819,12 @@ fts_stat(FTS *sp, register FTSENT *p, bool follow) + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (stat(p->fts_accpath, sbp)) { +- saved_errno = errno; + if (errno == ENOENT + && lstat(p->fts_accpath, sbp) == 0) { + __set_errno (0); + return (FTS_SLNONE); + } +- p->fts_errno = saved_errno; ++ p->fts_errno = errno; + goto err; + } + } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp, +@@ -1824,8 +1835,11 @@ err: memset(sbp, 0, sizeof(struct stat)); + } + + if (S_ISDIR(sbp->st_mode)) { +- p->fts_n_dirs_remaining = (sbp->st_nlink +- - (ISSET(FTS_SEEDOT) ? 0 : 2)); ++ p->fts_n_dirs_remaining ++ = ((sbp->st_nlink < MIN_DIR_NLINK ++ || p->fts_level <= FTS_ROOTLEVEL) ++ ? -1 ++ : sbp->st_nlink - (ISSET (FTS_SEEDOT) ? 0 : MIN_DIR_NLINK)); + if (ISDOT(p->fts_name)) { + /* Command-line "." and ".." are real directories. */ + return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT); +@@ -1914,17 +1928,7 @@ fts_alloc (FTS *sp, const char *name, register size_t namelen) + * The file name is a variable length array. Allocate the FTSENT + * structure and the file name in one chunk. + */ +- len = offsetof(FTSENT, fts_name) + namelen + 1; +- /* Align the allocation size so that it works for FTSENT, +- so that trailing padding may be referenced by direct access +- to the flexible array members, without triggering undefined behavior +- by accessing bytes beyond the heap allocation. This implicit access +- was seen for example with ISDOT() and GCC 5.1.1 at -O2. +- Do not use alignof (FTSENT) here, since C11 prohibits +- taking the alignment of a structure containing a flexible +- array member. */ +- len += alignof (max_align_t) - 1; +- len &= ~ (alignof (max_align_t) - 1); ++ len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1); + if ((p = malloc(len)) == NULL) + return (NULL); + +diff --git a/gl/lib/fts_.h b/gl/lib/fts_.h +index b9a3f12..70cc9e3 100644 +--- a/gl/lib/fts_.h ++++ b/gl/lib/fts_.h +@@ -1,6 +1,6 @@ + /* Traverse a file hierarchy. + +- Copyright (C) 2004-2015 Free Software Foundation, Inc. ++ Copyright (C) 2004-2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by +@@ -13,7 +13,7 @@ + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License +- along with this program. If not, see . */ ++ along with this program. If not, see . */ + + /* + * Copyright (c) 1989, 1993 +@@ -220,7 +220,11 @@ typedef struct _ftsent { + ptrdiff_t fts_level; /* depth (-1 to N) */ + + size_t fts_namelen; /* strlen(fts_name) */ +- nlink_t fts_n_dirs_remaining; /* count down from st_nlink */ ++ ++ /* If not (nlink_t) -1, an upper bound on the number of ++ remaining subdirectories of interest. If this becomes ++ zero, some work can be avoided. */ ++ nlink_t fts_n_dirs_remaining; + + # define FTS_D 1 /* preorder directory */ + # define FTS_DC 2 /* directory that causes cycles */ +-- +2.13.6 + + +From ea88dd373c60feab541fe037369805f326dc3494 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Mon, 12 Feb 2018 18:58:30 +0100 +Subject: [PATCH 2/2] fts: remove dependency on gnulib's fleximember.h + +... by reverting upstream commit edb9d82948cb23f67a19e1b435047a0570225df3 +--- + gl/lib/fts.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/gl/lib/fts.c b/gl/lib/fts.c +index bfa73e3..c37ebe2 100644 +--- a/gl/lib/fts.c ++++ b/gl/lib/fts.c +@@ -71,7 +71,6 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; + + #if ! _LIBC + # include "fcntl--.h" +-# include "flexmember.h" + # include "openat.h" + # include "same-inode.h" + #endif +@@ -1928,7 +1927,17 @@ fts_alloc (FTS *sp, const char *name, register size_t namelen) + * The file name is a variable length array. Allocate the FTSENT + * structure and the file name in one chunk. + */ +- len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1); ++ len = offsetof(FTSENT, fts_name) + namelen + 1; ++ /* Align the allocation size so that it works for FTSENT, ++ so that trailing padding may be referenced by direct access ++ to the flexible array members, without triggering undefined behavior ++ by accessing bytes beyond the heap allocation. This implicit access ++ was seen for example with ISDOT() and GCC 5.1.1 at -O2. ++ Do not use alignof (FTSENT) here, since C11 prohibits ++ taking the alignment of a structure containing a flexible ++ array member. */ ++ len += alignof (max_align_t) - 1; ++ len &= ~ (alignof (max_align_t) - 1); + if ((p = malloc(len)) == NULL) + return (NULL); + +-- +2.13.6 + diff --git a/findutils-4.6.0-leaf-opt.patch b/findutils-4.6.0-leaf-opt.patch new file mode 100644 index 0000000..7fb3035 --- /dev/null +++ b/findutils-4.6.0-leaf-opt.patch @@ -0,0 +1,83 @@ +From 1328926a705fdb4728c1f255dd368de928736d39 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Fri, 25 Sep 2015 16:09:39 +0200 +Subject: [PATCH 1/2] fts: introduce the FTS_NOLEAF flag + +The flag is needed to implement the -noleaf option of find. +* lib/fts.c (link_count_optimize_ok): Implement the FTS_NOLEAF flag. +* lib/fts_.h (FTS_NOLEAF): New macro, shifted conflicting constants. +--- + gl/lib/fts.c | 4 ++++ + gl/lib/fts_.h | 12 +++++++++--- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/gl/lib/fts.c b/gl/lib/fts.c +index d2d404f..808466f 100644 +--- a/gl/lib/fts.c ++++ b/gl/lib/fts.c +@@ -736,6 +736,10 @@ filesystem_type (FTSENT const *p) + struct dev_type *ent; + struct statfs fs_buf; + ++ if (ISSET(FTS_NOLEAF)) ++ /* leaf optimization explicitly disabled by the FTS_NOLEAF flag */ ++ return 0; ++ + /* If we're not in CWDFD mode, don't bother with this optimization, + since the caller is not serious about performance. */ + if (!ISSET (FTS_CWDFD)) +diff --git a/gl/lib/fts_.h b/gl/lib/fts_.h +index 63d4b74..f1d519b 100644 +--- a/gl/lib/fts_.h ++++ b/gl/lib/fts_.h +@@ -155,10 +155,16 @@ typedef struct { + from input path names during fts_open initialization. */ + # define FTS_VERBATIM 0x1000 + +-# define FTS_OPTIONMASK 0x1fff /* valid user option mask */ ++ /* Disable leaf optimization (which eliminates stat() calls during traversal, ++ based on the count of nested directories stored in stat.st_nlink of each ++ directory). Note that the optimization is by default enabled only for ++ selected file systems, and only if the FTS_CWDFD flag is set. */ ++# define FTS_NOLEAF 0x2000 + +-# define FTS_NAMEONLY 0x2000 /* (private) child names only */ +-# define FTS_STOP 0x4000 /* (private) unrecoverable error */ ++# define FTS_OPTIONMASK 0x3fff /* valid user option mask */ ++ ++# define FTS_NAMEONLY 0x4000 /* (private) child names only */ ++# define FTS_STOP 0x8000 /* (private) unrecoverable error */ + int fts_options; /* fts_open options, global flags */ + + /* Map a directory's device number to a boolean. The boolean is +-- +2.5.0 + + +From c186934e6e37ddadf7511abb9b1045192757618e Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Fri, 25 Sep 2015 19:13:15 +0200 +Subject: [PATCH 2/2] ftsfind: propagate the -noleaf option to FTS + +* find/ftsfind.c (find): Propagate the -noleaf option to FTS. +--- + find/ftsfind.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/find/ftsfind.c b/find/ftsfind.c +index 5159470..e34b672 100644 +--- a/find/ftsfind.c ++++ b/find/ftsfind.c +@@ -559,6 +559,9 @@ find (char *arg) + if (options.stay_on_filesystem) + ftsoptions |= FTS_XDEV; + ++ if (options.no_leaf_check) ++ ftsoptions |= FTS_NOLEAF; ++ + p = fts_open (arglist, ftsoptions, NULL); + if (NULL == p) + { +-- +2.5.0 + diff --git a/findutils.spec b/findutils.spec index 260bdb3..7e38d95 100644 --- a/findutils.spec +++ b/findutils.spec @@ -1,7 +1,7 @@ Summary: The GNU versions of find utilities (find and xargs) Name: findutils Version: 4.6.0 -Release: 15%{?dist} +Release: 16%{?dist} Epoch: 1 License: GPLv3+ Group: Applications/File @@ -27,15 +27,18 @@ Patch5: findutils-4.6.0-man-exec.patch # make sure that find -exec + passes all arguments (upstream bug #48030) Patch6: findutils-4.6.0-exec-args.patch -# implement the -noleaf option of find (#1252549) -Patch8: findutils-4.5.15-leaf-opt.patch - # avoid SIGSEGV in case the internal -noop option is used (#1346471) Patch9: findutils-4.6.0-internal-noop.patch # test-lock: disable the rwlock test Patch10: findutils-4.6.0-test-lock.patch +# import gnulib's FTS module from upstream commit 281b825e (#1544429) +Patch11: findutils-4.6.0-fts-update.patch + +# implement the -noleaf option of find (#1252549) +Patch12: findutils-4.6.0-leaf-opt.patch + Requires(post): /sbin/install-info Requires(preun): /sbin/install-info Conflicts: filesystem < 3 @@ -136,6 +139,9 @@ fi %{_infodir}/find-maint.info.gz %changelog +* Mon Feb 12 2018 Kamil Dudka - 1:4.6.0-16 +- import gnulib's FTS module from upstream commit 281b825e (#1544429) + * Wed Feb 07 2018 Fedora Release Engineering - 1:4.6.0-15 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild