code: mafs

ref: b5d191e8cfa4fac05a02ea309df2541a2652ff23
dir: /sub.c/

View raw version
#include	"all.h"

extern Srv mysrv;

u64
newqpath()
{
	u64 qpath;
	Iobuf *sb;
	Superb *s;

	sb = getbufchk(Bsuper, Bwritable, Tdata, Qpsuper);
	if(sb == nil){
		panic("newqpath: sb == nil\n");
	}
	s = (Superb*)sb->io->buf;
	qpath = s->qidgen++;
	putbuf(sb);
	return qpath;
}

/*
 * what are legal characters in a name?
 * only disallow control characters.
 * a) utf avoids control characters.
 * b) '/' may not be the separator
 */
int
checkname(char *n)
{
	int i, c;

	for(i=0; i<Namelen; i++) {
		c = *n & 0xff;
		if(c == 0) {
			if(i == 0)
				return 1;
			memset(n, 0, Namelen-i);
			return 0;
		}
		if(c <= 040)
			return 1;
		n++;
	}
	return 1;	/* too long */
}

u64
min(u64 a, u64 b)
{
	if(a<b)
		return a;
	else
		return b;
}

/* identify if the block is being used as a backup block */
int
isbkp(u64 bno)
{
	if(bno == config.config.dest[0] || bno == config.config.dest[1] ||
		bno == config.super.dest[0] || bno == config.super.dest[1] ||
		bno == config.root.dest[0] || bno == config.root.dest[1])
		return 1;
	else
		return 0;
}

/* making the assumption that all allocations are not readonly */
Iobuf *
allocblock(int tag, u64 qpath)
{
	u64 blkno;
	Iobuf *buf;

	blkno = balloc(&frees, 1);
	if(blkno == 0)
		return nil;	/* the caller should trigger an Efull message */

	if(chatty9p > 1)
		dprint("alloc %llud\n", blkno);
	/* cannot do getbufchk() unless we ream the whole disk at start */
	buf = getbuf(blkno, Bwritable, Bfreshalloc);
	/* clear the buf to avoid leaks on reuse */
	memset(buf->xiobuf, 0, Rawblocksize);
	settag(buf, tag, qpath);
	return buf;
}

u64
allocblocks(int tag, u64 qpath, u64 len, Iobuf **bufs)
{
	u64 blkno, i;
	Iobuf *buf;

	blkno = balloc(&frees, len);
	if(blkno == 0)
		return 0;	/* the caller should trigger an Efull message */

	if(chatty9p > 1)
		dprint("alloc %llud\n", blkno);

	for(i = 0; i < len; i++){
		/* cannot do getbufchk() unless we ream the whole disk at start */
		bufs[i] = buf = getbuf(blkno+i, Bwritable, Bfreshalloc);
		/* clear the buf to avoid leaks on reuse */
		memset(buf->xiobuf, 0, Rawblocksize);
		settag(buf, tag, qpath);
	}

	return len;
}

/* the buf should have been wlock()'ed */
void
freeblockbuf(Iobuf *buf)
{
	if(buf->readers)
		panic("freeblockbuf without Bwritable");

	/* clear the buf to avoid leaks on reuse */
	memset(buf->xiobuf, 0, Rawblocksize);
	bfree(&frees, buf->blkno, 1);
	putbuffree(buf);
}

/* add the block to the extents used to manage free blocks */
void
freeblock(u64 blkno, u16 tag, u64 qpath)
{
	Iobuf *buf;

	if(blkno == 0 || blkno > config.nblocks){
		panic("freeblock: bad addr %llud, nblocks %llud\n",
				 blkno, config.nblocks);
		return;
	}
	buf = getbufchk(blkno, Bwritable, tag, qpath);
	if(buf == nil)
		dprint("%s",errstring[Ephase]);
	freeblockbuf(buf);
}

int
Gfmt(Fmt *f1)
{
	u16 t;

	t = va_arg(f1->args, u16);
	if(t < MAXTAG)
		return fmtstrcpy(f1, tagnames[t]);
	else
		return fmtprint(f1, "<badtag %d>", t);
}

