1 #define _LARGEFILE64_SOURCE
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 
11 #include "io.h"
12 #include "dev.h"
13 #include "gen.h"
14 #include "utils.h"
15 #include "error.h"
16 
17 
18 char verbose = 0;
19 
20 typedef struct
21 {
22 	off64_t	offset;
23 	off64_t block_size;
24 } badblock;
25 
26 typedef struct
27 {
28 	badblock *bb_list;
29 	int bb_list_size;
30 	int fd;	/* fd of image */
31 	char *filename;
32 	off64_t	size; /* size of image */
33 } image;
34 
version(void)35 void version(void)
36 {
37 	printf("mergebad v" VERSION ", (c) 2004 by folkert@vanheusden.com\n\n");
38 }
39 
find_image_without_badblock(image * imgs,int n_imgs,off64_t offset)40 int find_image_without_badblock(image *imgs, int n_imgs, off64_t offset)
41 {
42 	int img_index, badblock_index;
43 
44 	if (verbose >= 3)
45 		printf("> find_image_without_badblock(%p %d %lld)\n", imgs, n_imgs, offset);
46 
47 	/* for each image... */
48 	for(img_index=0; img_index<n_imgs; img_index++)
49 	{
50 		char ok = 1;
51 
52 		/* for each badblock in an image... */
53 		for(badblock_index=0; badblock_index<imgs[img_index].bb_list_size; badblock_index++)
54 		{
55 			off64_t cur_offset = imgs[img_index].bb_list[badblock_index].offset;
56 			off64_t cur_bb_end = cur_offset + imgs[img_index].bb_list[badblock_index].block_size;
57 
58 			if (verbose >= 4)
59 				printf("= %d/%d, %lld - %lld\n", img_index, badblock_index, cur_offset, cur_bb_end);
60 
61 			/* see if the current offset is somewhere in the current bad block */
62 			if (offset >= cur_offset && offset < cur_bb_end)
63 			{
64 				/* yes, offset is in a badblock for image with index 'img_index' and its badblockindex
65 				 * is 'badblock_index', so we're not going to use this image
66 				 */
67 				ok = 0;
68 				break;
69 			}
70 			/* see if the offset of the current badblock is beyond the current offset: we can then
71 			 * stop searching for this image: since this image seems not to have any badblocks for
72 			 * offset 'offset', we can use this image
73 			 */
74 			else if (cur_offset > offset)
75 			{
76 				break;
77 			}
78 		}
79 
80 		/* is this an image we can use? */
81 		if (ok)
82 		{
83 			if (verbose >= 3)
84 				printf("< find_image_without_badblock: %d\n", img_index);
85 
86 			return img_index;
87 		}
88 	}
89 
90 	if (verbose >= 3)
91 		printf("< find_image_without_badblock: %d\n", img_index);
92 
93 	/* no image found */
94 	return -1;
95 }
96 
find_smallest_current_badblock(image * imgs,int n_imgs,off64_t offset,int * selected_image,int * selected_badblock)97 void find_smallest_current_badblock(image *imgs, int n_imgs, off64_t offset, int *selected_image, int *selected_badblock)
98 {
99 	off64_t smallest_block_size = (off64_t)1 << ((sizeof(off64_t) * 8) - 2); /* better not be > 2^62 bytes! */
100 	int img_index, badblock_index;
101 
102 	if (verbose >= 3)
103 		printf("> find_smallest_current_badblock(%p %d %lld %p %p)\n", imgs, n_imgs, offset, selected_image, selected_badblock);
104 
105 	*selected_image = -1;
106 	*selected_badblock = -1;
107 
108 	/* for each image... */
109 	for(img_index=0; img_index<n_imgs; img_index++)
110 	{
111 		/* find badblock (if any) containing a badblock containing the current offset */
112 		for(badblock_index=0; badblock_index<imgs[img_index].bb_list_size; badblock_index++)
113 		{
114 			off64_t cur_offset = imgs[img_index].bb_list[badblock_index].offset;
115 			off64_t cur_bb_end = cur_offset + imgs[img_index].bb_list[badblock_index].block_size;
116 
117 			if (verbose >= 4)
118 				printf("= %d/%d, %lld - %lld\n", img_index, badblock_index, cur_offset, cur_bb_end);
119 
120 			/* see if the current offset is somewhere in the current bad block */
121 			if (offset >= cur_offset && offset < cur_bb_end)
122 			{
123 				off64_t block_size_left = cur_bb_end - offset;
124 
125 				/* # bytes left for current badblock relative to current offset: we
126 				 * might have been able to get correct bytes (from another image) that
127 				 * (partially) overlaps the current badblock
128 				 */
129 				if (verbose >= 4)
130 					printf("= # bytes left: %lld\n", block_size_left);
131 
132 				/* is the end of this badblock nearer then the previous found one? (if any) */
133 				if (block_size_left < smallest_block_size)
134 				{
135 					if (verbose >= 4)
136 						printf("! block selected\n");
137 
138 					*selected_image = img_index;
139 					*selected_badblock = badblock_index;
140 					smallest_block_size = block_size_left;
141 					break;
142 				}
143 			}
144 		}
145 	}
146 
147 	if (verbose >= 3)
148 		printf("> find_smallest_current_badblock: %d %d (%lld)\n", *selected_image, *selected_badblock, smallest_block_size);
149 }
150 
find_badblock_after_offset(badblock * bb_list,int bb_list_size,off64_t offset)151 int find_badblock_after_offset(badblock *bb_list, int bb_list_size, off64_t offset)
152 {
153 	int badblock_index;
154 
155 	/* search from start of list (the lowest offset) upto the end */
156 	for(badblock_index=0; badblock_index<bb_list_size; badblock_index++)
157 	{
158 		if (bb_list[badblock_index].offset >= offset)
159 			return badblock_index;
160 	}
161 
162 	return -1;
163 }
164 
read_mapfile(char * filename,badblock ** pbb,int * nbb)165 int read_mapfile(char *filename, badblock **pbb, int *nbb)
166 {
167 	int n_bb = 0;
168 	badblock *bbs = NULL;
169 	FILE *fh;
170 
171 	if (verbose >= 2)
172 		printf("Reading mapfile from %s\n", filename);
173 
174 	fh = fopen(filename, "rb");
175 	if (!fh)
176 	{
177 		fprintf(stderr, "Problem opening %s for read! %s (%d)\n", filename, strerror(errno), errno);
178 		return -1;
179 	}
180 #if (_XOPEN_VERSION >= 600)
181 	(void)posix_fadvise(fileno(fh), 0, 0, POSIX_FADV_SEQUENTIAL); // or POSIX_FADV_NOREUSE?
182 #endif
183 
184 	while(!feof(fh))
185 	{
186 		off64_t	offset;
187 		int block_size;
188 
189 		fscanf(fh, "%lld %d", &offset, &block_size);
190 
191 		bbs = (badblock *)myrealloc(bbs, sizeof(badblock) * (n_bb + 1), "badblocks list");
192 		bbs[n_bb].offset = offset;
193 		bbs[n_bb].block_size = block_size;
194 
195 		if (verbose >= 4)
196 			printf("%d] %lld %d\n", n_bb, offset, block_size);
197 
198 		n_bb++;
199 	}
200 
201 	*pbb = bbs;
202 	*nbb = n_bb;
203 
204 	return 0;
205 }
206 
select_most_occuring_byte(unsigned char * bytes,int n_imgs)207 char select_most_occuring_byte(unsigned char *bytes, int n_imgs)
208 {
209 	char selected_byte = 0;
210 	int loop;
211 	int counters[256];
212 	int count_used = -1;
213 
214 	memset(counters, 0x00, sizeof(counters));
215 
216 	/* count frequency for byte in each file */
217 	for(loop=0; loop<n_imgs; loop++)
218 	{
219 		counters[bytes[loop]]++;
220 	}
221 
222 	/* find most used byte */
223 	for(loop=0; loop<256; loop++)
224 	{
225 		if (counters[loop] > count_used)
226 		{
227 			count_used = counters[loop];
228 			selected_byte = loop;
229 		}
230 		/* what to do with '== count_used'? ... */
231 	}
232 
233 	/* no bytes? that is definately an error :-) */
234 	if (count_used == -1)
235 		error_exit("Internal error: no byte found!");
236 
237 	return selected_byte;
238 }
239 
usage(void)240 void usage(void)
241 {
242 	fprintf(stderr, "-i <mapfile> <imagefile>\n");
243 	fprintf(stderr, "		With -i one selects a mapfile+imagefile to read.\n");
244 	fprintf(stderr, "-o <outputfile>\n");
245 	fprintf(stderr, "		File to write output to.\n");
246 	fprintf(stderr, "-l <mapfile>	In case all inputimages had a badblock on the same\n");
247 	fprintf(stderr, "		place, this file will list those blocks.\n");
248 	fprintf(stderr, "-s <size>	Limit or extend the size of the outputimage.\n");
249 	fprintf(stderr, "-v		Be verbose.\n");
250 	fprintf(stderr, "-h		This help.\n");
251 }
252 
main(int argc,char * argv[])253 int main(int argc, char *argv[])
254 {
255 	image *imgs = NULL;
256 	int n_imgs = 0;
257 	int loop;
258 	int fd_out = -1;
259 	off64_t offset = 0, length = 0;
260 	char *map_file = NULL;
261 	FILE *fh_map_file = NULL;
262 
263 	version();
264 
265 	for(loop=1; loop<argc; loop++)
266 	{
267 		if (strcmp(argv[loop], "-i") == 0)
268 		{
269 			imgs = myrealloc(imgs, sizeof(image) * (n_imgs + 1), "image structure");
270 
271 			if (read_mapfile(argv[++loop], &imgs[n_imgs].bb_list, &imgs[n_imgs].bb_list_size) == -1)
272 				return 1;
273 
274 			imgs[n_imgs].fd = open64(argv[++loop], O_RDONLY);
275 			if (imgs[n_imgs].fd == -1)
276 			{
277 				fprintf(stderr, "Failed to open file %s: %s (%d)\n", argv[loop], strerror(errno), errno);
278 				return 2;
279 			}
280 #if (_XOPEN_VERSION >= 600)
281 	(void)posix_fadvise(imgs[n_imgs].fd, 0, 0, POSIX_FADV_SEQUENTIAL); // or POSIX_FADV_NOREUSE?
282 #endif
283 
284 			/* remember filename */
285 			imgs[n_imgs].filename = argv[loop];
286 			/* get size of this image */
287 			imgs[n_imgs].size = get_filesize(argv[loop]);
288 
289 			if (verbose)
290 				printf("Adding image: %s/%lld number of badblocks: %d\n", imgs[n_imgs].filename, imgs[n_imgs].size, imgs[n_imgs].bb_list_size);
291 
292 			n_imgs++;
293 		}
294 		else if (strcmp(argv[loop], "-o") == 0)
295 		{
296 			fd_out = open64(argv[++loop], O_WRONLY | O_CREAT | O_EXCL | O_FSYNC, S_IRUSR | S_IWUSR);
297 			if (fd_out == -1)
298 			{
299 				fprintf(stderr, "Failed to create file %s: %s (%d)\n", argv[loop], strerror(errno), errno);
300 				return 3;
301 			}
302 #if (_XOPEN_VERSION >= 600)
303 	(void)posix_fadvise(fd_out, 0, 0, POSIX_FADV_SEQUENTIAL); // or POSIX_FADV_NOREUSE?
304 #endif
305 
306 			if (verbose)
307 				printf("Writing output to: %s\n", argv[loop]);
308 		}
309 		else if (strcmp(argv[loop], "-l") == 0)
310 		{
311 			map_file = argv[++loop];
312 		}
313 		else if (strcmp(argv[loop], "-s") == 0)
314 		{
315 			length = strtoll(argv[++loop], (char **)NULL, 10);
316 
317 			if (verbose)
318 				printf("Length set to: %lld\n", length);
319 		}
320 		else if (strcmp(argv[loop], "-v") == 0)
321 		{
322 			verbose++;
323 		}
324 		else if (strcmp(argv[loop], "-h") == 0)
325 		{
326 			usage();
327 			return 0;
328 		}
329 		else
330 		{
331 			fprintf(stderr, "Parameter '%s' is not recognized!\n", argv[loop]);
332 			return 9;
333 		}
334 	}
335 
336 	if (n_imgs < 2)
337 	{
338 		fprintf(stderr, "Not enough input-images selected! (at least 2 required)\n");
339 		return 10;
340 	}
341 
342 	if (map_file)
343 	{
344 		fh_map_file = fopen(map_file, "w");
345 		if (!fh_map_file)
346 		{
347 			fprintf(stderr, "Problem creating mapfile %s: %s (%d)\n", map_file, strerror(errno), errno);
348 			return 11;
349 		}
350 #if (_XOPEN_VERSION >= 600)
351 	(void)posix_fadvise(fileno(fh_map_file), 0, 0, POSIX_FADV_SEQUENTIAL); // or POSIX_FADV_NOREUSE?
352 #endif
353 	}
354 
355 	if (length == (off64_t)0)
356 	{
357 		printf("No filelength given, using length of file %s: %lld\n", imgs[0].filename, (length = get_filesize(imgs[0].filename)));
358 	}
359 
360 	for(offset=0; offset<length;)
361 	{
362 		int img_index, badblock_index;
363 
364 		if (verbose >= 2)
365 			printf("Current offset: %lld\n", offset);
366 
367 		/* find image which has no badblock at the current offset */
368 		img_index = find_image_without_badblock(imgs, n_imgs, offset);
369 
370 		/* image found? then copy upto the next badblock */
371 		if (img_index != -1)
372 		{
373 			/* find next badblock */
374 			int next_badblock = find_badblock_after_offset(imgs[img_index].bb_list, imgs[img_index].bb_list_size, offset);
375 			off64_t n_bytes_to_copy;
376 
377 			/* no more badblocks in this image: copy image upto its end */
378 			if (next_badblock == -1)
379 			{
380 				n_bytes_to_copy = min(imgs[img_index].size, length) - offset;
381 			}
382 			else
383 			{
384 				n_bytes_to_copy = imgs[img_index].bb_list[next_badblock].offset - offset;
385 			}
386 
387 			if (verbose)
388 				printf("Will copy %lld bytes from file %s\n", n_bytes_to_copy, imgs[img_index].filename);
389 
390 			/* seek in inputfile to location to read from */
391 			myseek(imgs[img_index].fd, offset);
392 
393 			/* copy the correct data! */
394 			if (copy_block(imgs[img_index].fd, fd_out, n_bytes_to_copy) == -1)
395 			{
396 				fprintf(stderr, "There was a problem copying %lld bytes of data from file %s to the outputfile: %s (%d)\n", n_bytes_to_copy, imgs[img_index].filename, strerror(errno), errno);
397 				return 16;
398 			}
399 
400 			offset += n_bytes_to_copy;
401 		}
402 		else
403 		{
404 			off64_t cur_offset, cur_bb_end;
405 			off64_t n_to_guess;
406 			char *output_buffer;
407 			int output_buffer_loop;
408 			unsigned char *guess_bytes;
409 
410 			if (verbose)
411 				printf("No image without badblocks for current offset (%lld) found: \"guessing\" one or more bytes.\n", offset);
412 
413 			guess_bytes = (unsigned char *)mymalloc(n_imgs, "temp buffer for bytes from each image to select from");
414 
415 			/* see what the smallest badblock is we're currently in */
416 			find_smallest_current_badblock(imgs, n_imgs, offset, &img_index, &badblock_index);
417 
418 			if (img_index == -1 || badblock_index == -1)
419 				error_exit("Internal error: could not find the badblock \"we're in\".\n");
420 
421 			/* see where this block ends */
422 			cur_offset = imgs[img_index].bb_list[badblock_index].offset;
423 			cur_bb_end = cur_offset + imgs[img_index].bb_list[badblock_index].block_size;
424 
425 			/* now that we known where the smalles badblock ends, we know how many bytes to 'guess' */
426 			n_to_guess = cur_bb_end - offset;
427 
428 			/* update mapfile */
429 			if (fh_map_file)
430 			{
431 				fprintf(fh_map_file, "%lld %lld\n", offset, n_to_guess);
432 				fflush(fh_map_file);
433 			}
434 
435 			if (verbose)
436 			{
437 				printf("\"Guessing\" %lld bytes\n", n_to_guess);
438 				printf("%s is the next file to be used\n", imgs[img_index].filename);
439 			}
440 			if (n_to_guess <= 0)
441 				error_exit("Number of \"bytes to guess\" less then 1!\n");
442 
443 			/** should be made 64bit safe: **/
444 			/* it is not because if the badblock is too large for the memory, things
445 			 * will fail here
446 			 */
447 
448 			/* allocate block of memory: better not be larger then memory available :-) */
449 			output_buffer = mymalloc(n_to_guess, "guessed bytes");
450 
451 			/* generate block */
452 			for(output_buffer_loop=0; output_buffer_loop<n_to_guess; output_buffer_loop++)
453 			{
454 				/* read from each image one byte */
455 				for(loop=0; loop<n_imgs; loop++)
456 				{
457 					/* seek in inputfile to location to read from */
458 					myseek(imgs[loop].fd, offset + (off64_t)output_buffer_loop);
459 
460 					if (READ(imgs[loop].fd, (char *)&guess_bytes[loop], 1) != 1)
461 					{
462 						fprintf(stderr, "Read-error from file %s: %s (%d)\n", imgs[loop].filename, strerror(errno), errno);
463 						return 17;
464 					}
465 				}
466 
467 				/* select the most occuring byte */
468 				output_buffer[output_buffer_loop] = select_most_occuring_byte(guess_bytes, n_imgs);
469 			}
470 
471 			/* write to outputfile */
472 			if (phantom_write(fd_out, output_buffer, n_to_guess) != n_to_guess)
473 			{
474 				fprintf(stderr, "Failed to write to outputfile: %s (%d)\n", strerror(errno), errno);
475 				return 20;
476 			}
477 
478 			free(output_buffer);
479 
480 			free(guess_bytes);
481 
482 			offset += n_to_guess;
483 		}
484 	}
485 
486 	close(fd_out);
487 
488 	if (verbose)
489 		printf("Finished\n");
490 
491 	return 0;
492 }
493