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