public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation
@ 2020-10-02 13:53 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2020-10-02 13:53 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=e9c542898335ba982c9ffcf0bf3fb7fe3953c523

commit e9c542898335ba982c9ffcf0bf3fb7fe3953c523
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Apr 14 13:54:07 2020 -0300

    linux: Use getdents64 on readdir64 compat implementation
    
    It uses a similar strategy from the non-LFS readdir that also
    uses getdents64 internally and reserves some space on the allocated
    internal DIR buffer to be used as a temporary buffer. The kernel
    obtained dirent64 data are copied to the temporary buffer for each
    readdir compat call.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 95 +-----------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/readdir.h    | 47 +++++++++++++++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 27 +++++-----
 4 files changed, 58 insertions(+), 113 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
-#  include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
-  /* If this is the first entry in the buffer, we can report the
-     error.  */
-  if (offset == 0)
-    {
-      __set_errno (EOVERFLOW);
-      return -1;
-    }
-
-  /* Otherwise, seek to the overflowing entry, so that the next call
-     will report the error, and return the data read so far.  */
-  if (__lseek64 (fd, offset, SEEK_SET) != 0)
-    return -1;
-  return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
-  /* We do not move the individual directory entries.  This is only
-     possible if the target type (struct __old_dirent64) is smaller
-     than the source type.  */
-  _Static_assert (offsetof (struct __old_dirent64, d_name)
-		  <= offsetof (struct dirent64, d_name),
-		  "__old_dirent64 is larger than dirent64");
-  _Static_assert (__alignof__ (struct __old_dirent64)
-		  <= __alignof__ (struct dirent64),
-		  "alignment of __old_dirent64 is larger than dirent64");
-
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
-  if (retval > 0)
-    {
-      /* This is the marker for the first entry.  Offset 0 is reserved
-	 for the first entry (see rewinddir).  Here, we use it as a
-	 marker for the first entry in the buffer.  We never actually
-	 seek to offset 0 because handle_overflow reports the error
-	 directly, so it does not matter that the offset is incorrect
-	 if entries have been read from the descriptor before (so that
-	 the descriptor is not actually at offset 0).  */
-      __off64_t previous_offset = 0;
-
-      char *p = buf;
-      char *end = buf + retval;
-      while (p < end)
-	{
-	  struct dirent64 *source = (struct dirent64 *) p;
-
-	  /* Copy out the fixed-size data.  */
-	  __ino_t ino = source->d_ino;
-	  __off64_t offset = source->d_off;
-	  unsigned int reclen = source->d_reclen;
-	  unsigned char type = source->d_type;
-
-	  /* Check for ino_t overflow.  */
-	  if (__glibc_unlikely (ino != source->d_ino))
-	    return handle_overflow (fd, previous_offset, p - buf);
-
-	  /* Convert to the target layout.  Use a separate struct and
-	     memcpy to side-step aliasing issues.  */
-	  struct __old_dirent64 result;
-	  result.d_ino = ino;
-	  result.d_off = offset;
-	  result.d_reclen = reclen;
-	  result.d_type = type;
-
-	  /* Write the fixed-sized part of the result to the
-	     buffer.  */
-	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
-	  memcpy (p, &result, result_name_offset);
-
-	  /* Adjust the position of the name if necessary.  Copy
-	     everything until the end of the record, including the
-	     terminating NUL byte.  */
-	  if (result_name_offset != offsetof (struct dirent64, d_name))
-	    memmove (p + result_name_offset, source->d_name,
-		     reclen - offsetof (struct dirent64, d_name));
-
-	  p += reclen;
-	  previous_offset = offset;
-	}
-     }
-  return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
-#endif /* _DIRENT_MATCHES_DIRENT64  */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 100644
--- a/sysdeps/unix/sysv/linux/olddirent.h
+++ b/sysdeps/unix/sysv/linux/olddirent.h
@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp)
         attribute_hidden;
 extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry,
 			  struct __old_dirent64 **__result);
-extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes)
-	attribute_hidden;
 int __old_scandir64 (const char * __dir,
 		     struct __old_dirent64 *** __namelist,
 		     int (*__selector) (const struct __old_dirent64 *),
diff --git a/sysdeps/unix/sysv/linux/readdir.h b/sysdeps/unix/sysv/linux/readdir.h
index 7f6cf3783d..234a7f26e6 100644
--- a/sysdeps/unix/sysv/linux/readdir.h
+++ b/sysdeps/unix/sysv/linux/readdir.h
@@ -21,6 +21,7 @@
 
 #if !_DIRENT_MATCHES_DIRENT64
 # include <dirstream.h>
+# include <olddirent.h>
 
 /* getdents64 is used internally for both LFS and non-LFS implementations.
    The non-LFS interface reserves part of the allocated buffer to return the
@@ -106,9 +107,53 @@ dirstream_ret_entry (struct __dirstream *ds)
 
   return dp;
 }
+
+/* Return the allocated buffer used on LFS compat readdir call.  */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+  return (struct __old_dirent64 *) ds->data;
+}
+
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+  struct dirent64 *dp64 = dirstream_entry (ds);
+  struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+  dp64_compat->d_ino = dp64->d_ino;
+  if (dp64_compat->d_ino != dp64->d_ino)
+    /* Overflow.  */
+    return NULL;
+
+  dp64_compat->d_off = dp64->d_off;
+
+  const size_t size_diff = (offsetof (struct dirent64, d_name)
+			    - offsetof (struct __old_dirent64, d_name));
+  const size_t alignment = _Alignof (struct __old_dirent64);
+  size_t new_reclen  = (dp64->d_reclen - size_diff + alignment - 1)
+			& ~(alignment - 1);
+  if (new_reclen > return_buffer_size)
+    /* Overflow.  */
+    return NULL;
+
+  /* The compat symbol report the kernel obtained d_reclen, even though
+     it has an incompatible dirent layout.  */
+  dp64_compat->d_reclen = dp64->d_reclen;
+
+  dp64_compat->d_type = dp64->d_type;
+
+  memcpy (dp64_compat->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  ds->offset += dp64->d_reclen;
+  ds->filepos = dp64->d_off;
+
+  return dp64_compat;
+}
+
 #else
 /* No need to reserve an buffer space if dirent has already LFS support.  */
 enum { return_buffer_size = 0 };
 #endif /* _DIRENT_MATCHES_DIRENT64  */
-
 #endif
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index ce50067637..79a2027fd1 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -112,27 +112,23 @@ weak_alias (__readdir64, readdir64)
 versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # endif
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
+#  include <readdir.h>
+#  include <unistd.h>
 
 attribute_compat_text_section
 struct __old_dirent64 *
 __old_readdir64_unlocked (DIR *dirp)
 {
   struct __old_dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
   do
     {
-      size_t reclen;
-
       if (dirp->offset >= dirp->size)
 	{
 	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
+	  ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp),
+					dirstream_alloc_size (dirp));
 	  if (bytes <= 0)
 	    {
 	      /* On some systems getdents fails with ENOENT when the
@@ -153,13 +149,12 @@ __old_readdir64_unlocked (DIR *dirp)
 	  dirp->offset = 0;
 	}
 
-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
-
-      dirp->offset += reclen;
-
-      dirp->filepos = dp->d_off;
+      dp = dirstream_ret_entry64_compat (dirp);
+      if (dp == NULL)
+	{
+	  __set_errno (EOVERFLOW);
+	  break;
+	}
 
       /* Skip deleted files.  */
     } while (dp->d_ino == 0);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation
@ 2020-04-17 13:23 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2020-04-17 13:23 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c8c5403c9fa9afdcd1d983638c104f8f4720955a

commit c8c5403c9fa9afdcd1d983638c104f8f4720955a
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Apr 14 13:54:07 2020 -0300

    linux: Use getdents64 on readdir64 compat implementation
    
    It uses a similar strategy from the non-LFS readdir that also
    uses getdents64 internally and reserves some space on the allocated
    internal DIR buffer to be used as a temporary buffer. The kernel
    obtained dirent64 data are copied to the temporary buffer for each
    readdir compat call.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 95 +-----------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/readdir.h    | 47 +++++++++++++++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 27 +++++-----
 4 files changed, 58 insertions(+), 113 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
-#  include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
-  /* If this is the first entry in the buffer, we can report the
-     error.  */
-  if (offset == 0)
-    {
-      __set_errno (EOVERFLOW);
-      return -1;
-    }
-
-  /* Otherwise, seek to the overflowing entry, so that the next call
-     will report the error, and return the data read so far.  */
-  if (__lseek64 (fd, offset, SEEK_SET) != 0)
-    return -1;
-  return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
-  /* We do not move the individual directory entries.  This is only
-     possible if the target type (struct __old_dirent64) is smaller
-     than the source type.  */
-  _Static_assert (offsetof (struct __old_dirent64, d_name)
-		  <= offsetof (struct dirent64, d_name),
-		  "__old_dirent64 is larger than dirent64");
-  _Static_assert (__alignof__ (struct __old_dirent64)
-		  <= __alignof__ (struct dirent64),
-		  "alignment of __old_dirent64 is larger than dirent64");
-
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
-  if (retval > 0)
-    {
-      /* This is the marker for the first entry.  Offset 0 is reserved
-	 for the first entry (see rewinddir).  Here, we use it as a
-	 marker for the first entry in the buffer.  We never actually
-	 seek to offset 0 because handle_overflow reports the error
-	 directly, so it does not matter that the offset is incorrect
-	 if entries have been read from the descriptor before (so that
-	 the descriptor is not actually at offset 0).  */
-      __off64_t previous_offset = 0;
-
-      char *p = buf;
-      char *end = buf + retval;
-      while (p < end)
-	{
-	  struct dirent64 *source = (struct dirent64 *) p;
-
-	  /* Copy out the fixed-size data.  */
-	  __ino_t ino = source->d_ino;
-	  __off64_t offset = source->d_off;
-	  unsigned int reclen = source->d_reclen;
-	  unsigned char type = source->d_type;
-
-	  /* Check for ino_t overflow.  */
-	  if (__glibc_unlikely (ino != source->d_ino))
-	    return handle_overflow (fd, previous_offset, p - buf);
-
-	  /* Convert to the target layout.  Use a separate struct and
-	     memcpy to side-step aliasing issues.  */
-	  struct __old_dirent64 result;
-	  result.d_ino = ino;
-	  result.d_off = offset;
-	  result.d_reclen = reclen;
-	  result.d_type = type;
-
-	  /* Write the fixed-sized part of the result to the
-	     buffer.  */
-	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
-	  memcpy (p, &result, result_name_offset);
-
-	  /* Adjust the position of the name if necessary.  Copy
-	     everything until the end of the record, including the
-	     terminating NUL byte.  */
-	  if (result_name_offset != offsetof (struct dirent64, d_name))
-	    memmove (p + result_name_offset, source->d_name,
-		     reclen - offsetof (struct dirent64, d_name));
-
-	  p += reclen;
-	  previous_offset = offset;
-	}
-     }
-  return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
-#endif /* _DIRENT_MATCHES_DIRENT64  */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 100644
--- a/sysdeps/unix/sysv/linux/olddirent.h
+++ b/sysdeps/unix/sysv/linux/olddirent.h
@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp)
         attribute_hidden;
 extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry,
 			  struct __old_dirent64 **__result);
-extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes)
-	attribute_hidden;
 int __old_scandir64 (const char * __dir,
 		     struct __old_dirent64 *** __namelist,
 		     int (*__selector) (const struct __old_dirent64 *),
diff --git a/sysdeps/unix/sysv/linux/readdir.h b/sysdeps/unix/sysv/linux/readdir.h
index 7f6cf3783d..234a7f26e6 100644
--- a/sysdeps/unix/sysv/linux/readdir.h
+++ b/sysdeps/unix/sysv/linux/readdir.h
@@ -21,6 +21,7 @@
 
 #if !_DIRENT_MATCHES_DIRENT64
 # include <dirstream.h>
+# include <olddirent.h>
 
 /* getdents64 is used internally for both LFS and non-LFS implementations.
    The non-LFS interface reserves part of the allocated buffer to return the
@@ -106,9 +107,53 @@ dirstream_ret_entry (struct __dirstream *ds)
 
   return dp;
 }
+
+/* Return the allocated buffer used on LFS compat readdir call.  */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+  return (struct __old_dirent64 *) ds->data;
+}
+
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+  struct dirent64 *dp64 = dirstream_entry (ds);
+  struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+  dp64_compat->d_ino = dp64->d_ino;
+  if (dp64_compat->d_ino != dp64->d_ino)
+    /* Overflow.  */
+    return NULL;
+
+  dp64_compat->d_off = dp64->d_off;
+
+  const size_t size_diff = (offsetof (struct dirent64, d_name)
+			    - offsetof (struct __old_dirent64, d_name));
+  const size_t alignment = _Alignof (struct __old_dirent64);
+  size_t new_reclen  = (dp64->d_reclen - size_diff + alignment - 1)
+			& ~(alignment - 1);
+  if (new_reclen > return_buffer_size)
+    /* Overflow.  */
+    return NULL;
+
+  /* The compat symbol report the kernel obtained d_reclen, even though
+     it has an incompatible dirent layout.  */
+  dp64_compat->d_reclen = dp64->d_reclen;
+
+  dp64_compat->d_type = dp64->d_type;
+
+  memcpy (dp64_compat->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  ds->offset += dp64->d_reclen;
+  ds->filepos = dp64->d_off;
+
+  return dp64_compat;
+}
+
 #else
 /* No need to reserve an buffer space if dirent has already LFS support.  */
 enum { return_buffer_size = 0 };
 #endif /* _DIRENT_MATCHES_DIRENT64  */
-
 #endif
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index 4ce6024b38..4095b94a0f 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -108,27 +108,23 @@ weak_alias (__readdir64, readdir)
 # include <shlib-compat.h>
 versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
