1 /* Get directory entries. Linux non-LFS version.
2 Copyright (C) 1993-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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/>. */
21 #if !_DIRENT_MATCHES_DIRENT64
27 # ifndef DIRENT_SET_DP_INO
28 # define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value)
31 /* Pack the dirent64 struct down into 32-bit offset/inode fields, and
32 ensure that no overflow occurs. */
34 __getdents (int fd
, char *buf
, size_t nbytes
)
38 /* For !_DIRENT_MATCHES_DIRENT64 kernel 'linux_dirent64' has the same
39 layout of 'struct dirent64'. */
43 } *kbuf
= (void *) buf
, *outp
, *inp
;
44 size_t kbytes
= nbytes
;
45 off64_t last_offset
= -1;
48 # define size_diff (offsetof (struct dirent64, d_name) \
49 - offsetof (struct dirent, d_name))
50 char kbuftmp
[sizeof (struct dirent
) + size_diff
];
51 if (nbytes
<= sizeof (struct dirent
))
52 kbuf
= (void*) kbuftmp
;
54 retval
= INLINE_SYSCALL_CALL (getdents64
, fd
, kbuf
, kbytes
);
58 /* These two pointers might alias the same memory buffer.
59 Standard C requires that we always use the same type for them,
60 so we must use the union type. */
64 while (&inp
->b
< &kbuf
->b
+ retval
)
66 const size_t alignment
= _Alignof (struct dirent
);
67 /* Since inp->k.d_reclen is already aligned for the kernel
68 structure this may compute a value that is bigger
70 size_t old_reclen
= inp
->k
.d_reclen
;
71 size_t new_reclen
= ((old_reclen
- size_diff
+ alignment
- 1)
74 /* Copy the data out of the old structure into temporary space.
75 Then copy the name, which may overlap if BUF == KBUF. */
76 const uint64_t d_ino
= inp
->k
.d_ino
;
77 const int64_t d_off
= inp
->k
.d_off
;
78 const uint8_t d_type
= inp
->k
.d_type
;
80 memmove (outp
->u
.d_name
, inp
->k
.d_name
,
81 old_reclen
- offsetof (struct dirent64
, d_name
));
83 /* Now we have copied the data from INP and access only OUTP. */
85 DIRENT_SET_DP_INO (&outp
->u
, d_ino
);
86 outp
->u
.d_off
= d_off
;
87 if ((sizeof (outp
->u
.d_ino
) != sizeof (inp
->k
.d_ino
)
88 && outp
->u
.d_ino
!= d_ino
)
89 || (sizeof (outp
->u
.d_off
) != sizeof (inp
->k
.d_off
)
90 && outp
->u
.d_off
!= d_off
))
92 /* Overflow. If there was at least one entry before this one,
93 return them without error, otherwise signal overflow. */
94 if (last_offset
!= -1)
96 __lseek64 (fd
, last_offset
, SEEK_SET
);
99 return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW
);
103 outp
->u
.d_reclen
= new_reclen
;
104 outp
->u
.d_type
= d_type
;
106 inp
= (void *) inp
+ old_reclen
;
107 outp
= (void *) outp
+ new_reclen
;
110 return outp
->b
- buf
;
113 # undef DIRENT_SET_DP_INO
115 #endif /* _DIRENT_MATCHES_DIRENT64 */