summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas K. Hüttel <dilfridge@gentoo.org>2022-01-04 11:58:39 +0100
committerAndreas K. Hüttel <dilfridge@gentoo.org>2022-01-04 11:58:39 +0100
commitfb003cf688e133a0fe792bee345b400735b7c333 (patch)
tree27717f1388d2720d6a180482c176c13127d7fa16
parentDrop C.UTF8 patch, finally that locale has been added upstream (diff)
downloadglibc-patches-fb003cf688e133a0fe792bee345b400735b7c333.tar.gz
glibc-patches-fb003cf688e133a0fe792bee345b400735b7c333.tar.bz2
glibc-patches-fb003cf688e133a0fe792bee345b400735b7c333.zip
Add patch series from azanella to fix 32bit qemu on 64bit filesystem regression
Signed-off-by: Andreas K. Hüttel <dilfridge@gentoo.org>
-rw-r--r--9999/0201-linux-Do-not-skip-entries-with-zero-d_ino-values-BZ-.patch182
-rw-r--r--9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch204
-rw-r--r--9999/0203-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch494
-rw-r--r--9999/0204-linux-Add-__readdir64_unlocked.patch181
-rw-r--r--9999/0205-linux-Add-__old_readdir64_unlocked.patch184
-rw-r--r--9999/0206-linux-Use-getdents64-on-readdir64-compat-implementat.patch297
-rw-r--r--9999/0207-dirent-Deprecate-getdirentries.patch101
7 files changed, 1643 insertions, 0 deletions
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.
+
+ 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
+