aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/getdents64.c37
-rw-r--r--sysdeps/unix/sysv/linux/tst-getdents64.c29
2 files changed, 42 insertions, 24 deletions
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
index a218f68..ed6589a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
+++ b/sysdeps/unix/sysv/linux/mips/mips64/getdents64.c
@@ -90,17 +90,14 @@ __getdents64 (int fd, void *buf, size_t nbytes)
while ((char *) kdp < (char *) skdp + r)
{
- /* This macro is used to avoid aliasing violation. */
-#define KDP_MEMBER(src, member) \
- (__typeof__((struct kernel_dirent){0}.member) *) \
- memcpy (&((__typeof__((struct kernel_dirent){0}.member)){0}), \
- ((char *)(src) + offsetof (struct kernel_dirent, member)),\
- sizeof ((struct kernel_dirent){0}.member))
-
/* This is a conservative approximation, since some of size_diff might
fit into the existing padding for alignment. */
- unsigned short int k_reclen = *KDP_MEMBER (kdp, d_reclen);
- unsigned short int new_reclen = ALIGN_UP (k_reclen + size_diff,
+
+ /* Obtain the d_ino, d_off, and d_reclen from kernel filled buffer. */
+ struct kernel_dirent kdirent;
+ memcpy (&kdirent, kdp, offsetof (struct kernel_dirent, d_name));
+
+ unsigned short int new_reclen = ALIGN_UP (kdirent.d_reclen + size_diff,
_Alignof (struct dirent64));
if (nb + new_reclen > nbytes)
{
@@ -118,19 +115,21 @@ __getdents64 (int fd, void *buf, size_t nbytes)
}
nb += new_reclen;
- memcpy (((char *) dp + offsetof (struct dirent64, d_ino)),
- KDP_MEMBER (kdp, d_ino), sizeof ((struct dirent64){0}.d_ino));
- memcpy (((char *) dp + offsetof (struct dirent64, d_off)),
- KDP_MEMBER (kdp, d_off), sizeof ((struct dirent64){0}.d_off));
- last_offset = *KDP_MEMBER (kdp, d_off);
- memcpy (((char *) dp + offsetof (struct dirent64, d_reclen)),
- &new_reclen, sizeof (new_reclen));
- dp->d_type = *((char *) kdp + k_reclen - 1);
+ struct dirent64 d64;
+ d64.d_ino = kdirent.d_ino;
+ d64.d_off = kdirent.d_off;
+ d64.d_reclen = new_reclen;
+ d64.d_type = *((char *) kdp + kdirent.d_reclen - 1);
+ /* First copy only the header. */
+ memcpy (dp, &d64, offsetof (struct dirent64, d_name));
+ /* And then the d_name. */
memcpy (dp->d_name, kdp->d_name,
- k_reclen - offsetof (struct kernel_dirent, d_name));
+ kdirent.d_reclen - offsetof (struct kernel_dirent, d_name));
+
+ last_offset = kdirent.d_off;
dp = (struct dirent64 *) ((char *) dp + new_reclen);
- kdp = (struct kernel_dirent *) (((char *) kdp) + k_reclen);
+ kdp = (struct kernel_dirent *) (((char *) kdp) + kdirent.d_reclen);
}
return (char *) dp - (char *) buf;
diff --git a/sysdeps/unix/sysv/linux/tst-getdents64.c b/sysdeps/unix/sysv/linux/tst-getdents64.c
index 379ecbb..691444d 100644
--- a/sysdeps/unix/sysv/linux/tst-getdents64.c
+++ b/sysdeps/unix/sysv/linux/tst-getdents64.c
@@ -76,8 +76,18 @@ large_buffer_checks (int fd)
}
}
-static int
-do_test (void)
+static void
+do_test_large_size (void)
+{
+ int fd = xopen (".", O_RDONLY | O_DIRECTORY, 0);
+ TEST_VERIFY (fd >= 0);
+ large_buffer_checks (fd);
+
+ xclose (fd);
+}
+
+static void
+do_test_by_size (size_t buffer_size)
{
/* The test compares the iteration order with readdir64. */
DIR *reference = opendir (".");
@@ -98,7 +108,7 @@ do_test (void)
non-existing data. */
struct
{
- char buffer[1024];
+ char buffer[buffer_size];
struct dirent64 pad;
} data;
@@ -153,10 +163,19 @@ do_test (void)
rewinddir (reference);
}
- large_buffer_checks (fd);
-
xclose (fd);
closedir (reference);
+}
+
+static int
+do_test (void)
+{
+ do_test_by_size (512);
+ do_test_by_size (1024);
+ do_test_by_size (4096);
+
+ do_test_large_size ();
+
return 0;
}