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