]>
Commit | Line | Data |
---|---|---|
8f0e0912 CH |
1 | /* |
2 | * Copyright (c) 2010 Red Hat, Inc. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it would be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
17 | */ | |
18 | ||
6b803e5a CH |
19 | #include "xfs.h" |
20 | #include "command.h" | |
8f0e0912 CH |
21 | #include <linux/fiemap.h> |
22 | #include <linux/fs.h> | |
23 | #include "init.h" | |
24 | #include "io.h" | |
25 | ||
26 | static cmdinfo_t fiemap_cmd; | |
27 | ||
28 | static void | |
29 | fiemap_help(void) | |
30 | { | |
31 | printf(_( | |
32 | "\n" | |
33 | " prints the block mapping for a file's data or attribute forks" | |
34 | "\n" | |
35 | " Example:\n" | |
36 | " 'fiemap -v' - tabular format verbose map\n" | |
37 | "\n" | |
38 | " fiemap prints the map of disk blocks used by the current file.\n" | |
39 | " The map lists each extent used by the file, as well as regions in the\n" | |
40 | " file that do not have any corresponding blocks (holes).\n" | |
41 | " By default, each line of the listing takes the following form:\n" | |
42 | " extent: [startoffset..endoffset]: startblock..endblock\n" | |
43 | " Holes are marked by replacing the startblock..endblock with 'hole'.\n" | |
44 | " All the file offsets and disk blocks are in units of 512-byte blocks.\n" | |
45 | " -a -- prints the attribute fork map instead of the data fork.\n" | |
46 | " -l -- also displays the length of each extent in 512-byte blocks.\n" | |
47 | " -n -- query n extents.\n" | |
48 | " -v -- Verbose information\n" | |
49 | "\n")); | |
50 | } | |
51 | ||
52 | static int | |
53 | numlen( | |
54 | __u64 val, | |
55 | int base) | |
56 | { | |
57 | __u64 tmp; | |
58 | int len; | |
59 | ||
60 | for (len = 0, tmp = val; tmp > 0; tmp = tmp/base) | |
61 | len++; | |
62 | return (len == 0 ? 1 : len); | |
63 | } | |
64 | ||
65 | static void | |
66 | print_verbose( | |
67 | struct fiemap_extent *extent, | |
68 | int blocksize, | |
69 | int foff_w, | |
70 | int boff_w, | |
71 | int tot_w, | |
72 | int flg_w, | |
73 | int max_extents, | |
74 | int *cur_extent, | |
75 | __u64 *last_logical) | |
76 | { | |
77 | __u64 lstart; | |
ef45fa5e | 78 | __u64 llast; |
8f0e0912 CH |
79 | __u64 len; |
80 | __u64 block; | |
81 | char lbuf[48]; | |
82 | char bbuf[48]; | |
83 | char flgbuf[16]; | |
84 | ||
ef45fa5e | 85 | llast = *last_logical / blocksize; |
8f0e0912 CH |
86 | lstart = extent->fe_logical / blocksize; |
87 | len = extent->fe_length / blocksize; | |
88 | block = extent->fe_physical / blocksize; | |
89 | ||
90 | memset(lbuf, 0, sizeof(lbuf)); | |
91 | memset(bbuf, 0, sizeof(bbuf)); | |
92 | ||
b3a5164e ES |
93 | if (*cur_extent == 0) { |
94 | printf("%4s: %-*s %-*s %*s %*s\n", _("EXT"), | |
95 | foff_w, _("FILE-OFFSET"), | |
96 | boff_w, _("BLOCK-RANGE"), | |
97 | tot_w, _("TOTAL"), | |
98 | flg_w, _("FLAGS")); | |
99 | } | |
100 | ||
ef45fa5e DC |
101 | if (lstart != llast) { |
102 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", llast, | |
8f0e0912 CH |
103 | lstart - 1ULL); |
104 | printf("%4d: %-*s %-*s %*llu\n", *cur_extent, foff_w, lbuf, | |
ef45fa5e | 105 | boff_w, _("hole"), tot_w, lstart - llast); |
8f0e0912 CH |
106 | (*cur_extent)++; |
107 | memset(lbuf, 0, sizeof(lbuf)); | |
108 | } | |
109 | ||
110 | if ((*cur_extent + 1) == max_extents) | |
111 | return; | |
112 | ||
113 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", lstart, | |
114 | lstart + len - 1ULL); | |
115 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, block + len - 1ULL); | |
116 | snprintf(flgbuf, sizeof(flgbuf), "0x%x", extent->fe_flags); | |
117 | printf("%4d: %-*s %-*s %*llu %*s\n", *cur_extent, foff_w, lbuf, | |
118 | boff_w, bbuf, tot_w, len, flg_w, flgbuf); | |
119 | ||
120 | (*cur_extent)++; | |
1b7b28f7 | 121 | *last_logical = extent->fe_logical + extent->fe_length; |
8f0e0912 CH |
122 | } |
123 | ||
124 | static void | |
125 | print_plain( | |
126 | struct fiemap_extent *extent, | |
127 | int lflag, | |
128 | int blocksize, | |
129 | int max_extents, | |
130 | int *cur_extent, | |
131 | __u64 *last_logical) | |
132 | { | |
133 | __u64 lstart; | |
ef45fa5e | 134 | __u64 llast; |
8f0e0912 CH |
135 | __u64 block; |
136 | __u64 len; | |
137 | ||
ef45fa5e | 138 | llast = *last_logical / blocksize; |
8f0e0912 CH |
139 | lstart = extent->fe_logical / blocksize; |
140 | len = extent->fe_length / blocksize; | |
141 | block = extent->fe_physical / blocksize; | |
142 | ||
ef45fa5e | 143 | if (lstart != llast) { |
8f0e0912 | 144 | printf("\t%d: [%llu..%llu]: hole", *cur_extent, |
ef45fa5e | 145 | llast, lstart - 1ULL); |
8f0e0912 | 146 | if (lflag) |
ef45fa5e | 147 | printf(_(" %llu blocks\n"), lstart - llast); |
8f0e0912 CH |
148 | else |
149 | printf("\n"); | |
150 | (*cur_extent)++; | |
151 | } | |
152 | ||
153 | if ((*cur_extent + 1) == max_extents) | |
154 | return; | |
155 | ||
156 | printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent, | |
157 | lstart, lstart + len - 1ULL, block, | |
158 | block + len - 1ULL); | |
159 | ||
160 | if (lflag) | |
161 | printf(_(" %llu blocks\n"), len); | |
162 | else | |
163 | printf("\n"); | |
164 | (*cur_extent)++; | |
1b7b28f7 | 165 | *last_logical = extent->fe_logical + extent->fe_length; |
8f0e0912 CH |
166 | } |
167 | ||
b3a5164e ES |
168 | /* |
169 | * Calculate the proper extent table format based on first | |
170 | * set of extents | |
171 | */ | |
172 | static void | |
173 | calc_print_format( | |
174 | struct fiemap *fiemap, | |
175 | __u64 blocksize, | |
176 | int *foff_w, | |
177 | int *boff_w, | |
178 | int *tot_w, | |
179 | int *flg_w) | |
180 | { | |
181 | int i; | |
182 | char lbuf[32]; | |
183 | char bbuf[32]; | |
184 | __u64 logical; | |
185 | __u64 block; | |
186 | __u64 len; | |
187 | struct fiemap_extent *extent; | |
188 | ||
189 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
190 | ||
191 | extent = &fiemap->fm_extents[i]; | |
192 | logical = extent->fe_logical / blocksize; | |
193 | len = extent->fe_length / blocksize; | |
194 | block = extent->fe_physical / blocksize; | |
195 | ||
196 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]", logical, | |
197 | logical + len - 1); | |
198 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, | |
199 | block + len - 1); | |
200 | *foff_w = max(*foff_w, strlen(lbuf)); | |
201 | *boff_w = max(*boff_w, strlen(bbuf)); | |
202 | *tot_w = max(*tot_w, numlen(len, 10)); | |
203 | *flg_w = max(*flg_w, numlen(extent->fe_flags, 16)); | |
204 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) | |
205 | break; | |
206 | } | |
207 | } | |
208 | ||
8f0e0912 CH |
209 | int |
210 | fiemap_f( | |
211 | int argc, | |
212 | char **argv) | |
213 | { | |
214 | struct fiemap *fiemap; | |
215 | int max_extents = 0; | |
216 | int num_extents = 32; | |
217 | int last = 0; | |
8f0e0912 CH |
218 | int lflag = 0; |
219 | int vflag = 0; | |
220 | int fiemap_flags = FIEMAP_FLAG_SYNC; | |
221 | int c; | |
222 | int i; | |
223 | int map_size; | |
224 | int ret; | |
225 | int cur_extent = 0; | |
226 | int foff_w = 16; /* 16 just for a good minimum range */ | |
227 | int boff_w = 16; | |
228 | int tot_w = 5; /* 5 since its just one number */ | |
229 | int flg_w = 5; | |
230 | __u64 blocksize = 512; | |
231 | __u64 last_logical = 0; | |
232 | struct stat st; | |
233 | ||
234 | while ((c = getopt(argc, argv, "aln:v")) != EOF) { | |
235 | switch (c) { | |
236 | case 'a': | |
237 | fiemap_flags |= FIEMAP_FLAG_XATTR; | |
238 | break; | |
239 | case 'l': | |
240 | lflag = 1; | |
241 | break; | |
242 | case 'n': | |
243 | max_extents = atoi(optarg); | |
8f0e0912 CH |
244 | break; |
245 | case 'v': | |
246 | vflag++; | |
247 | break; | |
248 | default: | |
249 | return command_usage(&fiemap_cmd); | |
250 | } | |
251 | } | |
252 | ||
253 | if (max_extents) | |
254 | num_extents = min(num_extents, max_extents); | |
255 | map_size = sizeof(struct fiemap) + | |
256 | (num_extents * sizeof(struct fiemap_extent)); | |
257 | fiemap = malloc(map_size); | |
258 | if (!fiemap) { | |
259 | fprintf(stderr, _("%s: malloc of %d bytes failed.\n"), | |
260 | progname, map_size); | |
261 | exitcode = 1; | |
262 | return 0; | |
263 | } | |
264 | ||
265 | printf("%s:\n", file->name); | |
266 | ||
8f0e0912 CH |
267 | while (!last && ((cur_extent + 1) != max_extents)) { |
268 | if (max_extents) | |
269 | num_extents = min(num_extents, | |
270 | max_extents - (cur_extent + 1)); | |
271 | ||
272 | memset(fiemap, 0, map_size); | |
273 | fiemap->fm_flags = fiemap_flags; | |
274 | fiemap->fm_start = last_logical; | |
1b7b28f7 | 275 | fiemap->fm_length = -1LL; |
8f0e0912 CH |
276 | fiemap->fm_extent_count = num_extents; |
277 | ||
278 | ret = ioctl(file->fd, FS_IOC_FIEMAP, (unsigned long)fiemap); | |
279 | if (ret < 0) { | |
280 | fprintf(stderr, "%s: ioctl(FS_IOC_FIEMAP) [\"%s\"]: " | |
281 | "%s\n", progname, file->name, strerror(errno)); | |
282 | free(fiemap); | |
283 | exitcode = 1; | |
284 | return 0; | |
285 | } | |
286 | ||
287 | /* No more extents to map, exit */ | |
288 | if (!fiemap->fm_mapped_extents) | |
289 | break; | |
290 | ||
291 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
292 | struct fiemap_extent *extent; | |
293 | ||
294 | extent = &fiemap->fm_extents[i]; | |
b3a5164e ES |
295 | if (vflag) { |
296 | if (cur_extent == 0) { | |
297 | calc_print_format(fiemap, blocksize, | |
298 | &foff_w, &boff_w, | |
299 | &tot_w, &flg_w); | |
300 | } | |
301 | ||
8f0e0912 CH |
302 | print_verbose(extent, blocksize, foff_w, |
303 | boff_w, tot_w, flg_w, | |
304 | max_extents, &cur_extent, | |
305 | &last_logical); | |
b3a5164e | 306 | } else |
8f0e0912 CH |
307 | print_plain(extent, lflag, blocksize, |
308 | max_extents, &cur_extent, | |
309 | &last_logical); | |
ef45fa5e | 310 | |
8f0e0912 CH |
311 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) { |
312 | last = 1; | |
313 | break; | |
314 | } | |
315 | ||
316 | if ((cur_extent + 1) == max_extents) | |
317 | break; | |
318 | } | |
319 | } | |
320 | ||
321 | if ((cur_extent + 1) == max_extents) | |
322 | goto out; | |
323 | ||
324 | memset(&st, 0, sizeof(st)); | |
325 | if (fstat(file->fd, &st)) { | |
326 | fprintf(stderr, "%s: fstat failed: %s\n", progname, | |
327 | strerror(errno)); | |
328 | free(fiemap); | |
329 | exitcode = 1; | |
330 | return 0; | |
331 | } | |
332 | ||
ef45fa5e | 333 | if (cur_extent && last_logical < st.st_size) { |
8f0e0912 CH |
334 | char lbuf[32]; |
335 | ||
336 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", | |
ef45fa5e | 337 | last_logical / blocksize, (st.st_size / blocksize) - 1); |
8f0e0912 CH |
338 | if (vflag) { |
339 | printf("%4d: %-*s %-*s %*llu\n", cur_extent, | |
340 | foff_w, lbuf, boff_w, _("hole"), tot_w, | |
ef45fa5e | 341 | (st.st_size - last_logical) / blocksize); |
8f0e0912 CH |
342 | } else { |
343 | printf("\t%d: %s %s", cur_extent, lbuf, | |
344 | _("hole")); | |
345 | if (lflag) | |
346 | printf(_(" %llu blocks\n"), | |
ef45fa5e | 347 | (st.st_size - last_logical) / blocksize); |
8f0e0912 CH |
348 | else |
349 | printf("\n"); | |
350 | } | |
351 | } | |
352 | ||
353 | out: | |
354 | free(fiemap); | |
355 | return 0; | |
356 | } | |
357 | ||
358 | void | |
359 | fiemap_init(void) | |
360 | { | |
361 | fiemap_cmd.name = "fiemap"; | |
362 | fiemap_cmd.cfunc = fiemap_f; | |
363 | fiemap_cmd.argmin = 0; | |
364 | fiemap_cmd.argmax = -1; | |
365 | fiemap_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
366 | fiemap_cmd.args = _("[-alv] [-n nx]"); | |
367 | fiemap_cmd.oneline = _("print block mapping for a file"); | |
368 | fiemap_cmd.help = fiemap_help; | |
369 | ||
370 | add_command(&fiemap_cmd); | |
371 | } |