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