public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Andreas K. Hüttel" <dilfridge@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/toolchain/glibc-patches:master commit in: 9999/
Date: Tue,  4 Jan 2022 11:00:24 +0000 (UTC)	[thread overview]
Message-ID: <1641293919.fb003cf688e133a0fe792bee345b400735b7c333.dilfridge@gentoo> (raw)

commit:     fb003cf688e133a0fe792bee345b400735b7c333
Author:     Andreas K. Hüttel <dilfridge <AT> gentoo <DOT> org>
AuthorDate: Tue Jan  4 10:58:39 2022 +0000
Commit:     Andreas K. Hüttel <dilfridge <AT> gentoo <DOT> org>
CommitDate: Tue Jan  4 10:58:39 2022 +0000
URL:        https://gitweb.gentoo.org/proj/toolchain/glibc-patches.git/commit/?id=fb003cf6

Add patch series from azanella to fix 32bit qemu on 64bit filesystem regression

Signed-off-by: Andreas K. Hüttel <dilfridge <AT> gentoo.org>

 ...t-skip-entries-with-zero-d_ino-values-BZ-.patch | 182 ++++++++
 ...2-linux-Use-getdents64-on-non-LFS-readdir.patch | 204 +++++++++
 ...nternal-DIR-filepos-as-off64_t-BZ-23960-B.patch | 494 +++++++++++++++++++++
 9999/0204-linux-Add-__readdir64_unlocked.patch     | 181 ++++++++
 9999/0205-linux-Add-__old_readdir64_unlocked.patch | 184 ++++++++
 ...etdents64-on-readdir64-compat-implementat.patch | 297 +++++++++++++
 9999/0207-dirent-Deprecate-getdirentries.patch     | 101 +++++
 7 files changed, 1643 insertions(+)

diff --git a/9999/0201-linux-Do-not-skip-entries-with-zero-d_ino-values-BZ-.patch b/9999/0201-linux-Do-not-skip-entries-with-zero-d_ino-values-BZ-.patch
new file mode 100644
index 0000000..5325a91
--- /dev/null
+++ b/9999/0201-linux-Do-not-skip-entries-with-zero-d_ino-values-BZ-.patch
@@ -0,0 +1,182 @@
+From 7856a2b7ae88602bc9ee65e08fe652b6a6ad5f7e Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 20 Oct 2020 12:18:56 -0300
+Subject: [PATCH 1/7] linux: Do not skip entries with zero d_ino values [BZ
+ #12165]
+
+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(-)
+
+diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c
+index b480135164..c0619ce06f 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 52b11eb9d9..3aea0b1df1 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);
+-- 
+2.32.0
+

diff --git a/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch b/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch
new file mode 100644
index 0000000..90910a6
--- /dev/null
+++ b/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch
@@ -0,0 +1,204 @@
+From 5180512e6c81b1b0423572594983c74c499b7e1e Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 20 Oct 2020 13:37:15 -0300
+Subject: [PATCH 2/7] linux: Use getdents64 on non-LFS readdir
+
+The opendir allocates a translation buffer to be used to return the
+non-LFS readdir entry.  The obtained dirent64 struct is translated
+to the temporary buffer on each readdir call.
+
+Entries that overflow d_off/d_ino and the buffer reallocation failure
+(in case of large d_name) are ignored.
+
+Checked on x86_64-linux-gnu and i686-linux-gnu.
+---
+ sysdeps/unix/sysv/linux/closedir.c  |  4 ++
+ sysdeps/unix/sysv/linux/dirstream.h |  5 ++
+ sysdeps/unix/sysv/linux/opendir.c   | 21 +++++++
+ sysdeps/unix/sysv/linux/readdir.c   | 97 +++++++++++++++++++++--------
+ 4 files changed, 101 insertions(+), 26 deletions(-)
+
+diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c
+index 4bb5274b00..1f71445ad9 100644
+--- a/sysdeps/unix/sysv/linux/closedir.c
++++ b/sysdeps/unix/sysv/linux/closedir.c
+@@ -47,6 +47,10 @@ __closedir (DIR *dirp)
+   __libc_lock_fini (dirp->lock);
+ #endif
+ 
++#if !_DIRENT_MATCHES_DIRENT64
++  free (dirp->tbuffer);
++#endif
++
+   free ((void *) dirp);
+ 
+   return __close_nocancel (fd);
+diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h
+index b5e1db8db0..64b1495ba0 100644
+--- a/sysdeps/unix/sysv/linux/dirstream.h
++++ b/sysdeps/unix/sysv/linux/dirstream.h
+@@ -41,6 +41,11 @@ struct __dirstream
+ 
+     int errcode;		/* Delayed error code.  */
+ 
++#if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T
++    char *tbuffer;		/* Translation buffer for non-LFS calls.  */
++    size_t tbuffer_size;	/* Size of translation buffer.  */
++#endif
++
+     /* Directory block.  We must make sure that this block starts
+        at an address that is aligned adequately enough to store
+        dirent entries.  Using the alignment of "void *" is not
+diff --git a/sysdeps/unix/sysv/linux/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
+index 48f254d169..d7df13575e 100644
+--- a/sysdeps/unix/sysv/linux/opendir.c
++++ b/sysdeps/unix/sysv/linux/opendir.c
+@@ -120,6 +120,27 @@ __alloc_dir (int fd, bool close_fd, int flags,
+       return NULL;
+     }
+ 
++#if !_DIRENT_MATCHES_DIRENT64
++  /* Allocates a translation buffer to use as the returned 'struct direct'
++     for non-LFS 'readdir' calls.
++
++     The initial NAME_MAX size should handle most cases, while readdir might
++     expand the buffer if required.  */
++  enum
++    {
++      tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
++    };
++  dirp->tbuffer = malloc (tbuffer_size);
++  if (dirp->tbuffer == NULL)
++    {
++      free (dirp);
++      if (close_fd)
++	__close_nocancel_nostatus (fd);
++      return NULL;
++    }
++  dirp->tbuffer_size = tbuffer_size;
++#endif
++
+   dirp->fd = fd;
+ #if IS_IN (libc)
+   __libc_lock_init (dirp->lock);
+diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c
+index c0619ce06f..8647bb0aef 100644
+--- a/sysdeps/unix/sysv/linux/readdir.c
++++ b/sysdeps/unix/sysv/linux/readdir.c
+@@ -21,42 +21,87 @@
+ #if !_DIRENT_MATCHES_DIRENT64
+ #include <dirstream.h>
+ 
++/* Translate the DP64 entry to the non-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_entry (struct __dirstream *ds, const struct dirent64 *dp64)
++{
++  off_t d_off = dp64->d_off;
++  if (d_off != dp64->d_off)
++    return false;
++  ino_t d_ino = dp64->d_ino;
++  if (d_ino != dp64->d_ino)
++    return false;
++
++  /* Expand the translation buffer to hold the new name size.  */
++  size_t new_reclen = sizeof (struct dirent)
++		    + dp64->d_reclen - offsetof (struct dirent64, d_name);
++  if (new_reclen > ds->tbuffer_size)
++    {
++      char *newbuffer = realloc (ds->tbuffer, new_reclen);
++      if (newbuffer == NULL)
++	return false;
++      ds->tbuffer = newbuffer;
++      ds->tbuffer_size = new_reclen;
++    }
++
++  struct dirent *dp = (struct dirent *) ds->tbuffer;
++
++  dp->d_off = d_off;
++  dp->d_ino = d_ino;
++  dp->d_reclen = new_reclen;
++  dp->d_type = dp64->d_type;
++  memcpy (dp->d_name, dp64->d_name,
++	  dp64->d_reclen - offsetof (struct dirent64, d_name));
++
++  return true;
++}
++
+ /* Read a directory entry from DIRP.  */
+ struct dirent *
+ __readdir_unlocked (DIR *dirp)
+ {
+   const int saved_errno = errno;
+ 
+-  if (dirp->offset >= dirp->size)
++  while (1)
+     {
+-      /* We've emptied out our buffer.  Refill it.  */
+-      ssize_t bytes = __getdents (dirp->fd, dirp->data, dirp->allocation);
+-      if (bytes <= 0)
++      if (dirp->offset >= dirp->size)
+ 	{
+-	  /* 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;
++	  /* We've emptied out our buffer.  Refill it.  */
++	  ssize_t bytes = __getdents64 (dirp->fd, dirp->data,
++					dirp->allocation);
++	  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);
++	      return NULL;
++	    }
++	  dirp->size = bytes;
++
++ 	  /* Reset the offset into the buffer.  */
++	  dirp->offset = 0;
++ 	}
++
++      struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
++      dirp->offset += dp64->d_reclen;
++
++      /* Skip entries which might overflow d_off/d_ino or if the translation
++	 buffer can't be resized.  */
++      if (dirstream_entry (dirp, dp64))
++	{
++          dirp->filepos = dp64->d_off;
++	  return (struct dirent *) dirp->tbuffer;
+ 	}
+-      dirp->size = bytes;
+-
+-      /* Reset the offset into the buffer.  */
+-      dirp->offset = 0;
+     }
+-
+-  struct dirent *dp = (struct dirent *) &dirp->data[dirp->offset];
+-
+-  dirp->offset += dp->d_reclen;
+-
+-  dirp->filepos = dp->d_off;
+-
+-  return dp;
+ }
+ 
+ struct dirent *
+-- 
+2.32.0
+

