Plan 9 from Bell Labs’s /usr/web/sources/contrib/blstuart/θfs/meta.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * Copyright (c) 2013, Coraid, Inc.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Coraid nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "dat.h"

enum {
	GMperBlk = BlkSize / sizeof(GMeta),
};

static void freeblob(uvlong);

static uvlong nget;
static uvlong nrm;
static uvlong nset;
static uvlong nalloc;
static uvlong nfree;
static uvlong nmiss;
static QLock mlock;
static QLock alock;
static QLock block;
static GMeta *mbuf;
static uvlong mblk;

void
reammeta(int fd)
{
	GMeta *mp;
	Blob *bp;
	char *p, *e;
	int i, j, n;

	/* First the GMeta structures */
	p = θmalloc(16 * BlkSize);
	e = p + 16 * BlkSize;
	mp = (GMeta *)p;
	mp->next = 0;
	mp->type = MTistring;
	strcpy(mp->name, "invalid");
	strcpy(mp->m.str, "errnt");
	for(++mp, j = 2; (char *)mp < e; ++mp, ++j)
		mp->next = j;
	pwrite(fd, p, 16 * BlkSize, super.firstmeta * BlkSize);
	memset(p, 0, sizeof(GMeta));
	for(i = 16; i < super.nmeta; i += 16) {
		n = super.nmeta - i;
		if(n > 16)
			n = 16;
		for(mp = (GMeta *)p; (char *)mp < e; ++mp, ++j)
			mp->next = j;
if((i/16) % 10 == 0) fprint(2, ",");
		pwrite(fd, p, n * BlkSize, (super.firstmeta + i) * BlkSize);
	}
	/* Now the string/blob pool */
	for(i = 0; i < super.nblob; i += 16) {
		n = super.nblob - i;
		if(n > 16)
			n = 16;
		memset(p, 0, n * BlkSize);
		for(j = 0; j < n; ++j) {
			bp = (Blob *)(p + j * BlkSize);
			bp->len = 0x8000 | (BlkSize/2 - sizeof(short));
			bp->next = (super.firstblob + i + j) * BlkSize + BlkSize/2;
			bp = (Blob *)(p + j * BlkSize + BlkSize/2);
			bp->len = 0x8000 | (BlkSize/2 - sizeof(short));
			if(i + j + 1 < super.nblob)
				bp->next = (super.firstblob + i + j + 1) * BlkSize;
		}
if((i/16) % 10 == 0) fprint(2, ";");
		pwrite(fd, p, n * BlkSize, (super.firstblob + i) * BlkSize);
	}
	free(p);
}

void
resetmeta(void)
{
	qlock(&mlock);
	if(mbuf) {
		brelease(mblk);
		mbuf = nil;
		mblk = 0;
	}
	qunlock(&mlock);
}

static int
getmstruct(int fd, GMeta *buf, uvlong idx)
{
	uvlong off, blk;

	if(idx > super.nmeta * (BlkSize / sizeof(GMeta))) {
		fprint(2, "Invalid metadata index: %ulld\n", idx);
		return -1;
	}
	if(fd != -1)
		return spread(fd, buf, sizeof(GMeta), idx * sizeof(GMeta) + super.firstmeta * BlkSize);
	blk = idx / GMperBlk + super.firstmeta;
	off = idx % GMperBlk;
	qlock(&mlock);
	if(blk != mblk) {
		++nmiss;
		if(mbuf)
			brelease(mblk);
		mbuf = cbread(blk);
		mblk = blk;
	}
	memmove(buf, mbuf + off, sizeof(GMeta));
	qunlock(&mlock);
	return sizeof(GMeta);
}

static int
savemstruct(GMeta *buf, uvlong idx)
{
	uvlong off, blk;

	if(idx > super.nmeta * (BlkSize / sizeof(GMeta))) {
		fprint(2, "Invalid metadata index: %ulld\n", idx);
		return -1;
	}
	blk = idx / GMperBlk + super.firstmeta;
	off = idx % GMperBlk;
	qlock(&mlock);
	if(blk != mblk) {
		++nmiss;
		if(mbuf)
			brelease(mblk);
		mbuf = cbread(blk);
		mblk = blk;
	}
	memmove(mbuf + off, buf, sizeof(GMeta));
	cbwrite(blk);
	qunlock(&mlock);
	return sizeof(GMeta);
}

static uvlong
allocmeta(GMeta *buf)
{
	uvlong nmeta;

	qlock(&alock);
	if(super.ffmeta == 0) {
		fprint(2, "Out of metadata space!\n");
		qunlock(&alock);
		return 0;
	}
	if(getmstruct(-1, buf, super.ffmeta) < 0) {
		qunlock(&alock);
		return 0;
	}
	++nalloc;
	nmeta = super.ffmeta;
	super.ffmeta = buf->next;
	savesuper();
	qunlock(&alock);
	return nmeta;
}

