]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/bmap.c
1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
7 #include "platform_defs.h"
13 static cmdinfo_t bmap_cmd
;
20 " prints the block mapping for an XFS file's data or attribute forks"
23 " 'bmap -vp' - tabular format verbose map, including unwritten extents\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"
53 struct xfs_fsop_geom fsgeo
;
64 int bmv_iflags
= 0; /* flags for XFS_IOC_GETBMAPX */
69 while ((c
= getopt(argc
, argv
, "acdeln:pv")) != EOF
) {
71 case 'a': /* Attribute fork. */
72 bmv_iflags
|= BMV_IF_ATTRFORK
;
75 case 'c': /* CoW fork. */
76 bmv_iflags
|= BMV_IF_COWFORK
| BMV_IF_DELALLOC
;
80 bmv_iflags
|= BMV_IF_DELALLOC
;
82 case 'l': /* list number of blocks with each extent */
85 case 'n': /* number of extents specified */
89 /* do not recall possibly offline DMAPI files */
90 bmv_iflags
|= BMV_IF_NO_DMAPI_READ
;
93 /* report unwritten preallocated blocks */
95 bmv_iflags
|= BMV_IF_PREALLOC
;
97 case 'v': /* Verbose output */
101 return command_usage(&bmap_cmd
);
105 bmv_iflags
&= ~(BMV_IF_PREALLOC
|BMV_IF_NO_DMAPI_READ
);
108 c
= xfsctl(file
->name
, file
->fd
, XFS_IOC_FSGEOMETRY_V1
, &fsgeo
);
111 _("%s: can't get geometry [\"%s\"]: %s\n"),
112 progname
, file
->name
, strerror(errno
));
116 c
= xfsctl(file
->name
, file
->fd
, FS_IOC_FSGETXATTR
, &fsx
);
119 _("%s: cannot read attrs on \"%s\": %s\n"),
120 progname
, file
->name
, strerror(errno
));
125 if (fsx
.fsx_xflags
== FS_XFLAG_REALTIME
) {
127 * ag info not applicable to rt, continue
134 map_size
= nflag
? nflag
+2 : 32; /* initial guess - 32 */
135 map
= malloc(map_size
*sizeof(*map
));
137 fprintf(stderr
, _("%s: malloc of %d bytes failed.\n"),
138 progname
, (int)(map_size
* sizeof(*map
)));
144 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
145 * by nflag, or the initial guess number of extents (32).
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.
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.
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).
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.
171 do { /* loop a miximum of two times */
173 memset(map
, 0, sizeof(*map
)); /* zero header */
175 map
->bmv_length
= -1;
176 map
->bmv_count
= map_size
;
177 map
->bmv_iflags
= bmv_iflags
;
179 i
= xfsctl(file
->name
, file
->fd
, XFS_IOC_GETBMAPX
, map
);
182 && !aflag
&& filesize() == 0) {
185 fprintf(stderr
, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
186 " iflags=0x%x [\"%s\"]: %s\n"),
187 progname
, map
->bmv_iflags
, file
->name
,
196 if (map
->bmv_entries
< map
->bmv_count
-1)
198 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
201 i
= xfsctl(file
->name
, file
->fd
, aflag
?
202 XFS_IOC_FSGETXATTRA
: FS_IOC_FSGETXATTR
, &fsx
);
204 fprintf(stderr
, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
205 "[\"%s\"]: %s\n", progname
, aflag
? "A" : "",
206 file
->name
, strerror(errno
));
211 if (2 * fsx
.fsx_nextents
> map_size
) {
212 map_size
= 2 * fsx
.fsx_nextents
+ 1;
213 map
= realloc(map
, map_size
*sizeof(*map
));
216 _("%s: cannot realloc %d bytes\n"),
217 progname
, (int)(map_size
*sizeof(*map
)));
222 } while (++loop
< 2);
224 if (map
->bmv_entries
<= 0) {
225 printf(_("%s: no extents\n"), file
->name
);
230 egcnt
= nflag
? min(nflag
, map
->bmv_entries
) : map
->bmv_entries
;
231 printf("%s:\n", file
->name
);
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)
240 else if (map
[i
+ 1].bmv_block
== -2)
241 printf(_("delalloc"));
244 (long long) map
[i
+ 1].bmv_block
,
245 (long long)(map
[i
+ 1].bmv_block
+
246 map
[i
+ 1].bmv_length
- 1LL));
250 printf(_(" %lld blocks\n"),
251 (long long)map
[i
+1].bmv_length
);
257 * Verbose mode displays:
258 * extent: [startoffset..endoffset]: startblock..endblock \
259 * ag# (agoffset..agendoffset) totalbbs
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 */
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];
278 foff_w
= boff_w
= aoff_w
= MINRANGE_WIDTH
;
279 tot_w
= MINTOT_WIDTH
;
281 sunit
= swidth
= bbperag
= 0;
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
;
291 * Go through the extents and figure out the width
292 * needed for all columns.
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
)
301 if (map
[i
+ 1].bmv_block
== -1) {
302 foff_w
= max(foff_w
, strlen(rbuf
));
304 numlen(map
[i
+1].bmv_length
, 10));
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
));
312 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
313 agoff
= map
[i
+ 1].bmv_block
-
315 snprintf(abuf
, sizeof(abuf
),
319 map
[i
+ 1].bmv_length
- 1LL));
320 aoff_w
= max(aoff_w
, strlen(abuf
));
323 foff_w
= max(foff_w
, strlen(rbuf
));
325 numlen(map
[i
+1].bmv_length
, 10));
328 agno_w
= is_rt
? 0 : max(MINAG_WIDTH
, numlen(fsgeo
.agcount
, 10));
329 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
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"),
336 flg
? _(" FLAGS") : "");
337 for (i
= 0; i
< egcnt
; i
++) {
339 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
) {
342 if (map
[i
+ 1].bmv_oflags
& BMV_OF_SHARED
)
344 if (map
[i
+ 1].bmv_oflags
& BMV_OF_DELALLOC
)
345 map
[i
+ 1].bmv_block
= -2;
347 * If striping enabled, determine if extent starts/ends
348 * on a stripe unit boundary.
351 if (map
[i
+ 1].bmv_block
% sunit
!= 0) {
354 if (((map
[i
+ 1].bmv_block
+
355 map
[i
+ 1].bmv_length
) % sunit
) != 0) {
358 if (map
[i
+ 1].bmv_block
% swidth
!= 0) {
361 if (((map
[i
+ 1].bmv_block
+
362 map
[i
+ 1].bmv_length
) % swidth
) != 0) {
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",
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",
382 boff_w
, _("delalloc"),
385 tot_w
, (long long)map
[i
+1].bmv_length
);
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
,
394 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
395 agoff
= map
[i
+ 1].bmv_block
-
397 snprintf(abuf
, sizeof(abuf
),
401 map
[i
+ 1].bmv_length
- 1LL));
402 printf(" %*d %-*s", agno_w
, agno
,
406 printf(" %*lld", tot_w
,
407 (long long)map
[i
+1].bmv_length
);
408 if (flg
== FLG_NULL
&& !pflag
) {
411 printf(" %-*.*o\n", NFLG
, NFLG
, flg
);
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
);
438 bmap_cmd
.name
= "bmap";
439 bmap_cmd
.cfunc
= bmap_f
;
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
;
447 add_command(&bmap_cmd
);