+#  include <readdir.h>
+#  include <unistd.h>
 
 attribute_compat_text_section
 struct __old_dirent64 *
 __old_readdir64_unlocked (DIR *dirp)
 {
   struct __old_dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
   do
     {
-      size_t reclen;
-
       if (dirp->offset >= dirp->size)
 	{
 	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
+	  ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp),
+					dirstream_alloc_size (dirp));
 	  if (bytes <= 0)
 	    {
 	      /* On some systems getdents fails with ENOENT when the
@@ -149,13 +145,12 @@ __old_readdir64_unlocked (DIR *dirp)
 	  dirp->offset = 0;
 	}
 
-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
-
-      dirp->offset += reclen;
-
-      dirp->filepos = dp->d_off;
+      dp = dirstream_ret_entry64_compat (dirp);
+      if (dp == NULL)
+	{
+	  __set_errno (EOVERFLOW);
+	  break;
+	}
 
       /* Skip deleted files.  */
     } while (dp->d_ino == 0);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation
@ 2020-04-16 13:21 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2020-04-16 13:21 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=2a73dbb0c0d97c2c159b3042e960aa80603890fd

commit 2a73dbb0c0d97c2c159b3042e960aa80603890fd
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Apr 14 13:54:07 2020 -0300

    linux: Use getdents64 on readdir64 compat implementation
    
    It uses a similar strategy from the non-LFS readdir that also
    uses getdents64 internally and reserves some space on the allocated
    internal DIR buffer to be used as a temporary buffer. The kernel
    obtained dirent64 data are copied to the temporary buffer for each
    readdir compat call.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 95 +-----------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/readdir.h    | 45 ++++++++++++++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 27 +++++-----
 4 files changed, 56 insertions(+), 113 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
-#  include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
-  /* If this is the first entry in the buffer, we can report the
-     error.  */
-  if (offset == 0)
-    {
-      __set_errno (EOVERFLOW);
-      return -1;
-    }
-
-  /* Otherwise, seek to the overflowing entry, so that the next call
-     will report the error, and return the data read so far.  */
-  if (__lseek64 (fd, offset, SEEK_SET) != 0)
-    return -1;
-  return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
-  /* We do not move the individual directory entries.  This is only
-     possible if the target type (struct __old_dirent64) is smaller
-     than the source type.  */
-  _Static_assert (offsetof (struct __old_dirent64, d_name)
-		  <= offsetof (struct dirent64, d_name),
-		  "__old_dirent64 is larger than dirent64");
-  _Static_assert (__alignof__ (struct __old_dirent64)
-		  <= __alignof__ (struct dirent64),
-		  "alignment of __old_dirent64 is larger than dirent64");
-
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
-  if (retval > 0)
-    {
-      /* This is the marker for the first entry.  Offset 0 is reserved
-	 for the first entry (see rewinddir).  Here, we use it as a
-	 marker for the first entry in the buffer.  We never actually
-	 seek to offset 0 because handle_overflow reports the error
-	 directly, so it does not matter that the offset is incorrect
-	 if entries have been read from the descriptor before (so that
-	 the descriptor is not actually at offset 0).  */
-      __off64_t previous_offset = 0;
-
-      char *p = buf;
-      char *end = buf + retval;
-      while (p < end)
-	{
-	  struct dirent64 *source = (struct dirent64 *) p;
-
-	  /* Copy out the fixed-size data.  */
-	  __ino_t ino = source->d_ino;
-	  __off64_t offset = source->d_off;
-	  unsigned int reclen = source->d_reclen;
-	  unsigned char type = source->d_type;
-
-	  /* Check for ino_t overflow.  */
-	  if (__glibc_unlikely (ino != source->d_ino))
-	    return handle_overflow (fd, previous_offset, p - buf);
-
-	  /* Convert to the target layout.  Use a separate struct and
-	     memcpy to side-step aliasing issues.  */
-	  struct __old_dirent64 result;
-	  result.d_ino = ino;
-	  result.d_off = offset;
-	  result.d_reclen = reclen;
-	  result.d_type = type;
-
-	  /* Write the fixed-sized part of the result to the
-	     buffer.  */
-	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
-	  memcpy (p, &result, result_name_offset);
-
-	  /* Adjust the position of the name if necessary.  Copy
-	     everything until the end of the record, including the
-	     terminating NUL byte.  */
-	  if (result_name_offset != offsetof (struct dirent64, d_name))
-	    memmove (p + result_name_offset, source->d_name,
-		     reclen - offsetof (struct dirent64, d_name));
-
-	  p += reclen;
-	  previous_offset = offset;
-	}
-     }
-  return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
-#endif /* _DIRENT_MATCHES_DIRENT64  */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 100644
--- a/sysdeps/unix/sysv/linux/olddirent.h
+++ b/sysdeps/unix/sysv/linux/olddirent.h
@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp)
         attribute_hidden;
 extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry,
 			  struct __old_dirent64 **__result);
-extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes)
-	attribute_hidden;
 int __old_scandir64 (const char * __dir,
 		     struct __old_dirent64 *** __namelist,
 		     int (*__selector) (const struct __old_dirent64 *),
diff --git a/sysdeps/unix/sysv/linux/readdir.h b/sysdeps/unix/sysv/linux/readdir.h
index 7f6cf3783d..be61735aed 100644
--- a/sysdeps/unix/sysv/linux/readdir.h
+++ b/sysdeps/unix/sysv/linux/readdir.h
@@ -21,6 +21,7 @@
 
 #if !_DIRENT_MATCHES_DIRENT64
 # include <dirstream.h>
+# include <olddirent.h>
 
 /* getdents64 is used internally for both LFS and non-LFS implementations.
    The non-LFS interface reserves part of the allocated buffer to return the
@@ -106,9 +107,51 @@ dirstream_ret_entry (struct __dirstream *ds)
 
   return dp;
 }
+
+/* Return the allocated buffer used on LFS compat readdir call.  */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+  return (struct __old_dirent64 *) ds->data;
+}
+
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+  struct dirent64 *dp64 = dirstream_entry (ds);
+  struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+  dp64_compat->d_ino = dp64->d_ino;
+  if (dp64_compat->d_ino != dp64->d_ino)
+    /* Overflow.  */
+    return NULL;
+
+  dp64_compat->d_off = dp64->d_off;
+
+  const size_t size_diff = (offsetof (struct dirent64, d_name)
+			    - offsetof (struct __old_dirent64, d_name));
+  const size_t alignment = _Alignof (struct __old_dirent64);
+  size_t new_reclen  = (dp64->d_reclen - size_diff + alignment - 1)
+			& ~(alignment - 1);
+  if (new_reclen > return_buffer_size)
+    /* Overflow.  */
+    return NULL;
+
+  dp64_compat->d_reclen = new_reclen;
+
+  dp64_compat->d_type = dp64->d_type;
+
+  memcpy (dp64_compat->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  ds->offset += dp64->d_reclen;
+  ds->filepos = dp64->d_off;
+
+  return dp64_compat;
+}
+
 #else
 /* No need to reserve an buffer space if dirent has already LFS support.  */
 enum { return_buffer_size = 0 };
 #endif /* _DIRENT_MATCHES_DIRENT64  */
-
 #endif
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index 4ce6024b38..4095b94a0f 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -108,27 +108,23 @@ weak_alias (__readdir64, readdir)
 # include <shlib-compat.h>
 versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
