code: mafs

ref: fb53d7e68bcb2108fb304c81dccb1d7ff50ffa1f
dir: /sub.c/

View raw version
#include	"all.h"

extern Srv mysrv;

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

	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	if(sb == nil){
		panic("newqpath: sb == nil\n");
	}
	s = (Dentry*)sb->new;
	qpath = s->qidgen++;
	putbuf(sb, 1);
	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.super.dest[0] ||
		bno == config.root.dest[0])
		return 1;
	else
		return 0;
}

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

	if(len > Maxdatablockunits)
		panic("allocblocks(): invalid len %llud tag %s qpath %llud\n",
				len, tagnames[tag], qpath);
	blkno = balloc(&frees, len);
	if(chatty9p > 1)
		dprint("alloc %llud\n", blkno);

	/* cannot do getbufchk() unless we ream the whole disk at start */
	buf = getbuf(blkno, len, Bwritable, Bfreshalloc);

	/* clear the buf to avoid leaks on reuse */
	memset(buf->xiobuf, 0, len*Blocksize);
	if(tag == Tdata){
		buf->io->len = len;
		buf->xiobuf64[len*Nu64perblock -1] = qpath;
	}else
		recentmetadata(buf->m, &buf->cur, &buf->new);
	settag(buf, tag, qpath);
	return buf;
}

Iobuf *
allocmeta(int tag, u64 qpath)
{
	Iobuf *b;

	b = allocblocks(Metadataunits, tag, qpath);
	if(b == nil)
		panic("out of free blocks\n");
	b->new->verd++;
	return b;
}

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

	buf->tag = Tblank;
	/* clear the buf to avoid leaks on reuse */
	memset(buf->xiobuf, 0, buf->len*Blocksize);
	bfree(&frees, buf->blkno, buf->len);
	putbuf(buf, 0);
}

/* add the block to the extents used to manage free blocks */
void
freeblocks(u64 blkno, u64 len, 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, len, 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;
	Dentry *s;

	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	if(sb == nil){
		panic("fsok: sb == nil\n");
	}
	s = (Dentry*)sb->new;
	s->fsok = ok;
	if(chatty9p > 1){
		dprint("fsok ok %d\n", ok);
		showsuper(2, (u8*)s);
	}
	putbuf(sb, 1);
}

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

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

	b = getmeta(dblkno, Bwritable, Bfreshalloc);
	if(b == nil)
		panic("reamfile dblkno %llud b == nil\n", dblkno);
	settag(b, Tdentry, qpath);
	d = (Dentry*)b->new;
	d->namelen = strlen(name);
	strcpy(d->name, name);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = qpath;
	d->version = 0;
	d->mtime = nsec();
	d->size = size;
	d->pdblkno = pdblkno;
	d->pqpath = pqpath;
	putbuf(b, 1);
}

/*
	The reamer is added to the adm group.
	The ctl file is owned by the ream'er and the adm group.
 */
