]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/fiemap.c
8482c9920ea3a006f410b319f52244ea7f3e2984
[thirdparty/xfsprogs-dev.git] / io / fiemap.c
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
19 #include "xfs.h"
20 #include "command.h"
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;
78 __u64 llast;
79 __u64 len;
80 __u64 block;
81 char lbuf[48];
82 char bbuf[48];
83 char flgbuf[16];
84
85 llast = *last_logical / blocksize;
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
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
101 if (lstart != llast) {
102 snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:", llast,
103 lstart - 1ULL);
104 printf("%4d: %-*s %-*s %*llu\n", *cur_extent, foff_w, lbuf,
105 boff_w, _("hole"), tot_w, lstart - llast);
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)++;
121 *last_logical = extent->fe_logical + extent->fe_length;
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;
134 __u64 llast;
135 __u64 block;
136 __u64 len;
137
138 llast = *last_logical / blocksize;
139 lstart = extent->fe_logical / blocksize;
140 len = extent->fe_length / blocksize;
141 block = extent->fe_physical / blocksize;
142
143 if (lstart != llast) {
144 printf("\t%d: [%llu..%llu]: hole", *cur_extent,
145 llast, lstart - 1ULL);
146 if (lflag)
147 printf(_(" %llu blocks\n"), lstart - llast);
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)++;
165 *last_logical = extent->fe_logical + extent->fe_length;
166 }
167
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
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;
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);
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
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;
275 fiemap->fm_length = -1LL;
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];
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
302 print_verbose(extent, blocksize, foff_w,
303 boff_w, tot_w, flg_w,
304 max_extents, &cur_extent,
305 &last_logical);
306 } else
307 print_plain(extent, lflag, blocksize,
308 max_extents, &cur_extent,
309 &last_logical);
310
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
333 if (cur_extent && last_logical < st.st_size) {
334 char lbuf[32];
335
336 snprintf(lbuf, sizeof(lbuf), "[%llu..%llu]:",
337 last_logical / blocksize, (st.st_size / blocksize) - 1);
338 if (vflag) {
339 printf("%4d: %-*s %-*s %*llu\n", cur_extent,
340 foff_w, lbuf, boff_w, _("hole"), tot_w,
341 (st.st_size - last_logical) / blocksize);
342 } else {
343 printf("\t%d: %s %s", cur_extent, lbuf,
344 _("hole"));
345 if (lflag)
346 printf(_(" %llu blocks\n"),
347 (st.st_size - last_logical) / blocksize);
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 }