void
formatinit(void)
{
	fmtinstall('G', Gfmt);	/* print tags */
}

/* should be called with wlock(t)? */
void
fsok(int ok)
{
	Iobuf *sb;
	Superb *s;

	sb = getbufchk(Bsuper, Bwritable, Tdata, Qpsuper);
	if(sb == nil){
		panic("newqpath: sb == nil\n");
	}
	s = (Superb*)sb->io->buf;
	s->fsok = ok;
	if(chatty9p > 1){
		dprint("fsok ok %d\n", ok);
		showsuper(2, (u8*)s);
	}
	putbuf(sb);
}

void
closefd(int fd)
{
	if(fd != -1)
		close(fd);
}

void
reamdata(u64 bno, u64 qpath)
{
	Iobuf *b;

	b = getbuf(bno, Bwritable, Bfreshalloc);
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdata, qpath);
	putbuf(b);
}

void
reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath, u64 contentblkno)
{
	Iobuf *b;
	Dentry *d;

	b = getbuf(dblkno, Bwritable, Bfreshalloc);
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdentry, qpath);
	d = &b->io->d;
	strcpy(d->name, name);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qid.path = qpath;
	d->qid.version = 0;
	d->mtime = nsec();
	d->size = size;
	d->pdblkno = pdblkno;
	d->pqpath = pqpath;
	if(contentblkno > 0)
		d->dblocks[0] = contentblkno;
	putbuf(b);
}

/*
	TODO
	The reamer is added to the sys group.
	The ctl file is owned by the ream'er and the sys group.
 */
void
reamdefaults(void)
{
	Iobuf *b;
	Dentry *d;
	char users[] = "-1:adm:adm:\n"
					"0:none:adm:\n"		/* user ID for "none" */
					"9999:noworld::\n"	/* conventional id for "noworld" group */
					"10000:sys::\n"
					"10001:upas:upas:\n"
					"10002:bootes:bootes:\n"
					"10006:glenda:glenda:\n"
					"10007:manies::\n";

	b = getbuf(Badm, Bwritable, Bfreshalloc);
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdentry, Qpadm);
	d = &b->io->d;
	strncpy(d->name, "adm", 4);
	d->uid = d->muid = d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qid.path = Qpadm;
	d->qid.version = 0;
	d->mtime = nsec();
	d->pdblkno = Broot;
	d->pqpath = Qproot;
	d->dblocks[0] = Bbkp;
	d->dblocks[1] = Bdconfig;
	d->dblocks[2] = Bdctl;
	d->dblocks[3] = Bdsuper;
	d->dblocks[4] = Bdusers;
	d->dblocks[5] = Bdfrees;
	putbuf(b);

	b = getbuf(Bbkp, Bwritable, Bfreshalloc);
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdentry, Qpbkp);
	d = &b->io->d;
	strncpy(d->name, "bkp", 4);
	d->uid = d->muid = d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qid.path = Qpbkp;
	d->qid.version = 0;
	d->mtime = nsec();
	d->size = strlen(users)+1;
	d->pdblkno = Badm;
	d->pqpath = Qpadm;
	d->dblocks[0] = Bdconfig0;
	d->dblocks[1] = Bdsuper0;
	d->dblocks[2] = Bdroot0;
	d->dblocks[3] = Bdconfig1;
	d->dblocks[4] = Bdsuper1;
	d->dblocks[5] = Bdroot1;
	putbuf(b);

	reamfile(Bdconfig, Qpconfig, "config", 0, Badm, Qpadm, Bconfig);
	reamfile(Bdctl, Qpctl, "ctl", 0, Badm, Qpadm, 0);
	reamfile(Bdsuper, Qpsuper, "super", 0, Badm, Qpadm, Bsuper);
	reamfile(Bdusers, Qpusers, "users",
			 strlen(users)+1, Badm, Qpadm, Busers);
	reamfile(Bdfrees, Qpfrees, "frees", 0, Badm, Qpadm, 0);

	reamfile(Bdconfig0, Qpconfig0, "config.0",
			 Blocksize, Bbkp, Qpbkp, config.config.dest[0]);
	reamfile(Bdsuper0, Qpsuper0, "super.0",
			 Blocksize, Bbkp, Qpbkp, config.super.dest[0]);
	reamfile(Bdroot0, Qproot0, "root.0",
			 Blocksize, Bbkp, Qpbkp, config.root.dest[0]);

	reamfile(Bdconfig1, Qpconfig1, "config.1",
			 Blocksize, Bbkp, Qpbkp, config.config.dest[1]);
	reamfile(Bdsuper1, Qpsuper1, "super.1",
			 Blocksize, Bbkp, Qpbkp, config.super.dest[1]);
	reamfile(Bdroot1, Qproot1, "root.1",
			 Blocksize, Bbkp, Qpbkp, config.root.dest[1]);

	b = getbuf(Busers, Bwritable, Bfreshalloc);
	if(b == nil)
		panic("cannot get Busers");
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdata, Qpusers);
	strcpy((char*)b->io->buf, users);
	putbuf(b);
}

