]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/fsmap.c
libfrog: convert fsgeom.c functions to negative error codes
[thirdparty/xfsprogs-dev.git] / io / fsmap.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "platform_defs.h"
7 #include "command.h"
8 #include "init.h"
9 #include "libfrog/paths.h"
10 #include "io.h"
11 #include "input.h"
12 #include "libfrog/fsgeom.h"
13
14 static cmdinfo_t fsmap_cmd;
15 static dev_t xfs_data_dev;
16
17 static void
18 fsmap_help(void)
19 {
20 printf(_(
21 "\n"
22 " Prints the block mapping for the filesystem hosting the current file"
23 "\n"
24 " fsmap prints the map of disk blocks used by the whole filesystem.\n"
25 " When possible, owner and offset information will be included in the\n"
26 " space report.\n"
27 "\n"
28 " By default, each line of the listing takes the following form:\n"
29 " extent: major:minor [startblock..endblock]: owner startoffset..endoffset length\n"
30 " The owner field is either an inode number or a special value.\n"
31 " All the file offsets and disk blocks are in units of 512-byte blocks.\n"
32 " -d -- query only the data device (default).\n"
33 " -l -- query only the log device.\n"
34 " -r -- query only the realtime device.\n"
35 " -n -- query n extents at a time.\n"
36 " -m -- output machine-readable format.\n"
37 " -v -- Verbose information, show AG and offsets. Show flags legend on 2nd -v\n"
38 "\n"
39 "The optional start and end arguments require one of -d, -l, or -r to be set.\n"
40 "\n"));
41 }
42
43 #define OWNER_BUF_SZ 32
44 static const char *
45 special_owner(
46 int64_t owner,
47 char *buf)
48 {
49 switch (owner) {
50 case XFS_FMR_OWN_FREE:
51 return _("free space");
52 case XFS_FMR_OWN_UNKNOWN:
53 return _("unknown");
54 case XFS_FMR_OWN_FS:
55 return _("static fs metadata");
56 case XFS_FMR_OWN_LOG:
57 return _("journalling log");
58 case XFS_FMR_OWN_AG:
59 return _("per-AG metadata");
60 case XFS_FMR_OWN_INOBT:
61 return _("inode btree");
62 case XFS_FMR_OWN_INODES:
63 return _("inodes");
64 case XFS_FMR_OWN_REFC:
65 return _("refcount btree");
66 case XFS_FMR_OWN_COW:
67 return _("cow reservation");
68 case XFS_FMR_OWN_DEFECTIVE:
69 return _("defective");
70 default:
71 snprintf(buf, OWNER_BUF_SZ, _("special %u:%u"),
72 FMR_OWNER_TYPE(owner), FMR_OWNER_CODE(owner));
73 return buf;
74 }
75 }
76
77 static void
78 dump_map(
79 unsigned long long *nr,
80 struct fsmap_head *head)
81 {
82 unsigned long long i;
83 struct fsmap *p;
84 char owner[OWNER_BUF_SZ];
85 char *fork;
86
87 for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
88 printf("\t%llu: %u:%u [%lld..%lld]: ", i + (*nr),
89 major(p->fmr_device), minor(p->fmr_device),
90 (long long)BTOBBT(p->fmr_physical),
91 (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
92 fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? _("attr") : _("data");
93 if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
94 printf("%s", special_owner(p->fmr_owner, owner));
95 else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
96 printf(_("inode %lld %s extent map"),
97 (long long) p->fmr_owner, fork);
98 else
99 printf(_("inode %lld %s %lld..%lld"),
100 (long long)p->fmr_owner, fork,
101 (long long)BTOBBT(p->fmr_offset),
102 (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
103 printf(_(" %lld\n"),
104 (long long)BTOBBT(p->fmr_length));
105 }
106
107 (*nr) += head->fmh_entries;
108 }
109
110 static void
111 dump_map_machine(
112 unsigned long long *nr,
113 struct fsmap_head *head)
114 {
115 unsigned long long i;
116 struct fsmap *p;
117 char *fork;
118
119 printf(_("EXT,MAJOR,MINOR,PSTART,PEND,OWNER,OSTART,OEND,LENGTH\n"));
120 for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
121 printf("%llu,%u,%u,%lld,%lld,", i + (*nr),
122 major(p->fmr_device), minor(p->fmr_device),
123 (long long)BTOBBT(p->fmr_physical),
124 (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
125 fork = (p->fmr_flags & FMR_OF_ATTR_FORK) ? "attr" : "data";
126 if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
127 printf("special_%u:%u,,,", FMR_OWNER_TYPE(p->fmr_owner),
128 FMR_OWNER_CODE(p->fmr_owner));
129 else if (p->fmr_flags & FMR_OF_EXTENT_MAP)
130 printf(_("inode_%lld_%s_bmbt,,,"),
131 (long long) p->fmr_owner, fork);
132 else
133 printf(_("inode_%lld_%s,%lld,%lld,"),
134 (long long)p->fmr_owner, fork,
135 (long long)BTOBBT(p->fmr_offset),
136 (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
137 printf("%lld\n",
138 (long long)BTOBBT(p->fmr_length));
139 }
140
141 (*nr) += head->fmh_entries;
142 }
143
144 /*
145 * Verbose mode displays:
146 * extent: major:minor [startblock..endblock]: startoffset..endoffset \
147 * ag# (agoffset..agendoffset) totalbbs flags
148 */
149 #define MINRANGE_WIDTH 16
150 #define MINAG_WIDTH 2
151 #define MINTOT_WIDTH 5
152 #define NFLG 7 /* count of flags */
153 #define FLG_NULL 00000000 /* Null flag */
154 #define FLG_ATTR_FORK 01000000 /* attribute fork */
155 #define FLG_SHARED 00100000 /* shared extent */
156 #define FLG_PRE 00010000 /* Unwritten extent */
157 #define FLG_BSU 00001000 /* Not on begin of stripe unit */
158 #define FLG_ESU 00000100 /* Not on end of stripe unit */
159 #define FLG_BSW 00000010 /* Not on begin of stripe width */
160 #define FLG_ESW 00000001 /* Not on end of stripe width */
161 static void
162 dump_map_verbose(
163 unsigned long long *nr,
164 struct fsmap_head *head,
165 bool *dumped_flags,
166 struct xfs_fsop_geom *fsgeo)
167 {
168 unsigned long long i;
169 struct fsmap *p;
170 int agno;
171 off64_t agoff, bperag;
172 int foff_w, boff_w, aoff_w, tot_w, agno_w, own_w;
173 int nr_w, dev_w;
174 char rbuf[40], bbuf[40], abuf[40], obuf[40];
175 char nbuf[40], dbuf[40], gbuf[40];
176 char owner[OWNER_BUF_SZ];
177 int sunit, swidth;
178 int flg = 0;
179
180 foff_w = boff_w = aoff_w = own_w = MINRANGE_WIDTH;
181 dev_w = 3;
182 nr_w = 4;
183 tot_w = MINTOT_WIDTH;
184 bperag = (off64_t)fsgeo->agblocks *
185 (off64_t)fsgeo->blocksize;
186 sunit = (fsgeo->sunit * fsgeo->blocksize);
187 swidth = (fsgeo->swidth * fsgeo->blocksize);
188
189 /*
190 * Go through the extents and figure out the width
191 * needed for all columns.
192 */
193 for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
194 if (p->fmr_flags & FMR_OF_PREALLOC ||
195 p->fmr_flags & FMR_OF_ATTR_FORK ||
196 p->fmr_flags & FMR_OF_SHARED)
197 flg = 1;
198 if (sunit &&
199 (p->fmr_physical % sunit != 0 ||
200 ((p->fmr_physical + p->fmr_length) % sunit) != 0 ||
201 p->fmr_physical % swidth != 0 ||
202 ((p->fmr_physical + p->fmr_length) % swidth) != 0))
203 flg = 1;
204 if (flg)
205 *dumped_flags = true;
206 snprintf(nbuf, sizeof(nbuf), "%llu", (*nr) + i);
207 nr_w = max(nr_w, strlen(nbuf));
208 if (head->fmh_oflags & FMH_OF_DEV_T)
209 snprintf(dbuf, sizeof(dbuf), "%u:%u",
210 major(p->fmr_device),
211 minor(p->fmr_device));
212 else
213 snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
214 dev_w = max(dev_w, strlen(dbuf));
215 snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
216 (long long)BTOBBT(p->fmr_physical),
217 (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
218 boff_w = max(boff_w, strlen(bbuf));
219 if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
220 own_w = max(own_w, strlen(
221 special_owner(p->fmr_owner, owner)));
222 else {
223 snprintf(obuf, sizeof(obuf), "%lld",
224 (long long)p->fmr_owner);
225 own_w = max(own_w, strlen(obuf));
226 }
227 if (p->fmr_flags & FMR_OF_EXTENT_MAP)
228 foff_w = max(foff_w, strlen(_("extent_map")));
229 else if (p->fmr_flags & FMR_OF_SPECIAL_OWNER)
230 ;
231 else {
232 snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
233 (long long)BTOBBT(p->fmr_offset),
234 (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
235 foff_w = max(foff_w, strlen(rbuf));
236 }
237 if (p->fmr_device == xfs_data_dev) {
238 agno = p->fmr_physical / bperag;
239 agoff = p->fmr_physical - (agno * bperag);
240 snprintf(abuf, sizeof(abuf),
241 "(%lld..%lld)",
242 (long long)BTOBBT(agoff),
243 (long long)BTOBBT(agoff + p->fmr_length - 1));
244 } else
245 abuf[0] = 0;
246 aoff_w = max(aoff_w, strlen(abuf));
247 tot_w = max(tot_w,
248 numlen(BTOBBT(p->fmr_length), 10));
249 }
250 agno_w = max(MINAG_WIDTH, numlen(fsgeo->agcount, 10));
251 if (*nr == 0)
252 printf("%*s: %-*s %-*s %-*s %-*s %*s %-*s %*s%s\n",
253 nr_w, _("EXT"),
254 dev_w, _("DEV"),
255 boff_w, _("BLOCK-RANGE"),
256 own_w, _("OWNER"),
257 foff_w, _("FILE-OFFSET"),
258 agno_w, _("AG"),
259 aoff_w, _("AG-OFFSET"),
260 tot_w, _("TOTAL"),
261 flg ? _(" FLAGS") : "");
262 for (i = 0, p = head->fmh_recs; i < head->fmh_entries; i++, p++) {
263 flg = FLG_NULL;
264 if (p->fmr_flags & FMR_OF_PREALLOC)
265 flg |= FLG_PRE;
266 if (p->fmr_flags & FMR_OF_ATTR_FORK)
267 flg |= FLG_ATTR_FORK;
268 if (p->fmr_flags & FMR_OF_SHARED)
269 flg |= FLG_SHARED;
270 /*
271 * If striping enabled, determine if extent starts/ends
272 * on a stripe unit boundary.
273 */
274 if (sunit) {
275 if (p->fmr_physical % sunit != 0)
276 flg |= FLG_BSU;
277 if (((p->fmr_physical +
278 p->fmr_length ) % sunit ) != 0)
279 flg |= FLG_ESU;
280 if (p->fmr_physical % swidth != 0)
281 flg |= FLG_BSW;
282 if (((p->fmr_physical +
283 p->fmr_length ) % swidth ) != 0)
284 flg |= FLG_ESW;
285 }
286 if (head->fmh_oflags & FMH_OF_DEV_T)
287 snprintf(dbuf, sizeof(dbuf), "%u:%u",
288 major(p->fmr_device),
289 minor(p->fmr_device));
290 else
291 snprintf(dbuf, sizeof(dbuf), "0x%x", p->fmr_device);
292 snprintf(bbuf, sizeof(bbuf), "[%lld..%lld]:",
293 (long long)BTOBBT(p->fmr_physical),
294 (long long)BTOBBT(p->fmr_physical + p->fmr_length - 1));
295 if (p->fmr_flags & FMR_OF_SPECIAL_OWNER) {
296 snprintf(obuf, sizeof(obuf), "%s",
297 special_owner(p->fmr_owner, owner));
298 snprintf(rbuf, sizeof(rbuf), " ");
299 } else {
300 snprintf(obuf, sizeof(obuf), "%lld",
301 (long long)p->fmr_owner);
302 snprintf(rbuf, sizeof(rbuf), "%lld..%lld",
303 (long long)BTOBBT(p->fmr_offset),
304 (long long)BTOBBT(p->fmr_offset + p->fmr_length - 1));
305 }
306 if (p->fmr_device == xfs_data_dev) {
307 agno = p->fmr_physical / bperag;
308 agoff = p->fmr_physical - (agno * bperag);
309 snprintf(abuf, sizeof(abuf),
310 "(%lld..%lld)",
311 (long long)BTOBBT(agoff),
312 (long long)BTOBBT(agoff + p->fmr_length - 1));
313 snprintf(gbuf, sizeof(gbuf),
314 "%lld",
315 (long long)agno);
316 } else {
317 abuf[0] = 0;
318 gbuf[0] = 0;
319 }
320 if (p->fmr_flags & FMR_OF_EXTENT_MAP)
321 printf("%*llu: %-*s %-*s %-*s %-*s %-*s %-*s %*lld\n",
322 nr_w, (*nr) + i,
323 dev_w, dbuf,
324 boff_w, bbuf,
325 own_w, obuf,
326 foff_w, _("extent map"),
327 agno_w, gbuf,
328 aoff_w, abuf,
329 tot_w, (long long)BTOBBT(p->fmr_length));
330 else {
331 printf("%*llu: %-*s %-*s %-*s %-*s", nr_w, (*nr) + i,
332 dev_w, dbuf, boff_w, bbuf, own_w, obuf,
333 foff_w, rbuf);
334 printf(" %-*s %-*s", agno_w, gbuf,
335 aoff_w, abuf);
336 printf(" %*lld", tot_w,
337 (long long)BTOBBT(p->fmr_length));
338 if (flg == FLG_NULL)
339 printf("\n");
340 else
341 printf(" %-*.*o\n", NFLG, NFLG, flg);
342 }
343 }
344
345 (*nr) += head->fmh_entries;
346 }
347
348 static void
349 dump_verbose_key(void)
350 {
351 printf(_(" FLAG Values:\n"));
352 printf(_(" %*.*o Attribute fork\n"),
353 NFLG+1, NFLG+1, FLG_ATTR_FORK);
354 printf(_(" %*.*o Shared extent\n"),
355 NFLG+1, NFLG+1, FLG_SHARED);
356 printf(_(" %*.*o Unwritten preallocated extent\n"),
357 NFLG+1, NFLG+1, FLG_PRE);
358 printf(_(" %*.*o Doesn't begin on stripe unit\n"),
359 NFLG+1, NFLG+1, FLG_BSU);
360 printf(_(" %*.*o Doesn't end on stripe unit\n"),
361 NFLG+1, NFLG+1, FLG_ESU);
362 printf(_(" %*.*o Doesn't begin on stripe width\n"),
363 NFLG+1, NFLG+1, FLG_BSW);
364 printf(_(" %*.*o Doesn't end on stripe width\n"),
365 NFLG+1, NFLG+1, FLG_ESW);
366 }
367
368 static int
369 fsmap_f(
370 int argc,
371 char **argv)
372 {
373 struct fsmap *p;
374 struct fsmap_head *nhead;
375 struct fsmap_head *head;
376 struct fsmap *l, *h;
377 struct xfs_fsop_geom fsgeo;
378 long long start = 0;
379 long long end = -1;
380 int nmap_size;
381 int map_size;
382 int nflag = 0;
383 int vflag = 0;
384 int mflag = 0;
385 int i = 0;
386 int c;
387 unsigned long long nr = 0;
388 size_t fsblocksize, fssectsize;
389 struct fs_path *fs;
390 static bool tab_init;
391 bool dumped_flags = false;
392 int dflag, lflag, rflag;
393
394 init_cvtnum(&fsblocksize, &fssectsize);
395
396 dflag = lflag = rflag = 0;
397 while ((c = getopt(argc, argv, "dlmn:rv")) != EOF) {
398 switch (c) {
399 case 'd': /* data device */
400 dflag = 1;
401 break;
402 case 'l': /* log device */
403 lflag = 1;
404 break;
405 case 'm': /* machine readable format */
406 mflag++;
407 break;
408 case 'n': /* number of extents specified */
409 nflag = cvt_u32(optarg, 10);
410 if (errno)
411 return command_usage(&fsmap_cmd);
412 break;
413 case 'r': /* rt device */
414 rflag = 1;
415 break;
416 case 'v': /* Verbose output */
417 vflag++;
418 break;
419 default:
420 return command_usage(&fsmap_cmd);
421 }
422 }
423
424 if ((dflag + lflag + rflag > 1) || (mflag > 0 && vflag > 0) ||
425 (argc > optind && dflag + lflag + rflag == 0))
426 return command_usage(&fsmap_cmd);
427
428 if (argc > optind) {
429 start = cvtnum(fsblocksize, fssectsize, argv[optind]);
430 if (start < 0) {
431 fprintf(stderr,
432 _("Bad rmap start_bblock %s.\n"),
433 argv[optind]);
434 return 0;
435 }
436 start <<= BBSHIFT;
437 }
438
439 if (argc > optind + 1) {
440 end = cvtnum(fsblocksize, fssectsize, argv[optind + 1]);
441 if (end < 0) {
442 fprintf(stderr,
443 _("Bad rmap end_bblock %s.\n"),
444 argv[optind + 1]);
445 return 0;
446 }
447 end <<= BBSHIFT;
448 }
449
450 if (vflag) {
451 c = -xfrog_geometry(file->fd, &fsgeo);
452 if (c) {
453 fprintf(stderr,
454 _("%s: can't get geometry [\"%s\"]: %s\n"),
455 progname, file->name, strerror(c));
456 exitcode = 1;
457 return 0;
458 }
459 }
460
461 map_size = nflag ? nflag : 131072 / sizeof(struct fsmap);
462 head = malloc(fsmap_sizeof(map_size));
463 if (head == NULL) {
464 fprintf(stderr, _("%s: malloc of %zu bytes failed.\n"),
465 progname, fsmap_sizeof(map_size));
466 exitcode = 1;
467 return 0;
468 }
469
470 memset(head, 0, sizeof(*head));
471 l = head->fmh_keys;
472 h = head->fmh_keys + 1;
473 if (dflag) {
474 l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
475 } else if (lflag) {
476 l->fmr_device = h->fmr_device = file->fs_path.fs_logdev;
477 } else if (rflag) {
478 l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
479 } else {
480 l->fmr_device = 0;
481 h->fmr_device = UINT_MAX;
482 }
483 l->fmr_physical = start;
484 h->fmr_physical = end;
485 h->fmr_owner = ULLONG_MAX;
486 h->fmr_flags = UINT_MAX;
487 h->fmr_offset = ULLONG_MAX;
488
489 /* Count mappings */
490 if (!nflag) {
491 head->fmh_count = 0;
492 i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
493 if (i < 0) {
494 fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
495 " iflags=0x%x [\"%s\"]: %s\n"),
496 progname, head->fmh_iflags, file->name,
497 strerror(errno));
498 free(head);
499 exitcode = 1;
500 return 0;
501 }
502 if (head->fmh_entries > map_size + 2) {
503 map_size = 11ULL * head->fmh_entries / 10;
504 nmap_size = map_size > (1 << 24) ? (1 << 24) : map_size;
505 nhead = realloc(head, fsmap_sizeof(nmap_size));
506 if (nhead == NULL) {
507 fprintf(stderr,
508 _("%s: cannot realloc %zu bytes\n"),
509 progname, fsmap_sizeof(nmap_size));
510 } else {
511 head = nhead;
512 map_size = nmap_size;
513 }
514 }
515 }
516
517 /*
518 * If this is an XFS filesystem, remember the data device.
519 * (We report AG number/block for data device extents on XFS).
520 */
521 if (!tab_init) {
522 fs_table_initialise(0, NULL, 0, NULL);
523 tab_init = true;
524 }
525 fs = fs_table_lookup(file->name, FS_MOUNT_POINT);
526 xfs_data_dev = fs ? fs->fs_datadev : 0;
527
528 head->fmh_count = map_size;
529 do {
530 /* Get some extents */
531 i = ioctl(file->fd, FS_IOC_GETFSMAP, head);
532 if (i < 0) {
533 fprintf(stderr, _("%s: xfsctl(XFS_IOC_GETFSMAP)"
534 " iflags=0x%x [\"%s\"]: %s\n"),
535 progname, head->fmh_iflags, file->name,
536 strerror(errno));
537 free(head);
538 exitcode = 1;
539 return 0;
540 }
541
542 if (head->fmh_entries == 0)
543 break;
544
545 if (vflag)
546 dump_map_verbose(&nr, head, &dumped_flags, &fsgeo);
547 else if (mflag)
548 dump_map_machine(&nr, head);
549 else
550 dump_map(&nr, head);
551
552 p = &head->fmh_recs[head->fmh_entries - 1];
553 if (p->fmr_flags & FMR_OF_LAST)
554 break;
555 fsmap_advance(head);
556 } while (true);
557
558 if (dumped_flags)
559 dump_verbose_key();
560
561 free(head);
562 return 0;
563 }
564
565 void
566 fsmap_init(void)
567 {
568 fsmap_cmd.name = "fsmap";
569 fsmap_cmd.cfunc = fsmap_f;
570 fsmap_cmd.argmin = 0;
571 fsmap_cmd.argmax = -1;
572 fsmap_cmd.flags = CMD_NOMAP_OK | CMD_FLAG_FOREIGN_OK;
573 fsmap_cmd.args = _("[-d|-l|-r] [-m|-v] [-n nx] [start] [end]");
574 fsmap_cmd.oneline = _("print filesystem mapping for a range of blocks");
575 fsmap_cmd.help = fsmap_help;
576
577 add_command(&fsmap_cmd);
578 }