2022-12-06 Paul Eggert <eggert@cs.ucla.edu>
+ fts: fix race + mishandling of fstatat failure
+ I hope this fixes a Luke Dashjr coreutils bug report about ext4
+ ramdisks; see “9.1: du Aborted (corrupt filesystem)”
+ <https://debbugs.gnu.org/59821>.
+ * lib/fts.c (fts_build): Fix two bugs. First, fts_stat was being
+ called without checking its return value, causing a later abort.
+ Second, there was a race between opening a directory and statting
+ it, fixed by using fstat on the file descriptor rather than
+ fstatat on the directory name.
+
fts: omit goto break_without_closedir
* lib/fts.c (fts_build): Refactor to omit goto.
/* Rather than calling fts_stat for each and every entry encountered
in the readdir loop (below), stat each directory only right after
opening it. */
- if (cur->fts_info == FTS_NSOK)
- 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
+ bool stat_optimization = cur->fts_info == FTS_NSOK;
+
+ if (stat_optimization
+ /* Also 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))
+ || ISSET (FTS_TIGHT_CYCLE_CHECK))
+ {
+ if (!stat_optimization)
+ LEAVE_DIR (sp, cur, "4");
+ if (fstat (dir_fd, cur->fts_statp) != 0)
+ {
+ int fstat_errno = errno;
+ closedir_and_clear (cur->fts_dirp);
+ if (type == BREAD)
+ {
+ cur->fts_errno = fstat_errno;
+ cur->fts_info = FTS_NS;
+ }
+ __set_errno (fstat_errno);
+ return NULL;
+ }
+ if (stat_optimization)
+ cur->fts_info = FTS_D;
+ else if (! enter_dir (sp, cur))
{
+ closedir_and_clear (cur->fts_dirp);
__set_errno (ENOMEM);
return NULL;
}