diff options
-rw-r--r-- | sysdeps/unix/sysv/linux/mips/mips64/getdents64.c | 37 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-getdents64.c | 29 |
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; } |