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