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