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

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

commit 6a15144809e2b4c6d51406d92df5b485536dc62c
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Oct 20 16:00:43 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 uses a translation buffer to return
    the compat readdir64 entry.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 93 ------------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/opendir.c    | 15 +++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 62 +++++++++++++++++++-----
 4 files changed, 64 insertions(+), 108 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 510a586859..92481526c5 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  */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index 00c84b9521..68aafd7c02 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/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
index 9a0b7ab4c4..df40b0a64e 100644
--- a/sysdeps/unix/sysv/linux/opendir.c
+++ b/sysdeps/unix/sysv/linux/opendir.c
@@ -23,6 +23,11 @@
 
 #include <not-cancel.h>
 
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#endif
+
 enum {
   opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
 };
@@ -128,7 +133,15 @@ __alloc_dir (int fd, bool close_fd, int flags,
      expand the buffer if required.  */
   enum
     {
-      tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
+      tbuffer_size =
+# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+      /* This is used on compat readdir64.  */
+		     MAX (sizeof (struct dirent),
+			  sizeof (struct __old_dirent64))
+# else
+		     sizeof (struct dirent)
+# endif
+                     + NAME_MAX + 1
     };
   dirp->tbuffer = malloc (tbuffer_size);
   if (dirp->tbuffer == NULL)
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index bbe247f95d..01e834e238 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -102,21 +102,52 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 #  include <olddirent.h>
 
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+   at dirstream DS.  Return true is the translation was possible or
+   false if either an internal fields can be represented in the non-LFS
+   entry or if the translation can not be resized.  */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+  /* Check for overflow.  */
+  ino_t d_ino = dp64->d_ino;
+  if (d_ino != dp64->d_ino)
+    return false;
+
+  /* Expand the translation buffer to hold the new namesize.  */
+  size_t d_reclen = sizeof (struct __old_dirent64)
+		    + dp64->d_reclen - offsetof (struct dirent64, d_name);
+  if (d_reclen > ds->tbuffer_size)
+    {
+      char *newbuffer = realloc (ds->tbuffer, d_reclen);
+      if (newbuffer == NULL)
+	return false;
+      ds->tbuffer = newbuffer;
+      ds->tbuffer_size = d_reclen;
+    }
+
+  struct __old_dirent64 *olddp64 = (struct __old_dirent64 *) ds->tbuffer;
+
+  olddp64->d_off = dp64->d_off;
+  olddp64->d_ino = dp64->d_ino;
+  olddp64->d_reclen = dp64->d_reclen;
+  olddp64->d_type = dp64->d_type;
+  memcpy (olddp64->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  return true;
+}
+
 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;
 
   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, dirp->data, dirp->allocation);
       if (bytes <= 0)
 	{
 	  /* Linux may fail with ENOENT on some file systems if the
@@ -127,17 +158,24 @@ __old_readdir64_unlocked (DIR *dirp)
 	    __set_errno (saved_errno);
 	  return NULL;
 	}
-      dirp->size = (size_t) bytes;
+      dirp->size = bytes;
 
       /* Reset the offset into the buffer.  */
       dirp->offset = 0;
     }
 
-  dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-  dirp->offset += dp->d_reclen;
-  dirp->filepos = dp->d_off;
+  struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
+  dirp->offset += dp64->d_reclen;
 
-  return dp;
+  /* Skip entries which might overflow d_ino or for memory allocation failure
+     in case of large file names.  */
+  if (dirstream_old_entry (dirp, dp64))
+    {
+      dirp->filepos = dp64->d_off;
+      return (struct __old_dirent64 *) dirp->tbuffer;
+    }
+
+  return NULL;
 }
 
 attribute_compat_text_section

