1 /*
2 * Copyright (c) 2020-2021 Dmitry V. Levin <ldv@strace.io>
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7
8 #include "xgetdents.h"
9 #include "kernel_dirent.h"
10
11 static void
12 decode_dents(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
13 const unsigned int header_size,
14 const decode_dentry_head_fn decode_dentry_head,
15 const decode_dentry_tail_fn decode_dentry_tail)
16 {
17 union {
18 kernel_dirent_t ent;
19 kernel_dirent64_t ent64;
20 } dent;
21 unsigned int count = 0;
22
23 if (abbrev(tcp))
24 printaddr(addr);
25
26 for (;;) {
27 if (len < header_size) {
28 if (!abbrev(tcp)) {
29 if (!len) {
30 tprint_array_begin();
31 ++count;
32 } else {
33 printstr_ex(tcp, addr, len,
34 QUOTE_FORCE_HEX);
35 }
36 }
37 break;
38 }
39
40 /* len >= header_size after this point. */
41 if (!tfetch_mem(tcp, addr, header_size, &dent)) {
42 if (!abbrev(tcp)) {
43 if (count) {
44 tprint_more_data_follows();
45 printaddr_comment(addr);
46 } else {
47 printaddr(addr);
48 }
49 }
50
51 break;
52 }
53
54 if (!abbrev(tcp)) {
55 if (!count)
56 tprint_array_begin();
57 }
58 ++count;
59
60 kernel_ulong_t next_addr = 0;
61 unsigned int next_len = 0;
62 unsigned int d_reclen = decode_dentry_head(tcp, &dent);
63
64 if (d_reclen > len) {
65 /* cannot happen? */
66 tprintf_comment("%s%u bytes overflow",
67 (abbrev(tcp) ? "d_reclen " : ""),
68 d_reclen - len);
69 d_reclen = len;
70 } else if (d_reclen < header_size) {
71 /* cannot happen? */
72 tprintf_comment("%s%u bytes underflow",
73 (abbrev(tcp) ? "d_reclen " : ""),
74 header_size - d_reclen);
75 d_reclen = header_size;
76 next_len = len - header_size;
77 } else {
78 next_len = len - d_reclen;
79 if (next_len) {
80 if (addr + d_reclen > addr) {
81 next_addr = addr + d_reclen;
82 } else {
83 /* cannot happen? */
84 tprints_comment("address overflow");
85 }
86 }
87 }
88
89 len = next_len;
90 /* Do not use len inside the loop after this point. */
91
92 if (!abbrev(tcp)) {
93 int rc = decode_dentry_tail(tcp, addr + header_size,
94 &dent,
95 d_reclen - header_size);
96 if (next_addr) {
97 tprint_array_next();
98 if (rc < 0) {
99 tprint_more_data_follows();
100 break;
101 }
102 }
103 }
104
105 if (!next_addr)
106 break;
107 addr = next_addr;
108 }
109
110 if (!abbrev(tcp)) {
111 if (count)
112 tprint_array_end();
113 } else {
114 tprintf_comment("%u%s entries", count, len ? "+" : "");
115 }
116 }
117
118 int
119 xgetdents(struct tcb *const tcp, const unsigned int header_size,
120 const decode_dentry_head_fn decode_dentry_head,
121 const decode_dentry_tail_fn decode_dentry_tail)
122 {
123 if (entering(tcp)) {
124 /* fd */
125 printfd(tcp, tcp->u_arg[0]);
126 #ifdef ENABLE_SECONTEXT
127 tcp->last_dirfd = (int) tcp->u_arg[0];
128 #endif
129 tprint_arg_next();
130 return 0;
131 }
132
133 const unsigned int count = tcp->u_arg[2];
134
135 if (syserror(tcp) || !verbose(tcp) ||
136 (kernel_ulong_t) tcp->u_rval > count /* kernel gone bananas? */) {
137 printaddr(tcp->u_arg[1]);
138 } else {
139 decode_dents(tcp, tcp->u_arg[1], tcp->u_rval, header_size,
140 decode_dentry_head, decode_dentry_tail);
141 }
142 tprint_arg_next();
143
144 PRINT_VAL_U(count);
145 return 0;
146 }