static void
freemeta(uvlong idx)
{
	GMeta buf;

	qlock(&alock);
	if(getmstruct(-1, &buf, idx) < 0) {
		qlock(&alock);
		return;
	}
	if(buf.type == MTstring || buf.type == MTblob)
		freeblob(buf.m.val);
	++nfree;
	memset(&buf, 0, sizeof(GMeta));
	buf.next = super.ffmeta;
	super.ffmeta = idx;
	savesuper();
	savemstruct(&buf, idx);
	qunlock(&alock);
}

/*
static void
insmeta(GMeta *buf, uvlong idx, uvlong after)
{
	GMeta abuf;

	if(getmstruct(-1, &abuf, after) < 0)
		return;
	buf->next = abuf.next;
	abuf.next = idx;
	savemstruct(&abuf, after);
	savemstruct(buf, idx);
}
*/

static Blob *
getbstruct(int fd, uvlong bp)
{
	Blob *b;
	void *a;
	uvlong blk;
	ulong off, m;

	blk = bp / BlkSize;
	if(blk < super.firstblob || blk >= super.firstblob + super.nblob)
		return nil;
	off = bp % BlkSize;
	m = BlkSize - off;
	if(m > 32768)
		m = 32768;
	if(fd == -1)
		b = (Blob *)((char *)cbread(blk) + off);
	else {
		b = θmalloc(m);
		spread(fd, b, m, bp);
	}
	m = b->len & 0x7fff;
	a = θmalloc(m + sizeof(short));
	memmove(a, b, m + sizeof(short));
	if(fd == -1)
		brelease(blk);
	else
		free(b);
	return a;
}

static int
savebstruct(Blob *b, uvlong bp)
{
	return cwrite(b, b->len & 0x7fff, bp);
}

static Blob *
allocblob(int n, uvlong *bp)
{
	Blob *cb, *pb, *nb;
	uvlong cur, prev;
	int an;

	an = (n + BlobQuan - 1) & ~(BlobQuan - 1);
	prev = 0;
	cb = nil;
	pb = nil;
	qlock(&block);
	for(cur = super.ffblob; cur; ) {
		cb = getbstruct(-1, cur);
		if(cb == nil) {
			qunlock(&block);
			return nil;
		}
		if((cb->len & 0x7fff) >= an)
			break;
		pb = cb;
		prev = cur;
		cur = cb->next;
	}
	if(cur == 0) {
		qunlock(&block);
		free(cb);
		fprint(2, "No free blobs\n");
		return nil;
	}
	if((cb->len & 0x7fff) >= an + BlobQuan) {
		if(prev != 0) {
			pb->next = cur + an;
			savebstruct(pb, prev);
		}
		else {
			super.ffblob = cur + an;
			savesuper();
		}
		nb = (Blob *)((char *)cb + an);
		nb->len = cb->len - an;
		nb->next = cb->next;
		savebstruct(nb, cur + an);
		cb->len = an - sizeof(short);
	}
	else {
		if(prev != 0) {
			pb->next = cb->next;
			savebstruct(pb, prev);
		}
		else {
			super.ffblob = cb->next;
			savesuper();
		}
		cb->len &= 0x7fff;
	}
	qunlock(&block);
	free(pb);
	if(bp)
		*bp = cur;
	return cb;
}

/*
 * This is a pretty gross hack, and probably oversimplified.
 * However, I'm not real happy with this part and may redo
 * it anyway.  When freeing a blob (or long string) in the pool,
 * we don't attempt to coalesce them, we just add it on to
 * the end of the list.  Until we've done enough allocations
 * to use up all the pool space once, we don't really care.
 * Because the most common use for the pool space is strings
 * and the quantum is set to 64, I suspect nearly all requests
 * will be satisfied with a single quantum and coalescing
 * wouldn't have significant benefit anyway.  So there's
 * the excuse for taking what is probably too simple an
 * approach.
 */
static void
freeblob(uvlong bp)
{
	Blob *b, *b2;

	qlock(&block);
	b = getbstruct(-1, bp);
	b->len |= 0x8000;
	b->next = 0;
	savebstruct(b, bp);
	b2 = getbstruct(-1, super.lfblob);
	if(b2) {
		b2->next = bp;
		savebstruct(b2, super.lfblob);
		super.lfblob = bp;
		savesuper();
	}
	else
		fprint(2, "Unexpected failure to read super.lfblob\n");
	qunlock(&block);
}

