Code cleanups after running with "sparse"
[jigit.git] / libjte / jte.c
1 /*
2  * jte.c
3  *
4  * 
5  * Copyright (c) 2004-2006 Steve McIntyre <steve@einval.com>
6  * Copyright (c) 2010-2011 Thomas Schmitt <scdbackup@gmx.net>
7  * Copyright (c) 2010-2011 George Danchev <danchev@spnet.net>
8  * 
9  * These routines were originally implemented by 
10  * Steve McIntyre <steve@einval.com>. 
11  * More recently few tweaks and additions were applied by 
12  * Thomas Schmitt <scdbackup@gmx.net> and
13  * George Danchev <danchev@spnet.net>
14  * 
15  * Implementation of the Jigdo Template Engine - make jigdo files
16  * directly when making ISO images
17  *
18  * GNU GPL v2+
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "../config.h"
23 #endif
24
25 #ifdef HAVE_STDINT_H
26 #include <stdint.h>
27 #else
28 #ifdef HAVE_INTTYPES_H
29 #include <inttypes.h>
30 #endif
31 #endif
32
33 #include <string.h>
34 #include <stdlib.h>
35
36 #ifdef LIBJTE_WITH_ZLIB
37 #include <zlib.h>
38 #endif
39
40 #ifdef LIBJTE_WITH_LIBBZ2
41 #include <bzlib.h>
42 #endif
43
44 #include <sys/types.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include "jte.h"
48 #include "checksum.h"
49 #include "endianconv.h"
50 #include "rsync.h"
51 #include "md5.h"
52 #include "libjte_private.h"
53 #include "libjte.h"
54
55 /* #include "version.h" */                 /* generated by autotools */
56
57 /* Different types used in building our state list below */
58 #define JTET_FILE_MATCH 1
59 #define JTET_NOMATCH    2
60
61 #define JTE_VER_MAJOR     0x0001
62 #define JTE_VER_MINOR     0x0013
63 #define JTE_NAME          "JTE"
64 #define JTE_COMMENT       "JTE at http://www.einval.com/~steve/software/JTE/ ; jigdo at http://atterer.org/jigdo/"
65
66 #define JIGDO_TEMPLATE_VERSION "1.1"
67
68 #define JTE_MAX_ERROR_LIST_LENGTH 20
69
70
71 /* Grab the file component from a full path */
72 static char *file_base_name(char *path)
73 {
74     char *endptr = path;
75     char *ptr = path;
76     
77     while (*ptr != '\0')
78     {
79         if ('/' == *ptr)
80             endptr = ++ptr;
81         else
82             ++ptr;
83     }
84     return endptr;
85 }
86
87 static void exit_if_enabled(struct libjte_env *o, int value)
88 {
89     if (!(o->error_behavior & 2))
90         return;
91     libjte_clear_msg_list(o, 1);
92     exit(value);
93 }
94
95 int libjte_report_no_mem(struct libjte_env *o, size_t size, int flag)
96 {
97     sprintf(o->message_buffer, "Out of memory for %.f new bytes",
98             (double) size);
99     libjte_add_msg_entry(o, o->message_buffer, 0);
100     return -1;
101 }
102
103 /* @param flag bit0-7 = mode
104                         0= include_list
105                         1= exclude_list
106 */
107 static int jte_add_path_match(struct libjte_env *o, char *pattern, int flag)
108 {
109     struct path_match *new = NULL, **old;
110     int mode;
111
112     mode = flag & 255;
113     if (mode == 0)
114         old = &(o->include_list);
115     else
116         old = &(o->exclude_list);
117
118     new = malloc(sizeof *new);
119     if (new == NULL)
120         return libjte_report_no_mem(o, sizeof *new, 0);
121     
122     regcomp(&new->match_pattern, pattern, REG_NEWLINE);
123     new->match_rule = strdup(pattern);
124     if (new->match_rule == NULL)
125         return libjte_report_no_mem(o, sizeof *new, 0);
126
127     /* Order on the list doesn't matter! */
128     new->next = *old;
129
130     *old = new;
131     return 0;
132 }
133
134 /* @param flag bit0-7 = mode
135                         0= include_list
136                         1= exclude_list
137 */
138 int libjte_destroy_path_match_list(struct libjte_env *o, int flag)
139 {
140     struct path_match **old, *s, *s_next;
141     int mode;
142
143     mode = flag & 255;
144     if (mode == 0)
145         old = &(o->include_list);
146     else
147         old = &(o->exclude_list);
148     for (s = *old; s != NULL; s = s_next) {
149         s_next = s->next;
150         regfree(&(s->match_pattern));
151         free(s->match_rule);
152         free(s);
153     }
154     *old = NULL;
155     return 1;
156 }
157
158 /* Build the list of exclusion regexps */
159 int jte_add_exclude(struct libjte_env *o, char *pattern)
160 {
161     int ret;
162     
163     ret = jte_add_path_match(o, pattern, 1);
164     return ret;
165 }
166
167 /* Check if the file should be excluded because of a filename match. 1
168    means exclude, 0 means not */
169 static int check_exclude_by_name(struct libjte_env *o,
170                                  char *filename, char **matched)
171 {
172     struct path_match *ptr = o->exclude_list;
173     regmatch_t pmatch[1];
174
175     while (ptr)
176     {
177         if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
178         {
179             *matched = ptr->match_rule;
180             return 1;
181         }
182         ptr = ptr->next;
183     }
184     
185     /* Not matched, so return 0 */
186     return 0;
187 }
188
189 /* Build the list of required inclusion regexps */
190 int jte_add_include(struct libjte_env *o, char *pattern)
191 {
192     int ret;
193     
194     ret = jte_add_path_match(o, pattern, 0);
195     return ret;
196 }
197
198 int libjte_destroy_entry_list(struct libjte_env *o, int flag)
199 {
200     entry_t *s, *s_next;
201
202     for (s = o->entry_list ; s != NULL; s = s_next) {
203         s_next = s->next;
204         if (s->entry_type == JTET_FILE_MATCH) {
205             if (s->data.file.filename != NULL)
206                 free(s->data.file.filename);
207         }
208         free(s);
209     }
210     o->entry_list = o->entry_last = NULL;
211     return 1;
212 }
213
214 /* Check if a file has to be MD5-matched to be valid. If we get called
215    here, we've failed to match any of the MD5 entries we were
216    given. If the path to the filename matches one of the paths in our
217    list, clearly it must have been corrupted. Abort with an error. */
218 static int check_md5_file_match(struct libjte_env *o, char *filename)
219 {
220     struct path_match *ptr = o->include_list;
221     regmatch_t pmatch[1];
222
223     while (ptr)
224     {
225         if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
226         {
227                         sprintf(o->message_buffer,
228                                 "File %1.1024s should have matched an MD5 entry, but didn't! (Rule '%1.1024s')",
229                                 filename, ptr->match_rule);
230                         libjte_add_msg_entry(o, o->message_buffer, 0);
231                         exit_if_enabled(o, 1);
232                         return -1;
233         }
234         ptr = ptr->next;
235     }
236     return 0;
237 }    
238
239 /* Should we list a file separately in the jigdo output, or should we
240    just dump it into the template file as binary data? Three things
241    cases to look for here:
242
243    1. Small files are better simply folded in, as they take less space that way.
244
245    2. Files in /doc (for example) may change in the archive all the
246       time and it's better to not have to fetch snapshot copies if we
247       can avoid it.      
248
249    3. Files living in specified paths *must* match an entry in the
250       md5-list, or they must have been corrupted. If we find a corrupt
251       file, bail out with an error.
252
253 */
254 int list_file_in_jigdo(struct libjte_env *o,
255             char *filename, off_t size, char **realname, unsigned char md5[16])
256 {
257     char *matched_rule;
258     md5_list_entry_t *entry = o->md5_list;
259     int md5sum_done = 0, ret;
260     
261     if (o->jtemplate_out == NULL)
262         return 0;
263
264     memset(md5, 0, 16);
265
266     /* Cheaper to check file size first */
267     if (size < o->jte_min_size)
268     {
269         if (o->verbose > 1) {
270             sprintf(o->message_buffer,
271                    "Jigdo-ignoring file %1.1024s; it's too small", filename);
272             libjte_add_msg_entry(o, o->message_buffer, 0);
273         }
274         return 0;
275     }
276     
277     /* Now check the excluded list by name */
278     if (check_exclude_by_name(o, filename, &matched_rule))
279     {
280         if (o->verbose > 1) {
281             sprintf(o->message_buffer,
282                     "Jigdo-ignoring file %1.1024s; it's covered in the exclude list by \"%1.1024s\"",
283                     filename, matched_rule);
284             libjte_add_msg_entry(o, o->message_buffer, 0);
285         }
286         return 0;
287     }
288
289     /* Check to see if the file is in our md5 list. Check three things:
290        
291        1. the size
292        2. the filename
293        3. (only if the first 2 match) the md5sum
294
295        If we get a match for all three, include the file and return
296        the full path to the file that we have gleaned from the mirror.
297     */
298
299     while (entry)
300     {
301         if (size == entry->size)
302         {
303             if (!strcmp(file_base_name(filename), file_base_name(entry->filename)))
304             {
305                 if (!md5sum_done)
306                 {
307                     ret = calculate_md5sum(filename, size, md5);
308                     if (ret < 0) { /* (0 is success) */
309                         sprintf(o->message_buffer,
310                                 "Error with file '%1.1024s' : errno=%d",
311                                 filename, errno);
312                         libjte_add_msg_entry(o, o->message_buffer, 0);
313                         return -1;
314                     }
315                     md5sum_done = 1;
316                 }
317                 if (!memcmp(md5, entry->MD5, sizeof(entry->MD5)))
318                 {
319                     *realname = entry->filename;
320                     return 1;
321                 }
322             }
323         }
324         entry = entry->next;
325     }
326
327     /* We haven't found an entry in our MD5 list to match this
328      * file. If we should have done, complain and bail out. */
329     ret = check_md5_file_match(o, filename);
330     return ret;
331 }
332
333 /* Add a mapping of pathnames (e.g. Debian=/mirror/debian). We should
334    be passed TO=FROM here */
335 int jte_add_mapping(struct libjte_env *o, char *arg)
336 {
337     struct path_mapping *new = NULL;
338     struct path_mapping *entry = NULL;
339     char *p = arg;
340     char *from = NULL;
341     char *to = NULL;
342     char *eqpt = NULL;
343
344     /* Find the "=" in the string passed. */
345     while (*p)
346     {
347         if ('=' == *p)
348         {
349             if (eqpt == NULL)
350                 eqpt = p;
351             p++;
352             from = p;
353             break;
354         }
355         p++;
356     }
357     if (from == NULL || !strlen(from) || eqpt == arg)
358         return EINVAL;
359     from = strdup(from);
360     if (from == NULL)
361         return ENOMEM;
362     to = calloc(1, (eqpt - arg) + 1);
363     if (to == NULL) {
364         free(from);
365         return ENOMEM;
366     }
367     strncpy(to, arg, eqpt - arg);
368     to[eqpt - arg] = 0;
369     
370     new = malloc(sizeof(*new));
371     if (!new) {
372         free(to);
373         free(from);
374         return ENOMEM;
375     }
376     new->from = from;
377     new->to = to;
378     new->next = NULL;
379
380     if (o->verbose > 0) {
381         sprintf(o->message_buffer,
382                 "Adding mapping from %1.1024s to %1.1024s for the jigdo file",
383                 from, to);
384         libjte_add_msg_entry(o, o->message_buffer, 0);
385     }
386     if (o->map_list == NULL)
387         o->map_list = new;
388     else
389     {
390         /* Order is important; add to the end of the list */
391         entry = o->map_list;
392         while (NULL != entry->next)
393             entry = entry->next;
394         entry->next = new;
395     }
396     return 0;
397 }
398
399
400 int libjte_destroy_path_mapping(struct libjte_env *o, int flag)
401 {
402     struct path_mapping *s, *s_next;
403
404     for (s = o->map_list; s != NULL; s = s_next) {
405         s_next = s->next;
406         free(s->from);
407         free(s->to);
408         free(s);
409     }
410     o->map_list = NULL;
411     return 1;
412 }
413
414
415 /* Check if the filename should be remapped; if so map it, otherwise
416    return the original name. */
417 static char *remap_filename(struct libjte_env *o, char *filename)
418 {
419     char *new_name = filename;
420     struct path_mapping *entry = o->map_list;
421     
422     while (entry)
423     {
424         if (!strncmp(filename, entry->from, strlen(entry->from)))
425         {
426             new_name = calloc(1, 2 + strlen(filename) + strlen(entry->to) - strlen(entry->from));
427             if (!new_name)
428             {
429                 sprintf(o->message_buffer,
430                         "Failed to malloc new filename; abort!");
431                 libjte_add_msg_entry(o, o->message_buffer, 0);
432                 exit_if_enabled(o, 1);
433                 return NULL;
434             }
435             sprintf(new_name, "%s:%s", entry->to, &filename[strlen(entry->from)]);
436             return new_name;
437         }
438         entry = entry->next;
439     }
440
441     /* No mapping in effect */
442     return strdup(filename);
443 }    
444
445 /* Write data to the template file and update the MD5 sum */
446 static int template_fwrite(struct libjte_env *o,
447                       const void *ptr, size_t size, size_t nmemb, FILE *stream)
448 {
449     size_t written;
450
451     checksum_update(o->template_context, ptr, size * nmemb);
452     written = fwrite(ptr, size, nmemb, stream);
453     o->template_size += written * size;
454     if (written != nmemb)
455         return 0;
456     return 1;
457 }
458
459 /* Create a new template file and initialise it */
460 static int write_template_header(struct libjte_env *o)
461 {
462     char buf[2048];
463     int i = 0;
464     char *p = buf;
465
466 #ifndef LIBJTE_WITH_LIBBZ2
467
468     if (o->jte_template_compression == JTE_TEMP_BZIP2) {
469         sprintf(o->message_buffer,
470             "libjte: Compression algorithm BZIP2 not enabled at compile time");
471         libjte_add_msg_entry(o, o->message_buffer, 0);
472         exit_if_enabled(o, 1);
473         return -1;
474     }
475
476 #endif /* LIBJTE_WITH_LIBBZ2 */
477
478     memset(buf, 0, sizeof(buf));
479
480     if (o->template_context != NULL)
481         checksum_free_context(o->template_context);
482     o->template_context = checksum_init_context(o->checksum_algo_tmpl,
483                                                 "template");
484     if (o->template_context == NULL)
485     {
486         sprintf(o->message_buffer,
487                 "cannot allocate template checksum contexts");
488         libjte_add_msg_entry(o, o->message_buffer, 0);
489         exit_if_enabled(o, 1);
490         return -1;
491     }
492     
493     i += sprintf(p, "JigsawDownload template %s libjte-%d.%d.%d \r\n",
494              JIGDO_TEMPLATE_VERSION, 
495              LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO);
496     p = &buf[i];
497
498     i += sprintf(p, "%s \r\n", JTE_COMMENT);
499     p = &buf[i];
500
501     i += sprintf(p, "\r\n");
502     if (template_fwrite(o, buf, i, 1, o->t_file) <= 0)
503         return 0;
504     return 1;
505 }
506
507 /* Read the MD5 list and build a list in memory for us to use later */
508 static void add_md5_entry(struct libjte_env *o,
509                    unsigned char *md5, uint64_t size, char *filename)
510 {
511     md5_list_entry_t *new = NULL;
512     
513     new = calloc(1, sizeof(md5_list_entry_t));
514     memcpy(new->MD5, md5, sizeof(new->MD5));
515     new->size = size;
516     new->filename = strdup(filename);
517     
518     /* Add to the end of the list */
519     if (NULL == o->md5_last)
520     {
521         o->md5_last = new;
522         o->md5_list = new;
523     }
524     else
525     {
526         o->md5_last->next = new;
527         o->md5_last = new;
528     }
529 }
530
531 int libjte_destroy_md5_list(struct libjte_env *o, int flag)
532 {
533     md5_list_entry_t *s, *s_next;
534
535     for (s = o->md5_list; s != NULL; s = s_next) {
536         s_next = s->next;
537         free(s->filename);
538         free(s);
539     }
540     o->md5_list = o->md5_last = NULL;
541     return 1;
542 }
543
544 /* Parse a 12-digit decimal number */
545 static uint64_t parse_number(unsigned char in[12])
546 {
547     uint64_t size = 0;
548     int i = 0;
549     
550     for (i = 0; i < 12; i++)
551     {
552         size *= 10;
553         if (isdigit(in[i]))
554             size += (in[i] - '0');
555     }
556
557     return size;
558 }
559     
560 /* Read the MD5 list and build a list in memory for us to use later
561    MD5 list format:
562
563    <---MD5--->  <--Size-->  <--Filename-->
564        32          12          remaining
565 */
566 static int parse_md5_list(struct libjte_env *o)
567 {
568     FILE *md5_file = NULL;
569     unsigned char buf[1024];
570     unsigned char md5[16];
571     char *filename = NULL;
572     unsigned char *numbuf = NULL;
573     int num_files = 0;
574     uint64_t size = 0;
575
576     md5_file = fopen(o->jmd5_list, "rb");
577     if (!md5_file)
578     {
579         sprintf(o->message_buffer, "cannot read from MD5 list file '%1.1024s'",
580                 o->jmd5_list);
581         libjte_add_msg_entry(o, o->message_buffer, 0);
582         exit_if_enabled(o, 1);
583         return -1;
584     }
585
586     memset(buf, 0, sizeof(buf));
587     while (fgets((char *)buf, sizeof(buf), md5_file))
588     {
589         numbuf = &buf[34];
590         filename = (char *)&buf[48];
591         /* Lose the trailing \n from the fgets() call */
592         if (buf[strlen((char *)buf)-1] == '\n')
593           buf[strlen((char *)buf)-1] = 0;
594
595         if (mk_MD5Parse(buf, md5))
596         {
597             sprintf(o->message_buffer, "cannot parse MD5 file '%1.1024s'",
598                     o->jmd5_list);
599             libjte_add_msg_entry(o, o->message_buffer, 0);
600             exit_if_enabled(o, 1);
601             return -1;
602         }
603         size = parse_number(numbuf);
604         add_md5_entry(o, md5, size, filename);
605         memset(buf, 0, sizeof(buf));
606         num_files++;
607     }
608     if (o->verbose > 0) {
609         sprintf(o->message_buffer,
610               "parse_md5_list: added MD5 checksums for %d files", num_files);
611         libjte_add_msg_entry(o, o->message_buffer, 0);
612     }
613     fclose(md5_file);
614     return 1;
615 }
616
617 /* Initialise state and start the jigdo template file */
618 int write_jt_header(struct libjte_env *o,
619                      FILE *template_file, FILE *jigdo_file)
620 {
621     int ret;
622
623     o->t_file = template_file;
624     o->j_file = jigdo_file;
625
626     /* Start checksum work for the image */
627     if (o->iso_context != NULL)
628         checksum_free_context(o->iso_context);
629     o->iso_context = checksum_init_context(o->checksum_algo_iso, "iso");
630     if (o->iso_context == NULL)
631     {
632         sprintf(o->message_buffer, "cannot allocate iso checksum contexts");
633         libjte_add_msg_entry(o, o->message_buffer, 0);
634         exit_if_enabled(o, 1);
635         return -1;
636     }
637
638     /* Start the template file */
639     ret = write_template_header(o);
640     if (ret <= 0)
641         return ret;
642
643     /* Load up the MD5 list if we've been given one */
644     if (o->jmd5_list) {
645         ret = parse_md5_list(o);
646         if (ret <= 0)
647             return ret;
648     }
649     return 1;
650 }
651
652 /* Compress and flush out a buffer full of template data */
653 /* Return 0 on failure, non-zero on success */
654 static int flush_gzip_chunk(struct libjte_env *o, void *buffer, off_t size)
655 {
656
657 #ifdef LIBJTE_WITH_ZLIB
658
659     z_stream c_stream; /* compression stream */
660     unsigned char comp_size_out[6];
661     unsigned char uncomp_size_out[6];
662     off_t compressed_size_out = 0;
663     int err = 0;
664     unsigned char *comp_buf = NULL;
665
666     c_stream.zalloc = NULL;
667     c_stream.zfree = NULL;
668     c_stream.opaque = NULL;
669
670     err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
671     if (err < 0)
672         return 0;
673     
674     if (NULL == (comp_buf = malloc(2 * size))) /* Worst case */
675         return 0;
676
677     c_stream.next_out = comp_buf;
678     c_stream.avail_out = 2 * size;
679     c_stream.next_in = buffer;
680     c_stream.avail_in = size;
681     
682     err = deflate(&c_stream, Z_NO_FLUSH);
683     if (err < 0)
684     {
685         free(comp_buf);
686         return 0;
687     }
688
689     err = deflate(&c_stream, Z_FINISH);
690     if (err < 0)
691     {
692         free(comp_buf);
693         return 0;
694     }
695     
696     compressed_size_out = c_stream.total_out + 16;
697     err = deflateEnd(&c_stream);
698     if (err < 0)
699     {
700         free(comp_buf);
701         return 0;
702     }
703
704     if (template_fwrite(o, "DATA", 4, 1, o->t_file) <= 0)
705     {
706         free(comp_buf);
707         return 0;
708     }
709
710     write_le48(compressed_size_out, &comp_size_out[0]);
711     if (template_fwrite(o, comp_size_out, sizeof(comp_size_out), 1, o->t_file)
712         <= 0)
713     {
714         free(comp_buf);
715         return 0;
716     }
717
718     write_le48(size, &uncomp_size_out[0]);
719     if (template_fwrite(o, uncomp_size_out, sizeof(uncomp_size_out), 1, 
720                         o->t_file) <= 0)
721     {
722         free(comp_buf);
723         return 0;
724     }
725     
726     if (template_fwrite(o, comp_buf, c_stream.total_out, 1, o->t_file) <= 0)
727         return 0;
728     free(comp_buf);
729     return 1;
730
731 #else /* LIBJTE_WITH_ZLIB */
732
733     static int complaints = 0;
734
735     if (complaints >= 3)
736         return 0;
737     complaints++;
738     fprintf(stderr,
739             "\nlibjte: Configuration error. Use without enabled zlib\n\n");
740     return 0;
741
742 #endif /* ! LIBJTE_WITH_ZLIB */
743
744 }
745
746
747 #ifdef LIBJTE_WITH_LIBBZ2
748
749 /* Compress and flush out a buffer full of template data */
750 /* Return 0 on failure, non-zero on success */
751 static int flush_bz2_chunk(struct libjte_env *o, void *buffer, off_t size)
752 {
753     bz_stream c_stream; /* compression stream */
754     unsigned char comp_size_out[6];
755     unsigned char uncomp_size_out[6];
756     off_t compressed_size_out = 0;
757     int err = 0;
758     char *comp_buf = NULL;
759
760     c_stream.bzalloc = NULL;
761     c_stream.bzfree = NULL;
762     c_stream.opaque = NULL;
763
764     err = BZ2_bzCompressInit(&c_stream, 9, 0, 0);
765     if (err < 0)
766         return 0;
767
768     if (NULL == (comp_buf = (char*) malloc(2 * size))) /* Worst case */
769         return 0;
770     c_stream.next_out = comp_buf;
771     c_stream.avail_out = 2 * size;
772     c_stream.next_in = buffer;
773     c_stream.avail_in = size;
774     
775     err = BZ2_bzCompress(&c_stream, BZ_FINISH);
776     if (err < 0)
777     {
778         free(comp_buf);
779         return 0;
780     }
781     
782     compressed_size_out = c_stream.total_out_lo32 + 16;
783     err = BZ2_bzCompressEnd(&c_stream);
784     if (err < 0)
785     {
786         free(comp_buf);
787         return 0;
788     }
789
790     if (template_fwrite(o, "BZIP", 4, 1, o->t_file) <= 0)
791     {
792         free(comp_buf);
793         return 0;
794     }
795
796     write_le48(compressed_size_out, &comp_size_out[0]);
797     if (template_fwrite(o, comp_size_out, sizeof(comp_size_out), 1, 
798                         o->t_file) <= 0)
799     {
800         free(comp_buf);
801         return 0;
802     }
803
804     write_le48(size, &uncomp_size_out[0]);
805     if (template_fwrite(o, uncomp_size_out, sizeof(uncomp_size_out), 1,
806                         o->t_file) <= 0)
807     {
808         free(comp_buf);
809         return 0;
810     }
811     
812     if (template_fwrite(o, comp_buf, c_stream.total_out_lo32, 1,
813                         o->t_file) <= 0)
814         return 0;
815     free(comp_buf);
816     return 1;
817 }
818
819 #else /* LIBJTE_WITH_LIBBZ2 */
820
821 /* Compress and flush out a buffer full of template data */
822 static int flush_bz2_chunk(struct libjte_env *o, void *buffer, off_t size)
823 {
824     return 0;
825 }
826
827 #endif /* ! LIBJTE_WITH_LIBBZ2 */
828
829
830 static int flush_compressed_chunk(struct libjte_env *o,
831                                   void *buffer, off_t size)
832 {
833     int ret;
834
835     if (size <= 0)
836         return 1;
837
838     if (o->jte_template_compression == JTE_TEMP_BZIP2)
839         ret = flush_bz2_chunk(o, buffer, size);
840     else
841         ret = flush_gzip_chunk(o, buffer, size);
842     return ret;
843 }
844
845 /* Append to an existing data buffer, and compress/flush it if
846    necessary */
847 static int write_compressed_chunk(struct libjte_env *o,
848                                    unsigned char *buffer, size_t size)
849 {
850     int ret;
851
852         if (o->uncomp_buf == NULL)
853         {
854                 if (o->jte_template_compression == JTE_TEMP_BZIP2)
855                         o->uncomp_size = 900 * 1024;
856                 else
857                         o->uncomp_size = 1024 * 1024;
858                 o->uncomp_buf = malloc(o->uncomp_size);
859                 if (o->uncomp_buf == NULL)
860                 {
861                    sprintf(o->message_buffer,
862                 "failed to allocate %lu bytes for template compression buffer",
863                            (unsigned long) o->uncomp_size);
864                    libjte_add_msg_entry(o, o->message_buffer, 0);
865                    exit_if_enabled(o, 1);
866                    return -1;
867                 }
868         }
869
870     if ((o->uncomp_buf_used + size) > o->uncomp_size)
871     {
872         ret = flush_compressed_chunk(o, o->uncomp_buf, o->uncomp_buf_used);
873         if (ret <= 0)
874             return ret;
875         o->uncomp_buf_used = 0;
876     }
877
878     if (!size) /* Signal a flush before we start writing the DESC entry */
879     {
880         ret = flush_compressed_chunk(o, o->uncomp_buf, o->uncomp_buf_used);
881         if (ret <= 0)
882             return ret;
883         return 1;
884     }
885     
886     if (!o->uncomp_buf_used)
887         memset(o->uncomp_buf, 0, o->uncomp_size);
888
889     while (size > o->uncomp_size)
890     {
891         ret = flush_compressed_chunk(o, buffer, o->uncomp_size);
892         if (ret <= 0)
893             return ret;
894         buffer += o->uncomp_size;
895         size -= o->uncomp_size;
896     }
897     memcpy(&(o->uncomp_buf[o->uncomp_buf_used]), buffer, size);
898     o->uncomp_buf_used += size;
899     return 1;
900 }
901
902 /* Loop through the list of DESC entries that we've built up and
903    append them to the template file */
904 static int write_template_desc_entries(struct libjte_env *o, off_t image_len)
905 {
906     entry_t *entry = o->entry_list;
907     off_t desc_len = 0;
908     unsigned char out_len[6];
909     jigdo_image_entry_t jimage;
910     int ret;
911
912     desc_len = 16 /* DESC + length twice */
913         + (sizeof(jigdo_file_entry_t) * o->num_matches)
914         + (sizeof(jigdo_chunk_entry_t) * o->num_chunks)
915         + sizeof(jigdo_image_entry_t);
916
917     write_le48(desc_len, &out_len[0]);
918     ret = write_compressed_chunk(o, NULL, 0);
919     if (ret <= 0)
920         return ret;
921     if (template_fwrite(o, "DESC", 4, 1, o->t_file) <= 0)
922         return 0;
923     if (template_fwrite(o, out_len, sizeof(out_len), 1, o->t_file) <= 0)
924         return 0;
925     
926     while (entry)
927     {
928         switch (entry->entry_type)
929         {
930             case JTET_FILE_MATCH:
931             {
932                 jigdo_file_entry_t jfile;
933                 jfile.type = 6; /* Matched file */
934                 write_le48(entry->data.file.file_length, &jfile.fileLen[0]);
935                 write_le64(entry->data.file.rsyncsum, &jfile.fileRsync[0]);
936                 memcpy(jfile.fileMD5, entry->data.file.md5, sizeof(jfile.fileMD5));
937                 if (template_fwrite(o, &jfile, sizeof(jfile), 1,
938                                     o->t_file) <= 0)
939                     return 0;
940                 break;
941             }
942             case JTET_NOMATCH:
943             {
944                 jigdo_chunk_entry_t jchunk;
945                                 jchunk.type = 2; /* Raw data, compressed */
946                 write_le48(entry->data.chunk.uncompressed_length, &jchunk.skipLen[0]);
947                 if (template_fwrite(o, &jchunk, sizeof(jchunk), 1,
948                                     o->t_file) <= 0)
949                     return 0;
950                 break;
951             }
952         }
953         entry = entry->next;
954     }
955
956     jimage.type = 5;
957     write_le48(image_len, &jimage.imageLen[0]);
958     checksum_copy(o->iso_context, CHECK_MD5, &jimage.imageMD5[0]);
959     write_le32(MIN_JIGDO_FILE_SIZE, &jimage.blockLen[0]);
960     if (template_fwrite(o, &jimage, sizeof(jimage), 1, o->t_file) <= 0)
961         return 0;
962     if(template_fwrite(o, out_len, sizeof(out_len), 1, o->t_file) <= 0)
963         return 0;
964     return 1;
965 }
966
967 /* Dump a buffer in jigdo-style "base64" */
968 static char *base64_dump(struct libjte_env *o,
969                          unsigned char *buf, size_t buf_size)
970 {
971     const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
972     int value = 0;
973     unsigned int i;
974     int bits = 0;
975     char *output_buffer = NULL;
976     int output_buffer_size;
977     char *p = NULL;
978
979     output_buffer_size = buf_size * 8 / 6 + 1 + 1; /* round up , care for 0 */
980     p = output_buffer = calloc(1, output_buffer_size);
981     if (output_buffer == NULL)
982     {
983         sprintf(o->message_buffer,
984                 "base64_dump: Out of memory for buffer size %d",
985                 output_buffer_size);
986         libjte_add_msg_entry(o, o->message_buffer, 0);
987         exit_if_enabled(o, 1);
988         return NULL;
989     }
990     memset(output_buffer, 0, output_buffer_size);
991
992     for (i = 0; i < buf_size ; i++)
993     {
994         value = (value << 8) | buf[i];
995         bits += 2;
996         p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
997         if (bits >= 6) {
998             bits -= 6;
999             p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
1000         }
1001     }
1002     if (bits > 0)
1003     {
1004         value <<= 6 - bits;
1005         p += sprintf(p, "%c", b64_enc[value & 63U]);
1006     }
1007     return output_buffer;
1008 }
1009
1010
1011 static char *uint64_to_dec(uint64_t num, char dec[40])
1012 {
1013     int i, l = 0, tr;
1014
1015     dec[0] = 0;
1016     while (num > 0 && l < 39) {
1017         dec[l++] = '0' + num % 10;
1018         num /= 10;
1019     }
1020     dec[l] = 0;
1021
1022     /* Revert sequence of digits to Big Endian Decimal */
1023     for (i = 0;i < l / 2; i++) {
1024         tr = dec[i];
1025         dec[i] = dec[l - i - 1];
1026         dec[l - i - 1] = tr;
1027     }
1028     return dec;
1029 }
1030
1031
1032 /* Write the .jigdo file to match the .template we've just finished. */
1033 static int write_jigdo_file(struct libjte_env *o)
1034 {
1035     unsigned char template_md5sum[16];
1036     entry_t *entry = o->entry_list;
1037     int i = 0;
1038     struct checksum_info *info = NULL;
1039     FILE *j_file = o->j_file;
1040     char *b64, dec[40];
1041
1042     checksum_final(o->template_context);
1043     checksum_copy(o->template_context, CHECK_MD5, &template_md5sum[0]);
1044
1045     fprintf(j_file, "# JigsawDownload\n");
1046     fprintf(j_file, "# See <http://atterer.org/jigdo/> for details about jigdo\n");
1047     fprintf(j_file, "# See <http://www.einval.com/~steve/software/CD/JTE/> for details about JTE\n\n");
1048     
1049     fprintf(j_file, "[Jigdo]\n");
1050     fprintf(j_file, "Version=%s\n", JIGDO_TEMPLATE_VERSION);
1051     fprintf(j_file, "Generator=libjte-%d.%d.%d\n\n",
1052             LIBJTE_VERSION_MAJOR, LIBJTE_VERSION_MINOR, LIBJTE_VERSION_MICRO);
1053
1054     fprintf(j_file, "[Image]\n");
1055     fprintf(j_file, "Filename=%s\n", file_base_name(o->outfile));
1056     fprintf(j_file, "Template=http://localhost/%s\n", o->jtemplate_out);
1057
1058     b64 = base64_dump(o, &template_md5sum[0], sizeof(template_md5sum));
1059     if (b64 == NULL)
1060         return -1;
1061     fprintf(j_file, "Template-MD5Sum=%s \n", b64);
1062     free(b64);
1063
1064     for (i = 0; i < NUM_CHECKSUMS; i++)
1065     {
1066         if (o->checksum_algo_tmpl & (1 << i))
1067         {
1068             info = checksum_information(i);
1069             fprintf(j_file, "# Template Hex %sSum %s\n", info->name,
1070                     checksum_hex(o->template_context, i));
1071         }
1072     }
1073     fprintf(j_file, "# Template size %s bytes\n",
1074             uint64_to_dec(o->template_size, dec));
1075
1076     for (i = 0; i < NUM_CHECKSUMS; i++)
1077     {
1078         if (o->checksum_algo_iso & (1 << i))
1079         {
1080             info = checksum_information(i);
1081             fprintf(j_file, "# Image Hex %sSum %s\n",
1082                     info->name, checksum_hex(o->iso_context, i));
1083         }
1084     }
1085
1086     fprintf(j_file, "# Image size %s bytes\n\n",
1087             uint64_to_dec(o->image_size, dec));
1088
1089     fprintf(j_file, "[Parts]\n");
1090     while (entry)
1091     {
1092         if (JTET_FILE_MATCH == entry->entry_type)
1093         {
1094             char *new_name = remap_filename(o, entry->data.file.filename);
1095
1096             if (new_name == NULL)
1097                 return -1;
1098             b64 = base64_dump(o, &entry->data.file.md5[0],
1099                               sizeof(entry->data.file.md5));
1100             if (b64 == NULL)
1101                 return -1;
1102             fprintf(j_file, "%s=%s\n", b64, new_name);
1103             free(b64);
1104             free(new_name);
1105         }
1106         entry = entry->next;
1107     }
1108
1109     fprintf(j_file, "\n[Servers]\n");
1110     fflush(j_file);
1111     return 1;
1112 }
1113
1114 /* Finish and flush state; for now:
1115    
1116    1. Dump the DESC blocks and the footer information in the jigdo template file
1117    2. Write the jigdo .jigdo file containing file pointers
1118 */
1119 int write_jt_footer(struct libjte_env *o)
1120 {
1121     int ret;
1122
1123     /* Finish calculating the image's checksum */
1124     checksum_final(o->iso_context);
1125
1126     ret = write_template_desc_entries(o, o->image_size);
1127     if (ret <= 0)
1128         return ret;
1129
1130     ret = write_jigdo_file(o);
1131     return ret;
1132 }
1133
1134 /* Add a raw data entry to the list of extents; no file to match */
1135 static void add_unmatched_entry(struct libjte_env *o, int uncompressed_length)
1136 {
1137     entry_t *new_entry = NULL;
1138
1139     /* Can we extend a previous non-match entry? */
1140     if (o->entry_last && (JTET_NOMATCH == o->entry_last->entry_type))
1141     {
1142         o->entry_last->data.chunk.uncompressed_length += uncompressed_length;
1143         return;
1144     }
1145
1146     new_entry = calloc(1, sizeof(entry_t));
1147     new_entry->entry_type = JTET_NOMATCH;
1148     new_entry->next = NULL;
1149     new_entry->data.chunk.uncompressed_length = uncompressed_length;
1150
1151     /* Add to the end of the list */
1152     if (NULL == o->entry_last)
1153     {
1154         o->entry_last = new_entry;
1155         o->entry_list = new_entry;
1156     }
1157     else
1158     {
1159         o->entry_last->next = new_entry;
1160         o->entry_last = new_entry;
1161     }
1162     o->num_chunks++;
1163 }
1164
1165 /* Add a file match entry to the list of extents */
1166 static void add_file_entry(struct libjte_env *o,
1167                            char *filename, off_t size, unsigned char *md5,
1168                            uint64_t rsyncsum)
1169 {
1170     entry_t *new_entry = NULL;
1171
1172     new_entry = calloc(1, sizeof(entry_t));
1173     new_entry->entry_type = JTET_FILE_MATCH;
1174     new_entry->next = NULL;
1175     memcpy(new_entry->data.file.md5, md5, sizeof(new_entry->data.file.md5));
1176     new_entry->data.file.file_length = size;
1177     new_entry->data.file.rsyncsum = rsyncsum;
1178     new_entry->data.file.filename = strdup(filename);
1179
1180     /* Add to the end of the list */
1181     if (NULL == o->entry_last)
1182     {
1183         o->entry_last = new_entry;
1184         o->entry_list = new_entry;
1185     }
1186     else
1187     {
1188         o->entry_last->next = new_entry;
1189         o->entry_last = new_entry;
1190     }
1191     o->num_matches++;
1192 }    
1193
1194 /* Cope with an unmatched block in the .iso file:
1195
1196    1. Write a compressed data chunk in the jigdo template file
1197    2. Add an entry in our list of unmatched chunks for later */
1198 int jtwrite(struct libjte_env *o, void *buffer, int size, int count)
1199 {
1200     int ret;
1201
1202     if (o->jtemplate_out == NULL)
1203         return 0;
1204
1205     /* Update the global image checksum */
1206     checksum_update(o->iso_context, buffer, size * count);
1207
1208     /* Write a compressed version of the data to the template file,
1209        and add a reference on the state list so we can write that
1210        later. */
1211     ret = write_compressed_chunk(o, buffer, size*count);
1212     if (ret <= 0)
1213         return ret;
1214     add_unmatched_entry(o, size*count);
1215     return 1;
1216 }
1217
1218 /* Cope with a file entry in the .iso file:
1219
1220    1. Read the file for the image's md5 checksum
1221    2. Add an entry in our list of files to be written into the .jigdo later
1222 */
1223 int write_jt_match_record(struct libjte_env *o,
1224                            char *filename, char *mirror_name, int sector_size,
1225                            off_t size, unsigned char md5[16])
1226 {
1227     char                buf[32768];
1228     off_t               remain = size;
1229         FILE               *infile = NULL;
1230         int                     use = 0;
1231     uint64_t  rsync64_sum = 0;
1232     int first_block = 1;
1233
1234     memset(buf, 0, sizeof(buf));
1235
1236     if ((infile = fopen(filename, "rb")) == NULL) {
1237 #ifndef HAVE_STRERROR
1238                 sprintf(o->message_buffer, "cannot open '%s': (%d)",
1239                                 filename, errno);
1240 #else
1241                 sprintf(o->message_buffer, "cannot open '%s': %s",
1242                                 filename, strerror(errno));
1243 #endif
1244                 libjte_add_msg_entry(o, o->message_buffer, 0);
1245                 exit_if_enabled(o, 1);
1246                 return -1;
1247         }
1248
1249     while (remain > 0)
1250     {
1251         use = remain;
1252         if (remain > sizeof(buf))
1253             use = sizeof(buf);
1254                 if (fread(buf, 1, use, infile) == 0)
1255         {
1256                         sprintf(o->message_buffer,
1257                                 "cannot read from '%s'", filename);
1258                         libjte_add_msg_entry(o, o->message_buffer, 0);
1259                         exit_if_enabled(o, 1);
1260                         return -1;
1261         }
1262         if (first_block)
1263             rsync64_sum = rsync64((unsigned char *) buf, MIN_JIGDO_FILE_SIZE);
1264         checksum_update(o->iso_context, (unsigned char *) buf, use);
1265         remain -= use;
1266         first_block = 0;
1267     }
1268
1269     fclose(infile);
1270     
1271     /* Update the image checksum with any necessary padding data */
1272     if (size % sector_size)
1273     {
1274         int pad_size = sector_size - (size % sector_size);
1275         memset(buf, 0, pad_size);
1276         checksum_update(o->iso_context, (unsigned char *) buf, pad_size);
1277     }
1278
1279     add_file_entry(o, mirror_name, size, &md5[0], rsync64_sum);
1280     if (size % sector_size)
1281     {
1282         int pad_size = sector_size - (size % sector_size);
1283         write_compressed_chunk(o, (unsigned char *) buf, pad_size);
1284         add_unmatched_entry(o, pad_size);
1285     }        
1286     return 1;
1287 }
1288
1289 int libjte_add_msg_entry(struct libjte_env *o, char *message, int flag)
1290 {
1291     jigdo_msg_entry_t *new_entry = NULL, *s = NULL;
1292     int list_length = 0;
1293
1294     if (o->error_behavior & 1) {
1295          fprintf(stderr, "libjte: %s\n", message);
1296          return 1;
1297     }
1298     if (o->msg_list != NULL) {
1299         /* Find end of list and eventually do an emergency message dump */
1300         for (s = o->msg_list; s->next != NULL; s = s->next)
1301             list_length++;
1302         if (list_length >= JTE_MAX_ERROR_LIST_LENGTH) {
1303             libjte_clear_msg_list(o, 1 | 2); /* dump to stderr */
1304             o->msg_list = s = NULL;
1305         }
1306     }
1307
1308     new_entry = calloc(1, sizeof(jigdo_msg_entry_t));
1309     if (new_entry == NULL) {
1310 no_mem:;
1311         fprintf(stderr, "libjte: %s\n", message);
1312         fprintf(stderr, "libjte: OUT OF MEMORY\n");
1313         return -1;
1314     }
1315     new_entry->next = NULL;
1316     new_entry->message = strdup(message);
1317     if (new_entry->message == NULL) {
1318         free(new_entry);
1319         goto no_mem;
1320     }
1321     if (o->msg_list == NULL)
1322         o->msg_list = new_entry;
1323     else
1324         s->next = new_entry;
1325     return 1;
1326 }
1327