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