diff options
Diffstat (limited to 'makedeltaiso.c')
-rw-r--r-- | makedeltaiso.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/makedeltaiso.c b/makedeltaiso.c new file mode 100644 index 0000000..321d9e2 --- /dev/null +++ b/makedeltaiso.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2005 Michael Schroeder (mls@suse.de) + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +#define _LARGEFILE64_SOURCE + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include <zlib.h> +#include <bzlib.h> +#include <lzma.h> + +#include "rpmoffs.h" +#include "delta.h" +#include "util.h" +#include "md5.h" +#include "cfile.h" + +double targetsize; +double writtensize; + +unsigned int readiso(FILE *fp, struct rpmpay *pay, int payn, unsigned char **isop) +{ + off64_t size; + off64_t skipsum; + off64_t last; + int i; + unsigned char *iso; + unsigned int l, p, lastl; + char *lastn; + + last = 0; + lastl = 0; + skipsum = 0; + lastn = "<start>"; + for (i = 0; i < payn; i++) + { + if (pay[i].o == last) + { + if (pay[i].l != lastl) + { + fprintf(stderr, "files at same pos with different sizes\n"); + exit(1); + } + continue; + } + if (last + lastl > pay[i].o) + { + fprintf(stderr, "files overlap %s %s %lld %d %lld\n", lastn, pay[i].name, (long long int)last, lastl, (long long int)pay[i].o); + exit(1); + } + last = pay[i].o; + lastl = pay[i].l; + lastn = pay[i].name; + skipsum += pay[i].l; + } + if (fseeko64(fp, (off64_t)0, SEEK_END) != 0) + { + perror("fseeko64"); + exit(1); + } + size = ftello64(fp); + if (size < skipsum) + { + fprintf(stderr, "size < skipsum\n"); + exit(1); + } + if (fseeko64(fp, (off64_t)0, SEEK_SET) != 0) + { + perror("fseeko64"); + exit(1); + } + if (size - skipsum >= 0x80000000) + { + fprintf(stderr, "remaining size too big: %lld\n", (long long int)(size - skipsum)); + exit(1); + } + l = size - skipsum; + if ((iso = malloc(l)) == 0) + { + fprintf(stderr, "out of mem for iso (%d bytes)", l); + exit(1); + } + last = 0; + lastl = 0; + p = 0; + pay[payn].o = size; + for (i = 0; i <= payn; i++) + { + if (pay[i].o == last) + continue; + if (fread(iso + p, pay[i].o - (last + lastl), 1, fp) != 1) + { + fprintf(stderr, "read error\n"); + exit(1); + } + pay[i].x = p; + pay[i].lx = pay[i].o - (last + lastl); + p += pay[i].o - (last + lastl); + last = pay[i].o; + lastl = pay[i].l; + if (fseeko64(fp, pay[i].o + pay[i].l, SEEK_SET)) + { + perror("fseeko64"); + exit(1); + } + } + if (p != l) + { + fprintf(stderr, "internal error %d %d\n", p, l); + exit(1); + } + *isop = iso; + return l; +} + +void +recode_instr(struct instr *instr, int instrlen, unsigned int **b1p, int *nb1p, unsigned int **b2p, int *nb2p, struct rpmpay *pay, int payn) +{ + unsigned int *b1; + int nb1; + unsigned int *b2; + int nb2; + int i, j; + int lastoff; + unsigned int x1, x2, x3, x4; + unsigned int left; + int payp; + + b1 = b2 = 0; + nb1 = nb2 = 0; + j = 0; + lastoff = 0; + left = pay && payn ? pay[0].lx : 0; + payp = 0; + for (i = 0; i < instrlen; i++) + { + x1 = instr[i].copyout; + x2 = instr[i].copyin; + x3 = instr[i].copyoutoff; + x4 = instr[i].copyinoff; +retry: + if (!x1) + { + if (!x2) + continue; + } + if (x1) + { + if ((nb2 & 15) == 0) + b2 = xrealloc2(b2, nb2 + 16, 2 * sizeof(unsigned int)); + if (lastoff <= x3) + b2[2 * nb2] = x3 - lastoff; + else + b2[2 * nb2] = (lastoff - x3) | 0x80000000; + if (left && x1 >= left) + { + b2[2 * nb2 + 1] = left; + nb2++; + j++; + x3 = lastoff = x3 + left; + x1 -= left; + if ((nb1 & 15) == 0) + b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int)); + b1[3 * nb1] = j; + b1[3 * nb1 + 1] = 0xffffffff; + b1[3 * nb1 + 2] = payp; + nb1++; + j = 0; + left = 0; + while (payp < payn && pay[++payp].x == 0) + ; + if (payp < payn) + left = pay[payp].lx; + goto retry; + } + else + { + b2[2 * nb2 + 1] = x1; + nb2++; + j++; + x3 = lastoff = x3 + x1; + if (left) + left -= x1; + } + } + if (x2) + { + if (left && x2 >= left) + { + if ((nb1 & 15) == 0) + b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int)); + b1[3 * nb1] = j; + b1[3 * nb1 + 1] = left; + b1[3 * nb1 + 2] = x4; + nb1++; + j = 0; + x2 -= left; + x4 += left; + if ((nb1 & 15) == 0) + b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int)); + b1[3 * nb1] = 0; + b1[3 * nb1 + 1] = 0xffffffff; + b1[3 * nb1 + 2] = payp; + nb1++; + left = 0; + while (payp < payn && pay[++payp].x == 0) + ; + if (payp < payn) + left = pay[payp].lx; + x1 = 0; + goto retry; + } + if ((nb1 & 15) == 0) + b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int)); + b1[3 * nb1] = j; + b1[3 * nb1 + 1] = x2; + b1[3 * nb1 + 2] = x4; + nb1++; + j = 0; + if (left) + left -= x2; + } + } + if (left || payp != payn) + { + fprintf(stderr, "oops, left = %d %d %d\n", left, payp, payn); + exit(1); + } + if (j) + { + if ((nb1 & 15) == 0) + b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int)); + b1[3 * nb1] = j; + b1[3 * nb1 + 1] = 0; + b1[3 * nb1 + 2] = 0; + nb1++; + } + *b1p = b1; + *nb1p = nb1; + *b2p = b2; + *nb2p = nb2; +} + +void +bzput4(struct cfile *fp, unsigned int d) +{ + unsigned char dd[4]; + dd[0] = d >> 24; + dd[1] = d >> 16; + dd[2] = d >> 8; + dd[3] = d; + if (fp->write(fp, dd, 4) != 4) + { + perror("bzwrite"); + exit(1); + } +} + +void +put4(FILE *fp, unsigned int d) +{ + unsigned char dd[4]; + dd[0] = d >> 24; + dd[1] = d >> 16; + dd[2] = d >> 8; + dd[3] = d; + if (fwrite(dd, 4, 1, fp) != 1) + { + perror("fwrite"); + exit(1); + } +} + +void diffit(struct cfile *fpout, FILE *fpold, FILE *fpnew, unsigned char *old, unsigned int oldl, unsigned char *new, int newl, struct rpmpay *newpays, int newpayn, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx); + +unsigned int payread(FILE *fp, off64_t off, unsigned int len, unsigned char **pp, MD5_CTX *ctx, unsigned char *namebuf) +{ + int l, r; + struct cfile *cfile; + + if (fseeko64(fp, off, SEEK_SET) != 0) + { + perror("fseeko"); + exit(1); + } + cfile = cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fp, CFILE_COMP_XX, len, ctx ? (cfile_ctxup)rpmMD5Update : 0, ctx); + if (!cfile) + { + fprintf(stderr, "cfile open failed\n"); + exit(1); + } + if (namebuf) + { + cfile_detect_rsync(cfile); + namebuf[0] = cfile->comp; + } + l = cfile_copy(cfile, cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, pp, CFILE_COMP_UN, CFILE_LEN_UNLIMITED, 0, 0), CFILE_COPY_CLOSE_OUT); + if (l == -1) + { + fprintf(stderr, "cfile_copy failed\n"); + exit(1); + } + r = cfile->close(cfile); + if (r) + { + fprintf(stderr, "cfile not used up (%d bytes left)\n", r); + exit(1); + } + return l; +} + +void +processrpm(struct cfile *fpout, FILE *fpold, FILE *fpnew, struct rpmpay *pay, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx) +{ + struct rpmpay *oldpay; + int i, n; + unsigned int l, newl, oldl; + unsigned char *new, *old; + unsigned char namebuf[258]; + + oldpay = 0; + for (i = 0; i < oldpayn; i++) + if (!strcmp(oldpays[i].name, pay->name)) + { + oldpay = oldpays + i; + break; + } + l = strlen(pay->name); + if (l > 255) + l = 255; + namebuf[0] = 255; + namebuf[1] = l; + memcpy(namebuf + 2, pay->name, l); + namebuf[l + 2] = 0; + targetsize += pay->l; + if (!oldpay) + { + printf("%s: not found in old iso...", pay->name); + if (fpout->write(fpout, namebuf, l + 2) != l + 2) + { + perror("namebuf write"); + exit(1); + } + bzput4(fpout, pay->l); + if (fseeko64(fpnew, pay->o, SEEK_SET) != 0) + { + perror("fseeko"); + exit(1); + } + if (cfile_copy(cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fpnew, CFILE_COMP_UN, pay->l, (cfile_ctxup)rpmMD5Update, ctx), fpout, CFILE_COPY_CLOSE_IN) != 0) + { + fprintf(stderr, "cfile_copy failed\n"); + exit(1); + } + } + else + { + n = 0; + for (i = 0; i < oldpayn; i++) + if (i == 0 || oldpays[i].x != 0) + { + if (oldpays[i].o == oldpay->o) + break; + else + n++; + } + if (i == oldpayn) + { + fprintf(stderr, "internal error\n"); + exit(1); + } + if (oldpay->l != oldpays[i].l) + { + fprintf(stderr, "internal error, length mismatch %d %d\n", oldpay->l, oldpays[i].l); + exit(1); + } + /* payread will fix namebuf[0] */ + newl = payread(fpnew, pay->o, pay->l, &new, ctx, namebuf); + oldl = payread(fpold, oldpay->o, oldpay->l, &old, 0, 0); + if (newl == oldl && pay->l == oldpay->l && !memcmp(new, old, newl)) + { + printf("%s: unchanged...", namebuf + 2); + namebuf[0] = 254; + } + else + { + int comp = cfile_setlevel(namebuf[0], pay->level); + printf("%s (%s): creating delta...", namebuf + 2, cfile_comp2str(comp)); + namebuf[0] = CFILE_COMPALGO(comp) | (CFILE_COMPLEVEL(comp) << 4); /* argh! */ + } + fflush(stdout); + if (fpout->write(fpout, namebuf, l + 2) != l + 2) + { + perror("namebuf write"); + exit(1); + } + bzput4(fpout, n); /* offset id */ + if (namebuf[0] == 254) + free(old); + else + diffit(fpout, 0, 0, old, oldl, new, newl, 0, 0, 0, 0, 0); + old = 0; + oldl = 0; + free(new); + new = 0; + newl = 0; + } + writtensize += fpout->bytes; + fpout->bytes = 0; + printf("%4.1f%%\n", writtensize * 100 / targetsize); +} + +/* frees old! */ +void diffit(struct cfile *fpout, FILE *fpold, FILE *fpnew, unsigned char *old, unsigned int oldl, unsigned char *new, int newl, struct rpmpay *newpays, int newpayn, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx) +{ + unsigned int *b1; + int nb1; + unsigned int *b2; + int nb2; + struct instr *instr = 0; + int instrlen = 0; + unsigned char *addblk = 0; + unsigned int addblklen = 0; + int i, j, b2i; + unsigned int off; + + mkdiff(DELTAMODE_HASH, old, oldl, new, newl, &instr, &instrlen, 0, 0, &addblk, &addblklen, 0, 0); + free(old); + old = 0; + recode_instr(instr, instrlen, &b1, &nb1, &b2, &nb2, newpays, newpayn); + free(instr); + instr = 0; + instrlen = 0; + bzput4(fpout, oldl); + bzput4(fpout, newl); + bzput4(fpout, nb1); + bzput4(fpout, nb2); + for (i = 0; i < nb1; i++) + bzput4(fpout, b1[3 * i]); + for (i = 0; i < nb1; i++) + bzput4(fpout, b1[3 * i + 1]); + for (i = 0; i < nb2; i++) + bzput4(fpout, b2[2 * i]); + for (i = 0; i < nb2; i++) + bzput4(fpout, b2[2 * i + 1]); + + /* write add section */ + bzput4(fpout, addblklen); + if (addblklen && fpout->write(fpout, addblk, addblklen) != addblklen) + { + perror("bzwrite"); + exit(1); + } + if (addblk) + free(addblk); + + /* write data section */ + b2i = 0; + off = 0; + for (i = 0; i < nb1; i++) + { + if (ctx) + { + for (j = 0; j < b1[3 * i]; j++, b2i++) + { + if (b2[2 * b2i + 1]) + rpmMD5Update(ctx, new + off, b2[2 * b2i + 1]); + off += b2[2 * b2i + 1]; + } + } + if (b1[3 * i + 1] == 0xffffffff) + { + processrpm(fpout, fpold, fpnew, newpays + b1[3 * i + 2], oldpays, oldpayn, ctx); + } + else if (b1[3 * i + 1]) + { + if (ctx) + { + if (off != b1[3 * i + 2]) + { + fprintf(stderr, "internal error: off mismatch %d %d\n", off, b1[3 * i + 2]); + exit(1); + } + rpmMD5Update(ctx, new + off, b1[3 * i + 1]); + off += b1[3 * i + 1]; + } + if (fpout->write(fpout, new + b1[3 * i + 2], b1[3 * i + 1]) != b1[3 * i + 1]) + { + perror("bzwrite"); + exit(1); + } + } + } + free(b1); + free(b2); +} + +int +main(int argc, char **argv) +{ + FILE *fpold, *fpnew, *fpout; + unsigned int oldisolen; + unsigned int newisolen; + unsigned char *oldiso; + unsigned char *newiso; + struct rpmpay *oldpays = 0; + int oldpayn = 0; + struct rpmpay *newpays = 0; + int newpayn = 0; + int i, n; + struct cfile *bf; + MD5_CTX targetmd5; + unsigned char targetmd5res[16]; + + if (argc != 4) + { + fprintf(stderr, "usage: makedeltaiso <oldiso> <newiso> <deltaiso>\n"); + exit(1); + } + if ((fpold = fopen64(argv[1], "r")) == 0) + { + perror(argv[1]); + exit(1); + } + if ((fpnew = fopen64(argv[2], "r")) == 0) + { + perror(argv[2]); + exit(1); + } + oldpayn = rpmoffs(fpold, argv[1], &oldpays); + printf("%s: %d rpms\n", argv[1], oldpayn); + newpayn = rpmoffs(fpnew, argv[2], &newpays); + printf("%s: %d rpms\n", argv[2], newpayn); + if ((fpout = fopen64(argv[3], "w")) == 0) + { + perror(argv[3]); + exit(1); + } + printf("reading old iso (omitting payloads)\n"); + oldisolen = readiso(fpold, oldpays, oldpayn, &oldiso); + printf("size without payloads is %d bytes\n", oldisolen); + printf("reading new iso (omitting payloads)\n"); + newisolen = readiso(fpnew, newpays, newpayn, &newiso); + printf("size without payloads is %d bytes\n", newisolen); + targetsize = newisolen; + + putc('D', fpout); + putc('I', fpout); + putc('S', fpout); + putc('O', fpout); + put4(fpout, 2); + if ((bf = cfile_open(CFILE_OPEN_WR, CFILE_IO_FILE, fpout, CFILE_COMP_BZ, CFILE_LEN_UNLIMITED, 0, 0)) == 0) + { + fprintf(stderr, "cfile wopen failed\n"); + exit(1); + } + /* write old map */ + n = 0; + for (i = 0; i < oldpayn; i++) + if (i == 0 || oldpays[i].x) + n++; + bzput4(bf, n); + for (i = 0; i < oldpayn; i++) + if (i == 0 || oldpays[i].x) + { + bzput4(bf, oldpays[i].lx); + bzput4(bf, oldpays[i].l); + } + bzput4(bf, oldpays[i].lx); + + printf("creating iso diff\n"); + rpmMD5Init(&targetmd5); + diffit(bf, fpold, fpnew, oldiso, oldisolen, newiso, newisolen, newpays, newpayn, oldpays, oldpayn, &targetmd5); + rpmMD5Final(targetmd5res, &targetmd5); + oldiso = 0; + oldisolen = 0; + free(newiso); + newiso = 0; + newisolen = 0; + writtensize += bf->bytes; + bf->bytes = 0; + printf("iso diff done, final compression: %4.1f%%\n", writtensize * 100 / targetsize); + if (bf->write(bf, targetmd5res, 16) != 16) + { + perror("md5sum write"); + exit(1); + } + if (bf->close(bf) == -1) + { + perror("cfile close"); + exit(1); + } + if (fclose(fpout)) + { + perror("fclose"); + exit(1); + } + printf("iso md5sum is: "); + for (i = 0; i < 16; i++) + printf("%02x", targetmd5res[i]); + printf("\n"); + for (i = 0; i < oldpayn; i++) + free(oldpays[i].name); + free(oldpays); + for (i = 0; i < newpayn; i++) + free(newpays[i].name); + free(newpays); + exit(0); +} |