summaryrefslogtreecommitdiff
path: root/applydeltarpm.c
diff options
context:
space:
mode:
Diffstat (limited to 'applydeltarpm.c')
-rw-r--r--applydeltarpm.c1842
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);
+}