void *
getblob(int fd, uvlong bp, int *n)
{
	Blob *b;
	void *a;

	b = getbstruct(fd, bp);
	if(b == nil)
		return nil;
	if(b->len & 0x8000) {
		free(b);
		return nil;
	}
	if(n)
		*n = b->len;
	a = θmalloc(b->len);
	memmove(a, b->data, b->len);
	free(b);
	return a;
}

uvlong
setblob(void *blob, int n, uvlong bp)
{
	Blob *b;
	uvlong nbp;

	b = getbstruct(-1, bp);
	if(b) {
		if(b->len == n) {
			memmove(b->data, blob, n);
			savebstruct(b, bp);
			return bp;
		}
		else {
			b->len |= 0x8000;
			savebstruct(b, bp);
		}
	}
	b = allocblob(n, &nbp);
	if(b == nil)
		return 0;
	memmove(b->data, blob, n);
	savebstruct(b, nbp);
	free(b);
	return nbp;
}

int
getmeta(int fd, uvlong stidx, char *name, MVal *val)
{
	GMeta buf;
	uvlong next;

	++nget;
	for(next = stidx; next; ) {
		if(getmstruct(fd, &buf, next) < 0)
			return MTnone;
		if(strcmp(name, buf.name) == 0)
			break;
		next = buf.next;
	}
	if(next == 0)
		return MTnone;
	*val = buf.m;
	return buf.type;
}

int
getmetaint(int fd, uvlong stidx, char *name, uvlong *val)
{
	MVal x;
	int typ;

	typ = getmeta(fd, stidx, name, &x);
	switch(typ) {
	case MTint:
		*val = x.val;
		return typ;
	default:
		return MTnone;
	}
}

char *
getmetastr(int fd, uvlong stidx, char *name)
{
	MVal x;
	char *p;
	int typ;

	typ = getmeta(fd, stidx, name, &x);
	switch(typ) {
	case MTistring:
		p = estrdup9p(x.str);
		return p;
	case MTstring:
		return getblob(fd, x.val, nil);
	default:
		return nil;
	}
}

uvlong
setmeta(uvlong stidx, char *name, char *newname, uvlong val, int type)
{
	GMeta buf, nbuf;
	uvlong next, last, nmeta;

	++nset;
	last = 0;
	for(next = stidx; next; ) {
		if(getmstruct(-1, &buf, next) < 0)
			return 0;
		last = next;
		if(strcmp(name, buf.name) == 0) {
			if(type == MTistring) {
				if(buf.type == MTstring)
					freeblob(buf.m.val);
				strcpy(buf.m.str, (char *)val);
			}
			else
				buf.m.val = val;
			buf.type = type;		/* in case we're changing the string length */
			if(newname)
				strcpy(buf.name, newname);
			savemstruct(&buf, last);
			return last;
		}
		next = buf.next;
	}
	nmeta = allocmeta(&nbuf);
	if(nmeta == 0)
		return 0;
	if(last == 0)
		nbuf.next = 0;
	else {
		nbuf.next = buf.next;
		buf.next = nmeta;
		savemstruct(&buf, last);
	}
	nbuf.type = type;
	if(newname)
		strcpy(nbuf.name, newname);
	else
		strcpy(nbuf.name, name);
	if(type == MTistring)
		strcpy(nbuf.m.str, (char *)val);
	else
		nbuf.m.val = val;
	savemstruct(&nbuf, nmeta);
	return nmeta;
}

uvlong
setmetaint(uvlong stidx, char *name, char *newname, uvlong val)
{
	return setmeta(stidx, name, newname, val, MTint);
}

uvlong
setmetastr(uvlong stidx, char *name, char *newname, char *s, uvlong bp)
{
	uvlong nbp;
	int n;

	n = strlen(s);
	if(n <= 7) {
		return setmeta(stidx, name, newname, (uvlong)s, MTistring);
	}
	nbp = setblob(s, n + 1, bp);
	return setmeta(stidx, name, newname, nbp, MTstring);
}

void
setmstruct(uvlong idx, uvlong next, char *name, int type, uvlong val)
{
	GMeta mb;

	if(getmstruct(-1, &mb, idx) < 0)
		return;
	mb.next = next;
	strcpy(mb.name, name);
	mb.type = type;
	mb.m.val = val;
	savemstruct(&mb, idx);
}

uvlong
setmetablob(uvlong stidx, char *name, char *newname, uchar *blob, int n, uvlong bp)
{
	uvlong nbp;

	nbp = setblob(blob, n, bp);
	return setmeta(stidx, name, newname, nbp, MTblob);
}

