Add simple time and performance measurement in verbose mode
[jigit.git] / mkimage.c
1 /*
2  * mkimage
3  *
4  * Tool to create an ISO image from jigdo files
5  *
6  * Copyright (c) 2004-2019 Steve McIntyre <steve@einval.com>
7  *
8  * GPL v2 - see COPYING
9  */
10
11 #include <errno.h>
12 #include <math.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <limits.h>
17 #include <zlib.h>
18 #include <bzlib.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 <stdint.h>
25 #include <time.h>
26 #include <sys/time.h>
27 #include "endian.h"
28 #include "jig-base64.h"
29 #include "md5.h"
30 #include "sha256.h"
31 #include "jigdo.h"
32
33 #define MISSING -1
34 #define UNKNOWN -2
35
36 static FILE *logfile = NULL;
37 static FILE *outfile = NULL;
38 static FILE *missing_file = NULL;
39 static long long start_offset = 0;
40 static long long end_offset = 0;
41 static int quick = 0;
42 static int verbose = 0;
43 static int image_md5_valid = 0;
44 static int image_sha256_valid = 0;
45 static int check_jigdo_header = 1;
46 static UINT64 out_size = 0;
47 static char *missing_filename = NULL;
48
49 #define ROUND_UP(N, S)      ((((N) + (S) - 1) / (S)) * (S))
50
51 #define MD5_BITS            128
52 #define MD5_BYTES           (MD5_BITS / 8)
53 #define HEX_MD5_BYTES       (MD5_BITS / 4)
54 #define BASE64_MD5_BYTES    ((ROUND_UP (MD5_BITS, 6)) / 6)
55
56 #define SHA256_BITS         256
57 #define SHA256_BYTES        (SHA256_BITS / 8)
58 #define HEX_SHA256_BYTES    (SHA256_BITS / 4)
59 #define BASE64_SHA256_BYTES ((ROUND_UP (SHA256_BITS, 6)) / 6)
60
61 /* number of chars used to print a file size in our input checksum
62  * file */
63 #define SIZE_BYTES          12
64
65 typedef struct match_list_
66 {
67     struct match_list_ *next;
68     char *match;
69     char *mirror_path;
70 } match_list_t;
71
72 static match_list_t *match_list_head = NULL;
73 static match_list_t *match_list_tail = NULL;
74
75 typedef struct md5_list_
76 {
77     struct md5_list_ *next;
78     INT64 file_size;
79     char *md5;
80     char *full_path;
81 } md5_list_t;
82
83 static md5_list_t *md5_list_head = NULL;
84 static md5_list_t *md5_list_tail = NULL;
85
86 typedef struct sha256_list_
87 {
88     struct sha256_list_ *next;
89     INT64 file_size;
90     char *sha256;
91     char *full_path;
92 } sha256_list_t;
93
94 static sha256_list_t *sha256_list_head = NULL;
95 static sha256_list_t *sha256_list_tail = NULL;
96 static zip_state_t zip_state;
97
98 /* Grab the file component from a full path */
99 static char *file_base_name(char *path)
100 {
101     char *endptr = path;
102     char *ptr = path;
103     
104     while (*ptr != '\0')
105     {
106         if ('/' == *ptr)
107             endptr = ++ptr;
108         else
109             ++ptr;
110     }
111     return endptr;
112 }
113
114 static void write_missing_entry(char *missing, char *filename)
115 {
116     if (!missing_file)
117     {
118         missing_file = fopen(missing, "wb");
119         if (!missing_file)
120         {
121             fprintf(logfile, "write_missing_entry: Unable to open missing log %s; error %d\n", missing, errno);
122             exit(1);
123         }
124     }
125     fprintf(missing_file, "%s\n", filename);
126 }
127
128 static INT64 get_file_size(char *filename)
129 {
130     struct stat sb;
131     int error = 0;
132     
133     error = stat(filename, &sb);
134     if (error)
135         return MISSING;
136     else
137         return sb.st_size;
138 }
139
140 static void display_progress(FILE *file, char *text)
141 {
142     INT64 written = ftello(file);
143     if (out_size > 0)
144         fprintf(logfile, "\r %5.2f%%  %-60.60s",
145                 100.0 * written / out_size, text);
146 }
147
148 static int add_match_entry(char *match)
149 {
150     match_list_t *entry = NULL;
151     char *mirror_path = NULL;
152     char *ptr = match;
153
154     /* Split "Foo=/mirror/foo" into its components */
155     while (*ptr)
156     {
157         if ('=' == *ptr)
158         {
159             *ptr = 0;
160             ptr++;
161             mirror_path = ptr;
162             break;
163         }
164         ptr++;
165     }
166
167     if (!mirror_path)
168     {
169         fprintf(logfile, "Could not parse malformed match entry \"%s\"\n", match);
170         return EINVAL;
171     }        
172     
173     entry = calloc(1, sizeof(*entry));
174     if (!entry)
175         return ENOMEM;
176
177     if (verbose)
178         fprintf(logfile, "Adding match entry %s:%s\n", match, mirror_path);
179
180     entry->match = match;
181     entry->mirror_path = mirror_path;
182     
183     if (!match_list_head)
184     {
185         match_list_head = entry;
186         match_list_tail = entry;
187     }
188     else
189     {
190         match_list_tail->next = entry;
191         match_list_tail = entry;
192     }
193     
194     return 0;
195 }
196
197 static int file_exists(char *path, INT64 *size)
198 {
199     struct stat sb;
200     int error = 0;
201     
202     error = stat(path, &sb);
203     if (!error && S_ISREG(sb.st_mode))
204     {
205         *size = sb.st_size;
206         return 1;
207     }
208     
209     /* else */
210     return 0;
211 }
212
213 static md5_list_t *find_file_in_md5_list(unsigned char *base64_md5, int need_size)
214 {
215     md5_list_t *md5_list_entry = md5_list_head;
216     
217     while (md5_list_entry)
218     {        
219         if (verbose > 2)
220             fprintf(logfile, "find_file_in_md5_list: looking for %s, looking at %s (%s)\n", 
221                     base64_md5, md5_list_entry->md5, md5_list_entry->full_path);
222         
223         if (!memcmp(md5_list_entry->md5, base64_md5, BASE64_MD5_BYTES)) {
224             if (need_size &&
225                 md5_list_entry->file_size == UNKNOWN)
226                 md5_list_entry->file_size =
227                     get_file_size(md5_list_entry->full_path);
228
229             return md5_list_entry;
230         }
231         /* else */
232         md5_list_entry = md5_list_entry->next;
233     }
234     return NULL; /* Not found */
235 }
236
237 static sha256_list_t *find_file_in_sha256_list(unsigned char *base64_sha256, int need_size)
238 {
239     sha256_list_t *sha256_list_entry = sha256_list_head;
240
241     while (sha256_list_entry)
242     {
243         if (verbose > 2)
244             fprintf(logfile, "find_file_in_sha256_list: looking for %s, looking at %s (%s)\n", 
245                     base64_sha256, sha256_list_entry->sha256, sha256_list_entry->full_path);
246
247         if (!memcmp(sha256_list_entry->sha256, base64_sha256, BASE64_SHA256_BYTES)) {
248             if (need_size &&
249                 sha256_list_entry->file_size == UNKNOWN)
250                 sha256_list_entry->file_size =
251                     get_file_size(sha256_list_entry->full_path);
252
253             return sha256_list_entry;
254         }
255         /* else */
256         sha256_list_entry = sha256_list_entry->next;
257     }
258     return NULL; /* Not found */
259 }
260
261 static int find_file_in_mirror(char *jigdo_match, char *jigdo_name,
262                                char *match, INT64 *file_size, char **mirror_path)
263 {
264     match_list_t *entry = match_list_head;
265     char *path = NULL;
266     int path_size = 0;
267     int jigdo_name_size = strlen(jigdo_name);
268
269     while (entry)
270     {
271         if (!strcmp(entry->match, match))
272         {
273             int mirror_path_size = strlen(entry->mirror_path);
274             if ((jigdo_name_size + 2 + mirror_path_size) > path_size)
275             {
276                 free(path);
277                 /* grow by 100 characters more than we need, to reduce
278                  * time taken in malloc if we work through lengthening
279                  * paths. */
280                 path_size = jigdo_name_size + mirror_path_size + 100;
281                 path = malloc(path_size);
282                 if (!path)
283                     return ENOMEM;
284             }
285             
286             sprintf(path, "%s/%s", entry->mirror_path, jigdo_name);
287             if (file_exists(path, file_size))
288             {
289                 *mirror_path = path;
290                 return 0;
291             }
292         }
293         entry = entry->next;
294     }
295     
296     free(path);
297     return ENOENT;
298 }
299
300 static int hex_to_nibble(char hex)
301 {
302     if (hex >= '0' && hex <= '9')
303         return hex - '0';
304     else if (hex >= 'A' && hex <= 'F')
305         return 10 + hex - 'A';
306     else if (hex >= 'a' && hex <= 'f')
307         return 10 + hex - 'a';
308     return 0;
309 }
310
311 static int add_md5_entry(INT64 size, char *md5, char *path)
312 {
313     md5_list_t *new = NULL;    
314     new = calloc(1, sizeof(*new));
315     if (!new)
316         return ENOMEM;
317
318     new->md5 = md5;
319     new->full_path = path;
320     new->file_size = size;
321     
322     if (!md5_list_head)
323     {
324         md5_list_head = new;
325         md5_list_tail = new;
326     }
327     else
328     {
329         md5_list_tail->next = new;
330         md5_list_tail = new;
331     }
332     
333     return 0;
334 }
335
336 /* Parse an incoming MD5 file entry, working in place in the
337  * (strduped) buffer we've been passed */
338 static int parse_md5_entry(char *md5_entry)
339 {
340     int error = 0;
341     char *file_name = NULL;
342     char *md5 = NULL;
343     unsigned char bin_md5[MD5_BYTES];
344     int i;
345
346     md5_entry[HEX_MD5_BYTES] = 0;
347     md5_entry[HEX_MD5_BYTES + 1] = 0;
348
349     /* Re-encode hex as base64 and overwrite in place; safe, as the
350      * md5 will be shorter than the hex. */
351     for (i = 0; i < MD5_BYTES; i++)
352         bin_md5[i] = (hex_to_nibble(md5_entry[2 * i]) << 4) |
353             hex_to_nibble(md5_entry[2 * i + 1]);
354     strncpy(md5_entry, base64_dump(bin_md5, MD5_BYTES), BASE64_MD5_BYTES);
355
356     md5_entry[BASE64_MD5_BYTES] = 0;
357     md5 = md5_entry;
358     file_name = &md5_entry[HEX_MD5_BYTES + 2 + SIZE_BYTES + 2];
359
360     if ('\n' == file_name[strlen(file_name) -1])
361         file_name[strlen(file_name) - 1] = 0;
362     
363     error = add_md5_entry(UNKNOWN, md5, file_name);
364     return error;
365 }
366
367 static int parse_md5_file(char *filename)
368 {
369     char buf[2048];
370     FILE *file = NULL;
371     char *ret = NULL;
372     int error = 0;
373
374     file = fopen(filename, "rb");
375     if (!file)
376     {
377         fprintf(logfile, "Failed to open MD5 file %s, error %d!\n", filename, errno);
378         return errno;
379     }
380     
381     while(1)
382     {
383         ret = fgets(buf, sizeof(buf), file);
384         if (NULL == ret)
385             break;
386         error = parse_md5_entry(strdup(buf));
387         if (error)
388             return error;
389     }
390     return 0;
391 }
392
393 static int add_sha256_entry(INT64 size, char *sha256, char *path)
394 {
395     sha256_list_t *new = NULL;
396     new = calloc(1, sizeof(*new));
397     if (!new)
398         return ENOMEM;
399
400     new->sha256 = sha256;
401     new->full_path = path;
402     new->file_size = size;
403
404     if (!sha256_list_head)
405     {
406         sha256_list_head = new;
407         sha256_list_tail = new;
408     }
409     else
410     {
411         sha256_list_tail->next = new;
412         sha256_list_tail = new;
413     }
414
415     return 0;
416 }
417
418 /* Parse an incoming SHA256 file entry, working in place in the
419  * (strduped) buffer we've been passed */
420 static int parse_sha256_entry(char *sha256_entry)
421 {
422     int error = 0;
423     char *file_name = NULL;
424     char *sha256 = NULL;
425     unsigned char bin_sha256[SHA256_BYTES];
426     int i;
427
428     sha256_entry[HEX_SHA256_BYTES] = 0;
429     sha256_entry[HEX_SHA256_BYTES + 1] = 0;
430
431     /* Re-encode hex as base64 and overwrite in place; safe, as the
432      * sha256 will be shorter than the hex. */
433     for (i = 0; i < SHA256_BYTES; i++)
434         bin_sha256[i] = (hex_to_nibble(sha256_entry[2 * i]) << 4) |
435             hex_to_nibble(sha256_entry[2 * i + 1]);
436     strncpy(sha256_entry, base64_dump(bin_sha256, SHA256_BYTES), BASE64_SHA256_BYTES);
437
438     sha256_entry[BASE64_SHA256_BYTES] = 0;
439     sha256 = sha256_entry;
440     file_name = &sha256_entry[HEX_SHA256_BYTES + 2 + SIZE_BYTES + 2];
441
442     if ('\n' == file_name[strlen(file_name) -1])
443         file_name[strlen(file_name) - 1] = 0;
444
445     error = add_sha256_entry(UNKNOWN, sha256, file_name);
446     return error;
447 }
448
449 static int parse_sha256_file(char *filename)
450 {
451     char buf[2048];
452     FILE *file = NULL;
453     char *ret = NULL;
454     int error = 0;
455
456     file = fopen(filename, "rb");
457     if (!file)
458     {
459         fprintf(logfile, "Failed to open SHA256 file %s, error %d!\n", filename, errno);
460         return errno;
461     }
462
463     while(1)
464     {
465         ret = fgets(buf, sizeof(buf), file);
466         if (NULL == ret)
467             break;
468         error = parse_sha256_entry(strdup(buf));
469         if (error)
470             return error;
471     }
472     return 0;
473 }
474
475 /* DELIBERATELY do not sort these, or do anything clever with
476    insertion. The entries in the jigdo file should be in the same
477    order as the ones we'll want from the template. Simply add to the
478    end of the singly-linked list each time! */
479 static int add_file_entry(char *jigdo_entry)
480 {
481     int error = 0;
482     char *file_name = NULL;
483     INT64 file_size = 0;
484     char *ptr = jigdo_entry;
485     char *base64_checksum = NULL;
486     int csum_length;
487     char *match = NULL;
488     char *jigdo_name = NULL;
489     
490     /* Grab out the component strings from the entry in the jigdo file */
491     base64_checksum = jigdo_entry;
492     while (0 != *ptr)
493     {
494         if ('=' == *ptr)
495         {
496             *ptr = 0;
497             ptr++;
498             match = ptr;
499         }
500         else if (':' == *ptr)
501         {
502             *ptr = 0;
503             ptr++;
504             jigdo_name = ptr;
505         }
506         else if ('\n' == *ptr)
507             *ptr = 0;
508         else
509             ptr++;
510     }
511
512     csum_length = strlen(base64_checksum);
513     if (csum_length == BASE64_SHA256_BYTES)
514     {
515         if (find_file_in_sha256_list((unsigned char *)base64_checksum, 0))
516         {
517             free(jigdo_entry);
518             return 0; /* We already have an entry for this file; don't
519                        * waste any more time on it */
520         }
521     }
522     else if (csum_length == BASE64_MD5_BYTES)
523     {
524         if (find_file_in_md5_list((unsigned char *)base64_checksum, 0))
525         {
526             free(jigdo_entry);
527             return 0; /* We already have an entry for this file; don't
528                        * waste any more time on it */
529         }
530     }
531     else
532     {
533         csum_length = -1; /* flag error */
534     }
535
536     /* else look for the file in the filesystem */
537     if (-1 == csum_length || NULL == match || NULL == jigdo_name)
538     {
539         fprintf(logfile, "Could not parse malformed jigdo entry \"%s\"\n", jigdo_entry);
540         free(jigdo_entry);
541         return EINVAL;
542     }
543
544     error = find_file_in_mirror(match, jigdo_name, match, &file_size, &file_name);
545     switch (error)
546     {
547         case 0:
548             base64_checksum = strdup(jigdo_entry);
549             if (base64_checksum)
550             {
551                 if (csum_length == BASE64_MD5_BYTES)
552                     add_md5_entry(file_size, base64_checksum, file_name);
553                 else
554                     add_sha256_entry(file_size, base64_checksum, file_name);
555                 free(jigdo_entry);
556                 break;
557             }
558             /* else, fall through... */
559
560         case ENOMEM:
561             fprintf(logfile, "Unable to allocate memory looking for %s\n", jigdo_name);
562             fprintf(logfile, "Abort!\n");
563             exit (ENOMEM);
564             break;
565
566         default: /* ENOENT */
567             if (missing_filename)
568             {
569                 if (csum_length == BASE64_MD5_BYTES)
570                     add_md5_entry(MISSING, base64_checksum, jigdo_name);
571                 else
572                     add_sha256_entry(MISSING, base64_checksum, jigdo_name);
573             }
574             else
575             {
576                 fprintf(logfile, "Unable to find a file to match %s\n", jigdo_name);
577                 fprintf(logfile, "Abort!\n");
578                 exit (ENOENT);
579             }
580             break;
581     }
582
583     return 0;
584 }
585
586 static int parse_jigdo_file(char *filename)
587 {
588     char buf[2048];
589     gzFile file = NULL;
590     char *ret = NULL;
591     int error = 0;
592     int num_files = 0;
593     
594     file = gzopen(filename, "rb");
595     if (!file)
596     {
597         fprintf(logfile, "Failed to open jigdo file %s, error %d!\n", filename, errno);
598         return errno;
599     }
600
601     /* Validate that we have a jigdo file */
602     if (check_jigdo_header)
603     {
604         ret = gzgets(file, buf, sizeof(buf));
605         if (NULL == ret)
606         {
607             gzclose(file);
608             fprintf(logfile, "Unable to read from jigdo file %s\n", filename);
609             return EIO;
610         }
611         if (strncmp(buf, "# JigsawDownload", 16))
612         {
613             gzclose(file);
614             fprintf(logfile, "Not a valid jigdo file: %s\n", filename);
615             return EINVAL;
616         }
617     }
618     
619     /* Find the [Parts] section of the jigdo file */
620     while (1)
621     {
622         ret = gzgets(file, buf, sizeof(buf));
623         if (NULL == ret)
624             break;
625         if (!strncmp(buf, "[Parts]", 7))
626             break;
627     }
628
629     /* Now grab the individual file entries and build a list */
630     while (1)
631     {
632         ret = gzgets(file, buf, sizeof(buf));
633         if (NULL == ret || !strcmp(buf, "\n"))
634             break;
635         if (!strcmp(buf, "[") || !strcmp(buf, "#"))
636             continue;
637         error = add_file_entry(strdup(buf));
638         num_files++;
639         if (error)
640             break;
641     }
642     if (verbose)
643         fprintf(logfile, "Found entries for %d files in jigdo file %s\n", num_files, filename);
644     
645     gzclose(file);
646     return error;
647 }
648
649 static int skip_data_block(INT64 data_size, FILE *template_file)
650 {
651     int error = 0;
652     INT64 remaining = data_size;
653     INT64 size = 0;
654
655     /* If we're coming in in the middle of the image, we'll need to
656        skip through some compressed data */
657     while (remaining)
658     {
659         if (!zip_state.data_buf)
660         {
661             error = read_data_block(template_file, logfile, &zip_state);
662             if (error)
663             {
664                 fprintf(logfile, "Unable to decompress template data, error %d\n",
665                         error);
666                 return error;
667             }
668         }
669         size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
670         zip_state.offset_in_curr_buf += size;
671         remaining -= size;
672         
673         if (zip_state.offset_in_curr_buf == zip_state.buf_size)
674         {
675             free(zip_state.data_buf);
676             zip_state.data_buf = NULL;
677         }
678     }
679     
680     fprintf(logfile, "skip_data_block: skipped %lld bytes of unmatched data\n", data_size);
681     return error;
682 }
683
684 /* Trivial helper - update all valid checksums */
685 static void update_checksum_context(struct mk_MD5Context *md5_context,
686                                     struct sha256_ctx *sha256_context,
687                                     const void *buffer,
688                                     size_t len)
689 {
690     if (md5_context && image_md5_valid)
691         mk_MD5Update(md5_context, buffer, len);
692     if (sha256_context && image_sha256_valid)
693         sha256_process_bytes(buffer, len, sha256_context);
694 }
695
696 static int parse_data_block(INT64 data_size, FILE *template_file,
697                             struct mk_MD5Context *md5_context,
698                             struct sha256_ctx *sha256_context)
699 {
700     int error = 0;
701     INT64 remaining = data_size;
702     INT64 size = 0;
703     int out_size = 0;
704
705     while (remaining)
706     {
707         if (!zip_state.data_buf)
708         {
709             error = read_data_block(template_file, logfile, &zip_state);
710             if (error)
711             {
712                 fprintf(logfile, "Unable to decompress template data, error %d\n",
713                         error);
714                 return error;
715             }
716         }
717         size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
718         out_size = fwrite(&zip_state.data_buf[zip_state.offset_in_curr_buf], size, 1, outfile);
719         if (!out_size)
720         {
721             fprintf(logfile, "parse_data_block: fwrite %lld failed with error %d; aborting\n", size, ferror(outfile));
722             return ferror(outfile);
723         }
724
725         if (verbose)
726             display_progress(outfile, "template data");
727
728         if (!quick)
729             update_checksum_context(md5_context, sha256_context,
730                                     (unsigned char *)&zip_state.data_buf[zip_state.offset_in_curr_buf],
731                                     size);
732         zip_state.offset_in_curr_buf += size;
733         remaining -= size;
734         
735         if (zip_state.offset_in_curr_buf == zip_state.buf_size)
736         {
737             free(zip_state.data_buf);
738             zip_state.data_buf = NULL;
739         }
740     }
741     if (verbose > 1)
742         fprintf(logfile, "parse_data_block: wrote %lld bytes of unmatched data\n", data_size);
743     return error;
744 }
745
746 static int parse_file_block_md5(INT64 offset, INT64 data_size, INT64 file_size, 
747                                 unsigned char *md5, struct mk_MD5Context *md5_context,
748                                 struct sha256_ctx *sha256_context, char *missing)
749 {
750     char *base64_md5 = base64_dump(md5, MD5_BYTES);
751     FILE *input_file = NULL;
752     char buf[BUF_SIZE];
753     INT64 remaining = data_size;
754     int num_read = 0;
755     struct mk_MD5Context file_context;
756     unsigned char file_md5[MD5_BYTES];
757     int out_size = 0;
758     md5_list_t *md5_list_entry = NULL;
759     
760     if (!quick)
761         mk_MD5Init(&file_context);
762
763     md5_list_entry = find_file_in_md5_list((unsigned char *)base64_md5, 1);
764     if (md5_list_entry && file_size == md5_list_entry->file_size)
765     {
766         if (verbose > 1)
767             fprintf(logfile, "Reading %s\n", md5_list_entry->full_path);
768         
769         input_file = fopen(md5_list_entry->full_path, "rb");
770         if (!input_file)
771         {
772             fprintf(logfile, "Unable to open mirror file %s, error %d\n",
773                     md5_list_entry->full_path, errno);
774             return errno;
775         }
776         
777         if (missing)
778         {
779             fclose(input_file);
780             return 0;
781         }
782         
783         fseek(input_file, offset, SEEK_SET);
784         while (remaining)
785         {
786             int size = MIN(BUF_SIZE, remaining);
787             memset(buf, 0, BUF_SIZE);
788             
789             num_read = fread(buf, size, 1, input_file);
790             if (!num_read)
791             {
792                 fprintf(logfile, "Unable to read from mirror file %s, error %d (offset %ld, length %d)\n",
793                         md5_list_entry->full_path, errno, ftell(input_file), size);
794                 fclose(input_file);
795                 return errno;
796             }
797             if (!quick)
798             {
799                 update_checksum_context(md5_context, sha256_context, (unsigned char *)buf, size);
800                 mk_MD5Update(&file_context, (unsigned char *)buf, size);
801             }
802             
803             out_size = fwrite(buf, size, 1, outfile);
804             if (!out_size)
805             {
806                 fprintf(logfile, "parse_file_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
807                 return ferror(outfile);
808             }
809             
810             if (verbose)
811                 display_progress(outfile, file_base_name(md5_list_entry->full_path));
812             
813             remaining -= size;
814         }
815         if (verbose > 1)
816             fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
817                     file_size, md5_list_entry->full_path);
818         fclose(input_file);
819         
820         if (!quick)
821         {
822             mk_MD5Final(file_md5, &file_context);
823         
824             if (memcmp(file_md5, md5, MD5_BYTES))
825             {
826                 fprintf(logfile, "MD5 MISMATCH for file %s\n", md5_list_entry->full_path);
827                 fprintf(logfile, "    template looking for %s\n", md5);
828                 fprintf(logfile, "    file in mirror is    %s\n", file_md5);
829                 return EINVAL;
830             }
831         }
832         return 0;
833     }
834     if ( missing &&
835          (MISSING == md5_list_entry->file_size) &&
836          (!memcmp(md5_list_entry->md5, base64_md5, MD5_BYTES) ) )
837     {
838         write_missing_entry(missing, md5_list_entry->full_path);
839         return 0;
840     }
841     /* else */
842     if (verbose)
843     {
844         char hex_md5[HEX_MD5_BYTES + 1];
845         int i;
846
847         for (i = 0; i < MD5_BYTES; i++)
848             sprintf(hex_md5 + 2 * i, "%2.2x", (unsigned int) md5[i]);
849
850         fprintf(logfile, "Unable to find a file for block with md5 %s (%s)\n", hex_md5, base64_md5);
851     }
852     return ENOENT;
853 }
854
855 static int parse_file_block_sha256(INT64 offset, INT64 data_size, INT64 file_size,
856                                    unsigned char *sha256, struct mk_MD5Context *md5_context,
857                                    struct sha256_ctx *sha256_context, char *missing)
858 {
859     char *base64_sha256 = base64_dump(sha256, SHA256_BYTES);
860     FILE *input_file = NULL;
861     char buf[BUF_SIZE];
862     INT64 remaining = data_size;
863     int num_read = 0;
864     struct sha256_ctx file_context;
865     unsigned char file_sha256[SHA256_BYTES];
866     int out_size = 0;
867     sha256_list_t *sha256_list_entry = NULL;
868
869     if (!quick)
870         sha256_init_ctx(&file_context);
871
872     sha256_list_entry = find_file_in_sha256_list((unsigned char *)base64_sha256, 1);
873     if (sha256_list_entry && file_size == sha256_list_entry->file_size)
874     {
875         if (verbose > 1)
876             fprintf(logfile, "Reading %s\n", sha256_list_entry->full_path);
877
878         input_file = fopen(sha256_list_entry->full_path, "rb");
879         if (!input_file)
880         {
881             fprintf(logfile, "Unable to open mirror file %s, error %d\n",
882                     sha256_list_entry->full_path, errno);
883             return errno;
884         }
885
886         if (missing)
887         {
888             fclose(input_file);
889             return 0;
890         }
891
892         fseek(input_file, offset, SEEK_SET);
893         while (remaining)
894         {
895             int size = MIN(BUF_SIZE, remaining);
896             memset(buf, 0, BUF_SIZE);
897
898             num_read = fread(buf, size, 1, input_file);
899             if (!num_read)
900             {
901                 fprintf(logfile, "Unable to read from mirror file %s, error %d (offset %ld, length %d)\n",
902                         sha256_list_entry->full_path, errno, ftell(input_file), size);
903                 fclose(input_file);
904                 return errno;
905             }
906             if (!quick)
907             {
908                 update_checksum_context(md5_context, sha256_context, (unsigned char *)buf, size);
909                 sha256_process_bytes((unsigned char *)buf, size, &file_context);
910             }
911
912             out_size = fwrite(buf, size, 1, outfile);
913             if (!out_size)
914             {
915                 fprintf(logfile, "parse_file_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
916                 return ferror(outfile);
917             }
918
919             if (verbose)
920                 display_progress(outfile, file_base_name(sha256_list_entry->full_path));
921
922             remaining -= size;
923         }
924         if (verbose > 1)
925             fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
926                     file_size, sha256_list_entry->full_path);
927         fclose(input_file);
928
929         if (!quick)
930         {
931             sha256_finish_ctx(&file_context, file_sha256);
932
933             if (memcmp(file_sha256, sha256, SHA256_BYTES))
934             {
935                 fprintf(logfile, "SHA256 MISMATCH for file %s\n", sha256_list_entry->full_path);
936                 fprintf(logfile, "    template looking for %s\n", sha256);
937                 fprintf(logfile, "    file in mirror is    %s\n", file_sha256);
938                 return EINVAL;
939             }
940         }
941         return 0;
942     }
943     if ( missing &&
944          (MISSING == sha256_list_entry->file_size) &&
945          (!memcmp(sha256_list_entry->sha256, base64_sha256, SHA256_BYTES) ) )
946     {
947         write_missing_entry(missing, sha256_list_entry->full_path);
948         return 0;
949     }
950     /* else */
951     if (verbose)
952     {
953         char hex_sha256[HEX_SHA256_BYTES + 1];
954         int i;
955
956         for (i = 0; i < SHA256_BYTES; i++)
957             sprintf(hex_sha256 + 2 * i, "%2.2x", (unsigned int) sha256[i]);
958
959         fprintf(logfile, "Unable to find a file for block with sha256 %s (%s)\n", hex_sha256, base64_sha256);
960     }
961     return ENOENT;
962 }
963
964 static void get_time(double *newtime)
965 {
966     struct timeval tv;
967     struct timezone tz;
968
969     tz.tz_minuteswest = 0;
970     tz.tz_dsttime = 0;
971
972     gettimeofday(&tv, &tz);
973     *newtime = (double)tv.tv_sec + ((double)tv.tv_usec / 1000000);
974 }
975
976 static int parse_template_file(char *filename, int sizeonly, char *missing, char *output_name)
977 {
978     INT64 template_offset = 0;
979     char *buf = NULL;
980     FILE *file = NULL;
981     INT64 file_size = 0;
982     INT64 desc_start = 0;
983     INT64 written_length = 0;
984     INT64 output_offset = 0;
985     INT64 desc_size = 0;
986     size_t bytes_read = 0;
987     size_t total_read = 0;
988     int i = 0;
989     int error = 0;
990     char *bufptr;
991     struct mk_MD5Context template_md5_context;
992     struct sha256_ctx template_sha256_context;
993     unsigned char image_md5sum[MD5_BYTES];
994     unsigned char image_sha256sum[SHA256_BYTES];
995     unsigned char image_md5sum_from_tmpl[MD5_BYTES];
996     unsigned char image_sha256sum_from_tmpl[SHA256_BYTES];
997     double start_time;
998     double end_time;
999
1000     zip_state.total_offset = 0;
1001     
1002     file = fopen(filename, "rb");
1003     if (!file)
1004     {
1005         fprintf(logfile, "Failed to open template file %s, error %d!\n", filename, errno);
1006         return errno;
1007     }
1008
1009     buf = malloc(BUF_SIZE);
1010     if (!buf)
1011     {
1012         fprintf(logfile, "Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
1013         fclose(file);
1014         return ENOMEM;
1015     }
1016
1017     get_time(&start_time);
1018
1019     /* Find the beginning of the desc block */
1020     file_size = get_file_size(filename);
1021     fseek(file, file_size - 6, SEEK_SET);
1022     fread(buf, 6, 1, file);
1023     desc_size = read_le48((unsigned char *)buf);
1024     desc_start = file_size - desc_size;
1025
1026     /* Load the DESC block in from the template file in and find the
1027      * final descriptor that describes the image
1028      * itself. Unfortunately, only way to do this is by scanning
1029      * through the whole set of descriptors in the template. */
1030     fseek(file, desc_start, SEEK_SET);
1031     buf = realloc(buf, desc_size);
1032     if (!buf)
1033     {
1034         fprintf(logfile, "Failed to malloc %lld bytes. Abort!\n", desc_size);
1035         fclose(file);
1036         return ENOMEM;
1037     }
1038     while (total_read < desc_size)
1039     {
1040         bytes_read = fread(buf, 1, desc_size, file);
1041         if (ferror(file))
1042         {
1043             fprintf(logfile, "Failed to read to the end of the template file, error %d\n", ferror(file));
1044             fclose(file);
1045             free(buf);
1046             return EIO;
1047         }
1048         total_read += bytes_read;
1049     }
1050
1051     /* Now start parsing the DESC block */
1052     bufptr = buf;
1053     if (strncmp(bufptr, "DESC", 4))
1054     {
1055         fprintf(logfile, "Failed to find desc start in the template file\n");
1056         fclose(file);
1057         free(buf);
1058         return EINVAL;
1059     }
1060     bufptr += 4;
1061
1062     if ((file_size - desc_start) != read_le48((unsigned char *)bufptr))
1063     {
1064         fprintf(logfile, "Inconsistent desc length in the template file!\n");
1065         fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
1066                 file_size - desc_start, read_le48((unsigned char *)bufptr));
1067         fclose(file);
1068         free(buf);
1069         return EINVAL;
1070     }
1071     bufptr += 6;
1072
1073     while (bufptr < (buf + desc_size - 6))
1074     {
1075         switch (bufptr[0]) {
1076             case BLOCK_DATA:
1077                 bufptr += 7;
1078                 break;
1079             case BLOCK_MATCH_MD5:
1080                 bufptr += 31;
1081                 break;
1082             case BLOCK_MATCH_SHA256:
1083                 bufptr += 47;
1084                 break;
1085             case BLOCK_IMAGE_MD5:
1086                 out_size = read_le48((unsigned char *)&bufptr[1]);
1087                 memcpy(image_md5sum_from_tmpl, (unsigned char*)&bufptr[7], MD5_BYTES);
1088                 image_md5_valid = 1;
1089                 bufptr += 27;
1090                 break;
1091             case BLOCK_IMAGE_SHA256:
1092                 out_size = read_le48((unsigned char *)&bufptr[1]);
1093                 memcpy(image_sha256sum_from_tmpl, (unsigned char*)&bufptr[7], SHA256_BYTES);
1094                 image_sha256_valid = 1;
1095                 bufptr += 43;
1096                 break;
1097             default:
1098                 fprintf(logfile, "Unknown block type %d, offset %ld\n", bufptr[0], bufptr - buf);
1099                 fclose(file);
1100                 free(buf);
1101                 return EINVAL;
1102         }
1103     }
1104
1105     if (!image_md5_valid && !image_sha256_valid)
1106     {
1107         fprintf(logfile, "Failed to find a valid image information block in the template file\n");
1108         fclose(file);
1109         free(buf);
1110         return EINVAL;
1111     }
1112
1113     if (sizeonly)
1114     {
1115         fclose(file);
1116         free(buf);
1117         printf("%lld\n", out_size);
1118         return 0;
1119     }
1120
1121     if (verbose)
1122     {
1123         if (image_md5_valid)
1124         {
1125             fprintf(logfile, "Image MD5 should be    ");
1126             for (i = 0; i < MD5_BYTES; i++)
1127                 fprintf(logfile, "%2.2x", image_md5sum_from_tmpl[i]);
1128             fprintf(logfile, "\n");
1129         }
1130         if (image_sha256_valid)
1131         {
1132             fprintf(logfile, "Image SHA256 should be ");
1133             for (i = 0; i < SHA256_BYTES; i++)
1134                 fprintf(logfile, "%2.2x", image_sha256sum_from_tmpl[i]);
1135             fprintf(logfile, "\n");
1136         }
1137         fprintf(logfile, "Image size should be   %lld bytes\n", out_size);
1138     }
1139
1140     if (!quick)
1141     {
1142         if (image_md5_valid)
1143             mk_MD5Init(&template_md5_context);
1144         if (image_sha256_valid)
1145             sha256_init_ctx(&template_sha256_context);
1146     }
1147
1148     if (verbose)
1149         fprintf(logfile, "Creating ISO image %s\n", output_name);
1150
1151     template_offset = 10;
1152
1153     /* Main loop - back to the start of the DESC block and now walk
1154      * through and expand each entry we find */
1155     while (1)
1156     {
1157         INT64 extent_size;
1158         INT64 skip = 0;
1159         INT64 read_length = 0;
1160
1161         bufptr = &buf[template_offset];
1162
1163         if (template_offset >= (desc_size - 6))
1164         {
1165             if (verbose > 1)
1166                 fprintf(logfile, "Reached end of template file\n");
1167             break; /* Finished! */
1168         }
1169         
1170         if (output_offset > end_offset) /* Past the range we were asked for */
1171         {
1172             fprintf(logfile, "Reached end of range requested\n");            
1173             break;
1174         }
1175
1176         extent_size = read_le48((unsigned char *)&bufptr[1]);
1177         read_length = extent_size;
1178
1179         switch (bufptr[0])
1180         {
1181             case BLOCK_DATA: /* unmatched data */
1182                 template_offset += 7;
1183                 if (missing)
1184                     break;
1185                 if ((output_offset + extent_size) >= start_offset)
1186                 {
1187                     if (skip)
1188                         error = skip_data_block(skip, file);
1189                     if (error)
1190                     {
1191                         fprintf(logfile, "Unable to read data block to skip, error %d\n", error);
1192                         fclose(file);
1193                         return error;
1194                     }
1195                     error = parse_data_block(read_length, file, &template_md5_context, &template_sha256_context);
1196                     if (error)
1197                     {
1198                         fprintf(logfile, "Unable to read data block, error %d\n", error);
1199                         fclose(file);
1200                         return error;
1201                     }
1202                     written_length += read_length;
1203                 }
1204                 else
1205                     error = skip_data_block(extent_size, file);
1206                 break;
1207             case BLOCK_MATCH_MD5:
1208                 template_offset += 31;
1209                 if ((output_offset + extent_size) >= start_offset)
1210                 {
1211                     error = parse_file_block_md5(skip, read_length, extent_size, (unsigned char *)&bufptr[15],
1212                                                  &template_md5_context, &template_sha256_context, missing);
1213                     if (error)
1214                     {
1215                         fprintf(logfile, "Unable to read file block, error %d\n", error);
1216                         fclose(file);
1217                         return error;
1218                     }
1219                     written_length += read_length;
1220                 }
1221                 break;
1222             case BLOCK_MATCH_SHA256:
1223                 template_offset += 47;
1224                 if ((output_offset + extent_size) >= start_offset)
1225                 {
1226                     error = parse_file_block_sha256(skip, read_length, extent_size, (unsigned char *)&bufptr[15],
1227                                                     &template_md5_context, &template_sha256_context, missing);
1228                     if (error)
1229                     {
1230                         fprintf(logfile, "Unable to read file block, error %d\n", error);
1231                         fclose(file);
1232                         return error;
1233                     }
1234                     written_length += read_length;
1235                 }
1236                 break;
1237             case BLOCK_IMAGE_MD5:
1238                 template_offset += 27;
1239                 break;
1240             case BLOCK_IMAGE_SHA256:
1241                 template_offset += 43;
1242                 break;
1243             default:
1244                 fprintf(logfile, "Unknown block type %d, offset %ld\n", bufptr[0], bufptr - buf);
1245                 fclose(file);
1246                 return EINVAL;
1247         }
1248         output_offset += extent_size;
1249     }
1250
1251     if (missing && missing_file)
1252         return ENOENT;
1253     
1254     fclose(file);
1255     if (verbose)
1256     {
1257         fprintf(logfile, "\n");
1258         if (!quick)
1259         {
1260             if (image_md5_valid)
1261             {
1262                 mk_MD5Final (image_md5sum, &template_md5_context);
1263                 fprintf(logfile, "Output image MD5 is    ");
1264                 for (i = 0; i < MD5_BYTES; i++)
1265                     fprintf(logfile, "%2.2x", image_md5sum[i]);
1266                 fprintf(logfile, "\n");
1267                 if (0 == memcmp(image_md5sum, image_md5sum_from_tmpl, MD5_BYTES))
1268                 {
1269                     fprintf(logfile, "OK: MD5 checksums match, image is good!\n");
1270                     fprintf(logfile, "WARNING: MD5 is not considered a secure hash!\n");
1271                     fprintf(logfile, "WARNING: It is recommended to verify your image in other ways too!\n");
1272                 }
1273                 else
1274                 {
1275                     fprintf(logfile, "CHECKSUMS DO NOT MATCH - PROBLEM DETECTED\n");
1276                     fclose(file);
1277                     free(buf);
1278                     return EIO;
1279                 }
1280             }
1281             if (image_sha256_valid)
1282             {
1283                 sha256_finish_ctx(&template_sha256_context, image_sha256sum);
1284                 fprintf(logfile, "Output image SHA256 is ");
1285                 for (i = 0; i < SHA256_BYTES; i++)
1286                     fprintf(logfile, "%2.2x", image_sha256sum[i]);
1287                 fprintf(logfile, "\n");
1288                 if (0 == memcmp(image_sha256sum, image_sha256sum_from_tmpl, SHA256_BYTES))
1289                 {
1290                     fprintf(logfile, "OK: SHA256 checksums match, image is good!\n");
1291                 }
1292                 else
1293                 {
1294                     fprintf(logfile, "CHECKSUMS DO NOT MATCH - PROBLEM DETECTED\n");
1295                     fclose(file);
1296                     free(buf);
1297                     return EIO;
1298                 }
1299             }
1300         }
1301         fprintf(logfile, "Output image length is %lld bytes\n", written_length);
1302
1303         if (verbose)
1304         {
1305             get_time(&end_time);
1306             end_time -= start_time;
1307             fprintf(logfile, "  Image creation iook %.2f seconds, (%.2f MB/s)\n",
1308                     end_time, (written_length / 1000000 / end_time));
1309         }
1310     }
1311     
1312     return 0;
1313 }
1314
1315 static void usage(char *progname)
1316 {
1317     printf("%s [OPTIONS]\n\n", progname);
1318     printf(" Options:\n");
1319     printf(" -f <MD5 name>       Specify an input MD5 file. MD5s must be in jigdo's\n");
1320     printf("                     pseudo-base64 format\n");
1321     printf(" -F <SHA256 name>    Specify an input SHA256 file. SHA256s must be in jigdo's\n");
1322     printf("                     pseudo-base64 format\n");
1323     printf(" -j <jigdo name>     Specify the input jigdo file\n");
1324     printf(" -t <template name>  Specify the input template file\n");
1325     printf(" -m <item=path>      Map <item> to <path> to find the files in the mirror\n");
1326     printf(" -M <missing name>   Rather than try to build the image, just check that\n");
1327     printf("                     all the needed files are available. If any are missing,\n");
1328     printf("                     list them in this file.\n");
1329     printf(" -v                  Make the output logging more verbose; may be added\n");
1330     printf("                     multiple times\n");
1331     printf(" -l <logfile>        Specify a logfile to append to.\n");
1332     printf("                     If not specified, will log to stderr\n");
1333     printf(" -o <outfile>        Specify a file to write the ISO image to.\n");
1334     printf("                     If not specified, will write to stdout\n");
1335     printf(" -q                  Quick mode. Don't check MD5sums. Dangerous!\n");
1336     printf(" -s <bytenum>        Start byte number; will start at 0 if not specified\n");
1337     printf(" -e <bytenum>        End byte number; will end at EOF if not specified\n");    
1338     printf(" -z                  Don't attempt to rebuild the image; simply print its\n");
1339     printf("                     size in bytes\n");
1340     printf(" -O                  Support Old-format .jigdo files without the JigsawDownload\n");
1341     printf("                     header\n");
1342 }
1343
1344 int main(int argc, char **argv)
1345 {
1346     char *template_filename = NULL;
1347     char *jigdo_filename = NULL;
1348     char *md5_filename = NULL;
1349     char *sha256_filename = NULL;
1350     char *output_name = NULL;
1351     int c = -1;
1352     int error = 0;
1353     int sizeonly = 0;
1354
1355     logfile = stderr;
1356     outfile = stdout;
1357
1358     memset(&zip_state, 0, sizeof(zip_state));
1359
1360     while(1)
1361     {
1362         c = getopt(argc, argv, ":ql:o:j:t:f:F:m:M:h?s:e:zvO");
1363         if (-1 == c)
1364             break;
1365         
1366         switch(c)
1367         {
1368             case 'v':
1369                 verbose++;
1370                 break;
1371             case 'q':
1372                 quick = 1;
1373                 break;
1374             case 'l':
1375                 logfile = fopen(optarg, "ab");
1376                 if (!logfile)
1377                 {
1378                     fprintf(stderr, "Unable to open log file %s\n", optarg);
1379                     return errno;
1380                 }
1381                 setlinebuf(logfile);
1382                 break;
1383             case 'o':
1384                 output_name = optarg;
1385                 outfile = fopen(output_name, "wb");
1386                 if (!outfile)
1387                 {
1388                     fprintf(logfile, "Unable to open output file %s\n", optarg);
1389                     return errno;
1390                 }
1391                 break;
1392             case 'j':
1393                 if (jigdo_filename)
1394                 {
1395                     fprintf(logfile, "Can only specify one jigdo file!\n");
1396                     return EINVAL;
1397                 }
1398                 /* else */
1399                 jigdo_filename = optarg;
1400                 break;
1401             case 't':
1402                 if (template_filename)
1403                 {
1404                     fprintf(logfile, "Can only specify one template file!\n");
1405                     return EINVAL;
1406                 }
1407                 /* else */
1408                 template_filename = optarg;
1409                 break;
1410             case 'f':
1411                 if (md5_filename)
1412                 {
1413                     fprintf(logfile, "Can only specify one MD5 file!\n");
1414                     return EINVAL;
1415                 }
1416                 /* else */
1417                 md5_filename = optarg;
1418                 break;                
1419             case 'F':
1420                 if (sha256_filename)
1421                 {
1422                     fprintf(logfile, "Can only specify one SHA256 file!\n");
1423                     return EINVAL;
1424                 }
1425                 /* else */
1426                 sha256_filename = optarg;
1427                 break;
1428             case 'm':
1429                 error = add_match_entry(strdup(optarg));
1430                 if (error)
1431                     return error;
1432                 break;
1433             case 'M':
1434                 missing_filename = optarg;
1435                 break;
1436             case ':':
1437                 fprintf(logfile, "Missing argument!\n");
1438                 return EINVAL;
1439                 break;
1440             case 'h':
1441             case '?':
1442                 usage(argv[0]);
1443                 return 0;
1444                 break;
1445             case 's':
1446                 start_offset = strtoull(optarg, NULL, 10);
1447                 if (start_offset != 0)
1448                     quick = 1;
1449                 break;
1450             case 'e':
1451                 end_offset = strtoull(optarg, NULL, 10);
1452                 if (end_offset != 0)
1453                     quick = 1;
1454                 break;
1455             case 'z':
1456                 sizeonly = 1;
1457                 break;
1458             case 'O':
1459                 check_jigdo_header = 0;
1460                 break;
1461             default:
1462                 fprintf(logfile, "Unknown option!\n");
1463                 return EINVAL;
1464         }
1465     }
1466
1467     if (0 == end_offset)
1468         end_offset = LLONG_MAX;
1469
1470     if ((NULL == jigdo_filename) &&
1471         (NULL == md5_filename) && 
1472         (NULL == sha256_filename) &&
1473         !sizeonly)
1474     {
1475         fprintf(logfile, "No jigdo file or MD5/SHA256 file specified!\n");
1476         usage(argv[0]);
1477         return EINVAL;
1478     }
1479     
1480     if (NULL == template_filename)
1481     {
1482         fprintf(logfile, "No template file specified!\n");
1483         usage(argv[0]);
1484         return EINVAL;
1485     }    
1486
1487     if (md5_filename)
1488     {
1489         /* Build up a list of the files we've been fed */
1490         error = parse_md5_file(md5_filename);
1491         if (error)
1492         {
1493             fprintf(logfile, "Unable to parse the MD5 file %s, error %d\n", md5_filename, error);
1494             return error;
1495         }
1496     }
1497
1498     if (sha256_filename)
1499     {
1500         /* Build up a list of the files we've been fed */
1501         error = parse_sha256_file(sha256_filename);
1502         if (error)
1503         {
1504             fprintf(logfile, "Unable to parse the SHA256 file %s, error %d\n", sha256_filename, error);
1505             return error;
1506         }
1507     }
1508
1509     if (jigdo_filename)
1510     {
1511         /* Build up a list of file mappings */
1512         error = parse_jigdo_file(jigdo_filename);
1513         if (error)
1514         {
1515             fprintf(logfile, "Unable to parse the jigdo file %s\n", jigdo_filename);
1516             return error;
1517         }
1518     }
1519
1520     if (!output_name)
1521         output_name = "to stdout";
1522     /* Read the template file and actually build the image to <outfile> */
1523     error = parse_template_file(template_filename, sizeonly, missing_filename, output_name);
1524     if (error)
1525     {
1526         fprintf(logfile, "Unable to recreate image from template file %s\n", template_filename);
1527         if (missing_filename)
1528             fprintf(logfile, "%s contains the list of missing files\n", missing_filename);
1529         return error;
1530     }        
1531
1532     fclose(logfile);
1533     return 0;
1534 }
1535