421 lines
7.9 KiB
C
421 lines
7.9 KiB
C
#include "stdinc.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
typedef struct AHash AHash;
|
|
|
|
/*
|
|
* hash table for finding arena's based on their names.
|
|
*/
|
|
struct AHash
|
|
{
|
|
AHash *next;
|
|
Arena *arena;
|
|
};
|
|
|
|
enum
|
|
{
|
|
AHashSize = 512
|
|
};
|
|
|
|
static AHash *ahash[AHashSize];
|
|
|
|
static u32int
|
|
hashstr(char *s)
|
|
{
|
|
u32int h;
|
|
int c;
|
|
|
|
h = 0;
|
|
for(; c = *s; s++){
|
|
c ^= c << 6;
|
|
h += (c << 11) ^ (c >> 1);
|
|
c = *s;
|
|
h ^= (c << 14) + (c << 7) + (c << 4) + c;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
int
|
|
addarena(Arena *arena)
|
|
{
|
|
AHash *a;
|
|
u32int h;
|
|
|
|
h = hashstr(arena->name) & (AHashSize - 1);
|
|
a = MK(AHash);
|
|
if(a == nil)
|
|
return -1;
|
|
a->arena = arena;
|
|
a->next = ahash[h];
|
|
ahash[h] = a;
|
|
return 0;
|
|
}
|
|
|
|
Arena*
|
|
findarena(char *name)
|
|
{
|
|
AHash *a;
|
|
u32int h;
|
|
|
|
h = hashstr(name) & (AHashSize - 1);
|
|
for(a = ahash[h]; a != nil; a = a->next)
|
|
if(strcmp(a->arena->name, name) == 0)
|
|
return a->arena;
|
|
return nil;
|
|
}
|
|
|
|
int
|
|
delarena(Arena *arena)
|
|
{
|
|
AHash *a, *last;
|
|
u32int h;
|
|
|
|
h = hashstr(arena->name) & (AHashSize - 1);
|
|
last = nil;
|
|
for(a = ahash[h]; a != nil; a = a->next){
|
|
if(a->arena == arena){
|
|
if(last != nil)
|
|
last->next = a->next;
|
|
else
|
|
ahash[h] = a->next;
|
|
free(a);
|
|
return 0;
|
|
}
|
|
last = a;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
ArenaPart*
|
|
initarenapart(Part *part)
|
|
{
|
|
AMapN amn;
|
|
ArenaPart *ap;
|
|
ZBlock *b;
|
|
u32int i;
|
|
int ok;
|
|
|
|
b = alloczblock(HeadSize, 0, 0);
|
|
if(b == nil || readpart(part, PartBlank, b->data, HeadSize) < 0){
|
|
seterr(EAdmin, "can't read arena partition header: %r");
|
|
return nil;
|
|
}
|
|
|
|
ap = MKZ(ArenaPart);
|
|
if(ap == nil){
|
|
freezblock(b);
|
|
return nil;
|
|
}
|
|
ap->part = part;
|
|
ok = unpackarenapart(ap, b->data);
|
|
freezblock(b);
|
|
if(ok < 0){
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
|
|
ap->tabbase = (PartBlank + HeadSize + ap->blocksize - 1) & ~(ap->blocksize - 1);
|
|
if(ap->version != ArenaPartVersion){
|
|
seterr(ECorrupt, "unknown arena partition version %d", ap->version);
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
if(ap->blocksize & (ap->blocksize - 1)){
|
|
seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", ap->blocksize);
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
if(ap->tabbase >= ap->arenabase){
|
|
seterr(ECorrupt, "arena partition table overlaps with arena storage");
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
ap->tabsize = ap->arenabase - ap->tabbase;
|
|
partblocksize(part, ap->blocksize);
|
|
ap->size = ap->part->size & ~(u64int)(ap->blocksize - 1);
|
|
|
|
if(readarenamap(&amn, part, ap->tabbase, ap->tabsize) < 0){
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
ap->narenas = amn.n;
|
|
ap->map = amn.map;
|
|
if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0){
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
|
|
ap->arenas = MKNZ(Arena*, ap->narenas);
|
|
for(i = 0; i < ap->narenas; i++){
|
|
debugarena = i;
|
|
ap->arenas[i] = initarena(part, ap->map[i].start, ap->map[i].stop - ap->map[i].start, ap->blocksize);
|
|
if(ap->arenas[i] == nil){
|
|
seterr(ECorrupt, "%s: %r", ap->map[i].name);
|
|
freearenapart(ap, 1);
|
|
return nil;
|
|
}
|
|
if(namecmp(ap->map[i].name, ap->arenas[i]->name) != 0){
|
|
seterr(ECorrupt, "arena name mismatches with expected name: %s vs. %s",
|
|
ap->map[i].name, ap->arenas[i]->name);
|
|
freearenapart(ap, 1);
|
|
return nil;
|
|
}
|
|
if(findarena(ap->arenas[i]->name)){
|
|
seterr(ECorrupt, "duplicate arena name %s in %s",
|
|
ap->map[i].name, ap->part->name);
|
|
freearenapart(ap, 1);
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < ap->narenas; i++) {
|
|
debugarena = i;
|
|
addarena(ap->arenas[i]);
|
|
}
|
|
debugarena = -1;
|
|
|
|
return ap;
|
|
}
|
|
|
|
ArenaPart*
|
|
newarenapart(Part *part, u32int blocksize, u32int tabsize)
|
|
{
|
|
ArenaPart *ap;
|
|
|
|
if(blocksize & (blocksize - 1)){
|
|
seterr(ECorrupt, "illegal non-power-of-2 block size %d\n", blocksize);
|
|
return nil;
|
|
}
|
|
ap = MKZ(ArenaPart);
|
|
if(ap == nil)
|
|
return nil;
|
|
|
|
ap->version = ArenaPartVersion;
|
|
ap->part = part;
|
|
ap->blocksize = blocksize;
|
|
partblocksize(part, blocksize);
|
|
ap->size = part->size & ~(u64int)(blocksize - 1);
|
|
ap->tabbase = (PartBlank + HeadSize + blocksize - 1) & ~(blocksize - 1);
|
|
ap->arenabase = (ap->tabbase + tabsize + blocksize - 1) & ~(blocksize - 1);
|
|
ap->tabsize = ap->arenabase - ap->tabbase;
|
|
ap->narenas = 0;
|
|
|
|
if(wbarenapart(ap) < 0){
|
|
freearenapart(ap, 0);
|
|
return nil;
|
|
}
|
|
|
|
return ap;
|
|
}
|
|
|
|
int
|
|
wbarenapart(ArenaPart *ap)
|
|
{
|
|
ZBlock *b;
|
|
|
|
if(okamap(ap->map, ap->narenas, ap->arenabase, ap->size, "arena table") < 0)
|
|
return -1;
|
|
b = alloczblock(HeadSize, 1, 0);
|
|
if(b == nil)
|
|
/* ZZZ set error message? */
|
|
return -1;
|
|
|
|
if(packarenapart(ap, b->data) < 0){
|
|
seterr(ECorrupt, "can't make arena partition header: %r");
|
|
freezblock(b);
|
|
return -1;
|
|
}
|
|
if(writepart(ap->part, PartBlank, b->data, HeadSize) < 0 ||
|
|
flushpart(ap->part) < 0){
|
|
seterr(EAdmin, "can't write arena partition header: %r");
|
|
freezblock(b);
|
|
return -1;
|
|
}
|
|
freezblock(b);
|
|
|
|
return wbarenamap(ap->map, ap->narenas, ap->part, ap->tabbase, ap->tabsize);
|
|
}
|
|
|
|
void
|
|
freearenapart(ArenaPart *ap, int freearenas)
|
|
{
|
|
int i;
|
|
|
|
if(ap == nil)
|
|
return;
|
|
if(freearenas){
|
|
for(i = 0; i < ap->narenas; i++){
|
|
if(ap->arenas[i] == nil)
|
|
continue;
|
|
delarena(ap->arenas[i]);
|
|
freearena(ap->arenas[i]);
|
|
}
|
|
}
|
|
free(ap->map);
|
|
free(ap->arenas);
|
|
free(ap);
|
|
}
|
|
|
|
int
|
|
okamap(AMap *am, int n, u64int start, u64int stop, char *what)
|
|
{
|
|
u64int last;
|
|
u32int i;
|
|
|
|
last = start;
|
|
for(i = 0; i < n; i++){
|
|
if(am[i].start < last){
|
|
if(i == 0)
|
|
seterr(ECorrupt, "invalid start address in %s", what);
|
|
else
|
|
seterr(ECorrupt, "overlapping ranges in %s", what);
|
|
return -1;
|
|
}
|
|
if(am[i].stop < am[i].start){
|
|
seterr(ECorrupt, "invalid range in %s", what);
|
|
return -1;
|
|
}
|
|
last = am[i].stop;
|
|
}
|
|
if(last > stop){
|
|
seterr(ECorrupt, "invalid ending address in %s", what);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
maparenas(AMap *am, Arena **arenas, int n, char *what)
|
|
{
|
|
u32int i;
|
|
|
|
for(i = 0; i < n; i++){
|
|
arenas[i] = findarena(am[i].name);
|
|
if(arenas[i] == nil){
|
|
seterr(EAdmin, "can't find arena '%s' for '%s'\n", am[i].name, what);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
readarenamap(AMapN *amn, Part *part, u64int base, u32int size)
|
|
{
|
|
IFile f;
|
|
u32int ok;
|
|
|
|
if(partifile(&f, part, base, size) < 0)
|
|
return -1;
|
|
ok = parseamap(&f, amn);
|
|
freeifile(&f);
|
|
return ok;
|
|
}
|
|
|
|
int
|
|
wbarenamap(AMap *am, int n, Part *part, u64int base, u64int size)
|
|
{
|
|
Fmt f;
|
|
ZBlock *b;
|
|
|
|
b = alloczblock(size, 1, part->blocksize);
|
|
if(b == nil)
|
|
return -1;
|
|
|
|
fmtzbinit(&f, b);
|
|
|
|
if(outputamap(&f, am, n) < 0){
|
|
seterr(ECorrupt, "arena set size too small");
|
|
freezblock(b);
|
|
return -1;
|
|
}
|
|
if(writepart(part, base, b->data, size) < 0 || flushpart(part) < 0){
|
|
seterr(EAdmin, "can't write arena set: %r");
|
|
freezblock(b);
|
|
return -1;
|
|
}
|
|
freezblock(b);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* amap: n '\n' amapelem * n
|
|
* n: u32int
|
|
* amapelem: name '\t' astart '\t' astop '\n'
|
|
* astart, astop: u64int
|
|
*/
|
|
int
|
|
parseamap(IFile *f, AMapN *amn)
|
|
{
|
|
AMap *am;
|
|
u64int v64;
|
|
u32int v;
|
|
char *s, *t, *flds[4];
|
|
int i, n;
|
|
|
|
/*
|
|
* arenas
|
|
*/
|
|
if(ifileu32int(f, &v) < 0){
|
|
seterr(ECorrupt, "syntax error: bad number of elements in %s", f->name);
|
|
return -1;
|
|
}
|
|
n = v;
|
|
if(n > MaxAMap){
|
|
seterr(ECorrupt, "illegal number of elements %d in %s",
|
|
n, f->name);
|
|
return -1;
|
|
}
|
|
am = MKNZ(AMap, n);
|
|
if(am == nil){
|
|
fprint(2, "out of memory\n");
|
|
return -1;
|
|
}
|
|
for(i = 0; i < n; i++){
|
|
s = ifileline(f);
|
|
if(s)
|
|
t = vtstrdup(s);
|
|
else
|
|
t = nil;
|
|
if(s == nil || getfields(s, flds, 4, 0, "\t") != 3){
|
|
fprint(2, "early eof after %d of %d, %s:#%d: %s\n", i, n, f->name, f->pos, t);
|
|
free(t);
|
|
return -1;
|
|
}
|
|
free(t);
|
|
if(nameok(flds[0]) < 0)
|
|
return -1;
|
|
namecp(am[i].name, flds[0]);
|
|
if(stru64int(flds[1], &v64) < 0){
|
|
seterr(ECorrupt, "syntax error: bad arena base address in %s", f->name);
|
|
free(am);
|
|
return -1;
|
|
}
|
|
am[i].start = v64;
|
|
if(stru64int(flds[2], &v64) < 0){
|
|
seterr(ECorrupt, "syntax error: bad arena size in %s", f->name);
|
|
free(am);
|
|
return -1;
|
|
}
|
|
am[i].stop = v64;
|
|
}
|
|
|
|
amn->map = am;
|
|
amn->n = n;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
outputamap(Fmt *f, AMap *am, int n)
|
|
{
|
|
int i;
|
|
|
|
if(fmtprint(f, "%ud\n", n) < 0)
|
|
return -1;
|
|
for(i = 0; i < n; i++)
|
|
if(fmtprint(f, "%s\t%llud\t%llud\n", am[i].name, am[i].start, am[i].stop) < 0)
|
|
return -1;
|
|
return 0;
|
|
}
|