libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / bin / bfs_tools / bfsinfo.cpp
blobd623c8cd8ce7b5e6e3ea4f120fe62c8d8ad7fe7f
1 /*
2 * Copyright 2001-2010 pinc Software. All Rights Reserved.
3 * Released under the terms of the MIT license.
4 */
7 //! Dumps various information about BFS volumes.
10 #include "Disk.h"
11 #include "BPlusTree.h"
12 #include "Inode.h"
13 #include "dump.h"
15 #include <StringForSize.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
23 using namespace BPrivate;
26 void
27 dump_bplustree(Disk &disk, BPositionIO *file, off_t size, bool hexDump)
29 uint8 *buffer = (uint8 *)malloc(size);
30 if (buffer == NULL) {
31 puts("no buffer");
32 return;
35 if (file->ReadAt(0, buffer, size) != size) {
36 puts("couldn't read whole file");
37 return;
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);
52 if (hexDump) {
53 putchar('\n');
54 dump_block((char *)node, header->node_size, 0);
57 node = (bplustree_node *)((addr_t)node + nodeSize);
62 void
63 dump_indirect_stream(Disk &disk, bfs_inode *node, bool showOffsets)
65 if (node->data.max_indirect_range == 0)
66 return;
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",
78 strerror(bytesRead));
79 return;
82 puts("indirect stream:");
84 for (int32 i = 0; i < count; i++) {
85 if (runs[i].IsZero())
86 return;
88 printf(" indirect[%04" B_PRId32 "] = ", i);
90 char buffer[256];
91 if (showOffsets)
92 snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
93 else
94 buffer[0] = '\0';
96 dump_block_run("", runs[i], buffer);
98 offset += runs[i].length * disk.BlockSize();
103 void
104 dump_double_indirect_stream(Disk& disk, bfs_inode* node, bool showOffsets)
106 if (node->data.max_double_indirect_range == 0)
107 return;
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));
120 return;
123 puts("double indirect stream:");
125 for (int32 i = 0; i < count; i++) {
126 if (runs[i].IsZero())
127 return;
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,
138 indirectBytes);
139 if (bytesRead < indirectBytes) {
140 fprintf(stderr, "couldn't read double indirect runs: %s\n",
141 strerror(bytesRead));
142 continue;
145 for (int32 j = 0; j < indirectCount; j++) {
146 if (indirectRuns[j].IsZero())
147 break;
149 printf(" [%04" B_PRId32 "] = ", j);
151 char buffer[256];
152 if (showOffsets)
153 snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
154 else
155 buffer[0] = '\0';
157 dump_block_run("", indirectRuns[j], buffer);
159 offset += indirectRuns[j].length * disk.BlockSize();
165 void
166 list_bplustree(Disk& disk, Directory* directory, off_t size)
168 directory->Rewind();
170 char name[B_FILE_NAME_LENGTH];
171 char buffer[512];
172 uint64 count = 0;
173 block_run run;
174 while (directory->GetNextEntry(name, &run) == B_OK) {
175 snprintf(buffer, sizeof(buffer), " %s", name);
176 dump_block_run("", run, buffer);
177 count++;
180 printf("--\n%" B_PRId64 " items.\n", count);
184 void
185 count_bplustree(Disk& disk, Directory* directory, off_t size)
187 directory->Rewind();
189 char name[B_FILE_NAME_LENGTH];
190 uint64 count = 0;
191 block_run run;
192 while (directory->GetNextEntry(name, &run) == B_OK)
193 count++;
195 printf("%" B_PRId64 " items.\n", count);
199 block_run
200 parseBlockRun(Disk &disk, char *first, char *last)
202 char *comma;
204 if (last) {
205 return block_run::Run(atol(first), atol(last), 1);
206 } else if ((comma = strchr(first, ',')) != NULL) {
207 *comma++ = '\0';
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 "
224 "parameters:\n"
225 "\t-i\tdump inode\n"
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"
230 "\t-h\thexdump\n"
231 "\t-o\tshow disk offsets\n",
232 filename ? filename + 1 : argv[0]);
233 return -1;
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;
246 while (*++argv) {
247 char *arg = *argv;
248 if (*arg == '-') {
249 while (*++arg && isalpha(*arg)) {
250 switch (*arg) {
251 case 's':
252 dumpSuperBlock = true;
253 break;
254 case 'r':
255 dumpRootNode = true;
256 break;
257 case 'i':
258 dumpInode = true;
259 break;
260 case 'b':
261 dumpBTree = true;
262 break;
263 case 'l':
264 listBTree = true;
265 break;
266 case 'c':
267 countBTree = true;
268 break;
269 case 'v':
270 validateBTree = true;
271 break;
272 case 'h':
273 dumpHex = true;
274 break;
275 case 'o':
276 showOffsets = true;
277 break;
280 } else
281 break;
284 Disk disk(argv[0]);
285 if (disk.InitCheck() < B_OK)
287 fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
288 return -1;
290 putchar('\n');
292 if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree
293 && !dumpHex && !listBTree && !countBTree) {
294 char buffer[16];
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,
300 sizeof(buffer));
301 printf(" Number of Blocks:%*" B_PRIdOFF "\t%*s\n", 17, disk.NumBlocks(),
302 16, buffer);
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);
314 int32 size
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());
326 putchar('\n');
327 } else if (dumpSuperBlock) {
328 dump_super_block(disk.SuperBlock());
329 putchar('\n');
332 if (disk.ValidateSuperBlock() < B_OK) {
333 fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS "
334 "device)!\n");
335 return 0;
338 if (dumpRootNode) {
339 bfs_inode inode;
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");
343 } else {
344 puts("Root node:\n-----------------------------------------");
345 dump_inode(NULL, &inode, showOffsets);
346 dump_indirect_stream(disk, &inode, showOffsets);
347 putchar('\n');
351 char buffer[disk.BlockSize()];
352 bfs_inode* bfsInode = (bfs_inode*)buffer;
353 block_run run;
354 Inode *inode = NULL;
356 if (dumpInode || dumpBTree || dumpHex || validateBTree || listBTree
357 || countBTree) {
358 // Set the block_run to the right value (as specified on the command
359 // line)
360 if (!argv[1]) {
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");
363 return -1;
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");
369 return -1;
372 inode = Inode::Factory(&disk, bfsInode, false);
373 if (inode == NULL || inode->InitCheck() < B_OK) {
374 fprintf(stderr,"Not a valid inode!\n");
375 delete inode;
376 inode = NULL;
380 if (dumpInode) {
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);
387 putchar('\n');
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);
395 putchar('\n');
396 } else
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());
404 putchar('\n');
405 } else
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());
413 putchar('\n');
414 } else
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()) {
422 BPlusTree *tree;
423 if (((Directory *)inode)->GetTree(&tree) == B_OK) {
424 if (tree->Validate(true) < B_OK)
425 puts("B+Tree is corrupt!");
426 else
427 puts("B+Tree seems to be okay.");
429 } else
430 fprintf(stderr, "Inode is not a directory!\n");
433 if (dumpHex) {
434 printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------"
435 "------------------------\n", disk.ToBlock(run));
436 dump_block(buffer, disk.BlockSize());
437 putchar('\n');
440 delete inode;
442 return 0;