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