uvlong
rmmeta(uvlong midx, uvlong victim)
{
	GMeta buf, vbuf;
	uvlong next;

	++nrm;
	if(getmstruct(-1, &vbuf, victim) < 0)
		return midx;
	if(midx == victim) {
		next = vbuf.next;
		freemeta(victim);
		return next;
	}
	for(next = midx; next; ) {
		if(getmstruct(-1, &buf, next) < 0)
			return midx;
		if(buf.next == victim) {
			buf.next = vbuf.next;
			freemeta(victim);
			savemstruct(&buf, next);
			return midx;
		}
		next = vbuf.next;
	}
	return midx;
}

void
rmmlist(uvlong midx)
{
	GMeta buf;
	uvlong next;

	++nrm;
	next = midx;
	while(next) {
		if(getmstruct(-1, &buf, next) < 0)
			return;
		freemeta(next);
		next = buf.next;
	}
}

static uvlong
promote1(uvlong midx, uvlong dblk, int)
{
	uvlong *p;
	uvlong nblk;

	nblk = allocblock();
	if(nblk == 0)
		return 0;
	p = cbclean(nblk);
	p[0] = dblk;
	cbwrite(nblk);
	setmetaint(midx, "dblock", "index", nblk);
	brelease(nblk);
	return nblk;
}

static uvlong
promote2(uvlong midx, uvlong iblk, int)
{
	uvlong *p;
	uvlong nblk;

	nblk = allocblock();
	if(nblk == 0)
		return 0;
	p = cbclean(nblk);
	p[0] = iblk;
	cbwrite(nblk);
	setmetaint(midx, "index", "indirect", nblk);
	brelease(nblk);
	return nblk;
}

static uvlong
promote3(uvlong midx, uvlong iblk, int levels)
{
	uvlong *p;
	char *name;
	uvlong nblk;

	if(levels == 1) {
		nblk = allocblock();
		if(nblk == 0)
			return 0;
		p = cbclean(nblk);
		p[0] = iblk;
		cbwrite(nblk);
		brelease(nblk);
		iblk = nblk;
	}
	nblk = allocblock();
	if(nblk == 0)
		return 0;
	p = cbclean(nblk);
	p[0] = iblk;
	cbwrite(nblk);
	if(levels == 1)
		name = "index";
	else
		name = "indirect";
	setmetaint(midx, name, "dblindir", nblk);
	brelease(nblk);
	return nblk;
}

static uvlong
doindir(int fd, uvlong iblk, int off, int allocate)
{
	uvlong *p;
	uvlong pblk;

	if(iblk < super.firstdat || iblk >= super.nblk)
		return 0;
	if(fd != -1)
		spread(fd, &pblk, sizeof(uvlong), iblk * BlkSize + off * sizeof(uvlong));
	else {
		p = cbread(iblk);
		pblk = p[off];
		if(pblk == 0) {
			if(allocate) {
				pblk = allocblock();
				if(pblk == 0)
					return 0;
				p[off] = pblk;
				cbwrite(iblk);
				cbclean(pblk);
				cbwrite(pblk);
				brelease(pblk);
			}
		}
		brelease(iblk);
	}
	return pblk;
}
	
uvlong
locate(int fd, uvlong midx, uvlong vblk, int allocate)
{
	uvlong *p;
	uvlong iblk, pblk;
	ulong pperb;
	int levels, l1off, l2off, l3off;

	if(getmetaint(fd, midx, "dblindir", &iblk) == MTint)
		levels = 3;
	else if(getmetaint(fd, midx, "indirect", &iblk) == MTint)
		levels = 2;
	else if(getmetaint(fd, midx, "index", &iblk) == MTint)
		levels = 1;
	else if(getmetaint(fd, midx, "dblock", &iblk) == MTint)
		levels = 0;
	else
		return 0;
	pperb = BlkSize / sizeof(uvlong);
	l1off = vblk % pperb;
	l2off = (vblk / pperb) % pperb;
	l3off = vblk / (pperb * pperb);
	if(levels < 3 && l3off != 0) {
		iblk = promote3(midx, iblk, levels);
		levels = 3;
	}
	else if(levels < 2 && l2off != 0) {
		iblk = promote2(midx, iblk, levels);
		levels = 2;
	}
	else if(levels < 1 && l1off > 0) {
		iblk = promote1(midx, iblk, levels);
		levels = 1;
	}
	pblk = 0;
	switch(levels) {
	case 3:
		iblk = doindir(fd, iblk, l3off, allocate);
	case 2:
		iblk = doindir(fd, iblk, l2off, allocate);
	case 1:
		if(iblk == 0)
			return 0;
		p = cbread(iblk);
		pblk = p[l1off];
		if(pblk == 0 && allocate) {
			pblk = allocblock();
			cbclean(pblk);
			p[l1off] = pblk;
			cbwrite(pblk);
			brelease(pblk);
			cbwrite(iblk);
		}
		brelease(iblk);
		break;
	case 0:
		pblk = iblk;
		if(pblk == 0 && allocate) {
			pblk = allocblock();
			cbclean(pblk);
			setmetaint(midx, "dblock", nil, pblk);
			cbwrite(pblk);
			brelease(pblk);
		}
		break;
	}
	if(pblk < super.firstdat || pblk >= super.nblk) {
		fprint(2, "Bogus block number found in locate: index:%ulld\n", iblk);
		return 0;
	}
	return pblk;
}

