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