1 | /* Get directory entries. Linux non-LFS version. |
2 | Copyright (C) 1993-2021 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
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. |
9 | |
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. |
14 | |
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/>. */ |
18 | |
19 | #include <dirent.h> |
20 | |
21 | #if !_DIRENT_MATCHES_DIRENT64 |
22 | |
23 | # include <unistd.h> |
24 | # include <string.h> |
25 | # include <errno.h> |
26 | |
27 | # ifndef DIRENT_SET_DP_INO |
28 | # define DIRENT_SET_DP_INO(dp, value) (dp)->d_ino = (value) |
29 | # endif |
30 | |
31 | /* Pack the dirent64 struct down into 32-bit offset/inode fields, and |
32 | ensure that no overflow occurs. */ |
33 | ssize_t |
34 | __getdents (int fd, void *buf0, size_t nbytes) |
35 | { |
36 | char *buf = buf0; |
37 | |
38 | union |
39 | { |
40 | /* For !_DIRENT_MATCHES_DIRENT64 kernel 'linux_dirent64' has the same |
41 | layout of 'struct dirent64'. */ |
42 | struct dirent64 k; |
43 | struct dirent u; |
44 | char b[1]; |
45 | } *kbuf = (void *) buf, *outp, *inp; |
46 | size_t kbytes = nbytes; |
47 | off64_t last_offset = -1; |
48 | ssize_t retval; |
49 | |
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; |
55 | |
56 | retval = INLINE_SYSCALL_CALL (getdents64, fd, kbuf, kbytes); |
57 | if (retval == -1) |
58 | return -1; |
59 | |
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. */ |
63 | inp = kbuf; |
64 | outp = (void *) buf; |
65 | |
66 | while (&inp->b < &kbuf->b + retval) |
67 | { |
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 |
71 | than necessary. */ |
72 | size_t old_reclen = inp->k.d_reclen; |
73 | size_t new_reclen = ((old_reclen - size_diff + alignment - 1) |
74 | & ~(alignment - 1)); |
75 | |
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; |
81 | |
82 | memmove (outp->u.d_name, inp->k.d_name, |
83 | old_reclen - offsetof (struct dirent64, d_name)); |
84 | |
85 | /* Now we have copied the data from INP and access only OUTP. */ |
86 | |
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)) |
93 | { |
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) |
97 | { |
98 | __lseek64 (fd, last_offset, SEEK_SET); |
99 | return outp->b - buf; |
100 | } |
101 | return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW); |
102 | } |
103 | |
104 | last_offset = d_off; |
105 | outp->u.d_reclen = new_reclen; |
106 | outp->u.d_type = d_type; |
107 | |
108 | inp = (void *) inp + old_reclen; |
109 | outp = (void *) outp + new_reclen; |
110 | } |
111 | |
112 | return outp->b - buf; |
113 | } |
114 | |
115 | # undef DIRENT_SET_DP_INO |
116 | |
117 | #endif /* _DIRENT_MATCHES_DIRENT64 */ |
118 | |