]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/bmap.c
xfs_io: clarify -c in bmap documentation
[thirdparty/xfsprogs-dev.git] / io / bmap.c
1 /*
2 * Copyright (c) 2000-2005 Silicon Graphics, 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 "platform_defs.h"
20 #include "command.h"
21 #include "input.h"
22 #include "init.h"
23 #include "io.h"
24
25 static cmdinfo_t bmap_cmd;
26
27 static void
28 bmap_help(void)
29 {
30 printf(_(
31 "\n"
32 " prints the block mapping for an XFS file's data or attribute forks"
33 "\n"
34 " Example:\n"
35 " 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
36 "\n"
37 " bmap prints the map of disk blocks used by the current file.\n"
38 " The map lists each extent used by the file, as well as regions in the\n"
39 " file that do not have any corresponding blocks (holes).\n"
40 " By default, each line of the listing takes the following form:\n"
41 " extent: [startoffset..endoffset]: startblock..endblock\n"
42 " Holes are marked by replacing the startblock..endblock with 'hole'.\n"
43 " All the file offsets and disk blocks are in units of 512-byte blocks.\n"
44 " -a -- prints the attribute fork map instead of the data fork.\n"
45 " -c -- prints the copy-on-write fork map instead of the data fork.\n"
46 " This works only if the kernel was compiled in debug mode.\n"
47 " -d -- suppresses a DMAPI read event, offline portions shown as holes.\n"
48 " -e -- print delayed allocation extents.\n"
49 " -l -- also displays the length of each extent in 512-byte blocks.\n"
50 " -n -- query n extents.\n"
51 " -p -- obtain all unwritten extents as well (w/ -v show which are unwritten.)\n"
52 " -v -- Verbose information, specify ag info. Show flags legend on 2nd -v\n"
53 " Note: the bmap for non-regular files can be obtained provided the file\n"
54 " was opened appropriately (in particular, must be opened read-only).\n"
55 "\n"));
56 }
57
58 int
59 bmap_f(
60 int argc,
61 char **argv)
62 {
63 struct fsxattr fsx;
64 struct getbmapx *map;
65 struct xfs_fsop_geom fsgeo;
66 int map_size;
67 int loop = 0;
68 int flg = 0;
69 int aflag = 0;
70 int cflag = 0;
71 int lflag = 0;
72 int nflag = 0;
73 int pflag = 0;
74 int vflag = 0;
75 int is_rt = 0;
76 int bmv_iflags = 0; /* flags for XFS_IOC_GETBMAPX */
77 int i = 0;
78 int c;
79 int egcnt;
80
81 while ((c = getopt(argc, argv, "acdeln:pv")) != EOF) {
82 switch (c) {
83 case 'a': /* Attribute fork. */
84 bmv_iflags |= BMV_IF_ATTRFORK;
85 aflag = 1;
86 break;
87 case 'c': /* CoW fork. */
88 bmv_iflags |= BMV_IF_COWFORK | BMV_IF_DELALLOC;
89 cflag = 1;
90 break;
91 case 'e':
92 bmv_iflags |= BMV_IF_DELALLOC;
93 break;
94 case 'l': /* list number of blocks with each extent */
95 lflag = 1;
96 break;
97 case 'n': /* number of extents specified */
98 nflag = atoi(optarg);
99 break;
100 case 'd':
101 /* do not recall possibly offline DMAPI files */
102 bmv_iflags |= BMV_IF_NO_DMAPI_READ;
103 break;
104 case 'p':
105 /* report unwritten preallocated blocks */
106 pflag = 1;
107 bmv_iflags |= BMV_IF_PREALLOC;
108 break;
109 case 'v': /* Verbose output */
110 vflag++;
111 break;
112 default:
113 return command_usage(&bmap_cmd);
114 }
115 }
116 if (aflag || cflag)
117 bmv_iflags &= ~(BMV_IF_PREALLOC|BMV_IF_NO_DMAPI_READ);
118
119 if (vflag) {
120 c = xfsctl(file->name, file->fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo);
121 if (c < 0) {
122 fprintf(stderr,
123 _("%s: can't get geometry [\"%s\"]: %s\n"),
124 progname, file->name, strerror(errno));
125 exitcode = 1;
126 return 0;
127 }
128 c = xfsctl(file->name, file->fd, FS_IOC_FSGETXATTR, &fsx);
129 if (c < 0) {
130 fprintf(stderr,
131 _("%s: cannot read attrs on \"%s\": %s\n"),
132 progname, file->name, strerror(errno));
133 exitcode = 1;
134 return 0;
135 }
136
137 if (fsx.fsx_xflags == FS_XFLAG_REALTIME) {
138 /*
139 * ag info not applicable to rt, continue
140 * without ag output.
141 */
142 is_rt = 1;
143 }
144 }
145
146 map_size = nflag ? nflag+2 : 32; /* initial guess - 32 */
147 map = malloc(map_size*sizeof(*map));
148 if (map == NULL) {
149 fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
150 progname, (int)(map_size * sizeof(*map)));
151 exitcode = 1;
152 return 0;
153 }
154
155
156 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
157 * by nflag, or the initial guess number of extents (32).
158 *
159 * If there are more extents than we guessed, use xfsctl
160 * (FS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
161 * space based on this count, and try again.
162 *
163 * If the initial FGETBMAPX attempt returns EINVAL, this may mean
164 * that we tried the FGETBMAPX on a zero length file. If we get
165 * EINVAL, check the length with fstat() and return "no extents"
166 * if the length == 0.
167 *
168 * Why not do the xfsctl(FS_IOC_FSGETXATTR[A]) first? Two reasons:
169 * (1) The extent count may be wrong for a file with delayed
170 * allocation blocks. The XFS_IOC_GETBMAPX forces the real
171 * allocation and fixes up the extent count.
172 * (2) For XFS_IOC_GETBMAP[X] on a DMAPI file that has been moved
173 * offline by a DMAPI application (e.g., DMF) the
174 * FS_IOC_FSGETXATTR only reflects the extents actually online.
175 * Doing XFS_IOC_GETBMAPX call first forces that data blocks online
176 * and then everything proceeds normally (see PV #545725).
177 *
178 * If you don't want this behavior on a DMAPI offline file,
179 * try the "-d" option which sets the BMV_IF_NO_DMAPI_READ
180 * iflag for XFS_IOC_GETBMAPX.
181 */
182
183 do { /* loop a miximum of two times */
184
185 memset(map, 0, sizeof(*map)); /* zero header */
186
187 map->bmv_length = -1;
188 map->bmv_count = map_size;
189 map->bmv_iflags = bmv_iflags;
190
191 i = xfsctl(file->name, file->fd, XFS_IOC_GETBMAPX, map);
192 if (i < 0) {
193 if ( errno == EINVAL
194 && !aflag && filesize() == 0) {
195 break;
196 } else {
197 fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
198 " iflags=0x%x [\"%s\"]: %s\n"),
199 progname, map->bmv_iflags, file->name,
200 strerror(errno));
201 free(map);
202 exitcode = 1;
203 return 0;
204 }
205 }
206 if (nflag)
207 break;
208 if (map->bmv_entries < map->bmv_count-1)
209 break;
210 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
211 * syscall.
212 */
213 i = xfsctl(file->name, file->fd, aflag ?
214 XFS_IOC_FSGETXATTRA : FS_IOC_FSGETXATTR, &fsx);
215 if (i < 0) {
216 fprintf(stderr, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
217 "[\"%s\"]: %s\n", progname, aflag ? "A" : "",
218 file->name, strerror(errno));
219 free(map);
220 exitcode = 1;
221 return 0;
222 }
223 if (2 * fsx.fsx_nextents > map_size) {
224 map_size = 2 * fsx.fsx_nextents + 1;
225 map = realloc(map, map_size*sizeof(*map));
226 if (map == NULL) {
227 fprintf(stderr,
228 _("%s: cannot realloc %d bytes\n"),
229 progname, (int)(map_size*sizeof(*map)));
230 exitcode = 1;
231 return 0;
232 }
233 }
234 } while (++loop < 2);
235 if (!nflag) {
236 if (map->bmv_entries <= 0) {
237 printf(_("%s: no extents\n"), file->name);
238 free(map);
239 return 0;
240 }
241 }
242 egcnt = nflag ? min(nflag, map->bmv_entries) : map->bmv_entries;
243 printf("%s:\n", file->name);
244 if (!vflag) {
245 for (i = 0; i < egcnt; i++) {
246 printf("\t%d: [%lld..%lld]: ", i,
247 (long long) map[i + 1].bmv_offset,
248 (long long)(map[i + 1].bmv_offset +
249 map[i + 1].bmv_length - 1LL));
250 if (map[i + 1].bmv_block == -1)
251 printf(_("hole"));
252 else if (map[i + 1].bmv_block == -2)
253 printf(_("delalloc"));
254 else {
255 printf("%lld..%lld",
256 (long long) map[i + 1].bmv_block,
257 (long long)(map[i + 1].bmv_block +
258 map[i + 1].bmv_length - 1LL));
259
260 }
261 if (lflag)
262 printf(_(" %lld blocks\n"),
263 (long long)map[i+1].bmv_length);
264 else
265 printf("\n");
266 }
267 } else {
268 /*
269 * Verbose mode displays:
270 * extent: [startoffset..endoffset]: startblock..endblock \
271 * ag# (agoffset..agendoffset) totalbbs
272 */
273 #define MINRANGE_WIDTH 16
274 #define MINAG_WIDTH 2
275 #define MINTOT_WIDTH 5
276 #define NFLG 6 /* count of flags */
277 #define FLG_NULL 0000000 /* Null flag */
278 #define FLG_SHARED 0100000 /* shared extent */
279 #define FLG_PRE 0010000 /* Unwritten extent */
280 #define FLG_BSU 0001000 /* Not on begin of stripe unit */
281 #define FLG_ESU 0000100 /* Not on end of stripe unit */
282 #define FLG_BSW 0000010 /* Not on begin of stripe width */
283 #define FLG_ESW 0000001 /* Not on end of stripe width */
284 int agno;
285 off64_t agoff, bbperag;
286 int foff_w, boff_w, aoff_w, tot_w, agno_w;
287 char rbuf[32], bbuf[32], abuf[32];
288 int sunit, swidth;
289
290 foff_w = boff_w = aoff_w = MINRANGE_WIDTH;
291 tot_w = MINTOT_WIDTH;
292 if (is_rt)
293 sunit = swidth = bbperag = 0;
294 else {
295 bbperag = (off64_t)fsgeo.agblocks *
296 (off64_t)fsgeo.blocksize / BBSIZE;
297 sunit = (fsgeo.sunit * fsgeo.blocksize) / BBSIZE;
298 swidth = (fsgeo.swidth * fsgeo.blocksize) / BBSIZE;
299 }
300 flg = sunit | pflag;
301
302 /*
303 * Go through the extents and figure out the width
304 * needed for all columns.
305 */
306 for (i = 0; i < egcnt; i++) {
307 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
308 (long long) map[i + 1].bmv_offset,
309 (long long)(map[i + 1].bmv_offset +
310 map[i + 1].bmv_length - 1LL));
311 if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC)
312 flg = 1;
313 if (map[i + 1].bmv_block == -1) {
314 foff_w = max(foff_w, strlen(rbuf));
315 tot_w = max(tot_w,
316 numlen(map[i+1].bmv_length, 10));
317 } else {
318 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
319 (long long) map[i + 1].bmv_block,
320 (long long)(map[i + 1].bmv_block +
321 map[i + 1].bmv_length - 1LL));
322 boff_w = max(boff_w, strlen(bbuf));
323 if (!is_rt) {
324 agno = map[i + 1].bmv_block / bbperag;
325 agoff = map[i + 1].bmv_block -
326 (agno * bbperag);
327 snprintf(abuf, sizeof(abuf),
328 "(%lld..%lld)",
329 (long long)agoff,
330 (long long)(agoff +
331 map[i + 1].bmv_length - 1LL));
332 aoff_w = max(aoff_w, strlen(abuf));
333 } else
334 aoff_w = 0;
335 foff_w = max(foff_w, strlen(rbuf));
336 tot_w = max(tot_w,
337 numlen(map[i+1].bmv_length, 10));
338 }
339 }
340 agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount, 10));
341 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
342 _("EXT"),
343 foff_w, _("FILE-OFFSET"),
344 boff_w, is_rt ? _("RT-BLOCK-RANGE") : _("BLOCK-RANGE"),
345 agno_w, is_rt ? "" : _("AG"),
346 aoff_w, is_rt ? "" : _("AG-OFFSET"),
347 tot_w, _("TOTAL"),
348 flg ? _(" FLAGS") : "");
349 for (i = 0; i < egcnt; i++) {
350 flg = FLG_NULL;
351 if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC) {
352 flg |= FLG_PRE;
353 }
354 if (map[i + 1].bmv_oflags & BMV_OF_SHARED)
355 flg |= FLG_SHARED;
356 if (map[i + 1].bmv_oflags & BMV_OF_DELALLOC)
357 map[i + 1].bmv_block = -2;
358 /*
359 * If striping enabled, determine if extent starts/ends
360 * on a stripe unit boundary.
361 */
362 if (sunit) {
363 if (map[i + 1].bmv_block % sunit != 0) {
364 flg |= FLG_BSU;
365 }
366 if (((map[i + 1].bmv_block +
367 map[i + 1].bmv_length ) % sunit ) != 0) {
368 flg |= FLG_ESU;
369 }
370 if (map[i + 1].bmv_block % swidth != 0) {
371 flg |= FLG_BSW;
372 }
373 if (((map[i + 1].bmv_block +
374 map[i + 1].bmv_length ) % swidth ) != 0) {
375 flg |= FLG_ESW;
376 }
377 }
378 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
379 (long long) map[i + 1].bmv_offset,
380 (long long)(map[i + 1].bmv_offset +
381 map[i + 1].bmv_length - 1LL));
382 if (map[i + 1].bmv_block == -1) {
383 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
384 i,
385 foff_w, rbuf,
386 boff_w, _("hole"),
387 agno_w, "",
388 aoff_w, "",
389 tot_w, (long long)map[i+1].bmv_length);
390 } else if (map[i + 1].bmv_block == -2) {
391 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
392 i,
393 foff_w, rbuf,
394 boff_w, _("delalloc"),
395 agno_w, "",
396 aoff_w, "",
397 tot_w, (long long)map[i+1].bmv_length);
398 } else {
399 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
400 (long long) map[i + 1].bmv_block,
401 (long long)(map[i + 1].bmv_block +
402 map[i + 1].bmv_length - 1LL));
403 printf("%4d: %-*s %-*s", i, foff_w, rbuf,
404 boff_w, bbuf);
405 if (!is_rt) {
406 agno = map[i + 1].bmv_block / bbperag;
407 agoff = map[i + 1].bmv_block -
408 (agno * bbperag);
409 snprintf(abuf, sizeof(abuf),
410 "(%lld..%lld)",
411 (long long)agoff,
412 (long long)(agoff +
413 map[i + 1].bmv_length - 1LL));
414 printf(" %*d %-*s", agno_w, agno,
415 aoff_w, abuf);
416 } else
417 printf(" ");
418 printf(" %*lld", tot_w,
419 (long long)map[i+1].bmv_length);
420 if (flg == FLG_NULL && !pflag) {
421 printf("\n");
422 } else {
423 printf(" %-*.*o\n", NFLG, NFLG, flg);
424 }
425 }
426 }
427 if ((flg || pflag) && vflag > 1) {
428 printf(_(" FLAG Values:\n"));
429 printf(_(" %*.*o Shared extent\n"),
430 NFLG+1, NFLG+1, FLG_SHARED);
431 printf(_(" %*.*o Unwritten preallocated extent\n"),
432 NFLG+1, NFLG+1, FLG_PRE);
433 printf(_(" %*.*o Doesn't begin on stripe unit\n"),
434 NFLG+1, NFLG+1, FLG_BSU);
435 printf(_(" %*.*o Doesn't end on stripe unit\n"),
436 NFLG+1, NFLG+1, FLG_ESU);
437 printf(_(" %*.*o Doesn't begin on stripe width\n"),
438 NFLG+1, NFLG+1, FLG_BSW);
439 printf(_(" %*.*o Doesn't end on stripe width\n"),
440 NFLG+1, NFLG+1, FLG_ESW);
441 }
442 }
443 free(map);
444 return 0;
445 }
446
447 void
448 bmap_init(void)
449 {
450 bmap_cmd.name = "bmap";
451 bmap_cmd.cfunc = bmap_f;
452 bmap_cmd.argmin = 0;
453 bmap_cmd.argmax = -1;
454 bmap_cmd.flags = CMD_NOMAP_OK;
455 bmap_cmd.args = _("[-adlpv] [-n nx]");
456 bmap_cmd.oneline = _("print block mapping for an XFS file");
457 bmap_cmd.help = bmap_help;
458
459 add_command(&bmap_cmd);
460 }