[v3,1/7] linux: Do not skip entries with zero d_ino values [BZ #12165]

Message ID 20201021141542.2003377-2-adhemerval.zanella@linaro.org
State Not applicable
Headers
Series Fix getdents{64} regression on some FS |

Commit Message

Adhemerval Zanella Oct. 21, 2020, 2:15 p.m. UTC
  According to Linux commit 2adc376c55194 (vfs: avoid creation of inode
number 0 in get_next_ino) Linux did not treat d_ino == 0 as a special
case (it is a valid inode number).

This patch fixes readdir{64} by not ignoring entried with d_ino being
0.

Checked on x86_64-linux-gnu and i686-linux-gnu.
---
 sysdeps/unix/sysv/linux/readdir.c   | 59 +++++++++++------------------
 sysdeps/unix/sysv/linux/readdir64.c | 59 +++++++++++------------------
 2 files changed, 44 insertions(+), 74 deletions(-)
  

Patch

diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c
index ca2a8964e9..1a9820e475 100644
--- a/sysdeps/unix/sysv/linux/readdir.c
+++ b/sysdeps/unix/sysv/linux/readdir.c
@@ -25,51 +25,36 @@ 
 struct dirent *
 __readdir_unlocked (DIR *dirp)
 {
-  struct dirent *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
-  do
+  if (dirp->offset >= dirp->size)
     {
-      size_t reclen;
-
-      if (dirp->offset >= dirp->size)
+      /* We've emptied out our buffer.  Refill it.  */
+      ssize_t bytes = __getdents (dirp->fd, dirp->data, dirp->allocation);
+      if (bytes <= 0)
 	{
-	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __getdents (dirp->fd, dirp->data, maxread);
-	  if (bytes <= 0)
-	    {
-	      /* On some systems getdents fails with ENOENT when the
-		 open directory has been rmdir'd already.  POSIX.1
-		 requires that we treat this condition like normal EOF.  */
-	      if (bytes < 0 && errno == ENOENT)
-		bytes = 0;
-
-	      /* Don't modifiy errno when reaching EOF.  */
-	      if (bytes == 0)
-		__set_errno (saved_errno);
-	      dp = NULL;
-	      break;
-	    }
-	  dirp->size = (size_t) bytes;
-
-	  /* Reset the offset into the buffer.  */
-	  dirp->offset = 0;
+	  /* On some systems getdents fails with ENOENT when the
+	     open directory has been rmdir'd already.  POSIX.1
+	     requires that we treat this condition like normal EOF.  */
+	  if (bytes < 0 && errno == ENOENT)
+	    bytes = 0;
+
+	  /* Don't modifiy errno when reaching EOF.  */
+	  if (bytes == 0)
+	    __set_errno (saved_errno);
+	  return NULL;
 	}
+      dirp->size = bytes;
 
-      dp = (struct dirent *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
+      /* Reset the offset into the buffer.  */
+      dirp->offset = 0;
+    }
 
-      dirp->offset += reclen;
+  struct dirent *dp = (struct dirent *) &dirp->data[dirp->offset];
 
-      dirp->filepos = dp->d_off;
+  dirp->offset += dp->d_reclen;
 
-      /* Skip deleted files.  */
-    } while (dp->d_ino == 0);
+  dirp->filepos = dp->d_off;
 
   return dp;
 }
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index 1aa6e2664f..d35a4595f6 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -30,55 +30,40 @@ 
 struct dirent64 *
 __readdir64 (DIR *dirp)
 {
-  struct dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
 #if IS_IN (libc)
   __libc_lock_lock (dirp->lock);
 #endif
 
-  do
+  if (dirp->offset >= dirp->size)
     {
-      size_t reclen;
-
-      if (dirp->offset >= dirp->size)
+      /* We've emptied out our buffer.  Refill it.  */
+      ssize_t bytes = __getdents64 (dirp->fd, dirp->data, dirp->allocation);
+      if (bytes <= 0)
 	{
-	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __getdents64 (dirp->fd, dirp->data, maxread);
-	  if (bytes <= 0)
-	    {
-	      /* On some systems getdents fails with ENOENT when the
-		 open directory has been rmdir'd already.  POSIX.1
-		 requires that we treat this condition like normal EOF.  */
-	      if (bytes < 0 && errno == ENOENT)
-		bytes = 0;
-
-	      /* Don't modifiy errno when reaching EOF.  */
-	      if (bytes == 0)
-		__set_errno (saved_errno);
-	      dp = NULL;
-	      break;
-	    }
-	  dirp->size = (size_t) bytes;
-
-	  /* Reset the offset into the buffer.  */
-	  dirp->offset = 0;
+	  /* On some systems getdents fails with ENOENT when the
+	     open directory has been rmdir'd already.  POSIX.1
+	     requires that we treat this condition like normal EOF.  */
+	  if (bytes < 0 && errno == ENOENT)
+	    bytes = 0;
+
+	  /* Don't modifiy errno when reaching EOF.  */
+	  if (bytes == 0)
+	    __set_errno (saved_errno);
+	  return NULL;
 	}
+      dirp->size = bytes;
 
-      dp = (struct dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
+      /* Reset the offset into the buffer.  */
+      dirp->offset = 0;
+   }
 
-      dirp->offset += reclen;
+  struct dirent64 *dp = (struct dirent64 *) &dirp->data[dirp->offset];
 
-      dirp->filepos = dp->d_off;
+  dirp->offset += dp->d_reclen;
 
-      /* Skip deleted files.  */
-    } while (dp->d_ino == 0);
+  dirp->filepos = dp->d_off;
 
 #if IS_IN (libc)
   __libc_lock_unlock (dirp->lock);