summaryrefslogtreecommitdiff
path: root/genisoimage/jte.c
diff options
context:
space:
mode:
Diffstat (limited to 'genisoimage/jte.c')
-rw-r--r--genisoimage/jte.c1042
1 files changed, 1042 insertions, 0 deletions
diff --git a/genisoimage/jte.c b/genisoimage/jte.c
new file mode 100644
index 0000000..0dff289
--- /dev/null
+++ b/genisoimage/jte.c
@@ -0,0 +1,1042 @@
+/*
+ * jte.c
+ *
+ * Copyright (c) 2004-2006 Steve McIntyre <steve@einval.com>
+ *
+ * Implementation of the Jigdo Template Engine - make jigdo files
+ * directly when making ISO images
+ *
+ * GNU GPL v2
+ */
+
+#include <mconfig.h>
+#include "genisoimage.h"
+#include <timedefs.h>
+#include <fctldefs.h>
+#include <zlib.h>
+#include <bzlib.h>
+#include <regex.h>
+#ifdef SORTING
+#include "match.h"
+#endif /* SORTING */
+#include <errno.h>
+#include <schily.h>
+#ifdef DVD_VIDEO
+#include "dvd_reader.h"
+#include "dvd_file.h"
+#include "ifo_read.h"
+#include "endianconv.h"
+#include "checksum.h"
+#endif
+#ifdef APPLE_HYB
+#include <ctype.h>
+#endif
+
+#ifdef VMS
+#include "vms.h"
+#endif
+
+/* Different types used in building our state list below */
+#define JTET_FILE_MATCH 1
+#define JTET_NOMATCH 2
+
+#define JTE_VER_MAJOR 0x0001
+#define JTE_VER_MINOR 0x0013
+#define JTE_NAME "JTE"
+#define JTE_COMMENT "JTE at http://www.einval.com/~steve/software/JTE/ ; jigdo at http://atterer.org/jigdo/"
+
+#define JIGDO_TEMPLATE_VERSION "1.1"
+
+/*
+ Simple list to hold the results of -jigdo-exclude and
+ -jigdo-force-match command line options. Seems easiest to do this
+ using regexps.
+*/
+struct path_match
+{
+ regex_t match_pattern;
+ char *match_rule;
+ struct path_match *next;
+};
+
+/* List of mappings e.g. Debian=/mirror/debian */
+struct path_mapping
+{
+ char *from;
+ char *to;
+ struct path_mapping *next;
+};
+
+FILE *jtjigdo = NULL; /* File handle used throughout for the jigdo file */
+FILE *jttemplate = NULL; /* File handle used throughout for the template file */
+char *jjigdo_out = NULL; /* Output name for jigdo .jigdo file; NULL means don't do it */
+char *jtemplate_out = NULL; /* Output name for jigdo template file; NULL means don't do it */
+char *jmd5_list = NULL; /* Name of file to use for MD5 checking */
+int jte_min_size = MIN_JIGDO_FILE_SIZE;
+jtc_t jte_template_compression = JTE_TEMP_GZIP;
+struct path_match *exclude_list = NULL;
+struct path_match *include_list = NULL;
+struct path_mapping *map_list = NULL;
+unsigned long long template_size = 0;
+unsigned long long image_size = 0;
+int checksum_algo_iso = (CHECK_MD5_USED | \
+ CHECK_SHA1_USED | \
+ CHECK_SHA256_USED | \
+ CHECK_SHA512_USED);
+int checksum_algo_tmpl = CHECK_MD5_USED;
+
+static checksum_context_t *iso_context = NULL;
+static checksum_context_t *template_context = NULL;
+
+/* List of files that we've seen, ready to write into the template and
+ jigdo files */
+typedef struct _file_entry
+{
+ unsigned char md5[16];
+ off_t file_length;
+ unsigned long long rsyncsum;
+ char *filename;
+} file_entry_t;
+
+typedef struct _unmatched_entry
+{
+ off_t uncompressed_length;
+} unmatched_entry_t;
+
+typedef struct _entry
+{
+ int entry_type; /* JTET_TYPE as above */
+ struct _entry *next;
+ union
+ {
+ file_entry_t file;
+ unmatched_entry_t chunk;
+ } data;
+} entry_t;
+
+typedef struct _jigdo_file_entry
+{
+ unsigned char type;
+ unsigned char fileLen[6];
+ unsigned char fileRsync[8];
+ unsigned char fileMD5[16];
+} jigdo_file_entry_t;
+
+typedef struct _jigdo_chunk_entry
+{
+ unsigned char type;
+ unsigned char skipLen[6];
+} jigdo_chunk_entry_t;
+
+typedef struct _jigdo_image_entry
+{
+ unsigned char type;
+ unsigned char imageLen[6];
+ unsigned char imageMD5[16];
+ unsigned char blockLen[4];
+} jigdo_image_entry_t;
+
+typedef struct _md5_list_entry
+{
+ struct _md5_list_entry *next;
+ unsigned char MD5[16];
+ unsigned long long size;
+ char *filename;
+} md5_list_entry_t;
+
+entry_t *entry_list = NULL;
+entry_t *entry_last = NULL;
+FILE *t_file = NULL;
+FILE *j_file = NULL;
+int num_matches = 0;
+int num_chunks = 0;
+md5_list_entry_t *md5_list = NULL;
+md5_list_entry_t *md5_last = NULL;
+
+/* Grab the file component from a full path */
+static char *file_base_name(char *path)
+{
+ char *endptr = path;
+ char *ptr = path;
+
+ while (*ptr != '\0')
+ {
+ if ('/' == *ptr)
+ endptr = ++ptr;
+ else
+ ++ptr;
+ }
+ return endptr;
+}
+
+/* Build the list of exclusion regexps */
+extern int jte_add_exclude(char *pattern)
+{
+ struct path_match *new = NULL;
+
+ new = malloc(sizeof *new);
+ if (!new)
+ return ENOMEM;
+
+ regcomp(&new->match_pattern, pattern, REG_NEWLINE);
+ new->match_rule = pattern;
+
+ /* Order on the exclude list doesn't matter! */
+ new->next = exclude_list;
+
+ exclude_list = new;
+ return 0;
+}
+
+/* Check if the file should be excluded because of a filename match. 1
+ means exclude, 0 means not */
+static int check_exclude_by_name(char *filename, char **matched)
+{
+ struct path_match *ptr = exclude_list;
+ regmatch_t pmatch[1];
+ int i = 0;
+
+ while (ptr)
+ {
+ if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
+ {
+ *matched = ptr->match_rule;
+ return 1;
+ }
+ ptr = ptr->next;
+ }
+
+ /* Not matched, so return 0 */
+ return 0;
+}
+
+/* Build the list of required inclusion regexps */
+extern int jte_add_include(char *pattern)
+{
+ struct path_match *new = NULL;
+
+ new = malloc(sizeof *new);
+ if (!new)
+ return ENOMEM;
+
+ regcomp(&new->match_pattern, pattern, REG_NEWLINE);
+ new->match_rule = pattern;
+
+ /* Order on the include list doesn't matter! */
+ new->next = include_list;
+
+ include_list = new;
+ return 0;
+}
+
+/* Check if a file has to be MD5-matched to be valid. If we get called
+ here, we've failed to match any of the MD5 entries we were
+ given. If the path to the filename matches one of the paths in our
+ list, clearly it must have been corrupted. Abort with an error. */
+static void check_md5_file_match(char *filename)
+{
+ struct path_match *ptr = include_list;
+ regmatch_t pmatch[1];
+ int i = 0;
+
+ while (ptr)
+ {
+ if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
+ {
+#ifdef USE_LIBSCHILY
+ comerr("File %s should have matched an MD5 entry, but didn't! (Rule '%s')\n", filename, ptr->match_rule);
+#else
+ fprintf(stderr, "File %s should have matched an MD5 entry, but didn't! (Rule '%s')\n", filename, ptr->match_rule);
+ exit(1);
+#endif
+ }
+ ptr = ptr->next;
+ }
+}
+
+/* Should we list a file separately in the jigdo output, or should we
+ just dump it into the template file as binary data? Three things
+ cases to look for here:
+
+ 1. Small files are better simply folded in, as they take less space that way.
+
+ 2. Files in /doc (for example) may change in the archive all the
+ time and it's better to not have to fetch snapshot copies if we
+ can avoid it.
+
+ 3. Files living in specified paths *must* match an entry in the
+ md5-list, or they must have been corrupted. If we find a corrupt
+ file, bail out with an error.
+
+*/
+extern int list_file_in_jigdo(char *filename, off_t size, char **realname, unsigned char md5[16])
+{
+ char *matched_rule;
+ md5_list_entry_t *entry = md5_list;
+ int md5sum_done = 0;
+
+ if (!jtemplate_out)
+ return 0;
+
+ memset(md5, 0, sizeof(md5));
+
+ /* Cheaper to check file size first */
+ if (size < jte_min_size)
+ {
+ if (verbose > 1)
+ fprintf(stderr, "Jigdo-ignoring file %s; it's too small\n", filename);
+ return 0;
+ }
+
+ /* Now check the excluded list by name */
+ if (check_exclude_by_name(filename, &matched_rule))
+ {
+ if (verbose > 1)
+ fprintf(stderr, "Jigdo-ignoring file %s; it's covered in the exclude list by \"%s\"\n", filename, matched_rule);
+ return 0;
+ }
+
+ /* Check to see if the file is in our md5 list. Check three things:
+
+ 1. the size
+ 2. the filename
+ 3. (only if the first 2 match) the md5sum
+
+ If we get a match for all three, include the file and return
+ the full path to the file that we have gleaned from the mirror.
+ */
+
+ while (entry)
+ {
+ if (size == entry->size)
+ {
+ if (!strcmp(file_base_name(filename), file_base_name(entry->filename)))
+ {
+ if (!md5sum_done)
+ {
+ calculate_md5sum(filename, size, md5);
+ md5sum_done = 1;
+ }
+ if (!memcmp(md5, entry->MD5, sizeof(entry->MD5)))
+ {
+ *realname = entry->filename;
+ return 1;
+ }
+ }
+ }
+ entry = entry->next;
+ }
+
+ /* We haven't found an entry in our MD5 list to match this
+ * file. If we should have done, complain and bail out. */
+ check_md5_file_match(filename);
+ return 0;
+}
+
+/* Add a mapping of pathnames (e.g. Debian=/mirror/debian). We should
+ be passed TO=FROM here */
+extern int jte_add_mapping(char *arg)
+{
+ int error = 0;
+ struct path_mapping *new = NULL;
+ struct path_mapping *entry = NULL;
+ char *p = arg;
+ char *from = NULL;
+ char *to = NULL;
+
+ /* Find the "=" in the string passed. Set it to NULL and we can
+ use the string in-place */
+ while (*p)
+ {
+ if ('=' == *p)
+ {
+ *p = 0;
+ p++;
+ to = arg;
+ from = p;
+ }
+ p++;
+ }
+ if (!from || !strlen(from) || !to || !strlen(to))
+ return EINVAL;
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return ENOMEM;
+
+ new->from = from;
+ new->to = to;
+ new->next = NULL;
+
+ if (verbose > 0)
+ fprintf(stderr, "Adding mapping from %s to %s for the jigdo file\n", from, to);
+ if (!map_list)
+ map_list = new;
+ else
+ {
+ /* Order is important; add to the end of the list */
+ entry = map_list;
+ while (NULL != entry->next)
+ entry = entry->next;
+ entry->next = new;
+ }
+ return 0;
+}
+
+/* Check if the filename should be remapped; if so map it, otherwise
+ return the original name. */
+static char *remap_filename(char *filename)
+{
+ char *new_name = filename;
+ struct path_mapping *entry = map_list;
+
+ while (entry)
+ {
+ if (!strncmp(filename, entry->from, strlen(entry->from)))
+ {
+ new_name = calloc(1, 2 + strlen(filename) + strlen(entry->to) - strlen(entry->from));
+ if (!new_name)
+ {
+ fprintf(stderr, "Failed to malloc new filename; abort!\n");
+ exit(1);
+ }
+ sprintf(new_name, "%s:%s", entry->to, &filename[strlen(entry->from)]);
+ return new_name;
+ }
+ entry = entry->next;
+ }
+
+ /* No mapping in effect */
+ return strdup(filename);
+}
+
+/* Write data to the template file and update the MD5 sum */
+static size_t template_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+ checksum_update(template_context, ptr, size * nmemb);
+ template_size += (unsigned long long)size * nmemb;
+ return fwrite(ptr, size, nmemb, stream);
+}
+
+/* Create a new template file and initialise it */
+static void write_template_header()
+{
+ char buf[2048];
+ int i = 0;
+ char *p = buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ template_context = checksum_init_context(checksum_algo_tmpl, "template");
+ if (!template_context)
+ {
+#ifdef USE_LIBSCHILY
+ comerr("cannot allocate template checksum contexts\n");
+#else
+ fprintf(stderr, "cannot allocate template checksum contexts\n");
+ exit(1);
+#endif
+ }
+
+ i += sprintf(p, "JigsawDownload template %s %s/%d.%d \r\n",
+ JIGDO_TEMPLATE_VERSION, JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
+ p = &buf[i];
+
+ i += sprintf(p, "%s \r\n", JTE_COMMENT);
+ p = &buf[i];
+
+ i += sprintf(p, "\r\n");
+ template_fwrite(buf, i, 1, t_file);
+}
+
+/* Read the MD5 list and build a list in memory for us to use later */
+static void add_md5_entry(unsigned char *md5, unsigned long long size, char *filename)
+{
+ int error = 0;
+ md5_list_entry_t *new = NULL;
+
+ new = calloc(1, sizeof(md5_list_entry_t));
+ memcpy(new->MD5, md5, sizeof(new->MD5));
+ new->size = size;
+ new->filename = strdup(filename);
+
+ /* Add to the end of the list */
+ if (NULL == md5_last)
+ {
+ md5_last = new;
+ md5_list = new;
+ }
+ else
+ {
+ md5_last->next = new;
+ md5_last = new;
+ }
+}
+
+/* Parse a 12-digit decimal number */
+static unsigned long long parse_number(unsigned char in[12])
+{
+ unsigned long long size = 0;
+ int i = 0;
+
+ for (i = 0; i < 12; i++)
+ {
+ size *= 10;
+ if (isdigit(in[i]))
+ size += (in[i] - '0');
+ }
+
+ return size;
+}
+
+/* Read the MD5 list and build a list in memory for us to use later
+ MD5 list format:
+
+ <---MD5---> <--Size--> <--Filename-->
+ 32 12 remaining
+*/
+static void parse_md5_list(void)
+{
+ FILE *md5_file = NULL;
+ unsigned char buf[1024];
+ unsigned char md5[16];
+ char *filename = NULL;
+ unsigned char *numbuf = NULL;
+ int num_files = 0;
+ unsigned long long size = 0;
+
+ md5_file = fopen(jmd5_list, "rb");
+ if (!md5_file)
+ {
+#ifdef USE_LIBSCHILY
+ comerr("cannot read from MD5 list file '%s'\n", jmd5_list);
+#else
+ fprintf(stderr, "cannot read from MD5 list file '%s'\n", jmd5_list);
+ exit(1);
+#endif
+ }
+
+ memset(buf, 0, sizeof(buf));
+ while (fgets((char *)buf, sizeof(buf), md5_file))
+ {
+ numbuf = &buf[34];
+ filename = (char *)&buf[48];
+ /* Lose the trailing \n from the fgets() call */
+ if (buf[strlen((char *)buf)-1] == '\n')
+ buf[strlen((char *)buf)-1] = 0;
+
+ if (mk_MD5Parse(buf, md5))
+ {
+#ifdef USE_LIBSCHILY
+ comerr("cannot parse MD5 file '%s'\n", jmd5_list);
+#else
+ fprintf(stderr, "cannot parse MD5 file '%s'\n", jmd5_list);
+ exit(1);
+#endif
+ }
+ size = parse_number(numbuf);
+ add_md5_entry(md5, size, filename);
+ memset(buf, 0, sizeof(buf));
+ num_files++;
+ }
+ if (verbose > 0)
+ fprintf(stderr, "parse_md5_list: added MD5 checksums for %d files\n", num_files);
+ fclose(md5_file);
+}
+
+/* Initialise state and start the jigdo template file */
+void write_jt_header(FILE *template_file, FILE *jigdo_file)
+{
+ t_file = template_file;
+ j_file = jigdo_file;
+
+ /* Start checksum work for the image */
+ iso_context = checksum_init_context(checksum_algo_iso, "iso");
+ if (!iso_context)
+ {
+#ifdef USE_LIBSCHILY
+ comerr("cannot allocate iso checksum contexts\n");
+#else
+ fprintf(stderr, "cannot allocate iso checksum contexts\n");
+ exit(1);
+#endif
+ }
+
+ /* Start the template file */
+ write_template_header();
+
+ /* Load up the MD5 list if we've been given one */
+ if (jmd5_list)
+ parse_md5_list();
+}
+
+/* Compress and flush out a buffer full of template data */
+static void flush_gzip_chunk(void *buffer, off_t size)
+{
+ z_stream c_stream; /* compression stream */
+ unsigned char comp_size_out[6];
+ unsigned char uncomp_size_out[6];
+ off_t compressed_size_out = 0;
+ int err = 0;
+ unsigned char *comp_buf = NULL;
+
+ c_stream.zalloc = NULL;
+ c_stream.zfree = NULL;
+ c_stream.opaque = NULL;
+
+ err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
+ comp_buf = malloc(2 * size); /* Worst case */
+ c_stream.next_out = comp_buf;
+ c_stream.avail_out = 2 * size;
+ c_stream.next_in = buffer;
+ c_stream.avail_in = size;
+
+ err = deflate(&c_stream, Z_NO_FLUSH);
+ err = deflate(&c_stream, Z_FINISH);
+
+ compressed_size_out = c_stream.total_out + 16;
+ err = deflateEnd(&c_stream);
+
+ template_fwrite("DATA", 4, 1, t_file);
+
+ write_le48(compressed_size_out, &comp_size_out[0]);
+ template_fwrite(comp_size_out, sizeof(comp_size_out), 1, t_file);
+
+ write_le48(size, &uncomp_size_out[0]);
+ template_fwrite(uncomp_size_out, sizeof(uncomp_size_out), 1, t_file);
+
+ template_fwrite(comp_buf, c_stream.total_out, 1, t_file);
+ free(comp_buf);
+}
+
+/* Compress and flush out a buffer full of template data */
+static void flush_bz2_chunk(void *buffer, off_t size)
+{
+ bz_stream c_stream; /* compression stream */
+ unsigned char comp_size_out[6];
+ unsigned char uncomp_size_out[6];
+ off_t compressed_size_out = 0;
+ int err = 0;
+ unsigned char *comp_buf = NULL;
+
+ c_stream.bzalloc = NULL;
+ c_stream.bzfree = NULL;
+ c_stream.opaque = NULL;
+
+ err = BZ2_bzCompressInit(&c_stream, 9, 0, 0);
+ comp_buf = malloc(2 * size); /* Worst case */
+ c_stream.next_out = comp_buf;
+ c_stream.avail_out = 2 * size;
+ c_stream.next_in = buffer;
+ c_stream.avail_in = size;
+
+ err = BZ2_bzCompress(&c_stream, BZ_FINISH);
+
+ compressed_size_out = c_stream.total_out_lo32 + 16;
+ err = BZ2_bzCompressEnd(&c_stream);
+
+ template_fwrite("BZIP", 4, 1, t_file);
+
+ write_le48(compressed_size_out, &comp_size_out[0]);
+ template_fwrite(comp_size_out, sizeof(comp_size_out), 1, t_file);
+
+ write_le48(size, &uncomp_size_out[0]);
+ template_fwrite(uncomp_size_out, sizeof(uncomp_size_out), 1, t_file);
+
+ template_fwrite(comp_buf, c_stream.total_out_lo32, 1, t_file);
+ free(comp_buf);
+}
+
+static void flush_compressed_chunk(void *buffer, off_t size)
+{
+ if (jte_template_compression == JTE_TEMP_BZIP2)
+ flush_bz2_chunk(buffer, size);
+ else
+ flush_gzip_chunk(buffer, size);
+}
+
+/* Append to an existing data buffer, and compress/flush it if
+ necessary */
+static void write_compressed_chunk(unsigned char *buffer, size_t size)
+{
+ static unsigned char *uncomp_buf = NULL;
+ static size_t uncomp_size = 0;
+ static size_t uncomp_buf_used = 0;
+
+ if (!uncomp_buf)
+ {
+ if (jte_template_compression == JTE_TEMP_BZIP2)
+ uncomp_size = 900 * 1024;
+ else
+ uncomp_size = 1024 * 1024;
+ uncomp_buf = malloc(uncomp_size);
+ if (!uncomp_buf)
+ {
+#ifdef USE_LIBSCHILY
+ comerr("failed to allocate %d bytes for template compression buffer\n", uncomp_size);
+#else
+ fprintf(stderr, "failed to allocate %d bytes for template compression buffer\n", uncomp_size);
+ exit(1);
+#endif
+ }
+ }
+
+ if ((uncomp_buf_used + size) > uncomp_size)
+ {
+ flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
+ uncomp_buf_used = 0;
+ }
+
+ if (!size) /* Signal a flush before we start writing the DESC entry */
+ {
+ flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
+ return;
+ }
+
+ if (!uncomp_buf_used)
+ memset(uncomp_buf, 0, uncomp_size);
+
+ while (size > uncomp_size)
+ {
+ flush_compressed_chunk(buffer, uncomp_size);
+ buffer += uncomp_size;
+ size -= uncomp_size;
+ }
+ memcpy(&uncomp_buf[uncomp_buf_used], buffer, size);
+ uncomp_buf_used += size;
+}
+
+/* Loop through the list of DESC entries that we've built up and
+ append them to the template file */
+static void write_template_desc_entries(off_t image_len)
+{
+ entry_t *entry = entry_list;
+ off_t desc_len = 0;
+ unsigned char out_len[6];
+ jigdo_image_entry_t jimage;
+
+ desc_len = 16 /* DESC + length twice */
+ + (sizeof(jigdo_file_entry_t) * num_matches)
+ + (sizeof(jigdo_chunk_entry_t) * num_chunks)
+ + sizeof(jigdo_image_entry_t);
+
+ write_le48(desc_len, &out_len[0]);
+ write_compressed_chunk(NULL, 0);
+ template_fwrite("DESC", 4, 1, t_file);
+ template_fwrite(out_len, sizeof(out_len), 1, t_file);
+
+ while (entry)
+ {
+ switch (entry->entry_type)
+ {
+ case JTET_FILE_MATCH:
+ {
+ jigdo_file_entry_t jfile;
+ jfile.type = 6; /* Matched file */
+ write_le48(entry->data.file.file_length, &jfile.fileLen[0]);
+ write_le64(entry->data.file.rsyncsum, &jfile.fileRsync[0]);
+ memcpy(jfile.fileMD5, entry->data.file.md5, sizeof(jfile.fileMD5));
+ template_fwrite(&jfile, sizeof(jfile), 1, t_file);
+ break;
+ }
+ case JTET_NOMATCH:
+ {
+ jigdo_chunk_entry_t jchunk;
+ jchunk.type = 2; /* Raw data, compressed */
+ write_le48(entry->data.chunk.uncompressed_length, &jchunk.skipLen[0]);
+ template_fwrite(&jchunk, sizeof(jchunk), 1, t_file);
+ break;
+ }
+ }
+ entry = entry->next;
+ }
+
+ jimage.type = 5;
+ write_le48(image_len, &jimage.imageLen[0]);
+ checksum_copy(iso_context, CHECK_MD5, &jimage.imageMD5[0]);
+ write_le32(MIN_JIGDO_FILE_SIZE, &jimage.blockLen[0]);
+ template_fwrite(&jimage, sizeof(jimage), 1, t_file);
+ template_fwrite(out_len, sizeof(out_len), 1, t_file);
+}
+
+/* Dump a buffer in jigdo-style "base64" */
+static char *base64_dump(unsigned char *buf, size_t buf_size)
+{
+ const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+ int value = 0;
+ unsigned int i;
+ int bits = 0;
+ static char output_buffer[2048];
+ char *p = output_buffer;
+
+ memset(output_buffer, 0, sizeof(output_buffer));
+ if (buf_size >= (sizeof(output_buffer) * 6/8))
+ {
+ fprintf(stderr, "base64_dump: Buffer too small!\n");
+ exit(1);
+ }
+
+ for (i = 0; i < buf_size ; i++)
+ {
+ value = (value << 8) | buf[i];
+ bits += 2;
+ p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
+ if (bits >= 6) {
+ bits -= 6;
+ p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
+ }
+ }
+ if (bits > 0)
+ {
+ value <<= 6 - bits;
+ p += sprintf(p, "%c", b64_enc[value & 63U]);
+ }
+ return output_buffer;
+}
+
+/* Write the .jigdo file to match the .template we've just finished. */
+static void write_jigdo_file(void)
+{
+ unsigned char template_md5sum[16];
+ entry_t *entry = entry_list;
+ struct path_mapping *map = map_list;
+ int i = 0;
+ struct checksum_info *info = NULL;
+
+ checksum_final(template_context);
+ checksum_copy(template_context, CHECK_MD5, &template_md5sum[0]);
+
+ fprintf(j_file, "# JigsawDownload\n");
+ fprintf(j_file, "# See <http://atterer.org/jigdo/> for details about jigdo\n");
+ fprintf(j_file, "# See <http://www.einval.com/~steve/software/CD/JTE/> for details about JTE\n\n");
+
+ fprintf(j_file, "[Jigdo]\n");
+ fprintf(j_file, "Version=%s\n", JIGDO_TEMPLATE_VERSION);
+ fprintf(j_file, "Generator=%s/%d.%d\n\n", JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
+
+ fprintf(j_file, "[Image]\n");
+ fprintf(j_file, "Filename=%s\n", file_base_name(outfile));
+ fprintf(j_file, "Template=http://localhost/%s\n", jtemplate_out);
+
+ fprintf(j_file, "Template-MD5Sum=%s \n",
+ base64_dump(&template_md5sum[0], sizeof(template_md5sum)));
+
+ for (i = 0; i < NUM_CHECKSUMS; i++)
+ {
+ if (checksum_algo_tmpl & (1 << i))
+ {
+ info = checksum_information(i);
+ fprintf(j_file, "# Template Hex %sSum %s\n", info->name, checksum_hex(template_context, i));
+ }
+ }
+ fprintf(j_file, "# Template size %lld bytes\n", template_size);
+
+ for (i = 0; i < NUM_CHECKSUMS; i++)
+ {
+ if (checksum_algo_iso & (1 << i))
+ {
+ info = checksum_information(i);
+ fprintf(j_file, "# Image Hex %sSum %s\n", info->name, checksum_hex(iso_context, i));
+ }
+ }
+
+ fprintf(j_file, "# Image size %lld bytes\n\n", image_size);
+
+ fprintf(j_file, "[Parts]\n");
+ while (entry)
+ {
+ if (JTET_FILE_MATCH == entry->entry_type)
+ {
+ char *new_name = remap_filename(entry->data.file.filename);
+ fprintf(j_file, "%s=%s\n",
+ base64_dump(&entry->data.file.md5[0], sizeof(entry->data.file.md5)),
+ new_name);
+ free(new_name);
+ }
+ entry = entry->next;
+ }
+
+ fprintf(j_file, "\n[Servers]\n");
+ fflush(j_file);
+}
+
+/* Finish and flush state; for now:
+
+ 1. Dump the DESC blocks and the footer information in the jigdo template file
+ 2. Write the jigdo .jigdo file containing file pointers
+*/
+void write_jt_footer(void)
+{
+ /* Finish calculating the image's checksum */
+ checksum_final(iso_context);
+
+ /* And calculate the image size */
+ image_size = (unsigned long long)SECTOR_SIZE * last_extent_written;
+
+ write_template_desc_entries(image_size);
+
+ write_jigdo_file();
+}
+
+/* Add a raw data entry to the list of extents; no file to match */
+static void add_unmatched_entry(int uncompressed_length)
+{
+ entry_t *new_entry = NULL;
+
+ /* Can we extend a previous non-match entry? */
+ if (entry_last && (JTET_NOMATCH == entry_last->entry_type))
+ {
+ entry_last->data.chunk.uncompressed_length += uncompressed_length;
+ return;
+ }
+
+ new_entry = calloc(1, sizeof(entry_t));
+ new_entry->entry_type = JTET_NOMATCH;
+ new_entry->next = NULL;
+ new_entry->data.chunk.uncompressed_length = uncompressed_length;
+
+ /* Add to the end of the list */
+ if (NULL == entry_last)
+ {
+ entry_last = new_entry;
+ entry_list = new_entry;
+ }
+ else
+ {
+ entry_last->next = new_entry;
+ entry_last = new_entry;
+ }
+ num_chunks++;
+}
+
+/* Add a file match entry to the list of extents */
+static void add_file_entry(char *filename, off_t size, unsigned char *md5,
+ unsigned long long rsyncsum)
+{
+ entry_t *new_entry = NULL;
+
+ new_entry = calloc(1, sizeof(entry_t));
+ new_entry->entry_type = JTET_FILE_MATCH;
+ new_entry->next = NULL;
+ memcpy(new_entry->data.file.md5, md5, sizeof(new_entry->data.file.md5));
+ new_entry->data.file.file_length = size;
+ new_entry->data.file.rsyncsum = rsyncsum;
+ new_entry->data.file.filename = strdup(filename);
+
+ /* Add to the end of the list */
+ if (NULL == entry_last)
+ {
+ entry_last = new_entry;
+ entry_list = new_entry;
+ }
+ else
+ {
+ entry_last->next = new_entry;
+ entry_last = new_entry;
+ }
+ num_matches++;
+}
+
+/* Cope with an unmatched block in the .iso file:
+
+ 1. Write a compressed data chunk in the jigdo template file
+ 2. Add an entry in our list of unmatched chunks for later */
+void jtwrite(buffer, size, count, submode, islast)
+ void *buffer;
+ int size;
+ int count;
+ int submode;
+ BOOL islast;
+{
+#ifdef JTWRITE_DEBUG
+ if (count != 1 || (size % 2048) != 0)
+ error("Count: %d, size: %d\n", count, size);
+#endif
+
+ if (!jtemplate_out)
+ return;
+
+ /* Update the global image checksum */
+ checksum_update(iso_context, buffer, size * count);
+// mk_MD5Update(&iso_context, buffer, size*count);
+
+ /* Write a compressed version of the data to the template file,
+ and add a reference on the state list so we can write that
+ later. */
+ write_compressed_chunk(buffer, size*count);
+ add_unmatched_entry(size*count);
+}
+
+/* Cope with a file entry in the .iso file:
+
+ 1. Read the file for the image's md5 checksum
+ 2. Add an entry in our list of files to be written into the .jigdo later
+*/
+void write_jt_match_record(char *filename, char *mirror_name, int sector_size, off_t size, unsigned char md5[16])
+{
+ unsigned long long tmp_size = 0;
+ char buf[32768];
+ off_t remain = size;
+ FILE *infile = NULL;
+ int use = 0;
+ unsigned long long rsync64_sum = 0;
+ int first_block = 1;
+
+ memset(buf, 0, sizeof(buf));
+
+ if ((infile = fopen(filename, "rb")) == NULL) {
+#ifdef USE_LIBSCHILY
+ comerr("cannot open '%s'\n", filename);
+#else
+#ifndef HAVE_STRERROR
+ fprintf(stderr, "cannot open '%s': (%d)\n",
+ filename, errno);
+#else
+ fprintf(stderr, "cannot open '%s': %s\n",
+ filename, strerror(errno));
+#endif
+ exit(1);
+#endif
+ }
+
+ while (remain > 0)
+ {
+ use = remain;
+ if (remain > sizeof(buf))
+ use = sizeof(buf);
+ if (fread(buf, 1, use, infile) == 0)
+ {
+#ifdef USE_LIBSCHILY
+ comerr("cannot read from '%s'\n", filename);
+#else
+ fprintf(stderr, "cannot read from '%s'\n", filename);
+ exit(1);
+#endif
+ }
+ if (first_block)
+ rsync64_sum = rsync64(buf, MIN_JIGDO_FILE_SIZE);
+ checksum_update(iso_context, buf, use);
+// mk_MD5Update(&iso_context, buf, use);
+ remain -= use;
+ first_block = 0;
+ }
+
+ fclose(infile);
+
+ /* Update the image checksum with any necessary padding data */
+ if (size % sector_size)
+ {
+ int pad_size = sector_size - (size % sector_size);
+ memset(buf, 0, pad_size);
+ checksum_update(iso_context, buf, pad_size);
+// mk_MD5Update(&iso_context, buf, pad_size);
+ }
+
+ add_file_entry(mirror_name, size, &md5[0], rsync64_sum);
+ if (size % sector_size)
+ {
+ int pad_size = sector_size - (size % sector_size);
+ write_compressed_chunk(buf, pad_size);
+ add_unmatched_entry(pad_size);
+ }
+}