From fb003cf688e133a0fe792bee345b400735b7c333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=2E=20H=C3=BCttel?= Date: Tue, 4 Jan 2022 11:58:39 +0100 Subject: Add patch series from azanella to fix 32bit qemu on 64bit filesystem regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas K. Hüttel --- ...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(+) create mode 100644 9999/0201-linux-Do-not-skip-entries-with-zero-d_ino-values-BZ-.patch create mode 100644 9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch create mode 100644 9999/0203-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch create mode 100644 9999/0204-linux-Add-__readdir64_unlocked.patch create mode 100644 9999/0205-linux-Add-__old_readdir64_unlocked.patch create mode 100644 9999/0206-linux-Use-getdents64-on-readdir64-compat-implementat.patch create mode 100644 9999/0207-dirent-Deprecate-getdirentries.patch 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 +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 +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 + ++/* 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 +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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* 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 +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 + + #include ++#include + + /* 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 @@ + . */ + + #include ++#include + + #if !_DIRENT_MATCHES_DIRENT64 + #include +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 + + /* 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 + + #include ++#include + + /* 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 ++ . */ ++ ++#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 ++#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 +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 +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 +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 +- +-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +-# include +-# include +- +-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 + ++#include ++#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) ++# include ++#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 + ++/* 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 +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 + -- cgit v1.2.3