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