changeset 18338:7276fe79480b

fts: handle readdir() errors * lib/fts.c (fts_build): readdir(3) returns NULL when finished, but also upon error when it will also set errno. Therefore flag the error case from readdir(). We treat the case where no items are read the same as if the dir can't be accessed, i.e. by setting fts_errno to FTS_DNR. The bug was initially reported by Peter Benie http://bugzilla.opensuse.org/show_bug.cgi?id=984910 where it was mentioned that readdir() may fail when an NFS server has a poor readdir cookie implementation.
author Pádraig Brady <P@draigBrady.com>
date Wed, 22 Jun 2016 13:49:53 +0100
parents 19bad4ce2272
children f1df88870b64
files ChangeLog lib/fts.c
diffstat 2 files changed, 21 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Jun 24 13:35:12 2016 +0200
+++ b/ChangeLog	Wed Jun 22 13:49:53 2016 +0100
@@ -1,3 +1,12 @@
+2016-06-26  Pádraig Brady  <P@draigBrady.com>
+
+	fts: handle readdir() errors
+	* lib/fts.c (fts_build): readdir(3) returns NULL when finished,
+	but also upon error when it will also set errno.  Therefore
+	flag the error case from readdir().  We treat the case where
+	no items are read the same as if the dir can't be accessed,
+	i.e. by setting fts_errno to FTS_DNR.
+
 2016-06-24  Paul Eggert  <eggert@cs.ucla.edu>
 
 	intprops: port better to GCC 7
--- a/lib/fts.c	Fri Jun 24 13:35:12 2016 +0200
+++ b/lib/fts.c	Wed Jun 22 13:49:53 2016 +0100
@@ -1461,9 +1461,18 @@
         while (cur->fts_dirp) {
                 bool is_dir;
                 size_t d_namelen;
+                __set_errno (0);
                 struct dirent *dp = readdir(cur->fts_dirp);
-                if (dp == NULL)
+                if (dp == NULL) {
+                        if (errno) {
+                                cur->fts_errno = errno;
+                                /* If we've not read any items yet, treat
+                                   the error as if we can't access the dir.  */
+                                cur->fts_info = (continue_readdir || nitems)
+                                                ? FTS_ERR : FTS_DNR;
+                        }
                         break;
+                }
                 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
                         continue;
 
@@ -1622,7 +1631,8 @@
 
         /* If didn't find anything, return NULL. */
         if (!nitems) {
-                if (type == BREAD)
+                if (type == BREAD
+                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)
                         cur->fts_info = FTS_DP;
                 fts_lfree(head);
                 return (NULL);