]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/btdump.c
xfsprogs: Release v4.18.0
[thirdparty/xfsprogs-dev.git] / db / btdump.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
21f0bffe
DW
2/*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
21f0bffe 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
21f0bffe
DW
5 */
6#include "libxfs.h"
7#include "command.h"
8#include "output.h"
9#include "init.h"
10#include "io.h"
11#include "type.h"
12#include "input.h"
13
14static void
15btdump_help(void)
16{
17 dbprintf(_(
18"\n"
19" If the cursor points to a btree block, 'btdump' dumps the btree\n"
20" downward from that block. If the cursor points to an inode,\n"
a05feedb
DW
21" the data fork btree root is selected by default. If the cursor\n"
22" points to a directory or extended attribute btree node, the tree\n"
23" will be printed downward from that block.\n"
21f0bffe
DW
24"\n"
25" Options:\n"
26" -a -- Display an inode's extended attribute fork btree.\n"
27" -i -- Print internal btree nodes.\n"
28"\n"
29));
30
31}
32
33static int
34eval(
35 const char *fmt, ...)
36{
37 va_list ap;
38 char buf[PATH_MAX];
39 char **v;
40 int c;
41 int ret;
42
43 va_start(ap, fmt);
44 vsnprintf(buf, sizeof(buf), fmt, ap);
45 va_end(ap);
46
47 v = breakline(buf, &c);
48 ret = command(c, v);
49 free(v);
50 return ret;
51}
52
53static bool
54btblock_has_rightsib(
55 struct xfs_btree_block *block,
56 bool long_format)
57{
58 if (long_format)
59 return block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK);
60 return block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK);
61}
62
63static int
64dump_btlevel(
65 int level,
66 bool long_format)
67{
68 xfs_daddr_t orig_daddr = iocur_top->bb;
69 xfs_daddr_t last_daddr;
70 unsigned int nr;
3540b418 71 int ret = 0;
21f0bffe 72
3540b418 73 push_cur_and_set_type();
21f0bffe
DW
74
75 nr = 1;
76 do {
77 last_daddr = iocur_top->bb;
78 dbprintf(_("%s level %u block %u daddr %llu\n"),
79 iocur_top->typ->name, level, nr, last_daddr);
80 if (level > 0) {
81 ret = eval("print keys");
82 if (ret)
83 goto err;
84 ret = eval("print ptrs");
85 } else {
86 ret = eval("print recs");
87 }
88 if (ret)
89 goto err;
90 if (btblock_has_rightsib(iocur_top->data, long_format)) {
91 ret = eval("addr rightsib");
92 if (ret)
93 goto err;
94 }
95 nr++;
96 } while (iocur_top->bb != orig_daddr && iocur_top->bb != last_daddr);
97
21f0bffe 98err:
3540b418 99 pop_cur();
21f0bffe
DW
100 return ret;
101}
102
103static int
104dump_btree(
105 bool dump_node_blocks,
106 bool long_format)
107{
108 xfs_daddr_t orig_daddr = iocur_top->bb;
109 xfs_daddr_t last_daddr;
110 int level;
3540b418 111 int ret = 0;
21f0bffe 112
3540b418 113 push_cur_and_set_type();
21f0bffe
DW
114
115 cur_agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
116 level = xfs_btree_get_level(iocur_top->data);
117 do {
118 last_daddr = iocur_top->bb;
119 if (level > 0) {
120 if (dump_node_blocks) {
121 ret = dump_btlevel(level, long_format);
122 if (ret)
123 goto err;
124 }
125 ret = eval("addr ptrs[1]");
126 } else {
127 ret = dump_btlevel(level, long_format);
128 }
129 if (ret)
130 goto err;
131 level--;
132 } while (level >= 0 &&
133 iocur_top->bb != orig_daddr &&
134 iocur_top->bb != last_daddr);
135
21f0bffe 136err:
3540b418 137 pop_cur();
21f0bffe
DW
138 return ret;
139}
140
141static inline int dump_btree_short(bool dump_node_blocks)
142{
143 return dump_btree(dump_node_blocks, false);
144}
145
146static inline int dump_btree_long(bool dump_node_blocks)
147{
148 return dump_btree(dump_node_blocks, true);
149}
150
151static int
152dump_inode(
153 bool dump_node_blocks,
154 bool attrfork)
155{
156 char *prefix;
157 struct xfs_dinode *dip;
3540b418 158 int ret = 0;
21f0bffe
DW
159
160 if (attrfork)
161 prefix = "a.bmbt";
162 else if (xfs_sb_version_hascrc(&mp->m_sb))
163 prefix = "u3.bmbt";
164 else
165 prefix = "u.bmbt";
166
167 dip = iocur_top->data;
168 if (attrfork) {
169 if (!dip->di_anextents ||
170 dip->di_aformat != XFS_DINODE_FMT_BTREE) {
171 dbprintf(_("attr fork not in btree format\n"));
172 return 0;
173 }
174 } else {
175 if (!dip->di_nextents ||
176 dip->di_format != XFS_DINODE_FMT_BTREE) {
177 dbprintf(_("data fork not in btree format\n"));
178 return 0;
179 }
180 }
181
3540b418 182 push_cur_and_set_type();
21f0bffe
DW
183
184 if (dump_node_blocks) {
185 ret = eval("print %s.keys", prefix);
186 if (ret)
187 goto err;
188 ret = eval("print %s.ptrs", prefix);
189 if (ret)
190 goto err;
191 }
192
193 ret = eval("addr %s.ptrs[1]", prefix);
194 if (ret)
195 goto err;
196
197 ret = dump_btree_long(dump_node_blocks);
198 if (ret)
199 goto err;
200
21f0bffe 201err:
3540b418 202 pop_cur();
21f0bffe
DW
203 return ret;
204}
205
a05feedb
DW
206static bool
207dir_has_rightsib(
208 void *block,
209 int level)
210{
211 struct xfs_dir3_icleaf_hdr lhdr;
212 struct xfs_da3_icnode_hdr nhdr;
213
214 if (level > 0) {
215 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
216 return nhdr.forw != 0;
217 }
218 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
219 return lhdr.forw != 0;
220}
221
222static int
223dir_level(
224 void *block)
225{
226 struct xfs_dir3_icleaf_hdr lhdr;
227 struct xfs_da3_icnode_hdr nhdr;
228
229 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
230 case cpu_to_be16(XFS_DIR2_LEAF1_MAGIC):
231 case cpu_to_be16(XFS_DIR2_LEAFN_MAGIC):
232 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
233 return 0;
234 case cpu_to_be16(XFS_DA_NODE_MAGIC):
235 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
236 return nhdr.level;
237 default:
238 return -1;
239 }
240}
241
242static int
243dir3_level(
244 void *block)
245{
246 struct xfs_dir3_icleaf_hdr lhdr;
247 struct xfs_da3_icnode_hdr nhdr;
248
249 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
250 case cpu_to_be16(XFS_DIR3_LEAF1_MAGIC):
251 case cpu_to_be16(XFS_DIR3_LEAFN_MAGIC):
252 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
253 return 0;
254 case cpu_to_be16(XFS_DA3_NODE_MAGIC):
255 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
256 return nhdr.level;
257 default:
258 return -1;
259 }
260}
261
262static bool
263attr_has_rightsib(
264 void *block,
265 int level)
266{
267 struct xfs_attr_leafblock lhdr;
268 struct xfs_da3_icnode_hdr nhdr;
269
270 if (level > 0) {
271 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
272 return nhdr.forw != 0;
273 }
274 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
275 return lhdr.hdr.info.forw != 0;
276}
277
278static int
279attr_level(
280 void *block)
281{
282 struct xfs_attr_leafblock lhdr;
283 struct xfs_da3_icnode_hdr nhdr;
284
285 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
286 case cpu_to_be16(XFS_ATTR_LEAF_MAGIC):
287 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
288 return 0;
289 case cpu_to_be16(XFS_DA_NODE_MAGIC):
290 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
291 return nhdr.level;
292 default:
293 return -1;
294 }
295}
296
297static int
298attr3_level(
299 void *block)
300{
301 struct xfs_attr_leafblock lhdr;
302 struct xfs_da3_icnode_hdr nhdr;
303
304 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
305 case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
306 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
307 return 0;
308 case cpu_to_be16(XFS_DA3_NODE_MAGIC):
309 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
310 return nhdr.level;
311 default:
312 return -1;
313 }
314}
315
316struct dabprinter_ops {
317 const char *print_node_entries;
318 const char *print_leaf_entries;
319 const char *go_node_forward;
320 const char *go_leaf_forward;
321 const char *go_down;
322 bool (*has_rightsib)(void *, int);
323 int (*level)(void *);
324};
325
326static struct dabprinter_ops attr_print = {
327 .print_node_entries = "btree",
328 .print_leaf_entries = "entries nvlist",
329 .go_node_forward = "hdr.info.forw",
330 .go_leaf_forward = "hdr.info.forw",
331 .go_down = "btree[0].before",
332 .has_rightsib = attr_has_rightsib,
333 .level = attr_level,
334};
335
336static struct dabprinter_ops attr3_print = {
337 .print_node_entries = "btree",
338 .print_leaf_entries = "entries nvlist",
339 .go_node_forward = "hdr.info.hdr.forw",
340 .go_leaf_forward = "hdr.info.hdr.forw",
341 .go_down = "btree[0].before",
342 .has_rightsib = attr_has_rightsib,
343 .level = attr3_level,
344};
345
346static struct dabprinter_ops dir_print = {
347 .print_node_entries = "nbtree",
348 .print_leaf_entries = "lents",
349 .go_node_forward = "nhdr.info.hdr.forw",
350 .go_leaf_forward = "lhdr.info.hdr.forw",
351 .go_down = "nbtree[0].before",
352 .has_rightsib = dir_has_rightsib,
353 .level = dir_level,
354};
355
356static struct dabprinter_ops dir3_print = {
357 .print_node_entries = "nbtree",
358 .print_leaf_entries = "lents",
359 .go_node_forward = "nhdr.info.forw",
360 .go_leaf_forward = "lhdr.info.forw",
361 .go_down = "nbtree[0].before",
362 .has_rightsib = dir_has_rightsib,
363 .level = dir3_level,
364};
365
366static int
367dump_dablevel(
368 int level,
369 struct dabprinter_ops *dbp)
370{
371 xfs_daddr_t orig_daddr = iocur_top->bb;
372 xfs_daddr_t last_daddr;
373 unsigned int nr;
3540b418 374 int ret = 0;
a05feedb 375
3540b418 376 push_cur_and_set_type();
a05feedb
DW
377
378 nr = 1;
379 do {
380 last_daddr = iocur_top->bb;
381 dbprintf(_("%s level %u block %u daddr %llu\n"),
382 iocur_top->typ->name, level, nr, last_daddr);
383 ret = eval("print %s", level > 0 ? dbp->print_node_entries :
384 dbp->print_leaf_entries);
385 if (ret)
386 goto err;
387 if (dbp->has_rightsib(iocur_top->data, level)) {
388 ret = eval("addr %s", level > 0 ? dbp->go_node_forward :
389 dbp->go_leaf_forward);
390 if (ret)
391 goto err;
392 }
393 nr++;
394 } while (iocur_top->bb != orig_daddr && iocur_top->bb != last_daddr);
395
a05feedb 396err:
3540b418 397 pop_cur();
a05feedb
DW
398 return ret;
399}
400
401static int
402dump_dabtree(
403 bool dump_node_blocks,
404 struct dabprinter_ops *dbp)
405{
406 xfs_daddr_t orig_daddr = iocur_top->bb;
407 xfs_daddr_t last_daddr;
408 int level;
3540b418 409 int ret = 0;
a05feedb 410
3540b418 411 push_cur_and_set_type();
a05feedb
DW
412
413 cur_agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
414 level = dbp->level(iocur_top->data);
415 if (level < 0) {
416 printf(_("Current location is not part of a dir/attr btree.\n"));
417 goto err;
418 }
419
420 do {
421 last_daddr = iocur_top->bb;
422 if (level > 0) {
423 if (dump_node_blocks) {
424 ret = dump_dablevel(level, dbp);
425 if (ret)
426 goto err;
427 }
428 ret = eval("addr %s", dbp->go_down);
429 } else {
430 ret = dump_dablevel(level, dbp);
431 }
432 if (ret)
433 goto err;
434 level--;
435 } while (level >= 0 &&
436 iocur_top->bb != orig_daddr &&
437 iocur_top->bb != last_daddr);
438
a05feedb 439err:
3540b418 440 pop_cur();
a05feedb
DW
441 return ret;
442}
443
21f0bffe
DW
444static int
445btdump_f(
446 int argc,
447 char **argv)
448{
449 bool aflag = false;
450 bool iflag = false;
a05feedb 451 bool crc = xfs_sb_version_hascrc(&mp->m_sb);
21f0bffe
DW
452 int c;
453
454 if (cur_typ == NULL) {
455 dbprintf(_("no current type\n"));
456 return 0;
457 }
458 while ((c = getopt(argc, argv, "ai")) != EOF) {
459 switch (c) {
460 case 'a':
461 aflag = true;
462 break;
463 case 'i':
464 iflag = true;
465 break;
466 default:
467 dbprintf(_("bad option for btdump command\n"));
468 return 0;
469 }
470 }
471
472 if (optind != argc) {
473 dbprintf(_("bad options for btdump command\n"));
474 return 0;
475 }
476 if (aflag && cur_typ->typnm != TYP_INODE) {
477 dbprintf(_("attrfork flag doesn't apply here\n"));
478 return 0;
479 }
480
481 switch (cur_typ->typnm) {
482 case TYP_BNOBT:
483 case TYP_CNTBT:
484 case TYP_INOBT:
485 case TYP_FINOBT:
486 case TYP_RMAPBT:
487 case TYP_REFCBT:
488 return dump_btree_short(iflag);
489 case TYP_BMAPBTA:
490 case TYP_BMAPBTD:
491 return dump_btree_long(iflag);
492 case TYP_INODE:
493 return dump_inode(iflag, aflag);
a05feedb
DW
494 case TYP_ATTR:
495 return dump_dabtree(iflag, crc ? &attr3_print : &attr_print);
496 case TYP_DIR2:
497 return dump_dabtree(iflag, crc ? &dir3_print : &dir_print);
21f0bffe
DW
498 default:
499 dbprintf(_("type \"%s\" is not a btree type or inode\n"),
500 cur_typ->name);
501 return 0;
502 }
503}
504
505static const cmdinfo_t btdump_cmd =
506 { "btdump", "b", btdump_f, 0, 2, 0, "[-a] [-i]",
507 N_("dump btree"), btdump_help };
508
509void
510btdump_init(void)
511{
512 add_command(&btdump_cmd);
513}