- cur->fts_info = fts_stat(sp, cur, false);
-
- /*
- * 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.
- * We won't be able to stat anything, but we can still return the
- * names themselves. Note, that since fts_read won't be able to
- * chdir into the directory, it will have to return different file
- * names than before, i.e. "a/b" instead of "b". Since the node
- * has already been visited in pre-order, have to wait until the
- * post-order visit to return the error. There is a special case
- * here, if there was nothing to stat then it's not an error to
- * not be able to stat. This is all fairly nasty. If a program
- * needed sorted entries or stat information, they had better be
- * checking FTS_NS on the returned nodes.
- */
- if (nlinks || type == BREAD) {
- int dir_fd = dirfd(dirp);
- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
- dir_fd = dup (dir_fd);
- if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
- if (nlinks && type == BREAD)
- cur->fts_errno = errno;
- cur->fts_flags |= FTS_DONTCHDIR;
- descend = false;
- closedir(dirp);
- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
- close (dir_fd);
- dirp = NULL;
- } else
- descend = true;
- } else
- descend = false;
-
- /*
- * Figure out the max file name length that can be stored in the
- * current buffer -- the inner loop allocates more space as necessary.
- * We really wouldn't have to do the maxlen calculations here, we
- * could do them in fts_read before returning the name, but it's a
- * lot easier here since the length is part of the dirent structure.
- *
- * If not changing directories set a pointer so that can just append
- * each new component into the file name.
- */
- len = NAPPEND(cur);
- if (ISSET(FTS_NOCHDIR)) {
- cp = sp->fts_path + len;
- *cp++ = '/';
- } else {
- /* GCC, you're too verbose. */
- cp = NULL;
- }
- len++;
- maxlen = sp->fts_pathlen - len;
-
- level = cur->fts_level + 1;
-
- /* Read the directory, attaching each entry to the `link' pointer. */
- doadjust = false;
- for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
- bool is_dir;
-
- if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
- continue;
-
- if ((p = fts_alloc (sp, dp->d_name,
- _D_EXACT_NAMLEN (dp))) == NULL)
- goto mem1;
- if (_D_EXACT_NAMLEN (dp) >= maxlen) {
- /* include space for NUL */
- oldaddr = sp->fts_path;
- if (! fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
- /*
- * No more memory. Save
- * errno, free up the current structure and the
- * structures already allocated.
- */
-mem1: saved_errno = errno;
- free(p);
- fts_lfree(head);
- closedir(dirp);
- cur->fts_info = FTS_ERR;
- SET(FTS_STOP);
- __set_errno (saved_errno);
- return (NULL);
- }
- /* Did realloc() change the pointer? */
- if (oldaddr != sp->fts_path) {
- doadjust = true;
- if (ISSET(FTS_NOCHDIR))
- cp = sp->fts_path + len;
- }
- maxlen = sp->fts_pathlen - len;
- }
-
- new_len = len + _D_EXACT_NAMLEN (dp);
- if (new_len < len) {
- /*
- * In the unlikely event that we would end up
- * with a file name longer than SIZE_MAX, free up
- * the current structure and the structures already
- * allocated, then error out with ENAMETOOLONG.
- */
- free(p);
- fts_lfree(head);
- closedir(dirp);
- cur->fts_info = FTS_ERR;
- SET(FTS_STOP);
- __set_errno (ENAMETOOLONG);
- return (NULL);
- }
- p->fts_level = level;
- p->fts_parent = sp->fts_cur;
- p->fts_pathlen = new_len;
+ cur->fts_info = fts_stat(sp, cur, false);
+ else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK) {
+ /* Now read the stat info again after opening a directory to
+ * reveal eventual changes caused by a submount triggered by
+ * the traversal. But do it only for utilities which use
+ * FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
+ * benefit/suffer from this feature for now.
+ */
+ LEAVE_DIR (sp, cur, "4");
+ fts_stat (sp, cur, false);
+ if (! enter_dir (sp, cur)) {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
+ }
+
+ /*
+ * 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.
+ * We won't be able to stat anything, but we can still return the
+ * names themselves. Note, that since fts_read won't be able to
+ * chdir into the directory, it will have to return different file
+ * names than before, i.e. "a/b" instead of "b". Since the node
+ * has already been visited in pre-order, have to wait until the
+ * post-order visit to return the error. There is a special case
+ * here, if there was nothing to stat then it's not an error to
+ * not be able to stat. This is all fairly nasty. If a program
+ * needed sorted entries or stat information, they had better be
+ * checking FTS_NS on the returned nodes.
+ */
+ if (nlinks || type == BREAD) {
+ if (ISSET(FTS_CWDFD))
+ {
+ dir_fd = dup (dir_fd);
+ if (0 <= dir_fd)
+ set_cloexec_flag (dir_fd, true);
+ }
+ if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
+ if (nlinks && type == BREAD)
+ cur->fts_errno = errno;
+ cur->fts_flags |= FTS_DONTCHDIR;
+ descend = false;
+ closedir(dirp);
+ if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
+ close (dir_fd);
+ dirp = NULL;
+ } else
+ descend = true;
+ } else
+ descend = false;
+
+ /*
+ * Figure out the max file name length that can be stored in the
+ * current buffer -- the inner loop allocates more space as necessary.
+ * We really wouldn't have to do the maxlen calculations here, we
+ * could do them in fts_read before returning the name, but it's a
+ * lot easier here since the length is part of the dirent structure.
+ *
+ * If not changing directories set a pointer so that can just append
+ * each new component into the file name.
+ */
+ len = NAPPEND(cur);
+ if (ISSET(FTS_NOCHDIR)) {
+ cp = sp->fts_path + len;
+ *cp++ = '/';
+ } else {
+ /* GCC, you're too verbose. */
+ cp = NULL;
+ }
+ len++;
+ maxlen = sp->fts_pathlen - len;
+
+ level = cur->fts_level + 1;
+
+ /* Read the directory, attaching each entry to the `link' pointer. */
+ doadjust = false;
+ for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
+ bool is_dir;
+
+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+ continue;
+
+ if ((p = fts_alloc (sp, dp->d_name,
+ _D_EXACT_NAMLEN (dp))) == NULL)
+ goto mem1;
+ if (_D_EXACT_NAMLEN (dp) >= maxlen) {
+ /* include space for NUL */
+ oldaddr = sp->fts_path;
+ if (! fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
+ /*
+ * No more memory. Save
+ * errno, free up the current structure and the
+ * structures already allocated.
+ */
+mem1: saved_errno = errno;
+ free(p);
+ fts_lfree(head);
+ closedir(dirp);
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ __set_errno (saved_errno);
+ return (NULL);
+ }
+ /* Did realloc() change the pointer? */
+ if (oldaddr != sp->fts_path) {
+ doadjust = true;
+ if (ISSET(FTS_NOCHDIR))
+ cp = sp->fts_path + len;
+ }
+ maxlen = sp->fts_pathlen - len;
+ }
+
+ new_len = len + _D_EXACT_NAMLEN (dp);
+ if (new_len < len) {
+ /*
+ * In the unlikely event that we would end up
+ * with a file name longer than SIZE_MAX, free up
+ * the current structure and the structures already
+ * allocated, then error out with ENAMETOOLONG.
+ */
+ free(p);
+ fts_lfree(head);
+ closedir(dirp);
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ __set_errno (ENAMETOOLONG);
+ return (NULL);
+ }
+ p->fts_level = level;
+ p->fts_parent = sp->fts_cur;
+ p->fts_pathlen = new_len;