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