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