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