void
freedata(uvlong midx)
{
	uvlong *index1, *index2, *index3;
	uvlong iblk;
	int i, j, k;

	if(getmetaint(-1, midx, "dblindir", &iblk) == MTint) {
		if(iblk == 0)
			return;
		if(iblk < super.firstdat) {
			fprint(2,"Bogus dblindir block in freedat: %ulld\n", iblk);
			return;
		}
		index3 = cbread(iblk);
		for(i = 0; i < BlkSize / sizeof(uvlong); ++i) {
			if(index3[i] >= super.firstdat && index3[i] < super.nblk) {
				index2 = cbread(index3[i]);
				for(j = 0; j < BlkSize / sizeof(uvlong); ++j) {
					if(index2[j] >= super.firstdat && index2[j] < super.nblk) {
						index1 = cbread(index2[j]);
						for(k = 0; k < BlkSize / sizeof(uvlong); ++k)
							if(index1[k] != 0)
								freeblock(index1[k]);
						brelease(index2[j]);
						freeblock(index2[j]);
					}
				}
				brelease(index3[i]);
				freeblock(index3[i]);
			}
		}
		brelease(iblk);
		freeblock(iblk);
	}
	else if(getmetaint(-1, midx, "indirect", &iblk) == MTint) {
		if(iblk == 0)
			return;
		if(iblk < super.firstdat) {
			fprint(2, "Bogus indirect block in freedat: %ulld\n", iblk);
			return;
		}
		index2 = cbread(iblk);
		for(i = 0; i < BlkSize / sizeof(uvlong); ++i) {
			if(index2[i] >= super.firstdat && index2[i] < super.nblk) {
				index1 = cbread(index2[i]);
				for(j = 0; j < BlkSize / sizeof(uvlong); ++j)
					if(index1[j] != 0)
						freeblock(index1[j]);
				brelease(index2[i]);
				freeblock(index2[i]);
			}
		}
		brelease(iblk);
		freeblock(iblk);
	}
	else if(getmetaint(-1, midx, "index", &iblk) == MTint) {
		if(iblk == 0)
			return;
		if(iblk < super.firstdat) {
			fprint(2, "Bogus index block in freedat: %ulld\n", iblk);
			return;
		}
		index1 = cbread(iblk);
		for(i = 0; i < BlkSize / sizeof(uvlong); ++i)
			if(index1[i] != 0)
				freeblock(index1[i]);
		brelease(iblk);
		freeblock(iblk);
	}
}

void
prmeta(int fd, uvlong qpath)
{
	GMeta buf;
	char *p;
	uvlong meta, next;
	int i, n;

	meta = q2m(-1, qpath, 0);
	if(meta == 0) {
		fprint(fd, "no metadata\n");
		return;
	}
	for(next = meta; next; ) {
		if(getmstruct(-1, &buf, next) < 0)
			break;
		switch(buf.type) {
		case MTnone:
			break;
		case MTint:
			fprint(fd, "%s: %ulld(%016ullx)\n", buf.name, buf.m.val, buf.m.val);
			break;
		case MTistring:
			fprint(fd, "%s: %s\n", buf.name, buf.m.str);
			break;
		case MTstring:
			p = getblob(-1, buf.m.val, nil);
			fprint(fd, "%s: %s\n", buf.name, p);
			free(p);
			break;
		case MTblob:
			fprint(fd, "%s:", buf.name);
			p = getblob(-1, buf.m.val, &n);
			for(i = 0; i < n; ++i)
				fprint(fd, " %02x", p[i]);
			fprint(fd, "\n");
			free(p);
			break;
		}
		next = buf.next;
	}
}

static char mstatbuf[1024];

char *
prmstat(void)
{
	char *p, *e;

	p = mstatbuf;
	e = p + nelem(mstatbuf);
	p = seprint(p, e, "Metadata stats:\n");
	p = seprint(p, e, "getmeta calls: %ulld\n", nget);
	p = seprint(p, e, "setmeta calls: %ulld\n", nset);
	p = seprint(p, e, "rmmeta calls: %ulld\n", nrm);
	p = seprint(p, e, "alloc calls: %ulld\n", nalloc);
	p = seprint(p, e, "free calls: %ulld\n", nfree);
	seprint(p, e, "misses: %ulld\n", nmiss);
	return mstatbuf;
}

static uvlong
qoffset(ulong bucket)
{
	return BlkSize * (super.nhashblk + 1) + bucket * sizeof(uvlong);
}

void
recovermeta(int fd)
{
	GMeta mb;
	uvlong midx1, midx2, qhnext, qb, n;
	int saidit;

	/* First set all the marker flags */
	fprint(fd, "Setting flags\n");
	for(midx1 = 1; midx1 < super.nmeta * GMperBlk; ++midx1) {
		getmstruct(-1, &mb, midx1);
		mb.type |= 0x80;
		savemstruct(&mb, midx1);
	}

	/* Go through all the q2m hash table and mark referenced ones in use */
	fprint(fd, "Marking ones referenced from QID hash table\n");
	for(qb = 0; qb < super.nht; ++qb) {
		cread(&midx1, sizeof(uvlong), qoffset(qb));
		qhnext = 0;
		n = 0;
		saidit = 0;
		while(midx1 != 0) {
			++n;
			getmstruct(-1, &mb, midx1);
			mb.type &= 0x7f;
			if(mb.type == MTnone && !saidit) {
				fprint(fd, "Unexpected null metadatum at %ulld in bucket %ulld\n", midx1, qb);
				saidit = 1;
			}
			midx2 = midx1;
			midx1 = mb.next;
			if(strcmp(mb.name, "qhnext") == 0 && mb.m.val != 0) {
				qhnext = mb.m.val;
				fprint(fd, "Warning, QID collision qb:%ulld midx:%ulld\n", qb, midx2);
			}
			savemstruct(&mb, midx2);
			if(midx1 == 0) {
				midx1 = qhnext;
				qhnext = 0;
			}
		}
		if(n > 128)
			fprint(fd, "Unexpected large list at qb %ulld, size %ulld\n", qb, n);
	}

	fprint(fd, "Sizing old free list\n");
	n = 0;
	for(midx1 = super.ffmeta; midx1 != 0; ) {
		++n;
		if(n >= super.nmeta * GMperBlk) {
			fprint(fd, "Cycle in old free list?\n");
			break;
		}
		getmstruct(-1, &mb, midx1);
		midx1 = mb.next;
	}
	fprint(fd, "Old free list has %ulld structures\n", n);

	/* Reclaim the free ones */
	fprint(fd, "Rebuilding free list\n");
	n = 0;
	super.ffmeta = 0;
	for(midx1 = super.nmeta * GMperBlk - 1; midx1 != 0; --midx1) {
		getmstruct(-1, &mb, midx1);
		if(mb.type & 0x80) {
			memset(&mb, 0, sizeof(GMeta));
			mb.next = super.ffmeta;
			super.ffmeta = midx1;
			savemstruct(&mb, midx1);
			++n;
		}
	}
	savesuper();
	fprint(fd, "Recovered %ulld free metadata structures\n", n);
}

static int
markinuse(char *shadow, uvlong blk)
{
	long byt;
	int bit, old;

	if(blk < super.firstdat || blk >= super.nblk)
		return -1;
	byt = blk / 8;
	bit = blk % 8;
	old = shadow[byt] & (1 << bit);
	shadow[byt] &= ~(1 << bit);
	return old;
}

/* Dealing with humans always makes the code ugly. */
static char *idxnames[] = {"data", "index", "indirect", "dblindir"};

static void
chkidxalloc(int fd, char *shadow, uvlong blk, uvlong midx, int lev)
{
	uvlong *iblk;
	int i;

	if(blk == 0)
		return;
	switch(markinuse(shadow, blk)) {
	case -1:
		fprint(fd, "Bogus %s block: %ulld metadataum %ulld\n", idxnames[lev+1], blk, midx);
		return;
	case 0:
		fprint(fd, "Doubly allocated %s block: %ulld meta %ulld\n", idxnames[lev+1], blk, midx);
		break;
	}
	iblk = cbread(blk);
	if(iblk == nil) {
		fprint(fd, "unexpected error reading block %ulld\n", blk);
		return;
	}
	for(i = 0; i < BlkSize / sizeof(uvlong); ++i) {
		if(iblk[i] == 0)
			continue;
		if(lev > 0)
			chkidxalloc(fd, shadow, iblk[i], midx, lev - 1);
		else {
			switch(markinuse(shadow, iblk[i])) {
			case -1:
				fprint(fd, "Bogus %s block: %ulld in %s block %ulld meta %ulld\n",
					idxnames[lev], iblk[i], idxnames[lev+1], blk, midx);
				i = BlkSize / sizeof(uvlong);
				break;
			case 0:
				fprint(fd, "Doubly allocated %s block: %ulld in %s block %ulld meta %ulld\n",
					idxnames[lev], iblk[i], idxnames[lev+1], blk, midx);
				break;
			}
		}
	}
	brelease(blk);
}

