code: mafs

ref: 5c21eca829380c28769f79beabb7cc51f1787f97
dir: /sub.c/

View raw version
#include	"all.h"

extern Srv mysrv;

Tlock*
tlocked(Iobuf *, Dentry *d)
{
	Tlock *t, *t1;
	long qpath, tim;

	tim = time(0);
	qpath = d->qid.path;
	t1 = 0;
	for(t=tlocks+NTLOCK-1; t>=tlocks; t--) {
		if(t->qpath == qpath)
		if(t->time >= tim)
			return 0;		/* its locked */
		if(!t1 && t->time < tim)
			t1 = t;			/* steal first lock */
	}
	if(t1) {
		t1->qpath = qpath;
		t1->time = tim + TLOCK;
	}
	/* botch
	 * out of tlock nodes simulates
	 * a locked file
	 */
	return t1;
}

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

	sb = getbufchk(Bsuper, 1, Bmod, 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;
}

Iobuf *
allocblocks(u16 len, int flags, int tag, u64 qpath)
{
	u64 blkno;
	Iobuf *buf;

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

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

/* the buf should have been wlock()'ed */
void
freeblockbuf(Iobuf *buf)
{
	if((buf->flags&Bmod) == 0)
		panic("freeblockbuf without Bmod");

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

/* add the block to the extents used to manage free blocks */
void
freeblocks(u64 blkno, u16 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, Bmod, 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, 1, Bmod, 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((u8*)s);
	}
	putbuf(sb);
}

/* almost obsolete as all writes except for the free extents are synchronous */
void
sync(void)
{
}

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

/*	"When we say that something was written to the disk, it should be written"
	Bimm is obsolete as all writes are synchronous
 */

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

	b = getbuf(bno, len, Bmod);
	memset(b->io, 0, len*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;
	u16 len;

	b = getbuf(dblkno, 1, Bmod);
	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){
		len = (size+sizeof(Tag))/Rawblocksize;
		if((size+sizeof(Tag))%Rawblocksize > 0)
			len++;
		d->dspans[0] = (Spanid){contentblkno,len};
	}
	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, 1, Bmod);
	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->dspans[0] = (Spanid){Bbkp, 1};
	d->dspans[1] = (Spanid){Bdconfig, 1};
	d->dspans[2] = (Spanid){Bdctl, 1};
	d->dspans[3] = (Spanid){Bdsuper, 1};
	d->dspans[4] = (Spanid){Bdusers, 1};
	d->dspans[5] = (Spanid){Bdfrees, 1};
	putbuf(b);

	b = getbuf(Bbkp, 1, Bmod);
	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->dspans[0] = (Spanid){Bdconfig0, 1};
	d->dspans[1] = (Spanid){Bdsuper0, 1};
	d->dspans[2] = (Spanid){Bdroot0, 1};
	d->dspans[3] = (Spanid){Bdconfig1, 1};
	d->dspans[4] = (Spanid){Bdsuper1, 1};
	d->dspans[5] = (Spanid){Bdroot1, 1};
	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, 1, Bmod);
	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, 1, Bmod);
	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->dspans[0] = (Spanid){Badm, 1};
	putbuf(b);
}

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

	nbused = Nbused;
	sbuf = getbuf(Bsuper, 1, Bmod);
	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], 1, Qpconfig0+3*i);
		reamdata(config.super.dest[i], 1, Qpsuper0+3*i);
		reamdata(config.root.dest[i], 1, 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, 1, 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("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[] = "mafs 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, 1);

	superream(size, nblocks);

	iob = getbuf(Bmagicb, 1, Bmod);
	snprint((s8*)iob->io->buf+256, Rawblocksize-256, "%s%d\n%llud\n",
			magic, Rawblocksize, Maxspansize);
	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, 1, Breadonly, Tdentry, qpath);
	if(b == nil){
		putbuf(b);
		panic("%s",errstring[Ephase]);
		return 0;
	}
	d = &b->io->d;
	ret = d->dspans[0].blkno;
	putbuf(b);
	return ret;
}

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

	size = devinit(devfile);
	if(size == 0)
		panic("null size %s", devfile);

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

	/* check magic */
	b = getbufchk(Bmagicb, 1, 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 %d rbufsize %d\n",
				Rawblocksize, rbufsize);
		panic("bad Rawblocksize != rbufsize");
	}
	maxspansize = strtoul(rptr+1, nil, 10);
	if(maxspansize != Maxspansize){
		print("init incorrect block size Maxspansize %llud maxspansize %ud\n",
				Maxspansize, maxspansize);
		panic("bad Maxspansize != maxspansize");
	}
	putbuf(b);

	/* check super */
	sb = getbufchk(Bsuper, 1, 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);
}