]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/btdump.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / db / btdump.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 "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
14 static void
15 btdump_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"
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"
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
33 static int
34 eval(
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
53 static bool
54 btblock_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
63 static int
64 dump_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;
71 int ret = 0;
72
73 push_cur_and_set_type();
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
98 err:
99 pop_cur();
100 return ret;
101 }
102
103 static int
104 dump_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;
111 int ret = 0;
112
113 push_cur_and_set_type();
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
136 err:
137 pop_cur();
138 return ret;
139 }
140
141 static inline int dump_btree_short(bool dump_node_blocks)
142 {
143 return dump_btree(dump_node_blocks, false);
144 }
145
146 static inline int dump_btree_long(bool dump_node_blocks)
147 {
148 return dump_btree(dump_node_blocks, true);
149 }
150
151 static int
152 dump_inode(
153 bool dump_node_blocks,
154 bool attrfork)
155 {
156 char *prefix;
157 struct xfs_dinode *dip;
158 int ret = 0;
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
182 push_cur_and_set_type();
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
201 err:
202 pop_cur();
203 return ret;
204 }
205
206 static bool
207 dir_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
222 static int
223 dir_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
242 static int
243 dir3_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
262 static bool
263 attr_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
278 static int
279 attr_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
297 static int
298 attr3_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
316 struct 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
326 static 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
336 static 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
346 static 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
356 static 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
366 static int
367 dump_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;
374 int ret = 0;
375
376 push_cur_and_set_type();
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
396 err:
397 pop_cur();
398 return ret;
399 }
400
401 static int
402 dump_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;
409 int ret = 0;
410
411 push_cur_and_set_type();
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
439 err:
440 pop_cur();
441 return ret;
442 }
443
444 static int
445 btdump_f(
446 int argc,
447 char **argv)
448 {
449 bool aflag = false;
450 bool iflag = false;
451 bool crc = xfs_sb_version_hascrc(&mp->m_sb);
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);
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);
498 default:
499 dbprintf(_("type \"%s\" is not a btree type or inode\n"),
500 cur_typ->name);
501 return 0;
502 }
503 }
504
505 static const cmdinfo_t btdump_cmd =
506 { "btdump", "b", btdump_f, 0, 2, 0, "[-a] [-i]",
507 N_("dump btree"), btdump_help };
508
509 void
510 btdump_init(void)
511 {
512 add_command(&btdump_cmd);
513 }