+#  include <readdir.h>
+#  include <unistd.h>
 
 attribute_compat_text_section
 struct __old_dirent64 *
 __old_readdir64_unlocked (DIR *dirp)
 {
   struct __old_dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
   do
     {
-      size_t reclen;
-
       if (dirp->offset >= dirp->size)
 	{
 	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
+	  ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp),
+					dirstream_alloc_size (dirp));
 	  if (bytes <= 0)
 	    {
 	      /* On some systems getdents fails with ENOENT when the
@@ -149,13 +145,12 @@ __old_readdir64_unlocked (DIR *dirp)
 	  dirp->offset = 0;
 	}
 
-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
-
-      dirp->offset += reclen;
-
-      dirp->filepos = dp->d_off;
+      dp = dirstream_ret_entry64_compat (dirp);
+      if (dp == NULL)
+	{
+	  __set_errno (EOVERFLOW);
+	  break;
+	}
 
       /* Skip deleted files.  */
     } while (dp->d_ino == 0);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation
@ 2020-04-15 14:15 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2020-04-15 14:15 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=5d2edfbc172b8788fcfcffbafc650cd313ec1f4d

commit 5d2edfbc172b8788fcfcffbafc650cd313ec1f4d
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Apr 14 13:54:07 2020 -0300

    linux: Use getdents64 on readdir64 compat implementation
    
    It uses a similar strategy from the non-LFS readdir that also
    uses getdents64 internally and reserves some space on the allocated
    internal DIR buffer to be used as a temporary buffer. The kernel
    obtained dirent64 data are copied to the temporary buffer for each
    readdir compat call.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 95 +-----------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/readdir.h    | 31 +++++++++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 27 +++++-----
 4 files changed, 42 insertions(+), 113 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
-#  include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
-  /* If this is the first entry in the buffer, we can report the
-     error.  */
-  if (offset == 0)
-    {
-      __set_errno (EOVERFLOW);
-      return -1;
-    }
-
-  /* Otherwise, seek to the overflowing entry, so that the next call
-     will report the error, and return the data read so far.  */
-  if (__lseek64 (fd, offset, SEEK_SET) != 0)
-    return -1;
-  return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
-  /* We do not move the individual directory entries.  This is only
-     possible if the target type (struct __old_dirent64) is smaller
-     than the source type.  */
-  _Static_assert (offsetof (struct __old_dirent64, d_name)
-		  <= offsetof (struct dirent64, d_name),
-		  "__old_dirent64 is larger than dirent64");
-  _Static_assert (__alignof__ (struct __old_dirent64)
-		  <= __alignof__ (struct dirent64),
-		  "alignment of __old_dirent64 is larger than dirent64");
-
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
-  if (retval > 0)
-    {
-      /* This is the marker for the first entry.  Offset 0 is reserved
-	 for the first entry (see rewinddir).  Here, we use it as a
-	 marker for the first entry in the buffer.  We never actually
-	 seek to offset 0 because handle_overflow reports the error
-	 directly, so it does not matter that the offset is incorrect
-	 if entries have been read from the descriptor before (so that
-	 the descriptor is not actually at offset 0).  */
-      __off64_t previous_offset = 0;
-
-      char *p = buf;
-      char *end = buf + retval;
-      while (p < end)
-	{
-	  struct dirent64 *source = (struct dirent64 *) p;
-
-	  /* Copy out the fixed-size data.  */
-	  __ino_t ino = source->d_ino;
-	  __off64_t offset = source->d_off;
-	  unsigned int reclen = source->d_reclen;
-	  unsigned char type = source->d_type;
-
-	  /* Check for ino_t overflow.  */
-	  if (__glibc_unlikely (ino != source->d_ino))
-	    return handle_overflow (fd, previous_offset, p - buf);
-
-	  /* Convert to the target layout.  Use a separate struct and
-	     memcpy to side-step aliasing issues.  */
-	  struct __old_dirent64 result;
-	  result.d_ino = ino;
-	  result.d_off = offset;
-	  result.d_reclen = reclen;
-	  result.d_type = type;
-
-	  /* Write the fixed-sized part of the result to the
-	     buffer.  */
-	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
-	  memcpy (p, &result, result_name_offset);
-
-	  /* Adjust the position of the name if necessary.  Copy
-	     everything until the end of the record, including the
-	     terminating NUL byte.  */
-	  if (result_name_offset != offsetof (struct dirent64, d_name))
-	    memmove (p + result_name_offset, source->d_name,
-		     reclen - offsetof (struct dirent64, d_name));
-
-	  p += reclen;
-	  previous_offset = offset;
-	}
-     }
-  return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
-#endif /* _DIRENT_MATCHES_DIRENT64  */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 100644
--- a/sysdeps/unix/sysv/linux/olddirent.h
+++ b/sysdeps/unix/sysv/linux/olddirent.h
@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp)
         attribute_hidden;
 extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry,
 			  struct __old_dirent64 **__result);
-extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes)
-	attribute_hidden;
 int __old_scandir64 (const char * __dir,
 		     struct __old_dirent64 *** __namelist,
 		     int (*__selector) (const struct __old_dirent64 *),
diff --git a/sysdeps/unix/sysv/linux/readdir.h b/sysdeps/unix/sysv/linux/readdir.h
index 581266f2c5..0b570de346 100644
--- a/sysdeps/unix/sysv/linux/readdir.h
+++ b/sysdeps/unix/sysv/linux/readdir.h
@@ -21,6 +21,7 @@
 
 #if !_DIRENT_MATCHES_DIRENT64
 # include <dirstream.h>
+# include <olddirent.h>
 
 /* getdents64 is used internally for both LFS and non-LFS implementations.
    The non-LFS interface reserves part of the allocated buffer to return the
@@ -88,9 +89,37 @@ dirstream_ret_entry (struct __dirstream *ds)
 
   return dp;
 }
+
+/* Return the allocated buffer used on LFS compat readdir call.  */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+  return (struct __old_dirent64 *) ds->data;
+}
+
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+  struct dirent64 *dp64 = dirstream_entry (ds);
+  struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+  dp64_compat->d_ino = dp64->d_ino;
+  if (dp64_compat->d_ino != dp64->d_ino)
+    return NULL;
+  dp64_compat->d_off = dp64->d_off;
+  dp64_compat->d_reclen = dp64->d_reclen;
+  dp64_compat->d_type = dp64->d_type;
+  memcpy (dp64_compat->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  ds->offset += dp64->d_reclen;
+  ds->filepos = dp64->d_off;
+
+  return dp64_compat;
+}
+
 #else
 /* No need to reserve an buffer space if dirent has already LFS support.  */
 enum { return_buffer_size = 0 };
 #endif /* _DIRENT_MATCHES_DIRENT64  */
