public inbox for glibc-cvs@sourceware.org
help / color / mirror / Atom feed
* [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation
@ 2023-01-26 12:17 Adhemerval Zanella
0 siblings, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2023-01-26 12:17 UTC (permalink / raw)
To: glibc-cvs
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=425419e2244b26670d09fb02e5660a7b0d6b2283
commit 425419e2244b26670d09fb02e5660a7b0d6b2283
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Tue Oct 20 16:00:43 2020 -0300
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.
Diff:
---
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 | 62 +++++++++++++++++++-----
4 files changed, 64 insertions(+), 108 deletions(-)
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 <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-# include <olddirent.h>
-# include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
- /* If this is the first entry in the buffer, we can report the
- error. */
- if (offset == 0)
- {
- __set_errno (EOVERFLOW);
- return -1;
- }
-
- /* Otherwise, seek to the overflowing entry, so that the next call
- will report the error, and return the data read so far. */
- if (__lseek64 (fd, offset, SEEK_SET) != 0)
- return -1;
- return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
- /* We do not move the individual directory entries. This is only
- possible if the target type (struct __old_dirent64) is smaller
- than the source type. */
- _Static_assert (offsetof (struct __old_dirent64, d_name)
- <= offsetof (struct dirent64, d_name),
- "__old_dirent64 is larger than dirent64");
- _Static_assert (__alignof__ (struct __old_dirent64)
- <= __alignof__ (struct dirent64),
- "alignment of __old_dirent64 is larger than dirent64");
-
- ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
- if (retval > 0)
- {
- /* This is the marker for the first entry. Offset 0 is reserved
- for the first entry (see rewinddir). Here, we use it as a
- marker for the first entry in the buffer. We never actually
- seek to offset 0 because handle_overflow reports the error
- directly, so it does not matter that the offset is incorrect
- if entries have been read from the descriptor before (so that
- the descriptor is not actually at offset 0). */
- __off64_t previous_offset = 0;
-
- char *p = buf;
- char *end = buf + retval;
- while (p < end)
- {
- struct dirent64 *source = (struct dirent64 *) p;
-
- /* Copy out the fixed-size data. */
- __ino_t ino = source->d_ino;
- __off64_t offset = source->d_off;
- unsigned int reclen = source->d_reclen;
- unsigned char type = source->d_type;
-
- /* Check for ino_t overflow. */
- if (__glibc_unlikely (ino != source->d_ino))
- return handle_overflow (fd, previous_offset, p - buf);
-
- /* Convert to the target layout. Use a separate struct and
- memcpy to side-step aliasing issues. */
- struct __old_dirent64 result;
- result.d_ino = ino;
- result.d_off = offset;
- result.d_reclen = reclen;
- result.d_type = type;
-
- /* Write the fixed-sized part of the result to the
- buffer. */
- size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
- memcpy (p, &result, result_name_offset);
-
- /* Adjust the position of the name if necessary. Copy
- everything until the end of the record, including the
- terminating NUL byte. */
- if (result_name_offset != offsetof (struct dirent64, d_name))
- memmove (p + result_name_offset, source->d_name,
- reclen - offsetof (struct dirent64, d_name));
-
- p += reclen;
- previous_offset = offset;
- }
- }
- return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */
#endif /* _DIRENT_MATCHES_DIRENT64 */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index 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/opendir.c b/sysdeps/unix/sysv/linux/opendir.c
index 9b9a9c4314..3d1ce841e2 100644
--- a/sysdeps/unix/sysv/linux/opendir.c
+++ b/sysdeps/unix/sysv/linux/opendir.c
@@ -23,6 +23,11 @@
#include <not-cancel.h>
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#endif
+
enum {
opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
};
@@ -128,7 +133,15 @@ __alloc_dir (int fd, bool close_fd, int flags,
expand the buffer if required. */
enum
{
- tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
+ tbuffer_size =
+# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+ /* This is used on compat readdir64. */
+ MAX (sizeof (struct dirent),
+ sizeof (struct __old_dirent64))
+# else
+ sizeof (struct dirent)
+# endif
+ + NAME_MAX + 1
};
dirp->tbuffer = malloc (tbuffer_size);
if (dirp->tbuffer == NULL)
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index b901071aa7..7651eeb08e 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -102,21 +102,52 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
# include <olddirent.h>
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+ at dirstream DS. Return true is the translation was possible or
+ false if either an internal fields can be represented in the non-LFS
+ entry or if the translation can not be resized. */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+ /* Check for overflow. */
+ ino_t d_ino = dp64->d_ino;
+ if (d_ino != dp64->d_ino)
+ return false;
+
+ /* Expand the translation buffer to hold the new namesize. */
+ size_t d_reclen = sizeof (struct __old_dirent64)
+ + dp64->d_reclen - offsetof (struct dirent64, d_name);
+ if (d_reclen > ds->tbuffer_size)
+ {
+ char *newbuffer = realloc (ds->tbuffer, d_reclen);
+ if (newbuffer == NULL)
+ return false;
+ ds->tbuffer = newbuffer;
+ ds->tbuffer_size = d_reclen;
+ }
+
+ struct __old_dirent64 *olddp64 = (struct __old_dirent64 *) ds->tbuffer;
+
+ olddp64->d_off = dp64->d_off;
+ olddp64->d_ino = dp64->d_ino;
+ olddp64->d_reclen = dp64->d_reclen;
+ olddp64->d_type = dp64->d_type;
+ memcpy (olddp64->d_name, dp64->d_name,
+ dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+ return true;
+}
+
attribute_compat_text_section
struct __old_dirent64 *
__old_readdir64_unlocked (DIR *dirp)
{
- struct __old_dirent64 *dp;
- int saved_errno = errno;
+ const int saved_errno = errno;
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 +158,24 @@ __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))
+ {
+ dirp->filepos = dp64->d_off;
+ return (struct __old_dirent64 *) dirp->tbuffer;
+ }
+
+ return NULL;
}
attribute_compat_text_section
^ permalink raw reply [flat|nested] 3+ messages in thread
* [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation
@ 2023-01-27 20:40 Adhemerval Zanella
0 siblings, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2023-01-27 20:40 UTC (permalink / raw)
To: glibc-cvs
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=4404fe3a816444f2bd385686d2828d0ceb00d291
commit 4404fe3a816444f2bd385686d2828d0ceb00d291
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Tue Oct 20 16:00:43 2020 -0300
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.
Diff:
---
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 <libc-lock.h>
#include <telldir.h>
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#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 <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-# include <olddirent.h>
-# include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
- /* If this is the first entry in the buffer, we can report the
- error. */
- if (offset == 0)
- {
- __set_errno (EOVERFLOW);
- return -1;
- }
-
- /* Otherwise, seek to the overflowing entry, so that the next call
- will report the error, and return the data read so far. */
- if (__lseek64 (fd, offset, SEEK_SET) != 0)
- return -1;
- return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
- /* We do not move the individual directory entries. This is only
- possible if the target type (struct __old_dirent64) is smaller
- than the source type. */
- _Static_assert (offsetof (struct __old_dirent64, d_name)
- <= offsetof (struct dirent64, d_name),
- "__old_dirent64 is larger than dirent64");
- _Static_assert (__alignof__ (struct __old_dirent64)
- <= __alignof__ (struct dirent64),
- "alignment of __old_dirent64 is larger than dirent64");
-
- ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
- if (retval > 0)
- {
- /* This is the marker for the first entry. Offset 0 is reserved
- for the first entry (see rewinddir). Here, we use it as a
- marker for the first entry in the buffer. We never actually
- seek to offset 0 because handle_overflow reports the error
- directly, so it does not matter that the offset is incorrect
- if entries have been read from the descriptor before (so that
- the descriptor is not actually at offset 0). */
- __off64_t previous_offset = 0;
-
- char *p = buf;
- char *end = buf + retval;
- while (p < end)
- {
- struct dirent64 *source = (struct dirent64 *) p;
-
- /* Copy out the fixed-size data. */
- __ino_t ino = source->d_ino;
- __off64_t offset = source->d_off;
- unsigned int reclen = source->d_reclen;
- unsigned char type = source->d_type;
-
- /* Check for ino_t overflow. */
- if (__glibc_unlikely (ino != source->d_ino))
- return handle_overflow (fd, previous_offset, p - buf);
-
- /* Convert to the target layout. Use a separate struct and
- memcpy to side-step aliasing issues. */
- struct __old_dirent64 result;
- result.d_ino = ino;
- result.d_off = offset;
- result.d_reclen = reclen;
- result.d_type = type;
-
- /* Write the fixed-sized part of the result to the
- buffer. */
- size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
- memcpy (p, &result, result_name_offset);
-
- /* Adjust the position of the name if necessary. Copy
- everything until the end of the record, including the
- terminating NUL byte. */
- if (result_name_offset != offsetof (struct dirent64, d_name))
- memmove (p + result_name_offset, source->d_name,
- reclen - offsetof (struct dirent64, d_name));
-
- p += reclen;
- previous_offset = offset;
- }
- }
- return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */
#endif /* _DIRENT_MATCHES_DIRENT64 */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index 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 <olddirent.h>
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+ at dirstream DS. Return true is the translation was possible or
+ false if either an internal fields can be represented in the non-LFS
+ entry or if the translation can not be resized. */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+ /* Check for overflow. */
+ 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
^ permalink raw reply [flat|nested] 3+ messages in thread
* [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation
@ 2022-11-02 13:07 Adhemerval Zanella
0 siblings, 0 replies; 3+ messages in thread
From: Adhemerval Zanella @ 2022-11-02 13:07 UTC (permalink / raw)
To: glibc-cvs
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=6a15144809e2b4c6d51406d92df5b485536dc62c
commit 6a15144809e2b4c6d51406d92df5b485536dc62c
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Tue Oct 20 16:00:43 2020 -0300
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.
Diff:
---
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 | 62 +++++++++++++++++++-----
4 files changed, 64 insertions(+), 108 deletions(-)
diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 510a586859..92481526c5 100644
--- a/sysdeps/unix/sysv/linux/getdents64.c
+++ b/sysdeps/unix/sysv/linux/getdents64.c
@@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64)
#if _DIRENT_MATCHES_DIRENT64
strong_alias (__getdents64, __getdents)
-#else
-# include <shlib-compat.h>
-
-# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-# include <olddirent.h>
-# include <unistd.h>
-
-static ssize_t
-handle_overflow (int fd, __off64_t offset, ssize_t count)
-{
- /* If this is the first entry in the buffer, we can report the
- error. */
- if (offset == 0)
- {
- __set_errno (EOVERFLOW);
- return -1;
- }
-
- /* Otherwise, seek to the overflowing entry, so that the next call
- will report the error, and return the data read so far. */
- if (__lseek64 (fd, offset, SEEK_SET) != 0)
- return -1;
- return count;
-}
-
-ssize_t
-__old_getdents64 (int fd, char *buf, size_t nbytes)
-{
- /* We do not move the individual directory entries. This is only
- possible if the target type (struct __old_dirent64) is smaller
- than the source type. */
- _Static_assert (offsetof (struct __old_dirent64, d_name)
- <= offsetof (struct dirent64, d_name),
- "__old_dirent64 is larger than dirent64");
- _Static_assert (__alignof__ (struct __old_dirent64)
- <= __alignof__ (struct dirent64),
- "alignment of __old_dirent64 is larger than dirent64");
-
- ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes);
- if (retval > 0)
- {
- /* This is the marker for the first entry. Offset 0 is reserved
- for the first entry (see rewinddir). Here, we use it as a
- marker for the first entry in the buffer. We never actually
- seek to offset 0 because handle_overflow reports the error
- directly, so it does not matter that the offset is incorrect
- if entries have been read from the descriptor before (so that
- the descriptor is not actually at offset 0). */
- __off64_t previous_offset = 0;
-
- char *p = buf;
- char *end = buf + retval;
- while (p < end)
- {
- struct dirent64 *source = (struct dirent64 *) p;
-
- /* Copy out the fixed-size data. */
- __ino_t ino = source->d_ino;
- __off64_t offset = source->d_off;
- unsigned int reclen = source->d_reclen;
- unsigned char type = source->d_type;
-
- /* Check for ino_t overflow. */
- if (__glibc_unlikely (ino != source->d_ino))
- return handle_overflow (fd, previous_offset, p - buf);
-
- /* Convert to the target layout. Use a separate struct and
- memcpy to side-step aliasing issues. */
- struct __old_dirent64 result;
- result.d_ino = ino;
- result.d_off = offset;
- result.d_reclen = reclen;
- result.d_type = type;
-
- /* Write the fixed-sized part of the result to the
- buffer. */
- size_t result_name_offset = offsetof (struct __old_dirent64, d_name);
- memcpy (p, &result, result_name_offset);
-
- /* Adjust the position of the name if necessary. Copy
- everything until the end of the record, including the
- terminating NUL byte. */
- if (result_name_offset != offsetof (struct dirent64, d_name))
- memmove (p + result_name_offset, source->d_name,
- reclen - offsetof (struct dirent64, d_name));
-
- p += reclen;
- previous_offset = offset;
- }
- }
- return retval;
-}
-# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */
#endif /* _DIRENT_MATCHES_DIRENT64 */
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index 00c84b9521..68aafd7c02 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 9a0b7ab4c4..df40b0a64e 100644
--- a/sysdeps/unix/sysv/linux/opendir.c
+++ b/sysdeps/unix/sysv/linux/opendir.c
@@ -23,6 +23,11 @@
#include <not-cancel.h>
+#include <shlib-compat.h>
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+# include <olddirent.h>
+#endif
+
enum {
opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC
};
@@ -128,7 +133,15 @@ __alloc_dir (int fd, bool close_fd, int flags,
expand the buffer if required. */
enum
{
- tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
+ tbuffer_size =
+# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
+ /* This is used on compat readdir64. */
+ MAX (sizeof (struct dirent),
+ sizeof (struct __old_dirent64))
+# else
+ sizeof (struct dirent)
+# endif
+ + NAME_MAX + 1
};
dirp->tbuffer = malloc (tbuffer_size);
if (dirp->tbuffer == NULL)
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index bbe247f95d..01e834e238 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -102,21 +102,52 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
# include <olddirent.h>
+/* Translate the DP64 entry to the old LFS one in the translation buffer
+ at dirstream DS. Return true is the translation was possible or
+ false if either an internal fields can be represented in the non-LFS
+ entry or if the translation can not be resized. */
+static bool
+dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64)
+{
+ /* Check for overflow. */
+ ino_t d_ino = dp64->d_ino;
+ if (d_ino != dp64->d_ino)
+ return false;
+
+ /* Expand the translation buffer to hold the new namesize. */
+ size_t d_reclen = sizeof (struct __old_dirent64)
+ + dp64->d_reclen - offsetof (struct dirent64, d_name);
+ if (d_reclen > ds->tbuffer_size)
+ {
+ char *newbuffer = realloc (ds->tbuffer, d_reclen);
+ if (newbuffer == NULL)
+ return false;
+ ds->tbuffer = newbuffer;
+ ds->tbuffer_size = d_reclen;
+ }
+
+ struct __old_dirent64 *olddp64 = (struct __old_dirent64 *) ds->tbuffer;
+
+ olddp64->d_off = dp64->d_off;
+ olddp64->d_ino = dp64->d_ino;
+ olddp64->d_reclen = dp64->d_reclen;
+ olddp64->d_type = dp64->d_type;
+ memcpy (olddp64->d_name, dp64->d_name,
+ dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+ return true;
+}
+
attribute_compat_text_section
struct __old_dirent64 *
__old_readdir64_unlocked (DIR *dirp)
{
- struct __old_dirent64 *dp;
- int saved_errno = errno;
+ const int saved_errno = errno;
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 +158,24 @@ __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))
+ {
+ dirp->filepos = dp64->d_off;
+ return (struct __old_dirent64 *) dirp->tbuffer;
+ }
+
+ return NULL;
}
attribute_compat_text_section
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-01-27 20:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-26 12:17 [glibc/azanella/bz23960-dirent] linux: Use getdents64 on readdir64 compat implementation Adhemerval Zanella
-- strict thread matches above, loose matches on Subject: below --
2023-01-27 20:40 Adhemerval Zanella
2022-11-02 13:07 Adhemerval Zanella
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).