Subject: bug#10305: coreutils-8.14, "rm -r" fails with EBADF

From: Paul Eggert <[email-masked]>
Date: Wed, 21 Dec 2011 10:42:04 -0800

On 12/21/11 08:27, Eric Blake wrote:
> maybe we should wrap opendir() so that the gnulib rpl_opendir()
> always opens a directory at the same time

That sounds a bit drastic, but it may be necessary.

How about this idea instead?  Use the following patch,
so that fts_build does not assume that dirfd works.

POSIX does not require dirfd to work, and NonStop is
within its rights to not support dirfd, and in this particular
case I think the code will work without it (albeit less
reliably in the presence of very large file trees).

Joachim, does this patch fix "rm" for you?  If not, what
problems does it run into?  (I have not audited the code
for all uses of dirfd, just for this one.)

diff --git a/lib/fts.c b/lib/fts.c
index ccd1980..4dc6809 100644
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -1283,7 +1283,7 @@ fts_build (register FTS *sp, int type)
         FTSENT *cur = sp->fts_cur;
         bool continue_readdir = !!cur->fts_dirp;
 
-        /* When cur->fts_dirp is non-NULL, that means we should
+        /* If cur->fts_dirp is non-NULL and dirfd is supported,
            continue calling readdir on that existing DIR* pointer
            rather than opening a new one.  */
         if (continue_readdir)
@@ -1292,16 +1292,20 @@ fts_build (register FTS *sp, int type)
             dir_fd = dirfd (dp);
             if (dir_fd < 0)
               {
+                int dirfd_errno = errno;
                 closedir_and_clear (cur->fts_dirp);
                 if (type == BREAD)
                   {
                     cur->fts_info = FTS_DNR;
                     cur->fts_errno = errno;
                   }
-                return NULL;
+                if (dirfd_errno != ENOTSUP)
+                  return NULL;
+                continue_readdir = false;
               }
           }
-        else
+
+        if (! continue_readdir)
           {
             /* Open the directory for reading.  If this fails, we're done.
                If being called from fts_read, set the fts_info field. */