]>
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 | ||
dcabd4e7 | 19 | #include "platform_defs.h" |
6b803e5a | 20 | #include "command.h" |
fb2bfcdf | 21 | #include "input.h" |
8f0e0912 | 22 | #include <linux/fiemap.h> |
8f0e0912 CH |
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 | ||
8f0e0912 CH |
52 | static void |
53 | print_verbose( | |
54 | struct fiemap_extent *extent, | |
55 | int blocksize, | |
56 | int foff_w, | |
57 | int boff_w, | |
58 | int tot_w, | |
59 | int flg_w, | |
60 | int max_extents, | |
61 | int *cur_extent, | |
62 | __u64 *last_logical) | |
63 | { | |
64 | __u64 lstart; | |
ef45fa5e | 65 | __u64 llast; |
8f0e0912 CH |
66 | __u64 len; |
67 | __u64 block; | |
68 | char lbuf[48]; | |
69 | char bbuf[48]; | |
70 | char flgbuf[16]; | |
71 | ||
ef45fa5e | 72 | llast = *last_logical / blocksize; |
8f0e0912 CH |
73 | lstart = extent->fe_logical / blocksize; |
74 | len = extent->fe_length / blocksize; | |
75 | block = extent->fe_physical / blocksize; | |
76 | ||
77 | memset(lbuf, 0, sizeof(lbuf)); | |
78 | memset(bbuf, 0, sizeof(bbuf)); | |
79 | ||
b3a5164e ES |
80 | if (*cur_extent == 0) { |
81 | printf("%4s: %-*s %-*s %*s %*s\n", _("EXT"), | |
82 | foff_w, _("FILE-OFFSET"), | |
83 | boff_w, _("BLOCK-RANGE"), | |
84 | tot_w, _("TOTAL"), | |
85 | flg_w, _("FLAGS")); | |
86 | } | |
87 | ||
ef45fa5e DC |
88 | if (lstart != llast) { |
89 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", llast, | |
8f0e0912 CH |
90 | lstart - 1ULL); |
91 | printf("%4d: %-*s %-*s %*llu\n", *cur_extent, foff_w, lbuf, | |
ef45fa5e | 92 | boff_w, _("hole"), tot_w, lstart - llast); |
8f0e0912 CH |
93 | (*cur_extent)++; |
94 | memset(lbuf, 0, sizeof(lbuf)); | |
95 | } | |
96 | ||
97 | if ((*cur_extent + 1) == max_extents) | |
98 | return; | |
99 | ||
100 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", lstart, | |
101 | lstart + len - 1ULL); | |
102 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, block + len - 1ULL); | |
103 | snprintf(flgbuf, sizeof(flgbuf), "0x%x", extent->fe_flags); | |
104 | printf("%4d: %-*s %-*s %*llu %*s\n", *cur_extent, foff_w, lbuf, | |
105 | boff_w, bbuf, tot_w, len, flg_w, flgbuf); | |
106 | ||
107 | (*cur_extent)++; | |
1b7b28f7 | 108 | *last_logical = extent->fe_logical + extent->fe_length; |
8f0e0912 CH |
109 | } |
110 | ||
111 | static void | |
112 | print_plain( | |
113 | struct fiemap_extent *extent, | |
114 | int lflag, | |
115 | int blocksize, | |
116 | int max_extents, | |
117 | int *cur_extent, | |
118 | __u64 *last_logical) | |
119 | { | |
120 | __u64 lstart; | |
ef45fa5e | 121 | __u64 llast; |
8f0e0912 CH |
122 | __u64 block; |
123 | __u64 len; | |
124 | ||
ef45fa5e | 125 | llast = *last_logical / blocksize; |
8f0e0912 CH |
126 | lstart = extent->fe_logical / blocksize; |
127 | len = extent->fe_length / blocksize; | |
128 | block = extent->fe_physical / blocksize; | |
129 | ||
ef45fa5e | 130 | if (lstart != llast) { |
8f0e0912 | 131 | printf("\t%d: [%llu..%llu]: hole", *cur_extent, |
ef45fa5e | 132 | llast, lstart - 1ULL); |
8f0e0912 | 133 | if (lflag) |
ef45fa5e | 134 | printf(_(" %llu blocks\n"), lstart - llast); |
8f0e0912 CH |
135 | else |
136 | printf("\n"); | |
137 | (*cur_extent)++; | |
138 | } | |
139 | ||
140 | if ((*cur_extent + 1) == max_extents) | |
141 | return; | |
142 | ||
143 | printf("\t%d: [%llu..%llu]: %llu..%llu", *cur_extent, | |
144 | lstart, lstart + len - 1ULL, block, | |
145 | block + len - 1ULL); | |
146 | ||
147 | if (lflag) | |
148 | printf(_(" %llu blocks\n"), len); | |
149 | else | |
150 | printf("\n"); | |
151 | (*cur_extent)++; | |
1b7b28f7 | 152 | *last_logical = extent->fe_logical + extent->fe_length; |
8f0e0912 CH |
153 | } |
154 | ||
b3a5164e ES |
155 | /* |
156 | * Calculate the proper extent table format based on first | |
157 | * set of extents | |
158 | */ | |
159 | static void | |
160 | calc_print_format( | |
161 | struct fiemap *fiemap, | |
162 | __u64 blocksize, | |
163 | int *foff_w, | |
164 | int *boff_w, | |
165 | int *tot_w, | |
166 | int *flg_w) | |
167 | { | |
168 | int i; | |
169 | char lbuf[32]; | |
170 | char bbuf[32]; | |
171 | __u64 logical; | |
172 | __u64 block; | |
173 | __u64 len; | |
174 | struct fiemap_extent *extent; | |
175 | ||
176 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
177 | ||
178 | extent = &fiemap->fm_extents[i]; | |
179 | logical = extent->fe_logical / blocksize; | |
180 | len = extent->fe_length / blocksize; | |
181 | block = extent->fe_physical / blocksize; | |
182 | ||
183 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]", logical, | |
184 | logical + len - 1); | |
185 | snprintf(bbuf, sizeof(bbuf), "%llu..%llu", block, | |
186 | block + len - 1); | |
187 | *foff_w = max(*foff_w, strlen(lbuf)); | |
188 | *boff_w = max(*boff_w, strlen(bbuf)); | |
189 | *tot_w = max(*tot_w, numlen(len, 10)); | |
190 | *flg_w = max(*flg_w, numlen(extent->fe_flags, 16)); | |
191 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) | |
192 | break; | |
193 | } | |
194 | } | |
195 | ||
8f0e0912 CH |
196 | int |
197 | fiemap_f( | |
198 | int argc, | |
199 | char **argv) | |
200 | { | |
201 | struct fiemap *fiemap; | |
202 | int max_extents = 0; | |
203 | int num_extents = 32; | |
204 | int last = 0; | |
8f0e0912 CH |
205 | int lflag = 0; |
206 | int vflag = 0; | |
207 | int fiemap_flags = FIEMAP_FLAG_SYNC; | |
208 | int c; | |
209 | int i; | |
210 | int map_size; | |
211 | int ret; | |
212 | int cur_extent = 0; | |
213 | int foff_w = 16; /* 16 just for a good minimum range */ | |
214 | int boff_w = 16; | |
215 | int tot_w = 5; /* 5 since its just one number */ | |
216 | int flg_w = 5; | |
217 | __u64 blocksize = 512; | |
218 | __u64 last_logical = 0; | |
219 | struct stat st; | |
220 | ||
221 | while ((c = getopt(argc, argv, "aln:v")) != EOF) { | |
222 | switch (c) { | |
223 | case 'a': | |
224 | fiemap_flags |= FIEMAP_FLAG_XATTR; | |
225 | break; | |
226 | case 'l': | |
227 | lflag = 1; | |
228 | break; | |
229 | case 'n': | |
230 | max_extents = atoi(optarg); | |
8f0e0912 CH |
231 | break; |
232 | case 'v': | |
233 | vflag++; | |
234 | break; | |
235 | default: | |
236 | return command_usage(&fiemap_cmd); | |
237 | } | |
238 | } | |
239 | ||
240 | if (max_extents) | |
241 | num_extents = min(num_extents, max_extents); | |
242 | map_size = sizeof(struct fiemap) + | |
243 | (num_extents * sizeof(struct fiemap_extent)); | |
244 | fiemap = malloc(map_size); | |
245 | if (!fiemap) { | |
246 | fprintf(stderr, _("%s: malloc of %d bytes failed.\n"), | |
247 | progname, map_size); | |
248 | exitcode = 1; | |
249 | return 0; | |
250 | } | |
251 | ||
252 | printf("%s:\n", file->name); | |
253 | ||
8f0e0912 CH |
254 | while (!last && ((cur_extent + 1) != max_extents)) { |
255 | if (max_extents) | |
256 | num_extents = min(num_extents, | |
257 | max_extents - (cur_extent + 1)); | |
258 | ||
259 | memset(fiemap, 0, map_size); | |
260 | fiemap->fm_flags = fiemap_flags; | |
261 | fiemap->fm_start = last_logical; | |
1b7b28f7 | 262 | fiemap->fm_length = -1LL; |
8f0e0912 CH |
263 | fiemap->fm_extent_count = num_extents; |
264 | ||
265 | ret = ioctl(file->fd, FS_IOC_FIEMAP, (unsigned long)fiemap); | |
266 | if (ret < 0) { | |
267 | fprintf(stderr, "%s: ioctl(FS_IOC_FIEMAP) [\"%s\"]: " | |
268 | "%s\n", progname, file->name, strerror(errno)); | |
269 | free(fiemap); | |
270 | exitcode = 1; | |
271 | return 0; | |
272 | } | |
273 | ||
274 | /* No more extents to map, exit */ | |
275 | if (!fiemap->fm_mapped_extents) | |
276 | break; | |
277 | ||
278 | for (i = 0; i < fiemap->fm_mapped_extents; i++) { | |
279 | struct fiemap_extent *extent; | |
280 | ||
281 | extent = &fiemap->fm_extents[i]; | |
b3a5164e ES |
282 | if (vflag) { |
283 | if (cur_extent == 0) { | |
284 | calc_print_format(fiemap, blocksize, | |
285 | &foff_w, &boff_w, | |
286 | &tot_w, &flg_w); | |
287 | } | |
f8149110 | 288 | |
8f0e0912 CH |
289 | print_verbose(extent, blocksize, foff_w, |
290 | boff_w, tot_w, flg_w, | |
291 | max_extents, &cur_extent, | |
292 | &last_logical); | |
b3a5164e | 293 | } else |
8f0e0912 CH |
294 | print_plain(extent, lflag, blocksize, |
295 | max_extents, &cur_extent, | |
296 | &last_logical); | |
ef45fa5e | 297 | |
8f0e0912 CH |
298 | if (extent->fe_flags & FIEMAP_EXTENT_LAST) { |
299 | last = 1; | |
300 | break; | |
301 | } | |
302 | ||
303 | if ((cur_extent + 1) == max_extents) | |
304 | break; | |
305 | } | |
306 | } | |
307 | ||
308 | if ((cur_extent + 1) == max_extents) | |
309 | goto out; | |
310 | ||
311 | memset(&st, 0, sizeof(st)); | |
312 | if (fstat(file->fd, &st)) { | |
313 | fprintf(stderr, "%s: fstat failed: %s\n", progname, | |
314 | strerror(errno)); | |
315 | free(fiemap); | |
316 | exitcode = 1; | |
317 | return 0; | |
318 | } | |
319 | ||
ef45fa5e | 320 | if (cur_extent && last_logical < st.st_size) { |
8f0e0912 CH |
321 | char lbuf[32]; |
322 | ||
323 | snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", | |
ef45fa5e | 324 | last_logical / blocksize, (st.st_size / blocksize) - 1); |
8f0e0912 CH |
325 | if (vflag) { |
326 | printf("%4d: %-*s %-*s %*llu\n", cur_extent, | |
327 | foff_w, lbuf, boff_w, _("hole"), tot_w, | |
ef45fa5e | 328 | (st.st_size - last_logical) / blocksize); |
8f0e0912 CH |
329 | } else { |
330 | printf("\t%d: %s %s", cur_extent, lbuf, | |
331 | _("hole")); | |
332 | if (lflag) | |
333 | printf(_(" %llu blocks\n"), | |
ef45fa5e | 334 | (st.st_size - last_logical) / blocksize); |
8f0e0912 CH |
335 | else |
336 | printf("\n"); | |
337 | } | |
338 | } | |
339 | ||
340 | out: | |
341 | free(fiemap); | |
342 | return 0; | |
343 | } | |
344 | ||
345 | void | |
346 | fiemap_init(void) | |
347 | { | |
348 | fiemap_cmd.name = "fiemap"; | |
349 | fiemap_cmd.cfunc = fiemap_f; | |
350 | fiemap_cmd.argmin = 0; | |
351 | fiemap_cmd.argmax = -1; | |
352 | fiemap_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; | |
353 | fiemap_cmd.args = _("[-alv] [-n nx]"); | |
354 | fiemap_cmd.oneline = _("print block mapping for a file"); | |
355 | fiemap_cmd.help = fiemap_help; | |
356 | ||
357 | add_command(&fiemap_cmd); | |
358 | } |