1
2#include <ctype.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10
11#define	int8	int8_t
12#define	int16	int16_t
13#define	int32	int32_t
14#define	int64	int64_t
15#define	uint8	uint8_t
16#define	uint16	uint16_t
17#define	uint32	uint32_t
18#define	uint64	uint64_t
19
20typedef int64 bfs_off_t;
21
22struct block_run {
23	int32		allocation_group;
24	uint16		start;
25	uint16		length;
26};
27
28typedef block_run inode_addr;
29
30#define BFS_DISK_NAME_LENGTH	32
31
32struct disk_super_block {
33	char		name[BFS_DISK_NAME_LENGTH];
34	int32		magic1;
35	int32		fs_byte_order;
36	uint32		block_size;
37	uint32		block_shift;
38	bfs_off_t	num_blocks;
39	bfs_off_t	used_blocks;
40	int32		inode_size;
41	int32		magic2;
42	int32		blocks_per_ag;
43	int32		ag_shift;
44	int32		num_ags;
45	int32		flags;
46	block_run	log_blocks;
47	bfs_off_t	log_start;
48	bfs_off_t	log_end;
49	int32		magic3;
50	inode_addr	root_dir;
51	inode_addr	indices;
52	int32		pad[8];
53};
54
55#define SUPER_BLOCK_MAGIC1			'BFS1'		/* BFS1 */
56
57const char *kHexDigits = "0123456789abcdef";
58
59
60static void
61read_from(int fd, off_t pos, void *buffer, size_t size)
62{
63	if (lseek(fd, pos, SEEK_SET) < 0
64		|| read(fd, buffer, size) != (int)size) {
65		fprintf(stderr, "Error: Failed to read %lu bytes at offset %lld\n",
66			size, pos);
67		exit(1);
68	}
69}
70
71
72static void
73write_to(int fd, off_t pos, const void *buffer, size_t size)
74{
75	if (lseek(fd, pos, SEEK_SET) < 0
76		|| write(fd, buffer, size) != (int)size) {
77		fprintf(stderr, "Error: Failed to write %lu bytes at offset %lld\n",
78			size, pos);
79		exit(1);
80	}
81}
82
83static int
84count_bits(int value)
85{
86	int count = 0;
87
88	while (value) {
89		if (value & 1)
90			count++;
91		value >>= 1;
92	}
93
94	return count;
95}
96
97int
98main(int argc, const char *const *argv)
99{
100	if (argc < 2 || argc > 3) {
101		fprintf(stderr, "Usage: %s <image> [ <hex OR pattern> ]\n", argv[0]);
102		exit(1);
103	}
104
105	const char *fileName = argv[1];
106	const char *patternString = (argc >= 3 ? argv[2] : "0f");
107
108	// skip leading "0x" in the pattern string
109	if (strncmp(patternString, "0x", 2) == 0)
110		patternString += 2;
111
112	// translate the pattern
113	uint8 *pattern = new uint8[strlen(patternString) / 2 + 1];
114	int patternLen = 0;
115	for (; *patternString; patternString++) {
116		if (!isspace(*patternString)) {
117			char c = *patternString;
118			const char *digitPos = strchr(kHexDigits, tolower(c));
119			if (!digitPos) {
120				fprintf(stderr, "Error: Invalid pattern character: \"%c\"\n",
121					c);
122				exit(1);
123			}
124			int digit = digitPos - kHexDigits;
125
126			if (patternLen & 1)
127				pattern[patternLen / 2] |= digit;
128			else
129				pattern[patternLen / 2] = digit << 4;
130			patternLen++;
131		}
132	}
133
134	if (patternLen == 0 || (patternLen & 1)) {
135		fprintf(stderr, "Error: Invalid pattern! Must have even number (>= 2) "
136			"of hex digits.\n");
137		exit(1);
138	}
139	patternLen /= 2;	// is now length in bytes
140
141	printf("bfs_fragmenter: using pattern: 0x");
142	for (int i = 0; i < patternLen; i++)
143		printf("%02x", pattern[i]);
144	printf("\n");
145
146	// open file
147	int fd = open(fileName, O_RDWR);
148	if (fd < 0) {
149		fprintf(stderr, "Error: Failed to open \"%s\": %s\n", fileName,
150			strerror(errno));
151		exit(1);
152	}
153
154	// read superblock
155	disk_super_block superBlock;
156	read_from(fd, 512, &superBlock, sizeof(superBlock));
157
158	if (superBlock.magic1 != SUPER_BLOCK_MAGIC1) {
159		fprintf(stderr, "Error: Bad superblock magic. This is probably not a "
160			"BFS image.\n");
161		exit(1);
162	}
163	int blockSize = superBlock.block_size;
164
165	// stat the image
166	struct stat st;
167	if (fstat(fd, &st) < 0) {
168		fprintf(stderr, "Error: Failed to stat image: %s\n", strerror(errno));
169		exit(1);
170	}
171
172	int64 blockCount = st.st_size / blockSize;
173	if (blockCount != superBlock.num_blocks) {
174		fprintf(stderr, "Error: Number of blocks in image and the number in "
175			"the superblock differ: %lld vs. %lld\n", blockCount,
176			superBlock.num_blocks);
177		exit(1);
178	}
179
180
181	// iterate through the block bitmap blocks and or the bytes with 0x0f
182	uint8 *block = new uint8[blockSize];
183	int64 occupiedBlocks = 0;
184	int64 bitmapByteCount = blockCount / 8;		// round down, we ignore the
185												// last partial byte
186	int64 bitmapBlockCount = (bitmapByteCount + blockSize - 1) / blockSize;
187
188	int bitmapByteIndex = 0;
189	for (int i = 1; i < bitmapBlockCount + 1; i++) {
190		off_t offset = i * blockSize;
191		read_from(fd, offset, block, blockSize);
192
193		int toProcess = blockSize;
194		if (toProcess > bitmapByteCount - bitmapByteIndex)
195			toProcess = (bitmapByteCount - bitmapByteIndex);
196
197		for (int k = 0; k < toProcess; k++) {
198			uint8 patternChar = pattern[bitmapByteIndex % patternLen];
199			occupiedBlocks += count_bits(~block[k] & patternChar);
200			block[k] |= patternChar;
201			bitmapByteIndex++;
202		}
203
204		write_to(fd, offset, block, blockSize);
205	}
206
207	printf("bfs_fragmenter: marked %lld blocks used\n", occupiedBlocks);
208
209	// write back the superblock
210	superBlock.used_blocks += occupiedBlocks;
211	write_to(fd, 512, &superBlock, sizeof(superBlock));
212
213	return 0;
214}
215
216