]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - io/fsmap.c
libfrog: convert fsgeom.c functions to negative error codes
[thirdparty/xfsprogs-dev.git] / io / fsmap.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
3fcab549
DW
2/*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
3fcab549 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
3fcab549
DW
5 */
6#include "platform_defs.h"
7#include "command.h"
8#include "init.h"
42b4c8e8 9#include "libfrog/paths.h"
3fcab549
DW
10#include "io.h"
11#include "input.h"
fee68490 12#include "libfrog/fsgeom.h"
3fcab549
DW
13
14static cmdinfo_t fsmap_cmd;
15static dev_t xfs_data_dev;
16
17static void
18fsmap_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"
2fc6b167
DW
38"\n"
39"The optional start and end arguments require one of -d, -l, or -r to be set.\n"
3fcab549
DW
40"\n"));
41}
42
43#define OWNER_BUF_SZ 32
44static const char *
45special_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
77static void
78dump_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
110static void
111dump_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 */
161static void
162dump_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;
41198ef1
DC
174 char rbuf[40], bbuf[40], abuf[40], obuf[40];
175 char nbuf[40], dbuf[40], gbuf[40];
3fcab549
DW
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
348static void
349dump_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
00ff2b10 368static int
3fcab549
DW
369fsmap_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) {
03d96c64 451 c = -xfrog_geometry(file->fd, &fsgeo);
9612817d 452 if (c) {
3fcab549
DW
453 fprintf(stderr,
454 _("%s: can't get geometry [\"%s\"]: %s\n"),
03d96c64 455 progname, file->name, strerror(c));
3fcab549
DW
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
565void
566fsmap_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}