^ permalink raw reply	[flat|nested] 3+ messages in thread
* [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation
@ 2023-01-26 12:17 Adhemerval Zanella
  0 siblings, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2023-01-26 12:17 UTC (permalink / raw)
  To: glibc-cvs

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

commit 425419e2244b26670d09fb02e5660a7b0d6b2283
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Oct 20 16:00:43 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 uses a translation buffer to return
    the compat readdir64 entry.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/getdents64.c | 93 ------------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/opendir.c    | 15 +++++-
 sysdeps/unix/sysv/linux/readdir64.c  | 62 +++++++++++++++++++-----
 4 files changed, 64 insertions(+), 108 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 01c3517deb..db299864ed 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  */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index cde95e192e..2d682a6919 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/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
index 9b9a9c4314..3d1ce841e2 100644
--- a/sysdeps/unix/sysv/linux/opendir.c
+++ b/sysdeps/unix/sysv/linux/opendir.c
@@ -23,6 +23,11 @@
 
 #include <not-cancel.h>
 
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#endif
+
 enum {
   opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
 };
@@ -128,7 +133,15 @@ __alloc_dir (int fd, bool close_fd, int flags,
      expand the buffer if required.  */
   enum
     {
-      tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
+      tbuffer_size =
+# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+      /* This is used on compat readdir64.  */
+		     MAX (sizeof (struct dirent),
+			  sizeof (struct __old_dirent64))
+# else
+		     sizeof (struct dirent)
+# endif
+                     + NAME_MAX + 1
     };
   dirp->tbuffer = malloc (tbuffer_size);
   if (dirp->tbuffer == NULL)
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index b901071aa7..7651eeb08e 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -102,21 +102,52 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 #  include <olddirent.h>
 
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+   at dirstream DS.  Return true is the translation was possible or
+   false if either an internal fields can be represented in the non-LFS
+   entry or if the translation can not be resized.  */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+  /* Check for overflow.  */
+  ino_t d_ino = dp64->d_ino;
+  if (d_ino != dp64->d_ino)
+    return false;
+
+  /* Expand the translation buffer to hold the new namesize.  */
+  size_t d_reclen = sizeof (struct __old_dirent64)
+		    + dp64->d_reclen - offsetof (struct dirent64, d_name);
+  if (d_reclen > ds->tbuffer_size)
+    {
+      char *newbuffer = realloc (ds->tbuffer, d_reclen);
+      if (newbuffer == NULL)
+	return false;
+      ds->tbuffer = newbuffer;
+      ds->tbuffer_size = d_reclen;
+    }
+
+  struct __old_dirent64 *olddp64 = (struct __old_dirent64 *) ds->tbuffer;
+
+  olddp64->d_off = dp64->d_off;
+  olddp64->d_ino = dp64->d_ino;
+  olddp64->d_reclen = dp64->d_reclen;
+  olddp64->d_type = dp64->d_type;
+  memcpy (olddp64->d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  return true;
+}
+
 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;
 
   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, dirp->data, dirp->allocation);
       if (bytes <= 0)
 	{
 	  /* Linux may fail with ENOENT on some file systems if the
@@ -127,17 +158,24 @@ __old_readdir64_unlocked (DIR *dirp)
 	    __set_errno (saved_errno);
 	  return NULL;
 	}
-      dirp->size = (size_t) bytes;
+      dirp->size = bytes;
 
       /* Reset the offset into the buffer.  */
       dirp->offset = 0;
     }
 
-  dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-  dirp->offset += dp->d_reclen;
-  dirp->filepos = dp->d_off;
+  struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
+  dirp->offset += dp64->d_reclen;
 
-  return dp;
+  /* Skip entries which might overflow d_ino or for memory allocation failure
+     in case of large file names.  */
+  if (dirstream_old_entry (dirp, dp64))
+    {
+      dirp->filepos = dp64->d_off;
+      return (struct __old_dirent64 *) dirp->tbuffer;
+    }
+
+  return NULL;
 }
 
 attribute_compat_text_section

