summaryrefslogtreecommitdiff
path: root/fragiso.c
diff options
context:
space:
mode:
Diffstat (limited to 'fragiso.c')
-rw-r--r--fragiso.c1288
1 files changed, 1288 insertions, 0 deletions
diff --git a/fragiso.c b/fragiso.c
new file mode 100644
index 0000000..6487886
--- /dev/null
+++ b/fragiso.c
@@ -0,0 +1,1288 @@
+/*
+ * Copyright (c) 2005 Michael Schroeder (mls@suse.de)
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <zlib.h>
+#include <lzma.h>
+#include <bzlib.h>
+
+#include "rpmhead.h"
+#include "md5.h"
+#include "util.h"
+#include "cfile.h"
+
+struct rpmpay {
+ char *name;
+ unsigned int x;
+ unsigned int lx;
+ off64_t o;
+ unsigned int l;
+ unsigned char lmd5[16];
+ unsigned char hmd5[16];
+};
+
+static unsigned int
+get4(unsigned char *p)
+{
+ return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+}
+
+static unsigned int
+get4n(unsigned char *p)
+{
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+static void
+readblk(FILE *fp, unsigned char *blk, unsigned int num)
+{
+ if (fseeko64(fp, 0x800 * (off64_t)num, SEEK_SET) != 0)
+ {
+ perror("fseeko");
+ exit(1);
+ }
+ if (fread(blk, 0x800, 1, fp) != 1)
+ {
+ perror("fread");
+ exit(1);
+ }
+}
+
+static struct rpmpay *rpmpays;
+static int rpmpayn;
+
+static void
+addrpm(char *name, off64_t o, unsigned int l, unsigned char *lmd5, unsigned char *hmd5)
+{
+ char *n;
+ if ((rpmpayn & 31) == 0)
+ {
+ if (rpmpays == 0)
+ rpmpays = malloc(32 * sizeof(*rpmpays));
+ else
+ rpmpays = realloc(rpmpays, (rpmpayn + 32) * sizeof(*rpmpays));
+ if (!rpmpays)
+ {
+ fprintf(stderr, "out of mem\n");
+ exit(1);
+ }
+ }
+ n = name ? strdup(name) : 0;
+ if (name && !n)
+ {
+ fprintf(stderr, "out of mem\n");
+ exit(1);
+ }
+ rpmpays[rpmpayn].name = n;
+ rpmpays[rpmpayn].o = o;
+ rpmpays[rpmpayn].l = l;
+ rpmpays[rpmpayn].x = 0;
+ rpmpays[rpmpayn].lx = 0;
+ if (lmd5)
+ memcpy(rpmpays[rpmpayn].lmd5, lmd5, 16);
+ else
+ memset(rpmpays[rpmpayn].lmd5, 0, 16);
+ if (hmd5)
+ memcpy(rpmpays[rpmpayn].hmd5, hmd5, 16);
+ else
+ memset(rpmpays[rpmpayn].hmd5, 0, 16);
+ rpmpayn++;
+}
+
+static int
+sortrpmcmp(const void *av, const void *bv)
+{
+ const struct rpmpay *a = av;
+ const struct rpmpay *b = bv;
+ if (a->o < b->o)
+ return -1;
+ if (a->o > b->o)
+ return 1;
+ return strcmp(a->name, b->name);
+}
+
+static void
+sortrpm()
+{
+ if (!rpmpayn)
+ return;
+ qsort(rpmpays, rpmpayn, sizeof(*rpmpays), sortrpmcmp);
+}
+
+void
+readrpm(char *name, FILE *fp, unsigned int filepos, unsigned int filelen)
+{
+ static unsigned int rpmbalen;
+ static unsigned char *rpmb;
+ int rpmblen;
+ int len, hcnt, hdcnt, hoffset;
+ struct rpmhead *h, *sigh;
+ char *rpmn, *rpma;
+ unsigned char lmd5[16], *hmd5;
+ char nbuf[256 * 3];
+ char *suf;
+ MD5_CTX ctx;
+ unsigned int filepos2 = filepos;
+
+ len = strlen(name);
+ suf = "";
+ if (len > 10 && !strcmp(name + len - 10, ".delta.rpm"))
+ suf = ".delta";
+ else if (len > 10 && !strcmp(name + len - 10, ".patch.rpm"))
+ suf = ".patch";
+
+ if (!rpmb)
+ {
+ rpmbalen += 0x800 * 4;
+ rpmb = xrealloc(rpmb, rpmbalen);
+ }
+ readblk(fp, rpmb, filepos++);
+ rpmblen = 0x800;
+
+ if (get4(rpmb) != 0xdbeeabed)
+ return; /* not really a rpm */
+
+ if (get4(rpmb + 0x60) != 0x01e8ad8e)
+ {
+ fprintf(stderr, "bad rpm (bad sigheader): %s\n", name);
+ exit(1);
+ }
+ hcnt = get4n(rpmb + 0x68);
+ hdcnt = get4n(rpmb + 0x6c);
+ if ((hdcnt & 7) != 0)
+ hdcnt += 8 - (hdcnt & 7);
+ len = 0x60 + 16 + hcnt * 16 + hdcnt + 16;
+ if (len > filelen)
+ {
+ fprintf(stderr, "bad rpm (EOF): %s\n", name);
+ exit(1);
+ }
+ while (rpmblen < len)
+ {
+ if (rpmblen + 0x800 > rpmbalen)
+ {
+ rpmbalen += 0x800 * 4;
+ rpmb = xrealloc(rpmb, rpmbalen);
+ }
+ readblk(fp, rpmb + rpmblen, filepos++);
+ rpmblen += 0x800;
+ }
+ sigh = readhead_buf(rpmb + 0x60, len - 16, 1);
+ if (!sigh)
+ {
+ fprintf(stderr, "bad rpm (bad sigh): %s\n", name);
+ exit(1);
+ }
+ hoffset = len - 16;
+ if (get4(rpmb + hoffset) != 0x01e8ad8e)
+ {
+ fprintf(stderr, "bad rpm (bad header): %s\n", name);
+ exit(1);
+ }
+ hcnt = get4n(rpmb + hoffset + 8);
+ hdcnt = get4n(rpmb + hoffset + 12);
+ len += hcnt * 16 + hdcnt;
+ if (len > filelen)
+ {
+ fprintf(stderr, "bad rpm (EOF): %s\n", name);
+ exit(1);
+ }
+ while (rpmblen < len)
+ {
+ if (rpmblen + 0x800 > rpmbalen)
+ {
+ rpmbalen += 0x800 * 4;
+ rpmb = xrealloc(rpmb, rpmbalen);
+ }
+ readblk(fp, rpmb + rpmblen, filepos++);
+ rpmblen += 0x800;
+ }
+ h = readhead_buf(rpmb + hoffset, 16 + hcnt * 16 + hdcnt, 0);
+ if (!h)
+ {
+ fprintf(stderr, "bad rpm (bad h): %s\n", name);
+ exit(1);
+ }
+ /* ok, all header are read in, extract information */
+ rpmn = headstring(h, TAG_NAME);
+ if (!rpmn)
+ {
+ fprintf(stderr, "bad rpm (header contains no name): %s\n", name);
+ exit(1);
+ }
+ rpma = headstring(h, TAG_ARCH);
+ if (!rpma)
+ {
+ fprintf(stderr, "bad rpm (header contains no arch): %s\n", name);
+ exit(1);
+ }
+ if (strlen(rpmn) > 256 || strchr(rpmn, ' ') || strchr(rpmn, '/') || strchr(rpmn, '\n'))
+ rpmn = "unknown";
+ if (!headstring(h, TAG_SOURCERPM))
+ {
+ if (headtagtype(h, TAG_NOSOURCE) || headtagtype(h, TAG_NOPATCH))
+ rpma = "nosrc";
+ else
+ rpma = "src";
+ }
+ if (strlen(rpma) > 256 || strchr(rpma, ' ') || strchr(rpma, '/') || strchr(rpma, '\n'))
+ rpma = "unknown";
+ hmd5 = headbin(sigh, SIGTAG_MD5, 16);
+ if (!hmd5)
+ {
+ fprintf(stderr, "bad rpm (signature contains no md5 entry): %s\n", name);
+ exit(1);
+ }
+ sprintf(nbuf, "%s.%s%s", rpmn, rpma, suf);
+ rpmMD5Init(&ctx);
+ rpmMD5Update(&ctx, rpmb, hoffset);
+ rpmMD5Final(lmd5, &ctx);
+ addrpm(nbuf, 0x800 * (off64_t)filepos2, filelen, lmd5, hmd5);
+ h = xfree(h);
+ sigh = xfree(sigh);
+}
+
+int
+rpmoffs(FILE *fp, char *isoname, struct rpmpay **retp)
+{
+ unsigned char blk[0x800];
+ unsigned char blk2[0x800];
+ unsigned char name[256];
+ int namel;
+ unsigned int filepos, filelen;
+ unsigned char *pt, *ep;
+ int i, j, l, nl, el, nml, nmf;
+
+ unsigned int path_table_size;
+ unsigned int path_table_pos;
+ unsigned int dirpos, dirlen;
+ int sp_bytes_skip = 0;
+ unsigned int ce_blk;
+ unsigned int ce_off;
+ unsigned int ce_len;
+
+ readblk(fp, blk, 16);
+ if (memcmp(blk, "\001CD001", 6))
+ {
+ fprintf(stderr, "primary volume descriptor missing\n");
+ exit(1);
+ }
+ path_table_size = get4(blk + 132);
+ path_table_pos = get4(blk + 140);
+ pt = malloc(path_table_size);
+ if (!pt)
+ {
+ fprintf(stderr, "out of mem\n");
+ exit(1);
+ }
+ readblk(fp, blk, path_table_pos);
+ if (fseeko64(fp, 0x800 * (off64_t)path_table_pos, SEEK_SET) != 0)
+ {
+ perror("fseeko64");
+ exit(1);
+ }
+ if (fread(pt, path_table_size, 1, fp) != 1)
+ {
+ perror("fread");
+ exit(1);
+ }
+ for (i = 0; i < path_table_size; )
+ {
+ l = pt[i];
+ if (l == 0)
+ {
+ fprintf(stderr, "empty dir in path table\n");
+ exit(1);
+ }
+ dirpos = get4(pt + i + 2);
+ i += 8 + l + (l & 1);
+ readblk(fp, blk, dirpos);
+ dirlen = get4(blk + 10);
+ if (dirlen & 0x7ff)
+ {
+ fprintf(stderr, "bad directory len\n");
+ exit(1);
+ }
+ for(j = 0; dirlen; )
+ {
+ if (j == 0x800 || (l = blk[j]) == 0)
+ {
+ readblk(fp, blk, ++dirpos);
+ j = 0;
+ dirlen -= 0x800;
+ continue;
+ }
+ if (j + l > 0x800)
+ {
+ fprintf(stderr, "bad dir entry\n");
+ exit(1);
+ }
+ if ((blk[j + 25] & 2) != 0) /* directory? */
+ {
+ j += l;
+ continue;
+ }
+ if ((blk[j + 25] & 4) != 0) /* associated file? */
+ {
+ fprintf(stderr, "associated file\n");
+ exit(1);
+ }
+ if (blk[j + 26] != 0 || blk[j + 27] != 0)
+ {
+ fprintf(stderr, "interleaved file\n");
+ exit(1);
+ }
+ filepos = get4(blk + j + 2);
+ filelen = get4(blk + j + 10);
+ nl = blk[j + 32];
+ if (nl == 0 || j + nl + 33 > 0x800)
+ {
+ fprintf(stderr, "bad dir entry\n");
+ exit(1);
+ }
+ if ((nl & 1) == 0)
+ nl++;
+ ep = blk + j + 33 + nl;
+ el = l - nl - 33;
+ if (el >= 7 && ep[0] == 'S' && ep[1] == 'P')
+ sp_bytes_skip = ep[6];
+ else
+ {
+ ep += sp_bytes_skip;
+ el -= sp_bytes_skip;
+ }
+ ce_len = 0;
+ ce_blk = 0;
+ ce_off = 0;
+ namel = 0;
+ nmf = 0;
+ for (;;)
+ {
+ if (el <= 2)
+ {
+ if (!ce_len)
+ break;
+ readblk(fp, blk2, ce_blk);
+ ep = blk2 + ce_off;
+ el = ce_len;
+ ce_len = 0;
+ }
+ if (ep[0] == 'C' && ep[1] == 'E')
+ {
+ ce_blk = get4(ep + 4);
+ ce_off = get4(ep + 12);
+ ce_len = get4(ep + 20);
+ }
+ else if (ep[0] == 'N' && ep[1] == 'M')
+ {
+ nml = ep[2] - 5;
+ if ((nmf & 1) == 0)
+ namel = 0;
+ nmf = ep[4];
+ if (namel + nml + 2 >= sizeof(name))
+ {
+ fprintf(stderr, "name overflow\n");
+ exit(1);
+ }
+ strncpy((char *)name + namel, (char *)ep + 5, nml);
+ namel += nml;
+ name[namel] = 0;
+ }
+ el -= ep[2];
+ ep += ep[2];
+ }
+ j += l;
+ if (namel < 5)
+ continue;
+ if (filelen < 0x70 || (strcmp((char *)name + namel - 4, ".rpm") && strcmp((char *)name + namel - 4, ".spm")))
+ continue;
+ readrpm((char *)name, fp, filepos, filelen);
+
+#if 0
+ readblk(fp, blk2, filepos);
+
+ filepos2 = filepos;
+ if (get4(blk2) != 0xdbeeabed)
+ continue;
+ if (get4(blk2 + 0x60) != 0x01e8ad8e)
+ {
+ fprintf(stderr, "bad rpm (bad sigheader): %s\n", name);
+ exit(1);
+ }
+ hcnt = get4n(blk2 + 0x68);
+ hdcnt = get4n(blk2 + 0x6c);
+ if ((hdcnt & 7) != 0)
+ hdcnt += 8 - (hdcnt & 7);
+ if (0x70 + hcnt * 16 + hdcnt + 0x70 >= filelen)
+ {
+ fprintf(stderr, "bad rpm (no header): %s\n", name);
+ exit(1);
+ }
+ hstart = 0x70 + hcnt * 16 + hdcnt;
+ if (hstart >= 0x800)
+ {
+ filepos2 += hstart / 0x800;
+ readblk(fp, blk2, filepos2);
+ hstart &= 0x7ff;
+ }
+ if (get4(blk2 + hstart) != 0x01e8ad8e)
+ {
+ fprintf(stderr, "bad rpm (bad header): %s\n", name);
+ exit(1);
+ }
+ hstart += 8;
+ if (hstart >= 0x800)
+ {
+ filepos2++;
+ readblk(fp, blk2, filepos2);
+ hstart &= 0x7ff;
+ }
+ hcnt = get4n(blk2 + hstart);
+ hdcnt = get4n(blk2 + hstart + 4);
+ hstart += 0x8 + hcnt * 16 + hdcnt;
+ if (hstart >= 0x800)
+ {
+ filepos2 += hstart / 0x800;
+ hstart &= 0x7ff;
+ }
+ if (hstart + (filepos2 - filepos) * 0x800 > filelen)
+ {
+ fprintf(stderr, "bad rpm (no payload): %s\n", name);
+ exit(1);
+ }
+ paystart = 0x800 * (off64_t)filepos2 + hstart;
+ paylen = filelen - (hstart + (filepos2 - filepos) * 0x800);
+ namel -= 4;
+ name[namel] = 0;
+ l = namel;
+ if (l > 6 && !strncmp((char *)name + l - 6, ".patch", 6))
+ l -= 6;
+ if (l > 6 && !strncmp((char *)name + l - 6, ".delta", 6))
+ l -= 6;
+ l--;
+ while (l > 0 && name[l] != '.')
+ l--;
+ if (l)
+ {
+ int l2 = l;
+ l--;
+ while (l > 0 && name[l] != '-')
+ l--;
+ if (l)
+ {
+ l--;
+ while (l > 0 && name[l] != '-')
+ l--;
+ if (l)
+ {
+ memmove(name + l, name + l2, namel - l2 + 1);
+ namel -= l2 - l;
+ }
+ }
+ }
+ addrpm((char *)name, paystart, paylen);
+#endif
+ }
+ }
+ sortrpm();
+ addrpm(0, 0, 0, 0, 0);
+ i = rpmpayn - 1;
+ *retp = rpmpays;
+ rpmpayn = 0;
+ rpmpays = 0;
+ return i;
+}
+
+static void
+write32(struct cfile *cf, unsigned int d)
+{
+ unsigned char dd[4];
+ dd[0] = d >> 24;
+ dd[1] = d >> 16;
+ dd[2] = d >> 8;
+ dd[3] = d;
+ if (cf->write(cf, dd, 4) != 4)
+ {
+ perror("write32");
+ exit(1);
+ }
+}
+
+static void
+write64(struct cfile *cf, off64_t d)
+{
+ unsigned char dd[8];
+ dd[0] = d >> 56;
+ dd[1] = d >> 48;
+ dd[2] = d >> 40;
+ dd[3] = d >> 32;
+ dd[4] = d >> 24;
+ dd[5] = d >> 16;
+ dd[6] = d >> 8;
+ dd[7] = d;
+ if (cf->write(cf, dd, 8) != 8)
+ {
+ perror("write64");
+ exit(1);
+ }
+}
+
+static unsigned int read32(struct cfile *bfp)
+{
+ unsigned char d[4];
+ if (bfp->read(bfp, d, 4) != 4)
+ {
+ perror("read32 error");
+ exit(1);
+ }
+ return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
+}
+
+static off64_t read64(struct cfile *bfp)
+{
+ unsigned char d[8];
+ if (bfp->read(bfp, d, 8) != 8)
+ {
+ perror("read64 error");
+ exit(1);
+ }
+ return (off64_t)d[0] << 56 | (off64_t)d[1] << 48 | (off64_t)d[2] << 40 | (off64_t)d[3] << 32 | (off64_t)d[4] << 24 | (off64_t)d[5] << 16 | (off64_t)d[6] << 8 | (off64_t)d[7];
+}
+
+void
+make(char *iso, char *fiso)
+{
+ int i, j;
+ FILE *fp, *ofp;
+ struct rpmpay *pays = 0;
+ int payn = 0;
+ struct cfile *cfp;
+ char buf[8192];
+ unsigned int l, l2;
+ off64_t o, lo;
+
+ if (!strcmp(iso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(iso, "r")) == 0)
+ {
+ perror(iso);
+ exit(1);
+ }
+ payn = rpmoffs(fp, iso, &pays);
+ for (i = j = 0; i < payn; i++)
+ {
+ if (i && pays[i - 1].o == pays[i].o)
+ continue;
+ if (i != j)
+ pays[j] = pays[i];
+ j++;
+ }
+ payn = j;
+
+ if (!strcmp(fiso, "-"))
+ ofp = stdout;
+ else if ((ofp = fopen64(fiso, "w")) == 0)
+ {
+ perror(fiso);
+ exit(1);
+ }
+ fputc('F', ofp);
+ fputc('I', ofp);
+ fputc('S', ofp);
+ fputc('O', ofp);
+ fputc(0, ofp);
+ fputc(0, ofp);
+ fputc(0, ofp);
+ fputc(1, ofp);
+ cfp = cfile_open(CFILE_OPEN_WR, CFILE_IO_FILE, ofp, CFILE_COMP_GZ, CFILE_LEN_UNLIMITED, 0, 0);
+ if (!cfp)
+ {
+ fprintf(stderr, "could not open compression stream\n");
+ exit(1);
+ }
+ write32(cfp, payn);
+ for (i = 0; i < payn; i++)
+ {
+ write64(cfp, pays[i].o);
+ write32(cfp, pays[i].l);
+ j = strlen(pays[i].name) + 1;
+ write32(cfp, j);
+ if (cfp->write(cfp, pays[i].name, j) != j)
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+ if (cfp->write(cfp, pays[i].lmd5, 16) != 16)
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+ if (cfp->write(cfp, pays[i].hmd5, 16) != 16)
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+ }
+ o = 0;
+ if (fseeko64(fp, o, SEEK_SET))
+ {
+ fprintf(stderr, "%s: seek error\n", iso);
+ exit(1);
+ }
+ for (i = 0; i < payn; i++)
+ {
+ if (o < pays[i].o)
+ {
+ lo = pays[i].o - o;
+ while (lo > 0)
+ {
+ l2 = lo > sizeof(buf) ? sizeof(buf) : lo;
+ if (fread(buf, l2, 1, fp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ if (cfp->write(cfp, buf, l2) != l2)
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+ lo -= l2;
+ }
+ o = pays[i].o;
+ }
+ o += pays[i].l;
+ if (fseeko64(fp, o, SEEK_SET))
+ {
+ fprintf(stderr, "%s: seek error\n", iso);
+ exit(1);
+ }
+ }
+ /* now to EOF */
+ while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+ {
+ if (cfp->write(cfp, buf, l) != l)
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+ }
+ if (ferror(fp))
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ if (fp != stdin)
+ fclose(fp);
+ if (cfp->close(cfp) == -1)
+ {
+ fprintf(stderr, "close error\n");
+ exit(1);
+ }
+ if (fflush(ofp) || (ofp != stdout && fclose(ofp) != 0))
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+}
+
+static int
+readfiso(FILE *fp, char *fiso, struct cfile **cfpp, struct rpmpay **paysp)
+{
+ char magic[8];
+ unsigned int version;
+ struct cfile *cfp;
+ int i, payn, nl;
+ struct rpmpay *pays;
+ off64_t o;
+
+ if (fread(magic, 8, 1, fp) != 1)
+ {
+ fprintf(stderr, "%s: not a fragiso file\n", fiso);
+ exit(1);
+ }
+ if (magic[0] != 'F' || magic[1] != 'I' || magic[2] != 'S' || magic[3] != 'O')
+ {
+ fprintf(stderr, "%s: not a fragiso file\n", fiso);
+ exit(1);
+ }
+ version = magic[4] << 24 | magic[5] << 16 | magic[6] << 8 || magic[7];
+ if (version != 1)
+ {
+ fprintf(stderr, "%s: unknown fragiso version: %d\n", fiso, version);
+ exit(1);
+ }
+ cfp = cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fp, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, 0, 0);
+ if (!cfp)
+ {
+ fprintf(stderr, "could not open compression stream\n");
+ exit(1);
+ }
+ payn = read32(cfp);
+ pays = xmalloc2(payn, sizeof(*pays));
+ o = 0;
+ for (i = 0; i < payn; i++)
+ {
+ pays[i].o = read64(cfp);
+ pays[i].l = read32(cfp);
+ if (pays[i].o <= o || pays[i].l < 0x70)
+ {
+ fprintf(stderr, "%s: bad fragiso\n", fiso);
+ exit(1);
+ }
+ o = pays[i].o;
+ nl = read32(cfp);
+ pays[i].name = xmalloc(nl + 1);
+ if (cfp->read(cfp, pays[i].name, nl) != nl)
+ {
+ fprintf(stderr, "read error\n");
+ exit(1);
+ }
+ pays[i].name[nl] = 0;
+ if (cfp->read(cfp, pays[i].lmd5, 16) != 16)
+ {
+ fprintf(stderr, "read error\n");
+ exit(1);
+ }
+ if (cfp->read(cfp, pays[i].hmd5, 16) != 16)
+ {
+ fprintf(stderr, "read error\n");
+ exit(1);
+ }
+ }
+ *cfpp = cfp;
+ *paysp = pays;
+ return payn;
+}
+
+static char *
+checknamearch(char *na)
+{
+ int i, c, n;
+ n = 0;
+ for (i = 0; ; i++)
+ {
+ c = ((unsigned char *)na)[i];
+ if (c <= 32 || c == '/')
+ break;
+ if (c == '.')
+ {
+ if (i == 0 || !na[i + 1])
+ break;
+ n++;
+ }
+ }
+ if (c || !n)
+ return "unknown.unknown";
+ return na;
+}
+
+static void
+list(struct rpmpay *pays, int payn)
+{
+ int i, j;
+ for (i = 0; i < payn; i++)
+ {
+ printf("%010llx:%08x ", (unsigned long long)pays[i].o, pays[i].l);
+ for (j = 0; j < 16; j++)
+ {
+ putchar("0123456789abcdef"[pays[i].lmd5[j] >> 4]);
+ putchar("0123456789abcdef"[pays[i].lmd5[j] & 15]);
+ }
+ for (j = 0; j < 16; j++)
+ {
+ putchar("0123456789abcdef"[pays[i].hmd5[j] >> 4]);
+ putchar("0123456789abcdef"[pays[i].hmd5[j] & 15]);
+ }
+ printf(" %s\n", checknamearch(pays[i].name));
+ }
+}
+
+void
+listfiso(char *fiso)
+{
+ FILE *fp;
+ struct cfile *cf;
+ int payn;
+ struct rpmpay *pays;
+
+ if (!strcmp(fiso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(fiso, "r")) == 0)
+ {
+ perror(fiso);
+ exit(1);
+ }
+ payn = readfiso(fp, fiso, &cf, &pays);
+ cf->close(cf);
+ if (fp != stdin)
+ fclose(fp);
+ list(pays, payn);
+}
+
+void
+listiso(char *iso)
+{
+ FILE *fp;
+ int payn, i, j;
+ struct rpmpay *pays;
+
+ if (!strcmp(iso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(iso, "r")) == 0)
+ {
+ perror(iso);
+ exit(1);
+ }
+ payn = rpmoffs(fp, iso, &pays);
+ if (fp != stdin)
+ fclose(fp);
+ for (i = j = 0; i < payn; i++)
+ {
+ if (i && pays[i - 1].o == pays[i].o)
+ continue;
+ if (i != j)
+ pays[j] = pays[i];
+ j++;
+ }
+ payn = j;
+ list(pays, payn);
+}
+
+void
+assemble(char *fiso, char *dir, char *iso)
+{
+ FILE *fp, *rfp, *ofp;
+ struct cfile *cf;
+ int i, payn;
+ struct rpmpay *pays;
+ char *dbuf;
+ int dl;
+ off64_t o, lo;
+ unsigned char buf[8192];
+ int l2;
+ unsigned int l, cl = 0, cl2;
+ MD5_CTX ctx;
+ int start;
+ unsigned char lmd5[16];
+
+ dl = strlen(dir) + 1;
+ dbuf = xmalloc(dl + 10 + 1 + 8 + 1);
+ strcpy(dbuf, dir);
+ dbuf[dl - 1] = '/';
+
+ if (!strcmp(fiso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(fiso, "r")) == 0)
+ {
+ perror(fiso);
+ exit(1);
+ }
+ payn = readfiso(fp, fiso, &cf, &pays);
+
+ if (!strcmp(iso, "-"))
+ ofp = stdout;
+ else if ((ofp = fopen64(iso, "w")) == 0)
+ {
+ perror(iso);
+ exit(1);
+ }
+ o = 0;
+ for (i = 0; i < payn; i++)
+ {
+ if (o < pays[i].o)
+ {
+ lo = pays[i].o - o;
+ while (lo > 0)
+ {
+ l2 = lo > sizeof(buf) ? sizeof(buf) : lo;
+ if (cf->read(cf, buf, l2) != l2)
+ {
+ fprintf(stderr, "%s: read error\n", fiso);
+ exit(1);
+ }
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ lo -= l2;
+ }
+ }
+ o = pays[i].o;
+ sprintf(dbuf + dl, "%010llx:%08x", (unsigned long long)pays[i].o, pays[i].l);
+ if ((rfp = fopen64(dbuf, "r")) == 0)
+ {
+ perror(dbuf);
+ exit(1);
+ }
+ l = pays[i].l;
+ rpmMD5Init(&ctx);
+ start = 1;
+ while (l > 0)
+ {
+ l2 = l > sizeof(buf) ? sizeof(buf) : l;
+ if (fread(buf, l2, 1, rfp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", dbuf);
+ exit(1);
+ }
+ if (start)
+ {
+ if (get4n(buf) != 0xedabeedb || get4n(buf + 0x60) != 0x8eade801)
+ {
+ fprintf(stderr, "%s: not a rpm\n", dbuf);
+ exit(1);
+ }
+ cl = 0x70 + get4n(buf + 0x68) * 16 + get4n(buf + 0x6c);
+ if ((cl & 7) != 0)
+ cl += 8 - (cl & 7);
+ if (cl > pays[i].l)
+ {
+ fprintf(stderr, "%s: bad rpm\n", dbuf);
+ exit(1);
+ }
+ start = 0;
+ }
+ if (cl)
+ {
+ cl2 = cl > l2 ? l2 : cl;
+ rpmMD5Update(&ctx, buf, cl2);
+ cl -= cl2;
+ }
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ l -= l2;
+ }
+ fclose(rfp);
+ rpmMD5Final(lmd5, &ctx);
+ if (memcmp(lmd5, pays[i].lmd5, 16))
+ {
+ fprintf(stderr, "%s: rpm does not match\n", dbuf);
+ exit(1);
+ }
+ o += pays[i].l;
+ }
+ while ((l2 = cf->read(cf, buf, sizeof(buf))) > 0)
+ {
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ }
+ if (l2)
+ {
+ fprintf(stderr, "%s: read error\n", fiso);
+ exit(1);
+ }
+ if (fflush(ofp) || (ofp != stdout && fclose(ofp) != 0))
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+}
+
+void
+fill(char *fiso, char *iso, int mopt)
+{
+ FILE *fp, *ofp;
+ struct cfile *cf;
+ int i, payn;
+ struct rpmpay *pays;
+ off64_t o, lo;
+ unsigned char buf[8192];
+ unsigned int cl, dl, cl2;
+ MD5_CTX ctx, mctx;
+ unsigned char lmd5[16];
+ int mfd;
+ int l2;
+
+ if (!strcmp(fiso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(fiso, "r")) == 0)
+ {
+ perror(fiso);
+ exit(1);
+ }
+ payn = readfiso(fp, fiso, &cf, &pays);
+ mfd = 1;
+ if (!strcmp(iso, "-"))
+ {
+ ofp = stdout;
+ mfd = 2;
+ }
+ else if ((ofp = fopen64(iso, "r+")) == 0)
+ {
+ perror(iso);
+ exit(1);
+ }
+ rpmMD5Init(&mctx);
+ o = 0;
+ for (i = 0; i < payn; i++)
+ {
+ if (o < pays[i].o)
+ {
+ fseeko64(ofp, o, SEEK_SET);
+ lo = pays[i].o - o;
+ while (lo > 0)
+ {
+ l2 = lo > sizeof(buf) ? sizeof(buf) : lo;
+ if (cf->read(cf, buf, l2) != l2)
+ {
+ fprintf(stderr, "%s: read error\n", fiso);
+ exit(1);
+ }
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ if (mopt)
+ rpmMD5Update(&mctx, buf, l2);
+ lo -= l2;
+ }
+ }
+ o = pays[i].o;
+ /* verify that right rpm is at pos o */
+ fseeko64(ofp, o, SEEK_SET);
+ if (fread(buf, 0x70, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ if (mopt)
+ rpmMD5Update(&mctx, buf, 0x70);
+ if (get4n(buf) != 0xedabeedb || get4n(buf + 0x60) != 0x8eade801)
+ {
+ fprintf(stderr, "%llx: not a rpm\n", (unsigned long long)o);
+ exit(1);
+ }
+ cl = 0x70 + get4n(buf + 0x68) * 16 + get4n(buf + 0x6c);
+ if ((cl & 7) != 0)
+ cl += 8 - (cl & 7);
+ if (cl > pays[i].l)
+ {
+ fprintf(stderr, "%llx: bad rpm\n", (unsigned long long)o);
+ exit(1);
+ }
+ dl = pays[i].l - cl;
+ rpmMD5Init(&ctx);
+ rpmMD5Update(&ctx, buf, 0x70);
+ cl -= 0x70;
+ while (cl)
+ {
+ cl2 = cl > sizeof(buf) ? sizeof(buf) : cl;
+ if (fread(buf, cl2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ rpmMD5Update(&ctx, buf, cl2);
+ if (mopt)
+ rpmMD5Update(&mctx, buf, cl2);
+ cl -= cl2;
+ }
+ rpmMD5Final(lmd5, &ctx);
+ if (memcmp(lmd5, pays[i].lmd5, 16))
+ {
+ fprintf(stderr, "%llx: rpm lead+signature does not match\n", (unsigned long long)o);
+ exit(1);
+ }
+ rpmMD5Init(&ctx);
+ while (dl)
+ {
+ cl2 = dl > sizeof(buf) ? sizeof(buf) : dl;
+ if (fread(buf, cl2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ rpmMD5Update(&ctx, buf, cl2);
+ if (mopt)
+ rpmMD5Update(&mctx, buf, cl2);
+ dl -= cl2;
+ }
+ rpmMD5Final(lmd5, &ctx);
+ if (memcmp(lmd5, pays[i].hmd5, 16))
+ {
+ fprintf(stderr, "%llx: rpm header+payload does not match\n", (unsigned long long)o);
+ exit(1);
+ }
+ o += pays[i].l;
+ }
+ fseeko64(ofp, o, SEEK_SET);
+ while ((l2 = cf->read(cf, buf, sizeof(buf))) > 0)
+ {
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ if (mopt)
+ rpmMD5Update(&mctx, buf, l2);
+ o += l2;
+ }
+ if (l2)
+ {
+ fprintf(stderr, "%s: read error\n", fiso);
+ exit(1);
+ }
+ if (fflush(ofp))
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ if (ftruncate64(fileno(ofp), o))
+ {
+ fprintf(stderr, "%s: truncate error\n", iso);
+ exit(1);
+ }
+ if (ofp != stdout && fclose(ofp) != 0)
+ {
+ fprintf(stderr, "%s: write error\n", iso);
+ exit(1);
+ }
+ if (mopt)
+ {
+ rpmMD5Final(lmd5, &mctx);
+ for (i = 0; i < 16; i++)
+ {
+ buf[2 * i + 0] = "0123456789abcdef"[lmd5[i] >> 4];
+ buf[2 * i + 1] = "0123456789abcdef"[lmd5[i] & 15];
+ }
+ buf[32] = '\n';
+ write(mfd, buf, 33);
+ }
+}
+
+void
+extract(char *iso, char *offlen, char *rpm)
+{
+ off64_t o;
+ unsigned int l, l2;
+ FILE *fp, *ofp;
+ int i;
+ char buf[8192];
+
+ if (strlen(offlen) != 10 + 1 + 8 || offlen[10] != ':')
+ {
+ fprintf(stderr, "bad off:len %s\n", offlen);
+ exit(1);
+ }
+ o = 0;
+ for (i = 0; i < 10; i++)
+ {
+ o <<= 4;
+ if (offlen[i] >= '0' && offlen[i] <= '9')
+ o |= offlen[i] - '0';
+ else if (offlen[i] >= 'a' && offlen[i] <= 'f')
+ o |= offlen[i] - ('a' - 10);
+ else if (offlen[i] >= 'A' && offlen[i] <= 'F')
+ o |= offlen[i] - ('A' - 10);
+ else
+ {
+ fprintf(stderr, "bad off:len %s\n", offlen);
+ exit(1);
+ }
+ }
+ l = 0;
+ for (i = 0; i < 8; i++)
+ {
+ l <<= 4;
+ if (offlen[11 + i] >= '0' && offlen[11 + i] <= '9')
+ l |= offlen[11 + i] - '0';
+ else if (offlen[11 + i] >= 'a' && offlen[11 + i] <= 'f')
+ l |= offlen[11 + i] - ('a' - 10);
+ else if (offlen[11 + i] >= 'A' && offlen[11 + i] <= 'F')
+ l |= offlen[11 + i] - ('A' - 10);
+ else
+ {
+ fprintf(stderr, "bad off:len %s\n", offlen);
+ exit(1);
+ }
+ }
+ if (!strcmp(iso, "-"))
+ fp = stdin;
+ else if ((fp = fopen64(iso, "r")) == 0)
+ {
+ perror(iso);
+ exit(1);
+ }
+ if (fseeko64(fp, o, SEEK_SET) != 0)
+ {
+ perror("fseek");
+ exit(1);
+ }
+ if (!strcmp(rpm, "-"))
+ ofp = stdout;
+ else if ((ofp = fopen64(rpm, "w")) == 0)
+ {
+ perror(rpm);
+ exit(1);
+ }
+ while (l > 0)
+ {
+ l2 = l > sizeof(buf) ? sizeof(buf) : l;
+ if (fread(buf, l2, 1, fp) != 1)
+ {
+ fprintf(stderr, "%s: read error\n", iso);
+ exit(1);
+ }
+ if (fwrite(buf, l2, 1, ofp) != 1)
+ {
+ fprintf(stderr, "%s: write error\n", rpm);
+ exit(1);
+ }
+ l -= l2;
+ }
+ if (fflush(ofp) || (ofp != stdout && fclose(ofp) != 0))
+ {
+ fprintf(stderr, "write error\n");
+ exit(1);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc == 4 && !strcmp(argv[1], "make"))
+ make(argv[2], argv[3]);
+ else if (argc == 3 && !strcmp(argv[1], "list"))
+ listfiso(argv[2]);
+ else if (argc == 3 && !strcmp(argv[1], "listiso"))
+ listiso(argv[2]);
+ else if (argc == 5 && !strcmp(argv[1], "assemble"))
+ assemble(argv[2], argv[3], argv[4]);
+ else if (argc == 5 && !strcmp(argv[1], "fill") && !strcmp(argv[2], "-m"))
+ fill(argv[3], argv[4], 1);
+ else if (argc == 4 && !strcmp(argv[1], "fill"))
+ fill(argv[2], argv[3], 0);
+ else if (argc == 5 && !strcmp(argv[1], "extract"))
+ extract(argv[2], argv[3], argv[4]);
+ else
+ {
+ fprintf(stderr, "usage: fragiso make <iso> <fiso>\n");
+ fprintf(stderr, " fragiso list <fiso>\n");
+ fprintf(stderr, " fragiso listiso <iso>\n");
+ fprintf(stderr, " fragiso assemble <fiso> <dir> <iso>\n");
+ fprintf(stderr, " fragiso fill [-m] <fiso> <iso>\n");
+ fprintf(stderr, " fragiso extract <iso> <off/len> <rpm>\n");
+ exit(1);
+ }
+ exit(0);
+}