diff options
Diffstat (limited to '9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch')
-rw-r--r-- | 9999/0006-linux-Set-internal-DIR-filepos-as-off64_t-BZ-23960-B.patch | 616 |
1 files changed, 328 insertions, 288 deletions
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 index 38acaf8..59e01fc 100644 --- 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 @@ -1,240 +1,121 @@ -From 36f553f67b8268341b7879640637fac5ea806017 Mon Sep 17 00:00:00 2001 +From 861793671bd8daeed1f00902d9a9523ddae77462 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella <adhemerval.zanella@linaro.org> -Date: Mon, 13 Apr 2020 18:09:20 -0300 -Subject: [PATCH 06/10] linux: Set internal DIR filepos as off64_t [BZ #23960, - BZ #24050] +Date: Fri, 27 Jan 2023 14:28:31 -0300 +Subject: [PATCH 6/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 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. +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 removed the requirement to check for overflow values on +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/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 + 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/Makefile b/dirent/Makefile -index cfa61826ed..9a9d91b8a5 100644 ---- a/dirent/Makefile -+++ b/dirent/Makefile -@@ -31,7 +31,7 @@ routines := opendir closedir readdir readdir_r rewinddir \ - scandir-cancel scandir-tail scandir64-tail - - tests := list tst-seekdir opendir-tst1 bug-readdir1 tst-fdopendir \ -- tst-fdopendir2 tst-scandir tst-scandir64 -+ tst-fdopendir2 tst-scandir tst-scandir64 tst-seekdir2 - - CFLAGS-scandir.c += $(uses-callbacks) - CFLAGS-scandir64.c += $(uses-callbacks) -diff --git a/dirent/tst-seekdir2.c b/dirent/tst-seekdir2.c -new file mode 100644 -index 0000000000..3e01b361e5 ---- /dev/null -+++ b/dirent/tst-seekdir2.c -@@ -0,0 +1,158 @@ -+/* Check multiple telldir and seekdir. -+ Copyright (C) 2020 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, see -+ <https://www.gnu.org/licenses/>. */ -+ -+#include <dirent.h> -+#include <stdlib.h> -+#include <unistd.h> -+#include <stdio.h> -+#include <string.h> -+ -+#include <support/temp_file.h> -+#include <support/support.h> -+#include <support/check.h> -+ -+/* Some filesystems returns a arbitrary value for d_off direnty entry (ext4 -+ for instance, where the value is an internal hash key). The idea of -+ create a large number of file is to try trigger a overflow d_off value -+ in a entry to check if telldir/seekdir does work corretly in such -+ case. */ -+static const char *dirname; -+static const size_t nfiles = 10240; -+ -+static void -+do_prepare (int argc, char *argv[]) -+{ -+ dirname = support_create_temp_directory ("tst-seekdir2-"); -+ -+ for (size_t i = 0; i < nfiles; i++) +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) + { -+ 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); ++ /* 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 f8bd12d991..09adb99b86 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -464,6 +464,7 @@ ifeq ($(subdir),dirent) + sysdep_routines += getdirentries getdirentries64 + 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 + -+ closedir (dirp); -+ free (tdirp); -+ for (i = 0; i < dirp_count; i++) -+ free (ddirp[i]); -+ free (ddirp); -+ -+ return 0; -+} -+ -+/* Same as before but with LFS support. */ -+static int -+do_test_lfs (void) -+{ -+ DIR *dirp = opendir (dirname); -+ TEST_VERIFY_EXIT (dirp != NULL); -+ -+ size_t dirp_count = 0; -+ for (struct dirent64 * dp = readdir64 (dirp); -+ dp != NULL; -+ dp = readdir64 (dirp)) -+ dirp_count++; -+ -+ /* The 2 extra files are '.' and '..'. */ -+ TEST_COMPARE (dirp_count, nfiles + 2); -+ -+ rewinddir (dirp); -+ -+ long *tdirp = xmalloc (dirp_count * sizeof (long)); -+ struct dirent64 **ddirp = xmalloc (dirp_count * sizeof (struct dirent64 *)); -+ -+ size_t i = 0; -+ do -+ { -+ tdirp[i] = telldir (dirp); -+ struct dirent64 *dp = readdir64 (dirp); -+ TEST_VERIFY_EXIT (dp != NULL); -+ ddirp[i] = xmalloc (dp->d_reclen); -+ memcpy (ddirp[i], dp, dp->d_reclen); -+ } while (++i < dirp_count); -+ -+ for (i = 0; i < dirp_count - 1; i++) -+ { -+ seekdir (dirp, tdirp[i]); -+ struct dirent64 *dp = readdir64 (dirp); -+ TEST_COMPARE (strcmp (dp->d_name, ddirp[i]->d_name), 0); -+ TEST_COMPARE (dp->d_ino, ddirp[i]->d_ino); -+ TEST_COMPARE (dp->d_off, ddirp[i]->d_off); -+ } -+ -+ closedir (dirp); -+ free (tdirp); -+ for (i = 0; i < dirp_count; i++) -+ free (ddirp[i]); -+ free (ddirp); -+ -+ return 0; -+} -+ -+static int -+do_test (void) -+{ -+ do_test_not_lfs (); -+ do_test_lfs (); -+ -+ return 0; -+} -+ -+#include <support/test-driver.c> ++/* 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 d876d49d78..8e5669963c 100644 +index f1c2608642..9585a6ca3a 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; +@@ -47,6 +47,10 @@ __closedir (DIR *dirp) + __libc_lock_fini (dirp->lock); + #endif -+#ifndef __LP64__ ++#if _DIRENT_OFFSET_TRANSLATION + dirstream_loc_clear (&dirp->locs); +#endif + - #if IS_IN (libc) - __libc_lock_fini (dirp->lock); - #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 064273cc31..a284292cb2 100644 +index adcf8234f1..8f58a1c3a6 100644 --- a/sysdeps/unix/sysv/linux/dirstream.h +++ b/sysdeps/unix/sysv/linux/dirstream.h -@@ -21,6 +21,7 @@ +@@ -22,6 +22,7 @@ #include <sys/types.h> #include <libc-lock.h> @@ -242,7 +123,7 @@ index 064273cc31..a284292cb2 100644 /* Directory stream type. -@@ -37,7 +38,7 @@ struct __dirstream +@@ -38,13 +39,16 @@ struct __dirstream size_t size; /* Total valid data in the block. */ size_t offset; /* Current offset into the block. */ @@ -251,44 +132,60 @@ index 064273cc31..a284292cb2 100644 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. */ + #if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T + struct dirent tdp; #endif -+#ifndef __LP64__ ++#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 bfd2f382a6..9a0b7ab4c4 100644 +index 4336196a4d..3e2caabb9d 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, +@@ -129,6 +129,9 @@ __alloc_dir (int fd, bool close_fd, int flags, dirp->offset = 0; dirp->filepos = 0; dirp->errcode = 0; -+#ifndef __LP64__ ++#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 c078146d7d..f377e5f268 100644 +index cd0ccaf33a..7a7f484c36 100644 --- a/sysdeps/unix/sysv/linux/readdir.c +++ b/sysdeps/unix/sysv/linux/readdir.c -@@ -17,6 +17,7 @@ - <https://www.gnu.org/licenses/>. */ +@@ -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; - #include <dirent.h> -+#include <unistd.h> ++ /* 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; - #if !_DIRENT_MATCHES_DIRENT64 - #include <dirstream.h> + 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 b1e8259703..0194d29e38 100644 +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) @@ -304,10 +201,10 @@ index b1e8259703..0194d29e38 100644 __libc_lock_unlock (dirp->lock); #endif diff --git a/sysdeps/unix/sysv/linux/seekdir.c b/sysdeps/unix/sysv/linux/seekdir.c -index f4e1a9f8e0..0c3e58a2ed 100644 +index 939ccc4447..30cce691a4 100644 --- a/sysdeps/unix/sysv/linux/seekdir.c +++ b/sysdeps/unix/sysv/linux/seekdir.c -@@ -22,14 +22,40 @@ +@@ -22,14 +22,39 @@ #include <dirstream.h> /* Seek to position POS in DIRP. */ @@ -323,11 +220,8 @@ index f4e1a9f8e0..0c3e58a2ed 100644 - dirp->offset = 0; - dirp->filepos = pos; + -+#ifndef __LP64__ -+ union dirstream_packed dsp; -+ -+ dsp.l = pos; -+ ++#if _DIRENT_OFFSET_TRANSLATION ++ union dirstream_packed dsp = { .l = pos }; + if (dsp.p.is_packed == 1) + filepos = dsp.p.info; + else @@ -335,9 +229,11 @@ index f4e1a9f8e0..0c3e58a2ed 100644 + 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; ++ { ++ __libc_lock_unlock (dirp->lock); ++ return; ++ } ++ filepos = *dirstream_loc_at (&dirp->locs, index); + } +#else + filepos = pos; @@ -354,10 +250,14 @@ index f4e1a9f8e0..0c3e58a2ed 100644 __libc_lock_unlock (dirp->lock); } diff --git a/sysdeps/unix/sysv/linux/telldir.c b/sysdeps/unix/sysv/linux/telldir.c -index b60b231e48..874905489f 100644 +index 1e5c129e9f..c3ef14f3da 100644 --- a/sysdeps/unix/sysv/linux/telldir.c +++ b/sysdeps/unix/sysv/linux/telldir.c -@@ -18,16 +18,59 @@ +@@ -15,9 +15,11 @@ + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + ++#include <assert.h> #include <dirent.h> #include <dirstream.h> @@ -365,68 +265,55 @@ index b60b231e48..874905489f 100644 /* 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. +@@ -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; -+ size_t i; - - __libc_lock_lock (dirp->lock); -- ret = dirp->filepos; + -+ if (dirp->filepos < (1U << 31)) ++ if (!telldir_need_dirstream (dirp->filepos)) + { + 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)) ++ else + { -+ dirstream_loc_add (&dirp->locs, -+ (struct dirstream_loc) { dirp->filepos }); -+ if (dirstream_loc_has_failed (&dirp->locs)) -+ goto out; ++ 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; + } + -+ dsp.p.is_packed = 0; -+ /* This assignment might overflow, however most likely ENOMEM would happen -+ long before. */ -+ dsp.p.info = i; -+ -+out: ++ ret = dsp.l; ++#else + ret = dirp->filepos; ++#endif __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 +index 0000000000..758bcb0eb3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/telldir.h -@@ -0,0 +1,64 @@ +@@ -0,0 +1,65 @@ +/* Linux internal telldir definitions. -+ Copyright (C) 2020 Free Software Foundation, Inc. ++ 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 @@ -446,9 +333,8 @@ index 0000000000..7c45886341 +#ifndef _TELLDIR_H +#define _TELLDIR_H 1 + -+#ifndef __LP64__ -+ -+/* On platforms where long int is smaller than off64_t this is how the ++#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. @@ -461,34 +347,188 @@ index 0000000000..7c45886341 + long int l; + struct + { -+ unsigned long is_packed:1; -+ unsigned long info:31; ++ 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 will mantain a list of offsets that describe the obtained diretory ++/* telldir maintains 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_ELEMENT off64_t +# define DYNARRAY_PREFIX dirstream_loc_ +# include <malloc/dynarray-skeleton.c> ++ ++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 ++ <https://www.gnu.org/licenses/>. */ ++ ++#include <dirent.h> ++#include <fcntl.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <unistd.h> ++ ++#include <support/check.h> ++#include <support/support.h> ++#include <support/temp_file.h> ++#include <support/xunistd.h> ++ ++/* 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 <support/test-driver.c> -- -2.38.2 +2.39.1 |