]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - io/bmap.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / io / bmap.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
2bd0ea18 2/*
da23017d
NS
3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
2bd0ea18
NS
5 */
6
dcabd4e7 7#include "platform_defs.h"
6b803e5a 8#include "command.h"
fb2bfcdf 9#include "input.h"
e246ba5f 10#include "init.h"
48c46ee3 11#include "io.h"
fee68490 12#include "libfrog/fsgeom.h"
2bd0ea18 13
e246ba5f 14static cmdinfo_t bmap_cmd;
2bd0ea18 15
e246ba5f
NS
16static void
17bmap_help(void)
2bd0ea18 18{
e246ba5f
NS
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"
7536ce44 34" -c -- prints the copy-on-write fork map instead of the data fork.\n"
c9a048f2 35" This works only if the kernel was compiled in debug mode.\n"
7536ce44 36" -e -- print delayed allocation extents.\n"
e246ba5f 37" -l -- also displays the length of each extent in 512-byte blocks.\n"
6f0d352c
ES
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"
e246ba5f
NS
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}
2bd0ea18 45
00ff2b10 46static int
e246ba5f
NS
47bmap_f(
48 int argc,
49 char **argv)
50{
51 struct fsxattr fsx;
52 struct getbmapx *map;
c0211f67 53 struct xfs_fsop_geom fsgeo;
e246ba5f
NS
54 int map_size;
55 int loop = 0;
56 int flg = 0;
57 int aflag = 0;
7536ce44 58 int cflag = 0;
e246ba5f
NS
59 int lflag = 0;
60 int nflag = 0;
66b57340 61 int pflag = 0;
e246ba5f 62 int vflag = 0;
e2eb7f3a 63 int is_rt = 0;
e246ba5f
NS
64 int bmv_iflags = 0; /* flags for XFS_IOC_GETBMAPX */
65 int i = 0;
66 int c;
619df821 67 int egcnt;
e246ba5f 68
4cfe2c37 69 while ((c = getopt(argc, argv, "aceln:pv")) != EOF) {
e246ba5f
NS
70 switch (c) {
71 case 'a': /* Attribute fork. */
2bd0ea18
NS
72 bmv_iflags |= BMV_IF_ATTRFORK;
73 aflag = 1;
74 break;
7536ce44
DW
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;
e246ba5f 82 case 'l': /* list number of blocks with each extent */
2bd0ea18
NS
83 lflag = 1;
84 break;
e246ba5f 85 case 'n': /* number of extents specified */
2bd0ea18
NS
86 nflag = atoi(optarg);
87 break;
2bd0ea18
NS
88 case 'p':
89 /* report unwritten preallocated blocks */
66b57340 90 pflag = 1;
2bd0ea18
NS
91 bmv_iflags |= BMV_IF_PREALLOC;
92 break;
e246ba5f 93 case 'v': /* Verbose output */
2bd0ea18
NS
94 vflag++;
95 break;
2bd0ea18 96 default:
48c46ee3 97 return command_usage(&bmap_cmd);
2bd0ea18
NS
98 }
99 }
7536ce44 100 if (aflag || cflag)
4cfe2c37 101 bmv_iflags &= ~BMV_IF_PREALLOC;
2bd0ea18
NS
102
103 if (vflag) {
03d96c64 104 c = -xfrog_geometry(file->fd, &fsgeo);
9612817d 105 if (c) {
9440d84d
NS
106 fprintf(stderr,
107 _("%s: can't get geometry [\"%s\"]: %s\n"),
9612817d 108 progname, file->name, strerror(c));
e246ba5f
NS
109 exitcode = 1;
110 return 0;
2bd0ea18 111 }
83f4b5ac 112 c = xfsctl(file->name, file->fd, FS_IOC_FSGETXATTR, &fsx);
48c46ee3 113 if (c < 0) {
9440d84d
NS
114 fprintf(stderr,
115 _("%s: cannot read attrs on \"%s\": %s\n"),
48c46ee3 116 progname, file->name, strerror(errno));
e246ba5f
NS
117 exitcode = 1;
118 return 0;
2bd0ea18
NS
119 }
120
b1faed5f 121 if (fsx.fsx_xflags & FS_XFLAG_REALTIME) {
dfc130f3 122 /*
2bd0ea18
NS
123 * ag info not applicable to rt, continue
124 * without ag output.
125 */
e2eb7f3a 126 is_rt = 1;
2bd0ea18
NS
127 }
128 }
129
0b52c691 130 map_size = nflag ? nflag+2 : 32; /* initial guess - 32 */
2bd0ea18
NS
131 map = malloc(map_size*sizeof(*map));
132 if (map == NULL) {
9440d84d 133 fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
c03d02f8 134 progname, (int)(map_size * sizeof(*map)));
e246ba5f
NS
135 exitcode = 1;
136 return 0;
2bd0ea18 137 }
dfc130f3 138
2bd0ea18 139
93d9f139 140/* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
0b52c691 141 * by nflag, or the initial guess number of extents (32).
2bd0ea18 142 *
93d9f139 143 * If there are more extents than we guessed, use xfsctl
83f4b5ac 144 * (FS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
2bd0ea18
NS
145 * space based on this count, and try again.
146 *
147 * If the initial FGETBMAPX attempt returns EINVAL, this may mean
148 * that we tried the FGETBMAPX on a zero length file. If we get
149 * EINVAL, check the length with fstat() and return "no extents"
150 * if the length == 0.
151 *
4cfe2c37
AI
152 * Why not do the xfsctl(FS_IOC_FSGETXATTR[A]) first?
153 * The extent count may be wrong for a file with delayed
154 * allocation blocks. The XFS_IOC_GETBMAPX forces the real
155 * allocation and fixes up the extent count.
2bd0ea18
NS
156 */
157
158 do { /* loop a miximum of two times */
159
dab9b8d6 160 memset(map, 0, sizeof(*map)); /* zero header */
2bd0ea18
NS
161
162 map->bmv_length = -1;
163 map->bmv_count = map_size;
164 map->bmv_iflags = bmv_iflags;
165
48c46ee3 166 i = xfsctl(file->name, file->fd, XFS_IOC_GETBMAPX, map);
2bd0ea18
NS
167 if (i < 0) {
168 if ( errno == EINVAL
e246ba5f 169 && !aflag && filesize() == 0) {
2bd0ea18
NS
170 break;
171 } else {
93d9f139
NS
172 fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
173 " iflags=0x%x [\"%s\"]: %s\n"),
48c46ee3 174 progname, map->bmv_iflags, file->name,
c03d02f8 175 strerror(errno));
2bd0ea18 176 free(map);
e246ba5f
NS
177 exitcode = 1;
178 return 0;
2bd0ea18
NS
179 }
180 }
181 if (nflag)
182 break;
183 if (map->bmv_entries < map->bmv_count-1)
184 break;
83f4b5ac 185 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
2bd0ea18
NS
186 * syscall.
187 */
48c46ee3 188 i = xfsctl(file->name, file->fd, aflag ?
83f4b5ac 189 XFS_IOC_FSGETXATTRA : FS_IOC_FSGETXATTR, &fsx);
2bd0ea18 190 if (i < 0) {
83f4b5ac 191 fprintf(stderr, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
93d9f139 192 "[\"%s\"]: %s\n", progname, aflag ? "A" : "",
48c46ee3 193 file->name, strerror(errno));
2bd0ea18 194 free(map);
e246ba5f
NS
195 exitcode = 1;
196 return 0;
2bd0ea18 197 }
f6d1a563
TM
198 if (2 * fsx.fsx_nextents > map_size) {
199 map_size = 2 * fsx.fsx_nextents + 1;
2bd0ea18
NS
200 map = realloc(map, map_size*sizeof(*map));
201 if (map == NULL) {
9440d84d
NS
202 fprintf(stderr,
203 _("%s: cannot realloc %d bytes\n"),
c03d02f8 204 progname, (int)(map_size*sizeof(*map)));
e246ba5f
NS
205 exitcode = 1;
206 return 0;
2bd0ea18
NS
207 }
208 }
209 } while (++loop < 2);
210 if (!nflag) {
211 if (map->bmv_entries <= 0) {
48c46ee3 212 printf(_("%s: no extents\n"), file->name);
2bd0ea18
NS
213 free(map);
214 return 0;
215 }
216 }
619df821 217 egcnt = nflag ? min(nflag, map->bmv_entries) : map->bmv_entries;
48c46ee3 218 printf("%s:\n", file->name);
2bd0ea18 219 if (!vflag) {
619df821 220 for (i = 0; i < egcnt; i++) {
2bd0ea18 221 printf("\t%d: [%lld..%lld]: ", i,
5b64e00a 222 (long long) map[i + 1].bmv_offset,
dfc130f3 223 (long long)(map[i + 1].bmv_offset +
5b64e00a 224 map[i + 1].bmv_length - 1LL));
2bd0ea18 225 if (map[i + 1].bmv_block == -1)
9440d84d 226 printf(_("hole"));
1c47bd67
DW
227 else if (map[i + 1].bmv_block == -2)
228 printf(_("delalloc"));
2bd0ea18 229 else {
5b64e00a
NS
230 printf("%lld..%lld",
231 (long long) map[i + 1].bmv_block,
232 (long long)(map[i + 1].bmv_block +
233 map[i + 1].bmv_length - 1LL));
2bd0ea18
NS
234
235 }
236 if (lflag)
9440d84d
NS
237 printf(_(" %lld blocks\n"),
238 (long long)map[i+1].bmv_length);
2bd0ea18
NS
239 else
240 printf("\n");
241 }
242 } else {
243 /*
dfc130f3 244 * Verbose mode displays:
2bd0ea18 245 * extent: [startoffset..endoffset]: startblock..endblock \
dfc130f3 246 * ag# (agoffset..agendoffset) totalbbs
2bd0ea18
NS
247 */
248#define MINRANGE_WIDTH 16
249#define MINAG_WIDTH 2
250#define MINTOT_WIDTH 5
7536ce44
DW
251#define NFLG 6 /* count of flags */
252#define FLG_NULL 0000000 /* Null flag */
253#define FLG_SHARED 0100000 /* shared extent */
254#define FLG_PRE 0010000 /* Unwritten extent */
255#define FLG_BSU 0001000 /* Not on begin of stripe unit */
256#define FLG_ESU 0000100 /* Not on end of stripe unit */
257#define FLG_BSW 0000010 /* Not on begin of stripe width */
258#define FLG_ESW 0000001 /* Not on end of stripe width */
4ca431fc 259 int agno;
9e726740 260 off_t agoff, bbperag;
dfc130f3
RC
261 int foff_w, boff_w, aoff_w, tot_w, agno_w;
262 char rbuf[32], bbuf[32], abuf[32];
4ca431fc 263 int sunit, swidth;
2bd0ea18
NS
264
265 foff_w = boff_w = aoff_w = MINRANGE_WIDTH;
266 tot_w = MINTOT_WIDTH;
e2eb7f3a
BN
267 if (is_rt)
268 sunit = swidth = bbperag = 0;
269 else {
9e726740
VP
270 bbperag = (off_t)fsgeo.agblocks *
271 (off_t)fsgeo.blocksize / BBSIZE;
e2eb7f3a
BN
272 sunit = (fsgeo.sunit * fsgeo.blocksize) / BBSIZE;
273 swidth = (fsgeo.swidth * fsgeo.blocksize) / BBSIZE;
274 }
66b57340 275 flg = sunit | pflag;
2bd0ea18 276
dfc130f3 277 /*
2bd0ea18
NS
278 * Go through the extents and figure out the width
279 * needed for all columns.
280 */
619df821 281 for (i = 0; i < egcnt; i++) {
dfc130f3 282 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
5b64e00a
NS
283 (long long) map[i + 1].bmv_offset,
284 (long long)(map[i + 1].bmv_offset +
285 map[i + 1].bmv_length - 1LL));
d371bee3
NS
286 if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC)
287 flg = 1;
2bd0ea18 288 if (map[i + 1].bmv_block == -1) {
dfc130f3
RC
289 foff_w = max(foff_w, strlen(rbuf));
290 tot_w = max(tot_w,
fb2bfcdf 291 numlen(map[i+1].bmv_length, 10));
2bd0ea18 292 } else {
dfc130f3 293 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
5b64e00a
NS
294 (long long) map[i + 1].bmv_block,
295 (long long)(map[i + 1].bmv_block +
296 map[i + 1].bmv_length - 1LL));
dfc130f3 297 boff_w = max(boff_w, strlen(bbuf));
e2eb7f3a
BN
298 if (!is_rt) {
299 agno = map[i + 1].bmv_block / bbperag;
300 agoff = map[i + 1].bmv_block -
301 (agno * bbperag);
302 snprintf(abuf, sizeof(abuf),
303 "(%lld..%lld)",
304 (long long)agoff,
305 (long long)(agoff +
306 map[i + 1].bmv_length - 1LL));
307 aoff_w = max(aoff_w, strlen(abuf));
308 } else
309 aoff_w = 0;
310 foff_w = max(foff_w, strlen(rbuf));
dfc130f3 311 tot_w = max(tot_w,
fb2bfcdf 312 numlen(map[i+1].bmv_length, 10));
2bd0ea18
NS
313 }
314 }
fb2bfcdf 315 agno_w = is_rt ? 0 : max(MINAG_WIDTH, numlen(fsgeo.agcount, 10));
d371bee3 316 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
9440d84d
NS
317 _("EXT"),
318 foff_w, _("FILE-OFFSET"),
e2eb7f3a
BN
319 boff_w, is_rt ? _("RT-BLOCK-RANGE") : _("BLOCK-RANGE"),
320 agno_w, is_rt ? "" : _("AG"),
321 aoff_w, is_rt ? "" : _("AG-OFFSET"),
d371bee3
NS
322 tot_w, _("TOTAL"),
323 flg ? _(" FLAGS") : "");
619df821 324 for (i = 0; i < egcnt; i++) {
d371bee3
NS
325 flg = FLG_NULL;
326 if (map[i + 1].bmv_oflags & BMV_OF_PREALLOC) {
327 flg |= FLG_PRE;
328 }
7536ce44
DW
329 if (map[i + 1].bmv_oflags & BMV_OF_SHARED)
330 flg |= FLG_SHARED;
331 if (map[i + 1].bmv_oflags & BMV_OF_DELALLOC)
332 map[i + 1].bmv_block = -2;
4595c940
JK
333 /*
334 * If striping enabled, determine if extent starts/ends
335 * on a stripe unit boundary.
336 */
4ca431fc 337 if (sunit) {
4ca431fc
NS
338 if (map[i + 1].bmv_block % sunit != 0) {
339 flg |= FLG_BSU;
340 }
dfc130f3 341 if (((map[i + 1].bmv_block +
4ca431fc
NS
342 map[i + 1].bmv_length ) % sunit ) != 0) {
343 flg |= FLG_ESU;
344 }
345 if (map[i + 1].bmv_block % swidth != 0) {
346 flg |= FLG_BSW;
347 }
dfc130f3 348 if (((map[i + 1].bmv_block +
4ca431fc
NS
349 map[i + 1].bmv_length ) % swidth ) != 0) {
350 flg |= FLG_ESW;
351 }
352 }
dfc130f3 353 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
5b64e00a
NS
354 (long long) map[i + 1].bmv_offset,
355 (long long)(map[i + 1].bmv_offset +
356 map[i + 1].bmv_length - 1LL));
2bd0ea18 357 if (map[i + 1].bmv_block == -1) {
dfc130f3
RC
358 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
359 i,
360 foff_w, rbuf,
9440d84d 361 boff_w, _("hole"),
2bd0ea18 362 agno_w, "",
dfc130f3 363 aoff_w, "",
5b64e00a 364 tot_w, (long long)map[i+1].bmv_length);
7536ce44
DW
365 } else if (map[i + 1].bmv_block == -2) {
366 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
367 i,
368 foff_w, rbuf,
369 boff_w, _("delalloc"),
370 agno_w, "",
371 aoff_w, "",
372 tot_w, (long long)map[i+1].bmv_length);
2bd0ea18 373 } else {
dfc130f3 374 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
5b64e00a
NS
375 (long long) map[i + 1].bmv_block,
376 (long long)(map[i + 1].bmv_block +
377 map[i + 1].bmv_length - 1LL));
e2eb7f3a
BN
378 printf("%4d: %-*s %-*s", i, foff_w, rbuf,
379 boff_w, bbuf);
380 if (!is_rt) {
381 agno = map[i + 1].bmv_block / bbperag;
382 agoff = map[i + 1].bmv_block -
383 (agno * bbperag);
384 snprintf(abuf, sizeof(abuf),
385 "(%lld..%lld)",
386 (long long)agoff,
387 (long long)(agoff +
388 map[i + 1].bmv_length - 1LL));
389 printf(" %*d %-*s", agno_w, agno,
390 aoff_w, abuf);
391 } else
392 printf(" ");
393 printf(" %*lld", tot_w,
394 (long long)map[i+1].bmv_length);
66b57340 395 if (flg == FLG_NULL && !pflag) {
4ca431fc
NS
396 printf("\n");
397 } else {
d371bee3 398 printf(" %-*.*o\n", NFLG, NFLG, flg);
4ca431fc 399 }
2bd0ea18
NS
400 }
401 }
66b57340 402 if ((flg || pflag) && vflag > 1) {
d371bee3 403 printf(_(" FLAG Values:\n"));
7536ce44
DW
404 printf(_(" %*.*o Shared extent\n"),
405 NFLG+1, NFLG+1, FLG_SHARED);
d371bee3
NS
406 printf(_(" %*.*o Unwritten preallocated extent\n"),
407 NFLG+1, NFLG+1, FLG_PRE);
408 printf(_(" %*.*o Doesn't begin on stripe unit\n"),
409 NFLG+1, NFLG+1, FLG_BSU);
410 printf(_(" %*.*o Doesn't end on stripe unit\n"),
411 NFLG+1, NFLG+1, FLG_ESU);
412 printf(_(" %*.*o Doesn't begin on stripe width\n"),
413 NFLG+1, NFLG+1, FLG_BSW);
414 printf(_(" %*.*o Doesn't end on stripe width\n"),
415 NFLG+1, NFLG+1, FLG_ESW);
4ca431fc 416 }
2bd0ea18
NS
417 }
418 free(map);
419 return 0;
420}
421
e246ba5f
NS
422void
423bmap_init(void)
2bd0ea18 424{
ad765595 425 bmap_cmd.name = "bmap";
e246ba5f
NS
426 bmap_cmd.cfunc = bmap_f;
427 bmap_cmd.argmin = 0;
428 bmap_cmd.argmax = -1;
48c46ee3 429 bmap_cmd.flags = CMD_NOMAP_OK;
4cfe2c37 430 bmap_cmd.args = _("[-acelpv] [-n nx]");
e246ba5f
NS
431 bmap_cmd.oneline = _("print block mapping for an XFS file");
432 bmap_cmd.help = bmap_help;
2bd0ea18 433
e246ba5f 434 add_command(&bmap_cmd);
2bd0ea18 435}