0fa99ebbd6c04ba28e8d4cc83f0121f6a1786226
[jigit.git] / mkimage.c
1 #undef BZ2_SUPPORT
2
3 #include <errno.h>
4 #include <math.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <limits.h>
9 #include <zlib.h>
10 #ifdef BZ2_SUPPORT
11 #   include <bzlib.h>
12 #endif
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <sys/mman.h>
18 #include "endian.h"
19 #include "md5.h"
20
21 typedef long long INT64;
22 typedef unsigned long long UINT64;
23 typedef unsigned long      UINT32;
24
25 #ifndef LLONG_MAX
26 #   define LLONG_MAX (INT64)INT_MAX * INT_MAX
27 #endif
28
29 #define BUF_SIZE 65536
30 #define MISSING -1
31
32 #ifndef MIN
33 #define MIN(x,y)        ( ((x) < (y)) ? (x) : (y))
34 #endif
35
36 #define COMP_GZIP 2
37 #define COMP_BZIP 8
38
39 FILE *logfile = NULL;
40 FILE *outfile = NULL;
41 FILE *missing_file = NULL;
42 long long start_offset = 0;
43 long long end_offset = 0;
44 int quick = 0;
45 int verbose = 0;
46 UINT64 out_size = 0;
47
48 typedef enum state_
49 {
50     STARTING,
51     IN_DATA,
52     IN_DESC,
53     DUMP_DESC,
54     DONE,
55     ERROR
56 } e_state;
57
58 typedef struct match_list_
59 {
60     struct match_list_ *next;
61     char *match;
62     char *mirror_path;
63 } match_list_t;
64
65 match_list_t *match_list_head = NULL;
66 match_list_t *match_list_tail = NULL;
67
68 typedef struct md5_list_
69 {
70     struct md5_list_ *next;
71     INT64 file_size;
72     char *md5;
73     char *full_path;
74 } md5_list_t;
75
76 md5_list_t *md5_list_head = NULL;
77 md5_list_t *md5_list_tail = NULL;
78
79 struct
80 {
81     char   *data_buf;
82     INT64  buf_size;
83     INT64   offset_in_curr_buf;
84     INT64   total_offset;
85 } zip_state;
86
87 /* Grab the file component from a full path */
88 static char *file_base_name(char *path)
89 {
90     char *endptr = path;
91     char *ptr = path;
92     
93     while (*ptr != '\0')
94     {
95         if ('/' == *ptr)
96             endptr = ++ptr;
97         else
98             ++ptr;
99     }
100     return endptr;
101 }
102
103 static void write_missing_entry(char *missing, char *filename)
104 {
105     if (!missing_file)
106     {
107         missing_file = fopen(missing, "wb");
108         if (!missing_file)
109         {
110             fprintf(logfile, "write_missing_entry: Unable to open missing log %s; error %d\n", missing, errno);
111             exit(1);
112         }
113     }
114     fprintf(missing_file, "%s\n", filename);
115 }
116
117 static INT64 get_file_size(char *filename)
118 {
119     struct stat sb;
120     int error = 0;
121     
122     error = stat(filename, &sb);
123     if (error)
124         return MISSING;
125     else
126         return sb.st_size;
127 }
128
129 static void display_progress(FILE *file, char *text)
130 {
131     INT64 written = ftello(file);
132     if (out_size > 0)
133         fprintf(logfile, "\r %5.2f%%  %-60.60s",
134                100.0 * written / out_size, text);
135 }
136
137 static int add_match_entry(char *match)
138 {
139     match_list_t *entry = NULL;
140     char *mirror_path = NULL;
141     char *ptr = match;
142
143     /* Split "Foo=/mirror/foo" into its components */
144     while (*ptr)
145     {
146         if ('=' == *ptr)
147         {
148             *ptr = 0;
149             ptr++;
150             mirror_path = ptr;
151             break;
152         }
153         ptr++;
154     }
155
156     if (!mirror_path)
157     {
158         fprintf(logfile, "Could not parse malformed match entry \"%s\"\n", match);
159         return EINVAL;
160     }        
161     
162     entry = calloc(1, sizeof(*entry));
163     if (!entry)
164         return ENOMEM;
165
166     fprintf(logfile, "Adding match entry %s:%s\n", match, mirror_path);
167
168     entry->match = match;
169     entry->mirror_path = mirror_path;
170     
171     if (!match_list_head)
172     {
173         match_list_head = entry;
174         match_list_tail = entry;
175     }
176     else
177     {
178         match_list_tail->next = entry;
179         match_list_tail = entry;
180     }
181     
182     return 0;
183 }
184
185 static int file_exists(char *path, INT64 *size)
186 {
187     struct stat sb;
188     int error = 0;
189     
190     error = stat(path, &sb);
191     if (!error && S_ISREG(sb.st_mode))
192     {
193         *size = sb.st_size;
194         return 1;
195     }
196     
197     /* else */
198     return 0;
199 }
200
201 static int find_file_in_mirror(char *jigdo_entry, char **mirror_path, char **md5sum, INT64 *file_size)
202 {
203     match_list_t *entry = match_list_head;
204     char path[PATH_MAX];
205     char *jigdo_name = NULL;
206     char *match = NULL;
207     char *ptr = jigdo_entry;
208
209     *md5sum = jigdo_entry;
210
211     /* Grab out the component strings from the entry in the jigdo file */
212     while (0 != *ptr)
213     {
214         if ('=' == *ptr)
215         {
216             *ptr = 0;
217             ptr++;
218             match = ptr;
219         }
220         else if (':' == *ptr)
221         {
222             *ptr = 0;
223             ptr++;
224             jigdo_name = ptr;
225         }
226         else if ('\n' == *ptr)
227             *ptr = 0;
228         else
229             ptr++;
230     }
231
232     if (NULL == match || NULL == jigdo_name)
233     {
234         fprintf(logfile, "Could not parse malformed jigdo entry \"%s\"\n", jigdo_entry);
235         return EINVAL;
236     }
237
238     while (entry)
239     {
240         if (!strcmp(entry->match, match))
241         {
242             sprintf(path, "%s/%s", entry->mirror_path, jigdo_name);
243             if (file_exists(path, file_size))
244             {
245                 *mirror_path = strdup(path);
246                 return 0;
247             }
248         }
249         entry = entry->next;
250     }
251     
252     *mirror_path = jigdo_name;
253     return ENOENT;
254 }
255
256
257 static int add_md5_entry(INT64 size, char *md5, char *path)
258 {
259     md5_list_t *new = NULL;    
260     new = calloc(1, sizeof(*new));
261     if (!new)
262         return ENOMEM;
263
264     new->md5 = md5;
265     new->full_path = path;
266     new->file_size = size;
267     
268     if (!md5_list_head)
269     {
270         md5_list_head = new;
271         md5_list_tail = new;
272     }
273     else
274     {
275         md5_list_tail->next = new;
276         md5_list_tail = new;
277     }
278     
279     return 0;
280 }
281
282 static int parse_md5_entry(char *md5_entry)
283 {
284     int error = 0;
285     char *file_name = NULL;
286     char *md5 = NULL;
287     INT64 file_size = 0;
288
289     md5_entry[22] = 0;
290     md5_entry[23] = 0;
291
292     md5 = md5_entry;
293     file_name = &md5_entry[24];
294
295     if ('\n' == file_name[strlen(file_name) -1])
296         file_name[strlen(file_name) - 1] = 0;
297     
298     file_size = get_file_size(file_name);
299
300     error = add_md5_entry(file_size, md5, file_name);
301     return 0;
302 }
303
304 static int parse_md5_file(char *filename)
305 {
306     unsigned char buf[2048];
307     FILE *file = NULL;
308     char *ret = NULL;
309     int error = 0;
310
311     file = fopen(filename, "rb");
312     if (!file)
313     {
314         fprintf(logfile, "Failed to open MD5 file %s, error %d!\n", filename, errno);
315         return errno;
316     }
317     
318     while(1)
319     {
320         ret = fgets(buf, sizeof(buf), file);
321         if (NULL == ret)
322             break;
323         error = parse_md5_entry(strdup(buf));
324     }
325     return 0;
326 }
327
328 /* DELIBERATELY do not sort these, or do anything clever with
329    insertion. The entries in the jigdo file should be in the same
330    order as the ones we'll want from the template. Simply add to the
331    end of the singly-linked list each time! */
332 static int add_file_entry(char *jigdo_entry)
333 {
334     int error = 0;
335     char *file_name = NULL;
336     char *md5 = NULL;
337     INT64 file_size = 0;
338     
339     error = find_file_in_mirror(jigdo_entry, &file_name, &md5, &file_size);
340
341     if (error)
342         add_md5_entry(MISSING, md5, file_name);
343     else
344         add_md5_entry(file_size, md5, file_name);
345     return 0;
346 }
347
348 static int parse_jigdo_file(char *filename)
349 {
350     unsigned char buf[2048];
351     gzFile *file = NULL;
352     char *ret = NULL;
353     int error = 0;
354     
355     file = gzopen(filename, "rb");
356     if (!file)
357     {
358         fprintf(logfile, "Failed to open jigdo file %s, error %d!\n", filename, errno);
359         return errno;
360     }
361
362     /* Find the [Parts] section of the jigdo file */
363     while (1)
364     {
365         ret = gzgets(file, buf, sizeof(buf));
366         if (NULL == ret)
367             break;
368         if (!strncmp(buf, "[Parts]", 7))
369             break;
370     }
371
372     /* Now grab the individual file entries and build a list */
373     while (1)
374     {
375         ret = gzgets(file, buf, sizeof(buf));
376         if (NULL == ret || !strcmp(buf, "\n"))
377             break;
378         if (!strcmp(buf, "[") || !strcmp(buf, "#"))
379             continue;
380         error = add_file_entry(strdup(buf));
381         if (error)
382             break;
383     }
384
385     gzclose(file);
386     return error;
387 }
388
389 static int ungzip_data_block(char *in_buf, INT64 in_len, char *out_buf, INT64 out_len)
390 {
391     int error = 0;
392     z_stream uc_stream;
393     
394     uc_stream.zalloc = NULL;
395     uc_stream.zfree = NULL;
396     uc_stream.opaque = NULL;
397     uc_stream.next_in = in_buf;
398     uc_stream.avail_in = in_len;
399
400     error = inflateInit(&uc_stream);
401     if (Z_OK != error)
402     {
403         fprintf(logfile, "ungzip_data_block: failed to init, error %d\n", error);
404         return EIO;
405     }
406     
407     uc_stream.next_out = out_buf;
408     uc_stream.avail_out = out_len;
409
410     error = inflate(&uc_stream, Z_FINISH);
411     if (Z_OK != error && Z_STREAM_END != error)
412     {
413         fprintf(logfile, "ungzip_data_block: failed to decompress, error %d\n", error);
414         return EIO;
415     }
416     
417     error = inflateEnd(&uc_stream);
418     if (Z_OK != error)
419     {
420         fprintf(logfile, "ungzip_data_block: failed to end, error %d\n", error);
421         return EIO;
422     }
423     
424     return 0;
425 }    
426
427 #ifdef BZ2_SUPPORT
428 static int unbzip_data_block(char *in_buf, INT64 in_len, char *out_buf, INT64 out_len)
429 {
430     int error = 0;
431     bz_stream uc_stream;
432     
433     uc_stream.bzalloc = NULL;
434     uc_stream.bzfree = NULL;
435     uc_stream.opaque = NULL;
436     uc_stream.next_in = in_buf;
437     uc_stream.avail_in = in_len;
438
439     error = BZ2_bzDecompressInit(&uc_stream, 0, 0);
440     if (BZ_OK != error)
441     {
442         fprintf(logfile, "unbzip_data_block: failed to init, error %d\n", error);
443         return EIO;
444     }
445     
446     uc_stream.next_out = out_buf;
447     uc_stream.avail_out = out_len;
448
449     error = BZ2_bzDecompress(&uc_stream);
450     if (BZ_OK != error && BZ_STREAM_END != error)
451     {
452         fprintf(logfile, "unbzip_data_block: failed to decompress, error %d\n", error);
453         return EIO;
454     }
455     
456     error = BZ2_bzDecompressEnd(&uc_stream);
457     if (BZ_OK != error)
458     {
459         fprintf(logfile, "unbzip_data_block: failed to end, error %d\n", error);
460         return EIO;
461     }
462     
463     return 0;
464 }    
465 #endif
466
467 static int decompress_data_block(char *in_buf, INT64 in_len, char *out_buf,
468                                  INT64 out_len, int compress_type)
469 {
470 #ifdef BZ2_SUPPORT
471     if (COMP_BZIP == compress_type)
472         return unbzip_data_block(in_buf, in_len, out_buf, out_len);
473     else
474 #endif
475         return ungzip_data_block(in_buf, in_len, out_buf, out_len);
476 }
477
478 static int read_data_block(FILE *template_file, int compress_type)
479 {
480     char inbuf[1024];
481     INT64 i = 0;
482     static INT64 template_offset = -1;
483     INT64 compressed_len = 0;
484     INT64 uncompressed_len = 0;
485     char *comp_buf = NULL;
486     int read_num = 0;
487     int error = 0;
488
489     if (-1 == template_offset)
490     {
491         fseek(template_file, 0, SEEK_SET);
492         fread(inbuf, sizeof(inbuf), 1, template_file);
493         for (i = 0; i < sizeof(inbuf); i++)
494         {
495             if (!strncmp(&inbuf[i], "DATA", 4))
496             {
497                 template_offset = i;
498                 break;
499             }
500         }
501         if (-1 == template_offset)
502         {
503             fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
504                     template_offset);
505             return EINVAL;
506         }    
507     }
508     
509     fseek(template_file, template_offset, SEEK_SET);
510     fread(inbuf, 16, 1, template_file);
511     if (strncmp(inbuf, "DATA", 4))
512     {
513         fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
514                 template_offset);
515         return EINVAL;
516     }    
517     
518     compressed_len = read_le48(&inbuf[4]);
519     uncompressed_len = read_le48(&inbuf[10]);
520
521     comp_buf = calloc(1, compressed_len);
522     if (!comp_buf)
523     {
524         fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
525                 template_offset);
526         return ENOMEM;
527     }
528     
529     zip_state.data_buf = calloc(1, uncompressed_len);
530     if (!zip_state.data_buf)
531     {
532         fprintf(logfile, "Unable to allocate %lld bytes for decompression\n",
533                 uncompressed_len);
534         return ENOMEM;
535     }
536
537     read_num = fread(comp_buf, compressed_len, 1, template_file);
538     if (0 == read_num)
539     {
540         fprintf(logfile, "Unable to read %lld bytes for decompression\n",
541                 uncompressed_len);
542         return EIO;
543     }
544
545     error = decompress_data_block(comp_buf, compressed_len,
546                                   zip_state.data_buf, uncompressed_len, compress_type);
547     if (error)
548     {
549         fprintf(logfile, "Unable to decompress data block, error %d\n", error);
550         return error;
551     }
552         
553     template_offset += compressed_len;
554     zip_state.buf_size = uncompressed_len;
555     zip_state.offset_in_curr_buf = 0;
556     free (comp_buf);
557     return 0;
558 }
559
560 static int skip_data_block(INT64 data_size, FILE *template_file, int compress_type)
561 {
562     int error = 0;
563     INT64 remaining = data_size;
564     INT64 size = 0;
565
566     /* If we're coming in in the middle of the image, we'll need to
567        skip through some compressed data */
568     while (remaining)
569     {
570         if (!zip_state.data_buf)
571         {
572             error = read_data_block(template_file, compress_type);
573             if (error)
574             {
575                 fprintf(logfile, "Unable to decompress template data, error %d\n",
576                         error);
577                 return error;
578             }
579         }
580         size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
581         zip_state.offset_in_curr_buf += size;
582         remaining -= size;
583         
584         if (zip_state.offset_in_curr_buf == zip_state.buf_size)
585         {
586             free(zip_state.data_buf);
587             zip_state.data_buf = NULL;
588         }
589     }
590     
591     fprintf(logfile, "skip_data_block: skipped %lld bytes of unmatched data\n", data_size);
592     return error;
593 }
594
595 static int parse_data_block(INT64 data_size, FILE *template_file,
596                             struct mk_MD5Context *context, int compress_type)
597 {
598     int error = 0;
599     INT64 remaining = data_size;
600     INT64 size = 0;
601     int out_size = 0;
602
603     while (remaining)
604     {
605         if (!zip_state.data_buf)
606         {
607             error = read_data_block(template_file, compress_type);
608             if (error)
609             {
610                 fprintf(logfile, "Unable to decompress template data, error %d\n",
611                         error);
612                 return error;
613             }
614         }
615         size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
616         out_size = fwrite(&zip_state.data_buf[zip_state.offset_in_curr_buf], size, 1, outfile);
617         if (!out_size)
618         {
619             fprintf(logfile, "parse_data_block: fwrite %lld failed with error %d; aborting\n", size, ferror(outfile));
620             return ferror(outfile);
621         }
622
623         if (verbose)
624             display_progress(outfile, "template data");
625
626         if (!quick)
627             mk_MD5Update(context, &zip_state.data_buf[zip_state.offset_in_curr_buf], size);
628         zip_state.offset_in_curr_buf += size;
629         remaining -= size;
630         
631         if (zip_state.offset_in_curr_buf == zip_state.buf_size)
632         {
633             free(zip_state.data_buf);
634             zip_state.data_buf = NULL;
635         }
636     }
637     if (verbose > 1)
638         fprintf(logfile, "parse_data_block: wrote %lld bytes of unmatched data\n", data_size);
639     return error;
640 }
641
642 static int parse_file_block(INT64 offset, INT64 data_size, INT64 file_size, 
643                             char *md5, struct mk_MD5Context *image_context,
644                             char *missing)
645 {
646     char *base64_md5 = base64_dump(md5, 16);
647     FILE *input_file = NULL;
648     char buf[BUF_SIZE];
649     INT64 remaining = data_size;
650     int num_read = 0;
651     struct mk_MD5Context file_context;
652     char file_md5[16];
653     md5_list_t *md5_list_current = md5_list_head;
654     int out_size = 0;
655     
656     if (!quick)
657         mk_MD5Init(&file_context);
658
659     while (md5_list_current)
660     {        
661         if ( (md5_list_current->file_size == file_size) &&
662              (!memcmp(md5_list_current->md5, base64_md5, 16) ) )
663         {
664             input_file = fopen(md5_list_current->full_path, "rb");
665             if (!input_file)
666             {
667                 fprintf(logfile, "Unable to open mirror file %s, error %d\n",
668                         md5_list_current->full_path, errno);
669                 return errno;
670             }
671
672             if (missing)
673             {
674                 fclose(input_file);
675                 return 0;
676             }
677             
678             fseek(input_file, offset, SEEK_SET);
679             while (remaining)
680             {
681                 int size = MIN(BUF_SIZE, remaining);
682                 memset(buf, 0, BUF_SIZE);
683
684                 num_read = fread(buf, size, 1, input_file);
685                 if (!num_read)
686                 {
687                     fprintf(logfile, "Unable to read from mirror file %s, error %d (offset %ld, length %d)\n",
688                             md5_list_current->full_path, errno, ftell(input_file), size);
689                     fclose(input_file);
690                     return errno;
691                 }
692                 if (!quick)
693                 {
694                     mk_MD5Update(image_context, buf, size);
695                     mk_MD5Update(&file_context, buf, size);
696                 }
697             
698                 out_size = fwrite(buf, size, 1, outfile);
699                 if (!out_size)
700                 {
701                     fprintf(logfile, "parse_file_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
702                     return ferror(outfile);
703                 }
704             
705                 if (verbose)
706                     display_progress(outfile, file_base_name(md5_list_current->full_path));
707
708                 remaining -= size;
709             }
710             if (verbose > 1)
711                 fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
712                     file_size, md5_list_current->full_path);
713             fclose(input_file);
714
715             if (!quick)
716             {
717                 mk_MD5Final(file_md5, &file_context);
718         
719                 if (memcmp(file_md5, md5, 16))
720                 {
721                     fprintf(logfile, "MD5 MISMATCH for file %s\n", md5_list_current->full_path);
722                     fprintf(logfile, "    template looking for %s\n", md5);
723                     fprintf(logfile, "    file in mirror is    %s\n", file_md5);
724                     return EINVAL;
725                 }
726             }
727             return 0;
728         }
729         if ( missing &&
730              (MISSING == md5_list_current->file_size) &&
731              (!memcmp(md5_list_current->md5, base64_md5, 16) ) )
732         {
733             write_missing_entry(missing, md5_list_current->full_path);
734             return 0;
735         }
736         md5_list_current = md5_list_current->next;
737     }
738     return ENOENT;
739 }
740
741 static int parse_template_file(char *filename, int sizeonly, char *missing, char *output_name)
742 {
743     INT64 template_offset = 0;
744     INT64 bytes = 0;
745     unsigned char *buf = NULL;
746     FILE *file = NULL;
747     INT64 file_size = 0;
748     INT64 desc_start = 0;
749     INT64 written_length = 0;
750     INT64 output_offset = 0;
751     int i = 0;
752     int error = 0;
753     struct mk_MD5Context template_context;
754     unsigned char image_md5sum[16];
755
756     zip_state.total_offset = 0;
757     
758     file = fopen(filename, "rb");
759     if (!file)
760     {
761         fprintf(logfile, "Failed to open template file %s, error %d!\n", filename, errno);
762         return errno;
763     }
764
765     buf = malloc(BUF_SIZE);
766     if (!buf)
767     {
768         fprintf(logfile, "Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
769         fclose(file);
770         return ENOMEM;
771     }
772
773     /* Find the beginning of the desc block */
774     file_size = get_file_size(filename);
775     fseek(file, file_size - 6, SEEK_SET);
776     fread(buf, 6, 1, file);
777     desc_start = file_size - read_le48(buf);
778
779     /* Now seek back to the beginning image desc block to grab the MD5
780        and image length */
781     fseek(file, file_size - 33, SEEK_SET);
782     fread(buf, BUF_SIZE, 1, file);
783     if (buf[0] != 5) /* image data */
784     {
785         fprintf(logfile, "Failed to find image desc in the template file\n");
786         fclose(file);
787         return EINVAL;
788     }
789
790     if (sizeonly)
791     {
792         fclose(file);
793         printf("%lld\n", read_le48(&buf[1]));
794         return 0;
795     }
796
797     if (verbose)
798     {
799         fprintf(logfile, "Image should be %lld bytes\n", read_le48(&buf[1]));
800         fprintf(logfile, "Image MD5 should be ");
801         for (i = 0; i < 16; i++)
802             fprintf(logfile, "%2.2x", buf[i+7]);
803         fprintf(logfile, "\n");
804     }
805
806     out_size = read_le48(&buf[1]);
807     
808     /* Now seek back to the start of the desc block */
809     fseek(file, desc_start, SEEK_SET);
810     fread(buf, 10, 1, file);
811     if (strncmp(buf, "DESC", 4))
812     {
813         fprintf(logfile, "Failed to find desc start in the template file\n");
814         fclose(file);
815         return EINVAL;
816     }
817     if ((file_size - desc_start) != read_le48(&buf[4]))
818     {
819         fprintf(logfile, "Inconsistent desc length in the template file!\n");
820         fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
821                 file_size - desc_start, read_le48(&buf[4]));
822         fclose(file);
823         return EINVAL;
824     }
825
826     if (!quick)
827         mk_MD5Init(&template_context);
828     template_offset = desc_start + 10;
829
830     if (1 == verbose)
831         fprintf(logfile, "Creating ISO image %s\n", output_name);
832
833     /* Main loop - walk through the template file and expand each entry we find */
834     while (1)
835     {
836         INT64 extent_size;
837         INT64 skip = 0;
838         INT64 read_length = 0;
839
840         if (template_offset >= (file_size - 33))
841         {
842             if (verbose > 1)
843                 fprintf(logfile, "Reached end of template file\n");
844             break; /* Finished! */
845         }
846         
847         if (output_offset > end_offset) /* Past the range we were asked for */
848         {
849             fprintf(logfile, "Reached end of range requested\n");            
850             break;
851         }
852         
853         fseek(file, template_offset, SEEK_SET);
854         bytes = fread(buf, (MIN (BUF_SIZE, file_size - template_offset)), 1, file);
855         if (1 != bytes)
856         {
857             fprintf(logfile, "Failed to read template file!\n");
858             fclose(file);
859             return EINVAL;
860         }
861         
862         extent_size = read_le48(&buf[1]);
863         read_length = extent_size;
864         
865         if (start_offset > output_offset)
866             skip = start_offset - output_offset;
867         if ((output_offset + extent_size) > end_offset)
868             read_length -= (output_offset + extent_size - end_offset - 1);
869         read_length -= skip;
870         
871         switch (buf[0])
872         {
873             
874             case 2: /* unmatched data, gzip */
875             case 8: /* unmatched data, bzip2 */
876                 template_offset += 7;
877                 if (missing)
878                     break;
879                 if ((output_offset + extent_size) >= start_offset)
880                 {
881                     if (skip)
882                         error = skip_data_block(skip, file, buf[0]);
883                     if (error)
884                     {
885                         fprintf(logfile, "Unable to read data block to skip, error %d\n", error);
886                         fclose(file);
887                         return error;
888                     }
889                     error = parse_data_block(read_length, file, &template_context, buf[0]);
890                     if (error)
891                     {
892                         fprintf(logfile, "Unable to read data block, error %d\n", error);
893                         fclose(file);
894                         return error;
895                     }
896                     written_length += read_length;
897                 }
898                 else
899                     error = skip_data_block(extent_size, file, buf[0]);
900                 break;
901             case 6:
902                 template_offset += 31;
903                 if ((output_offset + extent_size) >= start_offset)
904                 {
905                     error = parse_file_block(skip, read_length, extent_size, &buf[15], &template_context, missing);
906                     if (error)
907                     {
908                         fprintf(logfile, "Unable to read file block, error %d\n", error);
909                         fclose(file);
910                         return error;
911                     }
912                     written_length += read_length;
913                 }
914                 break;
915             default:
916                 fprintf(logfile, "Unknown block type %d!\n", buf[0]);
917                 fclose(file);
918                 return EINVAL;
919         }
920         output_offset += extent_size;
921     }
922
923     if (missing && missing_file)
924         return ENOENT;
925     
926     fclose(file);
927     if (verbose)
928     {
929         fprintf(logfile, "\n");
930         if (!quick)
931         {
932             mk_MD5Final (image_md5sum, &template_context);
933             fprintf(logfile, "Output image MD5 is ");
934             for (i = 0; i < 16; i++)
935                 fprintf(logfile, "%2.2x", image_md5sum[i]);
936             fprintf(logfile, "\n");
937         }
938         fprintf(logfile, "Output image length is %lld\n", written_length);
939     }
940     
941     return 0;
942 }
943
944 static void usage(char *progname)
945 {
946     printf("%s [OPTIONS]\n\n", progname);
947     printf(" Options:\n");
948     printf(" -f <MD5 name>       Specify an input MD5 file. MD5s must be in jigdo's\n");
949         printf("                     pseudo-base64 format\n");
950     printf(" -j <jigdo name>     Specify the input jigdo file\n");
951     printf(" -t <template name>  Specify the input template file\n");
952     printf(" -m <item=path>      Map <item> to <path> to find the files in the mirror\n");
953         printf(" -M <missing name>   Rather than try to build the image, just check that\n");
954         printf("                     all the needed files are available. If any are missing,\n");
955         printf("                     list them in this file.\n");
956         printf(" -v                  Make the output logging more verbose\n");
957     printf(" -l <logfile>        Specify a logfile to append to.\n");
958     printf("                     If not specified, will log to stderr\n");
959     printf(" -o <outfile>        Specify a file to write the ISO image to.\n");
960     printf("                     If not specified, will write to stdout\n");
961     printf(" -q                  Quick mode. Don't check MD5sums. Dangerous!\n");
962     printf(" -s <bytenum>        Start byte number; will start at 0 if not specified\n");
963     printf(" -e <bytenum>        End byte number; will end at EOF if not specified\n");    
964     printf(" -z                  Don't attempt to rebuild the image; simply print its\n");
965     printf("                     size in bytes\n");
966 }
967
968 int main(int argc, char **argv)
969 {
970     char *template_filename = NULL;
971     char *jigdo_filename = NULL;
972     char *md5_filename = NULL;
973     char *missing_filename = NULL;
974     char *output_name = NULL;
975     int c = -1;
976     int error = 0;
977     int sizeonly = 0;
978
979     logfile = stderr;
980     outfile = stdout;
981
982     bzero(&zip_state, sizeof(zip_state));
983
984     while(1)
985     {
986         c = getopt(argc, argv, ":ql:o:j:t:f:m:M:h?s:e:zv");
987         if (-1 == c)
988             break;
989         
990         switch(c)
991         {
992             case 'v':
993                 verbose++;
994                 break;
995             case 'q':
996                 quick = 1;
997                 break;
998             case 'l':
999                 logfile = fopen(optarg, "ab");
1000                 if (!logfile)
1001                 {
1002                     fprintf(stderr, "Unable to open log file %s\n", optarg);
1003                     return errno;
1004                 }
1005                 setlinebuf(logfile);
1006                 break;
1007             case 'o':
1008                 output_name = optarg;
1009                 outfile = fopen(output_name, "wb");
1010                 if (!outfile)
1011                 {
1012                     fprintf(stderr, "Unable to open output file %s\n", optarg);
1013                     return errno;
1014                 }
1015                 break;
1016             case 'j':
1017                 if (jigdo_filename)
1018                 {
1019                     fprintf(logfile, "Can only specify one jigdo file!\n");
1020                     return EINVAL;
1021                 }
1022                 /* else */
1023                 jigdo_filename = optarg;
1024                 break;
1025             case 't':
1026                 if (template_filename)
1027                 {
1028                     fprintf(logfile, "Can only specify one template file!\n");
1029                     return EINVAL;
1030                 }
1031                 /* else */
1032                 template_filename = optarg;
1033                 break;
1034             case 'f':
1035                 if (md5_filename)
1036                 {
1037                     fprintf(logfile, "Can only specify one MD5 file!\n");
1038                     return EINVAL;
1039                 }
1040                 /* else */
1041                 md5_filename = optarg;
1042                 break;                
1043             case 'm':
1044                 error = add_match_entry(strdup(optarg));
1045                 if (error)
1046                     return error;
1047                 break;
1048             case 'M':
1049                 missing_filename = optarg;
1050                 break;
1051             case ':':
1052                 fprintf(logfile, "Missing argument!\n");
1053                 return EINVAL;
1054                 break;
1055             case 'h':
1056             case '?':
1057                 usage(argv[0]);
1058                 return 0;
1059                 break;
1060             case 's':
1061                 start_offset = strtoull(optarg, NULL, 10);
1062                 if (start_offset != 0)
1063                     quick = 1;
1064                 break;
1065             case 'e':
1066                 end_offset = strtoull(optarg, NULL, 10);
1067                 if (end_offset != 0)
1068                     quick = 1;
1069                 break;
1070             case 'z':
1071                 sizeonly = 1;
1072                 break;
1073             default:
1074                 fprintf(logfile, "Unknown option!\n");
1075                 return EINVAL;
1076         }
1077     }
1078
1079     if (0 == end_offset)
1080         end_offset = LLONG_MAX;
1081
1082     if ((NULL == jigdo_filename) &&
1083         (NULL == md5_filename) && 
1084         !sizeonly)
1085     {
1086         fprintf(logfile, "No jigdo file or MD5 file specified!\n");
1087         usage(argv[0]);
1088         return EINVAL;
1089     }
1090     
1091     if (NULL == template_filename)
1092     {
1093         fprintf(logfile, "No template file specified!\n");
1094         usage(argv[0]);
1095         return EINVAL;
1096     }    
1097
1098     if (md5_filename)
1099     {
1100         /* Build up a list of the files we've been fed */
1101         error = parse_md5_file(md5_filename);
1102         if (error)
1103         {
1104             fprintf(logfile, "Unable to parse the MD5 file %s\n", md5_filename);
1105             return error;
1106         }
1107     }
1108
1109     if (jigdo_filename)
1110     {
1111         /* Build up a list of file mappings */
1112         error = parse_jigdo_file(jigdo_filename);
1113         if (error)
1114         {
1115             fprintf(logfile, "Unable to parse the jigdo file %s\n", jigdo_filename);
1116             return error;
1117         }
1118     }
1119
1120     if (!output_name)
1121         output_name = "to stdout";
1122     /* Read the template file and actually build the image to <outfile> */
1123     error = parse_template_file(template_filename, sizeonly, missing_filename, output_name);
1124     if (error)
1125     {
1126         fprintf(logfile, "Unable to recreate image from template file %s\n", template_filename);
1127         if (missing_filename)
1128             fprintf(logfile, "%s contains the list of missing files\n", missing_filename);
1129         return error;
1130     }        
1131
1132     fclose(logfile);
1133     return 0;
1134 }
1135