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
20void
21dump_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
56void
57dump_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
97block_run
98parseBlockRun(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
113int
114main(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