diff options
Diffstat (limited to 'rpmoffs.c')
-rw-r--r-- | rpmoffs.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/rpmoffs.c b/rpmoffs.c new file mode 100644 index 0000000..ddbc31a --- /dev/null +++ b/rpmoffs.c @@ -0,0 +1,373 @@ +/* + * 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 "rpmoffs.h" +#include "rpmhead.h" + +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, int level) +{ + 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; + rpmpays[rpmpayn].level = level; + 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); +} + +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, filepos2, hoffset; + unsigned char *pt, *ep; + int i, j, l, nl, el, nml, nmf, hcnt, hdcnt; + + 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; + + unsigned char *rpmb; + int len, rpmblen, rpmbalen; + int level; + + off64_t paystart; + int paylen; + struct rpmhead *h; + char *payloadflags; + + rpmbalen = 0x800 * 4; + rpmb = malloc(rpmbalen); + + 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")) + continue; + + filepos2 = filepos; + readblk(fp, rpmb, filepos2++); + rpmblen = 0x800; + if (get4(rpmb) != 0xdbeeabed) + continue; + 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 (no header): %s\n", name); + exit(1); + } + while (rpmblen < len) + { + if (rpmblen + 0x800 > rpmbalen) + { + rpmbalen += 0x800 * 4; + rpmb = realloc(rpmb, rpmbalen); + } + readblk(fp, rpmb + rpmblen, filepos2++); + rpmblen += 0x800; + } + 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 = realloc(rpmb, rpmbalen); + } + readblk(fp, rpmb + rpmblen, filepos2++); + rpmblen += 0x800; + } + paystart = 0x800 * (off64_t)filepos + len; + paylen = filelen - len; + h = readhead_buf(rpmb + hoffset, 16 + hcnt * 16 + hdcnt, 0); + if (!h) + { + fprintf(stderr, "bad rpm (bad h): %s\n", name); + exit(1); + } + level = 0; + payloadflags = headstring(h, TAG_PAYLOADFLAGS); + if (payloadflags && *payloadflags >= '1' && *payloadflags <= '9') + level = *payloadflags - '0'; + + free(h); + + 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, level); + } + } + free(rpmb); + sortrpm(); + addrpm(0, 0, 0, 0); + i = rpmpayn - 1; + *retp = rpmpays; + rpmpayn = 0; + rpmpays = 0; + return i; +} |