]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
8f0e0912 CH |
2 | /* |
3 | * Copyright (c) 2010 Red Hat, Inc. | |
4 | * All Rights Reserved. | |
8f0e0912 CH |
5 | */ |
6 | ||
dcabd4e7 | 7 | #include "platform_defs.h" |
6b803e5a | 8 | #include "command.h" |
fb2bfcdf | 9 | #include "input.h" |
8f0e0912 | 10 | #include <linux/fiemap.h> |
8f0e0912 CH |
11 | #include "init.h" |
12 | #include "io.h" | |
13 | ||
cb404e20 NB |
14 | #define EXTENT_BATCH 32 |
15 | ||
8f0e0912 | 16 | static cmdinfo_t fiemap_cmd; |
e7eaacef | 17 | static int max_extents = -1; |
8f0e0912 CH |
18 | |
19 | static void | |
20 | fiemap_help(void) | |
21 | { | |
22 | printf(_( | |
23 | "\n" | |
24 | " prints the block mapping for a file's data or attribute forks" | |
25 | "\n" | |
26 | " Example:\n" | |
27 | " 'fiemap -v' - tabular format verbose map\n" | |
28 | "\n" | |
29 | " fiemap prints the map of disk blocks used by the current file.\n" | |
30 | " The map lists each extent used by the file, as well as regions in the\n" | |
31 | " file that do not have any corresponding blocks (holes).\n" | |
32 | " By default, each line of the listing takes the following form:\n" | |
33 | " extent: [startoffset..endoffset]: startblock..endblock\n" | |
34 | " Holes are marked by replacing the startblock..endblock with 'hole'.\n" | |
35 | " All the file offsets and disk blocks are in units of 512-byte blocks.\n" | |
36 | " -a -- prints the attribute fork map instead of the data fork.\n" | |
37 | " -l -- also displays the length of each extent in 512-byte blocks.\n" | |
38 | " -n -- query n extents.\n" | |
39 | " -v -- Verbose information\n" | |
7c18973b NB |
40 | " offset is the starting offset to map, and is optional. If offset is\n" |
41 | " specified, mapping length may (optionally) be specified as well." | |
8f0e0912 CH |
42 | "\n")); |
43 | } | |
44 | ||
abd0c766 NB |
45 | static void |
46 | print_hole( | |
47 | int foff_w, | |
48 | int boff_w, | |
49 | int tot_w, | |
50 | int cur_extent, | |
51 | int lflag, | |
52 | bool plain, | |
53 | __u64 llast, | |
54 | __u64 lstart) | |
55 | { | |
56 | char lbuf[48]; | |
57 | ||
58 | if (plain) { | |
59 | printf("\t%d: [%llu..%llu]: hole", cur_extent, | |
51073f86 | 60 | (unsigned long long)llast, lstart - 1ULL); |
abd0c766 | 61 | if (lflag) |
51073f86 DW |
62 | printf(_(" %llu blocks\n"), |
63 | (unsigned long long)lstart - llast); | |
abd0c766 NB |
64 | else |
65 | printf("\n"); | |
66 | } else { | |
51073f86 DW |
67 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", |
68 | (unsigned long long)llast, lstart - 1ULL); | |
abd0c766 | 69 | printf("%4d: %-*s %-*s %*llu\n", cur_extent, foff_w, lbuf, |
51073f86 DW |
70 | boff_w, _("hole"), tot_w, |
71 | (unsigned long long)lstart - llast); | |
abd0c766 NB |
72 | } |
73 | ||
74 | ||
75 | } | |
76 | ||
997bfca5 | 77 | static int |
8f0e0912 CH |
78 | print_verbose( |
79 | struct fiemap_extent *extent, | |
8f0e0912 CH |
80 | int foff_w, |
81 | int boff_w, | |
82 | int tot_w, | |
83 | int flg_w, | |
997bfca5 NB |
84 | int cur_extent, |
85 | __u64 last_logical) | |
8f0e0912 CH |
86 | { |
87 | __u64 lstart; | |
ef45fa5e | 88 | __u64 llast; |
8f0e0912 CH |
89 | __u64 len; |
90 | __u64 block; | |
91 | char lbuf[48]; | |
92 | char bbuf[48]; | |
93 | char flgbuf[16]; | |
7c18973b | 94 | int num_printed = 0; |
8f0e0912 | 95 | |
997bfca5 | 96 | llast = BTOBBT(last_logical); |
3d12c088 NB |
97 | lstart = BTOBBT(extent->fe_logical); |
98 | len = BTOBBT(extent->fe_length); | |
99 | block = BTOBBT(extent->fe_physical); | |
8f0e0912 CH |
100 | |
101 | memset(lbuf, 0, sizeof(lbuf)); | |
102 | memset(bbuf, 0, sizeof(bbuf)); | |
103 | ||
997bfca5 | 104 | if (cur_extent == 0) { |
b3a5164e ES |
105 | printf("%4s: %-*s %-*s %*s %*s\n", _("EXT"), |
106 | foff_w, _("FILE-OFFSET"), | |
107 | boff_w, _("BLOCK-RANGE"), | |
108 | tot_w, _("TOTAL"), | |
109 | flg_w, _("FLAGS")); | |
110 | } | |
111 | ||
7c18973b | 112 | if (lstart > llast) { |
abd0c766 NB |
113 | print_hole(foff_w, boff_w, tot_w, cur_extent, 0, false, llast, |
114 | lstart); | |
997bfca5 | 115 | cur_extent++; |
7c18973b | 116 | num_printed++; |
8f0e0912 CH |
117 | } |
118 | ||
e7eaacef | 119 | if (cur_extent == max_extents) |
7c18973b | 120 | return num_printed; |
8f0e0912 | 121 | |
51073f86 DW |
122 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", |
123 | (unsigned long long)lstart, lstart + len - 1ULL); | |
124 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", | |
125 | (unsigned long long)block, block + len - 1ULL); | |
8f0e0912 | 126 | snprintf(flgbuf, sizeof(flgbuf), "0x%x", extent->fe_flags); |
997bfca5 | 127 | printf("%4d: %-*s %-*s %*llu %*s\n", cur_extent, foff_w, lbuf, |
51073f86 | 128 | boff_w, bbuf, tot_w, (unsigned long long)len, flg_w, flgbuf); |
8f0e0912 | 129 | |
7c18973b NB |
130 | num_printed++; |
131 | ||
132 | return num_printed; | |
8f0e0912 CH |
133 | } |
134 | ||
997bfca5 | 135 | static int |
8f0e0912 CH |
136 | print_plain( |
137 | struct fiemap_extent *extent, | |
138 | int lflag, | |
997bfca5 NB |
139 | int cur_extent, |
140 | __u64 last_logical) | |
8f0e0912 CH |
141 | { |
142 | __u64 lstart; | |
ef45fa5e | 143 | __u64 llast; |
8f0e0912 CH |
144 | __u64 block; |
145 | __u64 len; | |
7c18973b | 146 | int num_printed = 0; |
8f0e0912 | 147 | |
997bfca5 | 148 | llast = BTOBBT(last_logical); |
3d12c088 NB |
149 | lstart = BTOBBT(extent->fe_logical); |
150 | len = BTOBBT(extent->fe_length); | |
151 | block = BTOBBT(extent->fe_physical); | |
8f0e0912 | 152 | |
7c18973b | 153 | if (lstart > llast) { |
abd0c766 | 154 | print_hole(0, 0, 0, cur_extent, lflag, true, llast, lstart); |
997bfca5 | 155 | cur_extent++; |
7c18973b | 156 | num_printed++; |
8f0e0912 CH |
157 | } |
158 | ||
e7eaacef | 159 | if (cur_extent == max_extents) |
7c18973b | 160 | return num_printed; |
8f0e0912 | 161 | |
997bfca5 | 162 | printf("\t%d: [%llu..%llu]: %llu..%llu", cur_extent, |
51073f86 DW |
163 | (unsigned long long)lstart, lstart + len - 1ULL, |
164 | (unsigned long long)block, block + len - 1ULL); | |
8f0e0912 | 165 | |
7c18973b NB |
166 | num_printed++; |
167 | ||
8f0e0912 | 168 | if (lflag) |
51073f86 | 169 | printf(_(" %llu blocks\n"), (unsigned long long)len); |
8f0e0912 CH |
170 | else |
171 | printf("\n"); | |
7c18973b | 172 | return num_printed; |
8f0e0912 CH |
173 | } |
174 | ||
b3a5164e ES |
175 | /* |
176 | * Calculate the proper extent table format based on first | |
177 | * set of extents | |
178 | */ | |
179 | static void | |
180 | calc_print_format( | |
181 | struct fiemap *fiemap, | |
b3a5164e ES |
182 | int *foff_w, |
183 | int *boff_w, | |
184 | int *tot_w, | |
185 | int *flg_w) | |
186 | { | |
3d12c088 | 187 | int i; |
b3a5164e ES |
188 | char lbuf[32]; |
189 | char bbuf[32]; | |
190 | __u64 logical; | |
191 | __u64 block; | |
192 | __u64 len; | |
193 | struct fiemap_extent *extent; | |
194 | ||
195 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
196 | ||
197 | extent = &fiemap->fm_extents[i]; | |
3d12c088 NB |
198 | logical = BTOBBT(extent->fe_logical); |
199 | len = BTOBBT(extent->fe_length); | |
200 | block = BTOBBT(extent->fe_physical); | |
b3a5164e | 201 | |
51073f86 DW |
202 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]", |
203 | (unsigned long long)logical, | |
204 | (unsigned long long)logical + len - 1); | |
205 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", | |
206 | (unsigned long long)block, | |
207 | (unsigned long long)block + len - 1); | |
b3a5164e ES |
208 | *foff_w = max(*foff_w, strlen(lbuf)); |
209 | *boff_w = max(*boff_w, strlen(bbuf)); | |
210 | *tot_w = max(*tot_w, numlen(len, 10)); | |
211 | *flg_w = max(*flg_w, numlen(extent->fe_flags, 16)); | |
212 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) | |
213 | break; | |
214 | } | |
215 | } | |
216 | ||
00ff2b10 | 217 | static int |
8f0e0912 CH |
218 | fiemap_f( |
219 | int argc, | |
220 | char **argv) | |
221 | { | |
222 | struct fiemap *fiemap; | |
7c18973b | 223 | int done = 0; |
8f0e0912 CH |
224 | int lflag = 0; |
225 | int vflag = 0; | |
226 | int fiemap_flags = FIEMAP_FLAG_SYNC; | |
227 | int c; | |
228 | int i; | |
229 | int map_size; | |
230 | int ret; | |
231 | int cur_extent = 0; | |
232 | int foff_w = 16; /* 16 just for a good minimum range */ | |
233 | int boff_w = 16; | |
234 | int tot_w = 5; /* 5 since its just one number */ | |
235 | int flg_w = 5; | |
7c18973b NB |
236 | __u64 last_logical = 0; /* last extent offset handled */ |
237 | off64_t start_offset = 0; /* mapping start */ | |
238 | off64_t length = -1LL; /* mapping length */ | |
239 | off64_t range_end = -1LL; /* mapping end*/ | |
240 | size_t fsblocksize, fssectsize; | |
8f0e0912 CH |
241 | struct stat st; |
242 | ||
7c18973b NB |
243 | init_cvtnum(&fsblocksize, &fssectsize); |
244 | ||
8f0e0912 CH |
245 | while ((c = getopt(argc, argv, "aln:v")) != EOF) { |
246 | switch (c) { | |
247 | case 'a': | |
248 | fiemap_flags |= FIEMAP_FLAG_XATTR; | |
249 | break; | |
250 | case 'l': | |
251 | lflag = 1; | |
252 | break; | |
253 | case 'n': | |
254 | max_extents = atoi(optarg); | |
8f0e0912 CH |
255 | break; |
256 | case 'v': | |
257 | vflag++; | |
258 | break; | |
259 | default: | |
260 | return command_usage(&fiemap_cmd); | |
261 | } | |
262 | } | |
263 | ||
7c18973b NB |
264 | /* Range start (optional) */ |
265 | if (optind < argc) { | |
266 | start_offset = cvtnum(fsblocksize, fssectsize, argv[optind]); | |
267 | if (start_offset < 0) { | |
268 | printf("non-numeric offset argument -- %s\n", argv[optind]); | |
269 | return 0; | |
270 | } | |
271 | last_logical = start_offset; | |
272 | optind++; | |
273 | } | |
274 | ||
275 | /* Range length (optional if range start was specified) */ | |
276 | if (optind < argc) { | |
277 | length = cvtnum(fsblocksize, fssectsize, argv[optind]); | |
278 | if (length < 0) { | |
279 | printf("non-numeric len argument -- %s\n", argv[optind]); | |
280 | return 0; | |
281 | } | |
282 | range_end = start_offset + length; | |
283 | } | |
284 | ||
8f0e0912 | 285 | map_size = sizeof(struct fiemap) + |
cb404e20 | 286 | (EXTENT_BATCH * sizeof(struct fiemap_extent)); |
8f0e0912 CH |
287 | fiemap = malloc(map_size); |
288 | if (!fiemap) { | |
289 | fprintf(stderr, _("%s: malloc of %d bytes failed.\n"), | |
290 | progname, map_size); | |
291 | exitcode = 1; | |
292 | return 0; | |
293 | } | |
294 | ||
295 | printf("%s:\n", file->name); | |
296 | ||
7c18973b | 297 | while (!done) { |
8f0e0912 CH |
298 | memset(fiemap, 0, map_size); |
299 | fiemap->fm_flags = fiemap_flags; | |
300 | fiemap->fm_start = last_logical; | |
7c18973b | 301 | fiemap->fm_length = range_end - last_logical; |
cb404e20 | 302 | fiemap->fm_extent_count = EXTENT_BATCH; |
8f0e0912 CH |
303 | |
304 | ret = ioctl(file->fd, FS_IOC_FIEMAP, (unsigned long)fiemap); | |
305 | if (ret < 0) { | |
306 | fprintf(stderr, "%s: ioctl(FS_IOC_FIEMAP) [\"%s\"]: " | |
307 | "%s\n", progname, file->name, strerror(errno)); | |
308 | free(fiemap); | |
309 | exitcode = 1; | |
310 | return 0; | |
311 | } | |
312 | ||
313 | /* No more extents to map, exit */ | |
314 | if (!fiemap->fm_mapped_extents) | |
315 | break; | |
316 | ||
317 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
318 | struct fiemap_extent *extent; | |
997bfca5 | 319 | int num_printed = 0; |
8f0e0912 CH |
320 | |
321 | extent = &fiemap->fm_extents[i]; | |
b3a5164e ES |
322 | if (vflag) { |
323 | if (cur_extent == 0) { | |
3d12c088 NB |
324 | calc_print_format(fiemap, &foff_w, |
325 | &boff_w, &tot_w, | |
326 | &flg_w); | |
b3a5164e | 327 | } |
f8149110 | 328 | |
997bfca5 NB |
329 | num_printed = print_verbose(extent, foff_w, |
330 | boff_w, tot_w, | |
331 | flg_w, cur_extent, | |
332 | last_logical); | |
b3a5164e | 333 | } else |
997bfca5 NB |
334 | num_printed = print_plain(extent, lflag, |
335 | cur_extent, | |
336 | last_logical); | |
337 | ||
338 | cur_extent += num_printed; | |
339 | last_logical = extent->fe_logical + extent->fe_length; | |
ef45fa5e | 340 | |
7c18973b | 341 | /* Kernel has told us there are no more extents */ |
8f0e0912 | 342 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) { |
7c18973b NB |
343 | done = 1; |
344 | break; | |
345 | } | |
346 | ||
347 | /* We have exhausted the requested range */ | |
348 | if (last_logical >= range_end) { | |
349 | done = 1; | |
8f0e0912 CH |
350 | break; |
351 | } | |
352 | ||
7c18973b NB |
353 | /* We have printed requested nr of extents */ |
354 | if (cur_extent == max_extents) { | |
355 | done = 1; | |
8f0e0912 | 356 | break; |
7c18973b | 357 | } |
8f0e0912 CH |
358 | } |
359 | } | |
360 | ||
e7eaacef | 361 | if (cur_extent == max_extents) |
8f0e0912 CH |
362 | goto out; |
363 | ||
364 | memset(&st, 0, sizeof(st)); | |
365 | if (fstat(file->fd, &st)) { | |
366 | fprintf(stderr, "%s: fstat failed: %s\n", progname, | |
367 | strerror(errno)); | |
368 | free(fiemap); | |
369 | exitcode = 1; | |
370 | return 0; | |
371 | } | |
372 | ||
7c18973b NB |
373 | /* Print last hole to EOF or to end of requested range */ |
374 | range_end = min((uint64_t)range_end, st.st_size); | |
375 | ||
376 | if (cur_extent && last_logical < range_end) | |
abd0c766 | 377 | print_hole(foff_w, boff_w, tot_w, cur_extent, lflag, !vflag, |
7c18973b | 378 | BTOBBT(last_logical), BTOBBT(range_end)); |
8f0e0912 CH |
379 | |
380 | out: | |
381 | free(fiemap); | |
382 | return 0; | |
383 | } | |
384 | ||
385 | void | |
386 | fiemap_init(void) | |
387 | { | |
388 | fiemap_cmd.name = "fiemap"; | |
389 | fiemap_cmd.cfunc = fiemap_f; | |
390 | fiemap_cmd.argmin = 0; | |
391 | fiemap_cmd.argmax = -1; | |
392 | fiemap_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
7c18973b | 393 | fiemap_cmd.args = _("[-alv] [-n nx] [offset [len]]"); |
8f0e0912 CH |
394 | fiemap_cmd.oneline = _("print block mapping for a file"); |
395 | fiemap_cmd.help = fiemap_help; | |
396 | ||
397 | add_command(&fiemap_cmd); | |
398 | } |