xref: /haiku/src/bin/bfs_tools/bfsinfo.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
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 block_run
160 parseBlockRun(Disk &disk, char *first, char *last)
161 {
162 	char *comma;
163 
164 	if (last) {
165 		return block_run::Run(atol(first), atol(last), 1);
166 	} else if ((comma = strchr(first, ',')) != NULL) {
167 		*comma++ = '\0';
168 		return block_run::Run(atol(first), atol(comma));
169 	}
170 
171 	return disk.ToBlockRun(atoll(first));
172 }
173 
174 
175 int
176 main(int argc, char **argv)
177 {
178 	puts("Copyright (c) 2001-2010 pinc Software.");
179 
180 	if (argc < 2 || !strcmp(argv[1], "--help")) {
181 		char *filename = strrchr(argv[0],'/');
182 		fprintf(stderr,"usage: %s [-srib] <device> [allocation_group start]\n"
183 				"\t-s\tdump superblock\n"
184 				"\t-r\tdump root node\n"
185 				"       the following options need the allocation_group/start "
186 					"parameters:\n"
187 				"\t-i\tdump inode\n"
188 				"\t-b\tdump b+tree\n"
189 				"\t-v\tvalidate b+tree\n"
190 				"\t-h\thexdump\n"
191 				"\t-o\tshow disk offsets\n",
192 				filename ? filename + 1 : argv[0]);
193 		return -1;
194 	}
195 
196 	bool dumpRootNode = false;
197 	bool dumpInode = false;
198 	bool dumpSuperBlock = false;
199 	bool dumpBTree = false;
200 	bool validateBTree = false;
201 	bool dumpHex = false;
202 	bool showOffsets = false;
203 
204 	while (*++argv) {
205 		char *arg = *argv;
206 		if (*arg == '-') {
207 			while (*++arg && isalpha(*arg)) {
208 				switch (*arg) {
209 					case 's':
210 						dumpSuperBlock = true;
211 						break;
212 					case 'r':
213 						dumpRootNode = true;
214 						break;
215 					case 'i':
216 						dumpInode = true;
217 						break;
218 					case 'b':
219 						dumpBTree = true;
220 						break;
221 					case 'v':
222 						validateBTree = true;
223 						break;
224 					case 'h':
225 						dumpHex = true;
226 						break;
227 					case 'o':
228 						showOffsets = true;
229 						break;
230 				}
231 			}
232 		} else
233 			break;
234 	}
235 
236 	Disk disk(argv[0]);
237 	if (disk.InitCheck() < B_OK)
238 	{
239 		fprintf(stderr, "Could not open device or file: %s\n", strerror(disk.InitCheck()));
240 		return -1;
241 	}
242 	putchar('\n');
243 
244 	if (!dumpSuperBlock && !dumpRootNode && !dumpInode && !dumpBTree
245 		&& !dumpHex) {
246 		printf("  Name:\t\t\t\"%s\"\n", disk.SuperBlock()->name);
247 		printf("    (disk is %s)\n\n",
248 			disk.ValidateSuperBlock() == B_OK ? "valid" : "invalid!!");
249 		printf("  Block Size:\t\t%" B_PRIu32 " bytes\n", disk.BlockSize());
250 		printf("  Number of Blocks:\t%12" B_PRIdOFF "\t%10g MB\n",
251 			disk.NumBlocks(), disk.NumBlocks() * disk.BlockSize()
252 				/ (1024.0*1024));
253 		if (disk.BlockBitmap() != NULL) {
254 			printf("  Used Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n",
255 				disk.BlockBitmap()->UsedBlocks(),
256 				disk.BlockBitmap()->UsedBlocks() * disk.BlockSize()
257 					/ (1024.0*1024));
258 			printf("  Free Blocks:\t\t%12" B_PRIdOFF "\t%10g MB\n",
259 				disk.BlockBitmap()->FreeBlocks(),
260 				disk.BlockBitmap()->FreeBlocks() * disk.BlockSize()
261 					/ (1024.0*1024));
262 		}
263 		int32 size
264 			= (disk.AllocationGroups() * disk.SuperBlock()->blocks_per_ag);
265 		printf("  Bitmap Size:\t\t%" B_PRIu32 " bytes (%" B_PRId32 " blocks, %"
266 			B_PRId32 " per allocation group)\n", disk.BlockSize() * size, size,
267 			disk.SuperBlock()->blocks_per_ag);
268 		printf("  Allocation Groups:\t%" B_PRIu32 "\n\n",
269 			disk.AllocationGroups());
270 		dump_block_run("  Log:\t\t\t", disk.Log());
271 		printf("    (was %s)\n\n", disk.SuperBlock()->flags == SUPER_BLOCK_CLEAN
272 			? "cleanly unmounted" : "not unmounted cleanly!");
273 		dump_block_run("  Root Directory:\t", disk.Root());
274 		putchar('\n');
275 	} else if (dumpSuperBlock) {
276 		dump_super_block(disk.SuperBlock());
277 		putchar('\n');
278 	}
279 
280 	if (disk.ValidateSuperBlock() < B_OK) {
281 		fprintf(stderr, "The disk's superblock is corrupt (or it's not a BFS "
282 			"device)!\n");
283 		return 0;
284 	}
285 
286 	if (dumpRootNode) {
287 		bfs_inode inode;
288 		if (disk.ReadAt(disk.ToOffset(disk.Root()), (void *)&inode,
289 				sizeof(bfs_inode)) < B_OK) {
290 			fprintf(stderr,"Could not read root node from disk!\n");
291 		} else {
292 			puts("Root node:\n-----------------------------------------");
293 			dump_inode(NULL, &inode, showOffsets);
294 			dump_indirect_stream(disk, &inode, showOffsets);
295 			putchar('\n');
296 		}
297 	}
298 
299 	char buffer[disk.BlockSize()];
300 	bfs_inode* bfsInode = (bfs_inode*)buffer;
301 	block_run run;
302 	Inode *inode = NULL;
303 
304 	if (dumpInode || dumpBTree || dumpHex || validateBTree) {
305 		// Set the block_run to the right value (as specified on the command
306 		// line)
307 		if (!argv[1]) {
308 			fprintf(stderr, "The -i/b/f options need the allocation group and "
309 				"starting offset (or the block number) of the node to dump!\n");
310 			return -1;
311 		}
312 		run = parseBlockRun(disk, argv[1], argv[2]);
313 
314 		if (disk.ReadAt(disk.ToOffset(run), buffer, disk.BlockSize()) <= 0) {
315 			fprintf(stderr,"Could not read node from disk!\n");
316 			return -1;
317 		}
318 
319 		inode = Inode::Factory(&disk, bfsInode, false);
320 		if (inode == NULL || inode->InitCheck() < B_OK) {
321 			fprintf(stderr,"Not a valid inode!\n");
322 			delete inode;
323 			inode = NULL;
324 		}
325 	}
326 
327 	if (dumpInode) {
328 		printf("Inode at block %" B_PRIdOFF ":\n------------------------------"
329 			"-----------\n", disk.ToBlock(run));
330 		dump_inode(inode, bfsInode, showOffsets);
331 		dump_indirect_stream(disk, bfsInode, showOffsets);
332 		dump_double_indirect_stream(disk, bfsInode, showOffsets);
333 		dump_small_data(inode);
334 		putchar('\n');
335 	}
336 
337 	if (dumpBTree && inode != NULL) {
338 		printf("B+Tree at block %" B_PRIdOFF ":\n-----------------------------"
339 			"------------\n", disk.ToBlock(run));
340 		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
341 			dump_bplustree(disk, (Directory *)inode, inode->Size(), dumpHex);
342 			putchar('\n');
343 		} else
344 			fprintf(stderr, "Inode is not a directory!\n");
345 	}
346 
347 	if (validateBTree && inode != NULL) {
348 		printf("Validating B+Tree at block %" B_PRIdOFF ":\n------------------"
349 			"-----------------------\n", disk.ToBlock(run));
350 		if (inode->IsDirectory() || inode->IsAttributeDirectory()) {
351 			BPlusTree *tree;
352 			if (((Directory *)inode)->GetTree(&tree) == B_OK) {
353 				if (tree->Validate(true) < B_OK)
354 					puts("B+Tree is corrupt!");
355 				else
356 					puts("B+Tree seems to be okay.");
357 			}
358 		} else
359 			fprintf(stderr, "Inode is not a directory!\n");
360 	}
361 
362 	if (dumpHex) {
363 		printf("Hexdump from inode at block %" B_PRIdOFF ":\n-----------------"
364 			"------------------------\n", disk.ToBlock(run));
365 		dump_block(buffer, disk.BlockSize());
366 		putchar('\n');
367 	}
368 
369 	delete inode;
370 
371 	return 0;
372 }
373 
374