From d53894fd2ecb4fe3ae4242b36d314d95f8222a3e Mon Sep 17 00:00:00 2001 From: "Andreas K. Hüttel" Date: Fri, 12 Jan 2024 00:27:10 +0100 Subject: Update qemu patch series from azanella/bz23960-dirent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas K. Hüttel --- ...4-linux-Use-getdents64-on-non-LFS-readdir.patch | 179 +++---- ...nternal-DIR-filepos-as-off64_t-BZ-23960-B.patch | 534 ------------------- 9999/0005-support-Add-xreallocarray.patch | 76 +++ 9999/0006-linux-Add-__readdir64_unlocked.patch | 182 ------- ...nternal-DIR-filepos-as-off64_t-BZ-23960-B.patch | 574 +++++++++++++++++++++ 9999/0007-linux-Add-__old_readdir64_unlocked.patch | 194 ------- ...etdents64-on-readdir64-compat-implementat.patch | 250 --------- 7 files changed, 717 insertions(+), 1272 deletions(-) delete mode 100644 9999/0005-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch create mode 100644 9999/0005-support-Add-xreallocarray.patch delete mode 100644 9999/0006-linux-Add-__readdir64_unlocked.patch create mode 100644 9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch delete mode 100644 9999/0007-linux-Add-__old_readdir64_unlocked.patch delete mode 100644 9999/0008-linux-Use-getdents64-on-readdir64-compat-implementat.patch diff --git a/9999/0004-linux-Use-getdents64-on-non-LFS-readdir.patch b/9999/0004-linux-Use-getdents64-on-non-LFS-readdir.patch index a451fd6..ddb3660 100644 --- a/9999/0004-linux-Use-getdents64-on-non-LFS-readdir.patch +++ b/9999/0004-linux-Use-getdents64-on-non-LFS-readdir.patch @@ -1,114 +1,29 @@ -From 782410070379ffbf54cb8abe71cc13fd24cfaea5 Mon Sep 17 00:00:00 2001 +From 3fac92713621f894d33a9f499bacb5b20f0d8ec3 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella -Date: Fri, 27 Jan 2023 14:28:30 -0300 -Subject: [PATCH 4/9] linux: Use getdents64 on non-LFS readdir +Date: Tue, 20 Oct 2020 13:37:15 -0300 +Subject: [PATCH 1/3] linux: Use getdents64 on non-LFS readdir -The non-LFS opendir reserves a translation entry to be used to return -the entry and the 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. +It is similar to what non-LFS getdents do (including overflow check). Checked on x86_64-linux-gnu and i686-linux-gnu. --- - dirent/tst-scandir.c | 6 ++- - include/dirent.h | 2 +- - sysdeps/unix/sysv/linux/dirstream.h | 5 ++ - sysdeps/unix/sysv/linux/readdir.c | 83 +++++++++++++++++++---------- - 4 files changed, 67 insertions(+), 29 deletions(-) + sysdeps/unix/sysv/linux/readdir.c | 97 +++++++++++++++++++++++-------- + 1 file changed, 73 insertions(+), 24 deletions(-) -diff --git a/dirent/tst-scandir.c b/dirent/tst-scandir.c -index 8d87d4dd74..7bc666449e 100644 ---- a/dirent/tst-scandir.c -+++ b/dirent/tst-scandir.c -@@ -155,8 +155,12 @@ do_test (void) - } - if (n != 6) - { -+ /* Non-lfs opendir skips entries that can not be represented (for -+ instance if d_off is not an offset but rather an internal filesystem -+ representation. For this case there is no point in continue the -+ testcase. */ - printf ("scandir returned %d entries instead of 6\n", n); -- return 1; -+ return EXIT_UNSUPPORTED; - } - - struct -diff --git a/include/dirent.h b/include/dirent.h -index d7567f5e86..17827176ba 100644 ---- a/include/dirent.h -+++ b/include/dirent.h -@@ -1,8 +1,8 @@ - #ifndef _DIRENT_H -+# include - # ifndef _ISOMAC - # include - # endif --# include - # ifndef _ISOMAC - # include - # include -diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h -index 3cb313b410..adcf8234f1 100644 ---- a/sysdeps/unix/sysv/linux/dirstream.h -+++ b/sysdeps/unix/sysv/linux/dirstream.h -@@ -18,6 +18,7 @@ - #ifndef _DIRSTREAM_H - #define _DIRSTREAM_H 1 - -+#include - #include - - #include -@@ -41,6 +42,10 @@ struct __dirstream - - int errcode; /* Delayed error code. */ - -+#if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T -+ struct dirent tdp; -+#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/readdir.c b/sysdeps/unix/sysv/linux/readdir.c -index 4a4c00ea07..cd0ccaf33a 100644 +index 33bae4b57d..4e2214f21e 100644 --- a/sysdeps/unix/sysv/linux/readdir.c +++ b/sysdeps/unix/sysv/linux/readdir.c -@@ -21,42 +21,71 @@ +@@ -20,43 +20,92 @@ + #if !_DIRENT_MATCHES_DIRENT64 #include - -+/* Translate the DP64 entry to the non-LFS one in the translation entry -+ at dirstream DS. Return true is the translation was possible or -+ false if either an internal field can not be represented in the non-LFS -+ entry or if the name is too long. */ -+static bool -+dirstream_entry (struct __dirstream *ds, const struct dirent64 *dp64) -+{ -+ /* Check for overflow. */ -+ if (!in_off_t_range (dp64->d_off) || !in_ino_t_range (dp64->d_ino)) -+ return false; -+ -+ /* And if name is too large. */ -+ if (dp64->d_reclen - offsetof (struct dirent64, d_name) > NAME_MAX) -+ return false; -+ -+ ds->filepos = dp64->d_off; -+ -+ ds->tdp.d_off = dp64->d_off; -+ ds->tdp.d_ino = dp64->d_ino; -+ ds->tdp.d_reclen = sizeof (struct dirent) -+ + dp64->d_reclen - offsetof (struct dirent64, d_name); -+ ds->tdp.d_type = dp64->d_type; -+ memcpy (ds->tdp.d_name, dp64->d_name, -+ dp64->d_reclen - offsetof (struct dirent64, d_name)); -+ -+ return true; -+} ++#include + ++# ifndef DIRENT_SET_DP_INO ++# define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value) ++# endif + /* Read a directory entry from DIRP. */ struct dirent * __readdir_unlocked (DIR *dirp) @@ -134,13 +49,6 @@ index 4a4c00ea07..cd0ccaf33a 100644 - do not set errno in that case, to indicate success. */ - if (bytes == 0 || errno == ENOENT) - __set_errno (saved_errno); -- return NULL; -- } -- dirp->size = (size_t) bytes; -- -- /* Reset the offset into the buffer. */ -- dirp->offset = 0; -+ /* We've emptied out our buffer. Refill it. */ + ssize_t bytes = __getdents64 (dirp->fd, dirp->data, + dirp->allocation); + if (bytes <= 0) @@ -159,13 +67,60 @@ index 4a4c00ea07..cd0ccaf33a 100644 + dirp->offset = 0; + } + -+ struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; -+ dirp->offset += dp64->d_reclen; ++ /* These two pointers might alias the same memory buffer. Standard C ++ requires that we always use the same type for them, so we must use the ++ union type. */ ++ union ++ { ++ struct dirent64 dp64; ++ struct dirent dp; ++ char *b; ++ } *inp, *outp; ++ inp = (void*) &dirp->data[dirp->offset]; ++ outp = (void*) &dirp->data[dirp->offset]; ++ ++ const size_t size_diff = offsetof (struct dirent64, d_name) ++ - offsetof (struct dirent, d_name); ++ ++ /* Since inp->dp64.d_reclen is already aligned for the kernel structure ++ this may compute a value that is bigger than necessary. */ ++ size_t old_reclen = inp->dp64.d_reclen; ++ size_t new_reclen = ALIGN_UP (old_reclen - size_diff, ++ _Alignof (struct dirent)); ++ ++ if (!in_ino_t_range (inp->dp64.d_ino) ++ || !in_off_t_range (inp->dp64.d_off)) ++ { ++ /* Overflow. If there was at least one entry before this one, ++ return them without error, otherwise signal overflow. */ ++ if (dirp->offset != 0) ++ { ++ __lseek64 (dirp->fd, dirp->offset, SEEK_SET); ++ outp = (void*)(outp->b - dirp->data); ++ return &outp->dp; ++ } ++ __set_errno (EOVERFLOW); + return NULL; + } +- dirp->size = (size_t) bytes; + +- /* Reset the offset into the buffer. */ +- dirp->offset = 0; ++ /* Copy the data from INP and access only OUTP. */ ++ const uint64_t d_ino = inp->dp64.d_ino; ++ const int64_t d_off = inp->dp64.d_off; ++ const uint8_t d_type = inp->dp64.d_type; ++ outp->dp.d_ino = d_ino; ++ outp->dp.d_off = d_off; ++ outp->dp.d_reclen = new_reclen; ++ outp->dp.d_type = d_type; ++ memmove (outp->dp.d_name, inp->dp64.d_name, ++ old_reclen - offsetof (struct dirent64, d_name)); ++ ++ dirp->filepos = d_off; ++ dirp->offset += old_reclen; + -+ /* Skip entries which might overflow d_off/d_ino or if the translation -+ buffer can not be resized. */ -+ if (dirstream_entry (dirp, dp64)) -+ return &dirp->tdp; ++ return &outp->dp; } - - dp = (struct dirent *) &dirp->data[dirp->offset]; diff --git a/9999/0005-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch b/9999/0005-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch deleted file mode 100644 index 9b566a2..0000000 --- a/9999/0005-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch +++ /dev/null @@ -1,534 +0,0 @@ -From 8b628a35f0ce66d74be6557e74e28a49a58d749a Mon Sep 17 00:00:00 2001 -From: Adhemerval Zanella -Date: Fri, 27 Jan 2023 14:28:31 -0300 -Subject: [PATCH 5/9] 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 opendir creates a map entry between the DIR d_off -offset and the returned long int (the telldir return value). -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 removes 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/tst-seekdir.c | 8 ++ - sysdeps/unix/sysv/linux/Makefile | 1 + - sysdeps/unix/sysv/linux/alpha/bits/dirent.h | 3 + - sysdeps/unix/sysv/linux/bits/dirent.h | 4 + - 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 | 11 +- - sysdeps/unix/sysv/linux/rewinddir.c | 5 + - sysdeps/unix/sysv/linux/seekdir.c | 35 ++++- - sysdeps/unix/sysv/linux/telldir.c | 35 +++++ - sysdeps/unix/sysv/linux/telldir.h | 65 +++++++++ - sysdeps/unix/sysv/linux/tst-opendir-nolfs.c | 146 ++++++++++++++++++++ - 13 files changed, 319 insertions(+), 7 deletions(-) - create mode 100644 sysdeps/unix/sysv/linux/telldir.h - create mode 100644 sysdeps/unix/sysv/linux/tst-opendir-nolfs.c - -diff --git a/dirent/tst-seekdir.c b/dirent/tst-seekdir.c -index dcdd699b09..187eda7584 100644 ---- a/dirent/tst-seekdir.c -+++ b/dirent/tst-seekdir.c -@@ -41,6 +41,14 @@ do_test (void) - if (i == 400) - break; - } -+ if (i < 3) -+ { -+ /* Non-lfs opendir skips entries that can not be represented (for -+ instance if d_off is not an offset but rather an internal filesystem -+ representation. For this case there is no point in continue the -+ testcase. */ -+ return 77; -+ } - - printf ("going back past 4-th entry...\n"); - -diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile -index 415aa1f14d..dc204883db 100644 ---- a/sysdeps/unix/sysv/linux/Makefile -+++ b/sysdeps/unix/sysv/linux/Makefile -@@ -574,6 +574,7 @@ sysdep_routines += \ - - tests += \ - tst-getdents64 \ -+ tst-opendir-nolfs \ - tst-readdir64-compat \ - # tests - endif # $(subdir) == dirent -diff --git a/sysdeps/unix/sysv/linux/alpha/bits/dirent.h b/sysdeps/unix/sysv/linux/alpha/bits/dirent.h -index c8a0cfe93f..586d75586a 100644 ---- a/sysdeps/unix/sysv/linux/alpha/bits/dirent.h -+++ b/sysdeps/unix/sysv/linux/alpha/bits/dirent.h -@@ -54,4 +54,7 @@ struct dirent64 - /* Inform libc code that these two types are effectively identical. */ - #define _DIRENT_MATCHES_DIRENT64 1 - -+/* alpha 'long int' is enough to handle off64_t. */ -+#define _DIRENT_OFFSET_TRANSLATION 0 -+ - #endif /* bits/dirent.h */ -diff --git a/sysdeps/unix/sysv/linux/bits/dirent.h b/sysdeps/unix/sysv/linux/bits/dirent.h -index ab34d986ff..bb02dcb70a 100644 ---- a/sysdeps/unix/sysv/linux/bits/dirent.h -+++ b/sysdeps/unix/sysv/linux/bits/dirent.h -@@ -57,3 +57,7 @@ struct dirent64 - #else - # define _DIRENT_MATCHES_DIRENT64 0 - #endif -+ -+/* The telldir function returns long int, which may not be large enough to -+ store off64_t values. In this case, translation is required. */ -+#define _DIRENT_OFFSET_TRANSLATION (LONG_WIDTH < 64) -diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c -index f1c2608642..9585a6ca3a 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_OFFSET_TRANSLATION -+ dirstream_loc_clear (&dirp->locs); -+#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 adcf8234f1..8f58a1c3a6 100644 ---- a/sysdeps/unix/sysv/linux/dirstream.h -+++ b/sysdeps/unix/sysv/linux/dirstream.h -@@ -22,6 +22,7 @@ - #include - - #include -+#include - - /* Directory stream type. - -@@ -38,13 +39,16 @@ 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. */ - - #if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T - struct dirent tdp; - #endif -+#if _DIRENT_OFFSET_TRANSLATION -+ 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 4336196a4d..3e2caabb9d 100644 ---- a/sysdeps/unix/sysv/linux/opendir.c -+++ b/sysdeps/unix/sysv/linux/opendir.c -@@ -129,6 +129,9 @@ __alloc_dir (int fd, bool close_fd, int flags, - dirp->offset = 0; - dirp->filepos = 0; - dirp->errcode = 0; -+#if _DIRENT_OFFSET_TRANSLATION -+ 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 cd0ccaf33a..7a7f484c36 100644 ---- a/sysdeps/unix/sysv/linux/readdir.c -+++ b/sysdeps/unix/sysv/linux/readdir.c -@@ -36,6 +36,15 @@ dirstream_entry (struct __dirstream *ds, const struct dirent64 *dp64) - if (dp64->d_reclen - offsetof (struct dirent64, d_name) > NAME_MAX) - return false; - -+ /* telldir can not return an error, so preallocate the map if the entry can -+ not be packed directly. */ -+ if (telldir_need_dirstream (dp64->d_off)) -+ { -+ dirstream_loc_add (&ds->locs, dp64->d_off); -+ if (dirstream_loc_has_failed (&ds->locs)) -+ return false; -+ } -+ - ds->filepos = dp64->d_off; - - ds->tdp.d_off = dp64->d_off; -@@ -76,7 +85,7 @@ __readdir_unlocked (DIR *dirp) - - /* Reset the offset into the buffer. */ - dirp->offset = 0; -- } -+ } - - struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; - dirp->offset += dp64->d_reclen; -diff --git a/sysdeps/unix/sysv/linux/rewinddir.c b/sysdeps/unix/sysv/linux/rewinddir.c -index c0fb7aa765..1b158a584f 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 939ccc4447..30cce691a4 100644 ---- a/sysdeps/unix/sysv/linux/seekdir.c -+++ b/sysdeps/unix/sysv/linux/seekdir.c -@@ -22,14 +22,39 @@ - #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; -+ -+#if _DIRENT_OFFSET_TRANSLATION -+ union dirstream_packed 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)) -+ { -+ __libc_lock_unlock (dirp->lock); -+ return; -+ } -+ filepos = *dirstream_loc_at (&dirp->locs, index); -+ } -+#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 1e5c129e9f..c3ef14f3da 100644 ---- a/sysdeps/unix/sysv/linux/telldir.c -+++ b/sysdeps/unix/sysv/linux/telldir.c -@@ -15,9 +15,11 @@ - License along with the GNU C Library; if not, see - . */ - -+#include - #include - - #include -+#include - - /* Return the current position of DIRP. */ - long int -@@ -26,7 +28,40 @@ telldir (DIR *dirp) - long int ret; - - __libc_lock_lock (dirp->lock); -+ -+#if _DIRENT_OFFSET_TRANSLATION -+ /* 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; -+ -+ if (!telldir_need_dirstream (dirp->filepos)) -+ { -+ dsp.p.is_packed = 1; -+ dsp.p.info = dirp->filepos; -+ } -+ else -+ { -+ dsp.l = -1; -+ -+ size_t i; -+ for (i = 0; i < dirstream_loc_size (&dirp->locs); i++) -+ if (*dirstream_loc_at (&dirp->locs, i) == dirp->filepos) -+ break; -+ /* It should be pre-allocated on readdir. */ -+ assert (i != dirstream_loc_size (&dirp->locs)); -+ -+ dsp.p.is_packed = 0; -+ /* This assignment might overflow, however most likely ENOME would -+ happen long before. */ -+ dsp.p.info = i; -+ } -+ -+ ret = dsp.l; -+#else - ret = dirp->filepos; -+#endif - __libc_lock_unlock (dirp->lock); - - return ret; -diff --git a/sysdeps/unix/sysv/linux/telldir.h b/sysdeps/unix/sysv/linux/telldir.h -new file mode 100644 -index 0000000000..758bcb0eb3 ---- /dev/null -+++ b/sysdeps/unix/sysv/linux/telldir.h -@@ -0,0 +1,65 @@ -+/* Linux internal telldir definitions. -+ Copyright (C) 2023 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 -+ -+#if _DIRENT_OFFSET_TRANSLATION -+/* 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 int is_packed:1; -+ unsigned long int info:31; -+ } p; -+}; -+ -+_Static_assert (sizeof (long int) == sizeof (union dirstream_packed), -+ "sizeof (long int) != sizeof (union dirstream_packed)"); -+ -+/* telldir maintains a list of offsets that describe the obtained diretory -+ position if it can fit this information in the returned 'dirstream_packed' -+ struct. */ -+ -+# define DYNARRAY_STRUCT dirstream_loc_t -+# define DYNARRAY_ELEMENT off64_t -+# define DYNARRAY_PREFIX dirstream_loc_ -+# include -+ -+static __always_inline bool -+telldir_need_dirstream (__off64_t d_off) -+{ -+ return d_off >= 1UL << 31; -+} -+#else -+ -+_Static_assert (sizeof (long int) == sizeof (off64_t), -+ "sizeof (long int) != sizeof (off64_t)"); -+ -+#endif /* __LP64__ */ -+ -+#endif /* _TELLDIR_H */ -diff --git a/sysdeps/unix/sysv/linux/tst-opendir-nolfs.c b/sysdeps/unix/sysv/linux/tst-opendir-nolfs.c -new file mode 100644 -index 0000000000..52e18171a7 ---- /dev/null -+++ b/sysdeps/unix/sysv/linux/tst-opendir-nolfs.c -@@ -0,0 +1,146 @@ -+/* Check multiple telldir and seekdir. -+ Copyright (C) 2023 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 -+#include -+#include -+ -+/* Some filesystems returns an 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; -+/* The 2 extra files are '.' and '..'. */ -+static const size_t nfiles = (1<<14) + 2; -+ -+static inline bool -+in_ino_t_range (ino64_t v) -+{ -+ ino_t s = v; -+ return s == v; -+} -+ -+static inline bool -+in_off_t_range (off64_t v) -+{ -+ off_t s = v; -+ return s == v; -+} -+ -+static void -+do_prepare (int argc, char *argv[]) -+{ -+ dirname = support_create_temp_directory ("tst-opendir-nolfs-"); -+ -+ for (size_t i = 0; i < nfiles - 2; i++) -+ { -+ int fd = create_temp_file_in_dir ("tempfile.", dirname, NULL); -+ TEST_VERIFY_EXIT (fd > 0); -+ close (fd); -+ } -+} -+#define PREPARE do_prepare -+ -+static int -+do_test (void) -+{ -+ DIR *dirp = opendir (dirname); -+ TEST_VERIFY_EXIT (dirp != NULL); -+ -+ long int *tdirp = xmalloc (nfiles * sizeof (long int)); -+ struct dirent **ddirp = xmalloc (nfiles * sizeof (struct dirent *)); -+ -+ /* For non-LFS, the entry is skipped if it can not be converted. */ -+ int count = 0; -+ for (; count < nfiles; count++) -+ { -+ tdirp[count] = telldir (dirp); -+ struct dirent *dp = readdir (dirp); -+ if (dp == NULL) -+ break; -+ ddirp[count] = xmalloc (dp->d_reclen); -+ memcpy (ddirp[count], dp, dp->d_reclen); -+ } -+ -+ closedir (dirp); -+ -+ /* Check against the getdents64 syscall. */ -+ int fd = xopen (dirname, O_RDONLY | O_DIRECTORY, 0); -+ int i = 0; -+ while (true) -+ { -+ struct -+ { -+ char buffer[1024]; -+ struct dirent64 pad; -+ } data; -+ -+ ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer)); -+ if (ret < 0) -+ FAIL_EXIT1 ("getdents64: %m"); -+ if (ret == 0) -+ break; -+ -+ char *current = data.buffer; -+ char *end = data.buffer + ret; -+ while (current != end) -+ { -+ struct dirent64 entry; -+ memcpy (&entry, current, sizeof (entry)); -+ /* Truncate overlong strings. */ -+ entry.d_name[sizeof (entry.d_name) - 1] = '\0'; -+ TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1); -+ -+ if (in_ino_t_range (entry.d_ino) && in_off_t_range (entry.d_off)) -+ { -+ TEST_COMPARE_STRING (entry.d_name, ddirp[i]->d_name); -+ TEST_COMPARE (entry.d_ino, ddirp[i]->d_ino); -+ TEST_COMPARE (entry.d_off, ddirp[i]->d_off); -+ TEST_COMPARE (entry.d_type, ddirp[i]->d_type); -+ -+ /* Offset zero is reserved for the first entry. */ -+ TEST_VERIFY (entry.d_off != 0); -+ -+ TEST_VERIFY_EXIT (entry.d_reclen <= end - current); -+ i++; -+ } -+ -+ current += entry.d_reclen; -+ } -+ } -+ -+ /* direntries_read has been called more than once. */ -+ TEST_COMPARE (count, i); -+ -+ free (tdirp); -+ for (int i = 0; i < count; i++) -+ free (ddirp[i]); -+ free (ddirp); -+ -+ return 0; -+} -+ -+#include --- -2.41.0 - diff --git a/9999/0005-support-Add-xreallocarray.patch b/9999/0005-support-Add-xreallocarray.patch new file mode 100644 index 0000000..dbfae4d --- /dev/null +++ b/9999/0005-support-Add-xreallocarray.patch @@ -0,0 +1,76 @@ +From 7d0638e792ebe528af3f6d8e353eff68157d7105 Mon Sep 17 00:00:00 2001 +From: Adhemerval Zanella +Date: Wed, 1 Mar 2023 14:41:23 -0300 +Subject: [PATCH 2/3] support: Add xreallocarray + +As a wrapper over reallocarray. +--- + support/Makefile | 1 + + support/support.h | 2 ++ + support/xreallocarray.c | 29 +++++++++++++++++++++++++++++ + 3 files changed, 32 insertions(+) + create mode 100644 support/xreallocarray.c + +diff --git a/support/Makefile b/support/Makefile +index 362a51f882..d056563c15 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -201,6 +201,7 @@ libsupport-routines = \ + xread \ + xreadlink \ + xrealloc \ ++ xreallocarray \ + xrecvfrom \ + xsendto \ + xsetlocale \ +diff --git a/support/support.h b/support/support.h +index ba21ec9b5a..b4e31e4483 100644 +--- a/support/support.h ++++ b/support/support.h +@@ -107,6 +107,8 @@ extern void *xcalloc (size_t n, size_t s) + __returns_nonnull; + extern void *xrealloc (void *o, size_t n) + __attribute_malloc__ __attribute_alloc_size__ ((2)) __attr_dealloc_free; ++extern void *xreallocarray (void *p, size_t n, size_t s) ++ __attribute_alloc_size__ ((2, 3)) __attr_dealloc_free; + extern char *xstrdup (const char *) __attribute_malloc__ __attr_dealloc_free + __returns_nonnull; + void *xposix_memalign (size_t alignment, size_t n) +diff --git a/support/xreallocarray.c b/support/xreallocarray.c +new file mode 100644 +index 0000000000..74fdaa421b +--- /dev/null ++++ b/support/xreallocarray.c +@@ -0,0 +1,29 @@ ++/* Error-checking wrapper for reallocarray ++ Copyright (C) 2016-2023 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 ++ ++void * ++xreallocarray (void *p, size_t n, size_t s) ++{ ++ void *r = reallocarray (p, n, s); ++ if (r == NULL && (p == NULL || (n != 0 && s != 0))) ++ oom_error ("reallocarray", n); ++ return r; ++} +-- +2.41.0 + diff --git a/9999/0006-linux-Add-__readdir64_unlocked.patch b/9999/0006-linux-Add-__readdir64_unlocked.patch deleted file mode 100644 index 3510868..0000000 --- a/9999/0006-linux-Add-__readdir64_unlocked.patch +++ /dev/null @@ -1,182 +0,0 @@ -From a2a34383be6e082561a09f37c5747215a70e2439 Mon Sep 17 00:00:00 2001 -From: Adhemerval Zanella -Date: Fri, 27 Jan 2023 14:28:32 -0300 -Subject: [PATCH 6/9] 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 17827176ba..f391476298 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 db1c6214d8..2327511736 100644 ---- a/sysdeps/unix/sysv/linux/readdir64.c -+++ b/sysdeps/unix/sysv/linux/readdir64.c -@@ -28,15 +28,11 @@ - - /* Read a directory entry from DIRP. */ - struct dirent64 * --__readdir64 (DIR *dirp) -+__readdir64_unlocked (DIR *dirp) - { - struct dirent64 *dp; - 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. */ -@@ -68,6 +64,20 @@ __readdir64 (DIR *dirp) - dirp->offset += dp->d_reclen; - 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 285dc99509..5ae099bde7 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.41.0 - diff --git a/9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch b/9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch new file mode 100644 index 0000000..6450910 --- /dev/null +++ b/9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch @@ -0,0 +1,574 @@ +From 6d5975652503acffae0566500cc15f8efc89e30a Mon Sep 17 00:00:00 2001 +From: Adhemerval Zanella +Date: Mon, 13 Apr 2020 18:09:20 -0300 +Subject: [PATCH 3/3] 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 opendir creates a map entry between the DIR d_off +offset and the returned long int (the telldir return value). +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 removes 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. +--- + include/dirent.h | 2 +- + sysdeps/unix/sysv/linux/Makefile | 2 + + sysdeps/unix/sysv/linux/alpha/bits/dirent.h | 3 + + sysdeps/unix/sysv/linux/bits/dirent.h | 4 + + sysdeps/unix/sysv/linux/closedir.c | 4 + + sysdeps/unix/sysv/linux/dirstream.h | 9 +- + sysdeps/unix/sysv/linux/opendir.c | 3 + + sysdeps/unix/sysv/linux/readdir.c | 21 ++- + sysdeps/unix/sysv/linux/readdir64.c | 11 ++ + sysdeps/unix/sysv/linux/rewinddir.c | 5 + + sysdeps/unix/sysv/linux/seekdir.c | 30 +++- + sysdeps/unix/sysv/linux/telldir.c | 36 +++++ + sysdeps/unix/sysv/linux/telldir.h | 70 ++++++++++ + sysdeps/unix/sysv/linux/tst-opendir-lfs.c | 2 + + sysdeps/unix/sysv/linux/tst-opendir.c | 145 ++++++++++++++++++++ + 15 files changed, 329 insertions(+), 18 deletions(-) + create mode 100644 sysdeps/unix/sysv/linux/telldir.h + create mode 100644 sysdeps/unix/sysv/linux/tst-opendir-lfs.c + create mode 100644 sysdeps/unix/sysv/linux/tst-opendir.c + +diff --git a/include/dirent.h b/include/dirent.h +index d7567f5e86..17827176ba 100644 +--- a/include/dirent.h ++++ b/include/dirent.h +@@ -1,8 +1,8 @@ + #ifndef _DIRENT_H ++# include + # ifndef _ISOMAC + # include + # endif +-# include + # ifndef _ISOMAC + # include + # include +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index 415aa1f14d..d5b6e26e76 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -574,6 +574,8 @@ sysdep_routines += \ + + tests += \ + tst-getdents64 \ ++ tst-opendir \ ++ tst-opendir-lfs \ + tst-readdir64-compat \ + # tests + endif # $(subdir) == dirent +diff --git a/sysdeps/unix/sysv/linux/alpha/bits/dirent.h b/sysdeps/unix/sysv/linux/alpha/bits/dirent.h +index b604576188..4e511e7654 100644 +--- a/sysdeps/unix/sysv/linux/alpha/bits/dirent.h ++++ b/sysdeps/unix/sysv/linux/alpha/bits/dirent.h +@@ -54,4 +54,7 @@ struct dirent64 + /* Inform libc code that these two types are effectively identical. */ + #define _DIRENT_MATCHES_DIRENT64 1 + ++/* alpha 'long int' is enough to handle off64_t. */ ++#define _DIRENT_OFFSET_TRANSLATION 0 ++ + #endif /* bits/dirent.h */ +diff --git a/sysdeps/unix/sysv/linux/bits/dirent.h b/sysdeps/unix/sysv/linux/bits/dirent.h +index 8bf38f8188..7c5f3419cd 100644 +--- a/sysdeps/unix/sysv/linux/bits/dirent.h ++++ b/sysdeps/unix/sysv/linux/bits/dirent.h +@@ -57,3 +57,7 @@ struct dirent64 + #else + # define _DIRENT_MATCHES_DIRENT64 0 + #endif ++ ++/* The telldir function returns long int, which may not be large enough to ++ store off64_t values. In this case, translation is required. */ ++#define _DIRENT_OFFSET_TRANSLATION (LONG_WIDTH < 64) +diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c +index b86f79f133..3a4eba03f1 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_OFFSET_TRANSLATION ++ dirstream_loc_clear (&dirp->locs); ++#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 3d3643733a..fc2586566a 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,10 +38,16 @@ 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. */ + ++#if _DIRENT_OFFSET_TRANSLATION ++ /* The array is used to map long to off_64 for telldir/seekdir for ABIs ++ where long can not fully represend a LFS off_t value. */ ++ struct dirstream_loc_t locs; ++#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 b5218138ec..fda2c5f9fe 100644 +--- a/sysdeps/unix/sysv/linux/opendir.c ++++ b/sysdeps/unix/sysv/linux/opendir.c +@@ -129,6 +129,9 @@ __alloc_dir (int fd, bool close_fd, int flags, + dirp->offset = 0; + dirp->filepos = 0; + dirp->errcode = 0; ++#if _DIRENT_OFFSET_TRANSLATION ++ 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 4e2214f21e..d474f70a8d 100644 +--- a/sysdeps/unix/sysv/linux/readdir.c ++++ b/sysdeps/unix/sysv/linux/readdir.c +@@ -75,25 +75,22 @@ __readdir_unlocked (DIR *dirp) + size_t new_reclen = ALIGN_UP (old_reclen - size_diff, + _Alignof (struct dirent)); + +- if (!in_ino_t_range (inp->dp64.d_ino) +- || !in_off_t_range (inp->dp64.d_off)) ++ /* telldir can not return an error, so preallocate a map entry if ++ d_off can not be used directly. */ ++ if (telldir_need_dirstream (inp->dp64.d_off)) + { +- /* Overflow. If there was at least one entry before this one, +- return them without error, otherwise signal overflow. */ +- if (dirp->offset != 0) +- { +- __lseek64 (dirp->fd, dirp->offset, SEEK_SET); +- outp = (void*)(outp->b - dirp->data); +- return &outp->dp; +- } +- __set_errno (EOVERFLOW); +- return NULL; ++ dirstream_loc_add (&dirp->locs, inp->dp64.d_off); ++ if (dirstream_loc_has_failed (&dirp->locs)) ++ return NULL; + } + + /* Copy the data from INP and access only OUTP. */ + const uint64_t d_ino = inp->dp64.d_ino; + const int64_t d_off = inp->dp64.d_off; + const uint8_t d_type = inp->dp64.d_type; ++ /* This will clamp both d_off and d_ino values, which is required to ++ avoid return EOVERFLOW. The lelldir/seekdir uses the 'locs' value ++ if the value overflows. */ + outp->dp.d_ino = d_ino; + outp->dp.d_off = d_off; + outp->dp.d_reclen = new_reclen; +diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c +index e6f5108c0a..d61c5e6e26 100644 +--- a/sysdeps/unix/sysv/linux/readdir64.c ++++ b/sysdeps/unix/sysv/linux/readdir64.c +@@ -68,6 +68,17 @@ __readdir64 (DIR *dirp) + dirp->offset += dp->d_reclen; + dirp->filepos = dp->d_off; + ++#if _DIRENT_OFFSET_TRANSLATION ++ /* telldir can not return an error, so preallocate a map entry if ++ d_off can not be used directly. */ ++ if (telldir_need_dirstream (dp->d_off)) ++ { ++ dirstream_loc_add (&dirp->locs, dp->d_off); ++ if (dirstream_loc_has_failed (&dirp->locs)) ++ dp = NULL; ++ } ++#endif ++ + #if IS_IN (libc) + __libc_lock_unlock (dirp->lock); + #endif +diff --git a/sysdeps/unix/sysv/linux/rewinddir.c b/sysdeps/unix/sysv/linux/rewinddir.c +index af85000a28..4936be5c6a 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 52005878fc..cd49676750 100644 +--- a/sysdeps/unix/sysv/linux/seekdir.c ++++ b/sysdeps/unix/sysv/linux/seekdir.c +@@ -22,14 +22,36 @@ + #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; ++ ++#if _DIRENT_OFFSET_TRANSLATION ++ union dirstream_packed 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)) ++ { ++ __libc_lock_unlock (dirp->lock); ++ return; ++ } ++ filepos = *dirstream_loc_at (&dirp->locs, index); ++ } ++#else ++ filepos = pos; ++#endif ++ ++ __lseek64 (dirp->fd, filepos, SEEK_SET); ++ dirp->filepos = filepos; + dirp->offset = 0; +- dirp->filepos = pos; ++ 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 c8dff30719..8d9e2c9db2 100644 +--- a/sysdeps/unix/sysv/linux/telldir.c ++++ b/sysdeps/unix/sysv/linux/telldir.c +@@ -15,9 +15,12 @@ + License along with the GNU C Library; if not, see + . */ + ++#include ++#include + #include + + #include ++#include + + /* Return the current position of DIRP. */ + long int +@@ -26,7 +29,40 @@ telldir (DIR *dirp) + long int ret; + + __libc_lock_lock (dirp->lock); ++ ++#if _DIRENT_OFFSET_TRANSLATION ++ /* 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; ++ ++ if (!telldir_need_dirstream (dirp->filepos)) ++ { ++ dsp.p.is_packed = 1; ++ dsp.p.info = dirp->filepos; ++ } ++ else ++ { ++ dsp.l = -1; ++ ++ size_t i; ++ for (i = 0; ;i++) ++ { ++ /* It should be pre-allocated on readdir. */ ++ assert (i < dirstream_loc_size (&dirp->locs)); ++ if (*dirstream_loc_at (&dirp->locs, i) == dirp->filepos) ++ break; ++ } ++ ++ dsp.p.is_packed = 0; ++ dsp.p.info = i; ++ } ++ ++ ret = dsp.l; ++#else + ret = dirp->filepos; ++#endif + __libc_lock_unlock (dirp->lock); + + return ret; +diff --git a/sysdeps/unix/sysv/linux/telldir.h b/sysdeps/unix/sysv/linux/telldir.h +new file mode 100644 +index 0000000000..7772129db0 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/telldir.h +@@ -0,0 +1,70 @@ ++/* Linux internal telldir definitions. ++ Copyright (C) 2023 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 ++ ++#include ++ ++#if _DIRENT_OFFSET_TRANSLATION ++ ++_Static_assert (sizeof (long int) < sizeof (__off64_t), ++ "sizeof (long int) >= sizeof (__off64_t)"); ++ ++# include ++ ++/* 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 int is_packed:1; ++ unsigned long int info:31; ++ } p; ++}; ++ ++/* telldir maintains a list of offsets that describe the obtained diretory ++ position if it can fit this information in the returned 'dirstream_packed' ++ struct. */ ++ ++# define DYNARRAY_STRUCT dirstream_loc_t ++# define DYNARRAY_ELEMENT off64_t ++# define DYNARRAY_PREFIX dirstream_loc_ ++# include ++ ++static __always_inline bool ++telldir_need_dirstream (__off64_t d_off) ++{ ++ return ! (TYPE_MINIMUM (off_t) <= d_off && d_off <= TYPE_MAXIMUM (off_t)); ++} ++#else ++ ++_Static_assert (sizeof (long int) == sizeof (off64_t), ++ "sizeof (long int) != sizeof (off64_t)"); ++ ++#endif /* __LP64__ */ ++ ++#endif /* _TELLDIR_H */ +diff --git a/sysdeps/unix/sysv/linux/tst-opendir-lfs.c b/sysdeps/unix/sysv/linux/tst-opendir-lfs.c +new file mode 100644 +index 0000000000..1de1891fb4 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-opendir-lfs.c +@@ -0,0 +1,2 @@ ++#define _FILE_OFFSET_BITS 64 ++#include "tst-opendir.c" +diff --git a/sysdeps/unix/sysv/linux/tst-opendir.c b/sysdeps/unix/sysv/linux/tst-opendir.c +new file mode 100644 +index 0000000000..216ecf123f +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-opendir.c +@@ -0,0 +1,145 @@ ++/* Check multiple telldir and seekdir. ++ Copyright (C) 2023 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 ++#include ++#include ++ ++/* Some filesystems returns an 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; ++/* The 2 extra files are '.' and '..'. */ ++static const size_t nfiles = (1<<14) + 2; ++ ++static inline bool ++in_ino_t_range (ino64_t v) ++{ ++ ino_t s = v; ++ return s == v; ++} ++ ++static inline bool ++in_off_t_range (off64_t v) ++{ ++ off_t s = v; ++ return s == v; ++} ++ ++static void ++do_prepare (int argc, char *argv[]) ++{ ++ dirname = support_create_temp_directory ("tst-opendir-nolfs-"); ++ ++ for (size_t i = 0; i < nfiles - 2; i++) ++ { ++ int fd = create_temp_file_in_dir ("tempfile.", dirname, NULL); ++ TEST_VERIFY_EXIT (fd > 0); ++ close (fd); ++ } ++} ++#define PREPARE do_prepare ++ ++static int ++do_test (void) ++{ ++ DIR *dirp = opendir (dirname); ++ TEST_VERIFY_EXIT (dirp != NULL); ++ ++ long int *tdirp = xreallocarray (NULL, nfiles, sizeof (long int)); ++ struct dirent **ddirp = xreallocarray (NULL, nfiles, ++ sizeof (struct dirent *)); ++ ++ /* For non-LFS, the entry is skipped if it can not be converted. */ ++ int count = 0; ++ for (; count < nfiles; count++) ++ { ++ struct dirent *dp = readdir (dirp); ++ if (dp == NULL) ++ break; ++ tdirp[count] = telldir (dirp); ++ ddirp[count] = xmalloc (dp->d_reclen); ++ memcpy (ddirp[count], dp, dp->d_reclen); ++ } ++ ++ closedir (dirp); ++ ++ /* Check against the getdents64 syscall. */ ++ int fd = xopen (dirname, O_RDONLY | O_DIRECTORY, 0); ++ int i = 0; ++ while (true) ++ { ++ struct ++ { ++ char buffer[1024]; ++ struct dirent64 pad; ++ } data; ++ ++ ssize_t ret = getdents64 (fd, &data.buffer, sizeof (data.buffer)); ++ if (ret < 0) ++ FAIL_EXIT1 ("getdents64: %m"); ++ if (ret == 0) ++ break; ++ ++ char *current = data.buffer; ++ char *end = data.buffer + ret; ++ while (current != end) ++ { ++ struct dirent64 entry; ++ memcpy (&entry, current, sizeof (entry)); ++ /* Truncate overlong strings. */ ++ entry.d_name[sizeof (entry.d_name) - 1] = '\0'; ++ TEST_VERIFY (strlen (entry.d_name) < sizeof (entry.d_name) - 1); ++ ++ if (in_ino_t_range (entry.d_ino)) ++ { ++ TEST_COMPARE_STRING (entry.d_name, ddirp[i]->d_name); ++ TEST_COMPARE (entry.d_ino, ddirp[i]->d_ino); ++ TEST_COMPARE (entry.d_type, ddirp[i]->d_type); ++ ++ /* Offset zero is reserved for the first entry. */ ++ TEST_VERIFY (entry.d_off != 0); ++ ++ TEST_VERIFY_EXIT (entry.d_reclen <= end - current); ++ i++; ++ } ++ ++ current += entry.d_reclen; ++ } ++ } ++ ++ TEST_COMPARE (count, i); ++ ++ free (tdirp); ++ for (int i = 0; i < count; i++) ++ free (ddirp[i]); ++ free (ddirp); ++ ++ return 0; ++} ++ ++#include +-- +2.41.0 + diff --git a/9999/0007-linux-Add-__old_readdir64_unlocked.patch b/9999/0007-linux-Add-__old_readdir64_unlocked.patch deleted file mode 100644 index 2b99897..0000000 --- a/9999/0007-linux-Add-__old_readdir64_unlocked.patch +++ /dev/null @@ -1,194 +0,0 @@ -From f1d798104d5adfdec9aff9390dd7b62014d888eb Mon Sep 17 00:00:00 2001 -From: Adhemerval Zanella -Date: Fri, 27 Jan 2023 14:28:33 -0300 -Subject: [PATCH 7/9] 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 | 24 +++++--- - sysdeps/unix/sysv/linux/readdir64_r.c | 79 ++++++--------------------- - 3 files changed, 35 insertions(+), 70 deletions(-) - -diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h -index 9789ffae07..cde95e192e 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 2327511736..b901071aa7 100644 ---- a/sysdeps/unix/sysv/linux/readdir64.c -+++ b/sysdeps/unix/sysv/linux/readdir64.c -@@ -104,15 +104,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 -- - if (dirp->offset >= dirp->size) - { - /* We've emptied out our buffer. Refill it. */ -@@ -129,9 +125,6 @@ __old_readdir64 (DIR *dirp) - do not set errno in that case, to indicate success. */ - if (bytes == 0 || errno == ENOENT) - __set_errno (saved_errno); --#if IS_IN (libc) -- __libc_lock_unlock (dirp->lock); --#endif - return NULL; - } - dirp->size = (size_t) bytes; -@@ -144,6 +137,21 @@ __old_readdir64 (DIR *dirp) - dirp->offset += dp->d_reclen; - dirp->filepos = dp->d_off; - -+ 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 5ae099bde7..b499388de7 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.41.0 - diff --git a/9999/0008-linux-Use-getdents64-on-readdir64-compat-implementat.patch b/9999/0008-linux-Use-getdents64-on-readdir64-compat-implementat.patch deleted file mode 100644 index bf2981e..0000000 --- a/9999/0008-linux-Use-getdents64-on-readdir64-compat-implementat.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 43e064d5764246f49561def52f9fd592d58b7ac2 Mon Sep 17 00:00:00 2001 -From: Adhemerval Zanella -Date: Fri, 27 Jan 2023 14:28:34 -0300 -Subject: [PATCH 8/9] 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/dirstream.h | 13 +++- - sysdeps/unix/sysv/linux/getdents64.c | 93 ---------------------------- - sysdeps/unix/sysv/linux/olddirent.h | 2 - - sysdeps/unix/sysv/linux/readdir64.c | 50 +++++++++++---- - 4 files changed, 50 insertions(+), 108 deletions(-) - -diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h -index 8f58a1c3a6..b03ece4590 100644 ---- a/sysdeps/unix/sysv/linux/dirstream.h -+++ b/sysdeps/unix/sysv/linux/dirstream.h -@@ -24,6 +24,11 @@ - #include - #include - -+#include -+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) -+# include -+#endif -+ - /* Directory stream type. - - The miscellaneous Unix `readdir' implementations read directory data -@@ -44,7 +49,13 @@ struct __dirstream - int errcode; /* Delayed error code. */ - - #if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T -- struct dirent tdp; -+ union -+ { -+ struct dirent tdp; -+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) -+ struct __old_dirent64 tdp64; -+# endif -+ }; - #endif - #if _DIRENT_OFFSET_TRANSLATION - struct dirstream_loc_t locs; /* off64_t to long int map for telldir. */ -diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c -index 01c3517deb..db299864ed 100644 ---- a/sysdeps/unix/sysv/linux/getdents64.c -+++ b/sysdeps/unix/sysv/linux/getdents64.c -@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64) - - #if _DIRENT_MATCHES_DIRENT64 - strong_alias (__getdents64, __getdents) --#else --# include -- --# 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 cde95e192e..2d682a6919 100644 ---- a/sysdeps/unix/sysv/linux/olddirent.h -+++ b/sysdeps/unix/sysv/linux/olddirent.h -@@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp) - attribute_hidden; - extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry, - struct __old_dirent64 **__result); --extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes) -- attribute_hidden; - int __old_scandir64 (const char * __dir, - struct __old_dirent64 *** __namelist, - int (*__selector) (const struct __old_dirent64 *), -diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c -index b901071aa7..88e42c5e90 100644 ---- a/sysdeps/unix/sysv/linux/readdir64.c -+++ b/sysdeps/unix/sysv/linux/readdir64.c -@@ -102,21 +102,43 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); - # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) - # include - -+/* Translate the DP64 entry to the old LFS one in the translation buffer -+ at dirstream DS. Return true is the translation was possible or -+ false if either an internal fields can be represented in the non-LFS -+ entry or if the translation can not be resized. */ -+static bool -+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64) -+{ -+ /* Check for overflow. */ -+ if (!in_ino_t_range (dp64->d_ino)) -+ return false; -+ -+ /* And if name is too large. */ -+ if (dp64->d_reclen - offsetof (struct dirent64, d_name) > NAME_MAX) -+ return false; -+ -+ ds->filepos = dp64->d_off; -+ -+ ds->tdp64.d_off = dp64->d_off; -+ ds->tdp64.d_ino = dp64->d_ino; -+ ds->tdp64.d_reclen = dp64->d_reclen; -+ ds->tdp64.d_type = dp64->d_type; -+ memcpy (ds->tdp64.d_name, dp64->d_name, -+ dp64->d_reclen - offsetof (struct dirent64, d_name)); -+ -+ return true; -+} -+ - attribute_compat_text_section - struct __old_dirent64 * - __old_readdir64_unlocked (DIR *dirp) - { -- struct __old_dirent64 *dp; -- int saved_errno = errno; -+ const int saved_errno = errno; - - if (dirp->offset >= dirp->size) - { - /* We've emptied out our buffer. Refill it. */ -- -- size_t maxread = dirp->allocation; -- ssize_t bytes; -- -- bytes = __old_getdents64 (dirp->fd, dirp->data, maxread); -+ ssize_t bytes = __getdents64 (dirp->fd, dirp->data, dirp->allocation); - if (bytes <= 0) - { - /* Linux may fail with ENOENT on some file systems if the -@@ -127,17 +149,21 @@ __old_readdir64_unlocked (DIR *dirp) - __set_errno (saved_errno); - return NULL; - } -- dirp->size = (size_t) bytes; -+ dirp->size = bytes; - - /* Reset the offset into the buffer. */ - dirp->offset = 0; - } - -- dp = (struct __old_dirent64 *) &dirp->data[dirp->offset]; -- dirp->offset += dp->d_reclen; -- dirp->filepos = dp->d_off; -+ struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; -+ dirp->offset += dp64->d_reclen; - -- return dp; -+ /* Skip entries which might overflow d_ino or for memory allocation failure -+ in case of large file names. */ -+ if (dirstream_old_entry (dirp, dp64)) -+ return &dirp->tdp64; -+ -+ return NULL; - } - - attribute_compat_text_section --- -2.41.0 - -- cgit v1.2.3-65-gdbad