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