]> sourceware.org Git - glibc.git/blame - sysdeps/unix/sysv/linux/getdents.c
Update.
[glibc.git] / sysdeps / unix / sysv / linux / getdents.c
CommitLineData
f11b9da6 1/* Copyright (C) 1993, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
df4ef2ab
UD
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
18
8d57beea 19#include <alloca.h>
4186c9f4 20#include <assert.h>
0dee6738 21#include <errno.h>
df4ef2ab
UD
22#include <dirent.h>
23#include <stddef.h>
24#include <string.h>
25#include <unistd.h>
8d57beea 26#include <sys/param.h>
df4ef2ab
UD
27#include <sys/types.h>
28
0dee6738
UD
29#include <sysdep.h>
30#include <sys/syscall.h>
31
df4ef2ab
UD
32#include <linux/posix_types.h>
33
34#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
35
36
4186c9f4 37extern int __syscall_getdents (int fd, char *buf, size_t nbytes);
df4ef2ab
UD
38
39/* For Linux we need a special version of this file since the
40 definition of `struct dirent' is not the same for the kernel and
41 the libc. There is one additional field which might be introduced
42 in the kernel structure in the future.
43
0dee6738 44 Here is the kernel definition of `struct dirent' as of 2.1.20: */
df4ef2ab
UD
45
46struct kernel_dirent
47 {
48 long int d_ino;
49 __kernel_off_t d_off;
50 unsigned short int d_reclen;
51 char d_name[256];
52 };
53
9756dfe1 54#ifdef GETDENTS64
980e5832
UD
55# define __getdents __getdents64
56# define dirent dirent64
9756dfe1 57#endif
df4ef2ab
UD
58
59/* The problem here is that we cannot simply read the next NBYTES
60 bytes. We need to take the additional field into account. We use
831372e7 61 some heuristic. Assuming the directory contains names with 14
980e5832 62 characters on average we can compute an estimated number of entries
831372e7 63 which fit in the buffer. Taking this number allows us to specify a
980e5832
UD
64 reasonable number of bytes to read. If we should be wrong, we can
65 reset the file descriptor. In practice the kernel is limiting the
66 amount of data returned much more then the reduced buffer size. */
8d57beea 67ssize_t
980e5832
UD
68internal_function
69__getdents (int fd, char *buf, size_t nbytes)
df4ef2ab 70{
4186c9f4 71 off_t last_offset = -1;
df4ef2ab 72 size_t red_nbytes;
8d57beea 73 struct kernel_dirent *skdp, *kdp;
df4ef2ab
UD
74 struct dirent *dp;
75 int retval;
831372e7
UD
76 const size_t size_diff = (offsetof (struct dirent, d_name)
77 - offsetof (struct kernel_dirent, d_name));
df4ef2ab 78
4186c9f4
UD
79 red_nbytes = MIN (nbytes
80 - ((nbytes / (offsetof (struct dirent, d_name) + 14))
81 * size_diff),
82 nbytes - size_diff);
df4ef2ab
UD
83
84 dp = (struct dirent *) buf;
8d57beea 85 skdp = kdp = __alloca (red_nbytes);
df4ef2ab 86
0dee6738 87 retval = INLINE_SYSCALL (getdents, 3, fd, (char *) kdp, red_nbytes);
df4ef2ab 88
516d718a
AS
89 if (retval == -1)
90 return -1;
980e5832 91
8d57beea 92 while ((char *) kdp < (char *) skdp + retval)
df4ef2ab 93 {
831372e7
UD
94 const size_t alignment = __alignof__ (struct dirent);
95 /* Since kdp->d_reclen is already aligned for the kernel structure
96 this may compute a value that is bigger than necessary. */
97 size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
98 & ~(alignment - 1));
99 if ((char *) dp + new_reclen > buf + nbytes)
100 {
101 /* Our heuristic failed. We read too many entries. Reset
f11b9da6 102 the stream. */
4186c9f4 103 assert (last_offset != -1);
f11b9da6
UD
104 __lseek (fd, last_offset, SEEK_SET);
105
106 if ((char *) dp == buf)
107 {
108 /* The buffer the user passed in is too small to hold even
109 one entry. */
110 __set_errno (EINVAL);
111 return -1;
112 }
113
831372e7
UD
114 break;
115 }
116
117 last_offset = kdp->d_off;
df4ef2ab
UD
118 dp->d_ino = kdp->d_ino;
119 dp->d_off = kdp->d_off;
831372e7 120 dp->d_reclen = new_reclen;
df4ef2ab 121 dp->d_type = DT_UNKNOWN;
8d57beea
UD
122 memcpy (dp->d_name, kdp->d_name,
123 kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
df4ef2ab 124
831372e7 125 dp = (struct dirent *) ((char *) dp + new_reclen);
df4ef2ab 126 kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
df4ef2ab
UD
127 }
128
8d57beea 129 return (char *) dp - buf;
df4ef2ab 130}
This page took 0.140039 seconds and 5 git commands to generate.