-
 #endif
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index 4ce6024b38..4095b94a0f 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -108,27 +108,23 @@ weak_alias (__readdir64, readdir)
 # include <shlib-compat.h>
 versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
+#  include <readdir.h>
+#  include <unistd.h>
 
 attribute_compat_text_section
 struct __old_dirent64 *
 __old_readdir64_unlocked (DIR *dirp)
 {
   struct __old_dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
   do
     {
-      size_t reclen;
-
       if (dirp->offset >= dirp->size)
 	{
 	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
+	  ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp),
+					dirstream_alloc_size (dirp));
 	  if (bytes <= 0)
 	    {
 	      /* On some systems getdents fails with ENOENT when the
@@ -149,13 +145,12 @@ __old_readdir64_unlocked (DIR *dirp)
 	  dirp->offset = 0;
 	}
 
-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
-
-      dirp->offset += reclen;
-
-      dirp->filepos = dp->d_off;
+      dp = dirstream_ret_entry64_compat (dirp);
+      if (dp == NULL)
+	{
+	  __set_errno (EOVERFLOW);
+	  break;
+	}
 
       /* Skip deleted files.  */
     } while (dp->d_ino == 0);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation
@ 2020-04-14 21:09 Adhemerval Zanella
  0 siblings, 0 replies; 5+ messages in thread
From: Adhemerval Zanella @ 2020-04-14 21:09 UTC (permalink / raw)
  To: glibc-cvs

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=8621a1b20cd09e4cbbcb3677f016eff39996dadb

commit 8621a1b20cd09e4cbbcb3677f016eff39996dadb
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Apr 14 13:54:07 2020 -0300

    linux: Use getdents64 on readdir64 compat implementation
    
    It uses a similar strategy from the non-LFS getdents that also
    uses getdents64 internally, by reserving some space on the allocated
    internal DIR buffer to be used as a temporary buffer. The kernel
    obtained dirent64 struct are then copied to the temporary buffe
    for each readdir compat call.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/dirstream_nolfs.h | 29 ++++++++++
 sysdeps/unix/sysv/linux/getdents64.c      | 95 +------------------------------
 sysdeps/unix/sysv/linux/olddirent.h       |  2 -
 sysdeps/unix/sysv/linux/readdir64.c       | 27 ++++-----
 4 files changed, 41 insertions(+), 112 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/dirstream_nolfs.h b/sysdeps/unix/sysv/linux/dirstream_nolfs.h
index 32f8bf3070..5bb49a0cfe 100644
--- a/sysdeps/unix/sysv/linux/dirstream_nolfs.h
+++ b/sysdeps/unix/sysv/linux/dirstream_nolfs.h
@@ -20,6 +20,7 @@
 #define _DIRSTREAM_NOLFS_H
 
 #include <dirstream.h>
+#include <olddirent.h>
 
 /* getdents64 is used regardless the interface LFS support.  The non-LFS
    interface reserves part of the allocated buffer to return the non-LFS
@@ -57,6 +58,13 @@ dirstream_ret (struct __dirstream *ds)
   return (struct dirent *) ds->data;
 }
 
+/* Return the allocated buffer used on LFS compat readdir call.  */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+  return (struct __old_dirent64 *) ds->data;
+}
+
 static inline struct dirent64 *
 dirstream_entry (struct __dirstream *ds)
 {
@@ -85,4 +93,25 @@ dirstream_ret_entry (struct __dirstream *ds)
   return dp;
 }
 
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+  struct dirent64 *dp64 = dirstream_entry (ds);
+  struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+  dp64_compat->d_ino = dp64->d_ino;
+  if (dp64_compat->d_ino != dp64->d_ino)
+    return NULL;
+  dp64_compat->d_off = dp64->d_off;
+  dp64_compat->d_reclen = dp64->d_reclen;
+  dp64_compat->d_type = dp64->d_type;
+  memcpy (dp64_compat->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  ds->offset += dp64->d_reclen;
+  ds->filepos = dp64->d_off;
+
+  return dp64_compat;
+}
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
 
 #if _DIRENT_MATCHES_DIRENT64
 strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