void
checkalloc(int fd)
{
	GMeta mb;
	uvlong *hblk;
	char *shadow, *fb;
	uvlong idx;
	long i;
	int j, k, l;

	fprint(fd, "Initializing shadow free map\n");
	shadow = θmalloc(super.nfreemap * BlkSize);
	for(i = super.firstdat; i < super.nblk; ++i)
		shadow[i/8] |= 1 << (i % 8);
	for(i = 0, j = 0; j < super.nhashblk; ++j) {
		hblk = cbread(j + 1);
		for(k = 0; k < BlkSize / sizeof(uvlong) && i < super.nht; ++k, ++i) {
			if(i % 100000 == 0)
				fprint(fd, ".");
			idx = hblk[k];
			while(idx != 0) {
				switch(markinuse(shadow, idx)) {
				case -1:
					fprint(fd, "Bogus block number in hash list for bucket %ld\n", i);
					idx = 0;
					break;
				case 0:
					fprint(fd, "Doubly allocated block in hash table: %ulld bucket %ld\n", idx, i);
				default:
					if(cread(&idx, sizeof(uvlong), idx * BlkSize + (BlkSize - sizeof(uvlong))) < 0) {
						fprint(fd, "Error reading bucket next link: %ld %ulld\n", i, idx);
						idx = 0;
					}
					if(idx != 0)
						fprint(fd, ",");
					break;
				}
			}
		}
		brelease(j + 1);
	}
	fprint(fd, "Scanning metadata\n");
	for(idx = 1; idx < super.nmeta * GMperBlk; ++idx) {
		if(idx % 100000 == 0)
			fprint(fd, ".");
		getmstruct(-1, &mb, idx);
		if(mb.type != MTint)
			continue;
		if(strcmp(mb.name, "index") == 0)
			chkidxalloc(fd, shadow, mb.m.val, idx, 0);
		else if(strcmp(mb.name, "indirect") == 0)
			chkidxalloc(fd, shadow, mb.m.val, idx, 1);
		else if(strcmp(mb.name, "dblindir") == 0)
			chkidxalloc(fd, shadow, mb.m.val, idx, 2);
	}
	fprint(fd, "Comparing to on-disk free map\n");
	l = 0;
	for(j = 0; j < super.nfreemap; ++j) {
		fb = cbread(super.freemap + j);
		for(k = 0; k < BlkSize; ++k) {
			if(fb[k] != shadow[j*BlkSize+k]) {
				if(++l < 10)		/* don't flood the output with too many */
					fprint(fd, "%d:%02ux-%02ux\n", j*BlkSize+k, fb[k], (uchar)shadow[j*BlkSize+k]);
				if((fb[k] & shadow[j*BlkSize+k]) != fb[k]) {
					fprint(fd, "Marking in use\n");
					fb[k] &= shadow[j*BlkSize + k];
					cbwrite(super.freemap + j);
				}
			}
		}
		brelease(super.freemap + j);
	}
	free(shadow);
}

void
mprint(int fd, uvlong idx)
{
	GMeta mb;
	char *p;
	int i, n;

	getmstruct(-1, &mb, idx);
	switch(mb.type) {
	case MTnone:
		fprint(fd, "Meta:%ulld name:%s type:none next:%ulld val:%ulld\n", idx, mb.name, mb.next, mb.m.val);
		break;
	case MTint:
		fprint(fd, "Meta:%ulld name:%s type:%d next:%ulld val:%ulld(%016ullx)\n", idx, mb.name, mb.type, mb.next, mb.m.val, mb.m.val);
		break;
	case MTistring:
		fprint(fd, "Meta:%ulld name:%s type:%d next:%ulld val:%s\n", idx, mb.name, mb.type, mb.next, mb.m.str);
		break;
	case MTstring:
		p = getblob(-1, mb.m.val, nil);
		fprint(fd, "Meta:%ulld name:%s type:%d next:%ulld val:(%ulld)%s\n", idx, mb.name, mb.type, mb.next, mb.m.val, p);
		free(p);
		break;
	case MTblob:
		fprint(fd, "Meta:%ulld name:%s type:%d next:%ulld ", idx, mb.name, mb.type, mb.next);
		p = getblob(-1, mb.m.val, &n);
		for(i = 0; i < n; ++i)
			fprint(fd, " %02x", p[i]);
		fprint(fd, "\n");
		free(p);
		break;
	default:
		fprint(fd, "unknown Meta:%ulld type%d next%ulld\n", idx, mb.type, mb.next);
		break;
	}
}

