xref: /plan9/sys/src/cmd/cwfs/9p2.c (revision d3907fe5a68251e8b016f54f72acf8767ba044bb)
101a344a2SDavid du Colombier #include "all.h"
201a344a2SDavid du Colombier #include <fcall.h>
301a344a2SDavid du Colombier 
401a344a2SDavid du Colombier enum { MSIZE = MAXDAT+MAXMSG };
501a344a2SDavid du Colombier 
601a344a2SDavid du Colombier static int
mkmode9p1(ulong mode9p2)701a344a2SDavid du Colombier mkmode9p1(ulong mode9p2)
801a344a2SDavid du Colombier {
901a344a2SDavid du Colombier 	int mode;
1001a344a2SDavid du Colombier 
1101a344a2SDavid du Colombier 	/*
1201a344a2SDavid du Colombier 	 * Assume this is for an allocated entry.
1301a344a2SDavid du Colombier 	 */
1401a344a2SDavid du Colombier 	mode = DALLOC|(mode9p2 & 0777);
1501a344a2SDavid du Colombier 	if(mode9p2 & DMEXCL)
1601a344a2SDavid du Colombier 		mode |= DLOCK;
1701a344a2SDavid du Colombier 	if(mode9p2 & DMAPPEND)
1801a344a2SDavid du Colombier 		mode |= DAPND;
1901a344a2SDavid du Colombier 	if(mode9p2 & DMDIR)
2001a344a2SDavid du Colombier 		mode |= DDIR;
2101a344a2SDavid du Colombier 
2201a344a2SDavid du Colombier 	return mode;
2301a344a2SDavid du Colombier }
2401a344a2SDavid du Colombier 
2501a344a2SDavid du Colombier void
mkqid9p1(Qid9p1 * qid9p1,Qid * qid)2601a344a2SDavid du Colombier mkqid9p1(Qid9p1* qid9p1, Qid* qid)
2701a344a2SDavid du Colombier {
2801a344a2SDavid du Colombier 	if(qid->path & 0xFFFFFFFF00000000LL)
2901a344a2SDavid du Colombier 		panic("mkqid9p1: path %lluX", (Wideoff)qid->path);
3001a344a2SDavid du Colombier 	qid9p1->path = qid->path & 0xFFFFFFFF;
3101a344a2SDavid du Colombier 	if(qid->type & QTDIR)
3201a344a2SDavid du Colombier 		qid9p1->path |= QPDIR;
3301a344a2SDavid du Colombier 	qid9p1->version = qid->vers;
3401a344a2SDavid du Colombier }
3501a344a2SDavid du Colombier 
3601a344a2SDavid du Colombier static int
mktype9p2(int mode9p1)3701a344a2SDavid du Colombier mktype9p2(int mode9p1)
3801a344a2SDavid du Colombier {
3901a344a2SDavid du Colombier 	int type;
4001a344a2SDavid du Colombier 
4101a344a2SDavid du Colombier 	type = 0;
4201a344a2SDavid du Colombier 	if(mode9p1 & DLOCK)
4301a344a2SDavid du Colombier 		type |= QTEXCL;
4401a344a2SDavid du Colombier 	if(mode9p1 & DAPND)
4501a344a2SDavid du Colombier 		type |= QTAPPEND;
4601a344a2SDavid du Colombier 	if(mode9p1 & DDIR)
4701a344a2SDavid du Colombier 		type |= QTDIR;
4801a344a2SDavid du Colombier 
4901a344a2SDavid du Colombier 	return type;
5001a344a2SDavid du Colombier }
5101a344a2SDavid du Colombier 
5201a344a2SDavid du Colombier static ulong
mkmode9p2(int mode9p1)5301a344a2SDavid du Colombier mkmode9p2(int mode9p1)
5401a344a2SDavid du Colombier {
5501a344a2SDavid du Colombier 	ulong mode;
5601a344a2SDavid du Colombier 
5701a344a2SDavid du Colombier 	mode = mode9p1 & 0777;
5801a344a2SDavid du Colombier 	if(mode9p1 & DLOCK)
5901a344a2SDavid du Colombier 		mode |= DMEXCL;
6001a344a2SDavid du Colombier 	if(mode9p1 & DAPND)
6101a344a2SDavid du Colombier 		mode |= DMAPPEND;
6201a344a2SDavid du Colombier 	if(mode9p1 & DDIR)
6301a344a2SDavid du Colombier 		mode |= DMDIR;
6401a344a2SDavid du Colombier 
6501a344a2SDavid du Colombier 	return mode;
6601a344a2SDavid du Colombier }
6701a344a2SDavid du Colombier 
6801a344a2SDavid du Colombier void
mkqid9p2(Qid * qid,Qid9p1 * qid9p1,int mode9p1)6901a344a2SDavid du Colombier mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode9p1)
7001a344a2SDavid du Colombier {
7101a344a2SDavid du Colombier 	qid->path = (ulong)(qid9p1->path & ~QPDIR);
7201a344a2SDavid du Colombier 	qid->vers = qid9p1->version;
7301a344a2SDavid du Colombier 	qid->type = mktype9p2(mode9p1);
7401a344a2SDavid du Colombier }
7501a344a2SDavid du Colombier 
7601a344a2SDavid du Colombier static int
mkdir9p2(Dir * dir,Dentry * dentry,void * strs)7701a344a2SDavid du Colombier mkdir9p2(Dir* dir, Dentry* dentry, void* strs)
7801a344a2SDavid du Colombier {
7901a344a2SDavid du Colombier 	char *op, *p;
8001a344a2SDavid du Colombier 
8101a344a2SDavid du Colombier 	memset(dir, 0, sizeof(Dir));
8201a344a2SDavid du Colombier 	mkqid(&dir->qid, dentry, 1);
8301a344a2SDavid du Colombier 	dir->mode = mkmode9p2(dentry->mode);
8401a344a2SDavid du Colombier 	dir->atime = dentry->atime;
8501a344a2SDavid du Colombier 	dir->mtime = dentry->mtime;
8601a344a2SDavid du Colombier 	dir->length = dentry->size;
8701a344a2SDavid du Colombier 
8801a344a2SDavid du Colombier 	op = p = strs;
8901a344a2SDavid du Colombier 	dir->name = p;
9001a344a2SDavid du Colombier 	p += sprint(p, "%s", dentry->name)+1;
9101a344a2SDavid du Colombier 
9201a344a2SDavid du Colombier 	dir->uid = p;
9301a344a2SDavid du Colombier 	uidtostr(p, dentry->uid, 1);
9401a344a2SDavid du Colombier 	p += strlen(p)+1;
9501a344a2SDavid du Colombier 
9601a344a2SDavid du Colombier 	dir->gid = p;
9701a344a2SDavid du Colombier 	uidtostr(p, dentry->gid, 1);
9801a344a2SDavid du Colombier 	p += strlen(p)+1;
9901a344a2SDavid du Colombier 
10001a344a2SDavid du Colombier 	dir->muid = p;
10101a344a2SDavid du Colombier 	uidtostr(p, dentry->muid, 1);
10201a344a2SDavid du Colombier 	p += strlen(p)+1;
10301a344a2SDavid du Colombier 
10401a344a2SDavid du Colombier 	return p-op;
10501a344a2SDavid du Colombier }
10601a344a2SDavid du Colombier 
10701a344a2SDavid du Colombier static int
checkname9p2(char * name)10801a344a2SDavid du Colombier checkname9p2(char* name)
10901a344a2SDavid du Colombier {
11001a344a2SDavid du Colombier 	char *p;
11101a344a2SDavid du Colombier 
11201a344a2SDavid du Colombier 	/*
11301a344a2SDavid du Colombier 	 * Return error or 0 if OK.
11401a344a2SDavid du Colombier 	 */
11501a344a2SDavid du Colombier 	if(name == nil || *name == 0)
11601a344a2SDavid du Colombier 		return Ename;
11701a344a2SDavid du Colombier 
11801a344a2SDavid du Colombier 	for(p = name; *p != 0; p++){
11901a344a2SDavid du Colombier 		if(p-name >= NAMELEN-1)
12001a344a2SDavid du Colombier 			return Etoolong;
12101a344a2SDavid du Colombier 		if((*p & 0xFF) <= 040)
12201a344a2SDavid du Colombier 			return Ename;
12301a344a2SDavid du Colombier 	}
12401a344a2SDavid du Colombier 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
12501a344a2SDavid du Colombier 		return Edot;
12601a344a2SDavid du Colombier 
12701a344a2SDavid du Colombier 	return 0;
12801a344a2SDavid du Colombier }
12901a344a2SDavid du Colombier 
13001a344a2SDavid du Colombier static int
version(Chan * chan,Fcall * f,Fcall * r)13101a344a2SDavid du Colombier version(Chan* chan, Fcall* f, Fcall* r)
13201a344a2SDavid du Colombier {
13301a344a2SDavid du Colombier 	if(chan->protocol != nil)
13401a344a2SDavid du Colombier 		return Eversion;
13501a344a2SDavid du Colombier 
13601a344a2SDavid du Colombier 	if(f->msize < MSIZE)
13701a344a2SDavid du Colombier 		r->msize = f->msize;
13801a344a2SDavid du Colombier 	else
13901a344a2SDavid du Colombier 		r->msize = MSIZE;
14001a344a2SDavid du Colombier 
14101a344a2SDavid du Colombier 	/*
14201a344a2SDavid du Colombier 	 * Should check the '.' stuff here.
14301a344a2SDavid du Colombier 	 */
14401a344a2SDavid du Colombier 	if(strcmp(f->version, VERSION9P) == 0){
14501a344a2SDavid du Colombier 		r->version = VERSION9P;
14601a344a2SDavid du Colombier 		chan->protocol = serve9p2;
14701a344a2SDavid du Colombier 		chan->msize = r->msize;
14801a344a2SDavid du Colombier 	} else
14901a344a2SDavid du Colombier 		r->version = "unknown";
15001a344a2SDavid du Colombier 
15101a344a2SDavid du Colombier 	fileinit(chan);
15201a344a2SDavid du Colombier 	return 0;
15301a344a2SDavid du Colombier }
15401a344a2SDavid du Colombier 
15501a344a2SDavid du Colombier struct {
15601a344a2SDavid du Colombier 	Lock;
15701a344a2SDavid du Colombier 	ulong	hi;
15801a344a2SDavid du Colombier } authpath;
15901a344a2SDavid du Colombier 
16001a344a2SDavid du Colombier static int
auth(Chan * chan,Fcall * f,Fcall * r)16101a344a2SDavid du Colombier auth(Chan* chan, Fcall* f, Fcall* r)
16201a344a2SDavid du Colombier {
16301a344a2SDavid du Colombier 	char *aname;
16401a344a2SDavid du Colombier 	File *file;
16501a344a2SDavid du Colombier 	Filsys *fs;
16601a344a2SDavid du Colombier 	int error;
16701a344a2SDavid du Colombier 
16801a344a2SDavid du Colombier 	if(cons.flags & authdisableflag)
16901a344a2SDavid du Colombier 		return Eauthdisabled;
17001a344a2SDavid du Colombier 
17101a344a2SDavid du Colombier 	error = 0;
17201a344a2SDavid du Colombier 	aname = f->aname;
17301a344a2SDavid du Colombier 
17401a344a2SDavid du Colombier 	if(strcmp(f->uname, "none") == 0)
17501a344a2SDavid du Colombier 		return Eauthnone;
17601a344a2SDavid du Colombier 
17701a344a2SDavid du Colombier 	if(!aname[0])	/* default */
17801a344a2SDavid du Colombier 		aname = "main";
17901a344a2SDavid du Colombier 	file = filep(chan, f->afid, 1);
18001a344a2SDavid du Colombier 	if(file == nil){
18101a344a2SDavid du Colombier 		error = Efidinuse;
18201a344a2SDavid du Colombier 		goto out;
18301a344a2SDavid du Colombier 	}
18401a344a2SDavid du Colombier 	fs = fsstr(aname);
18501a344a2SDavid du Colombier 	if(fs == nil){
18601a344a2SDavid du Colombier 		error = Ebadspc;
18701a344a2SDavid du Colombier 		goto out;
18801a344a2SDavid du Colombier 	}
18901a344a2SDavid du Colombier 	lock(&authpath);
19001a344a2SDavid du Colombier 	file->qid.path = authpath.hi++;
19101a344a2SDavid du Colombier 	unlock(&authpath);
19201a344a2SDavid du Colombier 	file->qid.type = QTAUTH;
19301a344a2SDavid du Colombier 	file->qid.vers = 0;
19401a344a2SDavid du Colombier 	file->fs = fs;
19501a344a2SDavid du Colombier 	file->open = FREAD+FWRITE;
19601a344a2SDavid du Colombier 	freewp(file->wpath);
19701a344a2SDavid du Colombier 	file->wpath = 0;
19801a344a2SDavid du Colombier 	file->auth = authnew(f->uname, f->aname);
19901a344a2SDavid du Colombier 	if(file->auth == nil){
20001a344a2SDavid du Colombier 		error = Eauthfile;
20101a344a2SDavid du Colombier 		goto out;
20201a344a2SDavid du Colombier 	}
20301a344a2SDavid du Colombier 	r->aqid = file->qid;
20401a344a2SDavid du Colombier 
20501a344a2SDavid du Colombier out:
20601a344a2SDavid du Colombier 	if((cons.flags & attachflag) && error)
20701a344a2SDavid du Colombier 		print("9p2: auth %s %T SUCK EGGS --- %s\n",
20801a344a2SDavid du Colombier 			f->uname, time(nil), errstr9p[error]);
20901a344a2SDavid du Colombier 	if(file != nil){
21001a344a2SDavid du Colombier 		qunlock(file);
21101a344a2SDavid du Colombier 		if(error)
21201a344a2SDavid du Colombier 			freefp(file);
21301a344a2SDavid du Colombier 	}
21401a344a2SDavid du Colombier 	return error;
21501a344a2SDavid du Colombier }
21601a344a2SDavid du Colombier 
21701a344a2SDavid du Colombier int
authorize(Chan * chan,Fcall * f)21801a344a2SDavid du Colombier authorize(Chan* chan, Fcall* f)
21901a344a2SDavid du Colombier {
22001a344a2SDavid du Colombier 	File* af;
22101a344a2SDavid du Colombier 	int db, uid = -1;
22201a344a2SDavid du Colombier 
22301a344a2SDavid du Colombier 	db = cons.flags & authdebugflag;
22401a344a2SDavid du Colombier 
22501a344a2SDavid du Colombier 	if(strcmp(f->uname, "none") == 0){
22601a344a2SDavid du Colombier 		uid = strtouid(f->uname);
22701a344a2SDavid du Colombier 		if(db)
22801a344a2SDavid du Colombier 			print("permission granted to none: uid %s = %d\n",
22901a344a2SDavid du Colombier 				f->uname, uid);
23001a344a2SDavid du Colombier 		return uid;
23101a344a2SDavid du Colombier 	}
23201a344a2SDavid du Colombier 
23301a344a2SDavid du Colombier 	if(cons.flags & authdisableflag){
23401a344a2SDavid du Colombier 		uid = strtouid(f->uname);
23501a344a2SDavid du Colombier 		if(db)
23601a344a2SDavid du Colombier 			print("permission granted by authdisable uid %s = %d\n",
23701a344a2SDavid du Colombier 				f->uname, uid);
23801a344a2SDavid du Colombier 		return uid;
23901a344a2SDavid du Colombier 	}
24001a344a2SDavid du Colombier 
24101a344a2SDavid du Colombier 	af = filep(chan, f->afid, 0);
24201a344a2SDavid du Colombier 	if(af == nil){
24301a344a2SDavid du Colombier 		if(db)
24401a344a2SDavid du Colombier 			print("authorize: af == nil\n");
24501a344a2SDavid du Colombier 		return -1;
24601a344a2SDavid du Colombier 	}
24701a344a2SDavid du Colombier 	if(af->auth == nil){
24801a344a2SDavid du Colombier 		if(db)
24901a344a2SDavid du Colombier 			print("authorize: af->auth == nil\n");
25001a344a2SDavid du Colombier 		goto out;
25101a344a2SDavid du Colombier 	}
25201a344a2SDavid du Colombier 	if(strcmp(f->uname, authuname(af->auth)) != 0){
25301a344a2SDavid du Colombier 		if(db)
25401a344a2SDavid du Colombier 			print("authorize: strcmp(f->uname, authuname(af->auth)) != 0\n");
25501a344a2SDavid du Colombier 		goto out;
25601a344a2SDavid du Colombier 	}
25701a344a2SDavid du Colombier 	if(strcmp(f->aname, authaname(af->auth)) != 0){
25801a344a2SDavid du Colombier 		if(db)
25901a344a2SDavid du Colombier 			print("authorize: strcmp(f->aname, authaname(af->auth)) != 0\n");
26001a344a2SDavid du Colombier 		goto out;
26101a344a2SDavid du Colombier 	}
26201a344a2SDavid du Colombier 	uid = authuid(af->auth);
26301a344a2SDavid du Colombier 	if(db)
26401a344a2SDavid du Colombier 		print("authorize: uid is %d\n", uid);
26501a344a2SDavid du Colombier out:
26601a344a2SDavid du Colombier 	qunlock(af);
26701a344a2SDavid du Colombier 	return uid;
26801a344a2SDavid du Colombier }
26901a344a2SDavid du Colombier 
27001a344a2SDavid du Colombier static int
attach(Chan * chan,Fcall * f,Fcall * r)27101a344a2SDavid du Colombier attach(Chan* chan, Fcall* f, Fcall* r)
27201a344a2SDavid du Colombier {
27301a344a2SDavid du Colombier 	char *aname;
27401a344a2SDavid du Colombier 	Iobuf *p;
27501a344a2SDavid du Colombier 	Dentry *d;
27601a344a2SDavid du Colombier 	File *file;
27701a344a2SDavid du Colombier 	Filsys *fs;
27801a344a2SDavid du Colombier 	Off raddr;
27901a344a2SDavid du Colombier 	int error, u;
28001a344a2SDavid du Colombier 
28101a344a2SDavid du Colombier 	aname = f->aname;
28201a344a2SDavid du Colombier 	if(!aname[0])	/* default */
28301a344a2SDavid du Colombier 		aname = "main";
28401a344a2SDavid du Colombier 	p = nil;
28501a344a2SDavid du Colombier 	error = 0;
28601a344a2SDavid du Colombier 	file = filep(chan, f->fid, 1);
28701a344a2SDavid du Colombier 	if(file == nil){
28801a344a2SDavid du Colombier 		error = Efidinuse;
28901a344a2SDavid du Colombier 		goto out;
29001a344a2SDavid du Colombier 	}
29101a344a2SDavid du Colombier 
29201a344a2SDavid du Colombier 	u = -1;
29301a344a2SDavid du Colombier 	if(chan != cons.chan){
29401a344a2SDavid du Colombier 		if(noattach && strcmp(f->uname, "none")) {
29501a344a2SDavid du Colombier 			error = Enoattach;
29601a344a2SDavid du Colombier 			goto out;
29701a344a2SDavid du Colombier 		}
29801a344a2SDavid du Colombier 		u = authorize(chan, f);
29901a344a2SDavid du Colombier 		if(u < 0){
30001a344a2SDavid du Colombier 			error = Ebadu;
30101a344a2SDavid du Colombier 			goto out;
30201a344a2SDavid du Colombier 		}
30301a344a2SDavid du Colombier 	}
30401a344a2SDavid du Colombier 	file->uid = u;
30501a344a2SDavid du Colombier 
30601a344a2SDavid du Colombier 	fs = fsstr(aname);
30701a344a2SDavid du Colombier 	if(fs == nil){
30801a344a2SDavid du Colombier 		error = Ebadspc;
30901a344a2SDavid du Colombier 		goto out;
31001a344a2SDavid du Colombier 	}
31101a344a2SDavid du Colombier 	raddr = getraddr(fs->dev);
31201a344a2SDavid du Colombier 	p = getbuf(fs->dev, raddr, Brd);
31301a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPROOT)){
31401a344a2SDavid du Colombier 		error = Ealloc;
31501a344a2SDavid du Colombier 		goto out;
31601a344a2SDavid du Colombier 	}
31701a344a2SDavid du Colombier 	d = getdir(p, 0);
31801a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
31901a344a2SDavid du Colombier 		error = Ealloc;
32001a344a2SDavid du Colombier 		goto out;
32101a344a2SDavid du Colombier 	}
32201a344a2SDavid du Colombier 	if (iaccess(file, d, DEXEC) ||
32301a344a2SDavid du Colombier 	    file->uid == 0 && fs->dev->type == Devro) {
32401a344a2SDavid du Colombier 		/*
32501a344a2SDavid du Colombier 		 * 'none' not allowed on dump
32601a344a2SDavid du Colombier 		 */
32701a344a2SDavid du Colombier 		error = Eaccess;
32801a344a2SDavid du Colombier 		goto out;
32901a344a2SDavid du Colombier 	}
33001a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
33101a344a2SDavid du Colombier 	mkqid(&file->qid, d, 1);
33201a344a2SDavid du Colombier 	file->fs = fs;
33301a344a2SDavid du Colombier 	file->addr = raddr;
33401a344a2SDavid du Colombier 	file->slot = 0;
33501a344a2SDavid du Colombier 	file->open = 0;
33601a344a2SDavid du Colombier 	freewp(file->wpath);
33701a344a2SDavid du Colombier 	file->wpath = 0;
33801a344a2SDavid du Colombier 
33901a344a2SDavid du Colombier 	r->qid = file->qid;
34001a344a2SDavid du Colombier 
34101a344a2SDavid du Colombier 	strncpy(chan->whoname, f->uname, sizeof(chan->whoname));
34201a344a2SDavid du Colombier 	chan->whotime = time(nil);
34301a344a2SDavid du Colombier 	if(cons.flags & attachflag)
34401a344a2SDavid du Colombier 		print("9p2: attach %s %T to \"%s\" C%d\n",
34501a344a2SDavid du Colombier 			chan->whoname, chan->whotime, fs->name, chan->chan);
34601a344a2SDavid du Colombier 
34701a344a2SDavid du Colombier out:
34801a344a2SDavid du Colombier 	if((cons.flags & attachflag) && error)
34901a344a2SDavid du Colombier 		print("9p2: attach %s %T SUCK EGGS --- %s\n",
35001a344a2SDavid du Colombier 			f->uname, time(nil), errstr9p[error]);
35101a344a2SDavid du Colombier 	if(p != nil)
35201a344a2SDavid du Colombier 		putbuf(p);
35301a344a2SDavid du Colombier 	if(file != nil){
35401a344a2SDavid du Colombier 		qunlock(file);
35501a344a2SDavid du Colombier 		if(error)
35601a344a2SDavid du Colombier 			freefp(file);
35701a344a2SDavid du Colombier 	}
35801a344a2SDavid du Colombier 	return error;
35901a344a2SDavid du Colombier }
36001a344a2SDavid du Colombier 
36101a344a2SDavid du Colombier static int
flush(Chan * chan,Fcall *,Fcall *)36201a344a2SDavid du Colombier flush(Chan* chan, Fcall*, Fcall*)
36301a344a2SDavid du Colombier {
36401a344a2SDavid du Colombier 	runlock(&chan->reflock);
36501a344a2SDavid du Colombier 	wlock(&chan->reflock);
36601a344a2SDavid du Colombier 	wunlock(&chan->reflock);
36701a344a2SDavid du Colombier 	rlock(&chan->reflock);
36801a344a2SDavid du Colombier 
36901a344a2SDavid du Colombier 	return 0;
37001a344a2SDavid du Colombier }
37101a344a2SDavid du Colombier 
37201a344a2SDavid du Colombier static void
clone(File * nfile,File * file)37301a344a2SDavid du Colombier clone(File* nfile, File* file)
37401a344a2SDavid du Colombier {
37501a344a2SDavid du Colombier 	Wpath *wpath;
37601a344a2SDavid du Colombier 
37701a344a2SDavid du Colombier 	nfile->qid = file->qid;
37801a344a2SDavid du Colombier 
37901a344a2SDavid du Colombier 	lock(&wpathlock);
38001a344a2SDavid du Colombier 	nfile->wpath = file->wpath;
38101a344a2SDavid du Colombier 	for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
38201a344a2SDavid du Colombier 		wpath->refs++;
38301a344a2SDavid du Colombier 	unlock(&wpathlock);
38401a344a2SDavid du Colombier 
38501a344a2SDavid du Colombier 	nfile->fs = file->fs;
38601a344a2SDavid du Colombier 	nfile->addr = file->addr;
38701a344a2SDavid du Colombier 	nfile->slot = file->slot;
38801a344a2SDavid du Colombier 	nfile->uid = file->uid;
38901a344a2SDavid du Colombier 	nfile->open = file->open & ~FREMOV;
39001a344a2SDavid du Colombier }
39101a344a2SDavid du Colombier 
39201a344a2SDavid du Colombier static int
walkname(File * file,char * wname,Qid * wqid)39301a344a2SDavid du Colombier walkname(File* file, char* wname, Qid* wqid)
39401a344a2SDavid du Colombier {
39501a344a2SDavid du Colombier 	Wpath *w;
39601a344a2SDavid du Colombier 	Iobuf *p, *p1;
39701a344a2SDavid du Colombier 	Dentry *d, *d1;
39801a344a2SDavid du Colombier 	int error, slot;
39901a344a2SDavid du Colombier 	Off addr, qpath;
40001a344a2SDavid du Colombier 
40101a344a2SDavid du Colombier 	p = p1 = nil;
40201a344a2SDavid du Colombier 
40301a344a2SDavid du Colombier 	/*
40401a344a2SDavid du Colombier 	 * File must not have been opened for I/O by an open
40501a344a2SDavid du Colombier 	 * or create message and must represent a directory.
40601a344a2SDavid du Colombier 	 */
40701a344a2SDavid du Colombier 	if(file->open != 0){
40801a344a2SDavid du Colombier 		error = Emode;
40901a344a2SDavid du Colombier 		goto out;
41001a344a2SDavid du Colombier 	}
41101a344a2SDavid du Colombier 
41201a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
41301a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
41401a344a2SDavid du Colombier 		error = Edir1;
41501a344a2SDavid du Colombier 		goto out;
41601a344a2SDavid du Colombier 	}
41701a344a2SDavid du Colombier 	d = getdir(p, file->slot);
41801a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
41901a344a2SDavid du Colombier 		error = Ealloc;
42001a344a2SDavid du Colombier 		goto out;
42101a344a2SDavid du Colombier 	}
42201a344a2SDavid du Colombier 	if(!(d->mode & DDIR)){
42301a344a2SDavid du Colombier 		error = Edir1;
42401a344a2SDavid du Colombier 		goto out;
42501a344a2SDavid du Colombier 	}
42601a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
42701a344a2SDavid du Colombier 		goto out;
42801a344a2SDavid du Colombier 
42901a344a2SDavid du Colombier 	/*
43001a344a2SDavid du Colombier 	 * For walked elements the implied user must
43101a344a2SDavid du Colombier 	 * have permission to search the directory.
43201a344a2SDavid du Colombier 	 */
43301a344a2SDavid du Colombier 	if(file->cp != cons.chan && iaccess(file, d, DEXEC)){
43401a344a2SDavid du Colombier 		error = Eaccess;
43501a344a2SDavid du Colombier 		goto out;
43601a344a2SDavid du Colombier 	}
43701a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
43801a344a2SDavid du Colombier 
43901a344a2SDavid du Colombier 	if(strcmp(wname, ".") == 0){
44001a344a2SDavid du Colombier setdot:
44101a344a2SDavid du Colombier 		if(wqid != nil)
44201a344a2SDavid du Colombier 			*wqid = file->qid;
44301a344a2SDavid du Colombier 		goto out;
44401a344a2SDavid du Colombier 	}
44501a344a2SDavid du Colombier 	if(strcmp(wname, "..") == 0){
44601a344a2SDavid du Colombier 		if(file->wpath == 0)
44701a344a2SDavid du Colombier 			goto setdot;
44801a344a2SDavid du Colombier 		putbuf(p);
44901a344a2SDavid du Colombier 		p = nil;
45001a344a2SDavid du Colombier 		addr = file->wpath->addr;
45101a344a2SDavid du Colombier 		slot = file->wpath->slot;
45201a344a2SDavid du Colombier 		p1 = getbuf(file->fs->dev, addr, Brd);
45301a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, QPNONE)){
45401a344a2SDavid du Colombier 			error = Edir1;
45501a344a2SDavid du Colombier 			goto out;
45601a344a2SDavid du Colombier 		}
45701a344a2SDavid du Colombier 		d1 = getdir(p1, slot);
45801a344a2SDavid du Colombier 		if(d == nil || !(d1->mode & DALLOC)){
45901a344a2SDavid du Colombier 			error = Ephase;
46001a344a2SDavid du Colombier 			goto out;
46101a344a2SDavid du Colombier 		}
46201a344a2SDavid du Colombier 		lock(&wpathlock);
46301a344a2SDavid du Colombier 		file->wpath->refs--;
46401a344a2SDavid du Colombier 		file->wpath = file->wpath->up;
46501a344a2SDavid du Colombier 		unlock(&wpathlock);
46601a344a2SDavid du Colombier 		goto found;
46701a344a2SDavid du Colombier 	}
46801a344a2SDavid du Colombier 
46901a344a2SDavid du Colombier 	for(addr = 0; ; addr++){
47001a344a2SDavid du Colombier 		if(p == nil){
47101a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
47201a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
47301a344a2SDavid du Colombier 				error = Ealloc;
47401a344a2SDavid du Colombier 				goto out;
47501a344a2SDavid du Colombier 			}
47601a344a2SDavid du Colombier 			d = getdir(p, file->slot);
47701a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
47801a344a2SDavid du Colombier 				error = Ealloc;
47901a344a2SDavid du Colombier 				goto out;
48001a344a2SDavid du Colombier 			}
48101a344a2SDavid du Colombier 		}
48201a344a2SDavid du Colombier 		qpath = d->qid.path;
48301a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
48401a344a2SDavid du Colombier 		p = nil;
48501a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, qpath)){
48601a344a2SDavid du Colombier 			error = Eentry;
48701a344a2SDavid du Colombier 			goto out;
48801a344a2SDavid du Colombier 		}
48901a344a2SDavid du Colombier 		for(slot = 0; slot < DIRPERBUF; slot++){
49001a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
49101a344a2SDavid du Colombier 			if (!(d1->mode & DALLOC) ||
49201a344a2SDavid du Colombier 			    strncmp(wname, d1->name, NAMELEN) != 0)
49301a344a2SDavid du Colombier 				continue;
49401a344a2SDavid du Colombier 			/*
49501a344a2SDavid du Colombier 			 * update walk path
49601a344a2SDavid du Colombier 			 */
49701a344a2SDavid du Colombier 			if((w = newwp()) == nil){
49801a344a2SDavid du Colombier 				error = Ewalk;
49901a344a2SDavid du Colombier 				goto out;
50001a344a2SDavid du Colombier 			}
50101a344a2SDavid du Colombier 			w->addr = file->addr;
50201a344a2SDavid du Colombier 			w->slot = file->slot;
50301a344a2SDavid du Colombier 			w->up = file->wpath;
50401a344a2SDavid du Colombier 			file->wpath = w;
50501a344a2SDavid du Colombier 			slot += DIRPERBUF*addr;
50601a344a2SDavid du Colombier 			goto found;
50701a344a2SDavid du Colombier 		}
50801a344a2SDavid du Colombier 		putbuf(p1);
50901a344a2SDavid du Colombier 		p1 = nil;
51001a344a2SDavid du Colombier 	}
51101a344a2SDavid du Colombier 
51201a344a2SDavid du Colombier found:
51301a344a2SDavid du Colombier 	file->addr = p1->addr;
51401a344a2SDavid du Colombier 	mkqid(&file->qid, d1, 1);
51501a344a2SDavid du Colombier 	putbuf(p1);
51601a344a2SDavid du Colombier 	p1 = nil;
51701a344a2SDavid du Colombier 	file->slot = slot;
51801a344a2SDavid du Colombier 	if(wqid != nil)
51901a344a2SDavid du Colombier 		*wqid = file->qid;
52001a344a2SDavid du Colombier 
52101a344a2SDavid du Colombier out:
52201a344a2SDavid du Colombier 	if(p1 != nil)
52301a344a2SDavid du Colombier 		putbuf(p1);
52401a344a2SDavid du Colombier 	if(p != nil)
52501a344a2SDavid du Colombier 		putbuf(p);
52601a344a2SDavid du Colombier 
52701a344a2SDavid du Colombier 	return error;
52801a344a2SDavid du Colombier }
52901a344a2SDavid du Colombier 
53001a344a2SDavid du Colombier static int
walk(Chan * chan,Fcall * f,Fcall * r)53101a344a2SDavid du Colombier walk(Chan* chan, Fcall* f, Fcall* r)
53201a344a2SDavid du Colombier {
53301a344a2SDavid du Colombier 	int error, nwname;
53401a344a2SDavid du Colombier 	File *file, *nfile, tfile;
53501a344a2SDavid du Colombier 
53601a344a2SDavid du Colombier 	/*
53701a344a2SDavid du Colombier 	 * The file identified by f->fid must be valid in the
53801a344a2SDavid du Colombier 	 * current session and must not have been opened for I/O
53901a344a2SDavid du Colombier 	 * by an open or create message.
54001a344a2SDavid du Colombier 	 */
54101a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
54201a344a2SDavid du Colombier 		return Efid;
54301a344a2SDavid du Colombier 	if(file->open != 0){
54401a344a2SDavid du Colombier 		qunlock(file);
54501a344a2SDavid du Colombier 		return Emode;
54601a344a2SDavid du Colombier 	}
54701a344a2SDavid du Colombier 
54801a344a2SDavid du Colombier 	/*
54901a344a2SDavid du Colombier 	 * If newfid is not the same as fid, allocate a new file;
55001a344a2SDavid du Colombier 	 * a side effect is checking newfid is not already in use (error);
55101a344a2SDavid du Colombier 	 * if there are no names to walk this will be equivalent to a
55201a344a2SDavid du Colombier 	 * simple 'clone' operation.
55301a344a2SDavid du Colombier 	 * Otherwise, fid and newfid are the same and if there are names
55401a344a2SDavid du Colombier 	 * to walk make a copy of 'file' to be used during the walk as
55501a344a2SDavid du Colombier 	 * 'file' must only be updated on success.
55601a344a2SDavid du Colombier 	 * Finally, it's a no-op if newfid is the same as fid and f->nwname
55701a344a2SDavid du Colombier 	 * is 0.
55801a344a2SDavid du Colombier 	 */
55901a344a2SDavid du Colombier 	r->nwqid = 0;
56001a344a2SDavid du Colombier 	if(f->newfid != f->fid){
56101a344a2SDavid du Colombier 		if((nfile = filep(chan, f->newfid, 1)) == nil){
56201a344a2SDavid du Colombier 			qunlock(file);
56301a344a2SDavid du Colombier 			return Efidinuse;
56401a344a2SDavid du Colombier 		}
56501a344a2SDavid du Colombier 	} else if(f->nwname != 0){
56601a344a2SDavid du Colombier 		nfile = &tfile;
56701a344a2SDavid du Colombier 		memset(nfile, 0, sizeof(File));
56801a344a2SDavid du Colombier 		nfile->cp = chan;
56901a344a2SDavid du Colombier 		nfile->fid = ~0;
57001a344a2SDavid du Colombier 	} else {
57101a344a2SDavid du Colombier 		qunlock(file);
57201a344a2SDavid du Colombier 		return 0;
57301a344a2SDavid du Colombier 	}
57401a344a2SDavid du Colombier 	clone(nfile, file);
57501a344a2SDavid du Colombier 
57601a344a2SDavid du Colombier 	/*
57701a344a2SDavid du Colombier 	 * Should check name is not too long.
57801a344a2SDavid du Colombier 	 */
57901a344a2SDavid du Colombier 	error = 0;
58001a344a2SDavid du Colombier 	for(nwname = 0; nwname < f->nwname; nwname++){
58101a344a2SDavid du Colombier 		error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
58201a344a2SDavid du Colombier 		if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
58301a344a2SDavid du Colombier 			break;
58401a344a2SDavid du Colombier 	}
58501a344a2SDavid du Colombier 
58601a344a2SDavid du Colombier 	if(f->nwname == 0){
58701a344a2SDavid du Colombier 		/*
58801a344a2SDavid du Colombier 		 * Newfid must be different to fid (see above)
58901a344a2SDavid du Colombier 		 * so this is a simple 'clone' operation - there's
59001a344a2SDavid du Colombier 		 * nothing to do except unlock unless there's
59101a344a2SDavid du Colombier 		 * an error.
59201a344a2SDavid du Colombier 		 */
59301a344a2SDavid du Colombier 		if(error){
59401a344a2SDavid du Colombier 			freewp(nfile->wpath);
59501a344a2SDavid du Colombier 			qunlock(nfile);
59601a344a2SDavid du Colombier 			freefp(nfile);
59701a344a2SDavid du Colombier 		} else
59801a344a2SDavid du Colombier 			qunlock(nfile);
59901a344a2SDavid du Colombier 	} else if(r->nwqid < f->nwname){
60001a344a2SDavid du Colombier 		/*
60101a344a2SDavid du Colombier 		 * Didn't walk all elements, 'clunk' nfile
60201a344a2SDavid du Colombier 		 * and leave 'file' alone.
60301a344a2SDavid du Colombier 		 * Clear error if some of the elements were
60401a344a2SDavid du Colombier 		 * walked OK.
60501a344a2SDavid du Colombier 		 */
60601a344a2SDavid du Colombier 		freewp(nfile->wpath);
60701a344a2SDavid du Colombier 		if(nfile != &tfile){
60801a344a2SDavid du Colombier 			qunlock(nfile);
60901a344a2SDavid du Colombier 			freefp(nfile);
61001a344a2SDavid du Colombier 		}
61101a344a2SDavid du Colombier 		if(r->nwqid != 0)
61201a344a2SDavid du Colombier 			error = 0;
61301a344a2SDavid du Colombier 	} else {
61401a344a2SDavid du Colombier 		/*
61501a344a2SDavid du Colombier 		 * Walked all elements. If newfid is the same
61601a344a2SDavid du Colombier 		 * as fid must update 'file' from the temporary
61701a344a2SDavid du Colombier 		 * copy used during the walk.
61801a344a2SDavid du Colombier 		 * Otherwise just unlock (when using tfile there's
61901a344a2SDavid du Colombier 		 * no need to unlock as it's a local).
62001a344a2SDavid du Colombier 		 */
62101a344a2SDavid du Colombier 		if(nfile == &tfile){
62201a344a2SDavid du Colombier 			file->qid = nfile->qid;
62301a344a2SDavid du Colombier 			freewp(file->wpath);
62401a344a2SDavid du Colombier 			file->wpath = nfile->wpath;
62501a344a2SDavid du Colombier 			file->addr = nfile->addr;
62601a344a2SDavid du Colombier 			file->slot = nfile->slot;
62701a344a2SDavid du Colombier 		} else
62801a344a2SDavid du Colombier 			qunlock(nfile);
62901a344a2SDavid du Colombier 	}
63001a344a2SDavid du Colombier 	qunlock(file);
63101a344a2SDavid du Colombier 
63201a344a2SDavid du Colombier 	return error;
63301a344a2SDavid du Colombier }
63401a344a2SDavid du Colombier 
63501a344a2SDavid du Colombier static int
fs_open(Chan * chan,Fcall * f,Fcall * r)63601a344a2SDavid du Colombier fs_open(Chan* chan, Fcall* f, Fcall* r)
63701a344a2SDavid du Colombier {
63801a344a2SDavid du Colombier 	Iobuf *p;
63901a344a2SDavid du Colombier 	Dentry *d;
64001a344a2SDavid du Colombier 	File *file;
64101a344a2SDavid du Colombier 	Tlock *t;
64201a344a2SDavid du Colombier 	Qid qid;
64301a344a2SDavid du Colombier 	int error, ro, fmod, wok;
64401a344a2SDavid du Colombier 
64501a344a2SDavid du Colombier 	wok = 0;
64601a344a2SDavid du Colombier 	p = nil;
64701a344a2SDavid du Colombier 
64801a344a2SDavid du Colombier 	if(chan == cons.chan || writeallow)
64901a344a2SDavid du Colombier 		wok = 1;
65001a344a2SDavid du Colombier 
65101a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
65201a344a2SDavid du Colombier 		error = Efid;
65301a344a2SDavid du Colombier 		goto out;
65401a344a2SDavid du Colombier 	}
65501a344a2SDavid du Colombier 	if(file->open != 0){
65601a344a2SDavid du Colombier 		error = Emode;
65701a344a2SDavid du Colombier 		goto out;
65801a344a2SDavid du Colombier 	}
65901a344a2SDavid du Colombier 
66001a344a2SDavid du Colombier 	/*
66101a344a2SDavid du Colombier 	 * if remove on close, check access here
66201a344a2SDavid du Colombier 	 */
66301a344a2SDavid du Colombier 	ro = file->fs->dev->type == Devro;
66401a344a2SDavid du Colombier 	if(f->mode & ORCLOSE){
66501a344a2SDavid du Colombier 		if(ro){
66601a344a2SDavid du Colombier 			error = Eronly;
66701a344a2SDavid du Colombier 			goto out;
66801a344a2SDavid du Colombier 		}
66901a344a2SDavid du Colombier 		/*
67001a344a2SDavid du Colombier 		 * check on parent directory of file to be deleted
67101a344a2SDavid du Colombier 		 */
67201a344a2SDavid du Colombier 		if(file->wpath == 0 || file->wpath->addr == file->addr){
67301a344a2SDavid du Colombier 			error = Ephase;
67401a344a2SDavid du Colombier 			goto out;
67501a344a2SDavid du Colombier 		}
67601a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->wpath->addr, Brd);
67701a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
67801a344a2SDavid du Colombier 			error = Ephase;
67901a344a2SDavid du Colombier 			goto out;
68001a344a2SDavid du Colombier 		}
68101a344a2SDavid du Colombier 		d = getdir(p, file->wpath->slot);
68201a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
68301a344a2SDavid du Colombier 			error = Ephase;
68401a344a2SDavid du Colombier 			goto out;
68501a344a2SDavid du Colombier 		}
68601a344a2SDavid du Colombier 		if(iaccess(file, d, DWRITE)){
68701a344a2SDavid du Colombier 			error = Eaccess;
68801a344a2SDavid du Colombier 			goto out;
68901a344a2SDavid du Colombier 		}
69001a344a2SDavid du Colombier 		putbuf(p);
69101a344a2SDavid du Colombier 	}
69201a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
69301a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
69401a344a2SDavid du Colombier 		error = Ealloc;
69501a344a2SDavid du Colombier 		goto out;
69601a344a2SDavid du Colombier 	}
69701a344a2SDavid du Colombier 	d = getdir(p, file->slot);
69801a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
69901a344a2SDavid du Colombier 		error = Ealloc;
70001a344a2SDavid du Colombier 		goto out;
70101a344a2SDavid du Colombier 	}
70201a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
70301a344a2SDavid du Colombier 		goto out;
70401a344a2SDavid du Colombier 	mkqid(&qid, d, 1);
70501a344a2SDavid du Colombier 	switch(f->mode & 7){
70601a344a2SDavid du Colombier 
70701a344a2SDavid du Colombier 	case OREAD:
70801a344a2SDavid du Colombier 		if(iaccess(file, d, DREAD) && !wok)
70901a344a2SDavid du Colombier 			goto badaccess;
71001a344a2SDavid du Colombier 		fmod = FREAD;
71101a344a2SDavid du Colombier 		break;
71201a344a2SDavid du Colombier 
71301a344a2SDavid du Colombier 	case OWRITE:
71401a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
71501a344a2SDavid du Colombier 			goto badaccess;
71601a344a2SDavid du Colombier 		if(ro){
71701a344a2SDavid du Colombier 			error = Eronly;
71801a344a2SDavid du Colombier 			goto out;
71901a344a2SDavid du Colombier 		}
72001a344a2SDavid du Colombier 		fmod = FWRITE;
72101a344a2SDavid du Colombier 		break;
72201a344a2SDavid du Colombier 
72301a344a2SDavid du Colombier 	case ORDWR:
72401a344a2SDavid du Colombier 		if((d->mode & DDIR)
72501a344a2SDavid du Colombier 		|| (iaccess(file, d, DREAD) && !wok)
72601a344a2SDavid du Colombier 		|| (iaccess(file, d, DWRITE) && !wok))
72701a344a2SDavid du Colombier 			goto badaccess;
72801a344a2SDavid du Colombier 		if(ro){
72901a344a2SDavid du Colombier 			error = Eronly;
73001a344a2SDavid du Colombier 			goto out;
73101a344a2SDavid du Colombier 		}
73201a344a2SDavid du Colombier 		fmod = FREAD+FWRITE;
73301a344a2SDavid du Colombier 		break;
73401a344a2SDavid du Colombier 
73501a344a2SDavid du Colombier 	case OEXEC:
73601a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok))
73701a344a2SDavid du Colombier 			goto badaccess;
73801a344a2SDavid du Colombier 		fmod = FREAD;
73901a344a2SDavid du Colombier 		break;
74001a344a2SDavid du Colombier 
74101a344a2SDavid du Colombier 	default:
74201a344a2SDavid du Colombier 		error = Emode;
74301a344a2SDavid du Colombier 		goto out;
74401a344a2SDavid du Colombier 	}
74501a344a2SDavid du Colombier 	if(f->mode & OTRUNC){
74601a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
74701a344a2SDavid du Colombier 			goto badaccess;
74801a344a2SDavid du Colombier 		if(ro){
74901a344a2SDavid du Colombier 			error = Eronly;
75001a344a2SDavid du Colombier 			goto out;
75101a344a2SDavid du Colombier 		}
75201a344a2SDavid du Colombier 	}
75301a344a2SDavid du Colombier 	t = 0;
75401a344a2SDavid du Colombier 	if(d->mode & DLOCK){
75501a344a2SDavid du Colombier 		if((t = tlocked(p, d)) == nil){
75601a344a2SDavid du Colombier 			error = Elocked;
75701a344a2SDavid du Colombier 			goto out;
75801a344a2SDavid du Colombier 		}
75901a344a2SDavid du Colombier 	}
76001a344a2SDavid du Colombier 	if(f->mode & ORCLOSE)
76101a344a2SDavid du Colombier 		fmod |= FREMOV;
76201a344a2SDavid du Colombier 	file->open = fmod;
76301a344a2SDavid du Colombier 	if((f->mode & OTRUNC) && !(d->mode & DAPND)){
76401a344a2SDavid du Colombier 		dtrunc(p, d, file->uid);
76501a344a2SDavid du Colombier 		qid.vers = d->qid.version;
76601a344a2SDavid du Colombier 	}
76701a344a2SDavid du Colombier 	r->qid = qid;
76801a344a2SDavid du Colombier 	file->tlock = t;
76901a344a2SDavid du Colombier 	if(t != nil)
77001a344a2SDavid du Colombier 		t->file = file;
77101a344a2SDavid du Colombier 	file->lastra = 1;
77201a344a2SDavid du Colombier 	goto out;
77301a344a2SDavid du Colombier 
77401a344a2SDavid du Colombier badaccess:
77501a344a2SDavid du Colombier 	error = Eaccess;
77601a344a2SDavid du Colombier 	file->open = 0;
77701a344a2SDavid du Colombier 
77801a344a2SDavid du Colombier out:
77901a344a2SDavid du Colombier 	if(p != nil)
78001a344a2SDavid du Colombier 		putbuf(p);
78101a344a2SDavid du Colombier 	if(file != nil)
78201a344a2SDavid du Colombier 		qunlock(file);
78301a344a2SDavid du Colombier 
78401a344a2SDavid du Colombier 	r->iounit = chan->msize-IOHDRSZ;
78501a344a2SDavid du Colombier 
78601a344a2SDavid du Colombier 	return error;
78701a344a2SDavid du Colombier }
78801a344a2SDavid du Colombier 
78901a344a2SDavid du Colombier static int
fs_create(Chan * chan,Fcall * f,Fcall * r)79001a344a2SDavid du Colombier fs_create(Chan* chan, Fcall* f, Fcall* r)
79101a344a2SDavid du Colombier {
79201a344a2SDavid du Colombier 	Iobuf *p, *p1;
79301a344a2SDavid du Colombier 	Dentry *d, *d1;
79401a344a2SDavid du Colombier 	File *file;
79501a344a2SDavid du Colombier 	int error, slot, slot1, fmod, wok;
79601a344a2SDavid du Colombier 	Off addr, addr1, path;
79701a344a2SDavid du Colombier 	Tlock *t;
79801a344a2SDavid du Colombier 	Wpath *w;
79901a344a2SDavid du Colombier 
80001a344a2SDavid du Colombier 	wok = 0;
80101a344a2SDavid du Colombier 	p = nil;
80201a344a2SDavid du Colombier 
80301a344a2SDavid du Colombier 	if(chan == cons.chan || writeallow)
80401a344a2SDavid du Colombier 		wok = 1;
80501a344a2SDavid du Colombier 
80601a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
80701a344a2SDavid du Colombier 		error = Efid;
80801a344a2SDavid du Colombier 		goto out;
80901a344a2SDavid du Colombier 	}
81001a344a2SDavid du Colombier 	if(file->fs->dev->type == Devro){
81101a344a2SDavid du Colombier 		error = Eronly;
81201a344a2SDavid du Colombier 		goto out;
81301a344a2SDavid du Colombier 	}
81401a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
81501a344a2SDavid du Colombier 		error = Emode;
81601a344a2SDavid du Colombier 		goto out;
81701a344a2SDavid du Colombier 	}
81801a344a2SDavid du Colombier 
81901a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
82001a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
82101a344a2SDavid du Colombier 		error = Ealloc;
82201a344a2SDavid du Colombier 		goto out;
82301a344a2SDavid du Colombier 	}
82401a344a2SDavid du Colombier 	d = getdir(p, file->slot);
82501a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
82601a344a2SDavid du Colombier 		error = Ealloc;
82701a344a2SDavid du Colombier 		goto out;
82801a344a2SDavid du Colombier 	}
82901a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
83001a344a2SDavid du Colombier 		goto out;
83101a344a2SDavid du Colombier 	if(!(d->mode & DDIR)){
83201a344a2SDavid du Colombier 		error = Edir2;
83301a344a2SDavid du Colombier 		goto out;
83401a344a2SDavid du Colombier 	}
83501a344a2SDavid du Colombier 	if(iaccess(file, d, DWRITE) && !wok) {
83601a344a2SDavid du Colombier 		error = Eaccess;
83701a344a2SDavid du Colombier 		goto out;
83801a344a2SDavid du Colombier 	}
83901a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
84001a344a2SDavid du Colombier 
84101a344a2SDavid du Colombier 	/*
84201a344a2SDavid du Colombier 	 * Check the name is valid (and will fit in an old
84301a344a2SDavid du Colombier 	 * directory entry for the moment).
84401a344a2SDavid du Colombier 	 */
84501a344a2SDavid du Colombier 	if(error = checkname9p2(f->name))
84601a344a2SDavid du Colombier 		goto out;
84701a344a2SDavid du Colombier 
84801a344a2SDavid du Colombier 	addr1 = 0;
84901a344a2SDavid du Colombier 	slot1 = 0;	/* set */
85001a344a2SDavid du Colombier 	for(addr = 0; ; addr++){
85101a344a2SDavid du Colombier 		if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){
85201a344a2SDavid du Colombier 			if(addr1 != 0)
85301a344a2SDavid du Colombier 				break;
85401a344a2SDavid du Colombier 			p1 = dnodebuf(p, d, addr, Tdir, file->uid);
85501a344a2SDavid du Colombier 		}
85601a344a2SDavid du Colombier 		if(p1 == nil){
85701a344a2SDavid du Colombier 			error = Efull;
85801a344a2SDavid du Colombier 			goto out;
85901a344a2SDavid du Colombier 		}
86001a344a2SDavid du Colombier 		if(checktag(p1, Tdir, d->qid.path)){
86101a344a2SDavid du Colombier 			putbuf(p1);
86201a344a2SDavid du Colombier 			goto phase;
86301a344a2SDavid du Colombier 		}
86401a344a2SDavid du Colombier 		for(slot = 0; slot < DIRPERBUF; slot++){
86501a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
86601a344a2SDavid du Colombier 			if(!(d1->mode & DALLOC)){
86701a344a2SDavid du Colombier 				if(addr1 == 0){
86801a344a2SDavid du Colombier 					addr1 = p1->addr;
86901a344a2SDavid du Colombier 					slot1 = slot + addr*DIRPERBUF;
87001a344a2SDavid du Colombier 				}
87101a344a2SDavid du Colombier 				continue;
87201a344a2SDavid du Colombier 			}
87301a344a2SDavid du Colombier 			if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
87401a344a2SDavid du Colombier 				putbuf(p1);
87501a344a2SDavid du Colombier 				error = Eexist;
87601a344a2SDavid du Colombier 				goto out;
87701a344a2SDavid du Colombier 			}
87801a344a2SDavid du Colombier 		}
87901a344a2SDavid du Colombier 		putbuf(p1);
88001a344a2SDavid du Colombier 	}
88101a344a2SDavid du Colombier 
88201a344a2SDavid du Colombier 	switch(f->mode & 7){
88301a344a2SDavid du Colombier 	case OEXEC:
88401a344a2SDavid du Colombier 	case OREAD:		/* seems only useful to make directories */
88501a344a2SDavid du Colombier 		fmod = FREAD;
88601a344a2SDavid du Colombier 		break;
88701a344a2SDavid du Colombier 
88801a344a2SDavid du Colombier 	case OWRITE:
88901a344a2SDavid du Colombier 		fmod = FWRITE;
89001a344a2SDavid du Colombier 		break;
89101a344a2SDavid du Colombier 
89201a344a2SDavid du Colombier 	case ORDWR:
89301a344a2SDavid du Colombier 		fmod = FREAD+FWRITE;
89401a344a2SDavid du Colombier 		break;
89501a344a2SDavid du Colombier 
89601a344a2SDavid du Colombier 	default:
89701a344a2SDavid du Colombier 		error = Emode;
89801a344a2SDavid du Colombier 		goto out;
89901a344a2SDavid du Colombier 	}
90001a344a2SDavid du Colombier 	if(f->perm & PDIR)
90101a344a2SDavid du Colombier 		if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE))
90201a344a2SDavid du Colombier 			goto badaccess;
90301a344a2SDavid du Colombier 	/*
90401a344a2SDavid du Colombier 	 * do it
90501a344a2SDavid du Colombier 	 */
90601a344a2SDavid du Colombier 	path = qidpathgen(file->fs->dev);
90701a344a2SDavid du Colombier 	if((p1 = getbuf(file->fs->dev, addr1, Brd|Bimm|Bmod)) == nil)
90801a344a2SDavid du Colombier 		goto phase;
90901a344a2SDavid du Colombier 	d1 = getdir(p1, slot1);
91001a344a2SDavid du Colombier 	if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
91101a344a2SDavid du Colombier 		putbuf(p1);
91201a344a2SDavid du Colombier 		goto phase;
91301a344a2SDavid du Colombier 	}
91401a344a2SDavid du Colombier 	if(d1->mode & DALLOC){
91501a344a2SDavid du Colombier 		putbuf(p1);
91601a344a2SDavid du Colombier 		goto phase;
91701a344a2SDavid du Colombier 	}
91801a344a2SDavid du Colombier 
91901a344a2SDavid du Colombier 	strncpy(d1->name, f->name, sizeof(d1->name));
92001a344a2SDavid du Colombier 	if(chan == cons.chan){
92101a344a2SDavid du Colombier 		d1->uid = cons.uid;
92201a344a2SDavid du Colombier 		d1->gid = cons.gid;
92301a344a2SDavid du Colombier 	} else {
92401a344a2SDavid du Colombier 		d1->uid = file->uid;
92501a344a2SDavid du Colombier 		d1->gid = d->gid;
92601a344a2SDavid du Colombier 		f->perm &= d->mode | ~0666;
92701a344a2SDavid du Colombier 		if(f->perm & PDIR)
92801a344a2SDavid du Colombier 			f->perm &= d->mode | ~0777;
92901a344a2SDavid du Colombier 	}
93001a344a2SDavid du Colombier 	d1->qid.path = path;
93101a344a2SDavid du Colombier 	d1->qid.version = 0;
93201a344a2SDavid du Colombier 	d1->mode = DALLOC | (f->perm & 0777);
93301a344a2SDavid du Colombier 	if(f->perm & PDIR) {
93401a344a2SDavid du Colombier 		d1->mode |= DDIR;
93501a344a2SDavid du Colombier 		d1->qid.path |= QPDIR;
93601a344a2SDavid du Colombier 	}
93701a344a2SDavid du Colombier 	if(f->perm & PAPND)
93801a344a2SDavid du Colombier 		d1->mode |= DAPND;
93901a344a2SDavid du Colombier 	t = nil;
94001a344a2SDavid du Colombier 	if(f->perm & PLOCK){
94101a344a2SDavid du Colombier 		d1->mode |= DLOCK;
94201a344a2SDavid du Colombier 		t = tlocked(p1, d1);
94301a344a2SDavid du Colombier 		/* if nil, out of tlock structures */
94401a344a2SDavid du Colombier 	}
94501a344a2SDavid du Colombier 	accessdir(p1, d1, FWRITE, file->uid);
94601a344a2SDavid du Colombier 	mkqid(&r->qid, d1, 0);
94701a344a2SDavid du Colombier 	putbuf(p1);
94801a344a2SDavid du Colombier 	accessdir(p, d, FWRITE, file->uid);
94901a344a2SDavid du Colombier 
95001a344a2SDavid du Colombier 	/*
95101a344a2SDavid du Colombier 	 * do a walk to new directory entry
95201a344a2SDavid du Colombier 	 */
95301a344a2SDavid du Colombier 	if((w = newwp()) == nil){
95401a344a2SDavid du Colombier 		error = Ewalk;
95501a344a2SDavid du Colombier 		goto out;
95601a344a2SDavid du Colombier 	}
95701a344a2SDavid du Colombier 	w->addr = file->addr;
95801a344a2SDavid du Colombier 	w->slot = file->slot;
95901a344a2SDavid du Colombier 	w->up = file->wpath;
96001a344a2SDavid du Colombier 	file->wpath = w;
96101a344a2SDavid du Colombier 	file->qid = r->qid;
96201a344a2SDavid du Colombier 	file->tlock = t;
96301a344a2SDavid du Colombier 	if(t != nil)
96401a344a2SDavid du Colombier 		t->file = file;
96501a344a2SDavid du Colombier 	file->lastra = 1;
96601a344a2SDavid du Colombier 	if(f->mode & ORCLOSE)
96701a344a2SDavid du Colombier 		fmod |= FREMOV;
96801a344a2SDavid du Colombier 	file->open = fmod;
96901a344a2SDavid du Colombier 	file->addr = addr1;
97001a344a2SDavid du Colombier 	file->slot = slot1;
97101a344a2SDavid du Colombier 	goto out;
97201a344a2SDavid du Colombier 
97301a344a2SDavid du Colombier badaccess:
97401a344a2SDavid du Colombier 	error = Eaccess;
97501a344a2SDavid du Colombier 	goto out;
97601a344a2SDavid du Colombier 
97701a344a2SDavid du Colombier phase:
97801a344a2SDavid du Colombier 	error = Ephase;
97901a344a2SDavid du Colombier 
98001a344a2SDavid du Colombier out:
98101a344a2SDavid du Colombier 	if(p != nil)
98201a344a2SDavid du Colombier 		putbuf(p);
98301a344a2SDavid du Colombier 	if(file != nil)
98401a344a2SDavid du Colombier 		qunlock(file);
98501a344a2SDavid du Colombier 
98601a344a2SDavid du Colombier 	r->iounit = chan->msize-IOHDRSZ;
98701a344a2SDavid du Colombier 
98801a344a2SDavid du Colombier 	return error;
98901a344a2SDavid du Colombier }
99001a344a2SDavid du Colombier 
99101a344a2SDavid du Colombier static int
fs_read(Chan * chan,Fcall * f,Fcall * r,uchar * data)99201a344a2SDavid du Colombier fs_read(Chan* chan, Fcall* f, Fcall* r, uchar* data)
99301a344a2SDavid du Colombier {
99401a344a2SDavid du Colombier 	Iobuf *p, *p1;
99501a344a2SDavid du Colombier 	File *file;
99601a344a2SDavid du Colombier 	Dentry *d, *d1;
99701a344a2SDavid du Colombier 	Tlock *t;
99801a344a2SDavid du Colombier 	Off addr, offset, start;
99901a344a2SDavid du Colombier 	Timet tim;
100001a344a2SDavid du Colombier 	int error, iounit, nread, count, n, o, slot;
100101a344a2SDavid du Colombier 	Msgbuf *dmb;
100201a344a2SDavid du Colombier 	Dir dir;
100301a344a2SDavid du Colombier 
100401a344a2SDavid du Colombier 	p = nil;
100501a344a2SDavid du Colombier 
100601a344a2SDavid du Colombier 	error = 0;
100701a344a2SDavid du Colombier 	count = f->count;
100801a344a2SDavid du Colombier 	offset = f->offset;
100901a344a2SDavid du Colombier 	nread = 0;
101001a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
101101a344a2SDavid du Colombier 		error = Efid;
101201a344a2SDavid du Colombier 		goto out;
101301a344a2SDavid du Colombier 	}
101401a344a2SDavid du Colombier 	if(!(file->open & FREAD)){
101501a344a2SDavid du Colombier 		error = Eopen;
101601a344a2SDavid du Colombier 		goto out;
101701a344a2SDavid du Colombier 	}
101801a344a2SDavid du Colombier 	iounit = chan->msize-IOHDRSZ;
101901a344a2SDavid du Colombier 	if(count < 0 || count > iounit){
102001a344a2SDavid du Colombier 		error = Ecount;
102101a344a2SDavid du Colombier 		goto out;
102201a344a2SDavid du Colombier 	}
102301a344a2SDavid du Colombier 	if(offset < 0){
102401a344a2SDavid du Colombier 		error = Eoffset;
102501a344a2SDavid du Colombier 		goto out;
102601a344a2SDavid du Colombier 	}
102701a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
102801a344a2SDavid du Colombier 		nread = authread(file, (uchar*)data, count);
102901a344a2SDavid du Colombier 		if(nread < 0)
103001a344a2SDavid du Colombier 			error = Eauth2;
103101a344a2SDavid du Colombier 		goto out;
103201a344a2SDavid du Colombier 	}
103301a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
103401a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
103501a344a2SDavid du Colombier 		error = Ealloc;
103601a344a2SDavid du Colombier 		goto out;
103701a344a2SDavid du Colombier 	}
103801a344a2SDavid du Colombier 	d = getdir(p, file->slot);
103901a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
104001a344a2SDavid du Colombier 		error = Ealloc;
104101a344a2SDavid du Colombier 		goto out;
104201a344a2SDavid du Colombier 	}
104301a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
104401a344a2SDavid du Colombier 		goto out;
104501a344a2SDavid du Colombier 	if(t = file->tlock){
104601a344a2SDavid du Colombier 		tim = toytime();
104701a344a2SDavid du Colombier 		if(t->time < tim || t->file != file){
104801a344a2SDavid du Colombier 			error = Ebroken;
104901a344a2SDavid du Colombier 			goto out;
105001a344a2SDavid du Colombier 		}
105101a344a2SDavid du Colombier 		/* renew the lock */
105201a344a2SDavid du Colombier 		t->time = tim + TLOCK;
105301a344a2SDavid du Colombier 	}
105401a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
105501a344a2SDavid du Colombier 	if(d->mode & DDIR)
105601a344a2SDavid du Colombier 		goto dread;
105701a344a2SDavid du Colombier 	if(offset+count > d->size)
105801a344a2SDavid du Colombier 		count = d->size - offset;
105901a344a2SDavid du Colombier 	while(count > 0){
106001a344a2SDavid du Colombier 		if(p == nil){
106101a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
106201a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
106301a344a2SDavid du Colombier 				error = Ealloc;
106401a344a2SDavid du Colombier 				goto out;
106501a344a2SDavid du Colombier 			}
106601a344a2SDavid du Colombier 			d = getdir(p, file->slot);
106701a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
106801a344a2SDavid du Colombier 				error = Ealloc;
106901a344a2SDavid du Colombier 				goto out;
107001a344a2SDavid du Colombier 			}
107101a344a2SDavid du Colombier 		}
107201a344a2SDavid du Colombier 		addr = offset / BUFSIZE;
107301a344a2SDavid du Colombier 		file->lastra = dbufread(p, d, addr, file->lastra, file->uid);
107401a344a2SDavid du Colombier 		o = offset % BUFSIZE;
107501a344a2SDavid du Colombier 		n = BUFSIZE - o;
107601a344a2SDavid du Colombier 		if(n > count)
107701a344a2SDavid du Colombier 			n = count;
107801a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
107901a344a2SDavid du Colombier 		p = nil;
108001a344a2SDavid du Colombier 		if(p1 != nil){
108101a344a2SDavid du Colombier 			if(checktag(p1, Tfile, QPNONE)){
108201a344a2SDavid du Colombier 				error = Ephase;
108301a344a2SDavid du Colombier 				putbuf(p1);
108401a344a2SDavid du Colombier 				goto out;
108501a344a2SDavid du Colombier 			}
108601a344a2SDavid du Colombier 			memmove(data+nread, p1->iobuf+o, n);
108701a344a2SDavid du Colombier 			putbuf(p1);
108801a344a2SDavid du Colombier 		} else
108901a344a2SDavid du Colombier 			memset(data+nread, 0, n);
109001a344a2SDavid du Colombier 		count -= n;
109101a344a2SDavid du Colombier 		nread += n;
109201a344a2SDavid du Colombier 		offset += n;
109301a344a2SDavid du Colombier 	}
109401a344a2SDavid du Colombier 	goto out;
109501a344a2SDavid du Colombier 
109601a344a2SDavid du Colombier dread:
109701a344a2SDavid du Colombier 	/*
109801a344a2SDavid du Colombier 	 * Pick up where we left off last time if nothing has changed,
109901a344a2SDavid du Colombier 	 * otherwise must scan from the beginning.
110001a344a2SDavid du Colombier 	 */
110101a344a2SDavid du Colombier 	if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
110201a344a2SDavid du Colombier 		addr = file->dslot/DIRPERBUF;
110301a344a2SDavid du Colombier 		slot = file->dslot%DIRPERBUF;
110401a344a2SDavid du Colombier 		start = offset;
110501a344a2SDavid du Colombier 	} else {
110601a344a2SDavid du Colombier 		addr = 0;
110701a344a2SDavid du Colombier 		slot = 0;
110801a344a2SDavid du Colombier 		start = 0;
110901a344a2SDavid du Colombier 	}
111001a344a2SDavid du Colombier 
111101a344a2SDavid du Colombier 	dmb = mballoc(iounit, chan, Mbreply1);
111201a344a2SDavid du Colombier 	for (;;) {
111301a344a2SDavid du Colombier 		if(p == nil){
111401a344a2SDavid du Colombier 			/*
111501a344a2SDavid du Colombier 			 * This is just a check to ensure the entry hasn't
111601a344a2SDavid du Colombier 			 * gone away during the read of each directory block.
111701a344a2SDavid du Colombier 			 */
111801a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
111901a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
112001a344a2SDavid du Colombier 				error = Ealloc;
112101a344a2SDavid du Colombier 				goto out1;
112201a344a2SDavid du Colombier 			}
112301a344a2SDavid du Colombier 			d = getdir(p, file->slot);
112401a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
112501a344a2SDavid du Colombier 				error = Ealloc;
112601a344a2SDavid du Colombier 				goto out1;
112701a344a2SDavid du Colombier 			}
112801a344a2SDavid du Colombier 		}
112901a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
113001a344a2SDavid du Colombier 		p = nil;
113101a344a2SDavid du Colombier 		if(p1 == nil)
113201a344a2SDavid du Colombier 			goto out1;
113301a344a2SDavid du Colombier 		if(checktag(p1, Tdir, QPNONE)){
113401a344a2SDavid du Colombier 			error = Ephase;
113501a344a2SDavid du Colombier 			putbuf(p1);
113601a344a2SDavid du Colombier 			goto out1;
113701a344a2SDavid du Colombier 		}
113801a344a2SDavid du Colombier 
113901a344a2SDavid du Colombier 		for(; slot < DIRPERBUF; slot++){
114001a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
114101a344a2SDavid du Colombier 			if(!(d1->mode & DALLOC))
114201a344a2SDavid du Colombier 				continue;
114301a344a2SDavid du Colombier 			mkdir9p2(&dir, d1, dmb->data);
114401a344a2SDavid du Colombier 			n = convD2M(&dir, data+nread, iounit - nread);
114501a344a2SDavid du Colombier 			if(n <= BIT16SZ){
114601a344a2SDavid du Colombier 				putbuf(p1);
114701a344a2SDavid du Colombier 				goto out1;
114801a344a2SDavid du Colombier 			}
114901a344a2SDavid du Colombier 			start += n;
115001a344a2SDavid du Colombier 			if(start < offset)
115101a344a2SDavid du Colombier 				continue;
115201a344a2SDavid du Colombier 			if(count < n){
115301a344a2SDavid du Colombier 				putbuf(p1);
115401a344a2SDavid du Colombier 				goto out1;
115501a344a2SDavid du Colombier 			}
115601a344a2SDavid du Colombier 			count -= n;
115701a344a2SDavid du Colombier 			nread += n;
115801a344a2SDavid du Colombier 			offset += n;
115901a344a2SDavid du Colombier 		}
116001a344a2SDavid du Colombier 		putbuf(p1);
116101a344a2SDavid du Colombier 		slot = 0;
116201a344a2SDavid du Colombier 		addr++;
116301a344a2SDavid du Colombier 	}
116401a344a2SDavid du Colombier out1:
116501a344a2SDavid du Colombier 	mbfree(dmb);
116601a344a2SDavid du Colombier 	if(error == 0){
116701a344a2SDavid du Colombier 		file->doffset = offset;
116801a344a2SDavid du Colombier 		file->dvers = file->qid.vers;
116901a344a2SDavid du Colombier 		file->dslot = slot+DIRPERBUF*addr;
117001a344a2SDavid du Colombier 	}
117101a344a2SDavid du Colombier 
117201a344a2SDavid du Colombier out:
117301a344a2SDavid du Colombier 	/*
117401a344a2SDavid du Colombier 	 * Do we need this any more?
117501a344a2SDavid du Colombier 	count = f->count - nread;
117601a344a2SDavid du Colombier 	if(count > 0)
117701a344a2SDavid du Colombier 		memset(data+nread, 0, count);
117801a344a2SDavid du Colombier 	 */
117901a344a2SDavid du Colombier 	if(p != nil)
118001a344a2SDavid du Colombier 		putbuf(p);
118101a344a2SDavid du Colombier 	if(file != nil)
118201a344a2SDavid du Colombier 		qunlock(file);
118301a344a2SDavid du Colombier 	r->count = nread;
118401a344a2SDavid du Colombier 	r->data = (char*)data;
118501a344a2SDavid du Colombier 
118601a344a2SDavid du Colombier 	return error;
118701a344a2SDavid du Colombier }
118801a344a2SDavid du Colombier 
118901a344a2SDavid du Colombier static int
fs_write(Chan * chan,Fcall * f,Fcall * r)119001a344a2SDavid du Colombier fs_write(Chan* chan, Fcall* f, Fcall* r)
119101a344a2SDavid du Colombier {
119201a344a2SDavid du Colombier 	Iobuf *p, *p1;
119301a344a2SDavid du Colombier 	Dentry *d;
119401a344a2SDavid du Colombier 	File *file;
119501a344a2SDavid du Colombier 	Tlock *t;
119601a344a2SDavid du Colombier 	Off offset, addr, qpath;
119701a344a2SDavid du Colombier 	Timet tim;
119801a344a2SDavid du Colombier 	int count, error, nwrite, o, n;
119901a344a2SDavid du Colombier 
120001a344a2SDavid du Colombier 	error = 0;
120101a344a2SDavid du Colombier 	offset = f->offset;
120201a344a2SDavid du Colombier 	count = f->count;
120301a344a2SDavid du Colombier 
120401a344a2SDavid du Colombier 	nwrite = 0;
120501a344a2SDavid du Colombier 	p = nil;
120601a344a2SDavid du Colombier 
120701a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
120801a344a2SDavid du Colombier 		error = Efid;
120901a344a2SDavid du Colombier 		goto out;
121001a344a2SDavid du Colombier 	}
121101a344a2SDavid du Colombier 	if(!(file->open & FWRITE)){
121201a344a2SDavid du Colombier 		error = Eopen;
121301a344a2SDavid du Colombier 		goto out;
121401a344a2SDavid du Colombier 	}
121501a344a2SDavid du Colombier 	if(count < 0 || count > chan->msize-IOHDRSZ){
121601a344a2SDavid du Colombier 		error = Ecount;
121701a344a2SDavid du Colombier 		goto out;
121801a344a2SDavid du Colombier 	}
121901a344a2SDavid du Colombier 	if(offset < 0) {
122001a344a2SDavid du Colombier 		error = Eoffset;
122101a344a2SDavid du Colombier 		goto out;
122201a344a2SDavid du Colombier 	}
122301a344a2SDavid du Colombier 
122401a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
122501a344a2SDavid du Colombier 		nwrite = authwrite(file, (uchar*)f->data, count);
122601a344a2SDavid du Colombier 		if(nwrite < 0)
122701a344a2SDavid du Colombier 			error = Eauth2;
122801a344a2SDavid du Colombier 		goto out;
122901a344a2SDavid du Colombier 	} else if(file->fs->dev->type == Devro){
123001a344a2SDavid du Colombier 		error = Eronly;
123101a344a2SDavid du Colombier 		goto out;
123201a344a2SDavid du Colombier 	}
123301a344a2SDavid du Colombier 
123401a344a2SDavid du Colombier 	if ((p = getbuf(file->fs->dev, file->addr, Brd|Bmod)) == nil ||
123501a344a2SDavid du Colombier 	    (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) {
123601a344a2SDavid du Colombier 		error = Ealloc;
123701a344a2SDavid du Colombier 		goto out;
123801a344a2SDavid du Colombier 	}
123901a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
124001a344a2SDavid du Colombier 		goto out;
124101a344a2SDavid du Colombier 	if(t = file->tlock) {
124201a344a2SDavid du Colombier 		tim = toytime();
124301a344a2SDavid du Colombier 		if(t->time < tim || t->file != file){
124401a344a2SDavid du Colombier 			error = Ebroken;
124501a344a2SDavid du Colombier 			goto out;
124601a344a2SDavid du Colombier 		}
124701a344a2SDavid du Colombier 		/* renew the lock */
124801a344a2SDavid du Colombier 		t->time = tim + TLOCK;
124901a344a2SDavid du Colombier 	}
125001a344a2SDavid du Colombier 	accessdir(p, d, FWRITE, file->uid);
125101a344a2SDavid du Colombier 	if(d->mode & DAPND)
125201a344a2SDavid du Colombier 		offset = d->size;
125301a344a2SDavid du Colombier 	if(offset+count > d->size)
125401a344a2SDavid du Colombier 		d->size = offset+count;
125501a344a2SDavid du Colombier 	while(count > 0){
125601a344a2SDavid du Colombier 		if(p == nil){
125701a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd|Bmod);
125801a344a2SDavid du Colombier 			if(p == nil){
125901a344a2SDavid du Colombier 				error = Ealloc;
126001a344a2SDavid du Colombier 				goto out;
126101a344a2SDavid du Colombier 			}
126201a344a2SDavid du Colombier 			d = getdir(p, file->slot);
126301a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
126401a344a2SDavid du Colombier 				error = Ealloc;
126501a344a2SDavid du Colombier 				goto out;
126601a344a2SDavid du Colombier 			}
126701a344a2SDavid du Colombier 		}
126801a344a2SDavid du Colombier 		addr = offset / BUFSIZE;
126901a344a2SDavid du Colombier 		o = offset % BUFSIZE;
127001a344a2SDavid du Colombier 		n = BUFSIZE - o;
127101a344a2SDavid du Colombier 		if(n > count)
127201a344a2SDavid du Colombier 			n = count;
127301a344a2SDavid du Colombier 		qpath = d->qid.path;
127401a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, Tfile, file->uid);
127501a344a2SDavid du Colombier 		p = nil;
127601a344a2SDavid du Colombier 		if(p1 == nil) {
127701a344a2SDavid du Colombier 			error = Efull;
127801a344a2SDavid du Colombier 			goto out;
127901a344a2SDavid du Colombier 		}
128001a344a2SDavid du Colombier 		if(checktag(p1, Tfile, qpath)){
128101a344a2SDavid du Colombier 			putbuf(p1);
128201a344a2SDavid du Colombier 			error = Ephase;
128301a344a2SDavid du Colombier 			goto out;
128401a344a2SDavid du Colombier 		}
128501a344a2SDavid du Colombier 		memmove(p1->iobuf+o, f->data+nwrite, n);
128601a344a2SDavid du Colombier 		p1->flags |= Bmod;
128701a344a2SDavid du Colombier 		putbuf(p1);
128801a344a2SDavid du Colombier 		count -= n;
128901a344a2SDavid du Colombier 		nwrite += n;
129001a344a2SDavid du Colombier 		offset += n;
129101a344a2SDavid du Colombier 	}
129201a344a2SDavid du Colombier 
129301a344a2SDavid du Colombier out:
129401a344a2SDavid du Colombier 	if(p != nil)
129501a344a2SDavid du Colombier 		putbuf(p);
129601a344a2SDavid du Colombier 	if(file != nil)
129701a344a2SDavid du Colombier 		qunlock(file);
129801a344a2SDavid du Colombier 	r->count = nwrite;
129901a344a2SDavid du Colombier 
130001a344a2SDavid du Colombier 	return error;
130101a344a2SDavid du Colombier }
130201a344a2SDavid du Colombier 
130301a344a2SDavid du Colombier static int
_clunk(File * file,int remove,int wok)130401a344a2SDavid du Colombier _clunk(File* file, int remove, int wok)
130501a344a2SDavid du Colombier {
130601a344a2SDavid du Colombier 	Tlock *t;
130701a344a2SDavid du Colombier 	int error;
130801a344a2SDavid du Colombier 
130901a344a2SDavid du Colombier 	error = 0;
131001a344a2SDavid du Colombier 	if(t = file->tlock){
131101a344a2SDavid du Colombier 		if(t->file == file)
131201a344a2SDavid du Colombier 			t->time = 0;		/* free the lock */
131301a344a2SDavid du Colombier 		file->tlock = 0;
131401a344a2SDavid du Colombier 	}
131501a344a2SDavid du Colombier 	if(remove && (file->qid.type & QTAUTH) == 0)
131601a344a2SDavid du Colombier 		error = doremove(file, wok);
131701a344a2SDavid du Colombier 	file->open = 0;
131801a344a2SDavid du Colombier 	freewp(file->wpath);
131901a344a2SDavid du Colombier 	authfree(file->auth);
132001a344a2SDavid du Colombier 	freefp(file);
132101a344a2SDavid du Colombier 	qunlock(file);
132201a344a2SDavid du Colombier 
132301a344a2SDavid du Colombier 	return error;
132401a344a2SDavid du Colombier }
132501a344a2SDavid du Colombier 
132601a344a2SDavid du Colombier static int
clunk(Chan * chan,Fcall * f,Fcall *)132701a344a2SDavid du Colombier clunk(Chan* chan, Fcall* f, Fcall*)
132801a344a2SDavid du Colombier {
132901a344a2SDavid du Colombier 	File *file;
133001a344a2SDavid du Colombier 
133101a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
133201a344a2SDavid du Colombier 		return Efid;
133301a344a2SDavid du Colombier 
133401a344a2SDavid du Colombier 	_clunk(file, file->open & FREMOV, 0);
133501a344a2SDavid du Colombier 	return 0;
133601a344a2SDavid du Colombier }
133701a344a2SDavid du Colombier 
133801a344a2SDavid du Colombier static int
fs_remove(Chan * chan,Fcall * f,Fcall *)133901a344a2SDavid du Colombier fs_remove(Chan* chan, Fcall* f, Fcall*)
134001a344a2SDavid du Colombier {
134101a344a2SDavid du Colombier 	File *file;
134201a344a2SDavid du Colombier 
134301a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
134401a344a2SDavid du Colombier 		return Efid;
134501a344a2SDavid du Colombier 
134601a344a2SDavid du Colombier 	return _clunk(file, 1, chan == cons.chan);
134701a344a2SDavid du Colombier }
134801a344a2SDavid du Colombier 
134901a344a2SDavid du Colombier static int
fs_stat(Chan * chan,Fcall * f,Fcall * r,uchar * data)135001a344a2SDavid du Colombier fs_stat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
135101a344a2SDavid du Colombier {
135201a344a2SDavid du Colombier 	Dir dir;
135301a344a2SDavid du Colombier 	Iobuf *p;
135401a344a2SDavid du Colombier 	Dentry *d, dentry;
135501a344a2SDavid du Colombier 	File *file;
135601a344a2SDavid du Colombier 	int error, len;
135701a344a2SDavid du Colombier 
135801a344a2SDavid du Colombier 	error = 0;
135901a344a2SDavid du Colombier 	p = nil;
136001a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
136101a344a2SDavid du Colombier 		return Efid;
136201a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
136301a344a2SDavid du Colombier 		memset(&dentry, 0, sizeof dentry);
136401a344a2SDavid du Colombier 		d = &dentry;
136501a344a2SDavid du Colombier 		mkqid9p1(&d->qid, &file->qid);
136601a344a2SDavid du Colombier 		strcpy(d->name, "#¿");
136701a344a2SDavid du Colombier 		d->uid = authuid(file->auth);
136801a344a2SDavid du Colombier 		d->gid = d->uid;
136901a344a2SDavid du Colombier 		d->muid = d->uid;
137001a344a2SDavid du Colombier 		d->atime = time(nil);
137101a344a2SDavid du Colombier 		d->mtime = d->atime;
137201a344a2SDavid du Colombier 		d->size = 0;
137301a344a2SDavid du Colombier 	} else {
137401a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->addr, Brd);
137501a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
137601a344a2SDavid du Colombier 			error = Edir1;
137701a344a2SDavid du Colombier 			goto out;
137801a344a2SDavid du Colombier 		}
137901a344a2SDavid du Colombier 		d = getdir(p, file->slot);
138001a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
138101a344a2SDavid du Colombier 			error = Ealloc;
138201a344a2SDavid du Colombier 			goto out;
138301a344a2SDavid du Colombier 		}
138401a344a2SDavid du Colombier 		if(error = mkqidcmp(&file->qid, d))
138501a344a2SDavid du Colombier 			goto out;
138601a344a2SDavid du Colombier 
138701a344a2SDavid du Colombier 		if(d->qid.path == QPROOT)	/* stat of root gives time */
138801a344a2SDavid du Colombier 			d->atime = time(nil);
138901a344a2SDavid du Colombier 	}
139001a344a2SDavid du Colombier 	len = mkdir9p2(&dir, d, data);
139101a344a2SDavid du Colombier 	data += len;
139201a344a2SDavid du Colombier 
139301a344a2SDavid du Colombier 	if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
139401a344a2SDavid du Colombier 		error = Eedge;
139501a344a2SDavid du Colombier 	r->stat = data;
139601a344a2SDavid du Colombier 
139701a344a2SDavid du Colombier out:
139801a344a2SDavid du Colombier 	if(p != nil)
139901a344a2SDavid du Colombier 		putbuf(p);
140001a344a2SDavid du Colombier 	if(file != nil)
140101a344a2SDavid du Colombier 		qunlock(file);
140201a344a2SDavid du Colombier 
140301a344a2SDavid du Colombier 	return error;
140401a344a2SDavid du Colombier }
140501a344a2SDavid du Colombier 
140601a344a2SDavid du Colombier static int
fs_wstat(Chan * chan,Fcall * f,Fcall *,char * strs)140701a344a2SDavid du Colombier fs_wstat(Chan* chan, Fcall* f, Fcall*, char* strs)
140801a344a2SDavid du Colombier {
140901a344a2SDavid du Colombier 	Iobuf *p, *p1;
141001a344a2SDavid du Colombier 	Dentry *d, *d1;
141101a344a2SDavid du Colombier 	File *file;
141201a344a2SDavid du Colombier 	int error, err, gid, gl, muid, op, slot, tsync, uid;
141301a344a2SDavid du Colombier 	long addr;
141401a344a2SDavid du Colombier 	Dir dir;
141501a344a2SDavid du Colombier 
141601a344a2SDavid du Colombier 	if(convM2D(f->stat, f->nstat, &dir, strs) == 0)
141701a344a2SDavid du Colombier 		return Econvert;
141801a344a2SDavid du Colombier 
141901a344a2SDavid du Colombier 	/*
142001a344a2SDavid du Colombier 	 * Get the file.
142101a344a2SDavid du Colombier 	 * If user 'none' (uid == 0), can't do anything;
142201a344a2SDavid du Colombier 	 * if filesystem is read-only, can't change anything.
142301a344a2SDavid du Colombier 	 */
142401a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
142501a344a2SDavid du Colombier 		return Efid;
142601a344a2SDavid du Colombier 	p = p1 = nil;
142701a344a2SDavid du Colombier 	if(file->uid == 0){
142801a344a2SDavid du Colombier 		error = Eaccess;
142901a344a2SDavid du Colombier 		goto out;
143001a344a2SDavid du Colombier 	}
143101a344a2SDavid du Colombier 	if(file->fs->dev->type == Devro){
143201a344a2SDavid du Colombier 		error = Eronly;
143301a344a2SDavid du Colombier 		goto out;
143401a344a2SDavid du Colombier 	}
143501a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
143601a344a2SDavid du Colombier 		error = Emode;
143701a344a2SDavid du Colombier 		goto out;
143801a344a2SDavid du Colombier 	}
143901a344a2SDavid du Colombier 
144001a344a2SDavid du Colombier 	/*
144101a344a2SDavid du Colombier 	 * Get the current entry and check it is still valid.
144201a344a2SDavid du Colombier 	 */
144301a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
144401a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
144501a344a2SDavid du Colombier 		error = Ealloc;
144601a344a2SDavid du Colombier 		goto out;
144701a344a2SDavid du Colombier 	}
144801a344a2SDavid du Colombier 	d = getdir(p, file->slot);
144901a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
145001a344a2SDavid du Colombier 		error = Ealloc;
145101a344a2SDavid du Colombier 		goto out;
145201a344a2SDavid du Colombier 	}
145301a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
145401a344a2SDavid du Colombier 		goto out;
145501a344a2SDavid du Colombier 
145601a344a2SDavid du Colombier 	/*
145701a344a2SDavid du Colombier 	 * Run through each of the (sub-)fields in the provided Dir
145801a344a2SDavid du Colombier 	 * checking for validity and whether it's a default:
145901a344a2SDavid du Colombier 	 * .type, .dev and .atime are completely ignored and not checked;
146001a344a2SDavid du Colombier 	 * .qid.path, .qid.vers and .muid are checked for validity but
146101a344a2SDavid du Colombier 	 * any attempt to change them is an error.
146201a344a2SDavid du Colombier 	 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1463*d3907fe5SDavid du Colombier 	 * possibly be changed (and .muid iff wstatallow).
146401a344a2SDavid du Colombier 	 *
146501a344a2SDavid du Colombier 	 * 'Op' flags there are changed fields, i.e. it's not a no-op.
146601a344a2SDavid du Colombier 	 * 'Tsync' flags all fields are defaulted.
146701a344a2SDavid du Colombier 	 *
146801a344a2SDavid du Colombier 	 * Wstatallow and writeallow are set to allow changes during the
146901a344a2SDavid du Colombier 	 * fileserver bootstrap phase.
147001a344a2SDavid du Colombier 	 */
147101a344a2SDavid du Colombier 	tsync = 1;
147201a344a2SDavid du Colombier 	if(dir.qid.path != ~0){
147301a344a2SDavid du Colombier 		if(dir.qid.path != file->qid.path){
147401a344a2SDavid du Colombier 			error = Ewstatp;
147501a344a2SDavid du Colombier 			goto out;
147601a344a2SDavid du Colombier 		}
147701a344a2SDavid du Colombier 		tsync = 0;
147801a344a2SDavid du Colombier 	}
147901a344a2SDavid du Colombier 	if(dir.qid.vers != ~0){
148001a344a2SDavid du Colombier 		if(dir.qid.vers != file->qid.vers){
148101a344a2SDavid du Colombier 			error = Ewstatv;
148201a344a2SDavid du Colombier 			goto out;
148301a344a2SDavid du Colombier 		}
148401a344a2SDavid du Colombier 		tsync = 0;
148501a344a2SDavid du Colombier 	}
148601a344a2SDavid du Colombier 
148701a344a2SDavid du Colombier 	/*
148801a344a2SDavid du Colombier 	 * .qid.type and .mode have some bits in common. Only .mode
148901a344a2SDavid du Colombier 	 * is currently needed for comparisons with the old mode but
149001a344a2SDavid du Colombier 	 * if there are changes to the bits also encoded in .qid.type
149101a344a2SDavid du Colombier 	 * then file->qid must be updated appropriately later.
149201a344a2SDavid du Colombier 	 */
149301a344a2SDavid du Colombier 	if(dir.qid.type == (uchar)~0){
149401a344a2SDavid du Colombier 		if(dir.mode == ~0)
149501a344a2SDavid du Colombier 			dir.qid.type = mktype9p2(d->mode);
149601a344a2SDavid du Colombier 		else
149701a344a2SDavid du Colombier 			dir.qid.type = dir.mode>>24;
149801a344a2SDavid du Colombier 	} else
149901a344a2SDavid du Colombier 		tsync = 0;
150001a344a2SDavid du Colombier 	if(dir.mode == ~0)
150101a344a2SDavid du Colombier 		dir.mode = mkmode9p2(d->mode);
150201a344a2SDavid du Colombier 	else
150301a344a2SDavid du Colombier 		tsync = 0;
150401a344a2SDavid du Colombier 
150501a344a2SDavid du Colombier 	/*
150601a344a2SDavid du Colombier 	 * Check dir.qid.type and dir.mode agree, check for any unknown
150701a344a2SDavid du Colombier 	 * type/mode bits, check for an attempt to change the directory bit.
150801a344a2SDavid du Colombier 	 */
150901a344a2SDavid du Colombier 	if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
151001a344a2SDavid du Colombier 		error = Ewstatq;
151101a344a2SDavid du Colombier 		goto out;
151201a344a2SDavid du Colombier 	}
151301a344a2SDavid du Colombier 	if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){
151401a344a2SDavid du Colombier 		error = Ewstatb;
151501a344a2SDavid du Colombier 		goto out;
151601a344a2SDavid du Colombier 	}
151701a344a2SDavid du Colombier 
151801a344a2SDavid du Colombier 	op = dir.mode^mkmode9p2(d->mode);
151901a344a2SDavid du Colombier 	if(op & DMDIR){
152001a344a2SDavid du Colombier 		error = Ewstatd;
152101a344a2SDavid du Colombier 		goto out;
152201a344a2SDavid du Colombier 	}
152301a344a2SDavid du Colombier 
152401a344a2SDavid du Colombier 	if(dir.mtime != ~0){
152501a344a2SDavid du Colombier 		if(dir.mtime != d->mtime)
152601a344a2SDavid du Colombier 			op = 1;
152701a344a2SDavid du Colombier 		tsync = 0;
152801a344a2SDavid du Colombier 	} else
152901a344a2SDavid du Colombier 		dir.mtime = d->mtime;
153001a344a2SDavid du Colombier 
153101a344a2SDavid du Colombier 	if(dir.length == ~(Off)0)
153201a344a2SDavid du Colombier 		dir.length = d->size;
153301a344a2SDavid du Colombier 	else {
153401a344a2SDavid du Colombier 		if (dir.length < 0) {
153501a344a2SDavid du Colombier 			error = Ewstatl;
153601a344a2SDavid du Colombier 			goto out;
153701a344a2SDavid du Colombier 		} else if(dir.length != d->size)
153801a344a2SDavid du Colombier 			op = 1;
153901a344a2SDavid du Colombier 		tsync = 0;
154001a344a2SDavid du Colombier 	}
154101a344a2SDavid du Colombier 
154201a344a2SDavid du Colombier 	/*
154301a344a2SDavid du Colombier 	 * Check for permission to change .mode, .mtime or .length,
154401a344a2SDavid du Colombier 	 * must be owner or leader of either group, for which test gid
154501a344a2SDavid du Colombier 	 * is needed; permission checks on gid will be done later.
154601a344a2SDavid du Colombier 	 * 'Gl' counts whether neither, one or both groups are led.
154701a344a2SDavid du Colombier 	 */
154801a344a2SDavid du Colombier 	if(dir.gid != nil && *dir.gid != '\0'){
154901a344a2SDavid du Colombier 		gid = strtouid(dir.gid);
155001a344a2SDavid du Colombier 		tsync = 0;
155101a344a2SDavid du Colombier 	} else
155201a344a2SDavid du Colombier 		gid = d->gid;
155301a344a2SDavid du Colombier 	gl = leadgroup(file->uid, gid) != 0;
155401a344a2SDavid du Colombier 	gl += leadgroup(file->uid, d->gid) != 0;
155501a344a2SDavid du Colombier 
155601a344a2SDavid du Colombier 	if(op && !wstatallow && d->uid != file->uid && !gl){
155701a344a2SDavid du Colombier 		error = Ewstato;
155801a344a2SDavid du Colombier 		goto out;
155901a344a2SDavid du Colombier 	}
156001a344a2SDavid du Colombier 
156101a344a2SDavid du Colombier 	/*
156201a344a2SDavid du Colombier 	 * Rename.
156301a344a2SDavid du Colombier 	 * Check .name is valid and different to the current.
156401a344a2SDavid du Colombier 	 */
156501a344a2SDavid du Colombier 	if(dir.name != nil && *dir.name != '\0'){
156601a344a2SDavid du Colombier 		if(error = checkname9p2(dir.name))
156701a344a2SDavid du Colombier 			goto out;
156801a344a2SDavid du Colombier 		if(strncmp(dir.name, d->name, NAMELEN))
156901a344a2SDavid du Colombier 			op = 1;
157001a344a2SDavid du Colombier 		else
157101a344a2SDavid du Colombier 			dir.name = d->name;
157201a344a2SDavid du Colombier 		tsync = 0;
157301a344a2SDavid du Colombier 	} else
157401a344a2SDavid du Colombier 		dir.name = d->name;
157501a344a2SDavid du Colombier 
157601a344a2SDavid du Colombier 	/*
157701a344a2SDavid du Colombier 	 * If the name is really to be changed check it's unique
157801a344a2SDavid du Colombier 	 * and there is write permission in the parent.
157901a344a2SDavid du Colombier 	 */
158001a344a2SDavid du Colombier 	if(dir.name != d->name){
158101a344a2SDavid du Colombier 		/*
158201a344a2SDavid du Colombier 		 * First get parent.
158301a344a2SDavid du Colombier 		 * Must drop current entry to prevent
158401a344a2SDavid du Colombier 		 * deadlock when searching that new name
158501a344a2SDavid du Colombier 		 * already exists below.
158601a344a2SDavid du Colombier 		 */
158701a344a2SDavid du Colombier 		putbuf(p);
158801a344a2SDavid du Colombier 		p = nil;
158901a344a2SDavid du Colombier 
159001a344a2SDavid du Colombier 		if(file->wpath == nil){
159101a344a2SDavid du Colombier 			error = Ephase;
159201a344a2SDavid du Colombier 			goto out;
159301a344a2SDavid du Colombier 		}
159401a344a2SDavid du Colombier 		p1 = getbuf(file->fs->dev, file->wpath->addr, Brd);
159501a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, QPNONE)){
159601a344a2SDavid du Colombier 			error = Ephase;
159701a344a2SDavid du Colombier 			goto out;
159801a344a2SDavid du Colombier 		}
159901a344a2SDavid du Colombier 		d1 = getdir(p1, file->wpath->slot);
160001a344a2SDavid du Colombier 		if(d1 == nil || !(d1->mode & DALLOC)){
160101a344a2SDavid du Colombier 			error = Ephase;
160201a344a2SDavid du Colombier 			goto out;
160301a344a2SDavid du Colombier 		}
160401a344a2SDavid du Colombier 
160501a344a2SDavid du Colombier 		/*
160601a344a2SDavid du Colombier 		 * Check entries in parent for new name.
160701a344a2SDavid du Colombier 		 */
160801a344a2SDavid du Colombier 		for(addr = 0; ; addr++){
160901a344a2SDavid du Colombier 			if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil)
161001a344a2SDavid du Colombier 				break;
161101a344a2SDavid du Colombier 			if(checktag(p, Tdir, d1->qid.path)){
161201a344a2SDavid du Colombier 				putbuf(p);
161301a344a2SDavid du Colombier 				continue;
161401a344a2SDavid du Colombier 			}
161501a344a2SDavid du Colombier 			for(slot = 0; slot < DIRPERBUF; slot++){
161601a344a2SDavid du Colombier 				d = getdir(p, slot);
161701a344a2SDavid du Colombier 				if(!(d->mode & DALLOC) ||
161801a344a2SDavid du Colombier 				   strncmp(dir.name, d->name, sizeof d->name))
161901a344a2SDavid du Colombier 					continue;
162001a344a2SDavid du Colombier 				error = Eexist;
162101a344a2SDavid du Colombier 				goto out;
162201a344a2SDavid du Colombier 			}
162301a344a2SDavid du Colombier 			putbuf(p);
162401a344a2SDavid du Colombier 		}
162501a344a2SDavid du Colombier 
162601a344a2SDavid du Colombier 		/*
162701a344a2SDavid du Colombier 		 * Reacquire entry and check it's still OK.
162801a344a2SDavid du Colombier 		 */
162901a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->addr, Brd);
163001a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
163101a344a2SDavid du Colombier 			error = Ephase;
163201a344a2SDavid du Colombier 			goto out;
163301a344a2SDavid du Colombier 		}
163401a344a2SDavid du Colombier 		d = getdir(p, file->slot);
163501a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
163601a344a2SDavid du Colombier 			error = Ephase;
163701a344a2SDavid du Colombier 			goto out;
163801a344a2SDavid du Colombier 		}
163901a344a2SDavid du Colombier 
164001a344a2SDavid du Colombier 		/*
164101a344a2SDavid du Colombier 		 * Check write permission in the parent.
164201a344a2SDavid du Colombier 		 */
164301a344a2SDavid du Colombier 		if(!wstatallow && !writeallow && iaccess(file, d1, DWRITE)){
164401a344a2SDavid du Colombier 			error = Eaccess;
164501a344a2SDavid du Colombier 			goto out;
164601a344a2SDavid du Colombier 		}
164701a344a2SDavid du Colombier 	}
164801a344a2SDavid du Colombier 
164901a344a2SDavid du Colombier 	/*
165001a344a2SDavid du Colombier 	 * Check for permission to change owner - must be god.
165101a344a2SDavid du Colombier 	 */
165201a344a2SDavid du Colombier 	if(dir.uid != nil && *dir.uid != '\0'){
165301a344a2SDavid du Colombier 		uid = strtouid(dir.uid);
165401a344a2SDavid du Colombier 		if(uid != d->uid){
165501a344a2SDavid du Colombier 			if(!wstatallow){
165601a344a2SDavid du Colombier 				error = Ewstatu;
165701a344a2SDavid du Colombier 				goto out;
165801a344a2SDavid du Colombier 			}
165901a344a2SDavid du Colombier 			op = 1;
166001a344a2SDavid du Colombier 		}
166101a344a2SDavid du Colombier 		tsync = 0;
166201a344a2SDavid du Colombier 	} else
166301a344a2SDavid du Colombier 		uid = d->uid;
1664*d3907fe5SDavid du Colombier 	if(dir.muid != nil && *dir.muid != '\0'){
1665*d3907fe5SDavid du Colombier 		muid = strtouid(dir.muid);
1666*d3907fe5SDavid du Colombier 		if(muid != d->muid){
1667*d3907fe5SDavid du Colombier 			if(!wstatallow){
1668*d3907fe5SDavid du Colombier 				error = Ewstatm;
1669*d3907fe5SDavid du Colombier 				goto out;
1670*d3907fe5SDavid du Colombier 			}
1671*d3907fe5SDavid du Colombier 			op = 1;
1672*d3907fe5SDavid du Colombier 		}
1673*d3907fe5SDavid du Colombier 		tsync = 0;
1674*d3907fe5SDavid du Colombier 	} else
1675*d3907fe5SDavid du Colombier 		muid = d->muid;
167601a344a2SDavid du Colombier 
167701a344a2SDavid du Colombier 	/*
167801a344a2SDavid du Colombier 	 * Check for permission to change group, must be
167901a344a2SDavid du Colombier 	 * either owner and in new group or leader of both groups.
168001a344a2SDavid du Colombier 	 */
168101a344a2SDavid du Colombier 	if(gid != d->gid){
168201a344a2SDavid du Colombier 		if(!(wstatallow || writeallow)
168301a344a2SDavid du Colombier 		&& !(d->uid == file->uid && ingroup(file->uid, gid))
168401a344a2SDavid du Colombier 		&& !(gl == 2)){
168501a344a2SDavid du Colombier 			error = Ewstatg;
168601a344a2SDavid du Colombier 			goto out;
168701a344a2SDavid du Colombier 		}
168801a344a2SDavid du Colombier 		op = 1;
168901a344a2SDavid du Colombier 	}
169001a344a2SDavid du Colombier 
169101a344a2SDavid du Colombier 	/*
169201a344a2SDavid du Colombier 	 * Checks all done, update if necessary.
169301a344a2SDavid du Colombier 	 */
169401a344a2SDavid du Colombier 	if(op){
169501a344a2SDavid du Colombier 		d->mode = mkmode9p1(dir.mode);
169601a344a2SDavid du Colombier 		file->qid.type = mktype9p2(d->mode);
169701a344a2SDavid du Colombier 		d->mtime = dir.mtime;
169801a344a2SDavid du Colombier 		if (dir.length < d->size) {
169901a344a2SDavid du Colombier 			err = dtrunclen(p, d, dir.length, uid);
170001a344a2SDavid du Colombier 			if (error == 0)
170101a344a2SDavid du Colombier 				error = err;
170201a344a2SDavid du Colombier 		}
170301a344a2SDavid du Colombier 		d->size = dir.length;
170401a344a2SDavid du Colombier 		if(dir.name != d->name)
170501a344a2SDavid du Colombier 			strncpy(d->name, dir.name, sizeof(d->name));
170601a344a2SDavid du Colombier 		d->uid = uid;
170701a344a2SDavid du Colombier 		d->gid = gid;
1708*d3907fe5SDavid du Colombier 		d->muid = muid;
170901a344a2SDavid du Colombier 	}
171001a344a2SDavid du Colombier 	if(!tsync)
171101a344a2SDavid du Colombier 		accessdir(p, d, FREAD, file->uid);
171201a344a2SDavid du Colombier 
171301a344a2SDavid du Colombier out:
171401a344a2SDavid du Colombier 	if(p != nil)
171501a344a2SDavid du Colombier 		putbuf(p);
171601a344a2SDavid du Colombier 	if(p1 != nil)
171701a344a2SDavid du Colombier 		putbuf(p1);
171801a344a2SDavid du Colombier 	qunlock(file);
171901a344a2SDavid du Colombier 
172001a344a2SDavid du Colombier 	return error;
172101a344a2SDavid du Colombier }
172201a344a2SDavid du Colombier 
172301a344a2SDavid du Colombier int
serve9p2(Msgbuf * mb)172401a344a2SDavid du Colombier serve9p2(Msgbuf* mb)
172501a344a2SDavid du Colombier {
172601a344a2SDavid du Colombier 	Chan *chan;
172701a344a2SDavid du Colombier 	Fcall f, r;
172801a344a2SDavid du Colombier 	Msgbuf *data, *rmb;
172901a344a2SDavid du Colombier 	char ename[64];
173001a344a2SDavid du Colombier 	int error, n, type;
173101a344a2SDavid du Colombier 	static int once;
173201a344a2SDavid du Colombier 
173301a344a2SDavid du Colombier 	if(once == 0){
173401a344a2SDavid du Colombier 		fmtinstall('F', fcallfmt);
173501a344a2SDavid du Colombier 		once = 1;
173601a344a2SDavid du Colombier 	}
173701a344a2SDavid du Colombier 
173801a344a2SDavid du Colombier 	/*
173901a344a2SDavid du Colombier 	 * 0 return means i don't understand this message,
174001a344a2SDavid du Colombier 	 * 1 return means i dealt with it, including error
174101a344a2SDavid du Colombier 	 * replies.
174201a344a2SDavid du Colombier 	 */
174301a344a2SDavid du Colombier 	if(convM2S(mb->data, mb->count, &f) != mb->count)
174401a344a2SDavid du Colombier {
174501a344a2SDavid du Colombier print("didn't like %d byte message\n", mb->count);
174601a344a2SDavid du Colombier 		return 0;
174701a344a2SDavid du Colombier }
174801a344a2SDavid du Colombier 	type = f.type;
174901a344a2SDavid du Colombier 	if(type < Tversion || type >= Tmax || (type & 1) || type == Terror)
175001a344a2SDavid du Colombier 		return 0;
175101a344a2SDavid du Colombier 
175201a344a2SDavid du Colombier 	chan = mb->chan;
175301a344a2SDavid du Colombier 	if(CHAT(chan))
175401a344a2SDavid du Colombier 		print("9p2: f %F\n", &f);
175501a344a2SDavid du Colombier 	r.type = type+1;
175601a344a2SDavid du Colombier 	r.tag = f.tag;
175701a344a2SDavid du Colombier 	error = 0;
175801a344a2SDavid du Colombier 	data = nil;
175901a344a2SDavid du Colombier 
176001a344a2SDavid du Colombier 	switch(type){
176101a344a2SDavid du Colombier 	default:
176201a344a2SDavid du Colombier 		r.type = Rerror;
176301a344a2SDavid du Colombier 		snprint(ename, sizeof(ename), "unknown message: %F", &f);
176401a344a2SDavid du Colombier 		r.ename = ename;
176501a344a2SDavid du Colombier 		break;
176601a344a2SDavid du Colombier 	case Tversion:
176701a344a2SDavid du Colombier 		error = version(chan, &f, &r);
176801a344a2SDavid du Colombier 		break;
176901a344a2SDavid du Colombier 	case Tauth:
177001a344a2SDavid du Colombier 		error = auth(chan, &f, &r);
177101a344a2SDavid du Colombier 		break;
177201a344a2SDavid du Colombier 	case Tattach:
177301a344a2SDavid du Colombier 		error = attach(chan, &f, &r);
177401a344a2SDavid du Colombier 		break;
177501a344a2SDavid du Colombier 	case Tflush:
177601a344a2SDavid du Colombier 		error = flush(chan, &f, &r);
177701a344a2SDavid du Colombier 		break;
177801a344a2SDavid du Colombier 	case Twalk:
177901a344a2SDavid du Colombier 		error = walk(chan, &f, &r);
178001a344a2SDavid du Colombier 		break;
178101a344a2SDavid du Colombier 	case Topen:
178201a344a2SDavid du Colombier 		error = fs_open(chan, &f, &r);
178301a344a2SDavid du Colombier 		break;
178401a344a2SDavid du Colombier 	case Tcreate:
178501a344a2SDavid du Colombier 		error = fs_create(chan, &f, &r);
178601a344a2SDavid du Colombier 		break;
178701a344a2SDavid du Colombier 	case Tread:
178801a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
178901a344a2SDavid du Colombier 		error = fs_read(chan, &f, &r, data->data);
179001a344a2SDavid du Colombier 		break;
179101a344a2SDavid du Colombier 	case Twrite:
179201a344a2SDavid du Colombier 		error = fs_write(chan, &f, &r);
179301a344a2SDavid du Colombier 		break;
179401a344a2SDavid du Colombier 	case Tclunk:
179501a344a2SDavid du Colombier 		error = clunk(chan, &f, &r);
179601a344a2SDavid du Colombier 		break;
179701a344a2SDavid du Colombier 	case Tremove:
179801a344a2SDavid du Colombier 		error = fs_remove(chan, &f, &r);
179901a344a2SDavid du Colombier 		break;
180001a344a2SDavid du Colombier 	case Tstat:
180101a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
180201a344a2SDavid du Colombier 		error = fs_stat(chan, &f, &r, data->data);
180301a344a2SDavid du Colombier 		break;
180401a344a2SDavid du Colombier 	case Twstat:
180501a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
180601a344a2SDavid du Colombier 		error = fs_wstat(chan, &f, &r, (char*)data->data);
180701a344a2SDavid du Colombier 		break;
180801a344a2SDavid du Colombier 	}
180901a344a2SDavid du Colombier 
181001a344a2SDavid du Colombier 	if(error != 0){
181101a344a2SDavid du Colombier 		r.type = Rerror;
181201a344a2SDavid du Colombier 		if(error >= MAXERR){
181301a344a2SDavid du Colombier 			snprint(ename, sizeof(ename), "error %d", error);
181401a344a2SDavid du Colombier 			r.ename = ename;
181501a344a2SDavid du Colombier 		} else
181601a344a2SDavid du Colombier 			r.ename = errstr9p[error];
181701a344a2SDavid du Colombier 	}
181801a344a2SDavid du Colombier 	if(CHAT(chan))
181901a344a2SDavid du Colombier 		print("9p2: r %F\n", &r);
182001a344a2SDavid du Colombier 
182101a344a2SDavid du Colombier 	rmb = mballoc(chan->msize, chan, Mbreply2);
182201a344a2SDavid du Colombier 	n = convS2M(&r, rmb->data, chan->msize);
182301a344a2SDavid du Colombier 	if(data != nil)
182401a344a2SDavid du Colombier 		mbfree(data);
182501a344a2SDavid du Colombier 	if(n == 0){
182601a344a2SDavid du Colombier 		type = r.type;
182701a344a2SDavid du Colombier 		r.type = Rerror;
182801a344a2SDavid du Colombier 
182901a344a2SDavid du Colombier 		/*
183001a344a2SDavid du Colombier 		 * If a Tversion has not been seen on the chan then
183101a344a2SDavid du Colombier 		 * chan->msize will be 0. In that case craft a special
183201a344a2SDavid du Colombier 		 * Rerror message. It's fortunate that the mballoc above
183301a344a2SDavid du Colombier 		 * for rmb will have returned a Msgbuf of MAXMSG size
183401a344a2SDavid du Colombier 		 * when given a request with count of 0...
183501a344a2SDavid du Colombier 		 */
183601a344a2SDavid du Colombier 		if(chan->msize == 0){
183701a344a2SDavid du Colombier 			r.ename = "Tversion not seen";
183801a344a2SDavid du Colombier 			n = convS2M(&r, rmb->data, MAXMSG);
183901a344a2SDavid du Colombier 		} else {
184001a344a2SDavid du Colombier 			snprint(ename, sizeof(ename), "9p2: convS2M: type %d",
184101a344a2SDavid du Colombier 				type);
184201a344a2SDavid du Colombier 			r.ename = ename;
184301a344a2SDavid du Colombier 			n = convS2M(&r, rmb->data, chan->msize);
184401a344a2SDavid du Colombier 		}
184501a344a2SDavid du Colombier 		print("%s\n", r.ename);
184601a344a2SDavid du Colombier 		if(n == 0){
184701a344a2SDavid du Colombier 			/*
184801a344a2SDavid du Colombier 			 * What to do here, the failure notification failed?
184901a344a2SDavid du Colombier 			 */
185001a344a2SDavid du Colombier 			mbfree(rmb);
185101a344a2SDavid du Colombier 			return 1;
185201a344a2SDavid du Colombier 		}
185301a344a2SDavid du Colombier 	}
185401a344a2SDavid du Colombier 	rmb->count = n;
185501a344a2SDavid du Colombier 	rmb->param = mb->param;
185601a344a2SDavid du Colombier 
185701a344a2SDavid du Colombier 	/* done 9P processing, write reply to network */
185801a344a2SDavid du Colombier 	fs_send(chan->reply, rmb);
185901a344a2SDavid du Colombier 
186001a344a2SDavid du Colombier 	return 1;
186101a344a2SDavid du Colombier }
1862