2 * Copyright 2001-2010 pinc Software. All Rights Reserved.
6 //! Dumps various information about BFS volumes.
10 #include "BPlusTree.h"
21 dump_bplustree(Disk
&disk
, BPositionIO
*file
, off_t size
, bool hexDump
)
23 uint8
*buffer
= (uint8
*)malloc(size
);
29 if (file
->ReadAt(0, buffer
, size
) != size
) {
30 puts("couldn't read whole file");
34 bplustree_header
*header
= (bplustree_header
*)buffer
;
35 int32 nodeSize
= header
->node_size
;
37 dump_bplustree_header(header
);
39 bplustree_node
*node
= (bplustree_node
*)(buffer
+ nodeSize
);
40 while ((addr_t
)node
< (addr_t
)buffer
+ size
) {
41 printf("\n\n-------------------\n"
42 "** node at offset: %" B_PRIuADDR
"\n** used: %" B_PRId32
" bytes"
43 "\n", (addr_t
)node
- (addr_t
)buffer
, node
->Used());
44 dump_bplustree_node(node
, header
, &disk
);
48 dump_block((char *)node
, header
->node_size
, 0);
51 node
= (bplustree_node
*)((addr_t
)node
+ nodeSize
);
57 dump_indirect_stream(Disk
&disk
, bfs_inode
*node
, bool showOffsets
)
59 if (node
->data
.max_indirect_range
== 0)
62 int32 bytes
= node
->data
.indirect
.length
* disk
.BlockSize();
63 int32 count
= bytes
/ sizeof(block_run
);
64 block_run runs
[count
];
66 off_t offset
= node
->data
.max_direct_range
;
68 ssize_t bytesRead
= disk
.ReadAt(disk
.ToOffset(node
->data
.indirect
),
69 (uint8
*)runs
, bytes
);
70 if (bytesRead
< bytes
) {
71 fprintf(stderr
, "couldn't read indirect runs: %s\n",
76 puts("indirect stream:");
78 for (int32 i
= 0; i
< count
; i
++) {
82 printf(" indirect[%04" B_PRId32
"] = ", i
);
86 snprintf(buffer
, sizeof(buffer
), " %16" B_PRIdOFF
, offset
);
90 dump_block_run("", runs
[i
], buffer
);
92 offset
+= runs
[i
].length
* disk
.BlockSize();
98 dump_double_indirect_stream(Disk
& disk
, bfs_inode
* node
, bool showOffsets
)
100 if (node
->data
.max_double_indirect_range
== 0)
103 int32 bytes
= node
->data
.double_indirect
.length
* disk
.BlockSize();
104 int32 count
= bytes
/ sizeof(block_run
);
105 block_run runs
[count
];
107 off_t offset
= node
->data
.max_indirect_range
;
109 ssize_t bytesRead
= disk
.ReadAt(disk
.ToOffset(node
->data
.double_indirect
),
110 (uint8
*)runs
, bytes
);
111 if (bytesRead
< bytes
) {
112 fprintf(stderr
, "couldn't read double indirect runs: %s\n",
113 strerror(bytesRead
));
117 puts("double indirect stream:");
119 for (int32 i
= 0; i
< count
; i
++) {
120 if (runs
[i
].IsZero())
123 printf(" double_indirect[%02" B_PRId32
"] = ", i
);
125 dump_block_run("", runs
[i
], "");
127 int32 indirectBytes
= runs
[i
].length
* disk
.BlockSize();
128 int32 indirectCount
= indirectBytes
/ sizeof(block_run
);
129 block_run indirectRuns
[indirectCount
];
131 bytesRead
= disk
.ReadAt(disk
.ToOffset(runs
[i
]), (uint8
*)indirectRuns
,
133 if (bytesRead
< indirectBytes
) {
134 fprintf(stderr
, "couldn't read double indirect runs: %s\n",
135 strerror(bytesRead
));
139 for (int32 j
= 0; j
< indirectCount
; j
++) {
140 if (indirectRuns
[j
].IsZero())
143 printf(" [%04" B_PRId32
"] = ", j
);
147 snprintf(buffer
, sizeof(buffer
), " %16" B_PRIdOFF
, offset
);
151 dump_block_run("", indirectRuns
[j
], buffer
);
153 offset
+= indirectRuns
[j
].length
* disk
.BlockSize();
160 list_bplustree(Disk
& disk
, Directory
* directory
, off_t size
)
164 char name
[B_FILE_NAME_LENGTH
];
168 while (directory
->GetNextEntry(name
, &run
) == B_OK
) {
169 snprintf(buffer
, sizeof(buffer
), " %s", name
);
170 dump_block_run("", run
, buffer
);
174 printf("--\n%lld items.\n", count
);
179 count_bplustree(Disk
& disk
, Directory
* directory
, off_t size
)
183 char name
[B_FILE_NAME_LENGTH
];
186 while (directory
->GetNextEntry(name
, &run
) == B_OK
)
189 printf("%lld items.\n", count
);
194 parseBlockRun(Disk
&disk
, char *first
, char *last
)
199 return block_run::Run(atol(first
), atol(last
), 1);
200 } else if ((comma
= strchr(first
, ',')) != NULL
) {
202 return block_run::Run(atol(first
), atol(comma
));
205 return disk
.ToBlockRun(atoll(first
));
210 main(int argc
, char **argv
)
212 puts("Copyright (c) 2001-2010 pinc Software.");
214 if (argc
< 2 || !strcmp(argv
[1], "--help")) {
215 char *filename
= strrchr(argv
[0],'/');
216 fprintf(stderr
,"usage: %s [-srib] <device> [allocation_group start]\n"
217 "\t-s\tdump superblock\n"
218 "\t-r\tdump root node\n"
219 " the following options need the allocation_group/start "
222 "\t-b\tdump b+tree\n"
223 "\t-c\tlist b+tree leaves\n"
224 "\t-c\tcount b+tree leaves\n"
225 "\t-v\tvalidate b+tree\n"
227 "\t-o\tshow disk offsets\n",
228 filename
? filename
+ 1 : argv
[0]);
232 bool dumpRootNode
= false;
233 bool dumpInode
= false;
234 bool dumpSuperBlock
= false;
235 bool dumpBTree
= false;
236 bool listBTree
= false;
237 bool countBTree
= false;
238 bool validateBTree
= false;
239 bool dumpHex
= false;
240 bool showOffsets
= false;
245 while (*++arg
&& isalpha(*arg
)) {
248 dumpSuperBlock
= true;
266 validateBTree
= true;
281 if (disk
.InitCheck() < B_OK
)
283 fprintf(stderr
, "Could not open device or file: %s\n", strerror(disk
.InitCheck()));
288 if (!dumpSuperBlock
&& !dumpRootNode
&& !dumpInode
&& !dumpBTree
289 && !dumpHex
&& !listBTree
&& !countBTree
) {
290 printf(" Name:\t\t\t\"%s\"\n", disk
.SuperBlock()->name
);
291 printf(" (disk is %s)\n\n",
292 disk
.ValidateSuperBlock() == B_OK
? "valid" : "invalid!!");
293 printf(" Block Size:\t\t%" B_PRIu32
" bytes\n", disk
.BlockSize());
294 printf(" Number of Blocks:\t%12" B_PRIdOFF
"\t%10g MB\n",
295 disk
.NumBlocks(), disk
.NumBlocks() * disk
.BlockSize()
297 if (disk
.BlockBitmap() != NULL
) {
298 printf(" Used Blocks:\t\t%12" B_PRIdOFF
"\t%10g MB\n",
299 disk
.BlockBitmap()->UsedBlocks(),
300 disk
.BlockBitmap()->UsedBlocks() * disk
.BlockSize()
302 printf(" Free Blocks:\t\t%12" B_PRIdOFF
"\t%10g MB\n",
303 disk
.BlockBitmap()->FreeBlocks(),
304 disk
.BlockBitmap()->FreeBlocks() * disk
.BlockSize()
308 = (disk
.AllocationGroups() * disk
.SuperBlock()->blocks_per_ag
);
309 printf(" Bitmap Size:\t\t%" B_PRIu32
" bytes (%" B_PRId32
" blocks, %"
310 B_PRId32
" per allocation group)\n", disk
.BlockSize() * size
, size
,
311 disk
.SuperBlock()->blocks_per_ag
);
312 printf(" Allocation Groups:\t%" B_PRIu32
"\n\n",
313 disk
.AllocationGroups());
314 dump_block_run(" Log:\t\t\t", disk
.Log());
315 printf(" (was %s)\n\n", disk
.SuperBlock()->flags
== SUPER_BLOCK_CLEAN
316 ? "cleanly unmounted" : "not unmounted cleanly!");
317 dump_block_run(" Root Directory:\t", disk
.Root());
319 } else if (dumpSuperBlock
) {
320 dump_super_block(disk
.SuperBlock());
324 if (disk
.ValidateSuperBlock() < B_OK
) {
325 fprintf(stderr
, "The disk's superblock is corrupt (or it's not a BFS "
332 if (disk
.ReadAt(disk
.ToOffset(disk
.Root()), (void *)&inode
,
333 sizeof(bfs_inode
)) < B_OK
) {
334 fprintf(stderr
,"Could not read root node from disk!\n");
336 puts("Root node:\n-----------------------------------------");
337 dump_inode(NULL
, &inode
, showOffsets
);
338 dump_indirect_stream(disk
, &inode
, showOffsets
);
343 char buffer
[disk
.BlockSize()];
344 bfs_inode
* bfsInode
= (bfs_inode
*)buffer
;
348 if (dumpInode
|| dumpBTree
|| dumpHex
|| validateBTree
|| listBTree
350 // Set the block_run to the right value (as specified on the command
353 fprintf(stderr
, "The -i/b/f options need the allocation group and "
354 "starting offset (or the block number) of the node to dump!\n");
357 run
= parseBlockRun(disk
, argv
[1], argv
[2]);
359 if (disk
.ReadAt(disk
.ToOffset(run
), buffer
, disk
.BlockSize()) <= 0) {
360 fprintf(stderr
,"Could not read node from disk!\n");
364 inode
= Inode::Factory(&disk
, bfsInode
, false);
365 if (inode
== NULL
|| inode
->InitCheck() < B_OK
) {
366 fprintf(stderr
,"Not a valid inode!\n");
373 printf("Inode at block %" B_PRIdOFF
":\n------------------------------"
374 "-----------\n", disk
.ToBlock(run
));
375 dump_inode(inode
, bfsInode
, showOffsets
);
376 dump_indirect_stream(disk
, bfsInode
, showOffsets
);
377 dump_double_indirect_stream(disk
, bfsInode
, showOffsets
);
378 dump_small_data(inode
);
382 if (dumpBTree
&& inode
!= NULL
) {
383 printf("B+Tree at block %" B_PRIdOFF
":\n-----------------------------"
384 "------------\n", disk
.ToBlock(run
));
385 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
386 dump_bplustree(disk
, (Directory
*)inode
, inode
->Size(), dumpHex
);
389 fprintf(stderr
, "Inode is not a directory!\n");
392 if (listBTree
&& inode
!= NULL
) {
393 printf("Directory contents: ------------------------------------------\n");
394 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
395 list_bplustree(disk
, (Directory
*)inode
, inode
->Size());
398 fprintf(stderr
, "Inode is not a directory!\n");
401 if (countBTree
&& inode
!= NULL
) {
402 printf("Count contents: ------------------------------------------\n");
403 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
404 count_bplustree(disk
, (Directory
*)inode
, inode
->Size());
407 fprintf(stderr
, "Inode is not a directory!\n");
410 if (validateBTree
&& inode
!= NULL
) {
411 printf("Validating B+Tree at block %" B_PRIdOFF
":\n------------------"
412 "-----------------------\n", disk
.ToBlock(run
));
413 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
415 if (((Directory
*)inode
)->GetTree(&tree
) == B_OK
) {
416 if (tree
->Validate(true) < B_OK
)
417 puts("B+Tree is corrupt!");
419 puts("B+Tree seems to be okay.");
422 fprintf(stderr
, "Inode is not a directory!\n");
426 printf("Hexdump from inode at block %" B_PRIdOFF
":\n-----------------"
427 "------------------------\n", disk
.ToBlock(run
));
428 dump_block(buffer
, disk
.BlockSize());