void
mpred(int fd, uvlong idx)
{
	GMeta mb;
	uvlong i;

	for(i = 1; i < super.nmeta * GMperBlk; ++i) {
		getmstruct(-1, &mb, i);
		if(mb.type != MTnone && mb.next == idx) {
			fprint(fd, "Meta:%ulld predecessor:%ulld\n", idx, i);
			mprint(fd, i);
			return;
		}
	}
}

static void
idxuse(int fd, uvlong iblk, uvlong blk, uvlong midx, int lev)
{
	uvlong *bp;
	int i;

	if(iblk == 0)
		return;
	if(iblk == blk)
		fprint(fd, "%s entry meta: %ulld\n", idxnames[lev+1], midx);
	bp = cbread(iblk);
	if(bp == nil) {
		fprint(fd, "error reading block %ulld\n", iblk);
		return;
	}
	for(i = 0; i < BlkSize / sizeof(uvlong); ++i) {
		if(bp[i] == blk)
			fprint(fd, "%s block in %s block %ulld in meta %ulld\n", idxnames[lev], idxnames[lev+1], iblk, midx);
		if(lev > 0)
			idxuse(fd, bp[i], blk, midx, lev - 1);
	}
	brelease(iblk);
}

void
blockuse(int fd, uvlong blk)
{
	GMeta mb;
//	PQMap *pq, *pend;
//	char *p;
	uvlong /* hlist, */ midx;
//	long i;

	if(blk == 0) {
		fprint(fd, "superblock\n");
		return;
	}
	if(blk < super.nhashblk + 1) {
		fprint(fd, "P2Q hash table\n");
		return;
	}
	if(blk < 2 * super.nhashblk + 1) {
		fprint(fd, "Q2M hash table\n");
		return;
	}
	if(blk >= super.freemap && blk < super.freemap + super.nfreemap) {
		fprint(fd, "free bitmap\n");
		return;
	}
	if(blk >= super.firstmeta && blk < super.firstmeta + super.nmeta) {
		fprint(fd, "metadata structure pool\n");
		return;
	}
	if(blk >= super.firstblob && blk < super.firstblob + super.nblob) {
		fprint(fd, "string/blob pool\n");
		return;
	}
#ifdef NOTDEF
	for(i = 0; i < super.nht; ++i) {
		if(i % 100000 == 0)
			fprint(fd, ".");
		if(cread(&hlist, sizeof(uvlong), BlkSize + i * sizeof(uvlong)) < 0) {
			fprint(fd, "Error reading bucket %ld\n", i);
			continue;
		}
		if(hlist == blk)
			fprint(fd, "P2Q hash bucket %ld\n", i);
		while(hlist != 0) {
			p = cbread(hlist);
			if(p == nil) {
				fprint(fd, "Error reading hash list block %ulld\n", hlist);
				break;
			}
			pend = (PQMap *)(p + BlkSize);
			--pend;
//			for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) {
//			}
		}
	}
#endif
	for(midx = 1; midx < super.nmeta * GMperBlk; ++midx) {
		getmstruct(-1, &mb, midx);
		if(mb.type != MTint)
			continue;
		if(mb.m.val == 0)
			continue;
		if(strcmp(mb.name, "index") == 0)
			idxuse(fd, mb.m.val, blk, midx, 0);
		else if(strcmp(mb.name, "indirect") == 0)
			idxuse(fd, mb.m.val, blk, midx, 1);
		else if(strcmp(mb.name, "dblindir") == 0)
			idxuse(fd, mb.m.val, blk, midx, 2);
	}
}

void
fixfamilies(int fd)
{
	GMeta mb;
	uvlong midx;

	for(midx = 1; midx < super.nmeta * GMperBlk; ++midx) {
		if(getmstruct(-1, &mb, midx) < 0)
			continue;
		if(mb.type != MTint)
			continue;
		if(strcmp(mb.name, "child") != 0 && strcmp(mb.name, "sib") != 0)
			continue;
		if(mb.m.val == 0)
			continue;
		if(q2m(-1, mb.m.val, 0) == 0) {
			fprint(fd, "clearing dangling %s:%ulld in meta struct %ulld\n", mb.name, mb.m.val, midx);
			mb.m.val = 0;
			savemstruct(&mb, midx);
		}
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].