diff options
Diffstat (limited to 'genisoimage/jte.c')
-rw-r--r-- | genisoimage/jte.c | 1042 |
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); + } +} |