]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/bmap.c
cf4ea12b75a7eadc84cd1ff2c1c4409f8ed8f4ab
1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
7 #include "platform_defs.h"
12 #include "libfrog/fsgeom.h"
14 static cmdinfo_t bmap_cmd
;
21 " prints the block mapping for an XFS file's data or attribute forks"
24 " 'bmap -vp' - tabular format verbose map, including unwritten extents\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"
54 struct xfs_fsop_geom fsgeo
;
65 int bmv_iflags
= 0; /* flags for XFS_IOC_GETBMAPX */
70 while ((c
= getopt(argc
, argv
, "acdeln:pv")) != EOF
) {
72 case 'a': /* Attribute fork. */
73 bmv_iflags
|= BMV_IF_ATTRFORK
;
76 case 'c': /* CoW fork. */
77 bmv_iflags
|= BMV_IF_COWFORK
| BMV_IF_DELALLOC
;
81 bmv_iflags
|= BMV_IF_DELALLOC
;
83 case 'l': /* list number of blocks with each extent */
86 case 'n': /* number of extents specified */
90 /* do not recall possibly offline DMAPI files */
91 bmv_iflags
|= BMV_IF_NO_DMAPI_READ
;
94 /* report unwritten preallocated blocks */
96 bmv_iflags
|= BMV_IF_PREALLOC
;
98 case 'v': /* Verbose output */
102 return command_usage(&bmap_cmd
);
106 bmv_iflags
&= ~(BMV_IF_PREALLOC
|BMV_IF_NO_DMAPI_READ
);
109 c
= xfrog_geometry(file
->fd
, &fsgeo
);
112 _("%s: can't get geometry [\"%s\"]: %s\n"),
113 progname
, file
->name
, strerror(c
));
117 c
= xfsctl(file
->name
, file
->fd
, FS_IOC_FSGETXATTR
, &fsx
);
120 _("%s: cannot read attrs on \"%s\": %s\n"),
121 progname
, file
->name
, strerror(errno
));
126 if (fsx
.fsx_xflags
== FS_XFLAG_REALTIME
) {
128 * ag info not applicable to rt, continue
135 map_size
= nflag
? nflag
+2 : 32; /* initial guess - 32 */
136 map
= malloc(map_size
*sizeof(*map
));
138 fprintf(stderr
, _("%s: malloc of %d bytes failed.\n"),
139 progname
, (int)(map_size
* sizeof(*map
)));
145 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
146 * by nflag, or the initial guess number of extents (32).
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.
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.
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).
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.
172 do { /* loop a miximum of two times */
174 memset(map
, 0, sizeof(*map
)); /* zero header */
176 map
->bmv_length
= -1;
177 map
->bmv_count
= map_size
;
178 map
->bmv_iflags
= bmv_iflags
;
180 i
= xfsctl(file
->name
, file
->fd
, XFS_IOC_GETBMAPX
, map
);
183 && !aflag
&& filesize() == 0) {
186 fprintf(stderr
, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
187 " iflags=0x%x [\"%s\"]: %s\n"),
188 progname
, map
->bmv_iflags
, file
->name
,
197 if (map
->bmv_entries
< map
->bmv_count
-1)
199 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
202 i
= xfsctl(file
->name
, file
->fd
, aflag
?
203 XFS_IOC_FSGETXATTRA
: FS_IOC_FSGETXATTR
, &fsx
);
205 fprintf(stderr
, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
206 "[\"%s\"]: %s\n", progname
, aflag
? "A" : "",
207 file
->name
, strerror(errno
));
212 if (2 * fsx
.fsx_nextents
> map_size
) {
213 map_size
= 2 * fsx
.fsx_nextents
+ 1;
214 map
= realloc(map
, map_size
*sizeof(*map
));
217 _("%s: cannot realloc %d bytes\n"),
218 progname
, (int)(map_size
*sizeof(*map
)));
223 } while (++loop
< 2);
225 if (map
->bmv_entries
<= 0) {
226 printf(_("%s: no extents\n"), file
->name
);
231 egcnt
= nflag
? min(nflag
, map
->bmv_entries
) : map
->bmv_entries
;
232 printf("%s:\n", file
->name
);
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)
241 else if (map
[i
+ 1].bmv_block
== -2)
242 printf(_("delalloc"));
245 (long long) map
[i
+ 1].bmv_block
,
246 (long long)(map
[i
+ 1].bmv_block
+
247 map
[i
+ 1].bmv_length
- 1LL));
251 printf(_(" %lld blocks\n"),
252 (long long)map
[i
+1].bmv_length
);
258 * Verbose mode displays:
259 * extent: [startoffset..endoffset]: startblock..endblock \
260 * ag# (agoffset..agendoffset) totalbbs
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 */
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];
279 foff_w
= boff_w
= aoff_w
= MINRANGE_WIDTH
;
280 tot_w
= MINTOT_WIDTH
;
282 sunit
= swidth
= bbperag
= 0;
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
;
292 * Go through the extents and figure out the width
293 * needed for all columns.
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
)
302 if (map
[i
+ 1].bmv_block
== -1) {
303 foff_w
= max(foff_w
, strlen(rbuf
));
305 numlen(map
[i
+1].bmv_length
, 10));
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
));
313 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
314 agoff
= map
[i
+ 1].bmv_block
-
316 snprintf(abuf
, sizeof(abuf
),
320 map
[i
+ 1].bmv_length
- 1LL));
321 aoff_w
= max(aoff_w
, strlen(abuf
));
324 foff_w
= max(foff_w
, strlen(rbuf
));
326 numlen(map
[i
+1].bmv_length
, 10));
329 agno_w
= is_rt
? 0 : max(MINAG_WIDTH
, numlen(fsgeo
.agcount
, 10));
330 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
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"),
337 flg
? _(" FLAGS") : "");
338 for (i
= 0; i
< egcnt
; i
++) {
340 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
) {
343 if (map
[i
+ 1].bmv_oflags
& BMV_OF_SHARED
)
345 if (map
[i
+ 1].bmv_oflags
& BMV_OF_DELALLOC
)
346 map
[i
+ 1].bmv_block
= -2;
348 * If striping enabled, determine if extent starts/ends
349 * on a stripe unit boundary.
352 if (map
[i
+ 1].bmv_block
% sunit
!= 0) {
355 if (((map
[i
+ 1].bmv_block
+
356 map
[i
+ 1].bmv_length
) % sunit
) != 0) {
359 if (map
[i
+ 1].bmv_block
% swidth
!= 0) {
362 if (((map
[i
+ 1].bmv_block
+
363 map
[i
+ 1].bmv_length
) % swidth
) != 0) {
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",
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",
383 boff_w
, _("delalloc"),
386 tot_w
, (long long)map
[i
+1].bmv_length
);
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
,
395 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
396 agoff
= map
[i
+ 1].bmv_block
-
398 snprintf(abuf
, sizeof(abuf
),
402 map
[i
+ 1].bmv_length
- 1LL));
403 printf(" %*d %-*s", agno_w
, agno
,
407 printf(" %*lld", tot_w
,
408 (long long)map
[i
+1].bmv_length
);
409 if (flg
== FLG_NULL
&& !pflag
) {
412 printf(" %-*.*o\n", NFLG
, NFLG
, flg
);
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
);
439 bmap_cmd
.name
= "bmap";
440 bmap_cmd
.cfunc
= bmap_f
;
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
;
448 add_command(&bmap_cmd
);