* lib/fts.c (fts_open): Set rootparent n_dirs_remaining to -1 so that root need not be a special case later. (fts_read): Remove now-redundant test for fts_level. Do not assume that nlink_t is signed. (fts_build): Remove useless decrement of nlinks. (fts_stat): Avoid unlikely signed integer overflow later, if nlink_t is signed. --- ChangeLog | 9 +++++++++ lib/fts.c | 26 +++++++++----------------- lib/fts_.h | 6 +++++- 3 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 05ac15d93..da1fda1ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2017-07-24 Paul Eggert <egg...@cs.ucla.edu> + fts: nlink_t signedness fixups + * lib/fts.c (fts_open): Set rootparent n_dirs_remaining to -1 + so that root need not be a special case later. + (fts_read): Remove now-redundant test for fts_level. + Do not assume that nlink_t is signed. + (fts_build): Remove useless decrement of nlinks. + (fts_stat): Avoid unlikely signed integer overflow later, if + nlink_t is signed. + fts-tests: new module * modules/fts-tests, tests/test-fts.c: New files. diff --git a/lib/fts.c b/lib/fts.c index 3bac3244e..d5fc051a0 100644 --- a/lib/fts.c +++ b/lib/fts.c @@ -479,6 +479,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 @@ -1024,10 +1025,7 @@ 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)) @@ -1039,7 +1037,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--; } } @@ -1468,7 +1467,6 @@ 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); @@ -1569,19 +1567,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) @@ -1844,8 +1833,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 < 2 + || p->fts_level <= FTS_ROOTLEVEL) + ? -1 + : sbp->st_nlink - (ISSET (FTS_SEEDOT) ? 0 : 2)); if (ISDOT(p->fts_name)) { /* Command-line "." and ".." are real directories. */ return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT); diff --git a/lib/fts_.h b/lib/fts_.h index 46fd0df52..e8346dafb 100644 --- a/lib/fts_.h +++ b/lib/fts_.h @@ -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.3