]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - bmap/xfs_bmap.c
12ebfeb5f48966e903d5f01b1bf981f1e4918ab3
[thirdparty/xfsprogs-dev.git] / bmap / xfs_bmap.c
1 /*
2 * Copyright (c) 2000 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 /*
34 * Bmap display utility for xfs.
35 */
36
37 #include <libxfs.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
40 #include <sys/vfs.h>
41
42 int aflag = 0; /* Attribute fork. */
43 int lflag = 0; /* list number of blocks with each extent */
44 int nflag = 0; /* number of extents specified */
45 int vflag = 0; /* Verbose output */
46 int bmv_iflags = 0; /* Input flags for XFS_IOC_GETBMAPX */
47 char *progname;
48
49 int dofile(char *);
50 __off64_t file_size(int fd, char * fname);
51 int numlen(__off64_t);
52
53 int
54 main(int argc, char **argv)
55 {
56 char *fname;
57 int i = 0;
58 int option;
59
60 progname = basename(argv[0]);
61 while ((option = getopt(argc, argv, "adln:pvV")) != EOF) {
62 switch (option) {
63 case 'a':
64 bmv_iflags |= BMV_IF_ATTRFORK;
65 aflag = 1;
66 break;
67 case 'l':
68 lflag = 1;
69 break;
70 case 'n':
71 nflag = atoi(optarg);
72 break;
73 case 'd':
74 /* do not recall possibly offline DMAPI files */
75 bmv_iflags |= BMV_IF_NO_DMAPI_READ;
76 break;
77 case 'p':
78 /* report unwritten preallocated blocks */
79 bmv_iflags |= BMV_IF_PREALLOC;
80 break;
81 case 'v':
82 vflag++;
83 break;
84 case 'V':
85 printf("%s version %s\n", progname, VERSION);
86 exit(0);
87 default:
88 fprintf(stderr, "Usage: %s [-adlpV] [-n nx] file...\n",
89 progname);
90 exit(1);
91 }
92 }
93 if (aflag)
94 bmv_iflags &= ~(BMV_IF_PREALLOC|BMV_IF_NO_DMAPI_READ);
95 while (optind < argc) {
96 fname = argv[optind];
97 i += dofile(fname);
98 optind++;
99 }
100 return(i ? 1 : 0);
101 }
102
103 __off64_t
104 file_size(int fd, char *fname)
105 {
106 struct stat64 st;
107 int i;
108 int errno_save;
109
110 errno_save = errno; /* in case fstat64 fails */
111 i = fstat64(fd, &st);
112 if (i < 0) {
113 fprintf(stderr, "%s: fstat64 failed for %s: %s\n",
114 progname, fname, strerror(errno));
115 errno = errno_save;
116 return -1;
117 }
118 return st.st_size;
119 }
120
121
122 int
123 dofile(char *fname)
124 {
125 int i;
126 int fd;
127 struct statfs buf;
128 struct fsxattr fsx;
129 struct getbmapx *map;
130 int map_size;
131 int loop = 0;
132 xfs_fsop_geom_t fsgeo;
133
134 fd = open(fname, O_RDONLY);
135 if (fd < 0) {
136 fprintf(stderr, "%s: cannot open \"%s\": %s\n",
137 progname, fname, strerror(errno));
138 return 1;
139 }
140 fstatfs(fd, &buf);
141 if (buf.f_type != XFS_SUPER_MAGIC) {
142 fprintf(stderr, "%s: "
143 "specified file [\"%s\"] is not on an XFS filesystem\n",
144 progname, fname);
145 close(fd);
146 return 1;
147 }
148
149 if (vflag) {
150 if (ioctl(fd, XFS_IOC_FSGEOMETRY, &fsgeo) < 0) {
151 fprintf(stderr, "%s: can't get geometry [\"%s\"]: %s\n",
152 progname, fname, strerror(errno));
153 close(fd);
154 return 1;
155 }
156
157 if (vflag > 1)
158 printf(
159 "xfs_bmap: fsgeo.agblocks=%u, fsgeo.blocksize=%u, fsgeo.agcount=%u\n",
160 fsgeo.agblocks, fsgeo.blocksize,
161 fsgeo.agcount);
162
163 if ((ioctl(fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
164 fprintf(stderr, "%s: cannot read attrs on \"%s\": %s\n",
165 progname, fname, strerror(errno));
166 close(fd);
167 return 1;
168 }
169
170 if (vflag > 1)
171 printf(
172 "xfs_bmap: fsx.dsx_xflags=%u, fsx.fsx_extsize=%u, fsx.fsx_nextents=%u\n",
173 fsx.fsx_xflags, fsx.fsx_extsize,
174 fsx.fsx_nextents);
175
176 if (fsx.fsx_xflags == XFS_XFLAG_REALTIME) {
177 /*
178 * ag info not applicable to rt, continue
179 * without ag output.
180 */
181 vflag = 0;
182 }
183 }
184
185 map_size = nflag ? nflag+1 : 32; /* initial guess - 256 for checkin KCM */
186 map = malloc(map_size*sizeof(*map));
187 if (map == NULL) {
188 fprintf(stderr, "%s: malloc of %d bytes failed.\n",
189 progname, (int)(map_size * sizeof(*map)));
190 close(fd);
191 return 1;
192 }
193
194
195 /* Try the ioctl(XFS_IOC_GETBMAPX) for the number of extents specified by
196 * nflag, or the initial guess number of extents (256).
197 *
198 * If there are more extents than we guessed, use ioctl
199 * (XFS_IOC_FSGETXATTR[A]) to get the extent count, realloc some more
200 * space based on this count, and try again.
201 *
202 * If the initial FGETBMAPX attempt returns EINVAL, this may mean
203 * that we tried the FGETBMAPX on a zero length file. If we get
204 * EINVAL, check the length with fstat() and return "no extents"
205 * if the length == 0.
206 *
207 * Why not do the ioctl(XFS_IOC_FSGETXATTR[A]) first? Two reasons:
208 * (1) The extent count may be wrong for a file with delayed
209 * allocation blocks. The XFS_IOC_GETBMAPX forces the real
210 * allocation and fixes up the extent count.
211 * (2) For XFS_IOC_GETBMAP[X] on a DMAPI file that has been moved
212 * offline by a DMAPI application (e.g., DMF) the
213 * XFS_IOC_FSGETXATTR only reflects the extents actually online.
214 * Doing XFS_IOC_GETBMAPX call first forces that data blocks online
215 * and then everything proceeds normally (see PV #545725).
216 *
217 * If you don't want this behavior on a DMAPI offline file,
218 * try the "-d" option which sets the BMV_IF_NO_DMAPI_READ
219 * iflag for XFS_IOC_GETBMAPX.
220 */
221
222 do { /* loop a miximum of two times */
223
224 bzero(map, sizeof(*map)); /* zero header */
225
226 map->bmv_length = -1;
227 map->bmv_count = map_size;
228 map->bmv_iflags = bmv_iflags;
229
230 i = ioctl(fd, XFS_IOC_GETBMAPX, map);
231
232 if (vflag > 1)
233 printf(
234 "xfs_bmap: i=%d map.bmv_offset=%lld, map.bmv_block=%lld, "
235 "map.bmv_length=%lld, map.bmv_count=%d, map.bmv_entries=%d\n",
236 i, (long long)map->bmv_offset,
237 (long long)map->bmv_block,
238 (long long)map->bmv_length,
239 map->bmv_count, map->bmv_entries);
240 if (i < 0) {
241 if ( errno == EINVAL
242 && !aflag && file_size(fd, fname) == 0) {
243 break;
244 } else {
245 fprintf(stderr, "%s: ioctl(XFS_IOC_GETBMAPX) "
246 "iflags=0x%x [\"%s\"]: %s\n",
247 progname, map->bmv_iflags, fname,
248 strerror(errno));
249 close(fd);
250 free(map);
251 return 1;
252 }
253 }
254 if (nflag)
255 break;
256 if (map->bmv_entries < map->bmv_count-1)
257 break;
258 /* Get number of extents from ioctl XFS_IOC_FSGETXATTR[A]
259 * syscall.
260 */
261 i = ioctl(fd, aflag ? XFS_IOC_FSGETXATTRA : XFS_IOC_FSGETXATTR, &fsx);
262 if (i < 0) {
263 fprintf(stderr, "%s: ioctl(XFS_IOC_FSGETXATTR%s) "
264 "[\"%s\"]: %s\n", progname, aflag ? "A" : "",
265 fname, strerror(errno));
266 close(fd);
267 free(map);
268 return 1;
269 }
270 if (fsx.fsx_nextents >= map_size-1) {
271 map_size = 2*(fsx.fsx_nextents+1);
272 map = realloc(map, map_size*sizeof(*map));
273 if (map == NULL) {
274 fprintf(stderr, "%s: cannot realloc %d bytes\n",
275 progname, (int)(map_size*sizeof(*map)));
276 close(fd);
277 return 1;
278 }
279 }
280 } while (++loop < 2);
281 if (!nflag) {
282 if (map->bmv_entries <= 0) {
283 printf("%s: no extents\n", fname);
284 close(fd);
285 free(map);
286 return 0;
287 }
288 }
289 close(fd);
290 printf("%s:\n", fname);
291 if (!vflag) {
292 for (i = 0; i < map->bmv_entries; i++) {
293 printf("\t%d: [%lld..%lld]: ", i,
294 (long long) map[i + 1].bmv_offset,
295 (long long)(map[i + 1].bmv_offset +
296 map[i + 1].bmv_length - 1LL));
297 if (map[i + 1].bmv_block == -1)
298 printf("hole");
299 else {
300 printf("%lld..%lld",
301 (long long) map[i + 1].bmv_block,
302 (long long)(map[i + 1].bmv_block +
303 map[i + 1].bmv_length - 1LL));
304
305 }
306 if (lflag)
307 printf(" %lld blocks\n", (long long)map[i+1].bmv_length);
308 else
309 printf("\n");
310 }
311 } else {
312 /*
313 * Verbose mode displays:
314 * extent: [startoffset..endoffset]: startblock..endblock \
315 * ag# (agoffset..agendoffset) totalbbs
316 */
317 #define MINRANGE_WIDTH 16
318 #define MINAG_WIDTH 2
319 #define MINTOT_WIDTH 5
320 #define max(a,b) (a > b ? a : b)
321 int agno;
322 __off64_t agoff, bbperag;
323 int foff_w, boff_w, aoff_w, tot_w, agno_w;
324 char rbuf[32], bbuf[32], abuf[32];
325
326 foff_w = boff_w = aoff_w = MINRANGE_WIDTH;
327 tot_w = MINTOT_WIDTH;
328 bbperag = (__off64_t)fsgeo.agblocks *
329 (__off64_t)fsgeo.blocksize / BBSIZE;
330
331 /*
332 * Go through the extents and figure out the width
333 * needed for all columns.
334 */
335 for (i = 0; i < map->bmv_entries; i++) {
336 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
337 (long long) map[i + 1].bmv_offset,
338 (long long)(map[i + 1].bmv_offset +
339 map[i + 1].bmv_length - 1LL));
340 if (map[i + 1].bmv_block == -1) {
341 foff_w = max(foff_w, strlen(rbuf));
342 tot_w = max(tot_w,
343 numlen(map[i+1].bmv_length));
344 } else {
345 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
346 (long long) map[i + 1].bmv_block,
347 (long long)(map[i + 1].bmv_block +
348 map[i + 1].bmv_length - 1LL));
349 agno = map[i + 1].bmv_block / bbperag;
350 agoff = map[i + 1].bmv_block - (agno * bbperag);
351 snprintf(abuf, sizeof(abuf), "(%lld..%lld)",
352 (long long)agoff, (long long)
353 (agoff + map[i + 1].bmv_length - 1LL));
354 foff_w = max(foff_w, strlen(rbuf));
355 boff_w = max(boff_w, strlen(bbuf));
356 aoff_w = max(aoff_w, strlen(abuf));
357 tot_w = max(tot_w,
358 numlen(map[i+1].bmv_length));
359 }
360 }
361 agno_w = max(MINAG_WIDTH, numlen(fsgeo.agcount));
362 printf("%4s: %-*s %-*s %*s %-*s %*s\n",
363 "EXT",
364 foff_w, "FILE-OFFSET",
365 boff_w, "BLOCK-RANGE",
366 agno_w, "AG",
367 aoff_w, "AG-OFFSET",
368 tot_w, "TOTAL");
369 for (i = 0; i < map->bmv_entries; i++) {
370 snprintf(rbuf, sizeof(rbuf), "[%lld..%lld]:",
371 (long long) map[i + 1].bmv_offset,
372 (long long)(map[i + 1].bmv_offset +
373 map[i + 1].bmv_length - 1LL));
374 if (map[i + 1].bmv_block == -1) {
375 printf("%4d: %-*s %-*s %*s %-*s %*lld\n",
376 i,
377 foff_w, rbuf,
378 boff_w, "hole",
379 agno_w, "",
380 aoff_w, "",
381 tot_w, (long long)map[i+1].bmv_length);
382 } else {
383 snprintf(bbuf, sizeof(bbuf), "%lld..%lld",
384 (long long) map[i + 1].bmv_block,
385 (long long)(map[i + 1].bmv_block +
386 map[i + 1].bmv_length - 1LL));
387 agno = map[i + 1].bmv_block / bbperag;
388 agoff = map[i + 1].bmv_block - (agno * bbperag);
389 snprintf(abuf, sizeof(abuf), "(%lld..%lld)",
390 (long long)agoff, (long long)
391 (agoff + map[i + 1].bmv_length - 1LL));
392 printf("%4d: %-*s %-*s %*d %-*s %*lld\n",
393 i,
394 foff_w, rbuf,
395 boff_w, bbuf,
396 agno_w, agno,
397 aoff_w, abuf,
398 tot_w, (long long)map[i+1].bmv_length);
399 }
400 }
401 }
402 free(map);
403 return 0;
404 }
405
406 int
407 numlen( __off64_t val)
408 {
409 __off64_t tmp;
410 int len;
411
412 for (len=0, tmp=val; tmp > 0; tmp=tmp/10) len++;
413 return(len == 0 ? 1 : len);
414 }