char magic[] = "mafs device\n";
void
reamdefaults(u64 bdconfig0, u64 bdsuper0, u64 bdroot0)
{
	Iobuf *b;
	Dentry *d;
	char users[128+Userlen*3], cbuf[Ddatasize];
	char *user;
	int userslen, n;

	user = getuser();
	userslen = snprint(users, 128+Userlen*3, "-1:adm:adm:%s\n"
					"0:none:adm:\n"		/* user ID for "none" */
					"9999:noworld::\n"	/* conventional id for "noworld" group */
					"10000:sys::\n"
					"10001:upas:upas:\n"	/* what is this for? */
					"10006:%s:%s:\n", user, user, user);

	/* cannot show this in /adm though as the block number is 0 */
	b = getmeta(Bdmagic, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpmagic);
	d = (Dentry*)b->new;
	d->namelen = 5;
	strncpy(d->name, "magic", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qpath = Qpmagic;
	d->version = 0;
	d->mtime = nsec();
	d->pdblkno = Bdadm;
	d->pqpath = Qpadm;
	n = snprint((s8*)d->buf, Ddatasize, "%s%llud\n",
			magic, Blocksize);
	d->size = n;
	putbuf(b, 1);

	b = getmeta(Bdadm, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpadm);
	d = (Dentry*)b->new;
	d->namelen = 3;
	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->qpath = Qpadm;
	d->version = 0;
	d->mtime = nsec();
	d->pdblkno = Bdroot;
	d->pqpath = Qproot;
	d->dblocks[0] = Bdconfig;
	d->dblocks[1] = Bdsuper;
	d->dblocks[2] = Bdusers;
	d->dblocks[3] = Bdbkp;
	d->dblocks[4] = Bdfrees;
	d->dblocks[5] = Bdctl;
	putbuf(b, 1);

	b = getmeta(Bdbkp, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpbkp);
	d = (Dentry*)b->new;
	d->namelen = 3;
	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->qpath = Qpbkp;
	d->version = 0;
	d->mtime = nsec();
	d->size = strlen(users)+1;
	d->pdblkno = Bdadm;
	d->pqpath = Qpadm;
	d->dblocks[0] = bdconfig0;
	d->dblocks[1] = bdsuper0;
	d->dblocks[2] = bdroot0;
	putbuf(b, 1);

	b = getmeta(Bdusers, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpusers);
	d = (Dentry*)b->new;
	d->namelen = 5;
	strncpy(d->name, "users", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qpath = Qpusers;
	d->version = 0;
	d->mtime = nsec();
	d->size = strlen(users)+1;
	d->pdblkno = Bdadm;
	d->pqpath = Qpadm;
	d->dblocks[0] = Bdusersinuse;
	d->dblocks[1] = Bdusersstaging;
	putbuf(b, 1);

	b = getmeta(Bdusersinuse, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpusersinuse);
	d = (Dentry*)b->new;
	d->namelen = 5;
	strncpy(d->name, "inuse", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpusersinuse;
	d->version = 0;
	d->mtime = nsec();
	d->size = userslen;
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	strcpy((s8*)d->buf, users);
	putbuf(b, 1);

	b = getmeta(Bdroot, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qproot);
	d = (Dentry*)b->new;
	d->namelen = 1;
	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->qpath = Qproot;
	d->version = 0;
	d->mtime = nsec();
	d->dblocks[0] = Bdadm;
	putbuf(b, 1);

	b = getmeta(Bdconfig, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpconfig);
	d = (Dentry*)b->new;
	d->namelen = 6;
	strncpy(d->name, "config", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpconfig;
	d->version = 0;
	d->mtime = nsec();
	memset(cbuf, 0, Ddatasize);
	d->size = configstr(cbuf, Ddatasize);
	d->pdblkno = Bdadm;
	d->pqpath = Qpadm;
	strncpy((s8*)d->buf, cbuf, d->size);
	putbuf(b, 1);

	reamfile(Bdctl, Qpctl, "ctl", 0, Bdadm, Qpadm);
	reamfile(Bdfrees, Qpfrees, "frees", 0, Bdadm, Qpadm);

	reamfile(Bdusersstaging, Qpusersstaging, "staging",
			 0, Bdusers, Qpusers);
}

void
superream(u64 size, u64 nblocks)
{
	Iobuf *sbuf;
	Dentry *s;

	sbuf = getbuf(Bdsuper, Metadataunits, Bwritable, Bfreshalloc);
	if(sbuf == nil)
		panic("superream: sbuf == nil");
	sbuf->cur = sbuf->new = (Metadataunit*)sbuf->m;
	settag(sbuf, Tdentry, Qpsuper);
	s = (Dentry*)sbuf->new;
	s->verd++;
	s->namelen = 5;
	strncpy(s->name, "super", 6);
	s->uid = s->muid = s->gid = -1;
	s->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	s->qpath = Qpsuper;
	s->version = 0;
	s->mtime = nsec();
	s->size = sizeof(Super);
	s->pdblkno = Bdadm;
	s->pqpath = Qpadm;
	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-Bdconfig;
	config.super.dest[0] = nblocks-Bdsuper;
	config.root.dest[0] = nblocks-6;

	/* this will enable backups */
	config.config.srcbno = Bdconfig;
	config.super.srcbno = Bdsuper;
	config.root.srcbno = Bdroot;
	strncpy(config.service, service, Namelen);

	reamfile(config.config.dest[0], Qpconfig0, "config.0",
			 0, Bdbkp, Qpbkp);
	reamfile(config.super.dest[0], Qpsuper0, "super.0",
			 0, Bdbkp, Qpbkp);
	reamfile(config.root.dest[0], Qproot0, "root.0",
			 0, Bdbkp, Qpbkp);
	putbuf(sbuf, 1);

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

	reamdefaults(config.config.dest[0], config.super.dest[0], config.root.dest[0]);

	if(config.root.dest[0]-Nbused > 0)
		bfree(&frees, Nbused, config.root.dest[0]-Nbused);
	else if (config.root.dest[0] == Nbused)
		dprint("mafs: %s no free blocks: config.root.dest[0] %llud Nbused %ud Nminblocks %llud\n",
				service, config.root.dest[0], Nbused, Nminblocks);
	else
		panic("not enough blocks for mafs config.root.dest[0] %llud Nbused %ud Nminblocks %d\n",
				config.root.dest[0], Nbused, Nminblocks);
	if(chatty9p > 1)
		showextents(2, "free extents: ", &frees);
	if(chatty9p > 1)
		dprint("done\n");

	fsok(0);
}

/*
	open the dev file OEXCL?
		cannot do exclusive use file as the fd could get invalid
		if no I/O is done for an extended period.
 */
void
ream(u64 size)
{
	char buf[Blocksize];
	int i;
	u64 nblocks;

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

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

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

	superream(size, nblocks);
	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
 */
void
init(int doream, u64 size)
{
	u32 unitsize;
	Iobuf *sb, *b;
	Dentry *s;
	s8 *rptr;

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

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

	/* check super */
	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	if(sb == nil){
		panic("Invalid super: %s",errstring[Ephase]);
		return;
	}
	s = (Dentry*)sb->new;
	if(config.size != size){
		dprint("corrupted disk or disk size changed:"
				" config.size %llud size %llud\n"
				"This is an unrecoverable situation."
				" Ream the disk and start over.\n",
				config.size, size);
		panic(errstring[Edirty]);
	}
	if(s->fsok != 1){
		panic("There was an unsafe shutdown:\n"
				"	use \'disk/fsck %s\' to check the disk state.\n"
				"	Or, use \'disk/fsok %s\' to flip the fsok flag.\n",
				devfile, devfile);
		panic(errstring[Edirty]);
	}

	s->fsok = 0;
	putbuf(sb, 1);
	shuttingdown = 0;
	if(doream == 0)
		clearfrees();
	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);
}