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