diff options
Diffstat (limited to '9999/0010-linux-Use-getdents64-on-readdir64-compat-implementat.patch')
-rw-r--r-- | 9999/0010-linux-Use-getdents64-on-readdir64-compat-implementat.patch | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/9999/0010-linux-Use-getdents64-on-readdir64-compat-implementat.patch b/9999/0010-linux-Use-getdents64-on-readdir64-compat-implementat.patch new file mode 100644 index 0000000..5bbea04 --- /dev/null +++ b/9999/0010-linux-Use-getdents64-on-readdir64-compat-implementat.patch @@ -0,0 +1,298 @@ +From 7a72e18a7b710feea5375578450e11a106336102 Mon Sep 17 00:00:00 2001 +From: Adhemerval Zanella <adhemerval.zanella@linaro.org> +Date: Tue, 20 Oct 2020 16:00:43 -0300 +Subject: [PATCH 10/12] linux: Use getdents64 on readdir64 compat + implementation + +It uses a similar strategy from the non-LFS readdir that also +uses getdents64 internally and uses a translation buffer to return +the compat readdir64 entry. + +It allows to remove __old_getdents64. + +Checked on i686-linux-gnu. +--- + sysdeps/unix/sysv/linux/getdents64.c | 93 ------------------------ + sysdeps/unix/sysv/linux/olddirent.h | 2 - + sysdeps/unix/sysv/linux/opendir.c | 15 +++- + sysdeps/unix/sysv/linux/readdir64.c | 104 +++++++++++++++++---------- + 4 files changed, 79 insertions(+), 135 deletions(-) + +diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c +index 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 dbf6a8c54d..4b41299c6c 100644 +--- a/sysdeps/unix/sysv/linux/readdir64.c ++++ b/sysdeps/unix/sysv/linux/readdir64.c +@@ -99,57 +99,83 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); + # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) + # include <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; + +- do ++ if (dirp->offset >= dirp->size) + { +- size_t reclen; +- +- if (dirp->offset >= dirp->size) ++ /* We've emptied out our buffer. Refill it. */ ++ ssize_t bytes = __getdents64 (dirp->fd, dirp->data, dirp->allocation); ++ if (bytes <= 0) + { +- /* We've emptied out our buffer. Refill it. */ +- +- size_t maxread = dirp->allocation; +- ssize_t bytes; +- +- bytes = __old_getdents64 (dirp->fd, dirp->data, maxread); +- if (bytes <= 0) +- { +- /* On some systems getdents fails with ENOENT when the +- open directory has been rmdir'd already. POSIX.1 +- requires that we treat this condition like normal EOF. */ +- if (bytes < 0 && errno == ENOENT) +- bytes = 0; +- +- /* Don't modifiy errno when reaching EOF. */ +- if (bytes == 0) +- __set_errno (saved_errno); +- dp = NULL; +- break; +- } +- dirp->size = (size_t) bytes; +- +- /* Reset the offset into the buffer. */ +- dirp->offset = 0; +- } +- +- dp = (struct __old_dirent64 *) &dirp->data[dirp->offset]; ++ /* On some systems getdents fails with ENOENT when the ++ open directory has been rmdir'd already. POSIX.1 ++ requires that we treat this condition like normal EOF. */ ++ if (bytes < 0 && errno == ENOENT) ++ bytes = 0; + +- reclen = dp->d_reclen; ++ /* Don't modifiy errno when reaching EOF. */ ++ if (bytes == 0) ++ __set_errno (saved_errno); ++ return NULL; ++ } ++ dirp->size = bytes; + +- dirp->offset += reclen; ++ /* Reset the offset into the buffer. */ ++ dirp->offset = 0; ++ } + +- dirp->filepos = dp->d_off; ++ struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; ++ dirp->offset += dp64->d_reclen; + +- /* Skip deleted files. */ +- } while (dp->d_ino == 0); ++ /* Skip entries which might overflow d_ino or for memory allocation failure ++ in case of large file names. */ ++ if (dirstream_old_entry (dirp, dp64)) ++ { ++ dirp->filepos = dp64->d_off; ++ return (struct __old_dirent64 *) dirp->tbuffer; ++ } + +- return dp; ++ return NULL; + } + + attribute_compat_text_section +-- +2.35.1 + |