^ permalink raw reply	[flat|nested] 3+ messages in thread
* [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation
@ 2023-01-27 20:40 Adhemerval Zanella
  0 siblings, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2023-01-27 20:40 UTC (permalink / raw)
  To: glibc-cvs

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

commit 4404fe3a816444f2bd385686d2828d0ceb00d291
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Oct 20 16:00:43 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 uses a translation buffer to return
    the compat readdir64 entry.
    
    It allows to remove __old_getdents64.
    
    Checked on i686-linux-gnu.

Diff:
---
 sysdeps/unix/sysv/linux/dirstream.h  | 13 ++++-
 sysdeps/unix/sysv/linux/getdents64.c | 93 ------------------------------------
 sysdeps/unix/sysv/linux/olddirent.h  |  2 -
 sysdeps/unix/sysv/linux/readdir64.c  | 50 ++++++++++++++-----
 4 files changed, 50 insertions(+), 108 deletions(-)

diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h
index 8f58a1c3a6..b03ece4590 100644
--- a/sysdeps/unix/sysv/linux/dirstream.h
+++ b/sysdeps/unix/sysv/linux/dirstream.h
@@ -24,6 +24,11 @@
 #include <libc-lock.h>
 #include <telldir.h>
 
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#endif
+
 /* Directory stream type.
 
    The miscellaneous Unix `readdir' implementations read directory data
@@ -44,7 +49,13 @@ struct __dirstream
     int errcode;		/* Delayed error code.  */
 
 #if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T
-    struct dirent tdp;
+    union
+      {
+        struct dirent tdp;
+#if  SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+        struct __old_dirent64 tdp64;
+#    endif
+      };
 #endif
 #if _DIRENT_OFFSET_TRANSLATION
     struct dirstream_loc_t locs; /* off64_t to long int map for telldir.  */
diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 01c3517deb..db299864ed 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  */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index cde95e192e..2d682a6919 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 b901071aa7..88e42c5e90 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -102,21 +102,43 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
 #  include <olddirent.h>
 
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+   at dirstream DS.  Return true is the translation was possible or
+   false if either an internal fields can be represented in the non-LFS
+   entry or if the translation can not be resized.  */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+  /* Check for overflow.  */
+  if (!in_ino_t_range (dp64->d_ino))
+    return false;
+
+  /* And if name is too large.  */
+  if (dp64->d_reclen - offsetof (struct dirent64, d_name) > NAME_MAX)
+    return false;
+
+  ds->filepos = dp64->d_off;
+
+  ds->tdp64.d_off = dp64->d_off;
+  ds->tdp64.d_ino = dp64->d_ino;
+  ds->tdp64.d_reclen = dp64->d_reclen;
+  ds->tdp64.d_type = dp64->d_type;
+  memcpy (ds->tdp64.d_name, dp64->d_name,
+	  dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+  return true;
+}
+
 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;
 
   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, dirp->data, dirp->allocation);
       if (bytes <= 0)
 	{
 	  /* Linux may fail with ENOENT on some file systems if the
@@ -127,17 +149,21 @@ __old_readdir64_unlocked (DIR *dirp)
 	    __set_errno (saved_errno);
 	  return NULL;
 	}
-      dirp->size = (size_t) bytes;
+      dirp->size = bytes;
 
       /* Reset the offset into the buffer.  */
       dirp->offset = 0;
     }
 
-  dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-  dirp->offset += dp->d_reclen;
-  dirp->filepos = dp->d_off;
+  struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
+  dirp->offset += dp64->d_reclen;
 
-  return dp;
+  /* Skip entries which might overflow d_ino or for memory allocation failure
+     in case of large file names.  */
+  if (dirstream_old_entry (dirp, dp64))
+    return &dirp->tdp64;
+
+  return NULL;
 }
 
 attribute_compat_text_section

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

end of thread, other threads:[~2023-01-27 20:40 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-02 13:07 [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation Adhemerval Zanella
2023-01-26 12:17 Adhemerval Zanella
2023-01-27 20:40 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).