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
23using namespace BPrivate;
24
25
26void
27dump_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
62void
63dump_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
103void
104dump_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
165void
166list_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
184void
185count_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
199block_run
200parseBlockRun(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
215int
216main(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