diff options
author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2023-01-27 14:28:34 -0300 |
---|---|---|
committer | Andreas K. Hüttel <dilfridge@gentoo.org> | 2023-07-31 22:14:43 +0200 |
commit | fa7f864c669e484728fa6812b71466ee0efda875 (patch) | |
tree | d7918b510b685ca3ed67a7cf8aca7f7be832f328 | |
parent | linux: Add __old_readdir64_unlocked (diff) | |
download | glibc-fa7f864c669e484728fa6812b71466ee0efda875.tar.gz glibc-fa7f864c669e484728fa6812b71466ee0efda875.tar.bz2 glibc-fa7f864c669e484728fa6812b71466ee0efda875.zip |
linux: Use getdents64 on readdir64 compat implementationgentoo/glibc-2.38-1
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.
-rw-r--r-- | sysdeps/unix/sysv/linux/dirstream.h | 13 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/getdents64.c | 93 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/olddirent.h | 2 | ||||
-rw-r--r-- | 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 |