]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/bmap.c
Merge whitespace changes over
[thirdparty/xfsprogs-dev.git] / io / bmap.c
1 /*
2 * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31 */
32
33 #include <libxfs.h>
34 #include "command.h"
35 #include "init.h"
36
37 static cmdinfo_t bmap_cmd;
38
39 static int
40 usage(void)
41 {
42 printf("%s %s\n", bmap_cmd.name, bmap_cmd.oneline);
43 return 0;
44 }
45
46 static void
47 bmap_help(void)
48 {
49 printf(_(
50 "\n"
51 " prints the block mapping for an XFS file's data or attribute forks"
52 "\n"
53 " Example:\n"
54 " 'bmap -vp' - tabular format verbose map, including unwritten extents\n"
55 "\n"
56 " bmap prints the map of disk blocks used by the current file.\n"
57 " The map lists each extent used by the file, as well as regions in the\n"
58 " file that do not have any corresponding blocks (holes).\n"
59 " By default, each line of the listing takes the following form:\n"
60 " extent: [startoffset..endoffset]: startblock..endblock\n"
61 " Holes are marked by replacing the startblock..endblock with 'hole'.\n"
62 " All the file offsets and disk blocks are in units of 512-byte blocks.\n"
63 " -a -- prints the attribute fork map instead of the data fork.\n"
64 " -d -- suppresses a DMAPI read event, offline portions shown as holes.\n"
65 " -l -- also displays the length of each extent in 512-byte blocks.\n"
66 " Note: the bmap for non-regular files can be obtained provided the file\n"
67 " was opened appropriately (in particular, must be opened read-only).\n"
68 "\n"));
69 }
70
71 static int
72 numlen(
73 off64_t val)
74 {
75 off64_t tmp;
76 int len;
77
78 for (len = 0, tmp = val; tmp > 0; tmp = tmp/10)
79 len++;
80 return (len == 0 ? 1 : len);
81 }
82
83 int
84 bmap_f(
85 int argc,
86 char **argv)
87 {
88 struct fsxattr fsx;
89 struct getbmapx *map;
90 struct xfs_fsop_geom_v1 fsgeo;
91 int map_size;
92 int loop = 0;
93 int flg = 0;
94 int aflag = 0;
95 int lflag = 0;
96 int nflag = 0;
97 int vflag = 0;
98 int bmv_iflags = 0; /* flags for XFS_IOC_GETBMAPX */
99 int i = 0;
100 int c;
101
102 while ((c = getopt(argc, argv, "adln:pv")) != EOF) {
103 switch (c) {
104 case 'a': /* Attribute fork. */
105 bmv_iflags |= BMV_IF_ATTRFORK;
106 aflag = 1;
107 break;
108 case 'l': /* list number of blocks with each extent */
109 lflag = 1;
110 break;
111 case 'n': /* number of extents specified */
112 nflag = atoi(optarg);
113 break;
114 case 'd':
115 /* do not recall possibly offline DMAPI files */
116 bmv_iflags |= BMV_IF_NO_DMAPI_READ;
117 break;
118 case 'p':
119 /* report unwritten preallocated blocks */
120 bmv_iflags |= BMV_IF_PREALLOC;
121 break;
122 case 'v': /* Verbose output */
123 vflag++;
124 break;
125 default:
126 return usage();
127 }
128 }
129 if (aflag)
130 bmv_iflags &= ~(BMV_IF_PREALLOC|BMV_IF_NO_DMAPI_READ);
131
132 if (vflag) {
133 if (xfsctl(fname, fdesc, XFS_IOC_FSGEOMETRY_V1, &fsgeo) < 0) {
134 fprintf(stderr,
135 _("%s: can't get geometry [\"%s\"]: %s\n"),
136 progname, fname, strerror(errno));
137 exitcode = 1;
138 return 0;
139 }
140 if ((xfsctl(fname, fdesc, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
141 fprintf(stderr,
142 _("%s: cannot read attrs on \"%s\": %s\n"),
143 progname, fname, strerror(errno));
144 exitcode = 1;
145 return 0;
146 }
147
148 if (fsx.fsx_xflags == XFS_XFLAG_REALTIME) {
149 /*
150 * ag info not applicable to rt, continue
151 * without ag output.
152 */
153 vflag = 0;
154 }
155 }
156
157 map_size = nflag ? nflag+1 : 32; /* initial guess - 256 */
158 map = malloc(map_size*sizeof(*map));
159 if (map == NULL) {
160 fprintf(stderr, _("%s: malloc of %d bytes failed.\n"),
161 progname, (int)(map_size * sizeof(*map)));
162 exitcode = 1;
163 return 0;
164 }
165
166
167 /* Try the xfsctl(XFS_IOC_GETBMAPX) for the number of extents specified
168 * by nflag, or the initial guess number of extents (256).
169 *
170 * If there are more extents than we guessed, use xfsctl
171 * (XFS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
172 * space based on this count, and try again.
173 *
174 * If the initial FGETBMAPX attempt returns EINVAL, this may mean
175 * that we tried the FGETBMAPX on a zero length file. If we get
176 * EINVAL, check the length with fstat() and return "no extents"
177 * if the length == 0.
178 *
179 * Why not do the xfsctl(XFS_IOC_FSGETXATTR[A]) first? Two reasons:
180 * (1) The extent count may be wrong for a file with delayed
181 * allocation blocks. The XFS_IOC_GETBMAPX forces the real
182 * allocation and fixes up the extent count.
183 * (2) For XFS_IOC_GETBMAP[X] on a DMAPI file that has been moved
184 * offline by a DMAPI application (e.g., DMF) the
185 * XFS_IOC_FSGETXATTR only reflects the extents actually online.
186 * Doing XFS_IOC_GETBMAPX call first forces that data blocks online
187 * and then everything proceeds normally (see PV #545725).
188 *
189 * If you don't want this behavior on a DMAPI offline file,
190 * try the "-d" option which sets the BMV_IF_NO_DMAPI_READ
191 * iflag for XFS_IOC_GETBMAPX.
192 */
193
194 do { /* loop a miximum of two times */
195
196 bzero(map, sizeof(*map)); /* zero header */
197
198 map->bmv_length = -1;
199 map->bmv_count = map_size;
200 map->bmv_iflags = bmv_iflags;
201
202 i = xfsctl(fname, fdesc, XFS_IOC_GETBMAPX, map);
203 if (i < 0) {
204 if ( errno == EINVAL
205 && !aflag && filesize() == 0) {
206 break;
207 } else {
208 fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETBMAPX)"
209 " iflags=0x%x [\"%s\"]: %s\n"),
210 progname, map->bmv_iflags, fname,
211 strerror(errno));
212 free(map);
213 exitcode = 1;
214 return 0;
215 }
216 }
217 if (nflag)
218 break;
219 if (map->bmv_entries < map->bmv_count-1)
220 break;
221 /* Get number of extents from xfsctl XFS_IOC_FSGETXATTR[A]
222 * syscall.
223 */
224 i = xfsctl(fname, fdesc, aflag ?
225 XFS_IOC_FSGETXATTRA : XFS_IOC_FSGETXATTR, &fsx);
226 if (i < 0) {
227 fprintf(stderr, "%s: xfsctl(XFS_IOC_FSGETXATTR%s) "
228 "[\"%s\"]: %s\n", progname, aflag ? "A" : "",
229 fname, strerror(errno));
230 free(map);
231 exitcode = 1;
232 return 0;
233 }
234 if (fsx.fsx_nextents >= map_size-1) {
235 map_size = 2*(fsx.fsx_nextents+1);
236 map = realloc(map, map_size*sizeof(*map));
237 if (map == NULL) {
238 fprintf(stderr,
239 _("%s: cannot realloc %d bytes\n"),
240 progname, (int)(map_size*sizeof(*map)));
241 exitcode = 1;
242 return 0;
243 }
244 }
245 } while (++loop < 2);
246 if (!nflag) {
247 if (map->bmv_entries <= 0) {
248 printf(_("%s: no extents\n"), fname);
249 free(map);
250 return 0;
251 }
252 }
253 printf("%s:\n", fname);
254 if (!vflag) {
255 for (i = 0; i < map->bmv_entries; i++) {
256 printf("\t%d: [%lld..%lld]: ", i,
257 (long long) map[i + 1].bmv_offset,
258 (long long)(map[i + 1].bmv_offset +
259 map[i + 1].bmv_length - 1LL));
260 if (map[i + 1].bmv_block == -1)
261 printf(_("hole"));
262 else {
263 printf("%lld..%lld",
264 (long long) map[i + 1].bmv_block,
265 (long long)(map[i + 1].bmv_block +
266 map[i + 1].bmv_length - 1LL));
267
268 }
269 if (lflag)
270 printf(_(" %lld blocks\n"),
271 (long long)map[i+1].bmv_length);
272 else
273 printf("\n");
274 }
275 } else {
276 /*
277 * Verbose mode displays:
278 * extent: [startoffset..endoffset]: startblock..endblock \
279 * ag# (agoffset..agendoffset) totalbbs
280 */
281 #define MINRANGE_WIDTH 16
282 #define MINAG_WIDTH 2
283 #define MINTOT_WIDTH 5
284 #define max(a,b) (a > b ? a : b)
285 #define FLG_NULL 00000 /* Null flag */
286 #define FLG_BSU 01000 /* Not on begin of stripe unit */
287 #define FLG_ESU 00100 /* Not on end of stripe unit */
288 #define FLG_BSW 00010 /* Not on begin of stripe width */
289 #define FLG_ESW 00001 /* Not on end of stripe width */
290 int agno;
291 off64_t agoff, bbperag;
292 int foff_w, boff_w, aoff_w, tot_w, agno_w;
293 char rbuf[32], bbuf[32], abuf[32];
294 int sunit, swidth;
295
296 foff_w = boff_w = aoff_w = MINRANGE_WIDTH;
297 tot_w = MINTOT_WIDTH;
298 bbperag = (off64_t)fsgeo.agblocks *
299 (off64_t)fsgeo.blocksize / BBSIZE;
300 sunit = fsgeo.sunit;
301 swidth = fsgeo.swidth;
302
303 /*
304 * Go through the extents and figure out the width
305 * needed for all columns.
306 */
307 for (i = 0; i < map->bmv_entries; i++) {
308 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
309 (long long) map[i + 1].bmv_offset,
310 (long long)(map[i + 1].bmv_offset +
311 map[i + 1].bmv_length - 1LL));
312 if (map[i + 1].bmv_block == -1) {
313 foff_w = max(foff_w, strlen(rbuf));
314 tot_w = max(tot_w,
315 numlen(map[i+1].bmv_length));
316 } else {
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 agno = map[i + 1].bmv_block / bbperag;
322 agoff = map[i + 1].bmv_block - (agno * bbperag);
323 snprintf(abuf, sizeof(abuf), "(%lld..%lld)",
324 (long long)agoff, (long long)
325 (agoff + map[i + 1].bmv_length - 1LL));
326 foff_w = max(foff_w, strlen(rbuf));
327 boff_w = max(boff_w, strlen(bbuf));
328 aoff_w = max(aoff_w, strlen(abuf));
329 tot_w = max(tot_w,
330 numlen(map[i+1].bmv_length));
331 }
332 }
333 agno_w = max(MINAG_WIDTH, numlen(fsgeo.agcount));
334 printf("%4s: %-*s %-*s %*s %-*s %*s\n",
335 _("EXT"),
336 foff_w, _("FILE-OFFSET"),
337 boff_w, _("BLOCK-RANGE"),
338 agno_w, _("AG"),
339 aoff_w, _("AG-OFFSET"),
340 tot_w, _("TOTAL"));
341 for (i = 0; i < map->bmv_entries; i++) {
342 if (sunit) {
343 flg = FLG_NULL;
344 if (map[i + 1].bmv_block % sunit != 0) {
345 flg |= FLG_BSU;
346 }
347 if (((map[i + 1].bmv_block +
348 map[i + 1].bmv_length ) % sunit ) != 0) {
349 flg |= FLG_ESU;
350 }
351 if (map[i + 1].bmv_block % swidth != 0) {
352 flg |= FLG_BSW;
353 }
354 if (((map[i + 1].bmv_block +
355 map[i + 1].bmv_length ) % swidth ) != 0) {
356 flg |= FLG_ESW;
357 }
358 }
359 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
360 (long long) map[i + 1].bmv_offset,
361 (long long)(map[i + 1].bmv_offset +
362 map[i + 1].bmv_length - 1LL));
363 if (map[i + 1].bmv_block == -1) {
364 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
365 i,
366 foff_w, rbuf,
367 boff_w, _("hole"),
368 agno_w, "",
369 aoff_w, "",
370 tot_w, (long long)map[i+1].bmv_length);
371 } else {
372 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
373 (long long) map[i + 1].bmv_block,
374 (long long)(map[i + 1].bmv_block +
375 map[i + 1].bmv_length - 1LL));
376 agno = map[i + 1].bmv_block / bbperag;
377 agoff = map[i + 1].bmv_block - (agno * bbperag);
378 snprintf(abuf, sizeof(abuf), "(%lld..%lld)",
379 (long long)agoff, (long long)
380 (agoff + map[i + 1].bmv_length - 1LL));
381 printf("%4d: %-*s %-*s %*d %-*s %*lld",
382 i,
383 foff_w, rbuf,
384 boff_w, bbuf,
385 agno_w, agno,
386 aoff_w, abuf,
387 tot_w, (long long)map[i+1].bmv_length);
388 if (flg == FLG_NULL) {
389 printf("\n");
390 } else {
391 printf(" %-4.4o\n", flg);
392 }
393 }
394 }
395 if ( flg ) {
396 printf(_(" FLG Values:\n"));
397 printf(_(" %5.5o Doesn't begin on stripe unit\n"),
398 FLG_BSU);
399 printf(_(" %5.5o Doesn't end on stripe unit\n"),
400 FLG_ESU);
401 printf(_(" %5.5o Doesn't begin on stripe width\n"),
402 FLG_BSW);
403 printf(_(" %5.5o Doesn't end on stripe width\n"),
404 FLG_ESW);
405 }
406 }
407 free(map);
408 return 0;
409 }
410
411 void
412 bmap_init(void)
413 {
414 bmap_cmd.name = _("bmap");
415 bmap_cmd.cfunc = bmap_f;
416 bmap_cmd.argmin = 0;
417 bmap_cmd.argmax = -1;
418 bmap_cmd.args = _("[-adlpv] [-n nx]");
419 bmap_cmd.oneline = _("print block mapping for an XFS file");
420 bmap_cmd.help = bmap_help;
421
422 add_command(&bmap_cmd);
423 }