void
rootream(void)
{
	Iobuf *b;
	Dentry *d;

	b = getbuf(Broot, Bwritable, Bfreshalloc);
	if(b == nil)
		panic("rootream b == nil");
	memset(b->io, 0, Rawblocksize);
	settag(b, Tdentry, Qproot);

	d = &b->io->d;
	strncpy(d->name, "/", 5);
	d->uid = d->muid = -1;
	d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qid.path = Qproot;
	d->qid.version = 0;
	d->mtime = nsec();
	d->dblocks[0] = Badm;
	putbuf(b);
}

void
superream(u64 size, u64 nblocks)
{
	Iobuf *sbuf;
	Superb *s;
	u64 i, nbused;

	nbused = Nbused;
	sbuf = getbuf(Bsuper, Bwritable, Bfreshalloc);
	if(sbuf == nil)
		panic("superream: sbuf == nil");
	memset(sbuf->io, 0, Rawblocksize);
	settag(sbuf, Tdata, Qpsuper);
	s = (Superb*)sbuf->io->buf;
	s->qidgen = Nqidgen;
	s->fsok = 1;

	/* not bothering to ream the backup blocks,
		they will be overwritten almost immediately anyway */
	config.size = size;
	config.nblocks = nblocks;
	config.config.dest[0] = nblocks-Bconfig;
	config.config.dest[1] = nbused+((nblocks-nbused)/2)-Bconfig;
	config.super.dest[0] = nblocks-Bsuper;
	config.super.dest[1] = nbused+((nblocks-nbused)/2)-Bsuper;
	config.root.dest[0] = nblocks-Broot;
	config.root.dest[1] = nbused+((nblocks-nbused)/2)-Broot;
	for(i=0; i<Nbkp; i++){
		reamdata(config.config.dest[i], Qpconfig0+3*i);
		reamdata(config.super.dest[i], Qpsuper0+3*i);
		reamdata(config.root.dest[i], Qproot0+3*i);
	}
	putbuf(sbuf);

	if(chatty9p > 1)
	dprint("backup config %llud %llud"
		   " super %llud %llud"
		   " root %llud %llud\n",
				config.config.dest[0], config.config.dest[1],
				config.super.dest[0], config.super.dest[1],
				config.root.dest[0], config.root.dest[1]);

	reamdata(Bconfig, Qpconfig);
	reamdefaults();
	rootream();

	bfree(&frees, config.config.dest[1]+1, config.root.dest[0]-config.config.dest[1]-1);
	bfree(&frees, nbused, config.root.dest[1]-nbused);
	if(chatty9p > 1)
		showextents(2, "free extents: ", &frees);
	if(chatty9p > 1)
		dprint("done\n");

	/* this will enable backups */
	config.config.srcbno = Bconfig;
	config.super.srcbno = Bsuper;
	config.root.srcbno = Broot;
	strncpy(config.service, service, Namelen);

	fsok(0);
}

