diff options
Diffstat (limited to 'applydeltarpm.c')
-rw-r--r-- | applydeltarpm.c | 1842 |
1 files changed, 1842 insertions, 0 deletions
diff --git a/applydeltarpm.c b/applydeltarpm.c new file mode 100644 index 0000000..867d594 --- /dev/null +++ b/applydeltarpm.c @@ -0,0 +1,1842 @@ +/* + * Copyright (c) 2004,2005 Michael Schroeder (mls@suse.de) + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +#define _XOPEN_SOURCE 500 +#ifdef DELTARPM_64BIT +# define _LARGEFILE64_SOURCE +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <bzlib.h> +#include <zlib.h> +#include <lzma.h> + +#include "util.h" +#include "md5.h" +#include "sha256.h" +#include "rpmhead.h" +#include "cpio.h" +#include "cfile.h" +#include "deltarpm.h" +#include "prelink.h" + +#define BLKSHIFT 13 +#define BLKSIZE (1 << BLKSHIFT) +#define BLKMASK ((1 << BLKSHIFT) - 1) + +#define SEQCHECK_MD5 (1<<0) +#define SEQCHECK_SIZE (1<<1) + +#ifndef RPMDUMPHEADER +# define RPMDUMPHEADER "rpmdumpheader" +#endif + + +/***************************************************************** + * openfile, maintain a set of opened files, close descriptors if + * limit is reached. + */ + +struct openfile { + struct openfile *prev; + struct openfile *next; + char *name; + int fd; + unsigned int off; + struct seqdescr *sd; +}; + +struct openfile *openfiles; +struct openfile *openfilestail; +int nopenfile; +int maxopenfile = 50; + + + +struct openfile * +newopen(struct seqdescr *sd, struct fileblock *fb) +{ + int fd; + char *name; + struct openfile *of; + struct stat stb; + + name = fb->filenames[sd->i]; + if ((fd = open(name, O_RDONLY)) == -1) + { + perror(name); + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + if (fstat(fd, &stb) == 0 && stb.st_size != fb->filesizes[sd->i]) + { + unsigned char buf[128]; + if (is_prelinked(fd, buf, pread(fd, buf, 128, (off_t)0))) + { + close(fd); + return 0; + } + } + if (nopenfile < maxopenfile) + { + of = xmalloc(sizeof(*of)); + nopenfile++; + } + else + { + of = openfiles; + openfiles = of->next; + if (openfiles) + openfiles->prev = 0; + else + openfilestail = 0; + of->sd->f = 0; + // printf("closing %s\n", of->name); + close(of->fd); + } + // printf("opening %s\n", name); + of->fd = fd; + of->name = name; + of->off = 0; + of->sd = sd; + of->prev = of->next = 0; + if (openfilestail) + { + openfilestail->next = of; + of->prev = openfilestail; + openfilestail = of; + } + else + openfiles = openfilestail = of; + sd->f = of; + return of; +} + +/***************************************************************** + * blk stuff, block contents creation and paging + */ + +#define BLK_FREE 0 +#define BLK_CORE_REC 1 +#define BLK_CORE_ONE 2 +#define BLK_PAGE 3 + +struct blk { + struct blk *next; + int type; + int id; + union { + unsigned int off; + unsigned char *buf; + } e; +}; + +struct blk *coreblks; +struct blk *freecoreblks; +struct blk *pageblks; +int ncoreblk = 0; +int npageblk = 0; +int ndropblk = 0; + +int maxcoreblk = 5000; + +unsigned int *maxblockuse; /* last time the block will be used */ +struct blk **vmem; + +unsigned char *cpiodata; +int csdesc = -1; +char *symdata; + +char *fromrpm; +int fromrpm_raw; +struct cfile *outfp; +unsigned int outfpleft; +drpmuint outfpleft_raw; +int outfpid; + + +int pagefd = -1; + + +void (*fillblock_method)(struct blk *b, int id, struct seqdescr *sdesc, int nsdesc, struct fileblock *fb, int idx); + +void +pageoutblock(struct blk *cb, int idx) +{ + struct blk *b; + + // printf("pageoutblock %d\n", cb->id); + for (b = pageblks; b; b = b->next) + if (b->id == cb->id) + { + vmem[b->id] = b; + return; + } + for (b = pageblks; b; b = b->next) + if (maxblockuse[b->id] < idx) + break; + if (!b) + { + b = xmalloc(sizeof(*b)); + b->next = pageblks; + b->type = BLK_PAGE; + b->e.off = npageblk; + pageblks = b; + npageblk++; + if (pagefd < 0) + { + char tmpname[80]; + sprintf(tmpname, "/tmp/deltarpmpageXXXXXX"); +#ifdef DELTARPM_64BIT + pagefd = mkstemp64(tmpname); +#else + pagefd = mkstemp(tmpname); +#endif + if (pagefd < 0) + { + fprintf(stderr, "could not create page area\n"); + exit(1); + } + unlink(tmpname); + } + } + b->id = cb->id; +#ifdef DELTARPM_64BIT + if (pwrite64(pagefd, cb->e.buf, BLKSIZE, (off64_t)b->e.off * BLKSIZE) != BLKSIZE) + { + perror("page area write"); + exit(1); + } +#else + if (pwrite(pagefd, cb->e.buf, BLKSIZE, (off_t)b->e.off * BLKSIZE) != BLKSIZE) + { + perror("page area write"); + exit(1); + } +#endif + vmem[b->id] = b; +} + +void +pageinblock(struct blk *cb, struct blk *b) +{ + if (b->type != BLK_PAGE) + abort(); +#ifdef DELTARPM_64BIT + if (pread64(pagefd, cb->e.buf, BLKSIZE, (off64_t)b->e.off * BLKSIZE) != BLKSIZE) + { + perror("page area read"); + exit(1); + } +#else + if (pread(pagefd, cb->e.buf, BLKSIZE, (off_t)b->e.off * BLKSIZE) != BLKSIZE) + { + perror("page area read"); + exit(1); + } +#endif + cb->id = b->id; + cb->type = BLK_CORE_ONE; + vmem[cb->id] = cb; +} + +struct blk * +newcoreblk(void) +{ + struct blk *b; + b = xmalloc(sizeof(*b) + BLKSIZE); + b->next = coreblks; + b->type = BLK_FREE; + b->e.buf = (unsigned char *)(b + 1); + coreblks = b; + ncoreblk++; + // printf("created new coreblk, have now %d\n", ncoreblk); + return b; +} + +void +pushblock(struct blk *nb, int idx) +{ + struct blk *b; + + b = freecoreblks; + if (b) + { + freecoreblks = b->next; + b->next = coreblks; + coreblks = b; + } + if (!b && ncoreblk < maxcoreblk) + b = newcoreblk(); + if (!b) + { + /* could not find in-core place */ + if (nb->type == BLK_CORE_ONE) + pageoutblock(nb, idx); + else + vmem[nb->id] = 0; + return; + } + b->type = nb->type; + b->id = nb->id; + memcpy(b->e.buf, nb->e.buf, BLKSIZE); + vmem[b->id] = b; +} + +void +createcpiohead(struct seqdescr *sd, struct fileblock *fb) +{ + int i = sd->i; + unsigned int lsize, rdev; + char *np; + + if (i == -1) + { + sprintf((char *)cpiodata, "%s%c%c%c%c", "07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!", 0, 0, 0, 0); + return; + } + lsize = rdev = 0; + np = fb->filenames[i]; + if (*np == '/') + np++; + if (S_ISREG(fb->filemodes[i])) + lsize = fb->filesizes[i]; + else if (S_ISLNK(fb->filemodes[i])) + { + symdata = fb->filelinktos[i]; + lsize = strlen(fb->filelinktos[i]); + } + if (S_ISBLK(fb->filemodes[i]) || S_ISCHR(fb->filemodes[i])) + rdev = fb->filerdevs[i]; + sprintf((char *)cpiodata, "07070100000000%08x00000000000000000000000100000000%08x0000000000000000%08x%08x%08x00000000./%s%c%c%c%c", fb->filemodes[i], lsize, devmajor(rdev), devminor(rdev), (int)strlen(np) + 3, np, 0, 0, 0, 0); +} + +int nprelink = 0; + +void +fillblock_prelink(struct blk *b, int id, struct seqdescr *sd, struct fileblock *fb, int idx) +{ + int xid = id; + drpmuint off; + struct stat stb; + char *name; + int fd = -1; + int isp = 0; + int l; + unsigned char buf[128]; + unsigned char *bp, saveblk[BLKSIZE]; + + /* go to first block that doesn't start in the middle of a + * prelinked file */ + off = id << BLKSHIFT; + for (;;) + { + while (sd->off > off) + sd--; + /* now sd contains off */ + if (sd->i == -1 || sd->datalen == 0 || sd->off + sd->cpiolen >= off) + break; + if (S_ISLNK(fb->filemodes[sd->i])) + break; + /* off in regular file, check if prelinked */ + name = fb->filenames[sd->i]; + if ((fd = open(name, O_RDONLY)) == -1) + { + perror(name); + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + if (fstat(fd, &stb) != 0 || stb.st_size == fb->filesizes[sd->i]) + break; + if (!is_prelinked(fd, buf, pread(fd, buf, 128, (off_t)0))) + break; + close(fd); + fd = -1; + /* rewind blocks until we leave the file */ + do + { + id--; + off = id << BLKSHIFT; + } + while (sd->off + sd->cpiolen < off); + } + /* ok, got id, sd and maybe fd. create blocks. */ + l = BLKSIZE; + bp = b->e.buf; + if (fd != -1) + { + unsigned int u = off - (sd->off + sd->cpiolen); + if (u && u < fb->filesizes[sd->i]) + { + if (lseek(fd, (off_t)u, SEEK_SET) == (off_t)-1) + { + fprintf(stderr, "%s: seek error\n", fb->filenames[sd->i]); + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + } + } + for (;;) + { + while (l) + { + while (off >= sd->off + sd->cpiolen + sd->datalen) + { + if (fd != -1) + { + close(fd); + fd = -1; + } + sd++; + } + if (off < sd->off + sd->cpiolen) + { + int o = off - sd->off; + int l2 = l > sd->cpiolen - o ? sd->cpiolen - o : l; + createcpiohead(sd, fb); + memcpy(bp, cpiodata + o, l2); + bp += l2; + off += l2; + l -= l2; + } + if (!l) + break; + if (sd->i == -1) + { + memset(bp, 0, l); + bp += l; + off += l; + l -= l; + } + else if (S_ISLNK(fb->filemodes[sd->i])) + { + int o = off - (sd->off + sd->cpiolen); + char *ln = fb->filelinktos[sd->i]; + int l2 = strlen(ln) - o; + if (l2 < 0) + l2 = 0; + if (l2 > l) + l2 = l; + if (l2) + memcpy(bp, ln + o, l2); + bp += l2; + off += l2; + l -= l2; + o += l2; + l2 = l > sd->datalen - o ? sd->datalen - o : l; + if (l2 > 0) + { + memset(bp, 0, l2); + bp += l2; + off += l2; + l -= l2; + } + } + else if (sd->datalen) + { + int o = off - (sd->off + sd->cpiolen); + int l2; + if (o < fb->filesizes[sd->i]) + { + l2 = l > fb->filesizes[sd->i] - o ? fb->filesizes[sd->i] - o : l; + if (fd == -1) + { + name = fb->filenames[sd->i]; + isp = 0; + if ((fd = open(name, O_RDONLY)) == -1) + perror(name); + else if (fstat(fd, &stb) == 0 && stb.st_size != fb->filesizes[sd->i] && is_prelinked(fd, buf, pread(fd, buf, 128, (off_t)0))) + { + close(fd); + fd = prelinked_open(name); + nprelink++; + isp = 1; + } + if (fd == -1) + { + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + } + if (read(fd, bp, l2) != l2) + { + fprintf(stderr, "%s: read error\n", fb->filenames[sd->i]); + fprintf(stderr, "(tried to read %d bytes from offset %d\n", l2, o); + exit(1); + } + bp += l2; + off += l2; + l -= l2; + o += l2; + } + if (o >= fb->filesizes[sd->i]) + { + if (fd) + { + close(fd); + fd = -1; + } + l2 = l > sd->datalen - o ? sd->datalen - o : l; + if (l2) + memset(bp, 0, l2); + bp += l2; + off += l2; + l -= l2; + } + } + } + + b->type = BLK_CORE_ONE; + b->id = id; + if (id == xid) + memcpy(saveblk, b->e.buf, BLKSIZE); + else if (maxblockuse[b->id] > idx || (maxblockuse[b->id] == idx && id > xid)) + pushblock(b, idx); + /* finished block */ + if (fd == -1 || !isp) + break; + l = BLKSIZE; + bp = b->e.buf; + id++; + off = id << BLKSHIFT; + } + if (id < xid) + { + fprintf(stderr, "internal error, could not reach block %d (%d)\n", xid, id); + exit(1); + } + if (fd != -1) + close(fd); /* never prelinked */ + memcpy(b->e.buf, saveblk, BLKSIZE); + b->type = BLK_CORE_ONE; + b->id = xid; +} + +void +fillblock_disk(struct blk *b, int id, struct seqdescr *sdesc, int nsdesc, struct fileblock *fb, int idx) +{ + drpmuint off; + unsigned int u; + struct seqdescr *sd; + int i; + unsigned int l, l2; + unsigned char *bp; + + l = BLKSIZE; + bp = b->e.buf; + off = id << BLKSHIFT; + i = csdesc >= 0 ? csdesc : 0; + for (sd = sdesc + i; i > 0 && sd->off > off; i--, sd--) + ; + for (; i < nsdesc; i++, sd++) + if (sd->off <= off && sd->off + sd->cpiolen + sd->datalen > off) + break; + if (i == nsdesc) + { + fprintf(stderr, "fillblock_disk: block %d out of range\n", id); + exit(1); + } + if (i != csdesc) + { + csdesc = i; + createcpiohead(sd, fb); + } + i = sd->i; + while (l > 0) + { + if (off < sd->off + sd->cpiolen) + { + u = off - sd->off; + l2 = sd->cpiolen - u; + if (l2 > l) + l2 = l; + memcpy(bp, cpiodata + u, l2); + bp += l2; + off += l2; + l -= l2; + continue; + } + if (i == -1) + { + memset(bp, 0, l); + l = 0; + break; + } + if (off < sd->off + sd->cpiolen + sd->datalen) + { + u = off - (sd->off + sd->cpiolen); + if (S_ISLNK(fb->filemodes[i])) + { + l2 = sd->datalen - u; + if (l2 > l) + l2 = l; + if (u > strlen(symdata)) + memset(bp, 0, l2); + else + strncpy((char *)bp, symdata + u, l2); + } + else if (u < fb->filesizes[i]) + { + struct openfile *of; + l2 = fb->filesizes[i] - u; + if (l2 > l) + l2 = l; + if (!(of = sd->f)) + of = newopen(sd, fb); + if (!of) + { + fillblock_prelink(b, id, sd, fb, idx); + csdesc = -1; + return; + } + if (of->next) + { + of->next->prev = of->prev; + if (of->prev) + of->prev->next = of->next; + else + openfiles = of->next; + of->next = 0; + of->prev = openfilestail; + openfilestail->next = of; + openfilestail = of; + } + if (of->off != u) + { + if (lseek(of->fd, (off_t)u, SEEK_SET) == (off_t)-1) + { + fprintf(stderr, "%s: seek error\n", of->name); + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + } + if (read(of->fd, bp, l2) != l2) + { + fprintf(stderr, "%s: read error\n", of->name); + fprintf(stderr, "(tried to read %d bytes from offset %d)\n", l2, u); + fprintf(stderr, "cannot reconstruct rpm from disk files\n"); + exit(1); + } + of->off = u + l2; + } + else + { + l2 = sd->datalen - u; + if (l2 > l) + l2 = l; + memset(bp, 0, l2); + } + bp += l2; + off += l2; + l -= l2; + continue; + } + csdesc++; + sd++; + createcpiohead(sd, fb); + i = sd->i; + } + b->id = id; + b->type = BLK_CORE_REC; +} + +void +fillblock_rawrpm(struct blk *b, int id, struct seqdescr *sdesc, int nsdesc, struct fileblock *fb, int idx) +{ + unsigned char *bp; + unsigned int l2; + + for (;;) + { + bp = b->e.buf; + l2 = outfpleft_raw > BLKSIZE ? BLKSIZE : outfpleft_raw; + if (outfp->read(outfp, bp, l2) != l2) + { + fprintf(stderr, "read error"); + exit(1); + } + outfpleft_raw -= l2; + if (l2 < BLKSIZE) + memset(bp + l2, 0, BLKSIZE - l2); + b->type = BLK_CORE_ONE; + b->id = outfpid++; + if (b->id == id) + return; + if (b->id > id) + { + fprintf(stderr, "internal error, cannot rewind blocks (%d %d)\n", b->id, id); + exit(1); + } + if (maxblockuse[b->id] > idx) + pushblock(b, idx); + } +} + +void +fillblock_rpm(struct blk *b, int id, struct seqdescr *sdesc, int nsdesc, struct fileblock *fb, int idx) +{ + unsigned int size, nsize; + unsigned char *bp; + char *np; + int i; + unsigned int l, l2, u; + struct seqdescr *sd; + struct cpiophys cph; + static char *namebuf; + static int namebufl; + char skipbuf[4096]; + + l = BLKSIZE; + bp = b->e.buf; + for (;;) + { + if (outfpleft) + { + sd = sdesc + csdesc; + if (outfpleft > sd->datalen) + { + u = sd->cpiolen + sd->datalen - outfpleft; + l2 = sd->cpiolen - u; + if (l2 > l) + l2 = l; + memcpy(bp, cpiodata + u, l2); + bp += l2; + outfpleft -= l2; + l -= l2; + } + if (l && outfpleft) + { + l2 = outfpleft; + if (l2 > l) + l2 = l; + if (S_ISLNK(fb->filemodes[sd->i])) + { + strncpy((char *)bp, symdata, l2); + if (strlen(symdata) < l2) + symdata += strlen(symdata); + else + symdata += l2; + } + else + { + if (outfp->read(outfp, bp, l2) != l2) + { + fprintf(stderr, "read error"); + exit(1); + } + } + bp += l2; + outfpleft -= l2; + l -= l2; + } + } + if (l && csdesc >= 0 && sdesc[csdesc].i == -1) + { + memset(bp, 0, l); /* blocks are empty after trailer */ + l = 0; + } + if (l == 0) + { + b->type = BLK_CORE_ONE; + b->id = outfpid++; + if (b->id == id) + return; + if (b->id > id) + { + fprintf(stderr, "internal error, cannot rewind blocks (%d %d)\n", b->id, id); + exit(1); + } + if (maxblockuse[b->id] > idx) + pushblock(b, idx); + l = BLKSIZE; + bp = b->e.buf; + continue; + } + csdesc++; + sd = sdesc + csdesc; + i = sd->i; + if (i == -1) + { + createcpiohead(sd, fb); + outfpleft = sd->cpiolen + sd->datalen; + continue; + } + for (;;) + { + if (outfp->read(outfp, &cph, sizeof(cph)) != sizeof(cph)) + { + fprintf(stderr, "read error"); + exit(1); + } + if (memcmp(cph.magic, "070701", 6)) + { + fprintf(stderr, "read error: bad cpio archive\n"); + exit(1); + } + size = cpion(cph.filesize); + nsize = cpion(cph.namesize); + nsize += (4 - ((nsize + 2) & 3)) & 3; + if (nsize > namebufl) + { + namebuf = xrealloc(namebuf, nsize); + namebufl = nsize; + } + if (outfp->read(outfp, namebuf, nsize) != nsize) + { + fprintf(stderr, "read failed (name)\n"); + exit(1); + } + namebuf[nsize - 1] = 0; + if (!strcmp(namebuf, "TRAILER!!!")) + { + fprintf(stderr, "cpio end reached, bad rpm\n"); + exit(1); + } + np = namebuf; + if (*np == '.' && np[1] == '/') + np += 2; + if (!strcmp(fb->filenames[i][0] == '/' ? fb->filenames[i] + 1 : fb->filenames[i], np)) + break; + if (size & 3) + size += 4 - (size & 3); + while (size > 0) + { + l2 = size > sizeof(skipbuf) ? sizeof(skipbuf) : size; + if (outfp->read(outfp, skipbuf, l2) != l2) + { + fprintf(stderr, "read failed (name)\n"); + exit(1); + } + size -= l2; + } + } + createcpiohead(sd, fb); + if (size & 3) + size += 4 - (size & 3); + if (!S_ISREG(fb->filemodes[i])) + { + while (size > 0) + { + l2 = size > sizeof(skipbuf) ? sizeof(skipbuf) : size; + if (outfp->read(outfp, skipbuf, l2) != l2) + { + fprintf(stderr, "read failed (data skip)\n"); + exit(1); + } + size -= l2; + } + } + else if (size != sd->datalen) + { + fprintf(stderr, "cpio data size mismatch, bad rpm\n"); + exit(1); + } + outfpleft = sd->cpiolen + sd->datalen; + } +} + + +/* construct the block "id". Note that the tupel (idx, id) will + * only get bigger, so we use this to recycly no longer needed + * blocks */ + +struct blk * +getblock(int id, struct seqdescr *sdesc, int nsdesc, struct fileblock *fb, int idx) +{ + struct blk *b, **bb; + struct blk *pb; + static int cleanup_cnt; + +// printf("%d %d %d\n", idx, id, maxblockuse[id]); + b = vmem[id]; + if (b && (b->type == BLK_CORE_REC || b->type == BLK_CORE_ONE)) + return b; + + b = freecoreblks; + if (b) + { + freecoreblks = b->next; + b->next = coreblks; + coreblks = b; + } + + if (!b && ncoreblk < maxcoreblk && (++cleanup_cnt & 7) != 0) + b = newcoreblk(); + + if (!b) + { + for (bb = &coreblks; (b = *bb) != 0; bb = &b->next) + { + if (maxblockuse[b->id] < idx || (maxblockuse[b->id] == idx && b->id < id)) + { + *bb = b->next; + vmem[b->id] = 0; + b->type = BLK_FREE; + b->next = freecoreblks; + freecoreblks = b; + } + else + bb = &b->next; + } + b = freecoreblks; + if (b) + { + freecoreblks = b->next; + b->next = coreblks; + coreblks = b; + } + } + + if (!b && ncoreblk < maxcoreblk) + b = newcoreblk(); + if (!b) + { + /* use first created block */ + for (bb = &coreblks; (b = *bb); bb = &b->next) + if (b->next == 0) + break; + *bb = 0; + b->next = coreblks; + coreblks = b; + if (b->type == BLK_CORE_ONE) + pageoutblock(b, idx); + else + { + vmem[b->id] = 0; + ndropblk++; + } + b->type = BLK_FREE; + } + + /* got destination block, now fill it with data */ + pb = vmem[id]; + if (pb && pb->type == BLK_PAGE) + { + pageinblock(b, pb); + return b; + } + fillblock_method(b, id, sdesc, nsdesc, fb, idx); + vmem[id] = b; + return b; +} + +int +cfile_write_uncomp(struct cfile *f, void *buf, int len) +{ + int l2; + if (!len) + return 0; + l2 = len > f->len ? f->len : len; + if (fwrite(buf, l2, 1, (FILE *)f->fp) != 1) + return -1; + if (l2 && f->ctxup) + f->ctxup(f->ctx, buf, l2); + f->len -= l2; + if (f->len) + return l2; + f = cfile_open(CFILE_OPEN_WR, CFILE_IO_REOPEN, f, f->comp, CFILE_LEN_UNLIMITED, f->ctxup, f->ctx); + if (!f) + { + fprintf(stderr, "payload re-open error\n"); + exit(1); + } + if (f->write(f, buf + l2, len - l2) != len - l2) + return -1; + return len; +} + +typedef struct { + union { + MD5_CTX md5ctx; + SHA256_ctx sha256ctx; + } ctx; +} DIG_CTX; + + +static inline void +DIG_Init(DIG_CTX *ctx, int digestalgo) +{ + if (digestalgo == 1) + rpmMD5Init(&ctx->ctx.md5ctx); + else if (digestalgo == 8) + SHA256_init(&ctx->ctx.sha256ctx); +} + +static inline void +DIG_Update(DIG_CTX *ctx, int digestalgo, unsigned char const *buf, unsigned len) +{ + if (digestalgo == 1) + rpmMD5Update(&ctx->ctx.md5ctx, buf, len); + else if (digestalgo == 8) + SHA256_update(&ctx->ctx.sha256ctx, buf, len); +} + +static inline void +DIG_Final(DIG_CTX *ctx, int digestalgo, unsigned char *digest) +{ + if (digestalgo == 1) + rpmMD5Final(digest, &ctx->ctx.md5ctx); + else if (digestalgo == 8) + { + SHA256_final(&ctx->ctx.sha256ctx); + SHA256_digest(&ctx->ctx.sha256ctx, digest); + } + else + *digest = 0; +} + +static inline int +DIG_Len(int digestalgo) +{ + if (digestalgo == 1) + return 16; + if (digestalgo == 8) + return 32; + return 0; +} + +int +checkprelinked(char *name, int digestalgo, unsigned char *hmd5, unsigned int size) +{ + int fd, l; + unsigned char buf[4096]; + DIG_CTX ctx; + unsigned char md5[32]; + + nprelink++; + if ((fd = prelinked_open(name)) < 0) + { + perror(name); + return -1; + } + DIG_Init(&ctx, digestalgo); + while (size && (l = read(fd, buf, sizeof(buf))) > 0) + { + if (l > size) + l = size; + DIG_Update(&ctx, digestalgo, buf, l); + size -= l; + } + close(fd); + DIG_Final(&ctx, digestalgo, md5); + if (memcmp(md5, hmd5, DIG_Len(digestalgo))) + { + fprintf(stderr, "%s: contents have been changed\n", name); + return -1; + } + return 0; +} + +int +checkfilemd5(char *name, int digestalgo, unsigned char *hmd5, unsigned int size) +{ + int fd, l; + unsigned char buf[4096]; + DIG_CTX ctx; + unsigned char md5[32]; + struct stat stb; + + if ((fd = open(name, O_RDONLY)) < 0 || fstat(fd, &stb)) + { + perror(name); + return -1; + } + DIG_Init(&ctx, digestalgo); + if (stb.st_size > size && (l = read(fd, buf, sizeof(buf))) > 0) + { + if (is_prelinked(fd, buf, l)) + { + close(fd); + return checkprelinked(name, digestalgo, hmd5, size); + } + if (l > size) + l = size; + DIG_Update(&ctx, digestalgo, buf, l); + size -= l; + } + while (size && (l = read(fd, buf, sizeof(buf))) > 0) + { + if (l > size) + l = size; + DIG_Update(&ctx, digestalgo, buf, l); + size -= l; + } + close(fd); + DIG_Final(&ctx, digestalgo, md5); + if (memcmp(md5, hmd5, DIG_Len(digestalgo))) + { + fprintf(stderr, "%s: contents have been changed\n", name); + return -1; + } + return 0; +} + +int +checkfilesize(char *name, int digestalgo, unsigned char *hmd5, unsigned int size) +{ + struct stat stb; + unsigned char buf[128]; + int l; + + if (stat(name, &stb) == -1) + { + perror(name); + return -1; + } + if (stb.st_size == size) + return 0; + if (stb.st_size > size) + { + int fd; + fd = open(name, O_RDONLY); + if (fd != -1 && (l = read(fd, buf, sizeof(buf))) > 0 && is_prelinked(fd, buf, l)) + { + close(fd); + return checkprelinked(name, digestalgo, hmd5, size); + } + if (fd != -1) + close(fd); + } + fprintf(stderr, "%s: contents have been changed\n", name); + return -1; +} + +/***************************************************************** + * main program + */ + +int +main(int argc, char **argv) +{ + int c, i; + char *deltarpm; + struct rpmhead *h; + int fd; + struct cfile *bfp = 0; + struct cfile *obfp; + char *fnevr; + unsigned int inn; + unsigned int *in; + unsigned int outn; + unsigned int *out; + drpmuint off, paywritten; + struct fileblock fb; + struct seqdescr *sdesc; + int nsdesc; + struct blk *lastblk = 0; + int idx; + int on; + unsigned int len, l; + int bs, be; + unsigned char buf[4096]; + MD5_CTX wrmd5; + unsigned char wrmd5res[16]; + int nofullmd5 = 0; + FILE *ofp; + int numblks; + int percent = 0; + int curpercent; + int lastpercent = -1; + int verbose = 0; + int seqcheck = 0; + int check = 0; + int checkflags = 0; + int info = 0; + bz_stream addbz2strm; + z_stream addgzstrm; + int addblkcomp; + unsigned char *addblkbuf = 0; + unsigned char *b; + int seqmatches = 1; + FILE *vfp; + struct deltarpm d; + char *arch = 0; + + while ((c = getopt(argc, argv, "cCisvpr:a:")) != -1) + { + switch(c) + { + case 'v': + verbose++; + break; + case 'p': + percent++; + break; + case 'r': + fromrpm = optarg; + break; + case 's': + check = 1; + seqcheck = 1; + break; + case 'i': + info = 1; + break; + case 'c': + checkflags = SEQCHECK_MD5; + check = 1; + break; + case 'C': + checkflags = SEQCHECK_SIZE; + check = 1; + break; + case 'a': + arch = optarg; + break; + default: + fprintf(stderr, "usage: applydeltarpm [-r <rpm>] deltarpm rpm\n"); + exit(1); + } + } + + if (optind + (check || info ? 1 : 2) != argc) + { + fprintf(stderr, "usage: applydeltarpm [-r <rpm>] deltarpm rpm\n"); + exit(1); + } + if (checkflags && fromrpm) + { + fprintf(stderr, "on-disk checking does not work with the -r option.\n"); + exit(1); + } + + vfp = !(check || info) && !strcmp(argv[argc - 1], "-") ? stderr : stdout; + + deltarpm = argv[optind]; + + bfp = 0; + if (seqcheck) + { + char *hex; + int nevrl; + + if (info) + { + fprintf(stderr, "need real delta-rpm for info\n"); + exit(1); + } + memset(&d, 0, sizeof(d)); + d.name = deltarpm; + d.seql = strlen(deltarpm); + if (d.seql < 34 || (hex = strrchr(deltarpm, '-')) == 0) + { + fprintf(stderr, "%s: bad sequence\n", deltarpm); + exit(1); + } + d.nevr = deltarpm; + nevrl = hex - deltarpm; + d.seql -= nevrl + 1; + *hex++ = 0; + d.seql = (d.seql + 1) / 2; + d.seq = xmalloc(d.seql); + if (parsehex(hex, d.seq, d.seql) != d.seql) + { + fprintf(stderr, "bad sequence\n"); + exit(1); + } + } + else + { + if (verbose) + fprintf(vfp, "reading deltarpm\n"); + readdeltarpm(deltarpm, &d, &bfp); + nofullmd5 = !memcmp("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", d.targetmd5, 16); +#ifdef DELTARPM_64BIT + if (d.outlen >= 0xffffffffULL << BLKSHIFT) + { + fprintf(stderr, "cpio size too big\n"); + exit(1); + } +#endif + numblks = (unsigned int)(d.outlen >> BLKSHIFT); + if ((d.outlen & (BLKSIZE - 1)) != 0) + numblks++; + + maxblockuse = xcalloc(numblks, sizeof(unsigned int)); + vmem = xcalloc(numblks, sizeof(struct blk *)); + + if (verbose > 1) + { + fprintf(vfp, "%llu bytes source payload size\n", (unsigned long long)d.outlen); + fprintf(vfp, "%llu bytes target payload size\n", (unsigned long long)d.paylen); + fprintf(vfp, "%llu bytes internal data size\n", (unsigned long long)d.inlen); + fprintf(vfp, "%u bytes add data size\n", d.addblklen); + fprintf(vfp, "%d blocks\n", numblks); + fprintf(vfp, "%d copy instructions\n", d.inn + d.outn); + } + off = 0; + for (i = 0; i < d.outn; i++) + { + off += (int)d.out[2 * i]; + bs = off >> BLKSHIFT; + off += d.out[2 * i + 1]; + be = (off - 1) >> BLKSHIFT; + for (; bs <= be; bs++) + maxblockuse[bs] = i; + } + } + + addblkcomp = CFILE_COMP_BZ; + if (d.addblklen > 9 && d.addblk[0] == 0x1f && d.addblk[1] == 0x8b) + addblkcomp = CFILE_COMP_GZ; + else if (d.addblklen > 3 && (d.addblk[0] == 255 && d.addblk[1] == 'L' && d.addblk[2] == 'Z')) + addblkcomp = CFILE_COMP_LZMA; + else if (d.addblklen > 6 && (d.addblk[0] == 0xfd && d.addblk[1] == '7' && d.addblk[2] == 'z' && d.addblk[3] == 'X' && d.addblk[4] == 'Z')) + addblkcomp = CFILE_COMP_XZ; + if (info) + { + unsigned int *size; + if (d.version) + printf("deltarpm version: %c\n", d.version & 0xff); + printf("deltarpm type: %s\n", d.h ? "standard" : d.targetcomp != CFILE_COMP_UN || d.inn != 0 || d.outn != 0 ? "rpm-only" : "rpm-only, no diff"); + printf("deltarpm compression: %s\n", cfile_comp2str(d.deltacomp)); + printf("sequence: %s-", d.nevr); + for (i = 0; i < d.seql; i++) + printf("%02x", d.seq[i]); + putchar('\n'); + printf("source rpm: %s\n", d.nevr); + if (d.h || d.targetcomp != CFILE_COMP_UN || d.inn != 0 || d.outn != 0) + printf("source payload size: %llu\n", (unsigned long long)d.outlen); + printf("target rpm: %s\n", d.targetnevr); + if (d.h || d.targetcomp != CFILE_COMP_UN || d.inn != 0 || d.outn != 0) + { + printf("target payload size: %llu\n", (unsigned long long)d.paylen); + if (d.targetcomp != CFILE_COMP_XX) + printf("target payload compression: %s\n", cfile_comp2str(d.targetcomp)); + } + if (d.targetsize == 0) + { + struct rpmhead *dsigh = readhead_buf(d.lead + 96, d.leadl - 96, 0); + if (dsigh && (size = headint32(dsigh, 1000, (int *)0)) != 0) + { + d.targetsize = d.leadl + *size; + free(size); + } + xfree(dsigh); + } + if (d.targetsize) + printf("target size: %u\n", d.targetsize); + printf("target md5: "); + for (i = 0; i < 16; i++) + printf("%02x", d.targetmd5[i]); + putchar('\n'); + if (d.h || d.targetcomp != CFILE_COMP_UN || d.inn != 0 || d.outn != 0) + { + printf("internal data size: %llu\n", (unsigned long long)d.inlen); + printf("compressed add data size: %d\n", d.addblklen); + if (d.addblklen) + printf("compressed add data compression: %s\n", cfile_comp2str(addblkcomp)); + printf("instructions: %d\n", d.inn + d.outn); + } + if (bfp) + bfp->close(bfp); + exit(0); + } + + if (d.targetcompparalen) + { + fprintf(stderr, "deltarpm contains unknown compression parameters\n"); + exit(1); + } + + if (!fromrpm) + { + pid_t pid; + int pi[2]; + + if (!seqcheck && !d.h) + { + fprintf(stderr, "this deltarpm does not work from filesystem, use '-r <oldrpm>'.\n"); + exit(1); + } + if (!seqcheck && !headstring(d.h, TAG_SOURCERPM)) + { + fprintf(stderr, "cannot reconstruct source rpms from filesystem\n"); + exit(1); + } + if (pipe(pi)) + { + perror("pipe"); + exit(1); + } + if ((pid = fork()) == (pid_t)-1) + { + perror("fork"); + exit(1); + } + if (pid == 0) + { + close(pi[0]); + if (pi[1] != 1) + { + dup2(pi[1], 1); + close(pi[1]); + } + if (arch) + execlp(RPMDUMPHEADER, RPMDUMPHEADER, "-a", arch, d.nevr, (char *)0); + else + execlp(RPMDUMPHEADER, RPMDUMPHEADER, d.nevr, (char *)0); + perror(RPMDUMPHEADER); + _exit(1); + } + close(pi[1]); + fd = pi[0]; + } + else + { + unsigned char rpmlead[96]; + + if ((fd = open(fromrpm, O_RDONLY)) < 0) + { + perror(fromrpm); + 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", fromrpm); + 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", fromrpm); + exit(1); + } + h = readhead(fd, 1); + if (!h) + { + fprintf(stderr, "could not read signature header\n"); + exit(1); + } + if (!d.h) + { + unsigned char *hmd5 = headbin(h, 1004, 16); + if (!hmd5 || memcmp(hmd5, d.seq, 16) != 0) + seqmatches = 0; + if (seqcheck) + { + /* we don't know if this is a rpm-only deltarpm or not, + * so we assume yes if seqmatches is true */ + if (seqmatches && d.seql == 16) + seqcheck = 0; /* assume rpm-only, no expandseq */ + else + seqmatches = 1; /* assume normal, run expandseq */ + } + } + free(h); + } + h = readhead(fd, 0); + if (!h) + { + if (fromrpm) + fprintf(stderr, "could not read header\n"); + exit(1); + } + fnevr = headtonevr(h); + if (strcmp(fnevr, (char *)d.nevr) != 0) + { + fprintf(stderr, "delta rpm made for %s, not %s\n", d.nevr, fnevr); + exit(1); + } + if (!seqmatches) + { + fprintf(stderr, "rpm does not match the one used for creating the deltarpm\n"); + exit(1); + } + if (d.h || seqcheck) + { + int (*checkfunc)(char *, int, unsigned char *, unsigned int); + if (headtofb(h, &fb)) + { + fprintf(stderr, "bad header\n"); + exit(1); + } + checkfunc = 0; + if ((checkflags & SEQCHECK_MD5) != 0) + checkfunc = checkfilemd5; + else if ((checkflags & SEQCHECK_SIZE) != 0) + checkfunc = checkfilesize; + sdesc = expandseq(d.seq, d.seql, &nsdesc, &fb, checkfunc); + if (!sdesc) + { + fprintf(stderr, "could not expand sequence data\n"); + exit(1); + } + } + else + { + nsdesc = 0; + sdesc = 0; + } + if (!fromrpm) + { + int status; + close(fd); + wait(&status); + } + if (check) + exit(0); + + l = 0; + for (i = 0; i < nsdesc; i++) + if (sdesc[i].cpiolen > l) + l = sdesc[i].cpiolen; + if (l < 124) + l = 124; /* room for tailer */ + cpiodata = xmalloc(l + 4); /* extra room for padding */ + + + rpmMD5Init(&wrmd5); + if (!strcmp(argv[optind + 1], "-")) + ofp = stdout; + else if ((ofp = fopen(argv[optind + 1], "w")) == 0) + { + perror(argv[optind + 1]); + exit(1); + } + if (fwrite(d.lead, d.leadl, 1, ofp) != 1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + if (!nofullmd5) + rpmMD5Update(&wrmd5, d.lead, d.leadl); + if (!d.h) + fromrpm_raw = 1; + + if (fromrpm_raw && d.targetcomp == CFILE_COMP_UN && d.inn == 0 && d.outn == 0) + { + /* no diff, copy-through mode */ + if (fwrite(h->intro, 16, 1, ofp) != 1 || fwrite(h->data, 16 * h->cnt + h->dcnt, 1, ofp) != 1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + rpmMD5Update(&wrmd5, h->intro, 16); + rpmMD5Update(&wrmd5, h->data, 16 * h->cnt + h->dcnt); + while ((l = read(fd, buf, sizeof(buf))) > 0) + { + if (fwrite(buf, l, 1, ofp) != 1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + rpmMD5Update(&wrmd5, buf, l); + } + if (fflush(ofp) || (ofp != stdout && fclose(ofp) != 0)) + { + fprintf(stderr, "write error\n"); + exit(1); + } + rpmMD5Final(wrmd5res, &wrmd5); + if (nofullmd5) + { + struct rpmhead *dsigh = readhead_buf(d.lead + 96, d.leadl - 96, 0); + if (dsigh) + { + unsigned char *hmd5 = headbin(dsigh, SIGTAG_MD5, 16); + if (hmd5) + { + if (memcmp(wrmd5res, hmd5, 16) != 0) + { + fprintf(stderr, "%s: md5 mismatch of result\n", deltarpm); + exit(1); + } + } + xfree(dsigh); + } + } + else if (memcmp(wrmd5res, d.targetmd5, 16) != 0) + { + fprintf(stderr, "%s: md5 mismatch of result\n", deltarpm); + exit(1); + } + exit(0); + } + + if (!fromrpm_raw) + { + if (fwrite(d.h->intro, 16, 1, ofp) != 1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + rpmMD5Update(&wrmd5, d.h->intro, 16); + strncpy((char *)d.h->dp + d.payformatoff, "cpio", 4); + if (fwrite(d.h->data, 16 * d.h->cnt + d.h->dcnt, 1, ofp) != 1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + rpmMD5Update(&wrmd5, d.h->data, 16 * d.h->cnt + d.h->dcnt); + } + + if (fromrpm) + { + if ((outfp = cfile_open(CFILE_OPEN_RD, fd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, 0, 0)) == 0) + { + fprintf(stderr, "%s: payload open failed\n", deltarpm); + exit(1); + } + } + + if (d.addblklen) + { + switch (addblkcomp) + { + case CFILE_COMP_GZ: + if (memcmp(d.addblk, "\037\213\010\0\0\0\0\0\0\003", 10) != 0) + { + fprintf(stderr, "addblk: unsupported gz stream\n"); + exit(1); + } + addgzstrm.zalloc = NULL; + addgzstrm.zfree = NULL; + addgzstrm.opaque = NULL; + if (inflateInit2(&addgzstrm, -MAX_WBITS) != Z_OK) + { + fprintf(stderr, "addblk: inflateInit2 error\n"); + exit(1); + } + addgzstrm.next_in = d.addblk + 10; + addgzstrm.avail_in = d.addblklen - 10; + break; + default: + addbz2strm.bzalloc = NULL; + addbz2strm.bzfree = NULL; + addbz2strm.opaque = NULL; + if (BZ2_bzDecompressInit(&addbz2strm, 0, 0) != BZ_OK) + { + fprintf(stderr, "addblk: BZ2_bzDecompressInit error\n"); + exit(1); + } + addbz2strm.next_in = (char *)d.addblk; + addbz2strm.avail_in = d.addblklen; + break; + } + addblkbuf = xmalloc(BLKSIZE); + } + + obfp = cfile_open(CFILE_OPEN_WR, CFILE_IO_FILE, ofp, d.compheadlen ? CFILE_COMP_UN : d.targetcomp, CFILE_LEN_UNLIMITED, (cfile_ctxup)rpmMD5Update, &wrmd5); + if (!obfp) + { + fprintf(stderr, "payload write error\n"); + exit(1); + } + if (d.compheadlen) + { + obfp->comp = d.targetcomp; + obfp->len = d.compheadlen; + obfp->write = cfile_write_uncomp; + } + if (fromrpm) + fillblock_method = fillblock_rpm; + else + fillblock_method = fillblock_disk; + if (fromrpm_raw) + { + fillblock_method = fillblock_rawrpm; + if (outfp->unread(outfp, h->data, 16 * h->cnt + h->dcnt) || outfp->unread(outfp, h->intro, 16)) + { + fprintf(stderr, "could not unread header\n"); + exit(1); + } + outfpleft_raw = d.outlen; + } + if (verbose) + fprintf(vfp, "applying delta\n"); + idx = 0; + paywritten = 0; + inn = d.inn; + outn = d.outn; + in = d.in; + out = d.out; + off = 0; + while (inn > 0) + { + on = *in++; + if (on > outn) + { + fprintf(stderr, "corrupt delta instructions\n"); + exit(1); + } + while (on > 0) + { + off += (int)*out++; + len = *out++; + paywritten += len; + outn--; + on--; + bs = off >> BLKSHIFT; + while (len > 0) + { + if (!lastblk || bs != lastblk->id) + { + lastblk = vmem[bs]; + if (!lastblk || lastblk->type == BLK_PAGE) + lastblk = getblock(bs, sdesc, nsdesc, &fb, idx); + } + l = off & BLKMASK; + if (l + len > BLKSIZE) + l = BLKSIZE - l; + else + l = len; + b = lastblk->e.buf + (off & BLKMASK); + if (d.addblklen) + { + if (addblkcomp == CFILE_COMP_GZ) + { + addgzstrm.next_out = addblkbuf; + addgzstrm.avail_out = l; + inflate(&addgzstrm, Z_NO_FLUSH); + if (addgzstrm.avail_out != 0) + { + fprintf(stderr, "addblk: inflate error\n"); + exit(1); + } + } + else + { + addbz2strm.next_out = (char *)addblkbuf; + addbz2strm.avail_out = l; + BZ2_bzDecompress(&addbz2strm); + if (addbz2strm.avail_out != 0) + { + fprintf(stderr, "addblk: BZ2_bzDecompress error\n"); + exit(1); + } + } + for (i = 0; i < l; i++) + addblkbuf[i] += b[i]; + b = addblkbuf; + } + if (obfp->write(obfp, b, l) != l) + { + fprintf(stderr, "write error\n"); + exit(1); + } + len -= l; + off += l; + bs++; + } + idx++; + if (percent) + { + if (d.paylen >= 0x1000000) + curpercent = (paywritten >> 8) * 100 / (d.paylen >> 8); + else if (d.paylen) + curpercent = paywritten * 100 / d.paylen; + else + curpercent = 0; + if (curpercent != lastpercent) + { + if (percent > 1) + fprintf(vfp, "%d percent finished.\n", curpercent); + else + fprintf(vfp, "\r%d percent finished.", curpercent); + fflush(vfp); + lastpercent = curpercent; + } + } + } + len = *in++; + paywritten += len; + while (len > 0) + { + l = len > sizeof(buf) ? sizeof(buf) : len; + if (bfp->read(bfp, buf, l) != l) + { + fprintf(stderr, "%s: read error data area\n", deltarpm); + exit(1); + } + if (obfp->write(obfp, buf, l) != l) + { + fprintf(stderr, "write error\n"); + exit(1); + } + len -= l; + } + inn--; + if (percent) + { + curpercent = paywritten * 100. / (d.paylen ? d.paylen : 1); + if (curpercent != lastpercent) + { + if (percent > 1) + fprintf(vfp, "%d percent finished.\n", curpercent); + else + fprintf(vfp, "\r%d percent finished.", curpercent); + fflush(vfp); + lastpercent = curpercent; + } + } + } + if (percent > 1) + fprintf(vfp, "100 percent finished.\n"); + else if (percent) + fprintf(vfp, "\r100 percent finished.\n"); + if (obfp->close(obfp) == -1) + { + fprintf(stderr, "write error\n"); + exit(1); + } + if (fflush(ofp) || (ofp != stdout && fclose(ofp) != 0)) + { + fprintf(stderr, "write error\n"); + exit(1); + } + if (outfp) + outfp->close(outfp); + if (bfp) + bfp->close(bfp); + if (d.addblklen) + { + if (addblkcomp == CFILE_COMP_GZ) + inflateEnd(&addgzstrm); + else + BZ2_bzDecompressEnd(&addbz2strm); + } + if (verbose > 1) + { + fprintf(vfp, "used %d core pages\n", ncoreblk); + fprintf(vfp, "used %d swap pages\n", npageblk); + fprintf(vfp, "had to recreate %d core pages\n", ndropblk); + if (nprelink) + fprintf(vfp, "had to call prelink %d times\n", nprelink); + } + rpmMD5Final(wrmd5res, &wrmd5); + if (nofullmd5) + { + struct rpmhead *dsigh = readhead_buf(d.lead + 96, d.leadl - 96, 0); + if (dsigh) + { + unsigned char *hmd5 = headbin(dsigh, SIGTAG_MD5, 16); + if (hmd5) + { + if (memcmp(wrmd5res, hmd5, 16) != 0) + { + fprintf(stderr, "%s: md5 mismatch of result\n", deltarpm); + exit(1); + } + } + xfree(dsigh); + } + } + else if (memcmp(wrmd5res, d.targetmd5, 16) != 0) + { + fprintf(stderr, "%s: md5 mismatch of result\n", deltarpm); + exit(1); + } + exit(0); +} |