xref: /haiku/src/bin/bfs_tools/bfsinfo.cpp (revision 6889394848e2dc9f41ff53b12141d572822ca0c6)
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 ((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);
45 
46 		if (hexDump) {
47 			putchar('\n');
48 			dump_block((char *)node, header->node_size, 0);
49 		}
50 
51 		node = (bplustree_node *)((addr_t)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[%04" B_PRId32 "]            = ", i);
83 
84 		char buffer[256];
85 		if (showOffsets)
86 			snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, 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 void
98 dump_double_indirect_stream(Disk& disk, bfs_inode* node, bool showOffsets)
99 {
100 	if (node->data.max_double_indirect_range == 0)
101 		return;
102 
103 	int32 bytes = node->data.double_indirect.length * disk.BlockSize();
104 	int32 count = bytes / sizeof(block_run);
105 	block_run runs[count];
106 
107 	off_t offset = node->data.max_indirect_range;
108 
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));
114 		return;
115 	}
116 
117 	puts("double indirect stream:");
118 
119 	for (int32 i = 0; i < count; i++) {
120 		if (runs[i].IsZero())
121 			return;
122 
123 		printf("  double_indirect[%02" B_PRId32 "]       = ", i);
124 
125 		dump_block_run("", runs[i], "");
126 
127 		int32 indirectBytes = runs[i].length * disk.BlockSize();
128 		int32 indirectCount = indirectBytes / sizeof(block_run);
129 		block_run indirectRuns[indirectCount];
130 
131 		bytesRead = disk.ReadAt(disk.ToOffset(runs[i]), (uint8*)indirectRuns,
132 			indirectBytes);
133 		if (bytesRead < indirectBytes) {
134 			fprintf(stderr, "couldn't read double indirect runs: %s\n",
135 				strerror(bytesRead));
136 			continue;
137 		}
138 
139 		for (int32 j = 0; j < indirectCount; j++) {
140 			if (indirectRuns[j].IsZero())
141 				break;
142 
143 			printf("                     [%04" B_PRId32 "] = ", j);
144 
145 			char buffer[256];
146 			if (showOffsets)
147 				snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
148 			else
149 				buffer[0] = '\0';
150 
151 			dump_block_run("", indirectRuns[j], buffer);
152 
153 			offset += indirectRuns[j].length * disk.BlockSize();
154 		}
155 	}
156 }
157 
158 
159 void
160 list_bplustree(Disk& disk, Directory* directory, off_t size)
161 {
162 	directory->Rewind();
163 
164 	char name[B_FILE_NAME_LENGTH];
165 	char buffer[512];
166 	uint64 count = 0;
167 	block_run run;
168 	while (directory->GetNextEntry(name, &run) == B_OK) {
169 		snprintf(buffer, sizeof(buffer), " %s", name);
170 		dump_block_run("", run, buffer);
171 		count++;
172 	}
173 
174 	printf("--\n%lld items.\n", count);
175 }
176 
177 
178 void
179 count_bplustree(Disk& disk, Directory* directory, off_t size)
180 {
181 	directory->Rewind();
182 
183 	char name[B_FILE_NAME_LENGTH];
184 	uint64 count = 0;
185 	block_run run;
186 	while (directory->GetNextEntry(name, &run) == B_OK)
187 		count++;
188 
189 	printf("%lld items.\n", count);
190 }
191 
192 
193 block_run
194 parseBlockRun(Disk &disk, char *first, char *last)
195 {
196 	char *comma;
197 
198 	if (last) {
199 		return block_run::Run(atol(first), atol(last), 1);
200 	} else if ((comma = strchr(first, ',')) != NULL) {
201 		*comma++ = '\0';
202 		return block_run::Run(atol(first), atol(comma));
203 	}
204 
205 	return disk.ToBlockRun(atoll(first));
206 }
207 
208 
209 int
210 main(int argc, char **argv)
211 {
212 	puts("Copyright (c) 2001-2010 pinc Software.");
213 
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 "
220 					"parameters:\n"
221 				"\t-i\tdump inode\n"
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"
226 				"\t-h\thexdump\n"
227 				"\t-o\tshow disk offsets\n",
228 				filename ? filename + 1 : argv[0]);
229 		return -1;
230 	}
231 
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;
241 
242 	while (*++argv) {
243 		char *arg = *argv;
244 		if (*arg == '-') {
245 			while (*++arg && isalpha(*arg)) {
246 				switch (*arg) {
247 					case 's':
248 						dumpSuperBlock = true;
249 						break;
250 					case 'r':
251 						dumpRootNode = true;
252 						break;
253 					case 'i':
254 						dumpInode = true;
255 						break;
256 					case 'b':
257 						dumpBTree = true;
258 						break;
259 					case 'l':
260 						listBTree = true;
261 						break;
262 					case 'c':
263 						countBTree = true;
264 						break;
265 					case 'v':
266 						validateBTree = true;
267 						break;
268 					case 'h':
269 						dumpHex = true;
270 						break;
271 					case 'o':
272 						showOffsets = true;
273 						break;
274 				}
275 			}
276 		} else
277 			break;
278 	}
279 
280 	Disk disk(argv[0]);
281 	if (disk.InitCheck() < B_OK)
282 	{
283 		fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
284 		return -1;
285 	}
286 	putchar('\n');
287 
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()
296 				/ (1024.0*1024));
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()
301 					/ (1024.0*1024));
302 			printf("  Free Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n",
303 				disk.BlockBitmap()->FreeBlocks(),
304 				disk.BlockBitmap()->FreeBlocks() * disk.BlockSize()
305 					/ (1024.0*1024));
306 		}
307 		int32 size
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());
318 		putchar('\n');
319 	} else if (dumpSuperBlock) {
320 		dump_super_block(disk.SuperBlock());
321 		putchar('\n');
322 	}
323 
324 	if (disk.ValidateSuperBlock() < B_OK) {
325 		fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS "
326 			"device)!\n");
327 		return 0;
328 	}
329 
330 	if (dumpRootNode) {
331 		bfs_inode inode;
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");
335 		} else {
336 			puts("Root node:\n-----------------------------------------");
337 			dump_inode(NULL, &inode, showOffsets);
338 			dump_indirect_stream(disk, &inode, showOffsets);
339 			putchar('\n');
340 		}
341 	}
342 
343 	char buffer[disk.BlockSize()];
344 	bfs_inode* bfsInode = (bfs_inode*)buffer;
345 	block_run run;
346 	Inode *inode = NULL;
347 
348 	if (dumpInode || dumpBTree || dumpHex || validateBTree || listBTree
349 		|| countBTree) {
350 		// Set the block_run to the right value (as specified on the command
351 		// line)
352 		if (!argv[1]) {
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");
355 			return -1;
356 		}
357 		run = parseBlockRun(disk, argv[1], argv[2]);
358 
359 		if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) {
360 			fprintf(stderr,"Could not read node from disk!\n");
361 			return -1;
362 		}
363 
364 		inode = Inode::Factory(&disk, bfsInode, false);
365 		if (inode == NULL || inode->InitCheck() < B_OK) {
366 			fprintf(stderr,"Not a valid inode!\n");
367 			delete inode;
368 			inode = NULL;
369 		}
370 	}
371 
372 	if (dumpInode) {
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);
379 		putchar('\n');
380 	}
381 
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);
387 			putchar('\n');
388 		} else
389 			fprintf(stderr, "Inode is not a directory!\n");
390 	}
391 
392 	if (listBTree && inode != NULL) {
393 		printf("Directory contents: ------------------------------------------\n");
394 		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
395 			list_bplustree(disk, (Directory*)inode, inode->Size());
396 			putchar('\n');
397 		} else
398 			fprintf(stderr, "Inode is not a directory!\n");
399 	}
400 
401 	if (countBTree && inode != NULL) {
402 		printf("Count contents: ------------------------------------------\n");
403 		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
404 			count_bplustree(disk, (Directory*)inode, inode->Size());
405 			putchar('\n');
406 		} else
407 			fprintf(stderr, "Inode is not a directory!\n");
408 	}
409 
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()) {
414 			BPlusTree *tree;
415 			if (((Directory *)inode)->GetTree(&tree) == B_OK) {
416 				if (tree->Validate(true) < B_OK)
417 					puts("B+Tree is corrupt!");
418 				else
419 					puts("B+Tree seems to be okay.");
420 			}
421 		} else
422 			fprintf(stderr, "Inode is not a directory!\n");
423 	}
424 
425 	if (dumpHex) {
426 		printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------"
427 			"------------------------\n", disk.ToBlock(run));
428 		dump_block(buffer, disk.BlockSize());
429 		putchar('\n');
430 	}
431 
432 	delete inode;
433 
434 	return 0;
435 }
436 
437