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