summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch')
-rw-r--r--9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch204
1 files changed, 204 insertions, 0 deletions
diff --git a/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch b/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch
new file mode 100644
index 0000000..90910a6
--- /dev/null
+++ b/9999/0202-linux-Use-getdents64-on-non-LFS-readdir.patch
@@ -0,0 +1,204 @@
+From 5180512e6c81b1b0423572594983c74c499b7e1e Mon Sep 17 00:00:00 2001
+From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date: Tue, 20 Oct 2020 13:37:15 -0300
+Subject: [PATCH 2/7] linux: Use getdents64 on non-LFS readdir
+
+The opendir allocates a translation buffer to be used to return the
+non-LFS readdir entry. The obtained 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.
+
+Checked on x86_64-linux-gnu and i686-linux-gnu.
+---
+ sysdeps/unix/sysv/linux/closedir.c | 4 ++
+ sysdeps/unix/sysv/linux/dirstream.h | 5 ++
+ sysdeps/unix/sysv/linux/opendir.c | 21 +++++++
+ sysdeps/unix/sysv/linux/readdir.c | 97 +++++++++++++++++++++--------
+ 4 files changed, 101 insertions(+), 26 deletions(-)
+
+diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c
+index 4bb5274b00..1f71445ad9 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_MATCHES_DIRENT64
++ free (dirp->tbuffer);
++#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 b5e1db8db0..64b1495ba0 100644
+--- a/sysdeps/unix/sysv/linux/dirstream.h
++++ b/sysdeps/unix/sysv/linux/dirstream.h
+@@ -41,6 +41,11 @@ struct __dirstream
+
+ int errcode; /* Delayed error code. */
+
++#if !defined __OFF_T_MATCHES_OFF64_T || !defined __INO_T_MATCHES_INO64_T
++ char *tbuffer; /* Translation buffer for non-LFS calls. */
++ size_t tbuffer_size; /* Size of translation buffer. */
++#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 48f254d169..d7df13575e 100644
+--- a/sysdeps/unix/sysv/linux/opendir.c
++++ b/sysdeps/unix/sysv/linux/opendir.c
+@@ -120,6 +120,27 @@ __alloc_dir (int fd, bool close_fd, int flags,
+ return NULL;
+ }
+
++#if !_DIRENT_MATCHES_DIRENT64
++ /* Allocates a translation buffer to use as the returned 'struct direct'
++ for non-LFS 'readdir' calls.
++
++ The initial NAME_MAX size should handle most cases, while readdir might
++ expand the buffer if required. */
++ enum
++ {
++ tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1
++ };
++ dirp->tbuffer = malloc (tbuffer_size);
++ if (dirp->tbuffer == NULL)
++ {
++ free (dirp);
++ if (close_fd)
++ __close_nocancel_nostatus (fd);
++ return NULL;
++ }
++ dirp->tbuffer_size = tbuffer_size;
++#endif
++
+ dirp->fd = fd;
+ #if IS_IN (libc)
+ __libc_lock_init (dirp->lock);
+diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c
+index c0619ce06f..8647bb0aef 100644
+--- a/sysdeps/unix/sysv/linux/readdir.c
++++ b/sysdeps/unix/sysv/linux/readdir.c
+@@ -21,42 +21,87 @@
+ #if !_DIRENT_MATCHES_DIRENT64
+ #include <dirstream.h>
+
++/* Translate the DP64 entry to the non-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_entry (struct __dirstream *ds, const struct dirent64 *dp64)
++{
++ off_t d_off = dp64->d_off;
++ if (d_off != dp64->d_off)
++ return false;
++ ino_t d_ino = dp64->d_ino;
++ if (d_ino != dp64->d_ino)
++ return false;
++
++ /* Expand the translation buffer to hold the new name size. */
++ size_t new_reclen = sizeof (struct dirent)
++ + dp64->d_reclen - offsetof (struct dirent64, d_name);
++ if (new_reclen > ds->tbuffer_size)
++ {
++ char *newbuffer = realloc (ds->tbuffer, new_reclen);
++ if (newbuffer == NULL)
++ return false;
++ ds->tbuffer = newbuffer;
++ ds->tbuffer_size = new_reclen;
++ }
++
++ struct dirent *dp = (struct dirent *) ds->tbuffer;
++
++ dp->d_off = d_off;
++ dp->d_ino = d_ino;
++ dp->d_reclen = new_reclen;
++ dp->d_type = dp64->d_type;
++ memcpy (dp->d_name, dp64->d_name,
++ dp64->d_reclen - offsetof (struct dirent64, d_name));
++
++ return true;
++}
++
+ /* Read a directory entry from DIRP. */
+ struct dirent *
+ __readdir_unlocked (DIR *dirp)
+ {
+ const int saved_errno = errno;
+
+- if (dirp->offset >= dirp->size)
++ while (1)
+ {
+- /* We've emptied out our buffer. Refill it. */
+- ssize_t bytes = __getdents (dirp->fd, dirp->data, dirp->allocation);
+- if (bytes <= 0)
++ if (dirp->offset >= dirp->size)
+ {
+- /* 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);
+- return NULL;
++ /* We've emptied out our buffer. Refill it. */
++ ssize_t bytes = __getdents64 (dirp->fd, dirp->data,
++ dirp->allocation);
++ 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);
++ return NULL;
++ }
++ dirp->size = bytes;
++
++ /* Reset the offset into the buffer. */
++ dirp->offset = 0;
++ }
++
++ struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset];
++ dirp->offset += dp64->d_reclen;
++
++ /* Skip entries which might overflow d_off/d_ino or if the translation
++ buffer can't be resized. */
++ if (dirstream_entry (dirp, dp64))
++ {
++ dirp->filepos = dp64->d_off;
++ return (struct dirent *) dirp->tbuffer;
+ }
+- dirp->size = bytes;
+-
+- /* Reset the offset into the buffer. */
+- dirp->offset = 0;
+ }
+-
+- struct dirent *dp = (struct dirent *) &dirp->data[dirp->offset];
+-
+- dirp->offset += dp->d_reclen;
+-
+- dirp->filepos = dp->d_off;
+-
+- return dp;
+ }
+
+ struct dirent *
+--
+2.32.0
+