]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/btdump.c
xfs_db: write values into dir/attr blocks and recalculate CRCs
[thirdparty/xfsprogs-dev.git] / db / btdump.c
CommitLineData
21f0bffe
DW
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
28static void
29btdump_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"
a05feedb
DW
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"
21f0bffe
DW
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
47static int
48eval(
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
67static bool
68btblock_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
77static int
78dump_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;
86
87 ret = eval("push");
88 if (ret)
89 return ret;
90
91 nr = 1;
92 do {
93 last_daddr = iocur_top->bb;
94 dbprintf(_("%s level %u block %u daddr %llu\n"),
95 iocur_top->typ->name, level, nr, last_daddr);
96 if (level > 0) {
97 ret = eval("print keys");
98 if (ret)
99 goto err;
100 ret = eval("print ptrs");
101 } else {
102 ret = eval("print recs");
103 }
104 if (ret)
105 goto err;
106 if (btblock_has_rightsib(iocur_top->data, long_format)) {
107 ret = eval("addr rightsib");
108 if (ret)
109 goto err;
110 }
111 nr++;
112 } while (iocur_top->bb != orig_daddr && iocur_top->bb != last_daddr);
113
114 ret = eval("pop");
115 return ret;
116err:
117 eval("pop");
118 return ret;
119}
120
121static int
122dump_btree(
123 bool dump_node_blocks,
124 bool long_format)
125{
126 xfs_daddr_t orig_daddr = iocur_top->bb;
127 xfs_daddr_t last_daddr;
128 int level;
129 int ret;
130
131 ret = eval("push");
132 if (ret)
133 return ret;
134
135 cur_agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
136 level = xfs_btree_get_level(iocur_top->data);
137 do {
138 last_daddr = iocur_top->bb;
139 if (level > 0) {
140 if (dump_node_blocks) {
141 ret = dump_btlevel(level, long_format);
142 if (ret)
143 goto err;
144 }
145 ret = eval("addr ptrs[1]");
146 } else {
147 ret = dump_btlevel(level, long_format);
148 }
149 if (ret)
150 goto err;
151 level--;
152 } while (level >= 0 &&
153 iocur_top->bb != orig_daddr &&
154 iocur_top->bb != last_daddr);
155
156 ret = eval("pop");
157 return ret;
158err:
159 eval("pop");
160 return ret;
161}
162
163static inline int dump_btree_short(bool dump_node_blocks)
164{
165 return dump_btree(dump_node_blocks, false);
166}
167
168static inline int dump_btree_long(bool dump_node_blocks)
169{
170 return dump_btree(dump_node_blocks, true);
171}
172
173static int
174dump_inode(
175 bool dump_node_blocks,
176 bool attrfork)
177{
178 char *prefix;
179 struct xfs_dinode *dip;
180 int ret;
181
182 if (attrfork)
183 prefix = "a.bmbt";
184 else if (xfs_sb_version_hascrc(&mp->m_sb))
185 prefix = "u3.bmbt";
186 else
187 prefix = "u.bmbt";
188
189 dip = iocur_top->data;
190 if (attrfork) {
191 if (!dip->di_anextents ||
192 dip->di_aformat != XFS_DINODE_FMT_BTREE) {
193 dbprintf(_("attr fork not in btree format\n"));
194 return 0;
195 }
196 } else {
197 if (!dip->di_nextents ||
198 dip->di_format != XFS_DINODE_FMT_BTREE) {
199 dbprintf(_("data fork not in btree format\n"));
200 return 0;
201 }
202 }
203
204 ret = eval("push");
205 if (ret)
206 return ret;
207
208 if (dump_node_blocks) {
209 ret = eval("print %s.keys", prefix);
210 if (ret)
211 goto err;
212 ret = eval("print %s.ptrs", prefix);
213 if (ret)
214 goto err;
215 }
216
217 ret = eval("addr %s.ptrs[1]", prefix);
218 if (ret)
219 goto err;
220
221 ret = dump_btree_long(dump_node_blocks);
222 if (ret)
223 goto err;
224
225 ret = eval("pop");
226 return ret;
227err:
228 eval("pop");
229 return ret;
230}
231
a05feedb
DW
232static bool
233dir_has_rightsib(
234 void *block,
235 int level)
236{
237 struct xfs_dir3_icleaf_hdr lhdr;
238 struct xfs_da3_icnode_hdr nhdr;
239
240 if (level > 0) {
241 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
242 return nhdr.forw != 0;
243 }
244 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
245 return lhdr.forw != 0;
246}
247
248static int
249dir_level(
250 void *block)
251{
252 struct xfs_dir3_icleaf_hdr lhdr;
253 struct xfs_da3_icnode_hdr nhdr;
254
255 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
256 case cpu_to_be16(XFS_DIR2_LEAF1_MAGIC):
257 case cpu_to_be16(XFS_DIR2_LEAFN_MAGIC):
258 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
259 return 0;
260 case cpu_to_be16(XFS_DA_NODE_MAGIC):
261 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
262 return nhdr.level;
263 default:
264 return -1;
265 }
266}
267
268static int
269dir3_level(
270 void *block)
271{
272 struct xfs_dir3_icleaf_hdr lhdr;
273 struct xfs_da3_icnode_hdr nhdr;
274
275 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
276 case cpu_to_be16(XFS_DIR3_LEAF1_MAGIC):
277 case cpu_to_be16(XFS_DIR3_LEAFN_MAGIC):
278 M_DIROPS(mp)->leaf_hdr_from_disk(&lhdr, block);
279 return 0;
280 case cpu_to_be16(XFS_DA3_NODE_MAGIC):
281 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
282 return nhdr.level;
283 default:
284 return -1;
285 }
286}
287
288static bool
289attr_has_rightsib(
290 void *block,
291 int level)
292{
293 struct xfs_attr_leafblock lhdr;
294 struct xfs_da3_icnode_hdr nhdr;
295
296 if (level > 0) {
297 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
298 return nhdr.forw != 0;
299 }
300 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
301 return lhdr.hdr.info.forw != 0;
302}
303
304static int
305attr_level(
306 void *block)
307{
308 struct xfs_attr_leafblock lhdr;
309 struct xfs_da3_icnode_hdr nhdr;
310
311 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
312 case cpu_to_be16(XFS_ATTR_LEAF_MAGIC):
313 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
314 return 0;
315 case cpu_to_be16(XFS_DA_NODE_MAGIC):
316 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
317 return nhdr.level;
318 default:
319 return -1;
320 }
321}
322
323static int
324attr3_level(
325 void *block)
326{
327 struct xfs_attr_leafblock lhdr;
328 struct xfs_da3_icnode_hdr nhdr;
329
330 switch (((struct xfs_da_intnode *)block)->hdr.info.magic) {
331 case cpu_to_be16(XFS_ATTR3_LEAF_MAGIC):
332 xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, &lhdr, block);
333 return 0;
334 case cpu_to_be16(XFS_DA3_NODE_MAGIC):
335 M_DIROPS(mp)->node_hdr_from_disk(&nhdr, block);
336 return nhdr.level;
337 default:
338 return -1;
339 }
340}
341
342struct dabprinter_ops {
343 const char *print_node_entries;
344 const char *print_leaf_entries;
345 const char *go_node_forward;
346 const char *go_leaf_forward;
347 const char *go_down;
348 bool (*has_rightsib)(void *, int);
349 int (*level)(void *);
350};
351
352static struct dabprinter_ops attr_print = {
353 .print_node_entries = "btree",
354 .print_leaf_entries = "entries nvlist",
355 .go_node_forward = "hdr.info.forw",
356 .go_leaf_forward = "hdr.info.forw",
357 .go_down = "btree[0].before",
358 .has_rightsib = attr_has_rightsib,
359 .level = attr_level,
360};
361
362static struct dabprinter_ops attr3_print = {
363 .print_node_entries = "btree",
364 .print_leaf_entries = "entries nvlist",
365 .go_node_forward = "hdr.info.hdr.forw",
366 .go_leaf_forward = "hdr.info.hdr.forw",
367 .go_down = "btree[0].before",
368 .has_rightsib = attr_has_rightsib,
369 .level = attr3_level,
370};
371
372static struct dabprinter_ops dir_print = {
373 .print_node_entries = "nbtree",
374 .print_leaf_entries = "lents",
375 .go_node_forward = "nhdr.info.hdr.forw",
376 .go_leaf_forward = "lhdr.info.hdr.forw",
377 .go_down = "nbtree[0].before",
378 .has_rightsib = dir_has_rightsib,
379 .level = dir_level,
380};
381
382static struct dabprinter_ops dir3_print = {
383 .print_node_entries = "nbtree",
384 .print_leaf_entries = "lents",
385 .go_node_forward = "nhdr.info.forw",
386 .go_leaf_forward = "lhdr.info.forw",
387 .go_down = "nbtree[0].before",
388 .has_rightsib = dir_has_rightsib,
389 .level = dir3_level,
390};
391
392static int
393dump_dablevel(
394 int level,
395 struct dabprinter_ops *dbp)
396{
397 xfs_daddr_t orig_daddr = iocur_top->bb;
398 xfs_daddr_t last_daddr;
399 unsigned int nr;
400 int ret;
401
402 ret = eval("push");
403 if (ret)
404 return ret;
405
406 nr = 1;
407 do {
408 last_daddr = iocur_top->bb;
409 dbprintf(_("%s level %u block %u daddr %llu\n"),
410 iocur_top->typ->name, level, nr, last_daddr);
411 ret = eval("print %s", level > 0 ? dbp->print_node_entries :
412 dbp->print_leaf_entries);
413 if (ret)
414 goto err;
415 if (dbp->has_rightsib(iocur_top->data, level)) {
416 ret = eval("addr %s", level > 0 ? dbp->go_node_forward :
417 dbp->go_leaf_forward);
418 if (ret)
419 goto err;
420 }
421 nr++;
422 } while (iocur_top->bb != orig_daddr && iocur_top->bb != last_daddr);
423
424 ret = eval("pop");
425 return ret;
426err:
427 eval("pop");
428 return ret;
429}
430
431static int
432dump_dabtree(
433 bool dump_node_blocks,
434 struct dabprinter_ops *dbp)
435{
436 xfs_daddr_t orig_daddr = iocur_top->bb;
437 xfs_daddr_t last_daddr;
438 int level;
439 int ret;
440
441 ret = eval("push");
442 if (ret)
443 return ret;
444
445 cur_agno = XFS_FSB_TO_AGNO(mp, XFS_DADDR_TO_FSB(mp, iocur_top->bb));
446 level = dbp->level(iocur_top->data);
447 if (level < 0) {
448 printf(_("Current location is not part of a dir/attr btree.\n"));
449 goto err;
450 }
451
452 do {
453 last_daddr = iocur_top->bb;
454 if (level > 0) {
455 if (dump_node_blocks) {
456 ret = dump_dablevel(level, dbp);
457 if (ret)
458 goto err;
459 }
460 ret = eval("addr %s", dbp->go_down);
461 } else {
462 ret = dump_dablevel(level, dbp);
463 }
464 if (ret)
465 goto err;
466 level--;
467 } while (level >= 0 &&
468 iocur_top->bb != orig_daddr &&
469 iocur_top->bb != last_daddr);
470
471 ret = eval("pop");
472 return ret;
473err:
474 eval("pop");
475 return ret;
476}
477
21f0bffe
DW
478static int
479btdump_f(
480 int argc,
481 char **argv)
482{
483 bool aflag = false;
484 bool iflag = false;
a05feedb 485 bool crc = xfs_sb_version_hascrc(&mp->m_sb);
21f0bffe
DW
486 int c;
487
488 if (cur_typ == NULL) {
489 dbprintf(_("no current type\n"));
490 return 0;
491 }
492 while ((c = getopt(argc, argv, "ai")) != EOF) {
493 switch (c) {
494 case 'a':
495 aflag = true;
496 break;
497 case 'i':
498 iflag = true;
499 break;
500 default:
501 dbprintf(_("bad option for btdump command\n"));
502 return 0;
503 }
504 }
505
506 if (optind != argc) {
507 dbprintf(_("bad options for btdump command\n"));
508 return 0;
509 }
510 if (aflag && cur_typ->typnm != TYP_INODE) {
511 dbprintf(_("attrfork flag doesn't apply here\n"));
512 return 0;
513 }
514
515 switch (cur_typ->typnm) {
516 case TYP_BNOBT:
517 case TYP_CNTBT:
518 case TYP_INOBT:
519 case TYP_FINOBT:
520 case TYP_RMAPBT:
521 case TYP_REFCBT:
522 return dump_btree_short(iflag);
523 case TYP_BMAPBTA:
524 case TYP_BMAPBTD:
525 return dump_btree_long(iflag);
526 case TYP_INODE:
527 return dump_inode(iflag, aflag);
a05feedb
DW
528 case TYP_ATTR:
529 return dump_dabtree(iflag, crc ? &attr3_print : &attr_print);
530 case TYP_DIR2:
531 return dump_dabtree(iflag, crc ? &dir3_print : &dir_print);
21f0bffe
DW
532 default:
533 dbprintf(_("type \"%s\" is not a btree type or inode\n"),
534 cur_typ->name);
535 return 0;
536 }
537}
538
539static const cmdinfo_t btdump_cmd =
540 { "btdump", "b", btdump_f, 0, 2, 0, "[-a] [-i]",
541 N_("dump btree"), btdump_help };
542
543void
544btdump_init(void)
545{
546 add_command(&btdump_cmd);
547}