]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/bmap.c
2e4ff7b2343219028a218a4e6bf22f5a12573139
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
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
7 * published by the Free Software Foundation.
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.
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
19 #include "platform_defs.h"
25 static cmdinfo_t bmap_cmd
;
32 " prints the block mapping for an XFS file's data or attribute forks"
35 " 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
37 " bmap prints the map of disk blocks used by the current file.\n"
38 " The map lists each extent used by the file, as well as regions in the\n"
39 " file that do not have any corresponding blocks (holes).\n"
40 " By default, each line of the listing takes the following form:\n"
41 " extent: [startoffset..endoffset]: startblock..endblock\n"
42 " Holes are marked by replacing the startblock..endblock with 'hole'.\n"
43 " All the file offsets and disk blocks are in units of 512-byte blocks.\n"
44 " -a -- prints the attribute fork map instead of the data fork.\n"
45 " -c -- prints the copy-on-write fork map instead of the data fork.\n"
46 " -d -- suppresses a DMAPI read event, offline portions shown as holes.\n"
47 " -e -- print delayed allocation extents.\n"
48 " -l -- also displays the length of each extent in 512-byte blocks.\n"
49 " -n -- query n extents.\n"
50 " -p -- obtain all unwritten extents as well (w/ -v show which are unwritten.)\n"
51 " -v -- Verbose information, specify ag info. Show flags legend on 2nd -v\n"
52 " Note: the bmap for non-regular files can be obtained provided the file\n"
53 " was opened appropriately (in particular, must be opened read-only).\n"
64 struct xfs_fsop_geom fsgeo
;
75 int bmv_iflags
= 0; /* flags for XFS_IOC_GETBMAPX */
80 while ((c
= getopt(argc
, argv
, "acdeln:pv")) != EOF
) {
82 case 'a': /* Attribute fork. */
83 bmv_iflags
|= BMV_IF_ATTRFORK
;
86 case 'c': /* CoW fork. */
87 bmv_iflags
|= BMV_IF_COWFORK
| BMV_IF_DELALLOC
;
91 bmv_iflags
|= BMV_IF_DELALLOC
;
93 case 'l': /* list number of blocks with each extent */
96 case 'n': /* number of extents specified */
100 /* do not recall possibly offline DMAPI files */
101 bmv_iflags
|= BMV_IF_NO_DMAPI_READ
;
104 /* report unwritten preallocated blocks */
106 bmv_iflags
|= BMV_IF_PREALLOC
;
108 case 'v': /* Verbose output */
112 return command_usage(&bmap_cmd
);
116 bmv_iflags
&= ~(BMV_IF_PREALLOC
|BMV_IF_NO_DMAPI_READ
);
119 c
= xfsctl(file
->name
, file
->fd
, XFS_IOC_FSGEOMETRY_V1
, &fsgeo
);
122 _("%s: can't get geometry [\"%s\"]: %s\n"),
123 progname
, file
->name
, strerror(errno
));
127 c
= xfsctl(file
->name
, file
->fd
, FS_IOC_FSGETXATTR
, &fsx
);
130 _("%s: cannot read attrs on \"%s\": %s\n"),
131 progname
, file
->name
, strerror(errno
));
136 if (fsx
.fsx_xflags
== FS_XFLAG_REALTIME
) {
138 * ag info not applicable to rt, continue
145 map_size
= nflag
? nflag
+2 : 32; /* initial guess - 32 */
146 map
= malloc(map_size
*sizeof(*map
));
148 fprintf(stderr
, _("%s: malloc of %d bytes failed.\n"),
149 progname
, (int)(map_size
* sizeof(*map
)));
155 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
156 * by nflag, or the initial guess number of extents (32).
158 * If there are more extents than we guessed, use xfsctl
159 * (FS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
160 * space based on this count, and try again.
162 * If the initial FGETBMAPX attempt returns EINVAL, this may mean
163 * that we tried the FGETBMAPX on a zero length file. If we get
164 * EINVAL, check the length with fstat() and return "no extents"
165 * if the length == 0.
167 * Why not do the xfsctl(FS_IOC_FSGETXATTR[A]) first? Two reasons:
168 * (1) The extent count may be wrong for a file with delayed
169 * allocation blocks. The XFS_IOC_GETBMAPX forces the real
170 * allocation and fixes up the extent count.
171 * (2) For XFS_IOC_GETBMAP[X] on a DMAPI file that has been moved
172 * offline by a DMAPI application (e.g., DMF) the
173 * FS_IOC_FSGETXATTR only reflects the extents actually online.
174 * Doing XFS_IOC_GETBMAPX call first forces that data blocks online
175 * and then everything proceeds normally (see PV #545725).
177 * If you don't want this behavior on a DMAPI offline file,
178 * try the "-d" option which sets the BMV_IF_NO_DMAPI_READ
179 * iflag for XFS_IOC_GETBMAPX.
182 do { /* loop a miximum of two times */
184 memset(map
, 0, sizeof(*map
)); /* zero header */
186 map
->bmv_length
= -1;
187 map
->bmv_count
= map_size
;
188 map
->bmv_iflags
= bmv_iflags
;
190 i
= xfsctl(file
->name
, file
->fd
, XFS_IOC_GETBMAPX
, map
);
193 && !aflag
&& filesize() == 0) {
196 fprintf(stderr
, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
197 " iflags=0x%x [\"%s\"]: %s\n"),
198 progname
, map
->bmv_iflags
, file
->name
,
207 if (map
->bmv_entries
< map
->bmv_count
-1)
209 /* Get number of extents from xfsctl FS_IOC_FSGETXATTR[A]
212 i
= xfsctl(file
->name
, file
->fd
, aflag
?
213 XFS_IOC_FSGETXATTRA
: FS_IOC_FSGETXATTR
, &fsx
);
215 fprintf(stderr
, "%s: xfsctl(FS_IOC_FSGETXATTR%s) "
216 "[\"%s\"]: %s\n", progname
, aflag
? "A" : "",
217 file
->name
, strerror(errno
));
222 if (2 * fsx
.fsx_nextents
> map_size
) {
223 map_size
= 2 * fsx
.fsx_nextents
+ 1;
224 map
= realloc(map
, map_size
*sizeof(*map
));
227 _("%s: cannot realloc %d bytes\n"),
228 progname
, (int)(map_size
*sizeof(*map
)));
233 } while (++loop
< 2);
235 if (map
->bmv_entries
<= 0) {
236 printf(_("%s: no extents\n"), file
->name
);
241 egcnt
= nflag
? min(nflag
, map
->bmv_entries
) : map
->bmv_entries
;
242 printf("%s:\n", file
->name
);
244 for (i
= 0; i
< egcnt
; i
++) {
245 printf("\t%d: [%lld..%lld]: ", i
,
246 (long long) map
[i
+ 1].bmv_offset
,
247 (long long)(map
[i
+ 1].bmv_offset
+
248 map
[i
+ 1].bmv_length
- 1LL));
249 if (map
[i
+ 1].bmv_block
== -1)
251 else if (map
[i
+ 1].bmv_block
== -2)
252 printf(_("delalloc"));
255 (long long) map
[i
+ 1].bmv_block
,
256 (long long)(map
[i
+ 1].bmv_block
+
257 map
[i
+ 1].bmv_length
- 1LL));
261 printf(_(" %lld blocks\n"),
262 (long long)map
[i
+1].bmv_length
);
268 * Verbose mode displays:
269 * extent: [startoffset..endoffset]: startblock..endblock \
270 * ag# (agoffset..agendoffset) totalbbs
272 #define MINRANGE_WIDTH 16
273 #define MINAG_WIDTH 2
274 #define MINTOT_WIDTH 5
275 #define NFLG 6 /* count of flags */
276 #define FLG_NULL 0000000 /* Null flag */
277 #define FLG_SHARED 0100000 /* shared extent */
278 #define FLG_PRE 0010000 /* Unwritten extent */
279 #define FLG_BSU 0001000 /* Not on begin of stripe unit */
280 #define FLG_ESU 0000100 /* Not on end of stripe unit */
281 #define FLG_BSW 0000010 /* Not on begin of stripe width */
282 #define FLG_ESW 0000001 /* Not on end of stripe width */
284 off64_t agoff
, bbperag
;
285 int foff_w
, boff_w
, aoff_w
, tot_w
, agno_w
;
286 char rbuf
[32], bbuf
[32], abuf
[32];
289 foff_w
= boff_w
= aoff_w
= MINRANGE_WIDTH
;
290 tot_w
= MINTOT_WIDTH
;
292 sunit
= swidth
= bbperag
= 0;
294 bbperag
= (off64_t
)fsgeo
.agblocks
*
295 (off64_t
)fsgeo
.blocksize
/ BBSIZE
;
296 sunit
= (fsgeo
.sunit
* fsgeo
.blocksize
) / BBSIZE
;
297 swidth
= (fsgeo
.swidth
* fsgeo
.blocksize
) / BBSIZE
;
302 * Go through the extents and figure out the width
303 * needed for all columns.
305 for (i
= 0; i
< egcnt
; i
++) {
306 snprintf(rbuf
, sizeof(rbuf
), "[%lld..%lld]:",
307 (long long) map
[i
+ 1].bmv_offset
,
308 (long long)(map
[i
+ 1].bmv_offset
+
309 map
[i
+ 1].bmv_length
- 1LL));
310 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
)
312 if (map
[i
+ 1].bmv_block
== -1) {
313 foff_w
= max(foff_w
, strlen(rbuf
));
315 numlen(map
[i
+1].bmv_length
, 10));
317 snprintf(bbuf
, sizeof(bbuf
), "%lld..%lld",
318 (long long) map
[i
+ 1].bmv_block
,
319 (long long)(map
[i
+ 1].bmv_block
+
320 map
[i
+ 1].bmv_length
- 1LL));
321 boff_w
= max(boff_w
, strlen(bbuf
));
323 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
324 agoff
= map
[i
+ 1].bmv_block
-
326 snprintf(abuf
, sizeof(abuf
),
330 map
[i
+ 1].bmv_length
- 1LL));
331 aoff_w
= max(aoff_w
, strlen(abuf
));
334 foff_w
= max(foff_w
, strlen(rbuf
));
336 numlen(map
[i
+1].bmv_length
, 10));
339 agno_w
= is_rt
? 0 : max(MINAG_WIDTH
, numlen(fsgeo
.agcount
, 10));
340 printf("%4s: %-*s %-*s %*s %-*s %*s%s\n",
342 foff_w
, _("FILE-OFFSET"),
343 boff_w
, is_rt
? _("RT-BLOCK-RANGE") : _("BLOCK-RANGE"),
344 agno_w
, is_rt
? "" : _("AG"),
345 aoff_w
, is_rt
? "" : _("AG-OFFSET"),
347 flg
? _(" FLAGS") : "");
348 for (i
= 0; i
< egcnt
; i
++) {
350 if (map
[i
+ 1].bmv_oflags
& BMV_OF_PREALLOC
) {
353 if (map
[i
+ 1].bmv_oflags
& BMV_OF_SHARED
)
355 if (map
[i
+ 1].bmv_oflags
& BMV_OF_DELALLOC
)
356 map
[i
+ 1].bmv_block
= -2;
358 * If striping enabled, determine if extent starts/ends
359 * on a stripe unit boundary.
362 if (map
[i
+ 1].bmv_block
% sunit
!= 0) {
365 if (((map
[i
+ 1].bmv_block
+
366 map
[i
+ 1].bmv_length
) % sunit
) != 0) {
369 if (map
[i
+ 1].bmv_block
% swidth
!= 0) {
372 if (((map
[i
+ 1].bmv_block
+
373 map
[i
+ 1].bmv_length
) % swidth
) != 0) {
377 snprintf(rbuf
, sizeof(rbuf
), "[%lld..%lld]:",
378 (long long) map
[i
+ 1].bmv_offset
,
379 (long long)(map
[i
+ 1].bmv_offset
+
380 map
[i
+ 1].bmv_length
- 1LL));
381 if (map
[i
+ 1].bmv_block
== -1) {
382 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
388 tot_w
, (long long)map
[i
+1].bmv_length
);
389 } else if (map
[i
+ 1].bmv_block
== -2) {
390 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
393 boff_w
, _("delalloc"),
396 tot_w
, (long long)map
[i
+1].bmv_length
);
398 snprintf(bbuf
, sizeof(bbuf
), "%lld..%lld",
399 (long long) map
[i
+ 1].bmv_block
,
400 (long long)(map
[i
+ 1].bmv_block
+
401 map
[i
+ 1].bmv_length
- 1LL));
402 printf("%4d: %-*s %-*s", i
, foff_w
, rbuf
,
405 agno
= map
[i
+ 1].bmv_block
/ bbperag
;
406 agoff
= map
[i
+ 1].bmv_block
-
408 snprintf(abuf
, sizeof(abuf
),
412 map
[i
+ 1].bmv_length
- 1LL));
413 printf(" %*d %-*s", agno_w
, agno
,
417 printf(" %*lld", tot_w
,
418 (long long)map
[i
+1].bmv_length
);
419 if (flg
== FLG_NULL
&& !pflag
) {
422 printf(" %-*.*o\n", NFLG
, NFLG
, flg
);
426 if ((flg
|| pflag
) && vflag
> 1) {
427 printf(_(" FLAG Values:\n"));
428 printf(_(" %*.*o Shared extent\n"),
429 NFLG
+1, NFLG
+1, FLG_SHARED
);
430 printf(_(" %*.*o Unwritten preallocated extent\n"),
431 NFLG
+1, NFLG
+1, FLG_PRE
);
432 printf(_(" %*.*o Doesn't begin on stripe unit\n"),
433 NFLG
+1, NFLG
+1, FLG_BSU
);
434 printf(_(" %*.*o Doesn't end on stripe unit\n"),
435 NFLG
+1, NFLG
+1, FLG_ESU
);
436 printf(_(" %*.*o Doesn't begin on stripe width\n"),
437 NFLG
+1, NFLG
+1, FLG_BSW
);
438 printf(_(" %*.*o Doesn't end on stripe width\n"),
439 NFLG
+1, NFLG
+1, FLG_ESW
);
449 bmap_cmd
.name
= "bmap";
450 bmap_cmd
.cfunc
= bmap_f
;
452 bmap_cmd
.argmax
= -1;
453 bmap_cmd
.flags
= CMD_NOMAP_OK
;
454 bmap_cmd
.args
= _("[-adlpv] [-n nx]");
455 bmap_cmd
.oneline
= _("print block mapping for an XFS file");
456 bmap_cmd
.help
= bmap_help
;
458 add_command(&bmap_cmd
);