Bug 194132

Summary: rm(1) is ignoring more errors than it should in rm_tree(..)
Product: Base System Reporter: Enji Cooper <ngie>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: New ---    
Severity: Affects Many People CC: bdrewery, jilles
Priority: ---    
Version: CURRENT   
Hardware: Any   
OS: Any   
See Also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=192490

Description Enji Cooper freebsd_committer 2014-10-04 05:44:00 UTC
This code in bin/rm/rm.c catches more errors than it should:

338         if (!fflag && errno)
339                 err(1, "fts_read");
340         fts_close(fts);
341 }

In particular, the code will mask errors if fts_read fails with anything other than EACCES, ENOENT, or EPERM according to the intent of -f in rm(1):

     -f      Attempt to remove the files without prompting for confirmation,
             regardless of the file's permissions.  If the file does not
             exist, do not display a diagnostic message or modify the exit
             status to reflect an error.  The -f option overrides any previous
             -i options.

Please see the related bug for more details.
Comment 1 Warner Losh freebsd_committer 2014-10-04 20:30:44 UTC
So how is this an actual bug?
Comment 2 Warner Losh freebsd_committer 2014-10-04 20:31:17 UTC
and don't assign me bugs that I have no interest in fixing,
Comment 3 Jilles Tjoelker freebsd_committer 2014-10-05 15:13:03 UTC
The problem here is in fts(3), not in rm(1). If fts_read() aborts the traversal because of a concurrent modification, that is a bug. If a concurrent rm -rf X/Y causes an rm -rf X to abort, this may cause X/Z to escape removal erroneously.

A possible fix is to use the pathnames to go back to grandparent directories if opening ".." fails (as with "..", st_dev and st_ino must be checked). A consequence is that the current directory and fts_accpath in the FTS_DP result will be different (fts_accpath will contain a slash, which it normally doesn't if fts is changing directory). Also, this will not work in deep directory trees with pathnames longer than PATH_MAX.