code: mafs

ref: ce708f49bf5ec6c6b3fe1bb29d401c42b841b649
dir: /sub.c/

View raw version
#include	"all.h"

extern Srv mysrv;

u64
newqpaths(u8 n)
{
	u64 qpath;
	Iobuf *sb;
	Dentry *s;

	sb = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	s = sb->d;
	qpath = s->qidgen;
	s->qidgen += n;
	putbuf(sb, 1);
	return qpath;
}

u64
newqpath()
{
	return newqpaths(1);
}

/*
 * 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<strlen(n); i++) {
		c = *n & 0xff;
		if(c == 0) {
			if(i == 0)
				return 1;
			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);
	ualloc(&frees, len, &blkno);
	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, getcallerpc(&len));

	/* 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;
	}
	settag(buf, tag, qpath);
	return buf;
}

Iobuf *
allocmeta(int tag, u64 qpath)
{
	return allocblocks(1, tag, qpath);
}

u8
allocdentries(u16 n, Iobuf **iobufs, u64 qpath)
{
	u8 i, j;

	for(i = 0; i < n; i++){
		iobufs[i] = allocblocks(1, Tdentry, qpath+i);
		if(iobufs[i] == nil){
			for(j = 0; j < i; j++)
				freeblockbuf(iobufs[j]);
			return i;
		}
		iobufs[i]->d->qpath = qpath+i;
	}
	return i;
}

/* 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);
	ufree(&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 = egetbufchk(blkno, len, Bwritable, tag, qpath, getcallerpc(&blkno));
	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 = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	s = sb->d;
	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, u64 preli)
{
	Iobuf *b;
	Dentry *d;

	b = egetmeta(dblkno, Bwritable, Bfreshalloc);
	settag(b, Tdentry, qpath);
	d = b->d;
	USED(name);
/*	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 = 1;
	d->mtime = nsec();
	d->size = size;
	d->pdblkno = pdblkno;
	d->pqpath = pqpath;
	d->preli = preli;
	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 inle[] = "0 439 440\n\0";
	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 /a though as the block number is 0 */
	b = egetmeta(Bdmagic, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpmagic);
	d = b->d;
	// d->namelen = 5;
	// strncpy(d->name, "magic", 6);
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpmagic;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = 0;
	n = snprint((s8*)d->buf, Ddatasize, "%s%llud\n",
			magic, Blocksize);
	d->size = n;
	putbuf(b, 1);

	b = egetmeta(Bdrootnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qprootnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qprootnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdroot;
	d->pqpath = Qproot;
	d->preli = In;
	putbuf(b, 1);

	b = egetmeta(Bdrootlnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qprootlnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qprootlnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdroot;
	d->pqpath = Qproot;
	d->preli = Inl;
	putbuf(b, 1);

	b = egetmeta(Bdrootlnamesextents, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qprootlnamesextents);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qprootlnamesextents;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdroot;
	d->pqpath = Qproot;
	d->preli = Inle;
	d->size = sizeof inle;
	strcpy((s8*)d->buf, inle);
	putbuf(b, 1);

	b = egetmeta(Bdroot, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qproot);
	d = b->d;
	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 = 1;
	d->mtime = nsec();
	d->dblocks[0] = Bdrootnames;
	d->dblocks[1] = Bdrootlnames;
	d->dblocks[2] = Bdrootlnamesextents;
	d->dblocks[3] = Bda;
	addname(d, -1, 0, ".n");
	addname(d, -1, 1, ".nl");
	addname(d, -1, 2, ".nle");
	addname(d, -1, 3, "a");
	putbuf(b, 1);

	b = egetmeta(Bdanames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpanames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpanames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = In;
	putbuf(b, 1);

	b = egetmeta(Bdalnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpalnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpalnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = Inl;
	putbuf(b, 1);

	b = egetmeta(Bdalnamesextents, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpalnamesextents);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpalnamesextents;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = Inle;
	d->size = sizeof inle;
	strcpy((s8*)d->buf, inle);
	putbuf(b, 1);

	b = egetmeta(Bda, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpa);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode = DMDIR |
		((DMREAD|DMWRITE|DMEXEC) << 6) |
		((DMREAD|DMWRITE|DMEXEC) << 3) |
		((DMREAD|DMWRITE|DMEXEC) << 0);
	d->qpath = Qpa;
	d->version = 0;
	d->mtime = nsec();
	d->pdblkno = Bdroot;
	d->pqpath = Qproot;
	d->preli = 3;
	d->dblocks[0] = Bdanames;
	d->dblocks[1] = Bdalnames;
	d->dblocks[2] = Bdalnamesextents;
	d->dblocks[3] = Bdbkp;
	d->dblocks[4] = Bdconfig;
	d->dblocks[5] = Bdctl;
	d->dblocks[6] = Bdfrees;
	d->dblocks[7] = Bdsuper;
	d->dblocks[8] = Bdusers;
	addname(d, -1, 0, ".n");
	addname(d, -1, 1, ".nl");
	addname(d, -1, 2, ".nle");
	addname(d, -1, 3, "bkp");
	addname(d, -1, 4, "config");
	addname(d, -1, 5, "ctl");
	addname(d, -1, 6, "frees");
	addname(d, -1, 7, "super");
	addname(d, -1, 8, "users");
	putbuf(b, 1);

	b = egetmeta(Bdbkpnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpbkpnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpbkpnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdbkp;
	d->pqpath = Qpbkp;
	d->preli = In;
	putbuf(b, 1);

	b = egetmeta(Bdbkplnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpbkplnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpbkplnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdbkp;
	d->pqpath = Qpbkp;
	d->preli = Inl;
	putbuf(b, 1);

	b = egetmeta(Bdbkplnamesextents, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpbkplnamesextents);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpbkplnamesextents;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdbkp;
	d->pqpath = Qpbkp;
	d->preli = Inle;
	d->size = sizeof inle;
	strcpy((s8*)d->buf, inle);
	putbuf(b, 1);

	b = egetmeta(Bdbkp, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpbkp);
	d = b->d;
	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 = Bda;
	d->pqpath = Qpa;
	d->preli = 3;
	d->dblocks[0] = Bdbkpnames;
	d->dblocks[1] = Bdbkplnames;
	d->dblocks[2] = Bdbkplnamesextents;
	d->dblocks[3] = bdconfig0;
	d->dblocks[4] = bdroot0;
	d->dblocks[5] = bdsuper0;
	addname(d, -1, 0, ".n");
	addname(d, -1, 1, ".nl");
	addname(d, -1, 2, ".nle");
	addname(d, -1, 3, "config.0");
	addname(d, -1, 4, "root.0");
	addname(d, -1, 5, "super.0");
	putbuf(b, 1);

	b = egetmeta(Bdusersnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpusersnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpusersnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	d->preli = In;
	putbuf(b, 1);

	b = egetmeta(Bduserslnames, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpuserslnames);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpuserslnames;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	d->preli = Inl;
	putbuf(b, 1);

	b = egetmeta(Bduserslnamesextents, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpuserslnamesextents);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpuserslnamesextents;
	d->version = 1;
	d->mtime = nsec();
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	d->preli = Inle;
	d->size = sizeof inle;
	strcpy((s8*)d->buf, inle);
	putbuf(b, 1);

	b = egetmeta(Bdusers, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpusers);
	d = b->d;
	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 = 1;
	d->mtime = nsec();
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = 8;
	d->dblocks[0] = Bdusersnames;
	d->dblocks[1] = Bduserslnames;
	d->dblocks[2] = Bduserslnamesextents;
	d->dblocks[3] = Bdusersinuse;
	d->dblocks[4] = Bdusersstaging;
	addname(d, -1, 0, ".n");
	addname(d, -1, 1, ".nl");
	addname(d, -1, 2, ".nle");
	addname(d, -1, 3, "inuse");
	addname(d, -1, 4, "staging");
	putbuf(b, 1);

	b = egetmeta(Bdusersinuse, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpusersinuse);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpusersinuse;
	d->version = 1;
	d->mtime = nsec();
	d->size = userslen;
	d->pdblkno = Bdusers;
	d->pqpath = Qpusers;
	d->preli = 3;
	strcpy((s8*)d->buf, users);
	putbuf(b, 1);

	b = egetmeta(Bdconfig, Bwritable, Bfreshalloc);
	settag(b, Tdentry, Qpconfig);
	d = b->d;
	d->uid = d->muid = d->gid = -1;
	d->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	d->qpath = Qpconfig;
	d->version = 1;
	d->mtime = nsec();
	memset(cbuf, 0, Ddatasize);
	d->size = configstr(cbuf, Ddatasize);
	d->pdblkno = Bda;
	d->pqpath = Qpa;
	d->preli = 4;
	strncpy((s8*)d->buf, cbuf, d->size);
	putbuf(b, 1);

	reamfile(Bdctl, Qpctl, "ctl", 0, Bda, Qpa, 5);
	reamfile(Bdfrees, Qpfrees, "frees", 0, Bda, Qpa, 6);

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

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

	sbuf = getbuf(Bdsuper, 1, Bwritable, Bfreshalloc, getcallerpc(&size));
	settag(sbuf, Tdentry, Qpsuper);
	s = sbuf->d;
	s->uid = s->muid = s->gid = -1;
	s->mode =
		((DMREAD) << 6) |
		((DMREAD) << 3) |
		((DMREAD) << 0);
	s->qpath = Qpsuper;
	s->version = 1;
	s->mtime = nsec();
	s->size = sizeof(Super);
	s->pdblkno = Bda;
	s->pqpath = Qpa;
	s->preli = 7;
	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-3;

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

	reamfile(config.config.dest[0], Qpconfig0, "config.0",
			0, Bdbkp, Qpbkp, 3);
	reamfile(config.root.dest[0], Qproot0, "root.0",
			 0, Bdbkp, Qpbkp, 4);
	reamfile(config.super.dest[0], Qpsuper0, "super.0",
			 0, Bdbkp, Qpbkp, 5);
	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)
		ufree(&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("	(data block size: usable %d bytes)\n",
				Maxdatablocksize);
		dprint("	(name length: %d bytes)\n", Servicelen);
	}

	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 = egetmetachk(Bdmagic, Breadonly, Tdentry, Qpmagic);
	if(waserror()){
		putbuf(b, 0);
		nexterror();
	}
	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 != Blocksize){
		print("init incorrect block size Blocksize %llud unitsize %d\n",
				Blocksize, unitsize);
		panic("bad Blocksize != unitsize");
	}
	poperror();
	putbuf(b, 0);

	/* check super */
	sb = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
	s = sb->d;
	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);
}