-#  include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
-  /* If this is the first entry in the buffer, we can report the
-     error.  */
-  if (offset == 0)
-    {
-      __set_errno (EOVERFLOW);
-      return -1;
-    }
-
-  /* Otherwise, seek to the overflowing entry, so that the next call
-     will report the error, and return the data read so far.  */
-  if (__lseek64 (fd, offset, SEEK_SET) != 0)
-    return -1;
-  return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
-  /* We do not move the individual directory entries.  This is only
-     possible if the target type (struct __old_dirent64) is smaller
-     than the source type.  */
-  _Static_assert (offsetof (struct __old_dirent64, d_name)
-		  <= offsetof (struct dirent64, d_name),
-		  "__old_dirent64 is larger than dirent64");
-  _Static_assert (__alignof__ (struct __old_dirent64)
-		  <= __alignof__ (struct dirent64),
-		  "alignment of __old_dirent64 is larger than dirent64");
-
-  ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
-  if (retval > 0)
-    {
-      /* This is the marker for the first entry.  Offset 0 is reserved
-	 for the first entry (see rewinddir).  Here, we use it as a
-	 marker for the first entry in the buffer.  We never actually
-	 seek to offset 0 because handle_overflow reports the error
-	 directly, so it does not matter that the offset is incorrect
-	 if entries have been read from the descriptor before (so that
-	 the descriptor is not actually at offset 0).  */
-      __off64_t previous_offset = 0;
-
-      char *p = buf;
-      char *end = buf + retval;
-      while (p < end)
-	{
-	  struct dirent64 *source = (struct dirent64 *) p;
-
-	  /* Copy out the fixed-size data.  */
-	  __ino_t ino = source->d_ino;
-	  __off64_t offset = source->d_off;
-	  unsigned int reclen = source->d_reclen;
-	  unsigned char type = source->d_type;
-
-	  /* Check for ino_t overflow.  */
-	  if (__glibc_unlikely (ino != source->d_ino))
-	    return handle_overflow (fd, previous_offset, p - buf);
-
-	  /* Convert to the target layout.  Use a separate struct and
-	     memcpy to side-step aliasing issues.  */
-	  struct __old_dirent64 result;
-	  result.d_ino = ino;
-	  result.d_off = offset;
-	  result.d_reclen = reclen;
-	  result.d_type = type;
-
-	  /* Write the fixed-sized part of the result to the
-	     buffer.  */
-	  size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
-	  memcpy (p, &result, result_name_offset);
-
-	  /* Adjust the position of the name if necessary.  Copy
-	     everything until the end of the record, including the
-	     terminating NUL byte.  */
-	  if (result_name_offset != offsetof (struct dirent64, d_name))
-	    memmove (p + result_name_offset, source->d_name,
-		     reclen - offsetof (struct dirent64, d_name));
-
-	  p += reclen;
-	  previous_offset = offset;
-	}
-     }
-  return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
-#endif /* _DIRENT_MATCHES_DIRENT64  */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 100644
--- a/sysdeps/unix/sysv/linux/olddirent.h
+++ b/sysdeps/unix/sysv/linux/olddirent.h
@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp)
         attribute_hidden;
 extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry,
 			  struct __old_dirent64 **__result);
-extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes)
-	attribute_hidden;
 int __old_scandir64 (const char * __dir,
 		     struct __old_dirent64 *** __namelist,
 		     int (*__selector) (const struct __old_dirent64 *),
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index 4ce6024b38..670fbc9807 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -108,27 +108,23 @@ weak_alias (__readdir64, readdir)
 # include <shlib-compat.h>
 versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-#  include <olddirent.h>
+#  include <dirstream_nolfs.h>
+#  include <unistd.h>
 
 attribute_compat_text_section
 struct __old_dirent64 *
 __old_readdir64_unlocked (DIR *dirp)
 {
   struct __old_dirent64 *dp;
-  int saved_errno = errno;
+  const int saved_errno = errno;
 
   do
     {
-      size_t reclen;
-
       if (dirp->offset >= dirp->size)
 	{
 	  /* We've emptied out our buffer.  Refill it.  */
-
-	  size_t maxread = dirp->allocation;
-	  ssize_t bytes;
-
-	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
+	  ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp),
+					dirstream_alloc_size (dirp));
 	  if (bytes <= 0)
 	    {
 	      /* On some systems getdents fails with ENOENT when the
@@ -149,13 +145,12 @@ __old_readdir64_unlocked (DIR *dirp)
 	  dirp->offset = 0;
 	}
 
-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
-      reclen = dp->d_reclen;
-
-      dirp->offset += reclen;
-
-      dirp->filepos = dp->d_off;
+      dp = dirstream_ret_entry64_compat (dirp);
+      if (dp == NULL)
+	{
+	  __set_errno (EOVERFLOW);
+	  break;
+	}
 
       /* Skip deleted files.  */
     } while (dp->d_ino == 0);


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-10-02 13:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-02 13:53 [glibc/azanella/bz23960] linux: Use getdents64 on readdir64 compat implementation Adhemerval Zanella
  -- strict thread matches above, loose matches on Subject: below --
2020-04-17 13:23 Adhemerval Zanella
2020-04-16 13:21 Adhemerval Zanella
2020-04-15 14:15 Adhemerval Zanella
2020-04-14 21:09 Adhemerval Zanella

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).