char magic[] = "m[a]fs device\n";
/* TODO open the dev file OEXCL? */
void
ream(u64 size)
{
	char buf[Rawblocksize];
	int i;
	u64 nblocks;
	Iobuf *iob;

	nblocks = size/Rawblocksize;
	if(nblocks <= Nminblocks)
		panic("not enough space");

	if(chatty9p > 1){
		dprint("%s %s ream %llud bytes into %llud blocks\n",
				service, devfile, size, nblocks);
		dprint("	(block size: raw %d bytes, usable %d bytes)\n",
				Rawblocksize, Blocksize);
		dprint("	(name length: %d bytes)\n", Namelen);
	}

	memset(buf, 0, Rawblocksize);
	for(i = 0; i < Nbused; i++)
		devwrite(i, buf);

	superream(size, nblocks);

	iob = getbuf(Bmagicb, Bwritable, Bfreshalloc);
	snprint((s8*)iob->io->buf+256, Rawblocksize-256, "%s%llud\n",
			magic, Rawblocksize);
	settag(iob, Tmagic, Qpmagic);
	putbuf(iob);

	writeconfig(Bconfig);

	fsok(1);
}

/*
 * init the devices
 * wipe some of the file systems, or all if ream is set
 * this code really assumes that only one file system exists
 */
/* TODO open the file OEXCL? */
u64
readbkpblock(u64 dblkno, u64 qpath) /* obsolete, using Bconfig for this info */
{
	Iobuf *b;
	Dentry *d;
	u64 ret;

	b = getbufchk(dblkno, Breadonly, Tdentry, qpath);
	if(b == nil){
		putbuf(b);
		panic("%s",errstring[Ephase]);
		return 0;
	}
	d = &b->io->d;
	ret = d->dblocks[0];
	putbuf(b);
	return ret;
}

void
init(int doream, u64 size)
{
	u32 rbufsize;
	Iobuf *sb, *b;
	Superb *s;
	s8 *rptr;

	if(doream)
		ream(size);
	else{
		initconfig(Bconfig);
		loadfrees(Bdfrees);
	}

	/* check magic */
	b = getbufchk(Bmagicb, Breadonly, Tmagic, Qpmagic);
	if(b == nil){
		panic("Invalid magic: %s",errstring[Ephase]);
		return;
	}
	if(strncmp((s8*)b->io->buf+256,magic,strlen(magic)) != 0){
		print("init: bad magic -%s-", (s8*)b->io+256);
		panic("bad magic");
	}
	rbufsize = strtoul((s8*)b->io->buf+256+strlen(magic), &rptr, 10);
	if(rbufsize != Rawblocksize){
		print("init incorrect block size Rawblocksize %llud rbufsize %d\n",
				Rawblocksize, rbufsize);
		panic("bad Rawblocksize != rbufsize");
	}
	putbuf(b);

	/* check super */
	sb = getbufchk(Bsuper, Breadonly, Tdata, Qpsuper);
	if(sb == nil){
		panic("Invalid super: %s",errstring[Ephase]);
		return;
	}
	s = (Superb*)sb->io->buf;
	if(s->fsok != 1 || config.size != size)
		panic(errstring[Edirty]);

	s->fsok = 0;
	putbuf(sb);
	shuttingdown = 0;
	usersinit();
}

/*
 * returns 1 if n is prime
 * used for adjusting lengths
 * of hashing things.
 * there is no need to be clever
 */
int
prime(long n)
{
	long i;

	if((n%2) == 0)
		return 0;
	for(i=3;; i+=2) {
		if((n%i) == 0)
			return 0;
		if(i*i >= n)
			return 1;
	}
}

void
hexdump(void *a, int n)
{
	char s1[30], s2[4];
	uchar *p;
	int i;

	p = a;
	s1[0] = 0;
	for(i=0; i<n; i++) {
		sprint(s2, " %.2ux", p[i]);
		strcat(s1, s2);
		if((i&7) == 7) {
			print("%s\n", s1);
			s1[0] = 0;
		}
	}
	if(s1[0])
		print("%s\n", s1);
}