2 * Copyright 2001-2010 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
7 //! Dumps various information about BFS volumes.
11 #include "BPlusTree.h"
15 #include <StringForSize.h>
23 using namespace BPrivate
;
27 dump_bplustree(Disk
&disk
, BPositionIO
*file
, off_t size
, bool hexDump
)
29 uint8
*buffer
= (uint8
*)malloc(size
);
35 if (file
->ReadAt(0, buffer
, size
) != size
) {
36 puts("couldn't read whole file");
40 bplustree_header
*header
= (bplustree_header
*)buffer
;
41 int32 nodeSize
= header
->node_size
;
43 dump_bplustree_header(header
);
45 bplustree_node
*node
= (bplustree_node
*)(buffer
+ nodeSize
);
46 while ((addr_t
)node
< (addr_t
)buffer
+ size
) {
47 printf("\n\n-------------------\n"
48 "** node at offset: %" B_PRIuADDR
"\n** used: %" B_PRId32
" bytes"
49 "\n", (addr_t
)node
- (addr_t
)buffer
, node
->Used());
50 dump_bplustree_node(node
, header
, &disk
);
54 dump_block((char *)node
, header
->node_size
, 0);
57 node
= (bplustree_node
*)((addr_t
)node
+ nodeSize
);
63 dump_indirect_stream(Disk
&disk
, bfs_inode
*node
, bool showOffsets
)
65 if (node
->data
.max_indirect_range
== 0)
68 int32 bytes
= node
->data
.indirect
.length
* disk
.BlockSize();
69 int32 count
= bytes
/ sizeof(block_run
);
70 block_run runs
[count
];
72 off_t offset
= node
->data
.max_direct_range
;
74 ssize_t bytesRead
= disk
.ReadAt(disk
.ToOffset(node
->data
.indirect
),
75 (uint8
*)runs
, bytes
);
76 if (bytesRead
< bytes
) {
77 fprintf(stderr
, "couldn't read indirect runs: %s\n",
82 puts("indirect stream:");
84 for (int32 i
= 0; i
< count
; i
++) {
88 printf(" indirect[%04" B_PRId32
"] = ", i
);
92 snprintf(buffer
, sizeof(buffer
), " %16" B_PRIdOFF
, offset
);
96 dump_block_run("", runs
[i
], buffer
);
98 offset
+= runs
[i
].length
* disk
.BlockSize();
104 dump_double_indirect_stream(Disk
& disk
, bfs_inode
* node
, bool showOffsets
)
106 if (node
->data
.max_double_indirect_range
== 0)
109 int32 bytes
= node
->data
.double_indirect
.length
* disk
.BlockSize();
110 int32 count
= bytes
/ sizeof(block_run
);
111 block_run runs
[count
];
113 off_t offset
= node
->data
.max_indirect_range
;
115 ssize_t bytesRead
= disk
.ReadAt(disk
.ToOffset(node
->data
.double_indirect
),
116 (uint8
*)runs
, bytes
);
117 if (bytesRead
< bytes
) {
118 fprintf(stderr
, "couldn't read double indirect runs: %s\n",
119 strerror(bytesRead
));
123 puts("double indirect stream:");
125 for (int32 i
= 0; i
< count
; i
++) {
126 if (runs
[i
].IsZero())
129 printf(" double_indirect[%02" B_PRId32
"] = ", i
);
131 dump_block_run("", runs
[i
], "");
133 int32 indirectBytes
= runs
[i
].length
* disk
.BlockSize();
134 int32 indirectCount
= indirectBytes
/ sizeof(block_run
);
135 block_run indirectRuns
[indirectCount
];
137 bytesRead
= disk
.ReadAt(disk
.ToOffset(runs
[i
]), (uint8
*)indirectRuns
,
139 if (bytesRead
< indirectBytes
) {
140 fprintf(stderr
, "couldn't read double indirect runs: %s\n",
141 strerror(bytesRead
));
145 for (int32 j
= 0; j
< indirectCount
; j
++) {
146 if (indirectRuns
[j
].IsZero())
149 printf(" [%04" B_PRId32
"] = ", j
);
153 snprintf(buffer
, sizeof(buffer
), " %16" B_PRIdOFF
, offset
);
157 dump_block_run("", indirectRuns
[j
], buffer
);
159 offset
+= indirectRuns
[j
].length
* disk
.BlockSize();
166 list_bplustree(Disk
& disk
, Directory
* directory
, off_t size
)
170 char name
[B_FILE_NAME_LENGTH
];
174 while (directory
->GetNextEntry(name
, &run
) == B_OK
) {
175 snprintf(buffer
, sizeof(buffer
), " %s", name
);
176 dump_block_run("", run
, buffer
);
180 printf("--\n%" B_PRId64
" items.\n", count
);
185 count_bplustree(Disk
& disk
, Directory
* directory
, off_t size
)
189 char name
[B_FILE_NAME_LENGTH
];
192 while (directory
->GetNextEntry(name
, &run
) == B_OK
)
195 printf("%" B_PRId64
" items.\n", count
);
200 parseBlockRun(Disk
&disk
, char *first
, char *last
)
205 return block_run::Run(atol(first
), atol(last
), 1);
206 } else if ((comma
= strchr(first
, ',')) != NULL
) {
208 return block_run::Run(atol(first
), atol(comma
));
211 return disk
.ToBlockRun(atoll(first
));
216 main(int argc
, char **argv
)
218 if (argc
< 2 || !strcmp(argv
[1], "--help")) {
219 char *filename
= strrchr(argv
[0],'/');
220 fprintf(stderr
,"usage: %s [-srib] <device> [allocation_group start]\n"
221 "\t-s\tdump superblock\n"
222 "\t-r\tdump root node\n"
223 " the following options need the allocation_group/start "
226 "\t-b\tdump b+tree\n"
227 "\t-c\tlist b+tree leaves\n"
228 "\t-c\tcount b+tree leaves\n"
229 "\t-v\tvalidate b+tree\n"
231 "\t-o\tshow disk offsets\n",
232 filename
? filename
+ 1 : argv
[0]);
236 bool dumpRootNode
= false;
237 bool dumpInode
= false;
238 bool dumpSuperBlock
= false;
239 bool dumpBTree
= false;
240 bool listBTree
= false;
241 bool countBTree
= false;
242 bool validateBTree
= false;
243 bool dumpHex
= false;
244 bool showOffsets
= false;
249 while (*++arg
&& isalpha(*arg
)) {
252 dumpSuperBlock
= true;
270 validateBTree
= true;
285 if (disk
.InitCheck() < B_OK
)
287 fprintf(stderr
, "Could not open device or file: %s\n", strerror(disk
.InitCheck()));
292 if (!dumpSuperBlock
&& !dumpRootNode
&& !dumpInode
&& !dumpBTree
293 && !dumpHex
&& !listBTree
&& !countBTree
) {
295 printf(" Name:\t\t\t\"%s\"\n", disk
.SuperBlock()->name
);
296 printf(" SuperBlock:\t\t%s\n\n",
297 disk
.ValidateSuperBlock() == B_OK
? "valid" : "invalid!");
298 printf(" Block Size:%*" B_PRIu32
" bytes\n", 23, disk
.BlockSize());
299 string_for_size(disk
.NumBlocks() * disk
.BlockSize(), buffer
,
301 printf(" Number of Blocks:%*" B_PRIdOFF
"\t%*s\n", 17, disk
.NumBlocks(),
303 if (disk
.BlockBitmap() != NULL
) {
304 string_for_size(disk
.BlockBitmap()->UsedBlocks() * disk
.BlockSize(),
305 buffer
, sizeof(buffer
));
306 printf(" Used Blocks:%*" B_PRIdOFF
"\t%*s\n", 22,
307 disk
.BlockBitmap()->UsedBlocks(), 16, buffer
);
309 string_for_size(disk
.BlockBitmap()->FreeBlocks() * disk
.BlockSize(),
310 buffer
, sizeof(buffer
));
311 printf(" Free Blocks:%*" B_PRIdOFF
"\t%*s\n", 22,
312 disk
.BlockBitmap()->FreeBlocks(), 16, buffer
);
315 = (disk
.AllocationGroups() * disk
.SuperBlock()->blocks_per_ag
);
316 string_for_size(disk
.BlockSize() * size
, buffer
, sizeof(buffer
));
317 printf(" Bitmap Blocks:%*" B_PRId32
"\t%*s\n", 20, size
, 16, buffer
);
318 printf(" Allocation Group Size:%*" B_PRId32
" blocks\n", 12,
319 disk
.SuperBlock()->blocks_per_ag
);
320 printf(" Allocation Groups:%*" B_PRIu32
"\n\n", 16,
321 disk
.AllocationGroups());
322 dump_block_run(" Log:\t\t\t", disk
.Log());
323 printf("\t\t\t%s\n\n", disk
.SuperBlock()->flags
== SUPER_BLOCK_CLEAN
324 ? "cleanly unmounted" : "not unmounted cleanly!");
325 dump_block_run(" Root Directory:\t", disk
.Root());
327 } else if (dumpSuperBlock
) {
328 dump_super_block(disk
.SuperBlock());
332 if (disk
.ValidateSuperBlock() < B_OK
) {
333 fprintf(stderr
, "The disk's superblock is corrupt (or it's not a BFS "
340 if (disk
.ReadAt(disk
.ToOffset(disk
.Root()), (void *)&inode
,
341 sizeof(bfs_inode
)) < B_OK
) {
342 fprintf(stderr
,"Could not read root node from disk!\n");
344 puts("Root node:\n-----------------------------------------");
345 dump_inode(NULL
, &inode
, showOffsets
);
346 dump_indirect_stream(disk
, &inode
, showOffsets
);
351 char buffer
[disk
.BlockSize()];
352 bfs_inode
* bfsInode
= (bfs_inode
*)buffer
;
356 if (dumpInode
|| dumpBTree
|| dumpHex
|| validateBTree
|| listBTree
358 // Set the block_run to the right value (as specified on the command
361 fprintf(stderr
, "The -i/b/f options need the allocation group and "
362 "starting offset (or the block number) of the node to dump!\n");
365 run
= parseBlockRun(disk
, argv
[1], argv
[2]);
367 if (disk
.ReadAt(disk
.ToOffset(run
), buffer
, disk
.BlockSize()) <= 0) {
368 fprintf(stderr
,"Could not read node from disk!\n");
372 inode
= Inode::Factory(&disk
, bfsInode
, false);
373 if (inode
== NULL
|| inode
->InitCheck() < B_OK
) {
374 fprintf(stderr
,"Not a valid inode!\n");
381 printf("Inode at block %" B_PRIdOFF
":\n------------------------------"
382 "-----------\n", disk
.ToBlock(run
));
383 dump_inode(inode
, bfsInode
, showOffsets
);
384 dump_indirect_stream(disk
, bfsInode
, showOffsets
);
385 dump_double_indirect_stream(disk
, bfsInode
, showOffsets
);
386 dump_small_data(inode
);
390 if (dumpBTree
&& inode
!= NULL
) {
391 printf("B+Tree at block %" B_PRIdOFF
":\n-----------------------------"
392 "------------\n", disk
.ToBlock(run
));
393 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
394 dump_bplustree(disk
, (Directory
*)inode
, inode
->Size(), dumpHex
);
397 fprintf(stderr
, "Inode is not a directory!\n");
400 if (listBTree
&& inode
!= NULL
) {
401 printf("Directory contents: ------------------------------------------\n");
402 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
403 list_bplustree(disk
, (Directory
*)inode
, inode
->Size());
406 fprintf(stderr
, "Inode is not a directory!\n");
409 if (countBTree
&& inode
!= NULL
) {
410 printf("Count contents: ------------------------------------------\n");
411 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
412 count_bplustree(disk
, (Directory
*)inode
, inode
->Size());
415 fprintf(stderr
, "Inode is not a directory!\n");
418 if (validateBTree
&& inode
!= NULL
) {
419 printf("Validating B+Tree at block %" B_PRIdOFF
":\n------------------"
420 "-----------------------\n", disk
.ToBlock(run
));
421 if (inode
->IsDirectory() || inode
->IsAttributeDirectory()) {
423 if (((Directory
*)inode
)->GetTree(&tree
) == B_OK
) {
424 if (tree
->Validate(true) < B_OK
)
425 puts("B+Tree is corrupt!");
427 puts("B+Tree seems to be okay.");
430 fprintf(stderr
, "Inode is not a directory!\n");
434 printf("Hexdump from inode at block %" B_PRIdOFF
":\n-----------------"
435 "------------------------\n", disk
.ToBlock(run
));
436 dump_block(buffer
, disk
.BlockSize());