]>
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"
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 " -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
, "aceln: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 /* report unwritten preallocated blocks */
91 bmv_iflags
|= BMV_IF_PREALLOC
;
93 case 'v': /* Verbose output */
97 return command_usage(&bmap_cmd
);
101 bmv_iflags
&= ~BMV_IF_PREALLOC
;
104 c
= -xfrog_geometry(file
->fd
, &fsgeo
);
107 _("%s: can't get geometry [\"%s\"]: %s\n"),
108 progname
, file
->name
, strerror(c
));
112 c
= xfsctl(file
->name
, file
->fd
, FS_IOC_FSGETXATTR
, &fsx
);
115 _("%s: cannot read attrs on \"%s\": %s\n"),
116 progname
, file
->name
, strerror(errno
));
121 if (fsx
.fsx_xflags
& FS_XFLAG_REALTIME
) {
123 * ag info not applicable to rt, continue
130 map_size
= nflag
? nflag
+2 : 32; /* initial guess - 32 */
131 map
= malloc(map_size
*sizeof(*map
));
133 fprintf(stderr
, _("%s: malloc of %d bytes failed.\n"),
134 progname
, (int)(map_size
* sizeof(*map
)));
140 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
141 * by nflag, or the initial guess number of extents (32).
143 * If there are more extents than we guessed, use xfsctl
144 * (FS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
145 * space based on this count, and try again.
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.
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.
158 do { /* loop a miximum of two times */
160 memset(map
, 0, sizeof(*map
)); /* zero header */
162 map
->bmv_length
= -1;
163 map
->bmv_count
= map_size
;
164 map
->bmv_iflags
= bmv_iflags
;
166 i
= xfsctl(file
->name
, file
->fd
, XFS_IOC_GETBMAPX
, map
);
169 && !aflag
&& filesize() == 0) {
172 fprintf(stderr
, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
173 " iflags=0x%x [\"%s\"]: %s\n"),
174 progname
, map
->bmv_iflags
, file
->name
,
183 if (map
->bmv_entries
< map
->bmv_count
-1)
185 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
188 i
= xfsctl(file
->name
, file
->fd
, aflag
?
189 XFS_IOC_FSGETXATTRA
: FS_IOC_FSGETXATTR
, &fsx
);
191 fprintf(stderr
, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
192 "[\"%s\"]: %s\n", progname
, aflag
? "A" : "",
193 file
->name
, strerror(errno
));
198 if (2 * fsx
.fsx_nextents
> map_size
) {
199 map_size
= 2 * fsx
.fsx_nextents
+ 1;
200 map
= realloc(map
, map_size
*sizeof(*map
));
203 _("%s: cannot realloc %d bytes\n"),
204 progname
, (int)(map_size
*sizeof(*map
)));
209 } while (++loop
< 2);
211 if (map
->bmv_entries
<= 0) {
212 printf(_("%s: no extents\n"), file
->name
);
217 egcnt
= nflag
? min(nflag
, map
->bmv_entries
) : map
->bmv_entries
;
218 printf("%s:\n", file
->name
);
220 for (i
= 0; i
< egcnt
; i
++) {
221 printf("\t%d: [%lld..%lld]: ", i
,
222 (long long) map
[i
+ 1].bmv_offset
,
223 (long long)(map
[i
+ 1].bmv_offset
+
224 map
[i
+ 1].bmv_length
- 1LL));
225 if (map
[i
+ 1].bmv_block
== -1)
227 else if (map
[i
+ 1].bmv_block
== -2)
228 printf(_("delalloc"));
231 (long long) map
[i
+ 1].bmv_block
,
232 (long long)(map
[i
+ 1].bmv_block
+
233 map
[i
+ 1].bmv_length
- 1LL));
237 printf(_(" %lld blocks\n"),
238 (long long)map
[i
+1].bmv_length
);
244 * Verbose mode displays:
245 * extent: [startoffset..endoffset]: startblock..endblock \
246 * ag# (agoffset..agendoffset) totalbbs
248 #define MINRANGE_WIDTH 16
249 #define MINAG_WIDTH 2
250 #define MINTOT_WIDTH 5
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 */
260 off64_t agoff
, bbperag
;
261 int foff_w
, boff_w
, aoff_w
, tot_w
, agno_w
;
262 char rbuf
[32], bbuf
[32], abuf
[32];
265 foff_w
= boff_w
= aoff_w
= MINRANGE_WIDTH
;
266 tot_w
= MINTOT_WIDTH
;
268 sunit
= swidth
= bbperag
= 0;
270 bbperag
= (off64_t
)fsgeo
.agblocks
*
271 (off64_t
)fsgeo
.blocksize
/ BBSIZE
;
272 sunit
= (fsgeo
.sunit
* fsgeo
.blocksize
) / BBSIZE
;
273 swidth
= (fsgeo
.swidth
* fsgeo
.blocksize
) / BBSIZE
;
278 * Go through the extents and figure out the width
279 * needed for all columns.
281 for (i
= 0; i
< egcnt
; i
++) {
282 snprintf(rbuf
, sizeof(rbuf
), "[%lld..%lld]:",
283 (long long) map
[i
+ 1].bmv_offset
,
284 (long long)(map
[i
+ 1].bmv_offset
+
285 map
[i
+ 1].bmv_length
- 1LL));
286 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
)
288 if (map
[i
+ 1].bmv_block
== -1) {
289 foff_w
= max(foff_w
, strlen(rbuf
));
291 numlen(map
[i
+1].bmv_length
, 10));
293 snprintf(bbuf
, sizeof(bbuf
), "%lld..%lld",
294 (long long) map
[i
+ 1].bmv_block
,
295 (long long)(map
[i
+ 1].bmv_block
+
296 map
[i
+ 1].bmv_length
- 1LL));
297 boff_w
= max(boff_w
, strlen(bbuf
));
299 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
300 agoff
= map
[i
+ 1].bmv_block
-
302 snprintf(abuf
, sizeof(abuf
),
306 map
[i
+ 1].bmv_length
- 1LL));
307 aoff_w
= max(aoff_w
, strlen(abuf
));
310 foff_w
= max(foff_w
, strlen(rbuf
));
312 numlen(map
[i
+1].bmv_length
, 10));
315 agno_w
= is_rt
? 0 : max(MINAG_WIDTH
, numlen(fsgeo
.agcount
, 10));
316 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
318 foff_w
, _("FILE-OFFSET"),
319 boff_w
, is_rt
? _("RT-BLOCK-RANGE") : _("BLOCK-RANGE"),
320 agno_w
, is_rt
? "" : _("AG"),
321 aoff_w
, is_rt
? "" : _("AG-OFFSET"),
323 flg
? _(" FLAGS") : "");
324 for (i
= 0; i
< egcnt
; i
++) {
326 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
) {
329 if (map
[i
+ 1].bmv_oflags
& BMV_OF_SHARED
)
331 if (map
[i
+ 1].bmv_oflags
& BMV_OF_DELALLOC
)
332 map
[i
+ 1].bmv_block
= -2;
334 * If striping enabled, determine if extent starts/ends
335 * on a stripe unit boundary.
338 if (map
[i
+ 1].bmv_block
% sunit
!= 0) {
341 if (((map
[i
+ 1].bmv_block
+
342 map
[i
+ 1].bmv_length
) % sunit
) != 0) {
345 if (map
[i
+ 1].bmv_block
% swidth
!= 0) {
348 if (((map
[i
+ 1].bmv_block
+
349 map
[i
+ 1].bmv_length
) % swidth
) != 0) {
353 snprintf(rbuf
, sizeof(rbuf
), "[%lld..%lld]:",
354 (long long) map
[i
+ 1].bmv_offset
,
355 (long long)(map
[i
+ 1].bmv_offset
+
356 map
[i
+ 1].bmv_length
- 1LL));
357 if (map
[i
+ 1].bmv_block
== -1) {
358 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
364 tot_w
, (long long)map
[i
+1].bmv_length
);
365 } else if (map
[i
+ 1].bmv_block
== -2) {
366 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
369 boff_w
, _("delalloc"),
372 tot_w
, (long long)map
[i
+1].bmv_length
);
374 snprintf(bbuf
, sizeof(bbuf
), "%lld..%lld",
375 (long long) map
[i
+ 1].bmv_block
,
376 (long long)(map
[i
+ 1].bmv_block
+
377 map
[i
+ 1].bmv_length
- 1LL));
378 printf("%4d: %-*s %-*s", i
, foff_w
, rbuf
,
381 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
382 agoff
= map
[i
+ 1].bmv_block
-
384 snprintf(abuf
, sizeof(abuf
),
388 map
[i
+ 1].bmv_length
- 1LL));
389 printf(" %*d %-*s", agno_w
, agno
,
393 printf(" %*lld", tot_w
,
394 (long long)map
[i
+1].bmv_length
);
395 if (flg
== FLG_NULL
&& !pflag
) {
398 printf(" %-*.*o\n", NFLG
, NFLG
, flg
);
402 if ((flg
|| pflag
) && vflag
> 1) {
403 printf(_(" FLAG Values:\n"));
404 printf(_(" %*.*o Shared extent\n"),
405 NFLG
+1, NFLG
+1, FLG_SHARED
);
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
);
425 bmap_cmd
.name
= "bmap";
426 bmap_cmd
.cfunc
= bmap_f
;
428 bmap_cmd
.argmax
= -1;
429 bmap_cmd
.flags
= CMD_NOMAP_OK
;
430 bmap_cmd
.args
= _("[-acelpv] [-n nx]");
431 bmap_cmd
.oneline
= _("print block mapping for an XFS file");
432 bmap_cmd
.help
= bmap_help
;
434 add_command(&bmap_cmd
);