xref: /haiku/src/bin/bfs_tools/bfsinfo.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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