/* * Copyright (c) 2004 Michael Schroeder (mls@suse.de) * * This program is licensed under the BSD license, read LICENSE.BSD * for further information */ #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "md5.h" #include "rpmhead.h" #include "rpml.h" #include "cpio.h" #include "delta.h" #include "cfile.h" #include "deltarpm.h" char * headtofiles(struct rpmhead *h, struct rpmlfile **filesp, int *nfilesp) { char *nevr, *n; int cnt, i; struct rpmlfile *files; unsigned int *fileflags, *filemodes; char **filelist, **filemd5s; nevr = headtonevr(h); if (!nevr) return 0; filelist = headexpandfilelist(h, &cnt); if (!filelist || !cnt) { *nfilesp = 0; *filesp = 0; return nevr; } fileflags = headint32(h, TAG_FILEFLAGS, (int *)0); filemd5s = headstringarray(h, TAG_FILEMD5S, (int *)0); filemodes = headint16(h, TAG_FILEMODES, (int *)0); files = xmalloc2(cnt, sizeof(*files)); for (i = 0; i < cnt; i++) { n = filelist[i]; if (*n == '/') n++; files[i].name = xmalloc(strlen(n) + 1); strcpy(files[i].name, n); files[i].mode = filemodes[i]; files[i].fflags = fileflags[i]; parsemd5(filemd5s[i], files[i].md5); } *filesp = files; *nfilesp = cnt; free(fileflags); free(filemd5s); free(filemodes); free(filelist); return nevr; } unsigned char *seq; int seqp; int seql; int lastseq = -1; int lastseqstart = 0; void addtoseqn(int n) { int l = 1, j = 8; while (n >= j) { j *= 8; l++; } if (seqp + l > seql * 2) { seq = xrealloc(seq, seql + 32); seql += 32; } while (l-- > 0) { if (seqp & 1) seq[seqp / 2] |= (n & 7) | (l ? 8 : 0); else seq[seqp / 2] = ((n & 7) | (l ? 8 : 0)) << 4; seqp++; n >>= 3; } } void addtoseq(int i) { int n; // fprintf(stderr, "addtoseq %d lastseqstart=%d lastseq=%d\n", i, lastseqstart, lastseq); if (i == lastseq + 1) { lastseq = i; return; } n = lastseq - lastseqstart + 1; if (n) addtoseqn(n); if (i > lastseq + 1 && lastseq >= 0) { addtoseqn(i - lastseq - 1); } else if (i != -1) { addtoseqn(0); addtoseqn(i); } lastseq = lastseqstart = i; } struct prune { char *name; }; struct prune *prunes; int prunen; void read_prunelist(char *file) { FILE *fp; char *buf, *bp; int i, c, bufl; struct prune *p; if ((fp = fopen(file, "r")) == 0) { perror(file); exit(1); } bufl = 256; buf = xmalloc(256); prunes = xmalloc(sizeof(*prunes) * 16); for (i = 0;;) { c = getc(fp); if (c && c != EOF && c != '\n') { buf[i++] = c; if (i == bufl) { bufl += 256; buf = xrealloc(buf, bufl); } continue; } buf[i] = 0; bp = buf; if (*bp == '/') bp++; else if (*bp == '.' && bp[1] == '/') bp += 2; if (!*bp && c == EOF) break; if (!*bp) continue; if ((prunen & 15) == 0) prunes = xrealloc2(prunes, prunen + 16, sizeof(*prunes)); p = prunes + prunen++; p->name = xmalloc(i + 1 - (bp - buf)); memcpy(p->name, bp, i + 1 - (bp - buf)); i = 0; } fclose(fp); } int is_pruned(char *n) { int i; struct prune *p = prunes; for (i = 0; i < prunen; i++, p++) if (!strcmp(p->name, n)) return 1; return 0; } int is_unpatched(char *n, struct rpmlfile *files1, int nfiles1, struct rpmlfile *files2, int nfiles2, char *md5s) { int i; unsigned char md5[16]; for (i = 0; i < nfiles2; i++) if (!strcmp(n, files2[i].name)) break; if (i == nfiles2) return 0; if (!(files2[i].fflags & FILE_UNPATCHED)) return 0; for (i = 0; i < nfiles1; i++) if (!strcmp(n, files1[i].name)) break; if (i == nfiles1) return 1; /* should not happen... */ parsemd5(md5s, md5); if (memcmp(md5, files1[i].md5, 16)) return 1; /* file content may be different */ return 0; } struct streamdata { bsuint bsize; unsigned char *old; bsuint oldl; bsuint oldskip; int oldeof; unsigned char *new; bsuint newl; bsuint newskip; int neweof; struct cfile *newf; unsigned char *xnewdata; bsuint xnewdatal; void *stepd; bsuint scan; bsuint lastpos; bsuint lastscan; struct cfile *cfa; /* add block */ struct cfile *cfi; /* in data */ struct instr *instr; int instrlen; }; struct streamdata sd; /* hack: global for now */ void addtocpio_stream(unsigned char *d, int l) { bsuint bsize = sd.bsize; if (sd.lastscan == sd.newl && sd.neweof) { sd.oldskip += sd.oldl + l; sd.oldl = 0; return; } for (;;) { if (sd.oldl < bsize && !sd.oldeof) { int l2 = bsize - sd.oldl; if (l2 > l) l2 = l; memcpy(sd.old + sd.oldl, d, l2); sd.oldl += l2; l -= l2; d += l2; if (sd.oldl < bsize) return; } while (sd.newl < bsize && !sd.neweof) { int l2 = bsize - sd.newl; if (sd.xnewdatal) { if (l2 > sd.xnewdatal) l2 = sd.xnewdatal; memcpy(sd.new + sd.newl, sd.xnewdata, l2); sd.newl += l2; sd.xnewdata += l2; sd.xnewdatal -= l2; continue; } l2 = sd.newf->read(sd.newf, sd.new + sd.newl, l2); if (l2 < 0) { fprintf(stderr, "payload read failed\n"); exit(1); } if (l2 == 0) sd.neweof = 1; sd.newl += l2; } if (sd.lastscan != sd.newl) { struct instr instr; mkdiff_step(sd.stepd, sd.old, sd.oldl, sd.new, sd.newl, &instr, &sd.scan, &sd.lastpos, &sd.lastscan); if (instr.copyout && !sd.oldeof && (sd.lastscan == sd.newl || instr.copyoutoff + instr.copyout == sd.oldl)) { /* incomplete match, ignore indata part */ instr.copyin = 0; sd.scan = sd.lastscan = instr.copyinoff; sd.lastpos = instr.copyoutoff + instr.copyout; } else if (!instr.copyout && sd.lastscan == sd.newl) { /* no match found in old data, advance if we're behind */ if (sd.lastpos + sd.oldskip < sd.lastscan + sd.newskip) sd.lastpos = sd.oldl; } /* printf("INSTR: %d@%d %d@%d\n", instr.copyout, instr.copyoutoff + sd.oldskip, instr.copyin, instr.copyinoff + sd.newskip); */ if (instr.copyin) { if (instr.copyinoff + instr.copyin > sd.newl) abort(); if (sd.cfi->write(sd.cfi, sd.new + instr.copyinoff, instr.copyin) != instr.copyin) { fprintf(stderr, "could not create indata block\n"); exit(1); } } if (instr.copyout && sd.cfa) { bsuint lenf = instr.copyout; bsuint lastpos = instr.copyoutoff; bsuint lastscan = instr.copyinoff - lenf; if (instr.copyoutoff + instr.copyout > sd.oldl) abort(); if (instr.copyinoff - lenf + instr.copyout > sd.newl) abort(); while (lenf > 0) { unsigned char addblk[4096]; int len2, i; len2 = lenf > 4096 ? 4096 : lenf; for (i = 0; i < len2; i++) addblk[i] = sd.new[lastscan + i] - sd.old[lastpos + i]; if (sd.cfa->write(sd.cfa, addblk, len2) != len2) { fprintf(stderr, "could not create compressed add block\n"); exit(1); } lastscan += len2; lastpos += len2; lenf -= len2; } } instr.copyinoff += sd.newskip; instr.copyoutoff += sd.oldskip; if (sd.instrlen && sd.instr[sd.instrlen - 1].copyin == 0 && sd.instr[sd.instrlen - 1].copyoutoff + sd.instr[sd.instrlen - 1].copyout == instr.copyoutoff) { /* just add to last instruction */ sd.instr[sd.instrlen - 1].copyin = instr.copyin; sd.instr[sd.instrlen - 1].copyinoff = instr.copyinoff; sd.instr[sd.instrlen - 1].copyout += instr.copyout; } else if (instr.copyin || instr.copyout) { if ((sd.instrlen & 31) == 0) { if (sd.instr) sd.instr = realloc(sd.instr, sizeof(*sd.instr) * (sd.instrlen + 32)); else sd.instr = malloc(sizeof(*sd.instr) * (sd.instrlen + 32)); if (!sd.instr) { fprintf(stderr, "out of memory\n"); exit(1); } } sd.instr[sd.instrlen] = instr; sd.instrlen++; } } if (sd.lastscan > bsize / 4 && !sd.neweof) { if (sd.newl > sd.lastscan) memmove(sd.new, sd.new + sd.lastscan, sd.newl - sd.lastscan); sd.newl -= sd.lastscan; sd.newskip += sd.lastscan; sd.scan -= sd.lastscan; sd.lastscan = 0; } if (sd.lastpos > bsize / 2 && !sd.oldeof) { bsuint move = sd.lastpos - bsize / 4; if (sd.lastpos + sd.oldskip + bsize < sd.lastscan + sd.newskip) move = sd.lastpos; if (move > sd.lastpos) move = sd.lastpos; if (sd.oldl > move) memmove(sd.old, sd.old + move, sd.oldl - move); sd.oldl -= move; sd.oldskip += move; sd.lastpos -= move; mkdiff_step_freedata(sd.stepd); } if (sd.lastscan == sd.newl && sd.neweof) { sd.oldskip += sd.oldl + l; sd.oldl = 0; mkdiff_step_freedata(sd.stepd); return; } } } void addtocpio(unsigned char **cpiop, bsuint *cpiol, unsigned char *d, int l) { bsuint cpl = *cpiol; if (*cpiop == (unsigned char *)&sd) { *cpiol += l; addtocpio_stream(d, l); return; } if (cpl + l < cpl || cpl + l + 65535 < cpl + l) { fprintf(stderr, "cpio archive to big\n"); exit(1); } if (cpl == 0 || ((cpl - 1) & 65535) + l >= 65536) *cpiop = xrealloc(*cpiop, (cpl + l + 65535) & ~65535); memcpy(*cpiop + cpl, d, l); *cpiol = cpl + l; } void convertinstr(struct instr *instr, int instrlen, struct deltarpm *d) { int i, j; bsuint x1, x2, x3, off; unsigned int *b1; int nb1; unsigned int *b2; int nb2; b1 = b2 = 0; nb1 = nb2 = 0; j = 0; off = 0; for (i = 0; i < instrlen; i++) { x1 = instr[i].copyout; x2 = instr[i].copyin; x3 = instr[i].copyoutoff; if (!x1 && !x2) continue; if (x1) { retry1: if ((nb2 & 15) == 0) b2 = xrealloc2(b2, nb2 + 16, 2 * sizeof(unsigned int)); if (x3 > off && x3 - off >= 0x80000000) { b2[2 * nb2] = 0x7fffffff; b2[2 * nb2 + 1] = 0; nb2++; off += 0x7fffffff; j++; goto retry1; } if (x3 < off && off - x3 >= 0x80000000) { b2[2 * nb2] = -0x7fffffff; b2[2 * nb2 + 1] = 0; nb2++; off -= 0x7fffffff; j++; goto retry1; } b2[2 * nb2] = (int)(x3 - off); if (x1 >= 0x80000000) { b2[2 * nb2 + 1] = 0x7fffffff; nb2++; x1 -= 0x7fffffff; off = x3 = x3 + 0x7fffffff; j++; goto retry1; } b2[2 * nb2 + 1] = x1; nb2++; off = x3 + x1; j++; } if (x2) { retry2: if ((nb1 & 15) == 0) b1 = xrealloc2(b1, nb1 + 16, 2 * sizeof(unsigned int)); b1[2 * nb1] = j; j = 0; if (x2 >= 0x80000000) { b1[2 * nb1 + 1] = 0x7fffffff; x2 -= 0x7fffffff; nb1++; goto retry2; } b1[2 * nb1 + 1] = x2; nb1++; } } if (j) { if ((nb1 & 15) == 0) b1 = xrealloc2(b1, nb1 + 16, 2 * sizeof(unsigned int)); b1[2 * nb1] = j; b1[2 * nb1 + 1] = 0; nb1++; } d->inn = nb1; d->in = b1; d->outn = nb2; d->out = b2; } unsigned char ** createindatalist(struct instr *instr, int instrlen, struct deltarpm *d, unsigned char *new) { int i, j; bsuint off, left, todo; unsigned char **indatalist; left = off = 0; j = 0; d->inlen = 0; indatalist = xcalloc(d->inn, sizeof(unsigned char *)); for (i = 0; i < d->inn; i++) { todo = d->in[2 * i + 1]; while (!left && todo) { off = instr[j].copyinoff; left = instr[j].copyin; j++; } indatalist[i] = new + off; off += todo; left -= todo; d->inlen += todo; } return indatalist; } int str2comp(char *comp) { int n = strlen(comp); if (n > 2 && n < 20 && comp[n - 2] == '.' && comp[n - 1] >= '0' && comp[n - 1] <= '9') { char buf[20]; strcpy(buf, comp); buf[n - 2] = 0; return cfile_setlevel(str2comp(buf), comp[n - 1] - '0'); } if (!strcmp(comp, "bzip2")) return CFILE_COMP_BZ; if (!strcmp(comp, "gzip")) return CFILE_COMP_GZ; #ifdef CFILE_COMP_GZ_RSYNC if (!strcmp(comp, "gzip rsyncable")) return CFILE_COMP_GZ_RSYNC; #endif if (!strcmp(comp, "lzma")) return CFILE_COMP_LZMA; if (!strcmp(comp, "xz")) return CFILE_COMP_XZ; if (!strcmp(comp, "uncompressed")) return CFILE_COMP_UN; fprintf(stderr, "unknown compression type: %s\n", comp); exit(1); } void createaddblock(struct instr *instr, int instrlen, struct deltarpm *d, unsigned char *old, unsigned char *new, int comp) { struct cfile *cfa; unsigned int l, l2; unsigned char blk[4096]; unsigned char *o; int i, j; cfa = cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, &d->addblk, comp, CFILE_LEN_UNLIMITED, 0, 0); if (!cfa) { fprintf(stderr, "could not create compressed add block\n"); exit(1); } for (i = 0; i < instrlen; i++) { l = instr[i].copyout; o = old + instr[i].copyoutoff; while (l) { l2 = l > sizeof(blk) ? sizeof(blk) : l; for (j = 0; j < l2; j++) blk[j] = new[j] - o[j]; if (cfa->write(cfa, blk, l2) != l2) { fprintf(stderr, "could not create compressed add block\n"); exit(1); } new += l2; o += l2; l -= l2; } new += instr[i].copyin; } d->addblklen = cfa->close(cfa); } void write_seqfile(struct deltarpm *d, char *seqfile) { FILE *ifp; int i; if ((ifp = fopen(seqfile, "w")) == 0) { perror(seqfile); exit(1); } fprintf(ifp, "%s-", d->nevr); for (i = 0; i < d->seql; i++) fprintf(ifp, "%02x", d->seq[i]); fprintf(ifp, "\n"); if (fclose(ifp)) { perror("fclose seqfile"); exit(1); } } static int in_multilib_dir(char *fn) { char *dirs[] = { "lib/", "lib64/", "lib32/", NULL }; int i; for (i = 0; dirs[i] != NULL; i++) if (strstr(fn, dirs[i]) != NULL) return 1; return 0; } int main(int argc, char **argv) { char *rpmname; unsigned char rpmlead[96]; struct rpmhead *h, *sigh; char *nevr; int filecnt; char **filenames, **filemd5s, **filelinktos; unsigned int *fileflags, *filemodes, *filerdevs, *filesizes, *fileverify, *filecolors; int digestalgo = 1; unsigned int *digestalgoarray; int i, l, l2, l3; int fd, nfd; struct cfile *bfd; struct cpiophys cph; char *namebuf; int namebufl; char *np; char buf[4096]; int c, skip; char *prunelist = 0; int cpiocnt = 0; int skipped_notfound = 0; int skipped_pruned = 0; int skipped_unpatched = 0; int skipped_badsize = 0; int skipped_fileflags = 0; int skipped_verifyflags = 0; int skipped_multilib = 0; int skipped_all = 0; int pinfo = 0; struct rpmlfile *files1 = 0; int nfiles1 = 0; char *nevr1 = 0; struct rpmlfile *files2 = 0; int nfiles2 = 0; char *nevr2 = 0; struct rpmhead *h2 = 0; char *seqfile = 0; MD5_CTX seqmd5; unsigned char seqmd5res[16]; unsigned char *oldcpio = 0; bsuint oldcpiolen = 0; unsigned char *newcpio = 0; bsuint newcpiolen = 0; bsuint cpiopos, oldadjust; unsigned int *offadjs = 0; unsigned int offadjn = 0; char *payformat; MD5_CTX fullmd5; unsigned char fullmd5res[16]; unsigned int fullsize = 0; struct cfile *newbz; struct instr *instr = 0; int instrlen = 0; int verbose = 0; int version = 3; struct deltarpm d; unsigned char **indatalist = 0; int rpmonly = 0; int alone = 0; FILE *vfp = 0; char *compopt = 0; int paycomp = CFILE_COMP_XX; int addblkcomp = CFILE_COMP_BZ; int targetcomp = CFILE_COMP_XX; char *payloadflags; bsuint stream = 0; memset(&d, 0, sizeof(d)); memset(&sd, 0, sizeof(sd)); while ((c = getopt(argc, argv, "vV:prl:s:z:um:")) != -1) { switch (c) { case 'l': prunelist = optarg; break; case 'u': alone = 1; break; case 's': seqfile = optarg; break; case 'v': verbose++; break; case 'V': version = atoi(optarg); break; case 'p': pinfo = 1; break; case 'r': rpmonly = 1; break; case 'z': compopt = optarg; break; case 'm': stream = atoi(optarg) * (1024 * 256); break; default: fprintf(stderr, "usage: makedeltarpm [-l ] [-s seq] oldrpm newrpm deltarpm\n"); exit(1); } } if (verbose) vfp = !strcmp("-", argv[argc - 1]) ? stderr : stdout; if (compopt) { char *c2 = strchr(compopt, ','); if (c2) *c2++ = 0; if (*compopt && strcmp(compopt, "rpm") != 0) paycomp = str2comp(compopt); if (c2) { if (!strcmp(c2, "off") != 0) addblkcomp = -1; else addblkcomp = str2comp(c2); } } if (prunelist) read_prunelist(prunelist); if (argc - optind != (pinfo ? 5 : 3) - alone) { fprintf(stderr, "usage: makedeltarpm [-l ] [-s seq] oldrpm newrpm deltarpm\n"); exit(1); } if (version != 1 && version != 2 && version != 3) { fprintf(stderr, "illegal version: %d\n", version); exit(1); } if (pinfo) { FILE *pfp; int pfd; pfp = fopen(argv[argc - 5 + alone], "r"); if (!pfp) { perror(argv[argc - 5 + alone]); exit(1); } nevr1 = rpmlread(pfp, argv[argc - 5 + alone], 0, &files1, &nfiles1); fclose(pfp); pfd = open(argv[argc - 4 + alone], O_RDONLY); if (pfd < 0) { perror(argv[argc - 4 + alone]); exit(1); } if (read(pfd, rpmlead, 4) != 4) { fprintf(stderr, "%s: not a rpm or rpmlist\n", argv[argc - 4 + alone]); exit(1); } if (rpmlead[0] != 0xed || rpmlead[1] != 0xab || rpmlead[2] != 0xee || rpmlead[3] != 0xdb) { pfp = fdopen(pfd, "r"); if (!pfp) { perror(argv[argc - 4 + alone]); exit(1); } nevr2 = rpmlread(pfp, argv[argc - 4 + alone], 1, &files2, &nfiles2); fclose(pfp); } else { if (read(pfd, rpmlead + 4, 92) != 92 || rpmlead[4] != 0x03 || rpmlead[0x4e] != 0 || rpmlead[0x4f] != 5) { fprintf(stderr, "%s: not a v3 rpm or not new header styles\n", argv[argc - 4 + alone]); exit(1); } h2 = readhead(pfd, 1); if (!h2) { fprintf(stderr, "could not read signature header\n"); exit(1); } free(h2); h2 = readhead(pfd, 0); if (!h2) { fprintf(stderr, "could not read header\n"); exit(1); } close(pfd); nevr2 = headtofiles(h2, &files2, &nfiles2); } } rpmMD5Init(&seqmd5); /* open old rpm */ /* (if alone == 1, oldrpm == newrpm) */ rpmname = argv[argc - 3 + alone]; if (!strcmp(rpmname, "-")) fd = 0; else if ((fd = open(rpmname, O_RDONLY)) < 0) { perror(rpmname); exit(1); } if (read(fd, rpmlead, 96) != 96 || rpmlead[0] != 0xed || rpmlead[1] != 0xab || rpmlead[2] != 0xee || rpmlead[3] != 0xdb) { fprintf(stderr, "%s: not a rpm\n", rpmname); exit(1); } if (rpmlead[4] != 0x03 || rpmlead[0x4e] != 0 || rpmlead[0x4f] != 5) { fprintf(stderr, "%s: not a v3 rpm or not new header styles\n", rpmname); exit(1); } sigh = readhead(fd, 1); if (!sigh) { fprintf(stderr, "could not read signature header\n"); exit(1); } h = readhead(fd, 0); if (!h) { fprintf(stderr, "could not read header\n"); exit(1); } nevr = headtonevr(h); if (alone && rpmonly) { /* this mode just updates the lead and signatures, no need to do a real diff */ if (verbose) fprintf(vfp, "reading rpm header...\n"); rpmMD5Init(&fullmd5); /* don't have to compare, write a "no diff" deltarpm */ d.h = 0; d.name = argv[argc - 1]; d.version = 0x444c5430 + version; memcpy(d.rpmlead, rpmlead, 96); d.leadl = 96 + 16 + sigh->cnt * 16 + sigh->dcnt; d.lead = xmalloc(d.leadl); memcpy(d.lead, rpmlead, 96); memcpy(d.lead + 96, sigh->intro, 16); memcpy(d.lead + 96 + 16, sigh->data, d.leadl - 96 - 16); d.inn = d.outn = 0; d.in = d.out = 0; d.nevr = nevr; d.seql = 16; d.seq = xmalloc(d.seql); /* calculate seqmd5 and targetmd5 */ rpmMD5Update(&fullmd5, d.lead , d.leadl); rpmMD5Update(&fullmd5, h->intro , 16); rpmMD5Update(&fullmd5, h->data, 16 * h->cnt + h->dcnt); rpmMD5Update(&seqmd5, h->intro , 16); rpmMD5Update(&seqmd5, h->data, 16 * h->cnt + h->dcnt); fullsize = d.leadl + 16 + 16 * h->cnt + h->dcnt; while ((l = read(fd, buf, sizeof(buf))) > 0) { rpmMD5Update(&fullmd5, (unsigned char *)buf, l); rpmMD5Update(&seqmd5, (unsigned char *)buf, l); fullsize += l; } if (l == -1) { fprintf(stderr, "read error\n"); exit(1); } rpmMD5Final(d.seq, &seqmd5); rpmMD5Final(d.targetmd5, &fullmd5); targetcomp = CFILE_COMP_UN; if (paycomp == CFILE_COMP_XX) paycomp = CFILE_COMP_GZ; /* no need for better compression */ if (addblkcomp == CFILE_COMP_XX) addblkcomp = CFILE_COMP_GZ; d.targetsize = fullsize; d.targetcomp = targetcomp; d.targetcomppara = 0; d.targetcompparalen = 0; d.targetnevr = headtonevr(h); d.compheadlen = 16 + 16 * h->cnt + h->dcnt; d.offadjn = 0; d.offadjs = 0; d.payformatoff = 0; d.outlen = 0; d.deltacomp = paycomp; d.addblk = 0; d.addblklen = 0; if (verbose) fprintf(vfp, "writing delta rpm...\n"); writedeltarpm(&d, 0); if (seqfile) write_seqfile(&d, seqfile); sigh = xfree(sigh); h = xfree(h); d.seq = xfree(d.seq); d.seql = 0; d.lead = xfree(d.lead); d.leadl = 0; seq = xfree(seq); d.targetnevr = xfree(d.targetnevr); exit(0); } if (!alone) sigh = xfree(sigh); if (pinfo) { if (strcmp(nevr, nevr1) != 0) { fprintf(stderr, "pinfo rpmlist1 (%s) does not match rpm (%s)\n", nevr1, nevr); exit(1); } if (strcmp(nevr, nevr2) != 0) { fprintf(stderr, "pinfo rpmlist2 (%s) does not match rpm (%s)\n", nevr2, nevr); exit(1); } } filenames = headexpandfilelist(h, &filecnt); fileflags = headint32(h, TAG_FILEFLAGS, (int *)0); filemd5s = headstringarray(h, TAG_FILEMD5S, (int *)0); filerdevs = headint16(h, TAG_FILERDEVS, (int *)0); filesizes = headint32(h, TAG_FILESIZES, (int *)0); filemodes = headint16(h, TAG_FILEMODES, (int *)0); fileverify = headint32(h, TAG_FILEVERIFY, (int *)0); filelinktos = headstringarray(h, TAG_FILELINKTOS, (int *)0); filecolors = headint32(h, TAG_FILECOLORS, (int *)0); if ((digestalgoarray = headint32(h, TAG_FILEDIGESTALGO, (int *)0))) { digestalgo = digestalgoarray[0]; free(digestalgoarray); } if (digestalgo != 1 && digestalgo != 8) { fprintf(stderr, "Unknown digest type: %d\n", digestalgo); exit(1); } /***************************************************************************/ if (alone) { if (verbose) fprintf(vfp, "reading rpm...\n"); nfd = fd; fd = -1; d.h = h; h = 0; stream = 0; /* sorry! */ } else { if (verbose) fprintf(vfp, "reading new rpm...\n"); rpmname = argv[argc - 2]; if (!strcmp(rpmname, "-")) nfd = 0; else if ((nfd = open(rpmname, O_RDONLY)) < 0) { perror(rpmname); exit(1); } if (read(nfd, rpmlead, 96) != 96 || rpmlead[0] != 0xed || rpmlead[1] != 0xab || rpmlead[2] != 0xee || rpmlead[3] != 0xdb) { fprintf(stderr, "%s: not a rpm\n", rpmname); exit(1); } if (rpmlead[4] != 0x03 || rpmlead[0x4e] != 0 || rpmlead[0x4f] != 5) { fprintf(stderr, "%s: not a v3 rpm or not new header styles\n", rpmname); exit(1); } sigh = readhead(nfd, 1); if (!sigh) { fprintf(stderr, "could not read signature header\n"); exit(1); } d.h = readhead(nfd, 0); if (!d.h) { fprintf(stderr, "could not read header\n"); exit(1); } } rpmMD5Init(&fullmd5); rpmMD5Update(&fullmd5, rpmlead, 96); rpmMD5Update(&fullmd5, sigh->intro, 16); rpmMD5Update(&fullmd5, sigh->data, sigh->cnt * 16 + sigh->dcnt); rpmMD5Update(&fullmd5, d.h->intro, 16); rpmMD5Update(&fullmd5, d.h->data, d.h->cnt * 16 + d.h->dcnt); if (rpmonly) { /* add new header to cpio */ addtocpio(&newcpio, &newcpiolen, d.h->intro, 16); addtocpio(&newcpio, &newcpiolen, d.h->data, 16 * d.h->cnt + d.h->dcnt); } fullsize = 96 + 16 + sigh->cnt * 16 + sigh->dcnt + 16 + d.h->cnt * 16 + d.h->dcnt; newbz = cfile_open(CFILE_OPEN_RD, nfd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, (cfile_ctxup)rpmMD5Update, &fullmd5); if (!newbz) { fprintf(stderr, "payload open failed\n"); exit(1); } if (cfile_detect_rsync(newbz)) { fprintf(stderr, "detect_rsync failed\n"); exit(1); } targetcomp = newbz->comp; if ((payloadflags = headstring(d.h, TAG_PAYLOADFLAGS)) != 0) if (*payloadflags >= '1' && *payloadflags <= '9') targetcomp = cfile_setlevel(targetcomp, *payloadflags - '0'); if (paycomp == CFILE_COMP_XX) paycomp = targetcomp; if (addblkcomp == CFILE_COMP_XX) addblkcomp = targetcomp; if (stream) { memset(&sd, 0, sizeof(sd)); sd.xnewdata = newcpio; sd.xnewdatal = newcpiolen; sd.newf = newbz; sd.bsize = stream; sd.old = xmalloc(sd.bsize); sd.new = xmalloc(sd.bsize); if (addblkcomp != -1) sd.cfa = cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, &d.addblk, addblkcomp, CFILE_LEN_UNLIMITED, 0, 0); sd.cfi = cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, &d.indata, CFILE_COMP_UN, CFILE_LEN_UNLIMITED, 0, 0); oldcpio = (void *)&sd; oldcpiolen = 0; sd.stepd = mkdiff_step_setup(DELTAMODE_HASH | (addblkcomp == -1 ? DELTAMODE_NOADDBLK : 0)); } else { while ((l = newbz->read(newbz, buf, sizeof(buf))) > 0) addtocpio(&newcpio, &newcpiolen, (unsigned char *)buf, l); if (l < 0) { fprintf(stderr, "payload read failed\n"); exit(1); } } /***************************************************************************/ if (rpmonly) { /* add old header to cpio */ addtocpio(&oldcpio, &oldcpiolen, h->intro, 16); addtocpio(&oldcpio, &oldcpiolen, h->data, 16 * h->cnt + h->dcnt); rpmMD5Update(&seqmd5, h->intro, 16); rpmMD5Update(&seqmd5, h->data, 16 * h->cnt + h->dcnt); bfd = cfile_open(CFILE_OPEN_RD, fd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, (cfile_ctxup)rpmMD5Update, &seqmd5); } else if (alone) bfd = cfile_open(CFILE_OPEN_RD, CFILE_IO_BUFFER, newcpio, CFILE_COMP_UN, newcpiolen, 0, 0); else bfd = cfile_open(CFILE_OPEN_RD, fd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, 0, 0); if (!bfd) { fprintf(stderr, "payload open failed\n"); exit(1); } if (verbose && !alone) fprintf(vfp, "reading old rpm...\n"); if (rpmonly) { while ((l = bfd->read(bfd, buf, sizeof(buf))) > 0) addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)buf, l); } else { namebufl = 1; namebuf = xmalloc(namebufl); cpiopos = oldcpiolen; oldadjust = oldcpiolen; for (;;) { unsigned int size, nsize, lsize, nlink, rdev, hsize; if (bfd->read(bfd, &cph, sizeof(cph)) != sizeof(cph)) { fprintf(stderr, "payload read failed (header)\n"); exit(1); } cpiopos += sizeof(cph); if (memcmp(cph.magic, "070701", 6)) { fprintf(stderr, "bad cpio archive\n"); exit(1); } size = cpion(cph.filesize); nsize = cpion(cph.namesize); nlink = cpion(cph.nlink); nsize += (4 - ((nsize + 2) & 3)) & 3; if (nsize > namebufl) { namebuf = xrealloc(namebuf, nsize); namebufl = nsize; } if (bfd->read(bfd, namebuf, nsize) != nsize) { fprintf(stderr, "payload read failed (name)\n"); exit(1); } cpiopos += nsize; namebuf[nsize - 1] = 0; if (!strcmp(namebuf, "TRAILER!!!")) break; cpiocnt++; np = namebuf; if (*np == '.' && np[1] == '/') np += 2; skip = 1; /* look it up in the header */ for (i = 0; i < filecnt; i++) if (!strcmp(filenames[i] + (filenames[i][0] == '/' ? 1 : 0), np)) break; rdev = lsize = 0; if (i == filecnt) { if (verbose > 1) fprintf(vfp, "%s not found in rpm header\n", np); skipped_notfound++; } else if (prunes && is_pruned(np)) { if (verbose > 1) fprintf(vfp, "skipping %s: pruned\n", np); skipped_pruned++; } else if (pinfo && S_ISREG(filemodes[i]) && is_unpatched(np, files1, nfiles1, files2, nfiles2, filemd5s[i])) { if (verbose > 1) fprintf(vfp, "skipping %s: unpatched but different\n", np); skipped_unpatched++; } else if (S_ISREG(filemodes[i])) { if (size != filesizes[i]) { if (verbose > 1) fprintf(vfp, "skipping %s: size missmatch\n", np); skipped_badsize++; } else if ((fileflags[i] & (FILE_CONFIG|FILE_MISSINGOK|FILE_GHOST)) != 0) { if (verbose > 1) fprintf(vfp, "skipping %s: bad file flags\n", np); skipped_fileflags++; } else if ((fileverify[i] & (VERIFY_MD5|VERIFY_FILESIZE)) != (VERIFY_MD5|VERIFY_FILESIZE)) { if (verbose > 1) fprintf(vfp, "skipping %s: bad verify flags %x\n", np, fileverify[i]); skipped_verifyflags++; } else if (filecolors && (filecolors[i] & (RPMFC_ELF32 | RPMFC_ELF64)) != 0 && !in_multilib_dir(np)) { if (verbose > 1) fprintf(vfp, "skipping %s: colored file in non-multilib dir\n", np); skipped_multilib++; } else { if (verbose > 1) fprintf(vfp, "USING FILE %s\n", np); lsize = size; skip = 0; } } else if (S_ISLNK(filemodes[i])) { if (verbose > 1) fprintf(vfp, "USING SYMLINK %s\n", np); lsize = strlen(filelinktos[i]); skip = 0; } else { if (verbose > 1) fprintf(vfp, "USING ELEMENT %s\n", np); if (S_ISBLK(filemodes[i]) || S_ISCHR(filemodes[i])) rdev = filerdevs[i]; skip = 0; } if (i == filecnt) hsize = 0; else { hsize = 2 + strlen(np) + 1; hsize += (4 - ((hsize + 2) & 3)) & 3; hsize += S_ISREG(filemodes[i]) ? filesizes[i] : S_ISLNK(filemodes[i]) ? strlen(filelinktos[i]) : 0; hsize += (4 - (hsize & 3)) & 3; } l = nsize + size; l += (4 - (l & 3)) & 3; if (!skip) { unsigned char cpiobuf[110 + 3]; int ns = strlen(np); if (oldcpiolen != cpiopos - sizeof(cph) - nsize) { oaretry1: if ((offadjn & 15) == 0) offadjs = xrealloc2(offadjs, offadjn + 16, 2 * sizeof(unsigned int)); if (oldcpiolen - oldadjust >= 0x80000000) { offadjs[2 * offadjn] = 0x7fffffff; offadjs[2 * offadjn + 1] = 0; oldadjust += 0x7fffffff; offadjn++; goto oaretry1; } offadjs[2 * offadjn] = oldcpiolen - oldadjust; oldadjust = oldcpiolen; if (cpiopos - sizeof(cph) - nsize >= oldcpiolen) { drpmuint a = cpiopos - sizeof(cph) - nsize - oldcpiolen; if (a >= 0x80000000) { offadjs[2 * offadjn + 1] = 0x7fffffff; cpiopos -= 0x7fffffff; offadjn++; goto oaretry1; } offadjs[2 * offadjn + 1] = a; } else { drpmuint a = oldcpiolen - (cpiopos - sizeof(cph) - nsize); if (a >= 0x80000000) { offadjs[2 * offadjn + 1] = (unsigned int)-((int)0x7fffffff); cpiopos += 0x7fffffff; offadjn++; goto oaretry1; } offadjs[2 * offadjn + 1] = (unsigned int)(-(int)a); } offadjn++; cpiopos = oldcpiolen + sizeof(cph) + nsize; } sprintf((char *)cpiobuf, "07070100000000%08x00000000000000000000000100000000%08x0000000000000000%08x%08x%08x00000000./", filemodes[i], lsize, devmajor(rdev), devminor(rdev), ns + 3); addtocpio(&oldcpio, &oldcpiolen, cpiobuf, 112); addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)np, ns + 1); ns += 3 - 2; for (; ns & 3 ; ns++) addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)"", 1); rpmMD5Update(&seqmd5, (unsigned char *)np, strlen(np) + 1); rpmMD5Update32(&seqmd5, filemodes[i]); rpmMD5Update32(&seqmd5, lsize); rpmMD5Update32(&seqmd5, rdev); if (S_ISLNK(filemodes[i])) { addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)filelinktos[i], lsize); for (; lsize & 3 ; lsize++) addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)"", 1); skip = 1; rpmMD5Update(&seqmd5, (unsigned char *)filelinktos[i], strlen(filelinktos[i]) + 1); } if (S_ISREG(filemodes[i]) && lsize) { unsigned char fmd5[32]; if (digestalgo == 1) { parsemd5(filemd5s[i], fmd5); rpmMD5Update(&seqmd5, fmd5, 16); } else { parsesha256(filemd5s[i], fmd5); rpmMD5Update(&seqmd5, fmd5, 32); } } addtoseq(i); } else skipped_all++; l = l2 = size; while (l > 0) { l3 = l > sizeof(buf) ? sizeof(buf) : l; if (bfd->read(bfd, buf, l3) != l3) { fprintf(stderr, "payload read failed (data)\n"); exit(1); } cpiopos += l3; if (!skip) addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)buf, l3); l -= l3; } if ((l2 & 3) != 0) { l2 = 4 - (l2 & 3); if (bfd->read(bfd, buf, l2) != l2) { fprintf(stderr, "payload read failed (pad)\n"); exit(1); } cpiopos += l2; if (!skip) addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)"\0\0\0", l2); } } namebuf = xfree(namebuf); namebufl = 0; addtocpio(&oldcpio, &oldcpiolen, (unsigned char *)"07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!\0\0\0\0", 124); if (verbose) { fprintf(vfp, "files used: %4d/%d = %.1f%%\n", (cpiocnt - skipped_all), cpiocnt, (cpiocnt - skipped_all) * 100. / (cpiocnt ? cpiocnt : 1)); fprintf(vfp, "files skipped: %4d/%d = %.1f%%\n", skipped_all, cpiocnt, skipped_all * 100. / (cpiocnt ? cpiocnt : 1)); if (skipped_notfound) fprintf(vfp, " not found: %4d/%d = %.1f%%\n", skipped_notfound, skipped_all, skipped_notfound * 100. / skipped_all); if (skipped_pruned) fprintf(vfp, " pruned: %4d/%d = %.1f%%\n", skipped_pruned, skipped_all, skipped_pruned * 100. / skipped_all); if (skipped_unpatched) fprintf(vfp, " unpatched: %4d/%d = %.1f%%\n", skipped_unpatched, skipped_all, skipped_unpatched * 100. / skipped_all); if (skipped_badsize) fprintf(vfp, " bad size: %4d/%d = %.1f%%\n", skipped_badsize, skipped_all, skipped_badsize * 100. / skipped_all); if (skipped_fileflags) fprintf(vfp, " file flags: %4d/%d = %.1f%%\n", skipped_fileflags, skipped_all, skipped_fileflags * 100. / skipped_all); if (skipped_verifyflags) fprintf(vfp, " verify flags: %4d/%d = %.1f%%\n", skipped_verifyflags, skipped_all, skipped_verifyflags * 100. / skipped_all); if (skipped_multilib) fprintf(vfp, " colored-not-in-multidir: %4d/%d = %.1f%%\n", skipped_multilib, skipped_all, skipped_multilib * 100. / skipped_all); } addtoseq(-1); if (verbose > 1) { fprintf(vfp, "sequence: "); for (i = 0; i < (seqp + 1) / 2; i++) fprintf(vfp, "%02x", seq[i]); fprintf(vfp, "\n"); } } bfd->close(bfd); if (stream) { /* finish */ sd.oldeof = 1; addtocpio(&oldcpio, &oldcpiolen, 0, 0); if (sd.cfa) d.addblklen = sd.cfa->close(sd.cfa); d.inlen = sd.cfi->close(sd.cfi); sd.cfa = 0; sd.cfi = 0; instr = sd.instr; sd.instr = 0; instrlen = sd.instrlen; sd.instrlen = 0; mkdiff_step_free(sd.stepd); oldcpio = 0; } /* close old rpm */ /* fd == -1 in "alone" mode */ if (fd != -1 && strcmp(argv[argc - 3 + alone], "-") != 0) close(fd); rpmMD5Final(seqmd5res, &seqmd5); fileflags = xfree(fileflags); filemd5s = xfree(filemd5s); filerdevs = xfree(filerdevs); filesizes = xfree(filesizes); filemodes = xfree(filemodes); fileverify = xfree(fileverify); filelinktos = xfree(filelinktos); filenames = xfree(filenames); filecolors = xfree(filecolors); h = xfree(h); /* close new rpm */ fullsize += newbz->bytes; if (newbz->close(newbz)) { fprintf(stderr, "junk at end of payload\n"); exit(1); } if (strcmp(rpmname, "-") != 0) close(nfd); rpmMD5Final(fullmd5res, &fullmd5); /****************************************************************/ payformat = headstring(d.h, TAG_PAYLOADFORMAT); if (!payformat || strcmp(payformat, "cpio") != 0) { fprintf(stderr, "payload format is not cpio\n"); exit(1); } if (!stream) { if (verbose) fprintf(vfp, "creating diff...\n"); d.addblk = 0; d.addblklen = 0; mkdiff(DELTAMODE_HASH | (addblkcomp == -1 ? DELTAMODE_NOADDBLK : 0), oldcpio, oldcpiolen, newcpio, newcpiolen, &instr, &instrlen, (unsigned char **)0, (unsigned int *)0, (addblkcomp == CFILE_COMP_BZ ? &d.addblk : 0), (addblkcomp == CFILE_COMP_BZ ? &d.addblklen : 0), (unsigned char **)0, (unsigned int *)0); } /****************************************************************/ if (verbose) fprintf(vfp, "writing delta rpm...\n"); if (!stream && addblkcomp != -1 && addblkcomp != CFILE_COMP_BZ) createaddblock(instr, instrlen, &d, oldcpio, newcpio, addblkcomp); d.name = argv[argc - 1]; d.version = 0x444c5430 + version; memcpy(d.rpmlead, rpmlead, 96); d.leadl = 96 + 16 + sigh->cnt * 16 + sigh->dcnt; d.lead = xmalloc(d.leadl); memcpy(d.lead, rpmlead, 96); memcpy(d.lead + 96, sigh->intro, 16); memcpy(d.lead + 96 + 16, sigh->data, d.leadl - 96 - 16); convertinstr(instr, instrlen, &d); if (!stream) indatalist = createindatalist(instr, instrlen, &d, newcpio); d.nevr = nevr; d.seql = 16 + (seqp + 1) / 2; d.seq = xmalloc(d.seql); memcpy(d.seq, seqmd5res, 16); memcpy(d.seq + 16, seq, d.seql - 16); memcpy(d.targetmd5, fullmd5res, 16); d.targetnevr = headtonevr(d.h); d.targetsize = fullsize; d.targetcomp = targetcomp; d.targetcomppara = 0; d.targetcompparalen = 0; d.compheadlen = rpmonly ? 16 + 16 * d.h->cnt + d.h->dcnt : 0; d.offadjn = offadjn; d.offadjs = offadjs; d.payformatoff = payformat - (char *)d.h->dp; d.outlen = oldcpiolen; if (rpmonly) d.h = xfree(d.h); d.deltacomp = paycomp; writedeltarpm(&d, indatalist); if (seqfile) write_seqfile(&d, seqfile); d.addblk = xfree(d.addblk); d.addblklen = 0; instr = xfree(instr); instrlen = 0; oldcpio = xfree(oldcpio); newcpio = xfree(newcpio); sigh = xfree(sigh); d.h = xfree(d.h); indatalist = xfree(indatalist); d.indata = xfree(d.indata); d.in = xfree(d.in); d.out = xfree(d.out); d.inn = d.outn = 0; d.seq = xfree(d.seq); d.seql = 0; d.lead = xfree(d.lead); d.leadl = 0; nevr = xfree(nevr); seq = xfree(seq); sd.old = xfree(sd.old); sd.new = xfree(sd.new); offadjs = xfree(offadjs); d.targetnevr = xfree(d.targetnevr); exit(0); }