xref: /haiku/src/bin/bfs_tools/bfsinfo.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
1  /*
2   * Copyright 2001-2010 pinc Software. All Rights Reserved.
3   */
4  
5  
6  //!	Dumps various information about BFS volumes.
7  
8  
9  #include "Disk.h"
10  #include "BPlusTree.h"
11  #include "Inode.h"
12  #include "dump.h"
13  
14  #include <stdio.h>
15  #include <string.h>
16  #include <stdlib.h>
17  #include <ctype.h>
18  
19  
20  void
21  dump_bplustree(Disk &disk, BPositionIO *file, off_t size, bool hexDump)
22  {
23  	uint8 *buffer = (uint8 *)malloc(size);
24  	if (buffer == NULL) {
25  		puts("no buffer");
26  		return;
27  	}
28  
29  	if (file->ReadAt(0, buffer, size) != size) {
30  		puts("couldn't read whole file");
31  		return;
32  	}
33  
34  	bplustree_header *header = (bplustree_header *)buffer;
35  	int32 nodeSize = header->node_size;
36  
37  	dump_bplustree_header(header);
38  
39  	bplustree_node *node = (bplustree_node *)(buffer + nodeSize);
40  	while ((int32)node < (int32)buffer + size) {
41  		printf("\n\n-------------------\n"
42  			"** node at offset: %ld\n** used: %ld bytes\n",
43  			int32(node) - int32(buffer), node->Used());
44  		dump_bplustree_node(node, header, &disk);
45  
46  		if (hexDump) {
47  			putchar('\n');
48  			dump_block((char *)node, header->node_size, 0);
49  		}
50  
51  		node = (bplustree_node *)(int32(node) + nodeSize);
52  	}
53  }
54  
55  
56  void
57  dump_indirect_stream(Disk &disk, bfs_inode *node, bool showOffsets)
58  {
59  	if (node->data.max_indirect_range == 0)
60  		return;
61  
62  	int32 bytes = node->data.indirect.length * disk.BlockSize();
63  	int32 count = bytes / sizeof(block_run);
64  	block_run runs[count];
65  
66  	off_t offset = node->data.max_direct_range;
67  
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",
72  			strerror(bytesRead));
73  		return;
74  	}
75  
76  	puts("indirect stream:");
77  
78  	for (int32 i = 0; i < count; i++) {
79  		if (runs[i].IsZero())
80  			return;
81  
82  		printf("  indirect[%02ld]              = ", i);
83  
84  		char buffer[256];
85  		if (showOffsets)
86  			snprintf(buffer, sizeof(buffer), " %16lld", offset);
87  		else
88  			buffer[0] = '\0';
89  
90  		dump_block_run("", runs[i], buffer);
91  
92  		offset += runs[i].length * disk.BlockSize();
93  	}
94  }
95  
96  
97  block_run
98  parseBlockRun(Disk &disk, char *first, char *last)
99  {
100  	char *comma;
101  
102  	if (last) {
103  		return block_run::Run(atol(first), atol(last), 1);
104  	} else if ((comma = strchr(first, ',')) != NULL) {
105  		*comma++ = '\0';
106  		return block_run::Run(atol(first), atol(comma));
107  	}
108  
109  	return disk.ToBlockRun(atoll(first));
110  }
111  
112  
113  int
114  main(int argc, char **argv)
115  {
116  	puts("Copyright (c) 2001-2010 pinc Software.");
117  
118  	if (argc < 2 || !strcmp(argv[1], "--help")) {
119  		char *filename = strrchr(argv[0],'/');
120  		fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n"
121  				"\t-s\tdump superblock\n"
122  				"\t-r\tdump root node\n"
123  				"       the following options need the allocation_group/start "
124  					"parameters:\n"
125  				"\t-i\tdump inode\n"
126  				"\t-b\tdump b+tree\n"
127  				"\t-v\tvalidate b+tree\n"
128  				"\t-h\thexdump\n"
129  				"\t-o\tshow disk offsets\n",
130  				filename ? filename + 1 : argv[0]);
131  		return -1;
132  	}
133  
134  	bool dumpRootNode = false;
135  	bool dumpInode = false;
136  	bool dumpSuperBlock = false;
137  	bool dumpBTree = false;
138  	bool validateBTree = false;
139  	bool dumpHex = false;
140  	bool showOffsets = false;
141  
142  	while (*++argv) {
143  		char *arg = *argv;
144  		if (*arg == '-') {
145  			while (*++arg && isalpha(*arg)) {
146  				switch (*arg) {
147  					case 's':
148  						dumpSuperBlock = true;
149  						break;
150  					case 'r':
151  						dumpRootNode = true;
152  						break;
153  					case 'i':
154  						dumpInode = true;
155  						break;
156  					case 'b':
157  						dumpBTree = true;
158  						break;
159  					case 'v':
160  						validateBTree = true;
161  						break;
162  					case 'h':
163  						dumpHex = true;
164  						break;
165  					case 'o':
166  						showOffsets = true;
167  						break;
168  				}
169  			}
170  		} else
171  			break;
172  	}
173  
174  	Disk disk(argv[0]);
175  	if (disk.InitCheck() < B_OK)
176  	{
177  		fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
178  		return -1;
179  	}
180  	putchar('\n');
181  
182  	if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree
183  		&& !dumpHex) {
184  		printf("  Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name);
185  		printf("    (disk is %s)\n\n",
186  			disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!");
187  		printf("  Block Size:\t\t%ld bytes\n", disk.BlockSize());
188  		printf("  Number of Blocks:\t%12Lu\t%10g MB\n", disk.NumBlocks(),
189  			disk.NumBlocks() * disk.BlockSize() / (1024.0*1024));
190  		if (disk.BlockBitmap() != NULL) {
191  			printf("  Used Blocks:\t\t%12Lu\t%10g MB\n",
192  				disk.BlockBitmap()->UsedBlocks(),
193  				disk.BlockBitmap()->UsedBlocks() * disk.BlockSize()
194  					/ (1024.0*1024));
195  			printf("  Free Blocks:\t\t%12Lu\t%10g MB\n",
196  				disk.BlockBitmap()->FreeBlocks(),
197  				disk.BlockBitmap()->FreeBlocks() * disk.BlockSize()
198  					/ (1024.0*1024));
199  		}
200  		int32 size
201  			= (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag);
202  		printf("  Bitmap Size:\t\t%ld bytes (%ld blocks, %ld per allocation "
203  			"group)\n", disk.BlockSize() * size, size,
204  			disk.SuperBlock()->blocks_per_ag);
205  		printf("  Allocation Groups:\t%lu\n\n", disk.AllocationGroups());
206  		dump_block_run("  Log:\t\t\t", disk.Log());
207  		printf("    (was %s)\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN
208  			? "cleanly unmounted" : "not unmounted cleanly!");
209  		dump_block_run("  Root Directory:\t", disk.Root());
210  		putchar('\n');
211  	} else if (dumpSuperBlock) {
212  		dump_super_block(disk.SuperBlock());
213  		putchar('\n');
214  	}
215  
216  	if (disk.ValidateSuperBlock() < B_OK) {
217  		fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS "
218  			"device)!\n");
219  		return 0;
220  	}
221  
222  	if (dumpRootNode) {
223  		bfs_inode inode;
224  		if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode,
225  				sizeof(bfs_inode)) < B_OK) {
226  			fprintf(stderr,"Could not read root node from disk!\n");
227  		} else {
228  			puts("Root node:\n-----------------------------------------");
229  			dump_inode(NULL, &inode, showOffsets);
230  			dump_indirect_stream(disk, &inode, showOffsets);
231  			putchar('\n');
232  		}
233  	}
234  
235  	char buffer[disk.BlockSize()];
236  	block_run run;
237  	Inode *inode = NULL;
238  
239  	if (dumpInode || dumpBTree || dumpHex || validateBTree) {
240  		// Set the block_run to the right value (as specified on the command
241  		// line)
242  		if (!argv[1]) {
243  			fprintf(stderr, "The -i/b/f options need the allocation group and "
244  				"starting offset (or the block number) of the node to dump!\n");
245  			return -1;
246  		}
247  		run = parseBlockRun(disk, argv[1], argv[2]);
248  
249  		if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) {
250  			fprintf(stderr,"Could not read node from disk!\n");
251  			return -1;
252  		}
253  
254  		inode = Inode::Factory(&disk, (bfs_inode *)buffer, false);
255  		if (inode == NULL || inode->InitCheck() < B_OK) {
256  			fprintf(stderr,"Not a valid inode!\n");
257  			delete inode;
258  			inode = NULL;
259  		}
260  	}
261  
262  	if (dumpInode) {
263  		printf("Inode at block %Ld:\n-----------------------------------------"
264  			"\n", disk.ToBlock(run));
265  		dump_inode(inode, (bfs_inode *)buffer, showOffsets);
266  		dump_indirect_stream(disk, (bfs_inode *)buffer, showOffsets);
267  		dump_small_data(inode);
268  		putchar('\n');
269  	}
270  
271  	if (dumpBTree && inode) {
272  		printf("B+Tree at block %Ld:\n----------------------------------------"
273  			"-\n", disk.ToBlock(run));
274  		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
275  			dump_bplustree(disk, (Directory *)inode, inode->Size(), dumpHex);
276  			putchar('\n');
277  		} else
278  			fprintf(stderr, "Inode is not a directory!\n");
279  	}
280  
281  	if (validateBTree && inode) {
282  		printf("Validating B+Tree at block %Ld:\n-----------------------------"
283  			"------------\n", disk.ToBlock(run));
284  		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
285  			BPlusTree *tree;
286  			if (((Directory *)inode)->GetTree(&tree) == B_OK) {
287  				if (tree->Validate(true) < B_OK)
288  					puts("B+Tree is corrupt!");
289  				else
290  					puts("B+Tree seems to be okay.");
291  			}
292  		} else
293  			fprintf(stderr, "Inode is not a directory!\n");
294  	}
295  
296  	if (dumpHex) {
297  		printf("Hexdump from inode at block %Ld:\n-----------------------------"
298  			"------------\n", disk.ToBlock(run));
299  		dump_block(buffer, disk.BlockSize());
300  		putchar('\n');
301  	}
302  
303  	delete inode;
304  
305  	return 0;
306  }
307  
308