xref: /haiku/src/bin/bfs_tools/bfsinfo.cpp (revision 272510c3719bdc2f504c7ff258819ddf134c2413)
1 /*
2  * Copyright 2001-2010 pinc Software. All Rights Reserved.
3  * Released under the terms of the MIT license.
4  */
5 
6 
7 //!	Dumps various information about BFS volumes.
8 
9 
10 #include "Disk.h"
11 #include "BPlusTree.h"
12 #include "Inode.h"
13 #include "dump.h"
14 
15 #include <StringForSize.h>
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 
22 
23 using namespace BPrivate;
24 
25 
26 void
dump_bplustree(Disk & disk,BPositionIO * file,off_t size,bool hexDump)27 dump_bplustree(Disk &disk, BPositionIO *file, off_t size, bool hexDump)
28 {
29 	uint8 *buffer = (uint8 *)malloc(size);
30 	if (buffer == NULL) {
31 		puts("no buffer");
32 		return;
33 	}
34 
35 	if (file->ReadAt(0, buffer, size) != size) {
36 		puts("couldn't read whole file");
37 		return;
38 	}
39 
40 	bplustree_header *header = (bplustree_header *)buffer;
41 	int32 nodeSize = header->node_size;
42 
43 	dump_bplustree_header(header);
44 
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);
51 
52 		if (hexDump) {
53 			putchar('\n');
54 			dump_block((char *)node, header->node_size, 0);
55 		}
56 
57 		node = (bplustree_node *)((addr_t)node + nodeSize);
58 	}
59 }
60 
61 
62 void
dump_indirect_stream(Disk & disk,bfs_inode * node,bool showOffsets)63 dump_indirect_stream(Disk &disk, bfs_inode *node, bool showOffsets)
64 {
65 	if (node->data.max_indirect_range == 0)
66 		return;
67 
68 	int32 bytes = node->data.indirect.length * disk.BlockSize();
69 	int32 count = bytes / sizeof(block_run);
70 	block_run runs[count];
71 
72 	off_t offset = node->data.max_direct_range;
73 
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;
80 	}
81 
82 	puts("indirect stream:");
83 
84 	for (int32 i = 0; i < count; i++) {
85 		if (runs[i].IsZero())
86 			return;
87 
88 		printf("  indirect[%04" B_PRId32 "]            = ", i);
89 
90 		char buffer[256];
91 		if (showOffsets)
92 			snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
93 		else
94 			buffer[0] = '\0';
95 
96 		dump_block_run("", runs[i], buffer);
97 
98 		offset += runs[i].length * disk.BlockSize();
99 	}
100 }
101 
102 
103 void
dump_double_indirect_stream(Disk & disk,bfs_inode * node,bool showOffsets)104 dump_double_indirect_stream(Disk& disk, bfs_inode* node, bool showOffsets)
105 {
106 	if (node->data.max_double_indirect_range == 0)
107 		return;
108 
109 	int32 bytes = node->data.double_indirect.length * disk.BlockSize();
110 	int32 count = bytes / sizeof(block_run);
111 	block_run runs[count];
112 
113 	off_t offset = node->data.max_indirect_range;
114 
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;
121 	}
122 
123 	puts("double indirect stream:");
124 
125 	for (int32 i = 0; i < count; i++) {
126 		if (runs[i].IsZero())
127 			return;
128 
129 		printf("  double_indirect[%02" B_PRId32 "]       = ", i);
130 
131 		dump_block_run("", runs[i], "");
132 
133 		int32 indirectBytes = runs[i].length * disk.BlockSize();
134 		int32 indirectCount = indirectBytes / sizeof(block_run);
135 		block_run indirectRuns[indirectCount];
136 
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;
143 		}
144 
145 		for (int32 j = 0; j < indirectCount; j++) {
146 			if (indirectRuns[j].IsZero())
147 				break;
148 
149 			printf("                     [%04" B_PRId32 "] = ", j);
150 
151 			char buffer[256];
152 			if (showOffsets)
153 				snprintf(buffer, sizeof(buffer), " %16" B_PRIdOFF, offset);
154 			else
155 				buffer[0] = '\0';
156 
157 			dump_block_run("", indirectRuns[j], buffer);
158 
159 			offset += indirectRuns[j].length * disk.BlockSize();
160 		}
161 	}
162 }
163 
164 
165 void
list_bplustree(Disk & disk,Directory * directory,off_t size)166 list_bplustree(Disk& disk, Directory* directory, off_t size)
167 {
168 	directory->Rewind();
169 
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++;
178 	}
179 
180 	printf("--\n%" B_PRId64 " items.\n", count);
181 }
182 
183 
184 void
count_bplustree(Disk & disk,Directory * directory,off_t size)185 count_bplustree(Disk& disk, Directory* directory, off_t size)
186 {
187 	directory->Rewind();
188 
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++;
194 
195 	printf("%" B_PRId64 " items.\n", count);
196 }
197 
198 
199 block_run
parseBlockRun(Disk & disk,char * first,char * last)200 parseBlockRun(Disk &disk, char *first, char *last)
201 {
202 	char *comma;
203 
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));
209 	}
210 
211 	return disk.ToBlockRun(atoll(first));
212 }
213 
214 
215 int
main(int argc,char ** argv)216 main(int argc, char **argv)
217 {
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;
234 	}
235 
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;
245 
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;
278 				}
279 			}
280 		} else
281 			break;
282 	}
283 
284 	Disk disk(argv[0]);
285 	if (disk.InitCheck() < B_OK)
286 	{
287 		fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
288 		return -1;
289 	}
290 	putchar('\n');
291 
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);
308 
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);
313 		}
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');
330 	}
331 
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;
336 	}
337 
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');
348 		}
349 	}
350 
351 	char buffer[disk.BlockSize()];
352 	bfs_inode* bfsInode = (bfs_inode*)buffer;
353 	block_run run;
354 	Inode *inode = NULL;
355 
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;
364 		}
365 		run = parseBlockRun(disk, argv[1], argv[2]);
366 
367 		if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) {
368 			fprintf(stderr,"Could not read node from disk!\n");
369 			return -1;
370 		}
371 
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;
377 		}
378 	}
379 
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');
388 	}
389 
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");
398 	}
399 
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");
407 	}
408 
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");
416 	}
417 
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.");
428 			}
429 		} else
430 			fprintf(stderr, "Inode is not a directory!\n");
431 	}
432 
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');
438 	}
439 
440 	delete inode;
441 
442 	return 0;
443 }
444 
445