1 /* Get directory entries. Linux non-LFS version.
2 Copyright (C) 1993-2024 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 <https://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
, void *buf0
, size_t nbytes
)
40 /* For !_DIRENT_MATCHES_DIRENT64 kernel 'linux_dirent64' has the same
41 layout of 'struct dirent64'. */
45 } *kbuf
= (void *) buf
, *outp
, *inp
;
46 size_t kbytes
= nbytes
;
47 off64_t last_offset
= -1;
50 # define size_diff (offsetof (struct dirent64, d_name) \
51 - offsetof (struct dirent, d_name))
52 char kbuftmp
[sizeof (struct dirent
) + size_diff
];
53 if (nbytes
<= sizeof (struct dirent
))
54 kbuf
= (void*) kbuftmp
;
56 retval
= INLINE_SYSCALL_CALL (getdents64
, fd
, kbuf
, kbytes
);
60 /* These two pointers might alias the same memory buffer.
61 Standard C requires that we always use the same type for them,
62 so we must use the union type. */
66 while (&inp
->b
< &kbuf
->b
+ retval
)
68 const size_t alignment
= _Alignof (struct dirent
);
69 /* Since inp->k.d_reclen is already aligned for the kernel
70 structure this may compute a value that is bigger
72 size_t old_reclen
= inp
->k
.d_reclen
;
73 size_t new_reclen
= ((old_reclen
- size_diff
+ alignment
- 1)
76 /* Copy the data out of the old structure into temporary space.
77 Then copy the name, which may overlap if BUF == KBUF. */
78 const uint64_t d_ino
= inp
->k
.d_ino
;
79 const int64_t d_off
= inp
->k
.d_off
;
80 const uint8_t d_type
= inp
->k
.d_type
;
82 memmove (outp
->u
.d_name
, inp
->k
.d_name
,
83 old_reclen
- offsetof (struct dirent64
, d_name
));
85 /* Now we have copied the data from INP and access only OUTP. */
87 DIRENT_SET_DP_INO (&outp
->u
, d_ino
);
88 outp
->u
.d_off
= d_off
;
89 if ((sizeof (outp
->u
.d_ino
) != sizeof (inp
->k
.d_ino
)
90 && outp
->u
.d_ino
!= d_ino
)
91 || (sizeof (outp
->u
.d_off
) != sizeof (inp
->k
.d_off
)
92 && outp
->u
.d_off
!= d_off
))
94 /* Overflow. If there was at least one entry before this one,
95 return them without error, otherwise signal overflow. */
96 if (last_offset
!= -1)
98 __lseek64 (fd
, last_offset
, SEEK_SET
);
101 return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW
);
105 outp
->u
.d_reclen
= new_reclen
;
106 outp
->u
.d_type
= d_type
;
108 inp
= (void *) inp
+ old_reclen
;
109 outp
= (void *) outp
+ new_reclen
;
112 return outp
->b
- buf
;
115 # undef DIRENT_SET_DP_INO
117 #endif /* _DIRENT_MATCHES_DIRENT64 */