From: Adhemerval Zanella via Libc-alpha <libc-alpha@sourceware.org>
To: libc-alpha@sourceware.org
Cc: James Clarke <jrtc27@debian.org>,
John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Subject: [PATCH v2 8/9] linux: Use getdents64 on readdir64 compat implementation
Date: Fri, 2 Oct 2020 14:06:19 -0300 [thread overview]
Message-ID: <20201002170620.1611673-9-adhemerval.zanella@linaro.org> (raw)
In-Reply-To: <20201002170620.1611673-1-adhemerval.zanella@linaro.org>
It uses a similar strategy from the non-LFS readdir that also
uses getdents64 internally and reserves some space on the allocated
internal DIR buffer to be used as a temporary buffer. The kernel
obtained dirent64 data are copied to the temporary buffer for each
readdir compat call.
It allows to remove __old_getdents64.
Checked on i686-linux-gnu.
---
sysdeps/unix/sysv/linux/getdents64.c | 95 +---------------------------
sysdeps/unix/sysv/linux/olddirent.h | 2 -
sysdeps/unix/sysv/linux/readdir.h | 47 +++++++++++++-
sysdeps/unix/sysv/linux/readdir64.c | 27 ++++----
4 files changed, 58 insertions(+), 113 deletions(-)
diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c
index 75892c2823..f40dfacbfe 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 */
+#endif
diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h
index b118b0ef96..63d8e97868 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/readdir.h b/sysdeps/unix/sysv/linux/readdir.h
index 7f6cf3783d..234a7f26e6 100644
--- a/sysdeps/unix/sysv/linux/readdir.h
+++ b/sysdeps/unix/sysv/linux/readdir.h
@@ -21,6 +21,7 @@
#if !_DIRENT_MATCHES_DIRENT64
# include <dirstream.h>
+# include <olddirent.h>
/* getdents64 is used internally for both LFS and non-LFS implementations.
The non-LFS interface reserves part of the allocated buffer to return the
@@ -106,9 +107,53 @@ dirstream_ret_entry (struct __dirstream *ds)
return dp;
}
+
+/* Return the allocated buffer used on LFS compat readdir call. */
+static inline struct __old_dirent64 *
+dirstream_ret64_compat (struct __dirstream *ds)
+{
+ return (struct __old_dirent64 *) ds->data;
+}
+
+static inline struct __old_dirent64 *
+dirstream_ret_entry64_compat (struct __dirstream *ds)
+{
+ struct dirent64 *dp64 = dirstream_entry (ds);
+ struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds);
+
+ dp64_compat->d_ino = dp64->d_ino;
+ if (dp64_compat->d_ino != dp64->d_ino)
+ /* Overflow. */
+ return NULL;
+
+ dp64_compat->d_off = dp64->d_off;
+
+ const size_t size_diff = (offsetof (struct dirent64, d_name)
+ - offsetof (struct __old_dirent64, d_name));
+ const size_t alignment = _Alignof (struct __old_dirent64);
+ size_t new_reclen = (dp64->d_reclen - size_diff + alignment - 1)
+ & ~(alignment - 1);
+ if (new_reclen > return_buffer_size)
+ /* Overflow. */
+ return NULL;
+
+ /* The compat symbol report the kernel obtained d_reclen, even though
+ it has an incompatible dirent layout. */
+ dp64_compat->d_reclen = dp64->d_reclen;
+
+ dp64_compat->d_type = dp64->d_type;
+
+ memcpy (dp64_compat->d_name, dp64->d_name,
+ dp64->d_reclen - offsetof (struct dirent64, d_name));
+
+ ds->offset += dp64->d_reclen;
+ ds->filepos = dp64->d_off;
+
+ return dp64_compat;
+}
+
#else
/* No need to reserve an buffer space if dirent has already LFS support. */
enum { return_buffer_size = 0 };
#endif /* _DIRENT_MATCHES_DIRENT64 */
-
#endif
diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c
index ce50067637..79a2027fd1 100644
--- a/sysdeps/unix/sysv/linux/readdir64.c
+++ b/sysdeps/unix/sysv/linux/readdir64.c
@@ -112,27 +112,23 @@ weak_alias (__readdir64, readdir64)
versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2);
# endif
# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
-# include <olddirent.h>
+# include <readdir.h>
+# include <unistd.h>
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
{
- size_t reclen;
-
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, dirstream_data (dirp),
+ dirstream_alloc_size (dirp));
if (bytes <= 0)
{
/* On some systems getdents fails with ENOENT when the
@@ -153,13 +149,12 @@ __old_readdir64_unlocked (DIR *dirp)
dirp->offset = 0;
}
- dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
-
- reclen = dp->d_reclen;
-
- dirp->offset += reclen;
-
- dirp->filepos = dp->d_off;
+ dp = dirstream_ret_entry64_compat (dirp);
+ if (dp == NULL)
+ {
+ __set_errno (EOVERFLOW);
+ break;
+ }
/* Skip deleted files. */
} while (dp->d_ino == 0);
--
2.25.1
next prev parent reply other threads:[~2020-10-02 17:07 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-10-02 17:06 [PATCH v2 0/9] Fix getdents{64} regression on some FS Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 1/9] linux: Move posix dir implementations to Linux Adhemerval Zanella via Libc-alpha
2020-10-13 15:33 ` Florian Weimer via Libc-alpha
2020-10-15 14:08 ` Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 2/9] linux: Simplify opendir buffer allocation Adhemerval Zanella via Libc-alpha
2020-10-13 15:34 ` Florian Weimer via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 3/9] linux: Add __readdir_unlocked Adhemerval Zanella via Libc-alpha
2020-10-13 15:43 ` Florian Weimer via Libc-alpha
2020-10-15 14:10 ` Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 4/9] linux: Use getdents64 on non-LFS readdir Adhemerval Zanella via Libc-alpha
2020-10-13 15:59 ` Florian Weimer via Libc-alpha
2020-10-15 14:25 ` Adhemerval Zanella via Libc-alpha
2020-10-19 8:18 ` Florian Weimer via Libc-alpha
2020-10-19 20:00 ` Adhemerval Zanella via Libc-alpha
2020-10-19 20:50 ` Florian Weimer via Libc-alpha
2020-10-19 21:09 ` Adhemerval Zanella via Libc-alpha
2020-10-20 7:38 ` Florian Weimer via Libc-alpha
2020-10-20 12:05 ` Adhemerval Zanella via Libc-alpha
2020-10-20 12:35 ` Florian Weimer via Libc-alpha
2020-10-20 14:09 ` Adhemerval Zanella via Libc-alpha
2020-10-20 17:42 ` Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 5/9] linux: Set internal DIR filepos as off64_t [BZ #23960, BZ #24050] Adhemerval Zanella via Libc-alpha
2020-10-13 16:00 ` Florian Weimer via Libc-alpha
2020-10-15 14:26 ` Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 6/9] linux: Add __readdir64_unlocked Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` [PATCH v2 7/9] linux: Add __old_readdir64_unlocked Adhemerval Zanella via Libc-alpha
2020-10-02 17:06 ` Adhemerval Zanella via Libc-alpha [this message]
2020-10-02 17:06 ` [PATCH v2 9/9] dirent: Deprecate getdirentries Adhemerval Zanella via Libc-alpha
2020-10-04 13:08 ` [PATCH v2 0/9] Fix getdents{64} regression on some FS Dave Flogeras via Libc-alpha
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/libc/involved.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201002170620.1611673-9-adhemerval.zanella@linaro.org \
--to=libc-alpha@sourceware.org \
--cc=adhemerval.zanella@linaro.org \
--cc=glaubitz@physik.fu-berlin.de \
--cc=jrtc27@debian.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).