1 /*
2 * jte.c
3 *
4 * Copyright (c) 2004-2006 Steve McIntyre <steve@einval.com>
5 *
6 * Implementation of the Jigdo Template Engine - make jigdo files
7 * directly when making ISO images
8 *
9 * GNU GPL v2
10 */
11
12 #include <mconfig.h>
13 #include "genisoimage.h"
14 #include <timedefs.h>
15 #include <fctldefs.h>
16 #include <zlib.h>
17 #include <bzlib.h>
18 #include <regex.h>
19 #ifdef SORTING
20 #include "match.h"
21 #endif /* SORTING */
22 #include <errno.h>
23 #include <schily.h>
24 #ifdef DVD_VIDEO
25 #include "dvd_reader.h"
26 #include "dvd_file.h"
27 #include "ifo_read.h"
28 #include "endianconv.h"
29 #include "checksum.h"
30 #endif
31 #ifdef APPLE_HYB
32 #include <ctype.h>
33 #endif
34
35 #ifdef VMS
36 #include "vms.h"
37 #endif
38
39 /* Different types used in building our state list below */
40 #define JTET_FILE_MATCH 1
41 #define JTET_NOMATCH 2
42
43 #define JTE_VER_MAJOR 0x0001
44 #define JTE_VER_MINOR 0x0013
45 #define JTE_NAME "JTE"
46 #define JTE_COMMENT "JTE at http://www.einval.com/~steve/software/JTE/ ; jigdo at http://atterer.org/jigdo/"
47
48 #define JIGDO_TEMPLATE_VERSION "1.1"
49
50 /*
51 Simple list to hold the results of -jigdo-exclude and
52 -jigdo-force-match command line options. Seems easiest to do this
53 using regexps.
54 */
55 struct path_match
56 {
57 regex_t match_pattern;
58 char *match_rule;
59 struct path_match *next;
60 };
61
62 /* List of mappings e.g. Debian=/mirror/debian */
63 struct path_mapping
64 {
65 char *from;
66 char *to;
67 struct path_mapping *next;
68 };
69
70 FILE *jtjigdo = NULL; /* File handle used throughout for the jigdo file */
71 FILE *jttemplate = NULL; /* File handle used throughout for the template file */
72 char *jjigdo_out = NULL; /* Output name for jigdo .jigdo file; NULL means don't do it */
73 char *jtemplate_out = NULL; /* Output name for jigdo template file; NULL means don't do it */
74 char *jmd5_list = NULL; /* Name of file to use for MD5 checking */
75 int jte_min_size = MIN_JIGDO_FILE_SIZE;
76 jtc_t jte_template_compression = JTE_TEMP_GZIP;
77 struct path_match *exclude_list = NULL;
78 struct path_match *include_list = NULL;
79 struct path_mapping *map_list = NULL;
80 unsigned long long template_size = 0;
81 unsigned long long image_size = 0;
82 int checksum_algo_iso = (CHECK_MD5_USED | \
83 CHECK_SHA1_USED | \
84 CHECK_SHA256_USED | \
85 CHECK_SHA512_USED);
86 int checksum_algo_tmpl = CHECK_MD5_USED;
87
88 static checksum_context_t *iso_context = NULL;
89 static checksum_context_t *template_context = NULL;
90
91 /* List of files that we've seen, ready to write into the template and
92 jigdo files */
93 typedef struct _file_entry
94 {
95 unsigned char md5[16];
96 off_t file_length;
97 unsigned long long rsyncsum;
98 char *filename;
99 } file_entry_t;
100
101 typedef struct _unmatched_entry
102 {
103 off_t uncompressed_length;
104 } unmatched_entry_t;
105
106 typedef struct _entry
107 {
108 int entry_type; /* JTET_TYPE as above */
109 struct _entry *next;
110 union
111 {
112 file_entry_t file;
113 unmatched_entry_t chunk;
114 } data;
115 } entry_t;
116
117 typedef struct _jigdo_file_entry
118 {
119 unsigned char type;
120 unsigned char fileLen[6];
121 unsigned char fileRsync[8];
122 unsigned char fileMD5[16];
123 } jigdo_file_entry_t;
124
125 typedef struct _jigdo_chunk_entry
126 {
127 unsigned char type;
128 unsigned char skipLen[6];
129 } jigdo_chunk_entry_t;
130
131 typedef struct _jigdo_image_entry
132 {
133 unsigned char type;
134 unsigned char imageLen[6];
135 unsigned char imageMD5[16];
136 unsigned char blockLen[4];
137 } jigdo_image_entry_t;
138
139 typedef struct _md5_list_entry
140 {
141 struct _md5_list_entry *next;
142 unsigned char MD5[16];
143 unsigned long long size;
144 char *filename;
145 } md5_list_entry_t;
146
147 entry_t *entry_list = NULL;
148 entry_t *entry_last = NULL;
149 FILE *t_file = NULL;
150 FILE *j_file = NULL;
151 int num_matches = 0;
152 int num_chunks = 0;
153 md5_list_entry_t *md5_list = NULL;
154 md5_list_entry_t *md5_last = NULL;
155
156 /* Grab the file component from a full path */
file_base_name(char * path)157 static char *file_base_name(char *path)
158 {
159 char *endptr = path;
160 char *ptr = path;
161
162 while (*ptr != '\0')
163 {
164 if ('/' == *ptr)
165 endptr = ++ptr;
166 else
167 ++ptr;
168 }
169 return endptr;
170 }
171
172 /* Build the list of exclusion regexps */
jte_add_exclude(char * pattern)173 extern int jte_add_exclude(char *pattern)
174 {
175 struct path_match *new = NULL;
176
177 new = malloc(sizeof *new);
178 if (!new)
179 return ENOMEM;
180
181 regcomp(&new->match_pattern, pattern, REG_NEWLINE);
182 new->match_rule = pattern;
183
184 /* Order on the exclude list doesn't matter! */
185 new->next = exclude_list;
186
187 exclude_list = new;
188 return 0;
189 }
190
191 /* Check if the file should be excluded because of a filename match. 1
192 means exclude, 0 means not */
check_exclude_by_name(char * filename,char ** matched)193 static int check_exclude_by_name(char *filename, char **matched)
194 {
195 struct path_match *ptr = exclude_list;
196 regmatch_t pmatch[1];
197 int i = 0;
198
199 while (ptr)
200 {
201 if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
202 {
203 *matched = ptr->match_rule;
204 return 1;
205 }
206 ptr = ptr->next;
207 }
208
209 /* Not matched, so return 0 */
210 return 0;
211 }
212
213 /* Build the list of required inclusion regexps */
jte_add_include(char * pattern)214 extern int jte_add_include(char *pattern)
215 {
216 struct path_match *new = NULL;
217
218 new = malloc(sizeof *new);
219 if (!new)
220 return ENOMEM;
221
222 regcomp(&new->match_pattern, pattern, REG_NEWLINE);
223 new->match_rule = pattern;
224
225 /* Order on the include list doesn't matter! */
226 new->next = include_list;
227
228 include_list = new;
229 return 0;
230 }
231
232 /* Check if a file has to be MD5-matched to be valid. If we get called
233 here, we've failed to match any of the MD5 entries we were
234 given. If the path to the filename matches one of the paths in our
235 list, clearly it must have been corrupted. Abort with an error. */
check_md5_file_match(char * filename)236 static void check_md5_file_match(char *filename)
237 {
238 struct path_match *ptr = include_list;
239 regmatch_t pmatch[1];
240 int i = 0;
241
242 while (ptr)
243 {
244 if (!regexec(&ptr->match_pattern, filename, 1, pmatch, 0))
245 {
246 #ifdef USE_LIBSCHILY
247 comerr("File %s should have matched an MD5 entry, but didn't! (Rule '%s')\n", filename, ptr->match_rule);
248 #else
249 fprintf(stderr, "File %s should have matched an MD5 entry, but didn't! (Rule '%s')\n", filename, ptr->match_rule);
250 exit(1);
251 #endif
252 }
253 ptr = ptr->next;
254 }
255 }
256
257 /* Should we list a file separately in the jigdo output, or should we
258 just dump it into the template file as binary data? Three things
259 cases to look for here:
260
261 1. Small files are better simply folded in, as they take less space that way.
262
263 2. Files in /doc (for example) may change in the archive all the
264 time and it's better to not have to fetch snapshot copies if we
265 can avoid it.
266
267 3. Files living in specified paths *must* match an entry in the
268 md5-list, or they must have been corrupted. If we find a corrupt
269 file, bail out with an error.
270
271 */
list_file_in_jigdo(char * filename,off_t size,char ** realname,unsigned char md5[16])272 extern int list_file_in_jigdo(char *filename, off_t size, char **realname, unsigned char md5[16])
273 {
274 char *matched_rule;
275 md5_list_entry_t *entry = md5_list;
276 int md5sum_done = 0;
277
278 if (!jtemplate_out)
279 return 0;
280
281 memset(md5, 0, sizeof(md5));
282
283 /* Cheaper to check file size first */
284 if (size < jte_min_size)
285 {
286 if (verbose > 1)
287 fprintf(stderr, "Jigdo-ignoring file %s; it's too small\n", filename);
288 return 0;
289 }
290
291 /* Now check the excluded list by name */
292 if (check_exclude_by_name(filename, &matched_rule))
293 {
294 if (verbose > 1)
295 fprintf(stderr, "Jigdo-ignoring file %s; it's covered in the exclude list by \"%s\"\n", filename, matched_rule);
296 return 0;
297 }
298
299 /* Check to see if the file is in our md5 list. Check three things:
300
301 1. the size
302 2. the filename
303 3. (only if the first 2 match) the md5sum
304
305 If we get a match for all three, include the file and return
306 the full path to the file that we have gleaned from the mirror.
307 */
308
309 while (entry)
310 {
311 if (size == entry->size)
312 {
313 if (!strcmp(file_base_name(filename), file_base_name(entry->filename)))
314 {
315 if (!md5sum_done)
316 {
317 calculate_md5sum(filename, size, md5);
318 md5sum_done = 1;
319 }
320 if (!memcmp(md5, entry->MD5, sizeof(entry->MD5)))
321 {
322 *realname = entry->filename;
323 return 1;
324 }
325 }
326 }
327 entry = entry->next;
328 }
329
330 /* We haven't found an entry in our MD5 list to match this
331 * file. If we should have done, complain and bail out. */
332 check_md5_file_match(filename);
333 return 0;
334 }
335
336 /* Add a mapping of pathnames (e.g. Debian=/mirror/debian). We should
337 be passed TO=FROM here */
jte_add_mapping(char * arg)338 extern int jte_add_mapping(char *arg)
339 {
340 int error = 0;
341 struct path_mapping *new = NULL;
342 struct path_mapping *entry = NULL;
343 char *p = arg;
344 char *from = NULL;
345 char *to = NULL;
346
347 /* Find the "=" in the string passed. Set it to NULL and we can
348 use the string in-place */
349 while (*p)
350 {
351 if ('=' == *p)
352 {
353 *p = 0;
354 p++;
355 to = arg;
356 from = p;
357 }
358 p++;
359 }
360 if (!from || !strlen(from) || !to || !strlen(to))
361 return EINVAL;
362
363 new = malloc(sizeof(*new));
364 if (!new)
365 return ENOMEM;
366
367 new->from = from;
368 new->to = to;
369 new->next = NULL;
370
371 if (verbose > 0)
372 fprintf(stderr, "Adding mapping from %s to %s for the jigdo file\n", from, to);
373 if (!map_list)
374 map_list = new;
375 else
376 {
377 /* Order is important; add to the end of the list */
378 entry = map_list;
379 while (NULL != entry->next)
380 entry = entry->next;
381 entry->next = new;
382 }
383 return 0;
384 }
385
386 /* Check if the filename should be remapped; if so map it, otherwise
387 return the original name. */
remap_filename(char * filename)388 static char *remap_filename(char *filename)
389 {
390 char *new_name = filename;
391 struct path_mapping *entry = map_list;
392
393 while (entry)
394 {
395 if (!strncmp(filename, entry->from, strlen(entry->from)))
396 {
397 new_name = calloc(1, 2 + strlen(filename) + strlen(entry->to) - strlen(entry->from));
398 if (!new_name)
399 {
400 fprintf(stderr, "Failed to malloc new filename; abort!\n");
401 exit(1);
402 }
403 sprintf(new_name, "%s:%s", entry->to, &filename[strlen(entry->from)]);
404 return new_name;
405 }
406 entry = entry->next;
407 }
408
409 /* No mapping in effect */
410 return strdup(filename);
411 }
412
413 /* Write data to the template file and update the MD5 sum */
template_fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream)414 static size_t template_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
415 {
416 checksum_update(template_context, ptr, size * nmemb);
417 template_size += (unsigned long long)size * nmemb;
418 return fwrite(ptr, size, nmemb, stream);
419 }
420
421 /* Create a new template file and initialise it */
write_template_header()422 static void write_template_header()
423 {
424 char buf[2048];
425 int i = 0;
426 char *p = buf;
427
428 memset(buf, 0, sizeof(buf));
429
430 template_context = checksum_init_context(checksum_algo_tmpl, "template");
431 if (!template_context)
432 {
433 #ifdef USE_LIBSCHILY
434 comerr("cannot allocate template checksum contexts\n");
435 #else
436 fprintf(stderr, "cannot allocate template checksum contexts\n");
437 exit(1);
438 #endif
439 }
440
441 i += sprintf(p, "JigsawDownload template %s %s/%d.%d \r\n",
442 JIGDO_TEMPLATE_VERSION, JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
443 p = &buf[i];
444
445 i += sprintf(p, "%s \r\n", JTE_COMMENT);
446 p = &buf[i];
447
448 i += sprintf(p, "\r\n");
449 template_fwrite(buf, i, 1, t_file);
450 }
451
452 /* Read the MD5 list and build a list in memory for us to use later */
add_md5_entry(unsigned char * md5,unsigned long long size,char * filename)453 static void add_md5_entry(unsigned char *md5, unsigned long long size, char *filename)
454 {
455 int error = 0;
456 md5_list_entry_t *new = NULL;
457
458 new = calloc(1, sizeof(md5_list_entry_t));
459 memcpy(new->MD5, md5, sizeof(new->MD5));
460 new->size = size;
461 new->filename = strdup(filename);
462
463 /* Add to the end of the list */
464 if (NULL == md5_last)
465 {
466 md5_last = new;
467 md5_list = new;
468 }
469 else
470 {
471 md5_last->next = new;
472 md5_last = new;
473 }
474 }
475
476 /* Parse a 12-digit decimal number */
parse_number(unsigned char in[12])477 static unsigned long long parse_number(unsigned char in[12])
478 {
479 unsigned long long size = 0;
480 int i = 0;
481
482 for (i = 0; i < 12; i++)
483 {
484 size *= 10;
485 if (isdigit(in[i]))
486 size += (in[i] - '0');
487 }
488
489 return size;
490 }
491
492 /* Read the MD5 list and build a list in memory for us to use later
493 MD5 list format:
494
495 <---MD5---> <--Size--> <--Filename-->
496 32 12 remaining
497 */
parse_md5_list(void)498 static void parse_md5_list(void)
499 {
500 FILE *md5_file = NULL;
501 unsigned char buf[1024];
502 unsigned char md5[16];
503 char *filename = NULL;
504 unsigned char *numbuf = NULL;
505 int num_files = 0;
506 unsigned long long size = 0;
507
508 md5_file = fopen(jmd5_list, "rb");
509 if (!md5_file)
510 {
511 #ifdef USE_LIBSCHILY
512 comerr("cannot read from MD5 list file '%s'\n", jmd5_list);
513 #else
514 fprintf(stderr, "cannot read from MD5 list file '%s'\n", jmd5_list);
515 exit(1);
516 #endif
517 }
518
519 memset(buf, 0, sizeof(buf));
520 while (fgets((char *)buf, sizeof(buf), md5_file))
521 {
522 numbuf = &buf[34];
523 filename = (char *)&buf[48];
524 /* Lose the trailing \n from the fgets() call */
525 if (buf[strlen((char *)buf)-1] == '\n')
526 buf[strlen((char *)buf)-1] = 0;
527
528 if (mk_MD5Parse(buf, md5))
529 {
530 #ifdef USE_LIBSCHILY
531 comerr("cannot parse MD5 file '%s'\n", jmd5_list);
532 #else
533 fprintf(stderr, "cannot parse MD5 file '%s'\n", jmd5_list);
534 exit(1);
535 #endif
536 }
537 size = parse_number(numbuf);
538 add_md5_entry(md5, size, filename);
539 memset(buf, 0, sizeof(buf));
540 num_files++;
541 }
542 if (verbose > 0)
543 fprintf(stderr, "parse_md5_list: added MD5 checksums for %d files\n", num_files);
544 fclose(md5_file);
545 }
546
547 /* Initialise state and start the jigdo template file */
write_jt_header(FILE * template_file,FILE * jigdo_file)548 void write_jt_header(FILE *template_file, FILE *jigdo_file)
549 {
550 t_file = template_file;
551 j_file = jigdo_file;
552
553 /* Start checksum work for the image */
554 iso_context = checksum_init_context(checksum_algo_iso, "iso");
555 if (!iso_context)
556 {
557 #ifdef USE_LIBSCHILY
558 comerr("cannot allocate iso checksum contexts\n");
559 #else
560 fprintf(stderr, "cannot allocate iso checksum contexts\n");
561 exit(1);
562 #endif
563 }
564
565 /* Start the template file */
566 write_template_header();
567
568 /* Load up the MD5 list if we've been given one */
569 if (jmd5_list)
570 parse_md5_list();
571 }
572
573 /* Compress and flush out a buffer full of template data */
flush_gzip_chunk(void * buffer,off_t size)574 static void flush_gzip_chunk(void *buffer, off_t size)
575 {
576 z_stream c_stream; /* compression stream */
577 unsigned char comp_size_out[6];
578 unsigned char uncomp_size_out[6];
579 off_t compressed_size_out = 0;
580 int err = 0;
581 unsigned char *comp_buf = NULL;
582
583 c_stream.zalloc = NULL;
584 c_stream.zfree = NULL;
585 c_stream.opaque = NULL;
586
587 err = deflateInit(&c_stream, Z_BEST_COMPRESSION);
588 comp_buf = malloc(2 * size); /* Worst case */
589 c_stream.next_out = comp_buf;
590 c_stream.avail_out = 2 * size;
591 c_stream.next_in = buffer;
592 c_stream.avail_in = size;
593
594 err = deflate(&c_stream, Z_NO_FLUSH);
595 err = deflate(&c_stream, Z_FINISH);
596
597 compressed_size_out = c_stream.total_out + 16;
598 err = deflateEnd(&c_stream);
599
600 template_fwrite("DATA", 4, 1, t_file);
601
602 write_le48(compressed_size_out, &comp_size_out[0]);
603 template_fwrite(comp_size_out, sizeof(comp_size_out), 1, t_file);
604
605 write_le48(size, &uncomp_size_out[0]);
606 template_fwrite(uncomp_size_out, sizeof(uncomp_size_out), 1, t_file);
607
608 template_fwrite(comp_buf, c_stream.total_out, 1, t_file);
609 free(comp_buf);
610 }
611
612 /* Compress and flush out a buffer full of template data */
flush_bz2_chunk(void * buffer,off_t size)613 static void flush_bz2_chunk(void *buffer, off_t size)
614 {
615 bz_stream c_stream; /* compression stream */
616 unsigned char comp_size_out[6];
617 unsigned char uncomp_size_out[6];
618 off_t compressed_size_out = 0;
619 int err = 0;
620 unsigned char *comp_buf = NULL;
621
622 c_stream.bzalloc = NULL;
623 c_stream.bzfree = NULL;
624 c_stream.opaque = NULL;
625
626 err = BZ2_bzCompressInit(&c_stream, 9, 0, 0);
627 comp_buf = malloc(2 * size); /* Worst case */
628 c_stream.next_out = comp_buf;
629 c_stream.avail_out = 2 * size;
630 c_stream.next_in = buffer;
631 c_stream.avail_in = size;
632
633 err = BZ2_bzCompress(&c_stream, BZ_FINISH);
634
635 compressed_size_out = c_stream.total_out_lo32 + 16;
636 err = BZ2_bzCompressEnd(&c_stream);
637
638 template_fwrite("BZIP", 4, 1, t_file);
639
640 write_le48(compressed_size_out, &comp_size_out[0]);
641 template_fwrite(comp_size_out, sizeof(comp_size_out), 1, t_file);
642
643 write_le48(size, &uncomp_size_out[0]);
644 template_fwrite(uncomp_size_out, sizeof(uncomp_size_out), 1, t_file);
645
646 template_fwrite(comp_buf, c_stream.total_out_lo32, 1, t_file);
647 free(comp_buf);
648 }
649
flush_compressed_chunk(void * buffer,off_t size)650 static void flush_compressed_chunk(void *buffer, off_t size)
651 {
652 if (jte_template_compression == JTE_TEMP_BZIP2)
653 flush_bz2_chunk(buffer, size);
654 else
655 flush_gzip_chunk(buffer, size);
656 }
657
658 /* Append to an existing data buffer, and compress/flush it if
659 necessary */
write_compressed_chunk(unsigned char * buffer,size_t size)660 static void write_compressed_chunk(unsigned char *buffer, size_t size)
661 {
662 static unsigned char *uncomp_buf = NULL;
663 static size_t uncomp_size = 0;
664 static size_t uncomp_buf_used = 0;
665
666 if (!uncomp_buf)
667 {
668 if (jte_template_compression == JTE_TEMP_BZIP2)
669 uncomp_size = 900 * 1024;
670 else
671 uncomp_size = 1024 * 1024;
672 uncomp_buf = malloc(uncomp_size);
673 if (!uncomp_buf)
674 {
675 #ifdef USE_LIBSCHILY
676 comerr("failed to allocate %d bytes for template compression buffer\n", uncomp_size);
677 #else
678 fprintf(stderr, "failed to allocate %d bytes for template compression buffer\n", uncomp_size);
679 exit(1);
680 #endif
681 }
682 }
683
684 if ((uncomp_buf_used + size) > uncomp_size)
685 {
686 flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
687 uncomp_buf_used = 0;
688 }
689
690 if (!size) /* Signal a flush before we start writing the DESC entry */
691 {
692 flush_compressed_chunk(uncomp_buf, uncomp_buf_used);
693 return;
694 }
695
696 if (!uncomp_buf_used)
697 memset(uncomp_buf, 0, uncomp_size);
698
699 while (size > uncomp_size)
700 {
701 flush_compressed_chunk(buffer, uncomp_size);
702 buffer += uncomp_size;
703 size -= uncomp_size;
704 }
705 memcpy(&uncomp_buf[uncomp_buf_used], buffer, size);
706 uncomp_buf_used += size;
707 }
708
709 /* Loop through the list of DESC entries that we've built up and
710 append them to the template file */
write_template_desc_entries(off_t image_len)711 static void write_template_desc_entries(off_t image_len)
712 {
713 entry_t *entry = entry_list;
714 off_t desc_len = 0;
715 unsigned char out_len[6];
716 jigdo_image_entry_t jimage;
717
718 desc_len = 16 /* DESC + length twice */
719 + (sizeof(jigdo_file_entry_t) * num_matches)
720 + (sizeof(jigdo_chunk_entry_t) * num_chunks)
721 + sizeof(jigdo_image_entry_t);
722
723 write_le48(desc_len, &out_len[0]);
724 write_compressed_chunk(NULL, 0);
725 template_fwrite("DESC", 4, 1, t_file);
726 template_fwrite(out_len, sizeof(out_len), 1, t_file);
727
728 while (entry)
729 {
730 switch (entry->entry_type)
731 {
732 case JTET_FILE_MATCH:
733 {
734 jigdo_file_entry_t jfile;
735 jfile.type = 6; /* Matched file */
736 write_le48(entry->data.file.file_length, &jfile.fileLen[0]);
737 write_le64(entry->data.file.rsyncsum, &jfile.fileRsync[0]);
738 memcpy(jfile.fileMD5, entry->data.file.md5, sizeof(jfile.fileMD5));
739 template_fwrite(&jfile, sizeof(jfile), 1, t_file);
740 break;
741 }
742 case JTET_NOMATCH:
743 {
744 jigdo_chunk_entry_t jchunk;
745 jchunk.type = 2; /* Raw data, compressed */
746 write_le48(entry->data.chunk.uncompressed_length, &jchunk.skipLen[0]);
747 template_fwrite(&jchunk, sizeof(jchunk), 1, t_file);
748 break;
749 }
750 }
751 entry = entry->next;
752 }
753
754 jimage.type = 5;
755 write_le48(image_len, &jimage.imageLen[0]);
756 checksum_copy(iso_context, CHECK_MD5, &jimage.imageMD5[0]);
757 write_le32(MIN_JIGDO_FILE_SIZE, &jimage.blockLen[0]);
758 template_fwrite(&jimage, sizeof(jimage), 1, t_file);
759 template_fwrite(out_len, sizeof(out_len), 1, t_file);
760 }
761
762 /* Dump a buffer in jigdo-style "base64" */
base64_dump(unsigned char * buf,size_t buf_size)763 static char *base64_dump(unsigned char *buf, size_t buf_size)
764 {
765 const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
766 int value = 0;
767 unsigned int i;
768 int bits = 0;
769 static char output_buffer[2048];
770 char *p = output_buffer;
771
772 memset(output_buffer, 0, sizeof(output_buffer));
773 if (buf_size >= (sizeof(output_buffer) * 6/8))
774 {
775 fprintf(stderr, "base64_dump: Buffer too small!\n");
776 exit(1);
777 }
778
779 for (i = 0; i < buf_size ; i++)
780 {
781 value = (value << 8) | buf[i];
782 bits += 2;
783 p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
784 if (bits >= 6) {
785 bits -= 6;
786 p += sprintf(p, "%c", b64_enc[(value >> bits) & 63U]);
787 }
788 }
789 if (bits > 0)
790 {
791 value <<= 6 - bits;
792 p += sprintf(p, "%c", b64_enc[value & 63U]);
793 }
794 return output_buffer;
795 }
796
797 /* Write the .jigdo file to match the .template we've just finished. */
write_jigdo_file(void)798 static void write_jigdo_file(void)
799 {
800 unsigned char template_md5sum[16];
801 entry_t *entry = entry_list;
802 struct path_mapping *map = map_list;
803 int i = 0;
804 struct checksum_info *info = NULL;
805
806 checksum_final(template_context);
807 checksum_copy(template_context, CHECK_MD5, &template_md5sum[0]);
808
809 fprintf(j_file, "# JigsawDownload\n");
810 fprintf(j_file, "# See <http://atterer.org/jigdo/> for details about jigdo\n");
811 fprintf(j_file, "# See <http://www.einval.com/~steve/software/CD/JTE/> for details about JTE\n\n");
812
813 fprintf(j_file, "[Jigdo]\n");
814 fprintf(j_file, "Version=%s\n", JIGDO_TEMPLATE_VERSION);
815 fprintf(j_file, "Generator=%s/%d.%d\n\n", JTE_NAME, JTE_VER_MAJOR, JTE_VER_MINOR);
816
817 fprintf(j_file, "[Image]\n");
818 fprintf(j_file, "Filename=%s\n", file_base_name(outfile));
819 fprintf(j_file, "Template=http://localhost/%s\n", jtemplate_out);
820
821 fprintf(j_file, "Template-MD5Sum=%s \n",
822 base64_dump(&template_md5sum[0], sizeof(template_md5sum)));
823
824 for (i = 0; i < NUM_CHECKSUMS; i++)
825 {
826 if (checksum_algo_tmpl & (1 << i))
827 {
828 info = checksum_information(i);
829 fprintf(j_file, "# Template Hex %sSum %s\n", info->name, checksum_hex(template_context, i));
830 }
831 }
832 fprintf(j_file, "# Template size %lld bytes\n", template_size);
833
834 for (i = 0; i < NUM_CHECKSUMS; i++)
835 {
836 if (checksum_algo_iso & (1 << i))
837 {
838 info = checksum_information(i);
839 fprintf(j_file, "# Image Hex %sSum %s\n", info->name, checksum_hex(iso_context, i));
840 }
841 }
842
843 fprintf(j_file, "# Image size %lld bytes\n\n", image_size);
844
845 fprintf(j_file, "[Parts]\n");
846 while (entry)
847 {
848 if (JTET_FILE_MATCH == entry->entry_type)
849 {
850 char *new_name = remap_filename(entry->data.file.filename);
851 fprintf(j_file, "%s=%s\n",
852 base64_dump(&entry->data.file.md5[0], sizeof(entry->data.file.md5)),
853 new_name);
854 free(new_name);
855 }
856 entry = entry->next;
857 }
858
859 fprintf(j_file, "\n[Servers]\n");
860 fflush(j_file);
861 }
862
863 /* Finish and flush state; for now:
864
865 1. Dump the DESC blocks and the footer information in the jigdo template file
866 2. Write the jigdo .jigdo file containing file pointers
867 */
write_jt_footer(void)868 void write_jt_footer(void)
869 {
870 /* Finish calculating the image's checksum */
871 checksum_final(iso_context);
872
873 /* And calculate the image size */
874 image_size = (unsigned long long)SECTOR_SIZE * last_extent_written;
875
876 write_template_desc_entries(image_size);
877
878 write_jigdo_file();
879 }
880
881 /* Add a raw data entry to the list of extents; no file to match */
add_unmatched_entry(int uncompressed_length)882 static void add_unmatched_entry(int uncompressed_length)
883 {
884 entry_t *new_entry = NULL;
885
886 /* Can we extend a previous non-match entry? */
887 if (entry_last && (JTET_NOMATCH == entry_last->entry_type))
888 {
889 entry_last->data.chunk.uncompressed_length += uncompressed_length;
890 return;
891 }
892
893 new_entry = calloc(1, sizeof(entry_t));
894 new_entry->entry_type = JTET_NOMATCH;
895 new_entry->next = NULL;
896 new_entry->data.chunk.uncompressed_length = uncompressed_length;
897
898 /* Add to the end of the list */
899 if (NULL == entry_last)
900 {
901 entry_last = new_entry;
902 entry_list = new_entry;
903 }
904 else
905 {
906 entry_last->next = new_entry;
907 entry_last = new_entry;
908 }
909 num_chunks++;
910 }
911
912 /* Add a file match entry to the list of extents */
add_file_entry(char * filename,off_t size,unsigned char * md5,unsigned long long rsyncsum)913 static void add_file_entry(char *filename, off_t size, unsigned char *md5,
914 unsigned long long rsyncsum)
915 {
916 entry_t *new_entry = NULL;
917
918 new_entry = calloc(1, sizeof(entry_t));
919 new_entry->entry_type = JTET_FILE_MATCH;
920 new_entry->next = NULL;
921 memcpy(new_entry->data.file.md5, md5, sizeof(new_entry->data.file.md5));
922 new_entry->data.file.file_length = size;
923 new_entry->data.file.rsyncsum = rsyncsum;
924 new_entry->data.file.filename = strdup(filename);
925
926 /* Add to the end of the list */
927 if (NULL == entry_last)
928 {
929 entry_last = new_entry;
930 entry_list = new_entry;
931 }
932 else
933 {
934 entry_last->next = new_entry;
935 entry_last = new_entry;
936 }
937 num_matches++;
938 }
939
940 /* Cope with an unmatched block in the .iso file:
941
942 1. Write a compressed data chunk in the jigdo template file
943 2. Add an entry in our list of unmatched chunks for later */
jtwrite(buffer,size,count,submode,islast)944 void jtwrite(buffer, size, count, submode, islast)
945 void *buffer;
946 int size;
947 int count;
948 int submode;
949 BOOL islast;
950 {
951 #ifdef JTWRITE_DEBUG
952 if (count != 1 || (size % 2048) != 0)
953 error("Count: %d, size: %d\n", count, size);
954 #endif
955
956 if (!jtemplate_out)
957 return;
958
959 /* Update the global image checksum */
960 checksum_update(iso_context, buffer, size * count);
961 // mk_MD5Update(&iso_context, buffer, size*count);
962
963 /* Write a compressed version of the data to the template file,
964 and add a reference on the state list so we can write that
965 later. */
966 write_compressed_chunk(buffer, size*count);
967 add_unmatched_entry(size*count);
968 }
969
970 /* Cope with a file entry in the .iso file:
971
972 1. Read the file for the image's md5 checksum
973 2. Add an entry in our list of files to be written into the .jigdo later
974 */
write_jt_match_record(char * filename,char * mirror_name,int sector_size,off_t size,unsigned char md5[16])975 void write_jt_match_record(char *filename, char *mirror_name, int sector_size, off_t size, unsigned char md5[16])
976 {
977 unsigned long long tmp_size = 0;
978 char buf[32768];
979 off_t remain = size;
980 FILE *infile = NULL;
981 int use = 0;
982 unsigned long long rsync64_sum = 0;
983 int first_block = 1;
984
985 memset(buf, 0, sizeof(buf));
986
987 if ((infile = fopen(filename, "rb")) == NULL) {
988 #ifdef USE_LIBSCHILY
989 comerr("cannot open '%s'\n", filename);
990 #else
991 #ifndef HAVE_STRERROR
992 fprintf(stderr, "cannot open '%s': (%d)\n",
993 filename, errno);
994 #else
995 fprintf(stderr, "cannot open '%s': %s\n",
996 filename, strerror(errno));
997 #endif
998 exit(1);
999 #endif
1000 }
1001
1002 while (remain > 0)
1003 {
1004 use = remain;
1005 if (remain > sizeof(buf))
1006 use = sizeof(buf);
1007 if (fread(buf, 1, use, infile) == 0)
1008 {
1009 #ifdef USE_LIBSCHILY
1010 comerr("cannot read from '%s'\n", filename);
1011 #else
1012 fprintf(stderr, "cannot read from '%s'\n", filename);
1013 exit(1);
1014 #endif
1015 }
1016 if (first_block)
1017 rsync64_sum = rsync64(buf, MIN_JIGDO_FILE_SIZE);
1018 checksum_update(iso_context, buf, use);
1019 // mk_MD5Update(&iso_context, buf, use);
1020 remain -= use;
1021 first_block = 0;
1022 }
1023
1024 fclose(infile);
1025
1026 /* Update the image checksum with any necessary padding data */
1027 if (size % sector_size)
1028 {
1029 int pad_size = sector_size - (size % sector_size);
1030 memset(buf, 0, pad_size);
1031 checksum_update(iso_context, buf, pad_size);
1032 // mk_MD5Update(&iso_context, buf, pad_size);
1033 }
1034
1035 add_file_entry(mirror_name, size, &md5[0], rsync64_sum);
1036 if (size % sector_size)
1037 {
1038 int pad_size = sector_size - (size % sector_size);
1039 write_compressed_chunk(buf, pad_size);
1040 add_unmatched_entry(pad_size);
1041 }
1042 }
1043