17 typedef unsigned long long UINT64;
18 typedef unsigned long UINT32;
20 #define BUF_SIZE 65536
23 #define MIN(x,y) ( ((x) < (y)) ? (x) : (y))
28 unsigned long long start_offset = 0;
29 unsigned long long end_offset = 0;
42 typedef struct match_list_
44 struct match_list_ *next;
49 match_list_t *match_list_head = NULL;
50 match_list_t *match_list_tail = NULL;
52 typedef struct jigdo_list_
54 struct jigdo_list_ *next;
60 jigdo_list_t *jigdo_list_head = NULL;
61 jigdo_list_t *jigdo_list_tail = NULL;
67 off_t offset_in_curr_buf;
71 static int add_match_entry(char *match)
73 match_list_t *entry = NULL;
74 char *mirror_path = NULL;
77 /* Split "Foo=/mirror/foo" into its components */
92 fprintf(logfile, "Could not parse malformed match entry \"%s\"\n", match);
96 entry = calloc(1, sizeof(*entry));
100 fprintf(logfile, "Adding match entry %s:%s\n", match, mirror_path);
102 entry->match = match;
103 entry->mirror_path = mirror_path;
105 if (!match_list_head)
107 match_list_head = entry;
108 match_list_tail = entry;
112 match_list_tail->next = entry;
113 match_list_tail = entry;
119 static int file_exists(char *path, off_t *size)
124 error = stat(path, &sb);
125 if (!error && S_ISREG(sb.st_mode))
135 static char *base64_dump(unsigned char *buf, size_t buf_size)
137 const char *b64_enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
141 char *out = calloc(1, 30);
142 unsigned int out_pos = 0;
147 for (i = 0; i < buf_size ; i++)
149 value = (value << 8) | buf[i];
151 out[out_pos++] = b64_enc[(value >> bits) & 63U];
154 out[out_pos++] = b64_enc[(value >> bits) & 63U];
160 out[out_pos++] = b64_enc[(value >> bits) & 63U];
165 static int find_file_in_mirror(char *jigdo_entry, char **mirror_path, char **md5sum, off_t *file_size)
167 match_list_t *entry = match_list_head;
169 char *jigdo_name = NULL;
171 char *ptr = jigdo_entry;
173 *md5sum = jigdo_entry;
175 /* Grab out the component strings from the entry in the jigdo file */
184 else if (':' == *ptr)
190 else if ('\n' == *ptr)
196 if (NULL == match || NULL == jigdo_name)
198 fprintf(logfile, "Could not parse malformed jigdo entry \"%s\"\n", jigdo_entry);
204 if (!strcmp(entry->match, match))
206 sprintf(path, "%s/%s", entry->mirror_path, jigdo_name);
207 if (file_exists(path, file_size))
209 *mirror_path = strdup(path);
216 fprintf(logfile, "Could not find file %s:%s in any path\n", match, jigdo_name);
220 /* DELIBERATELY do not sort these, or do anything clever with
221 insertion. The entries in the jigdo file should be in the same
222 order as the ones we'll want from the template. Simply add to the
223 end of the singly-linked list each time! */
224 static int add_file_entry(char *jigdo_entry)
227 char *file_name = NULL;
229 jigdo_list_t *new = NULL;
232 error = find_file_in_mirror(jigdo_entry, &file_name, &md5, &file_size);
236 new = calloc(1, sizeof(*new));
241 new->full_path = file_name;
242 new->file_size = file_size;
244 if (!jigdo_list_head)
246 jigdo_list_head = new;
247 jigdo_list_tail = new;
251 jigdo_list_tail->next = new;
252 jigdo_list_tail = new;
258 static int parse_jigdo_file(char *filename)
260 unsigned char buf[2048];
265 file = gzopen(filename, "rb");
268 fprintf(logfile, "Failed to open jigdo file %s, error %d!\n", filename, errno);
272 /* Find the [Parts] section of the jigdo file */
275 ret = gzgets(file, buf, sizeof(buf));
278 if (!strncmp(buf, "[Parts]", 7))
282 /* Now grab the individual file entries and build a list */
285 ret = gzgets(file, buf, sizeof(buf));
286 if (NULL == ret || !strcmp(buf, "\n"))
288 if (!strcmp(buf, "[") || !strcmp(buf, "#"))
290 error = add_file_entry(strdup(buf));
299 static off_t get_file_size(char *filename)
304 error = stat(filename, &sb);
311 static int decompress_data_block(char *in_buf, size_t in_len, char *out_buf, size_t out_len)
316 uc_stream.zalloc = NULL;
317 uc_stream.zfree = NULL;
318 uc_stream.opaque = NULL;
319 uc_stream.next_in = in_buf;
320 uc_stream.avail_in = in_len;
322 error = inflateInit(&uc_stream);
325 fprintf(logfile, "decompress_data_block: failed to init, error %d\n", error);
329 uc_stream.next_out = out_buf;
330 uc_stream.avail_out = out_len;
332 error = inflate(&uc_stream, Z_FINISH);
333 if (Z_OK != error && Z_STREAM_END != error)
335 fprintf(logfile, "decompress_data_block: failed to decompress, error %d\n", error);
339 error = inflateEnd(&uc_stream);
342 fprintf(logfile, "decompress_data_block: failed to end, error %d\n", error);
349 static int read_data_block(FILE *template_file)
353 static off_t template_offset = -1;
354 off_t compressed_len = 0;
355 off_t uncompressed_len = 0;
356 char *comp_buf = NULL;
360 if (-1 == template_offset)
362 fseek(template_file, 0, SEEK_SET);
363 fread(inbuf, sizeof(inbuf), 1, template_file);
364 for (i = 0; i < sizeof(inbuf); i++)
366 if (!strncmp(&inbuf[i], "DATA", 4))
372 if (-1 == template_offset)
374 fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
380 fseek(template_file, template_offset, SEEK_SET);
381 fread(inbuf, 16, 1, template_file);
382 if (strncmp(inbuf, "DATA", 4))
384 fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
389 compressed_len = read_le48(&inbuf[4]);
390 uncompressed_len = read_le48(&inbuf[10]);
392 comp_buf = calloc(1, compressed_len);
395 fprintf(logfile, "Unable to locate DATA block in template (offset %lld)\n",
400 zip_state.data_buf = calloc(1, uncompressed_len);
401 if (!zip_state.data_buf)
403 fprintf(logfile, "Unable to allocate %lld bytes for decompression\n",
408 read_num = fread(comp_buf, compressed_len, 1, template_file);
411 fprintf(logfile, "Unable to read %lld bytes for decompression\n",
416 error = decompress_data_block(comp_buf, compressed_len,
417 zip_state.data_buf, uncompressed_len);
420 fprintf(logfile, "Unable to decompress data block, error %d\n", error);
424 template_offset += compressed_len;
425 zip_state.buf_size = uncompressed_len;
426 zip_state.offset_in_curr_buf = 0;
431 static int skip_data_block(size_t data_size, FILE *template_file)
434 size_t remaining = data_size;
437 /* If we're coming in in the middle of the image, we'll need to
438 skip through some compressed data */
441 if (!zip_state.data_buf)
443 error = read_data_block(template_file);
446 fprintf(logfile, "Unable to decompress template data, error %d\n",
451 size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
452 zip_state.offset_in_curr_buf += size;
455 if (zip_state.offset_in_curr_buf == zip_state.buf_size)
457 free(zip_state.data_buf);
458 zip_state.data_buf = NULL;
462 fprintf(logfile, "skip_data_block: skipped %d bytes of unmatched data\n", data_size);
466 static int parse_data_block(size_t data_size, FILE *template_file, struct mk_MD5Context *context)
469 size_t remaining = data_size;
475 if (!zip_state.data_buf)
477 error = read_data_block(template_file);
480 fprintf(logfile, "Unable to decompress template data, error %d\n",
485 size = MIN((zip_state.buf_size - zip_state.offset_in_curr_buf), remaining);
486 out_size = fwrite(&zip_state.data_buf[zip_state.offset_in_curr_buf], size, 1, outfile);
489 fprintf(logfile, "parse_data_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
490 return ferror(outfile);
494 mk_MD5Update(context, &zip_state.data_buf[zip_state.offset_in_curr_buf], size);
495 zip_state.offset_in_curr_buf += size;
498 if (zip_state.offset_in_curr_buf == zip_state.buf_size)
500 free(zip_state.data_buf);
501 zip_state.data_buf = NULL;
505 fprintf(logfile, "parse_data_block: wrote %d bytes of unmatched data\n", data_size);
509 static int parse_file_block(off_t offset, size_t data_size, off_t file_size, char *md5, struct mk_MD5Context *image_context)
511 char *base64_md5 = base64_dump(md5, 16);
512 FILE *input_file = NULL;
514 size_t remaining = data_size;
516 struct mk_MD5Context file_context;
518 jigdo_list_t *jigdo_list_current = jigdo_list_head;
522 mk_MD5Init(&file_context);
524 while (jigdo_list_current)
526 if ( (jigdo_list_current->file_size == file_size) &&
527 (!memcmp(jigdo_list_current->md5, base64_md5, 16) ) )
529 input_file = fopen(jigdo_list_current->full_path, "rb");
532 fprintf(logfile, "Unable to open mirror file %s, error %d\n",
533 jigdo_list_current->full_path, errno);
536 fseek(input_file, offset, SEEK_SET);
539 int size = MIN(BUF_SIZE, remaining);
540 memset(buf, 0, BUF_SIZE);
542 num_read = fread(buf, size, 1, input_file);
545 fprintf(logfile, "Unable to open mirror file %s, error %d\n",
546 jigdo_list_current->full_path, errno);
552 mk_MD5Update(image_context, buf, size);
553 mk_MD5Update(&file_context, buf, size);
556 out_size = fwrite(buf, size, 1, outfile);
559 fprintf(logfile, "parse_file_block: fwrite %d failed with error %d; aborting\n", size, ferror(outfile));
560 return ferror(outfile);
565 fprintf(logfile, "parse_file_block: wrote %lld bytes of data from %s\n",
566 file_size, jigdo_list_current->full_path);
571 mk_MD5Final(file_md5, &file_context);
573 if (memcmp(file_md5, md5, 16))
575 fprintf(logfile, "MD5 MISMATCH for file %s\n", jigdo_list_current->full_path);
576 fprintf(logfile, " template looking for %s\n", md5);
577 fprintf(logfile, " file in mirror is %s\n", file_md5);
583 jigdo_list_current = jigdo_list_current->next;
588 static int parse_template_file(char *filename, int sizeonly)
590 off_t template_offset = 0;
592 unsigned char *buf = NULL;
595 off_t desc_start = 0;
596 off_t written_length = 0;
597 off_t output_offset = 0;
600 struct mk_MD5Context template_context;
601 unsigned char image_md5sum[16];
603 zip_state.total_offset = 0;
605 file = fopen(filename, "rb");
608 fprintf(logfile, "Failed to open template file %s, error %d!\n", filename, errno);
612 buf = malloc(BUF_SIZE);
615 fprintf(logfile, "Failed to malloc %d bytes. Abort!\n", BUF_SIZE);
620 /* Find the beginning of the desc block */
621 file_size = get_file_size(filename);
622 fseek(file, file_size - 6, SEEK_SET);
623 fread(buf, 6, 1, file);
624 desc_start = file_size - read_le48(buf);
626 /* Now seek back to the beginning image desc block to grab the MD5
628 fseek(file, file_size - 33, SEEK_SET);
629 fread(buf, BUF_SIZE, 1, file);
630 if (buf[0] != 5) /* image data */
632 fprintf(logfile, "Failed to find image desc in the template file\n");
640 printf("%lld\n", read_le48(&buf[1]));
644 fprintf(logfile, "Image is %lld bytes\n", read_le48(&buf[1]));
645 fprintf(logfile, "Image MD5 is ");
646 for (i = 0; i < 16; i++)
647 fprintf(logfile, "%2.2x", buf[i+7]);
648 fprintf(logfile, "\n");
650 /* Now seek back to the start of the desc block */
651 fseek(file, desc_start, SEEK_SET);
652 fread(buf, 10, 1, file);
653 if (strncmp(buf, "DESC", 4))
655 fprintf(logfile, "Failed to find desc start in the template file\n");
659 if ((file_size - desc_start) != read_le48(&buf[4]))
661 fprintf(logfile, "Inconsistent desc length in the template file!\n");
662 fprintf(logfile, "Final chunk says %lld, first chunk says %lld\n",
663 file_size - desc_start, read_le48(&buf[4]));
669 mk_MD5Init(&template_context);
670 template_offset = desc_start + 10;
672 /* Main loop - walk through the template file and expand each entry we find */
677 off_t read_length = 0;
679 if (template_offset >= (file_size - 33))
681 fprintf(logfile, "Reached end of template file\n");
682 break; /* Finished! */
685 if (output_offset > end_offset) /* Past the range we were asked for */
687 fprintf(logfile, "Reached end of range requested\n");
691 fseek(file, template_offset, SEEK_SET);
692 bytes = fread(buf, (MIN (BUF_SIZE, file_size - template_offset)), 1, file);
695 fprintf(logfile, "Failed to read template file!\n");
700 extent_size = read_le48(&buf[1]);
701 read_length = extent_size;
703 if (start_offset > output_offset)
704 skip = start_offset - output_offset;
705 if ((output_offset + extent_size) > end_offset)
706 read_length -= (output_offset + extent_size - end_offset - 1);
712 case 2: /* unmatched data */
713 if ((output_offset + extent_size) >= start_offset)
716 error = skip_data_block(skip, file);
719 fprintf(logfile, "Unable to read data block to skip, error %d\n", error);
723 error = parse_data_block(read_length, file, &template_context);
726 fprintf(logfile, "Unable to read data block, error %d\n", error);
730 written_length += read_length;
733 error = skip_data_block(extent_size, file);
734 template_offset += 7;
737 if ((output_offset + extent_size) >= start_offset)
739 error = parse_file_block(skip, read_length, extent_size, &buf[15], &template_context);
742 fprintf(logfile, "Unable to read file block, error %d\n", error);
746 written_length += read_length;
748 template_offset += 31;
751 fprintf(logfile, "Unknown block type %d!\n", buf[0]);
755 output_offset += extent_size;
761 mk_MD5Final (image_md5sum, &template_context);
762 fprintf(logfile, "Output image MD5 is ");
763 for (i = 0; i < 16; i++)
764 fprintf(logfile, "%2.2x", image_md5sum[i]);
765 fprintf(logfile, "\n");
767 fprintf(logfile, "Output image length is %lld\n", written_length);
772 static void usage(char *progname)
774 printf("%s [OPTIONS]\n\n", progname);
775 printf(" Options:\n");
776 printf(" -j <jigdo name> Specify the input jigdo file\n");
777 printf(" -t <template name> Specify the input template file\n");
778 printf(" -m <item=path> Map <item> to <path> to find the files in the mirror\n");
779 printf(" -l <logfile> Specify a logfile to append to.\n");
780 printf(" If not specified, will log to stderr\n");
781 printf(" -o <outfile> Specify a file to write the ISO image to.\n");
782 printf(" If not specified, will write to stdout\n");
783 printf(" -q Quick mode. Don't check MD5sums. Dangerous!\n");
784 printf(" -s <bytenum> Start byte number; will start at 0 if not specified\n");
785 printf(" -e <bytenum> End byte number; will end at EOF if not specified\n");
786 printf(" -z Don't attempt to rebuild the image; simply print its\n");
787 printf(" size in bytes\n");
790 int main(int argc, char **argv)
792 char *template_filename = NULL;
793 char *jigdo_filename = NULL;
801 bzero(&zip_state, sizeof(zip_state));
805 c = getopt(argc, argv, ":ql:o:j:t:m:h?s:e:z");
815 logfile = fopen(optarg, "ab");
818 fprintf(stderr, "Unable to open log file %s\n", optarg);
824 outfile = fopen(optarg, "wb");
827 fprintf(stderr, "Unable to open output file %s\n", optarg);
834 fprintf(logfile, "Can only specify one jigdo file!\n");
838 jigdo_filename = optarg;
841 if (template_filename)
843 fprintf(logfile, "Can only specify one template file!\n");
847 template_filename = optarg;
850 error = add_match_entry(strdup(optarg));
855 fprintf(logfile, "Missing argument!\n");
864 start_offset = strtoull(optarg, NULL, 10);
865 if (start_offset != 0)
869 end_offset = strtoull(optarg, NULL, 10);
877 fprintf(logfile, "Unknown option!\n");
883 end_offset = (unsigned long long)LONG_MAX * LONG_MAX;
885 if ((NULL == jigdo_filename) && !sizeonly)
887 fprintf(logfile, "No jigdo file specified!\n");
892 if (NULL == template_filename)
894 fprintf(logfile, "No template file specified!\n");
901 /* Build up a list of file mappings */
902 error = parse_jigdo_file(jigdo_filename);
905 fprintf(logfile, "Unable to parse the jigdo file %s\n", jigdo_filename);
910 /* Read the template file and actually build the image to <outfile> */
911 error = parse_template_file(template_filename, sizeonly);
914 fprintf(logfile, "Unable to recreate image from template file %s\n", template_filename);