code: mafs

ref: 9cf2807db9e8268a4dfbce19561bd712e4430a39
dir: /sub.c/

View raw version
#include	"all.h"

extern Srv mysrv;

u64
newqpath()
{
	u64 qpath;
	Iobuf *sb;
	Super *s;
	s32 on, nn;
	s8 buf[Ddatasize-16];

	sb = getbufchk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	if(sb == nil){
		panic("newqpath: sb == nil\n");
	}
	s = (Super*)sb->d;
	on = snprint(buf, Ddatasize-16, "%llud", s->qidgen);
	qpath = s->qidgen++;
	nn = snprint(buf, Ddatasize-16, "%llud", s->qidgen);
	if(on != nn)
		s->size += nn-on;
	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;
	u16 len;

	len = blklen(tag);
	blkno = balloc(&frees, len);
	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, len, Bwritable, Bfreshalloc);
	/* clear the buf to avoid leaks on reuse */
	memset(buf->xiobuf, 0, len*Unit);
	settag(buf, tag, qpath);
	return buf;
}

/* 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, buf->len*Unit);
	bfree(&frees, buf->blkno, buf->len);
	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;
	Super *s;

	sb = getbufchk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	if(sb == nil){
		panic("fsok: sb == nil\n");
	}
	s = (Super*)sb->d;
	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
reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath, u64 contentblkno)
{
	Iobuf *b;
	Dentry *d;

	b = getbuf(dblkno, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->io, 0, Dentrysize);
	settag(b, Tdentry, qpath);
	d = b->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);
}

/*
	The reamer is added to the adm group.
	The ctl file is owned by the ream'er and the adm group.
 */
char magic[] = "m[a]fs device\n";
void
reamdefaults(u64 bdconfig0, u64 bdsuper0, u64 bdroot0, u64 bdconfig1, u64 bdsuper1, u64 bdroot1)
{
	Iobuf *b;
	Dentry *d;
	char users[128+Userlen*3];
	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 = getbuf(Bdmagic, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qpmagic);
	d = b->d;
	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->qid.path = Qpmagic;
	d->qid.version = 0;
	d->mtime = nsec();
	d->pdblkno = Bdadm;
	d->pqpath = Qpadm;
	n = snprint((s8*)d->buf, Ddatasize, "%s%llud\n",
			magic, Unit);
	d->size = n;
	putbuf(b);

	b = getbuf(Bdadm, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qpadm);
	d = b->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 = 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);

	b = getbuf(Bdbkp, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qpbkp);
	d = b->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 = Bdadm;
	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);

	b = getbuf(Bdusers, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qpusers);
	d = b->d;
	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->qid.path = Qpusers;
	d->qid.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);

	b = getbuf(Bdusersinuse, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qpusersinuse);
	d = b->d;
	strncpy(d->name, "inuse", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qid.path = Qpusersinuse;
	d->qid.version = 0;
	d->mtime = nsec();
	d->size = userslen;
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	strcpy((s8*)d->buf, users);
	putbuf(b);

	b = getbuf(Bdroot, Dentryunits, Bwritable, Bfreshalloc);
	memset(b->d, 0, Dentrysize);
	settag(b, Tdentry, Qproot);
	d = b->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] = Bdadm;
	putbuf(b);

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

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

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

	nbused = Nbused;
	sbuf = getbuf(Bdsuper, Dentryunits, Bwritable, Bfreshalloc);
	if(sbuf == nil)
		panic("superream: sbuf == nil");
	memset(sbuf->d, 0, Dentrysize);
	settag(sbuf, Tdentry, Qpsuper);
	s = (Super*)sbuf->d;
	strncpy(s->name, "super", 6);
	s->uid = s->muid = s->gid = -1;
	s->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	s->qid.path = Qpsuper;
	s->qid.version = 0;
	s->mtime = nsec();
	s->size = 18;
	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.config.dest[1] = nbused+((nblocks-nbused)/2)-Bdconfig;
	config.super.dest[0] = nblocks-Bdsuper;
	config.super.dest[1] = nbused+((nblocks-nbused)/2)-Bdsuper;
	config.root.dest[0] = nblocks-3;
	config.root.dest[1] = nbused+((nblocks-nbused)/2)-3;

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

	reamfile(config.config.dest[1], Qpconfig1, "config.1",
			 0, Bdbkp, Qpbkp, config.config.dest[1]);
	reamfile(config.super.dest[1], Qpsuper1, "super.1",
			 0, Bdbkp, Qpbkp, config.super.dest[1]);
	reamfile(config.root.dest[1], Qproot1, "root.1",
			 0, Bdbkp, Qpbkp, config.root.dest[1]);
	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]);

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

	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 = Bdconfig;
	config.super.srcbno = Bdsuper;
	config.root.srcbno = Bdroot;
	strncpy(config.service, service, Namelen);

	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[Unit];
	int i;
	u64 nblocks;

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

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

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

	superream(size, nblocks);
	writeconfig(Bdconfig);
	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;
	Super *s;
	s8 *rptr;

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

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

	/* check super */
	sb = getbufchk(Bdsuper, Breadonly, Tdentry, Qpsuper);
	if(sb == nil){
		panic("Invalid super: %s",errstring[Ephase]);
		return;
	}
	s = (Super*)sb->io;
	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);
}