summaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/getdents.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/getdents.c')
-rw-r--r--sysdeps/unix/sysv/linux/getdents.c26
1 files changed, 14 insertions, 12 deletions
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c
index 0504393e67..40b31617ef 100644
--- a/sysdeps/unix/sysv/linux/getdents.c
+++ b/sysdeps/unix/sysv/linux/getdents.c
@@ -16,10 +16,12 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#include <alloca.h>
#include <dirent.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
+#include <sys/param.h>
#include <sys/types.h>
#include <linux/posix_types.h>
@@ -52,8 +54,7 @@ struct kernel_dirent
in the buffer. Taking this number allows us to specify a correct
number of bytes to read. If we should be wrong, we can reset the
file descriptor. */
-int __getdirentries __P ((int fd, char *buf, size_t nbytes, off_t *basep));
-int
+ssize_t
__getdirentries (fd, buf, nbytes, basep)
int fd;
char *buf;
@@ -62,32 +63,33 @@ __getdirentries (fd, buf, nbytes, basep)
{
off_t base = __lseek (fd, (off_t) 0, SEEK_CUR);
size_t red_nbytes;
- struct kernel_dirent *kdp;
+ struct kernel_dirent *skdp, *kdp;
struct dirent *dp;
int retval;
red_nbytes = nbytes - (nbytes / (offsetof (struct dirent, d_name) + 3));
dp = (struct dirent *) buf;
- kdp = (struct kernel_dirent *) (buf + (nbytes - red_nbytes));
+ skdp = kdp = __alloca (red_nbytes);
retval = __getdents (fd, (char *) kdp, red_nbytes);
- while ((char *) kdp < buf + (nbytes - red_nbytes) + retval)
+ while ((char *) kdp < (char *) skdp + retval)
{
+ const size_t size_diff = MAX (offsetof (struct dirent, d_name)
+ - offsetof (struct kernel_dirent, d_name),
+ __alignof__ (struct dirent));
dp->d_ino = kdp->d_ino;
dp->d_off = kdp->d_off;
- dp->d_reclen = (kdp->d_reclen
- + (offsetof (struct dirent, d_name)
- - offsetof (struct kernel_dirent, d_name)));
+ dp->d_reclen = kdp->d_reclen + size_diff;
dp->d_type = DT_UNKNOWN;
- memmove (dp->d_name, kdp->d_name,
- kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
+ memcpy (dp->d_name, kdp->d_name,
+ kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
dp = (struct dirent *) (((char *) dp) + dp->d_reclen);
kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
- if ((char *) dp >= (char *) kdp)
+ if ((char *) dp >= buf + nbytes)
{
/* Our heuristic failed. We read too many entries. Reset
the stream. */
@@ -99,7 +101,7 @@ __getdirentries (fd, buf, nbytes, basep)
if (basep)
*basep = base;
- return (char *) dp - (char *) buf;
+ return (char *) dp - buf;
}
weak_alias (__getdirentries, getdirentries)