1 /* Copyright (C) 1993-2016 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Simplified from sysdeps/unix/sysv/linux/getdents.c.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <http://www.gnu.org/licenses/>. */
27 #include <sys/param.h>
28 #include <sys/types.h>
31 #include <sys/syscall.h>
33 /* Pack the dirent64 struct down into 32-bit offset/inode fields, and
34 ensure that no overflow occurs. */
36 __getdents (int fd
, char *buf
, size_t nbytes
)
40 struct dirent64 k
; /* Kernel structure. */
43 } *kbuf
= (void *) buf
, *outp
, *inp
;
44 size_t kbytes
= nbytes
;
45 off64_t last_offset
= -1;
48 const size_t size_diff
= (offsetof (struct dirent64
, d_name
)
49 - offsetof (struct dirent
, d_name
));
50 if (nbytes
<= sizeof (struct dirent
))
52 kbytes
= nbytes
+ offsetof (struct dirent64
, d_name
)
53 - offsetof (struct dirent
, d_name
);
54 kbuf
= __alloca(kbytes
);
57 retval
= INLINE_SYSCALL (getdents64
, 3, fd
, kbuf
, kbytes
);
61 /* These two pointers might alias the same memory buffer.
62 Standard C requires that we always use the same type for them,
63 so we must use the union type. */
67 while (&inp
->b
< &kbuf
->b
+ retval
)
69 const size_t alignment
= __alignof__ (struct dirent
);
70 /* Since inp->k.d_reclen is already aligned for the kernel
71 structure this may compute a value that is bigger
73 size_t old_reclen
= inp
->k
.d_reclen
;
74 size_t new_reclen
= ((old_reclen
- size_diff
+ alignment
- 1)
77 /* Copy the data out of the old structure into temporary space.
78 Then copy the name, which may overlap if BUF == KBUF. */
79 const uint64_t d_ino
= inp
->k
.d_ino
;
80 const int64_t d_off
= inp
->k
.d_off
;
81 const uint8_t d_type
= inp
->k
.d_type
;
83 memmove (outp
->u
.d_name
, inp
->k
.d_name
,
84 old_reclen
- offsetof (struct dirent64
, d_name
));
86 /* Now we have copied the data from INP and access only OUTP. */
88 outp
->u
.d_ino
= d_ino
;
89 outp
->u
.d_off
= d_off
;
90 if ((sizeof (outp
->u
.d_ino
) != sizeof (inp
->k
.d_ino
)
91 && outp
->u
.d_ino
!= d_ino
)
92 || (sizeof (outp
->u
.d_off
) != sizeof (inp
->k
.d_off
)
93 && outp
->u
.d_off
!= d_off
))
95 /* Overflow. If there was at least one entry before this one,
96 return them without error, otherwise signal overflow. */
97 if (last_offset
!= -1)
99 __lseek64 (fd
, last_offset
, SEEK_SET
);
100 return outp
->b
- buf
;
102 __set_errno (EOVERFLOW
);
107 outp
->u
.d_reclen
= new_reclen
;
108 outp
->u
.d_type
= d_type
;
110 inp
= (void *) inp
+ old_reclen
;
111 outp
= (void *) outp
+ new_reclen
;
114 return outp
->b
- buf
;