diff options
Diffstat (limited to 'fragiso.c')
-rw-r--r-- | fragiso.c | 1288 |
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); +} |