diff --git a/9999/0203-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch b/9999/0203-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch
new file mode 100644
index 0000000..06e8d14
--- /dev/null
+++ b/9999/0203-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch
@@ -0,0 +1,494 @@
+From 7d2845e6ed10f2109d2e0f6432932b6693f0037d Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Mon, 13 Apr 2020 18:09:20 -0300
+Subject: [PATCH 3/7] linux: Set internal DIR filepos as off64_t [BZ #23960, BZ
+ #24050]
+
+It allows to obtain the expected entry offset on telldir and set
+it correctly on seekdir on platforms where long int is smaller
+than off64_t.
+
+On such cases telldir will mantain an internal list that maps the
+DIR object off64_t offsets to the returned long int (the function
+return value).  The seekdir will then set the correct offset from
+the internal list using the telldir as the list key.
+
+It also removes the overflow check on readdir and the returned value
+will be truncated by the non-LFS off_t size.  As Joseph has noted
+in BZ #23960 comment #22, d_off is an opaque value and since
+telldir/seekdir works regardless of the returned dirent d_off value.
+
+Finally it removed the requirement to check for overflow values on
+telldir (BZ #24050).
+
+Checked on x86_64-linux-gnu, i686-linux-gnu, powerpc-linux-gnu,
+and arm-linux-gnueabihf.
+---
+ dirent/Makefile                     |   2 +-
+ dirent/tst-seekdir2.c               | 158 ++++++++++++++++++++++++++++
+ sysdeps/unix/sysv/linux/closedir.c  |   4 +
+ sysdeps/unix/sysv/linux/dirstream.h |   6 +-
+ sysdeps/unix/sysv/linux/opendir.c   |   3 +
+ sysdeps/unix/sysv/linux/readdir.c   |   1 +
+ sysdeps/unix/sysv/linux/rewinddir.c |   5 +
+ sysdeps/unix/sysv/linux/seekdir.c   |  36 ++++++-
+ sysdeps/unix/sysv/linux/telldir.c   |  47 ++++++++-
+ sysdeps/unix/sysv/linux/telldir.h   |  64 +++++++++++
+ 10 files changed, 317 insertions(+), 9 deletions(-)
+ create mode 100644 dirent/tst-seekdir2.c
+ create mode 100644 sysdeps/unix/sysv/linux/telldir.h
+
+diff --git a/dirent/Makefile b/dirent/Makefile
+index afc7226a5b..c7f046b3f7 100644
+--- a/dirent/Makefile
++++ b/dirent/Makefile
+@@ -31,7 +31,7 @@ routines	:= opendir closedir readdir readdir_r rewinddir \
+ 		   scandir-cancel scandir-tail scandir64-tail
+ 
+ tests	   := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \
+-	      tst-fdopendir2 tst-scandir tst-scandir64
++	      tst-fdopendir2 tst-scandir tst-scandir64 tst-seekdir2
+ 
+ CFLAGS-scandir.c += $(uses-callbacks)
+ CFLAGS-scandir64.c += $(uses-callbacks)
+diff --git a/dirent/tst-seekdir2.c b/dirent/tst-seekdir2.c
+new file mode 100644
+index 0000000000..3e01b361e5
+--- /dev/null
++++ b/dirent/tst-seekdir2.c
+@@ -0,0 +1,158 @@
++/* Check multiple telldir and seekdir.
++   Copyright (C) 2020 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <https://www.gnu.org/licenses/>.  */
++
++#include <dirent.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <support/temp_file.h>
++#include <support/support.h>
++#include <support/check.h>
++
++/* Some filesystems returns a arbitrary value for d_off direnty entry (ext4
++   for instance, where the value is an internal hash key).  The idea of
++   create a large number of file is to try trigger a overflow d_off value
++   in a entry to check if telldir/seekdir does work corretly in such
++   case.  */
++static const char *dirname;
++static const size_t nfiles = 10240;
++
++static void
++do_prepare (int argc, char *argv[])
++{
++  dirname = support_create_temp_directory ("tst-seekdir2-");
++
++  for (size_t i = 0; i < nfiles; i++)
++    {
++      int fd = create_temp_file_in_dir ("tempfile.", dirname, NULL);
++      TEST_VERIFY_EXIT (fd > 0);
++      close (fd);
++    }
++}
++#define PREPARE do_prepare
++
++/* Check for old non Large File Support (LFS).  */
++static int
++do_test_not_lfs (void)
++{
++  DIR *dirp = opendir (dirname);
++  TEST_VERIFY_EXIT (dirp != NULL);
++
++  size_t dirp_count = 0;
++  for (struct dirent *dp = readdir (dirp);
++       dp != NULL;
++       dp = readdir (dirp))
++    dirp_count++;
++
++  /* The 2 extra files are '.' and '..'.  */
++  TEST_COMPARE (dirp_count, nfiles + 2);
++
++  rewinddir (dirp);
++
++  long *tdirp = xmalloc (dirp_count * sizeof (long));
++  struct dirent **ddirp = xmalloc (dirp_count * sizeof (struct dirent *));
++
++  size_t i = 0;
++  do
++    {
++      tdirp[i] = telldir (dirp);
++      struct dirent *dp = readdir (dirp);
++      TEST_VERIFY_EXIT (dp != NULL);
++      ddirp[i] = xmalloc (dp->d_reclen);
++      memcpy (ddirp[i], dp, dp->d_reclen);
++    } while (++i < dirp_count);
++
++  for (i = 0; i < dirp_count - 1; i++)
++    {
++      seekdir (dirp, tdirp[i]);
++      struct dirent *dp = readdir (dirp);
++      TEST_COMPARE (strcmp (dp->d_name, ddirp[i]->d_name), 0);
++      TEST_COMPARE (dp->d_ino, ddirp[i]->d_ino);
++      TEST_COMPARE (dp->d_off, ddirp[i]->d_off);
++    }
++
++  closedir (dirp);
++  free (tdirp);
++  for (i = 0; i < dirp_count; i++)
++    free (ddirp[i]);
++  free (ddirp);
++
++  return 0;
++}
++
++/* Same as before but with LFS support.  */
++static int
++do_test_lfs (void)
++{
++  DIR *dirp = opendir (dirname);
++  TEST_VERIFY_EXIT (dirp != NULL);
++
++  size_t dirp_count = 0;
++  for (struct dirent64 * dp = readdir64 (dirp);
++       dp != NULL;
++       dp = readdir64 (dirp))
++    dirp_count++;
++
++  /* The 2 extra files are '.' and '..'.  */
++  TEST_COMPARE (dirp_count, nfiles + 2);
++
++  rewinddir (dirp);
++
++  long *tdirp = xmalloc (dirp_count * sizeof (long));
++  struct dirent64 **ddirp = xmalloc (dirp_count * sizeof (struct dirent64 *));
++
++  size_t i = 0;
++  do
++    {
++      tdirp[i] = telldir (dirp);
++      struct dirent64 *dp = readdir64 (dirp);
++      TEST_VERIFY_EXIT (dp != NULL);
++      ddirp[i] = xmalloc (dp->d_reclen);
++      memcpy (ddirp[i], dp, dp->d_reclen);
++    } while (++i < dirp_count);
++
++  for (i = 0; i < dirp_count - 1; i++)
++    {
++      seekdir (dirp, tdirp[i]);
++      struct dirent64 *dp = readdir64 (dirp);
++      TEST_COMPARE (strcmp (dp->d_name, ddirp[i]->d_name), 0);
++      TEST_COMPARE (dp->d_ino, ddirp[i]->d_ino);
++      TEST_COMPARE (dp->d_off, ddirp[i]->d_off);
++    }
++
++  closedir (dirp);
++  free (tdirp);
++  for (i = 0; i < dirp_count; i++)
++    free (ddirp[i]);
++  free (ddirp);
++
++  return 0;
++}
++
++static int
++do_test (void)
++{
++  do_test_not_lfs ();
++  do_test_lfs ();
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c
+index 1f71445ad9..1845c48be0 100644
+--- a/sysdeps/unix/sysv/linux/closedir.c
++++ b/sysdeps/unix/sysv/linux/closedir.c
+@@ -43,6 +43,10 @@ __closedir (DIR *dirp)
+ 
+   fd = dirp->fd;
+ 
++#ifndef __LP64__
++  dirstream_loc_clear (&dirp->locs);
++#endif
++
+ #if IS_IN (libc)
+   __libc_lock_fini (dirp->lock);
+ #endif
+diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h
+index 64b1495ba0..1a3362fda6 100644
+--- a/sysdeps/unix/sysv/linux/dirstream.h
++++ b/sysdeps/unix/sysv/linux/dirstream.h
+@@ -21,6 +21,7 @@
+ #include <sys/types.h>
+ 
+ #include <libc-lock.h>
++#include <telldir.h>
+ 
+ /* Directory stream type.
+ 
+@@ -37,7 +38,7 @@ struct __dirstream
+     size_t size;		/* Total valid data in the block.  */
+     size_t offset;		/* Current offset into the block.  */
+ 
+-    off_t filepos;		/* Position of next entry to read.  */
++    off64_t filepos;		/* Position of next entry to read.  */
+ 
+     int errcode;		/* Delayed error code.  */
+ 
+@@ -45,6 +46,9 @@ struct __dirstream
+     char *tbuffer;		/* Translation buffer for non-LFS calls.  */
+     size_t tbuffer_size;	/* Size of translation buffer.  */
+ #endif
++#ifndef __LP64__
++    struct dirstream_loc_t locs; /* off64_t to long int map for telldir.  */
++#endif
+ 
+     /* Directory block.  We must make sure that this block starts
+        at an address that is aligned adequately enough to store
+diff --git a/sysdeps/unix/sysv/linux/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
+index d7df13575e..56365f9da5 100644
+--- a/sysdeps/unix/sysv/linux/opendir.c
++++ b/sysdeps/unix/sysv/linux/opendir.c
+@@ -150,6 +150,9 @@ __alloc_dir (int fd, bool close_fd, int flags,
+   dirp->offset = 0;
+   dirp->filepos = 0;
+   dirp->errcode = 0;
++#ifndef __LP64__
++  dirstream_loc_init (&dirp->locs);
++#endif
+ 
+   return dirp;
+ }
+diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c
+index 8647bb0aef..b26d2756b9 100644
+--- a/sysdeps/unix/sysv/linux/readdir.c
++++ b/sysdeps/unix/sysv/linux/readdir.c
+@@ -17,6 +17,7 @@
+    <https://www.gnu.org/licenses/>.  */
+ 
+ #include <dirent.h>
++#include <unistd.h>
+ 
+ #if !_DIRENT_MATCHES_DIRENT64
+ #include <dirstream.h>
+diff --git a/sysdeps/unix/sysv/linux/rewinddir.c b/sysdeps/unix/sysv/linux/rewinddir.c
+index 5b68db7167..74b336bfd8 100644
+--- a/sysdeps/unix/sysv/linux/rewinddir.c
++++ b/sysdeps/unix/sysv/linux/rewinddir.c
+@@ -33,6 +33,11 @@ __rewinddir (DIR *dirp)
+   dirp->offset = 0;
+   dirp->size = 0;
+   dirp->errcode = 0;
++
++#ifndef __LP64__
++  dirstream_loc_clear (&dirp->locs);
++#endif
++
+ #if IS_IN (libc)
+   __libc_lock_unlock (dirp->lock);
+ #endif
+diff --git a/sysdeps/unix/sysv/linux/seekdir.c b/sysdeps/unix/sysv/linux/seekdir.c
+index b128ae8e76..2fcf689dc0 100644
+--- a/sysdeps/unix/sysv/linux/seekdir.c
++++ b/sysdeps/unix/sysv/linux/seekdir.c
+@@ -22,14 +22,40 @@
+ #include <dirstream.h>
+ 
+ /* Seek to position POS in DIRP.  */
+-/* XXX should be __seekdir ? */
+ void
+ seekdir (DIR *dirp, long int pos)
+ {
++  off64_t filepos;
++
+   __libc_lock_lock (dirp->lock);
+-  (void) __lseek (dirp->fd, pos, SEEK_SET);
+-  dirp->size = 0;
+-  dirp->offset = 0;
+-  dirp->filepos = pos;
++
++#ifndef __LP64__
++  union dirstream_packed dsp;
++
++  dsp.l = pos;
++
++  if (dsp.p.is_packed == 1)
++    filepos = dsp.p.info;
++  else
++    {
++      size_t index = dsp.p.info;
++
++      if (index >= dirstream_loc_size (&dirp->locs))
++	return;
++      struct dirstream_loc *loc = dirstream_loc_at (&dirp->locs, index);
++      filepos = loc->filepos;
++    }
++#else
++  filepos = pos;
++#endif
++
++  if (dirp->filepos != filepos)
++    {
++      __lseek64 (dirp->fd, filepos, SEEK_SET);
++      dirp->filepos = filepos;
++      dirp->offset = 0;
++      dirp->size = 0;
++    }
++
+   __libc_lock_unlock (dirp->lock);
+ }
+diff --git a/sysdeps/unix/sysv/linux/telldir.c b/sysdeps/unix/sysv/linux/telldir.c
+index b184db8d2c..a0eb1efeff 100644
+--- a/sysdeps/unix/sysv/linux/telldir.c
++++ b/sysdeps/unix/sysv/linux/telldir.c
+@@ -18,16 +18,59 @@
+ #include <dirent.h>
+ 
+ #include <dirstream.h>
++#include <telldir.h>
+ 
+ /* Return the current position of DIRP.  */
+ long int
+ telldir (DIR *dirp)
+ {
+-  long int ret;
++#ifndef __LP64__
++  /* If the directory position fits in the packet structure returns it.
++     Otherwise, check if the position is already been recorded in the
++     dynamic array.  If not, add the new record.  */
++
++  union dirstream_packed dsp;
++  size_t i;
+ 
+   __libc_lock_lock (dirp->lock);
+-  ret = dirp->filepos;
++
++  if (dirp->filepos < (1U << 31))
++    {
++      dsp.p.is_packed = 1;
++      dsp.p.info = dirp->filepos;
++      goto out;
++    }
++
++  dsp.l = -1;
++
++  for (i = 0; i < dirstream_loc_size (&dirp->locs); i++)
++    {
++      struct dirstream_loc *loc = dirstream_loc_at (&dirp->locs, i);
++      if (loc->filepos == dirp->filepos)
++	break;
++    }
++  if (i == dirstream_loc_size (&dirp->locs))
++    {
++      dirstream_loc_add (&dirp->locs,
++	(struct dirstream_loc) { dirp->filepos });
++      if (dirstream_loc_has_failed (&dirp->locs))
++	goto out;
++    }
++
++  dsp.p.is_packed = 0;
++  /* This assignment might overflow, however most likely ENOMEM would happen
++     long before.  */
++  dsp.p.info = i;
++
++out:
+   __libc_lock_unlock (dirp->lock);
+ 
++  return dsp.l;
++#else
++  long int ret;
++  __libc_lock_lock (dirp->lock);
++  ret = dirp->filepos;
++  __libc_lock_unlock (dirp->lock);
+   return ret;
++#endif
+ }
+diff --git a/sysdeps/unix/sysv/linux/telldir.h b/sysdeps/unix/sysv/linux/telldir.h
+new file mode 100644
+index 0000000000..7c45886341
+--- /dev/null
++++ b/sysdeps/unix/sysv/linux/telldir.h
+@@ -0,0 +1,64 @@
++/* Linux internal telldir definitions.
++   Copyright (C) 2020 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <https://www.gnu.org/licenses/>.  */
++
++#ifndef _TELLDIR_H
++#define _TELLDIR_H 1
++
++#ifndef __LP64__
++
++/* On platforms where long int is smaller than off64_t this is how the
++   returned value is encoded and returned by 'telldir'.  If the directory
++   offset can be enconded in 31 bits it is returned in the 'info' member
++   with 'is_packed' set to 1.
++
++   Otherwise, the 'info' member describes an index in a dynamic array at
++   'DIR' structure.  */
++
++union dirstream_packed
++{
++  long int l;
++  struct
++  {
++    unsigned long is_packed:1;
++    unsigned long info:31;
++  } p;
++};
++
++_Static_assert (sizeof (long int) == sizeof (union dirstream_packed),
++		"sizeof (long int) != sizeof (union dirstream_packed)");
++
++/* telldir will mantain a list of offsets that describe the obtained diretory
++   position if it can fit this information in the returned 'dirstream_packed'
++   struct.  */
++
++struct dirstream_loc
++{
++  off64_t filepos;
++};
++
++# define DYNARRAY_STRUCT  dirstream_loc_t
++# define DYNARRAY_ELEMENT struct dirstream_loc
++# define DYNARRAY_PREFIX  dirstream_loc_
++# include <malloc/dynarray-skeleton.c>
++#else
++
++_Static_assert (sizeof (long int) == sizeof (off64_t),
++		"sizeof (long int) != sizeof (off64_t)");
++#endif /* __LP64__  */
++
++#endif /* _TELLDIR_H  */
+-- 
+2.32.0
+

diff --git a/9999/0204-linux-Add-__readdir64_unlocked.patch b/9999/0204-linux-Add-__readdir64_unlocked.patch
new file mode 100644
index 0000000..29609dd
--- /dev/null
+++ b/9999/0204-linux-Add-__readdir64_unlocked.patch
@@ -0,0 +1,181 @@
+From 1524804c8133564c204340a0d618f04c585d7706 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Mon, 13 Apr 2020 08:35:40 -0300
+Subject: [PATCH 4/7] linux: Add __readdir64_unlocked
+
+And use it on readdir_r implementation.
+
+Checked on i686-linux-gnu.
+---
+ include/dirent.h                      |  1 +
+ sysdeps/unix/sysv/linux/readdir64.c   | 20 +++++--
+ sysdeps/unix/sysv/linux/readdir64_r.c | 80 ++++++---------------------
+ 3 files changed, 33 insertions(+), 68 deletions(-)
+
+diff --git a/include/dirent.h b/include/dirent.h
+index d7567f5e86..0c6715d0e4 100644
+--- a/include/dirent.h
++++ b/include/dirent.h
+@@ -21,6 +21,7 @@ extern DIR *__fdopendir (int __fd) attribute_hidden;
+ extern int __closedir (DIR *__dirp) attribute_hidden;
+ extern struct dirent *__readdir (DIR *__dirp) attribute_hidden;
+ extern struct dirent *__readdir_unlocked (DIR *__dirp) attribute_hidden;
++extern struct dirent64 *__readdir64_unlocked (DIR *__dirp) attribute_hidden;
+ extern struct dirent64 *__readdir64 (DIR *__dirp);
+ libc_hidden_proto (__readdir64)
+ extern int __readdir_r (DIR *__dirp, struct dirent *__entry,
+diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
+index 3aea0b1df1..5519487ede 100644
+--- a/sysdeps/unix/sysv/linux/readdir64.c
++++ b/sysdeps/unix/sysv/linux/readdir64.c
+@@ -28,14 +28,10 @@
+ 
+ /* Read a directory entry from DIRP.  */
+ struct dirent64 *
+-__readdir64 (DIR *dirp)
++__readdir64_unlocked (DIR *dirp)
+ {
+   const int saved_errno = errno;
+ 
+-#if IS_IN (libc)
+-  __libc_lock_lock (dirp->lock);
+-#endif
+-
+   if (dirp->offset >= dirp->size)
+     {
+       /* We've emptied out our buffer.  Refill it.  */
+@@ -65,6 +61,20 @@ __readdir64 (DIR *dirp)
+ 
+   dirp->filepos = dp->d_off;
+ 
++  return dp;
++}
++
++struct dirent64 *
++__readdir64 (DIR *dirp)
++{
++  struct dirent64 *dp;
++
++#if IS_IN (libc)
++  __libc_lock_lock (dirp->lock);
++#endif
++
++  dp = __readdir64_unlocked (dirp);
++
+ #if IS_IN (libc)
+   __libc_lock_unlock (dirp->lock);
+ #endif
+diff --git a/sysdeps/unix/sysv/linux/readdir64_r.c b/sysdeps/unix/sysv/linux/readdir64_r.c
+index 073a6453d1..058d401279 100644
+--- a/sysdeps/unix/sysv/linux/readdir64_r.c
++++ b/sysdeps/unix/sysv/linux/readdir64_r.c
+@@ -32,89 +32,43 @@ __readdir64_r (DIR *dirp, struct dirent64 *entry, struct dirent64 **result)
+ {
+   struct dirent64 *dp;
+   size_t reclen;
+-  const int saved_errno = errno;
+-  int ret;
+ 
+   __libc_lock_lock (dirp->lock);
+-
+-  do
++  while (1)
+     {
+-      if (dirp->offset >= dirp->size)
+-	{
+-	  /* We've emptied out our buffer.  Refill it.  */
+-
+-	  size_t maxread = dirp->allocation;
+-	  ssize_t bytes;
+-
+-	  maxread = dirp->allocation;
+-
+-	  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;
+-		  __set_errno (saved_errno);
+-		}
+-	      if (bytes < 0)
+-		dirp->errcode = errno;
+-
+-	      dp = NULL;
+-	      break;
+-	    }
+-	  dirp->size = (size_t) bytes;
+-
+-	  /* Reset the offset into the buffer.  */
+-	  dirp->offset = 0;
+-	}
+-
+-      dp = (struct dirent64 *) &dirp->data[dirp->offset];
++      dp = __readdir64_unlocked (dirp);
++      if (dp == NULL)
++	break;
+ 
+       reclen = dp->d_reclen;
++      if (reclen <= offsetof (struct dirent64, d_name) + NAME_MAX + 1)
++	break;
+ 
+-      dirp->offset += reclen;
+-
+-      dirp->filepos = dp->d_off;
+-
+-      if (reclen > offsetof (struct dirent64, d_name) + NAME_MAX + 1)
++      /* The record is very long.  It could still fit into the caller-supplied
++	 buffer if we can skip padding at the end.  */
++      size_t namelen = _D_EXACT_NAMLEN (dp);
++      if (namelen <= NAME_MAX)
+ 	{
+-	  /* The record is very long.  It could still fit into the
+-	     caller-supplied buffer if we can skip padding at the
+-	     end.  */
+-	  size_t namelen = _D_EXACT_NAMLEN (dp);
+-	  if (namelen <= NAME_MAX)
+-	    reclen = offsetof (struct dirent64, d_name) + namelen + 1;
+-	  else
+-	    {
+-	      /* The name is too long.  Ignore this file.  */
+-	      dirp->errcode = ENAMETOOLONG;
+-	      dp->d_ino = 0;
+-	      continue;
+-	    }
++	  reclen = offsetof (struct dirent64, d_name) + namelen + 1;
++	  break;
+ 	}
+ 
+-      /* Skip deleted and ignored files.  */
++      /* The name is too long.  Ignore this file.  */
++      dirp->errcode = ENAMETOOLONG;
++      dp->d_ino = 0;
+     }
+-  while (dp->d_ino == 0);
+ 
+   if (dp != NULL)
+     {
+       *result = memcpy (entry, dp, reclen);
+       entry->d_reclen = reclen;
+-      ret = 0;
+     }
+   else
+-    {
+-      *result = NULL;
+-      ret = dirp->errcode;
+-    }
++    *result = NULL;
+ 
+   __libc_lock_unlock (dirp->lock);
+ 
+-  return ret;
++  return dp != NULL ? 0 : dirp->errcode;
+ }
+ 
+ 
+-- 
+2.32.0
+

diff --git a/9999/0205-linux-Add-__old_readdir64_unlocked.patch b/9999/0205-linux-Add-__old_readdir64_unlocked.patch
new file mode 100644
index 0000000..c1ccb61
--- /dev/null
+++ b/9999/0205-linux-Add-__old_readdir64_unlocked.patch
@@ -0,0 +1,184 @@
+From f16c3815c6ad46450d6e58e179ddf494c52a98a4 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 14 Apr 2020 11:14:22 -0300
+Subject: [PATCH 5/7] linux: Add __old_readdir64_unlocked
+
+And use it __old_readdir64_r.
+
+Checked on i686-linux-gnu.
+---
+ sysdeps/unix/sysv/linux/olddirent.h   |  2 +
+ sysdeps/unix/sysv/linux/readdir64.c   | 21 +++++--
+ sysdeps/unix/sysv/linux/readdir64_r.c | 79 ++++++---------------------
+ 3 files changed, 35 insertions(+), 67 deletions(-)
+
+diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
+index 3e672d47f5..42ab593c4d 100644
+--- a/sysdeps/unix/sysv/linux/olddirent.h
++++ b/sysdeps/unix/sysv/linux/olddirent.h
+@@ -32,6 +32,8 @@ struct __old_dirent64
+ /* Now define the internal interfaces.  */
+ extern struct __old_dirent64 *__old_readdir64 (DIR *__dirp);
+ libc_hidden_proto (__old_readdir64);
++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)
+diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
+index 5519487ede..8869e49150 100644
+--- a/sysdeps/unix/sysv/linux/readdir64.c
++++ b/sysdeps/unix/sysv/linux/readdir64.c
+@@ -101,15 +101,11 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
+ 
+ attribute_compat_text_section
+ struct __old_dirent64 *
+-__old_readdir64 (DIR *dirp)
++__old_readdir64_unlocked (DIR *dirp)
+ {
+   struct __old_dirent64 *dp;
+   int saved_errno = errno;
+ 
+-#if IS_IN (libc)
+-  __libc_lock_lock (dirp->lock);
+-#endif
+-
+   do
+     {
+       size_t reclen;
+@@ -153,6 +149,21 @@ __old_readdir64 (DIR *dirp)
+       /* Skip deleted files.  */
+     } while (dp->d_ino == 0);
+ 
++  return dp;
++}
++
++attribute_compat_text_section
++struct __old_dirent64 *
++__old_readdir64 (DIR *dirp)
++{
++  struct __old_dirent64 *dp;
++
++#if IS_IN (libc)
++  __libc_lock_lock (dirp->lock);
++#endif
++
++  dp = __old_readdir64_unlocked (dirp);
++
+ #if IS_IN (libc)
+   __libc_lock_unlock (dirp->lock);
+ #endif
+diff --git a/sysdeps/unix/sysv/linux/readdir64_r.c b/sysdeps/unix/sysv/linux/readdir64_r.c
+index 058d401279..e4a0baeaf7 100644
+--- a/sysdeps/unix/sysv/linux/readdir64_r.c
++++ b/sysdeps/unix/sysv/linux/readdir64_r.c
+@@ -91,89 +91,44 @@ __old_readdir64_r (DIR *dirp, struct __old_dirent64 *entry,
+ {
+   struct __old_dirent64 *dp;
+   size_t reclen;
+-  const int saved_errno = errno;
+-  int ret;
+ 
+   __libc_lock_lock (dirp->lock);
+ 
+-  do
++  while (1)
+     {
+-      if (dirp->offset >= dirp->size)
+-	{
+-	  /* We've emptied out our buffer.  Refill it.  */
+-
+-	  size_t maxread = dirp->allocation;
+-	  ssize_t bytes;
+-
+-	  maxread = dirp->allocation;
+-
+-	  bytes = __old_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;
+-		  __set_errno (saved_errno);
+-		}
+-	      if (bytes < 0)
+-		dirp->errcode = errno;
+-
+-	      dp = NULL;
+-	      break;
+-	    }
+-	  dirp->size = (size_t) bytes;
+-
+-	  /* Reset the offset into the buffer.  */
+-	  dirp->offset = 0;
+-	}
+-
+-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
++      dp = __old_readdir64_unlocked (dirp);
++      if (dp == NULL)
++	break;
+ 
+       reclen = dp->d_reclen;
++      if (reclen <= offsetof (struct __old_dirent64, d_name) + NAME_MAX + 1)
++	break;
+ 
+-      dirp->offset += reclen;
+-
+-      dirp->filepos = dp->d_off;
+-
+-      if (reclen > offsetof (struct __old_dirent64, d_name) + NAME_MAX + 1)
++      /* The record is very long.  It could still fit into the caller-supplied
++	 buffer if we can skip padding at the end.  */
++      size_t namelen = _D_EXACT_NAMLEN (dp);
++      if (namelen <= NAME_MAX)
+ 	{
+-	  /* The record is very long.  It could still fit into the
+-	     caller-supplied buffer if we can skip padding at the
+-	     end.  */
+-	  size_t namelen = _D_EXACT_NAMLEN (dp);
+-	  if (namelen <= NAME_MAX)
+-	    reclen = offsetof (struct __old_dirent64, d_name) + namelen + 1;
+-	  else
+-	    {
+-	      /* The name is too long.  Ignore this file.  */
+-	      dirp->errcode = ENAMETOOLONG;
+-	      dp->d_ino = 0;
+-	      continue;
+-	    }
++	  reclen = offsetof (struct dirent64, d_name) + namelen + 1;
++	  break;
+ 	}
+ 
+-      /* Skip deleted and ignored files.  */
++      /* The name is too long.  Ignore this file.  */
++      dirp->errcode = ENAMETOOLONG;
++      dp->d_ino = 0;
+     }
+-  while (dp->d_ino == 0);
+ 
+   if (dp != NULL)
+     {
+       *result = memcpy (entry, dp, reclen);
+       entry->d_reclen = reclen;
+-      ret = 0;
+     }
+   else
+-    {
+-      *result = NULL;
+-      ret = dirp->errcode;
+-    }
++    *result = NULL;
+ 
+   __libc_lock_unlock (dirp->lock);
+ 
+-  return ret;
++  return dp != NULL ? 0 : dirp->errcode;
+ }
+ 
+ compat_symbol (libc, __old_readdir64_r, readdir64_r, GLIBC_2_1);
+-- 
+2.32.0
+

diff --git a/9999/0206-linux-Use-getdents64-on-readdir64-compat-implementat.patch b/9999/0206-linux-Use-getdents64-on-readdir64-compat-implementat.patch
new file mode 100644
index 0000000..90f5d89
--- /dev/null
+++ b/9999/0206-linux-Use-getdents64-on-readdir64-compat-implementat.patch
@@ -0,0 +1,297 @@
+From 46b9383118182cb2ac2e81637e00fde6c21097bb Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 20 Oct 2020 16:00:43 -0300
+Subject: [PATCH 6/7] 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.
+---
+ 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  | 104 +++++++++++++++++----------
+ 4 files changed, 79 insertions(+), 135 deletions(-)
+
+diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
+index 6323e003b3..38285e9f4b 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 42ab593c4d..b7c51d5ccc 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 56365f9da5..de722c98e1 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 8869e49150..7ecc8c1b16 100644
+--- a/sysdeps/unix/sysv/linux/readdir64.c
++++ b/sysdeps/unix/sysv/linux/readdir64.c
+@@ -99,57 +99,83 @@ 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;
+ 
+-  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 = __old_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;
+-	}
+-
+-      dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
++	  /* 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;
+ 
+-      reclen = dp->d_reclen;
++	  /* Don't modifiy errno when reaching EOF.  */
++	  if (bytes == 0)
++	    __set_errno (saved_errno);
++	  return NULL;
++	}
++      dirp->size = bytes;
+ 
+-      dirp->offset += reclen;
++      /* Reset the offset into the buffer.  */
++      dirp->offset = 0;
++    }
+ 
+-      dirp->filepos = dp->d_off;
++  struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
++  dirp->offset += dp64->d_reclen;
+ 
+-      /* Skip deleted files.  */
+-    } while (dp->d_ino == 0);
++  /* 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 dp;
++  return NULL;
+ }
+ 
+ attribute_compat_text_section
+-- 
+2.32.0
+

diff --git a/9999/0207-dirent-Deprecate-getdirentries.patch b/9999/0207-dirent-Deprecate-getdirentries.patch
new file mode 100644
index 0000000..7b00018
--- /dev/null
+++ b/9999/0207-dirent-Deprecate-getdirentries.patch
@@ -0,0 +1,101 @@
+From 8180167096d51c9767888a695e60a542b64813f0 Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Fri, 17 Apr 2020 09:59:51 -0300
+Subject: [PATCH 7/7] dirent: Deprecate getdirentries
+
+The interface has some issues:
+
+  1. It is build on top getdents on Linux and requires handling
+     non-LFS call using LFS getdents.
+
+  2. It is not wildly used and the non-LFS support is as problematic
+     as non-LFS readdir.  glibc only exports the LFS getdents.
+
+  3. It is not a direct replacement over BSD since on some plataform
+     its signature has changed (FreeBSD 11, for instance, used to
+     set the offset as a 'long' and changed to 'off_t' on version 12).
+
+The idea is to eventually move the symbols to compat ones.
+---
+ NEWS                             |  5 +++++
+ dirent/dirent.h                  | 14 ++++++++++----
+ sysdeps/unix/sysv/linux/Makefile |  3 +++
+ 3 files changed, 18 insertions(+), 4 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index d9b344027b..a18a1d7a8c 100644
+--- a/NEWS
++++ b/NEWS
+@@ -7,6 +7,11 @@ using `glibc' in the "product" field.
+ \f
+ Version 2.34.1
+ 
++Deprecated and removed features, and other changes affecting compatibility:
++
++* The function getdirentries is now deprecated, applications should use
++  either getdents64, readdir64 or readdir.
++
+ The following bugs are resolved with this release:
+ 
+   [12889] nptl: Fix race between pthread_kill and thread exit
+diff --git a/dirent/dirent.h b/dirent/dirent.h
+index 1d1fab7e55..8ad5fbf430 100644
+--- a/dirent/dirent.h
++++ b/dirent/dirent.h
+@@ -348,29 +348,35 @@ extern int alphasort64 (const struct dirent64 **__e1,
+ /* Read directory entries from FD into BUF, reading at most NBYTES.
+    Reading starts at offset *BASEP, and *BASEP is updated with the new
+    position after reading.  Returns the number of bytes read; zero when at
+-   end of directory; or -1 for errors.  */
++   end of directory; or -1 for errors.
++   This is deprecated and getdents64 or readdir should be used instead.  */
+ # ifndef __USE_FILE_OFFSET64
+ extern __ssize_t getdirentries (int __fd, char *__restrict __buf,
+ 				size_t __nbytes,
+ 				__off_t *__restrict __basep)
+-     __THROW __nonnull ((2, 4));
++     __THROW __nonnull ((2, 4))
++     __attribute_deprecated_msg__ ("Use getdents64 instead");
+ # else
+ #  ifdef __REDIRECT
+ extern __ssize_t __REDIRECT_NTH (getdirentries,
+ 				 (int __fd, char *__restrict __buf,
+ 				  size_t __nbytes,
+ 				  __off64_t *__restrict __basep),
+-				 getdirentries64) __nonnull ((2, 4));
++				 getdirentries64)
++     __THROW __nonnull ((2, 4))
++     __attribute_deprecated_msg__ ("Use getdents64 instead");
+ #  else
+ #   define getdirentries getdirentries64
+ #  endif
+ # endif
+ 
+ # ifdef __USE_LARGEFILE64
++/* This is deprecated and getdents64 or readdir64 should be used instead.  */
+ extern __ssize_t getdirentries64 (int __fd, char *__restrict __buf,
+ 				  size_t __nbytes,
+ 				  __off64_t *__restrict __basep)
+-     __THROW __nonnull ((2, 4));
++     __THROW __nonnull ((2, 4))
++     __attribute_deprecated_msg__ ("Use getdents64 instead");
+ # endif
+ #endif /* Use misc.  */
+ 
+diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
+index 76ad06361c..65ec7529f6 100644
+--- a/sysdeps/unix/sysv/linux/Makefile
++++ b/sysdeps/unix/sysv/linux/Makefile
+@@ -313,6 +313,9 @@ tests += tst-getdents64
+ # The tested readdir64 symbol was replaced in glibc 2.2.
+ ifeq ($(have-GLIBC_2.1.3)$(build-shared),yesyes)
+ tests += tst-readdir64-compat
++
++# Avoid the warning for the weak_alias for _DIRENT_MATCHES_DIRENT64
++CFLAGS-getdirentries64.c = -Wno-deprecated-declarations
+ endif
+ endif # $(subdir) == dirent
+ 
+-- 
+2.32.0
+


             reply	other threads:[~2022-01-04 11:00 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-01-04 11:00 Andreas K. Hüttel [this message]
  -- strict thread matches above, loose matches on Subject: below --
2024-08-26 16:38 [gentoo-commits] proj/toolchain/glibc-patches:master commit in: 9999/ Sam James
2024-02-19  0:52 Andreas K. Hüttel
2024-01-11 23:28 Andreas K. Hüttel
2024-01-11 23:27 Andreas K. Hüttel
2023-12-25 20:15 Andreas K. Hüttel
2023-12-25 20:11 Andreas K. Hüttel
2023-12-23 21:58 Andreas K. Hüttel
2023-10-04 19:13 Andreas K. Hüttel
2023-09-11 17:21 Andreas K. Hüttel
2023-07-17 20:40 Andreas K. Hüttel
2023-02-01 19:47 Andreas K. Hüttel
2023-01-01 18:42 Andreas K. Hüttel
2022-09-20 17:56 Andreas K. Hüttel
2022-09-19 21:26 Andreas K. Hüttel
2022-08-08 21:22 Andreas K. Hüttel
2022-07-29 12:20 WANG Xuerui
2022-07-05  4:02 Andreas K. Hüttel
2022-04-16 11:54 Andreas K. Hüttel
2022-04-16 11:54 Andreas K. Hüttel
2022-04-16 11:54 Andreas K. Hüttel
2022-03-27 16:12 Andreas K. Hüttel
2022-03-07  1:04 Andreas K. Hüttel
2022-02-21 21:42 Andreas K. Hüttel
2022-02-12 18:45 Andreas K. Hüttel
2022-01-06 15:13 Andreas K. Hüttel
2022-01-05 21:19 Andreas K. Hüttel
2021-12-01 16:30 Andreas K. Hüttel
2021-12-01 16:17 Andreas K. Hüttel
2021-08-02 22:42 Andreas K. Hüttel
2021-08-02 22:41 Andreas K. Hüttel
2021-07-22 23:17 Andreas K. Hüttel
2021-05-25 20:30 Andreas K. Hüttel
2021-04-10 15:34 Andreas K. Hüttel
2021-02-27 19:18 Andreas K. Hüttel
2020-07-23 23:20 Andreas K. Hüttel
2020-07-18 16:23 Andreas K. Hüttel
2020-05-13  8:52 Andreas K. Hüttel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1641293919.fb003cf688e133a0fe792bee345b400735b7c333.dilfridge@gentoo \
    --to=dilfridge@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox