]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
3bc05641 BF |
2 | /* |
3 | * Copyright (c) 2013 Red Hat, Inc. | |
4 | * All Rights Reserved. | |
3bc05641 BF |
5 | */ |
6 | ||
6b803e5a CH |
7 | #include "command.h" |
8 | #include "input.h" | |
3bc05641 BF |
9 | #include "init.h" |
10 | #include "io.h" | |
11 | ||
12 | #include <sys/types.h> | |
13 | #include <dirent.h> | |
14 | ||
2b86cff7 RS |
15 | #ifndef _DIRENT_HAVE_D_RECLEN |
16 | #include <string.h> | |
17 | #endif | |
18 | ||
3bc05641 BF |
19 | static struct cmdinfo readdir_cmd; |
20 | ||
00ff2b10 | 21 | static const char *d_type_str(unsigned int type) |
3bc05641 BF |
22 | { |
23 | const char *str; | |
24 | ||
25 | switch (type) { | |
26 | case DT_UNKNOWN: | |
27 | str = "DT_UNKNOWN"; | |
28 | break; | |
29 | case DT_FIFO: | |
30 | str = "DT_FIFO"; | |
31 | break; | |
32 | case DT_CHR: | |
33 | str = "DT_CHR"; | |
34 | break; | |
35 | case DT_DIR: | |
36 | str = "DT_DIR"; | |
37 | break; | |
38 | case DT_BLK: | |
39 | str = "DT_BLK"; | |
40 | break; | |
41 | case DT_REG: | |
42 | str = "DT_REG"; | |
43 | break; | |
44 | case DT_LNK: | |
45 | str = "DT_LNK"; | |
46 | break; | |
47 | case DT_SOCK: | |
48 | str = "DT_SOCK"; | |
49 | break; | |
50 | case DT_WHT: | |
51 | str = "DT_WHT"; | |
52 | break; | |
53 | default: | |
54 | str = "ERROR!"; | |
55 | break; | |
56 | } | |
57 | ||
58 | return str; | |
59 | } | |
60 | ||
61 | static void | |
62 | dump_dirent( | |
63 | long long offset, | |
64 | struct dirent *dirent) | |
65 | { | |
5e303e25 ES |
66 | printf("%08llx: d_ino: 0x%08llx", offset, |
67 | (unsigned long long)dirent->d_ino); | |
3bc05641 | 68 | #ifdef _DIRENT_HAVE_D_OFF |
5e303e25 | 69 | printf(" d_off: 0x%08llx", (unsigned long long)dirent->d_off); |
3bc05641 BF |
70 | #endif |
71 | #ifdef _DIRENT_HAVE_D_RECLEN | |
72 | printf(" d_reclen: 0x%x", dirent->d_reclen); | |
73 | #endif | |
74 | #ifdef _DIRENT_HAVE_D_TYPE | |
75 | printf(" d_type: %s", d_type_str(dirent->d_type)); | |
76 | #endif | |
77 | printf(" d_name: %s\n", dirent->d_name); | |
78 | } | |
79 | ||
80 | static int | |
81 | read_directory( | |
82 | DIR *dir, | |
83 | long long offset, | |
84 | unsigned long long length, | |
85 | int dump, | |
86 | unsigned long long *total) | |
87 | { | |
88 | struct dirent *dirent; | |
89 | int count = 0; | |
90 | ||
91 | seekdir(dir, offset); | |
92 | ||
93 | *total = 0; | |
94 | while (*total < length) { | |
95 | dirent = readdir(dir); | |
96 | if (!dirent) | |
97 | break; | |
98 | ||
19863f7b | 99 | #ifdef _DIRENT_HAVE_D_RECLEN |
3bc05641 | 100 | *total += dirent->d_reclen; |
19863f7b | 101 | #else |
2b86cff7 | 102 | *total += strlen(dirent->d_name) + sizeof(*dirent); |
19863f7b | 103 | #endif |
3bc05641 BF |
104 | count++; |
105 | ||
106 | if (dump) { | |
107 | dump_dirent(offset, dirent); | |
19863f7b | 108 | #ifdef _DIRENT_HAVE_D_OFF |
3bc05641 | 109 | offset = dirent->d_off; |
19863f7b JT |
110 | #else |
111 | /* Some platforms don't have dirent->d_off, but because | |
112 | * it is used only for dumping the value, it should be | |
113 | * safe to only set it to zero in such case. | |
114 | */ | |
115 | offset = 0; | |
116 | #endif | |
3bc05641 BF |
117 | } |
118 | } | |
119 | ||
120 | return count; | |
121 | } | |
122 | ||
123 | static int | |
124 | readdir_f( | |
125 | int argc, | |
126 | char **argv) | |
127 | { | |
128 | int cnt; | |
129 | unsigned long long total; | |
130 | int c; | |
131 | size_t fsblocksize, fssectsize; | |
132 | struct timeval t1, t2; | |
133 | char s1[64], s2[64], ts[64]; | |
134 | long long offset = -1; | |
135 | unsigned long long length = -1; /* max length limit */ | |
136 | int verbose = 0; | |
137 | DIR *dir; | |
138 | int dfd; | |
139 | ||
140 | init_cvtnum(&fsblocksize, &fssectsize); | |
141 | ||
142 | while ((c = getopt(argc, argv, "l:o:v")) != EOF) { | |
143 | switch (c) { | |
144 | case 'l': | |
145 | length = cvtnum(fsblocksize, fssectsize, optarg); | |
146 | break; | |
147 | case 'o': | |
148 | offset = cvtnum(fsblocksize, fssectsize, optarg); | |
149 | break; | |
150 | case 'v': | |
151 | verbose = 1; | |
152 | break; | |
153 | default: | |
154 | return command_usage(&readdir_cmd); | |
155 | } | |
156 | } | |
157 | ||
158 | dfd = dup(file->fd); | |
159 | if (dfd < 0) | |
160 | return -1; | |
161 | ||
162 | dir = fdopendir(dfd); | |
163 | if (!dir) { | |
164 | close(dfd); | |
165 | return -1; | |
166 | } | |
167 | ||
168 | if (offset == -1) { | |
169 | rewinddir(dir); | |
170 | offset = telldir(dir); | |
171 | } | |
172 | ||
173 | gettimeofday(&t1, NULL); | |
174 | cnt = read_directory(dir, offset, length, verbose, &total); | |
175 | gettimeofday(&t2, NULL); | |
176 | ||
177 | closedir(dir); | |
11e06961 | 178 | close(dfd); |
3bc05641 BF |
179 | |
180 | t2 = tsub(t2, t1); | |
181 | timestr(&t2, ts, sizeof(ts), 0); | |
182 | ||
183 | cvtstr(total, s1, sizeof(s1)); | |
184 | cvtstr(tdiv(total, t2), s2, sizeof(s2)); | |
185 | ||
186 | printf(_("read %llu bytes from offset %lld\n"), total, offset); | |
187 | printf(_("%s, %d ops, %s (%s/sec and %.4f ops/sec)\n"), | |
188 | s1, cnt, ts, s2, tdiv(cnt, t2)); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | void | |
194 | readdir_init(void) | |
195 | { | |
196 | readdir_cmd.name = "readdir"; | |
197 | readdir_cmd.cfunc = readdir_f; | |
198 | readdir_cmd.argmax = 5; | |
199 | readdir_cmd.flags = CMD_NOMAP_OK|CMD_FOREIGN_OK; | |
200 | readdir_cmd.args = _("[-v][-o offset][-l length]"); | |
201 | readdir_cmd.oneline = _("read directory entries"); | |
202 | ||
203 | add_command(&readdir_cmd); | |
204 | } |