1/*
2 * Copyright 2005, Axel D��rfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "bfs.h"
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <string.h>
12#include <unistd.h>
13#include <fcntl.h>
14#include <errno.h>
15#include <ctype.h>
16
17
18extern const char *__progname;
19static const char *sProgramName = __progname;
20
21static bool sDumpBlocks = false;
22
23
24bool
25disk_super_block::IsValid()
26{
27	if (Magic1() != (int32)SUPER_BLOCK_MAGIC1
28		|| Magic2() != (int32)SUPER_BLOCK_MAGIC2
29		|| Magic3() != (int32)SUPER_BLOCK_MAGIC3
30		|| (int32)block_size != inode_size
31		|| ByteOrder() != SUPER_BLOCK_FS_LENDIAN
32		|| (1UL << BlockShift()) != BlockSize()
33		|| AllocationGroups() < 1
34		|| AllocationGroupShift() < 1
35		|| BlocksPerAllocationGroup() < 1
36		|| NumBlocks() < 10
37		|| AllocationGroups() != divide_roundup(NumBlocks(),
38			1L << AllocationGroupShift()))
39		return false;
40
41	return true;
42}
43
44
45off_t
46toBlock(disk_super_block &superBlock, block_run run)
47{
48	return ((((off_t)run.AllocationGroup()) << superBlock.AllocationGroupShift()) | (off_t)run.Start());
49}
50
51
52off_t
53toOffset(disk_super_block &superBlock, block_run run)
54{
55	return toBlock(superBlock, run) << superBlock.BlockShift();
56}
57
58
59void
60dumpBlock(const char *buffer, int size)
61{
62	const int blockSize = 16;
63
64	printf("\t");
65
66	for (int i = 0; i < size;) {
67		int start = i;
68
69		for (; i < start + blockSize; i++) {
70			if (!(i % 4))
71				printf(" ");
72
73			if (i >= size)
74				printf("  ");
75			else
76				printf("%02x", *(unsigned char *)(buffer+i));
77		}
78		printf("  ");
79
80		for (i = start; i < start + blockSize; i++) {
81			if (i < size) {
82				char c = *(buffer+i);
83
84				if (c < 30)
85					printf(".");
86				else
87					printf("%c", c);
88			}
89			else
90				break;
91		}
92
93		if (i < size)
94			printf("\n\t");
95		else
96			printf("\n");
97	}
98}
99
100
101void
102dumpLogEntry(int device, disk_super_block &superBlock, int32 &start, uint8 *block)
103{
104	int32 blockSize = superBlock.BlockSize();
105	int32 entry = 0;
106	off_t count = 1;
107	int32 arrayBlocks = 1;
108	off_t blockStart = start;
109	bool first = true;
110	int32 indexOffset = -1;
111	int32 index = 0;
112	off_t dataStart = blockStart;
113	off_t bitmapSize = superBlock.AllocationGroups() * superBlock.BlocksPerAllocationGroup();
114
115	uint8 *data = NULL;
116	if (sDumpBlocks)
117		data = (uint8 *)malloc(blockSize);
118
119	for (int32 i = 0; i < arrayBlocks; i++) {
120		off_t blockNumber = toBlock(superBlock, superBlock.log_blocks)
121			+ blockStart++ % superBlock.log_blocks.Length();
122		if (read_pos(device, blockNumber << superBlock.BlockShift(),
123				block, blockSize) != blockSize) {
124			fprintf(stderr, "%s: could not read block %lld.\n", sProgramName, blockNumber);
125			exit(-1);
126		}
127
128		off_t *array = (off_t *)block;
129		int32 arraySize = blockSize / sizeof(off_t);
130
131		if (first) {
132			count = array[0];
133			arrayBlocks = ((count + 1) * sizeof(off_t) + blockSize - 1) / blockSize;
134			arraySize--;
135			dataStart += arrayBlocks;
136
137			start += arrayBlocks + count;
138
139			printf("Entry %ld contains %lld blocks (entry starts at block %lld):\n", entry, count, blockNumber);
140			first = false;
141		} else
142			indexOffset += arraySize;
143
144		for (; index < count; index++) {
145			int32 arrayIndex = index - indexOffset;
146			if (arrayIndex >= arraySize)
147				break;
148
149			off_t value = array[arrayIndex];
150			printf("%7ld: %lld%s\n", index, value, value == 0 ? " (superblock)" :
151				value < bitmapSize + 1 ? " (bitmap block)" : "");
152
153			if (data != NULL) {
154				off_t blockNumber = toBlock(superBlock, superBlock.log_blocks)
155					+ ((dataStart + index) % superBlock.log_blocks.Length());
156				if (read_pos(device, blockNumber << superBlock.BlockShift(),
157						data, blockSize) != blockSize) {
158					fprintf(stderr, "%s: could not read block %lld.\n", sProgramName, blockNumber);
159				} else
160					dumpBlock((char *)data, blockSize);
161			}
162		}
163	}
164
165	free(data);
166}
167
168
169void
170usage(void)
171{
172	fprintf(stderr, "usage: %s [-d] bfs-volume\n"
173		"    -d\tdump blocks in log\n", sProgramName);
174	exit(0);
175}
176
177
178int
179main(int argc, char **argv)
180{
181	if (argc < 2 || !strcmp(argv[1], "--help"))
182		usage();
183
184	while (*++argv) {
185		char *arg = *argv;
186		if (*arg == '-') {
187			while (*++arg && isalpha(*arg)) {
188				switch (*arg) {
189					case 'd':
190						sDumpBlocks = true;
191						break;
192					default:
193						usage();
194				}
195			}
196		}
197		else
198			break;
199	}
200
201	char *devicePath = argv[0];
202	int device = open(devicePath, O_RDONLY);
203	if (device < 0) {
204		fprintf(stderr, "%s: could not open device %s: %s\n",
205			sProgramName, devicePath, strerror(errno));
206		return -1;
207	}
208
209	disk_super_block superBlock;
210	if (read_pos(device, 512, &superBlock, sizeof(disk_super_block)) < (ssize_t)sizeof(disk_super_block)) {
211		fprintf(stderr, "%s: could not read superblock.\n", sProgramName);
212		return -1;
213	}
214
215	if (!superBlock.IsValid()) {
216		fprintf(stderr, "%s: invalid superblock!\n", sProgramName);
217		return -1;
218	}
219
220	if (superBlock.Flags() == SUPER_BLOCK_DISK_CLEAN) {
221		printf("volume is clean; log is empty.\n");
222		return 0;
223	}
224
225	off_t bitmapSize = superBlock.AllocationGroups() * superBlock.BlocksPerAllocationGroup();
226	printf("Log area = (%lld - %lld), current start = %lld, end = %lld, %lld bitmap blocks\n",
227		toBlock(superBlock, superBlock.log_blocks),
228		toBlock(superBlock, superBlock.log_blocks) + superBlock.log_blocks.Length(),
229		superBlock.LogStart(), superBlock.LogEnd(), bitmapSize);
230
231	uint8 *block = (uint8 *)malloc(superBlock.BlockSize());
232	if (block == NULL) {
233		fprintf(stderr, "%s: could not allocate block!\n", sProgramName);
234		return -1;
235	}
236
237	int32 start = superBlock.LogStart();
238	int32 lastStart = -1;
239
240	while (true) {
241		if (start == superBlock.LogEnd())
242			break;
243		if (start == lastStart)
244			break;
245
246		lastStart = start;
247		dumpLogEntry(device, superBlock, start, block);
248	}
249
250	close(device);
251	return 0;
252}
253
254