xref: /plan9/sys/src/cmd/cwfs/9p2.c (revision 01a344a29f2ff35133953eaef092a50fc8c3163b)
1*01a344a2SDavid du Colombier #include "all.h"
2*01a344a2SDavid du Colombier #include <fcall.h>
3*01a344a2SDavid du Colombier 
4*01a344a2SDavid du Colombier enum { MSIZE = MAXDAT+MAXMSG };
5*01a344a2SDavid du Colombier 
6*01a344a2SDavid du Colombier static int
7*01a344a2SDavid du Colombier mkmode9p1(ulong mode9p2)
8*01a344a2SDavid du Colombier {
9*01a344a2SDavid du Colombier 	int mode;
10*01a344a2SDavid du Colombier 
11*01a344a2SDavid du Colombier 	/*
12*01a344a2SDavid du Colombier 	 * Assume this is for an allocated entry.
13*01a344a2SDavid du Colombier 	 */
14*01a344a2SDavid du Colombier 	mode = DALLOC|(mode9p2 & 0777);
15*01a344a2SDavid du Colombier 	if(mode9p2 & DMEXCL)
16*01a344a2SDavid du Colombier 		mode |= DLOCK;
17*01a344a2SDavid du Colombier 	if(mode9p2 & DMAPPEND)
18*01a344a2SDavid du Colombier 		mode |= DAPND;
19*01a344a2SDavid du Colombier 	if(mode9p2 & DMDIR)
20*01a344a2SDavid du Colombier 		mode |= DDIR;
21*01a344a2SDavid du Colombier 
22*01a344a2SDavid du Colombier 	return mode;
23*01a344a2SDavid du Colombier }
24*01a344a2SDavid du Colombier 
25*01a344a2SDavid du Colombier void
26*01a344a2SDavid du Colombier mkqid9p1(Qid9p1* qid9p1, Qid* qid)
27*01a344a2SDavid du Colombier {
28*01a344a2SDavid du Colombier 	if(qid->path & 0xFFFFFFFF00000000LL)
29*01a344a2SDavid du Colombier 		panic("mkqid9p1: path %lluX", (Wideoff)qid->path);
30*01a344a2SDavid du Colombier 	qid9p1->path = qid->path & 0xFFFFFFFF;
31*01a344a2SDavid du Colombier 	if(qid->type & QTDIR)
32*01a344a2SDavid du Colombier 		qid9p1->path |= QPDIR;
33*01a344a2SDavid du Colombier 	qid9p1->version = qid->vers;
34*01a344a2SDavid du Colombier }
35*01a344a2SDavid du Colombier 
36*01a344a2SDavid du Colombier static int
37*01a344a2SDavid du Colombier mktype9p2(int mode9p1)
38*01a344a2SDavid du Colombier {
39*01a344a2SDavid du Colombier 	int type;
40*01a344a2SDavid du Colombier 
41*01a344a2SDavid du Colombier 	type = 0;
42*01a344a2SDavid du Colombier 	if(mode9p1 & DLOCK)
43*01a344a2SDavid du Colombier 		type |= QTEXCL;
44*01a344a2SDavid du Colombier 	if(mode9p1 & DAPND)
45*01a344a2SDavid du Colombier 		type |= QTAPPEND;
46*01a344a2SDavid du Colombier 	if(mode9p1 & DDIR)
47*01a344a2SDavid du Colombier 		type |= QTDIR;
48*01a344a2SDavid du Colombier 
49*01a344a2SDavid du Colombier 	return type;
50*01a344a2SDavid du Colombier }
51*01a344a2SDavid du Colombier 
52*01a344a2SDavid du Colombier static ulong
53*01a344a2SDavid du Colombier mkmode9p2(int mode9p1)
54*01a344a2SDavid du Colombier {
55*01a344a2SDavid du Colombier 	ulong mode;
56*01a344a2SDavid du Colombier 
57*01a344a2SDavid du Colombier 	mode = mode9p1 & 0777;
58*01a344a2SDavid du Colombier 	if(mode9p1 & DLOCK)
59*01a344a2SDavid du Colombier 		mode |= DMEXCL;
60*01a344a2SDavid du Colombier 	if(mode9p1 & DAPND)
61*01a344a2SDavid du Colombier 		mode |= DMAPPEND;
62*01a344a2SDavid du Colombier 	if(mode9p1 & DDIR)
63*01a344a2SDavid du Colombier 		mode |= DMDIR;
64*01a344a2SDavid du Colombier 
65*01a344a2SDavid du Colombier 	return mode;
66*01a344a2SDavid du Colombier }
67*01a344a2SDavid du Colombier 
68*01a344a2SDavid du Colombier void
69*01a344a2SDavid du Colombier mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode9p1)
70*01a344a2SDavid du Colombier {
71*01a344a2SDavid du Colombier 	qid->path = (ulong)(qid9p1->path & ~QPDIR);
72*01a344a2SDavid du Colombier 	qid->vers = qid9p1->version;
73*01a344a2SDavid du Colombier 	qid->type = mktype9p2(mode9p1);
74*01a344a2SDavid du Colombier }
75*01a344a2SDavid du Colombier 
76*01a344a2SDavid du Colombier static int
77*01a344a2SDavid du Colombier mkdir9p2(Dir* dir, Dentry* dentry, void* strs)
78*01a344a2SDavid du Colombier {
79*01a344a2SDavid du Colombier 	char *op, *p;
80*01a344a2SDavid du Colombier 
81*01a344a2SDavid du Colombier 	memset(dir, 0, sizeof(Dir));
82*01a344a2SDavid du Colombier 	mkqid(&dir->qid, dentry, 1);
83*01a344a2SDavid du Colombier 	dir->mode = mkmode9p2(dentry->mode);
84*01a344a2SDavid du Colombier 	dir->atime = dentry->atime;
85*01a344a2SDavid du Colombier 	dir->mtime = dentry->mtime;
86*01a344a2SDavid du Colombier 	dir->length = dentry->size;
87*01a344a2SDavid du Colombier 
88*01a344a2SDavid du Colombier 	op = p = strs;
89*01a344a2SDavid du Colombier 	dir->name = p;
90*01a344a2SDavid du Colombier 	p += sprint(p, "%s", dentry->name)+1;
91*01a344a2SDavid du Colombier 
92*01a344a2SDavid du Colombier 	dir->uid = p;
93*01a344a2SDavid du Colombier 	uidtostr(p, dentry->uid, 1);
94*01a344a2SDavid du Colombier 	p += strlen(p)+1;
95*01a344a2SDavid du Colombier 
96*01a344a2SDavid du Colombier 	dir->gid = p;
97*01a344a2SDavid du Colombier 	uidtostr(p, dentry->gid, 1);
98*01a344a2SDavid du Colombier 	p += strlen(p)+1;
99*01a344a2SDavid du Colombier 
100*01a344a2SDavid du Colombier 	dir->muid = p;
101*01a344a2SDavid du Colombier 	uidtostr(p, dentry->muid, 1);
102*01a344a2SDavid du Colombier 	p += strlen(p)+1;
103*01a344a2SDavid du Colombier 
104*01a344a2SDavid du Colombier 	return p-op;
105*01a344a2SDavid du Colombier }
106*01a344a2SDavid du Colombier 
107*01a344a2SDavid du Colombier static int
108*01a344a2SDavid du Colombier checkname9p2(char* name)
109*01a344a2SDavid du Colombier {
110*01a344a2SDavid du Colombier 	char *p;
111*01a344a2SDavid du Colombier 
112*01a344a2SDavid du Colombier 	/*
113*01a344a2SDavid du Colombier 	 * Return error or 0 if OK.
114*01a344a2SDavid du Colombier 	 */
115*01a344a2SDavid du Colombier 	if(name == nil || *name == 0)
116*01a344a2SDavid du Colombier 		return Ename;
117*01a344a2SDavid du Colombier 
118*01a344a2SDavid du Colombier 	for(p = name; *p != 0; p++){
119*01a344a2SDavid du Colombier 		if(p-name >= NAMELEN-1)
120*01a344a2SDavid du Colombier 			return Etoolong;
121*01a344a2SDavid du Colombier 		if((*p & 0xFF) <= 040)
122*01a344a2SDavid du Colombier 			return Ename;
123*01a344a2SDavid du Colombier 	}
124*01a344a2SDavid du Colombier 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
125*01a344a2SDavid du Colombier 		return Edot;
126*01a344a2SDavid du Colombier 
127*01a344a2SDavid du Colombier 	return 0;
128*01a344a2SDavid du Colombier }
129*01a344a2SDavid du Colombier 
130*01a344a2SDavid du Colombier static int
131*01a344a2SDavid du Colombier version(Chan* chan, Fcall* f, Fcall* r)
132*01a344a2SDavid du Colombier {
133*01a344a2SDavid du Colombier 	if(chan->protocol != nil)
134*01a344a2SDavid du Colombier 		return Eversion;
135*01a344a2SDavid du Colombier 
136*01a344a2SDavid du Colombier 	if(f->msize < MSIZE)
137*01a344a2SDavid du Colombier 		r->msize = f->msize;
138*01a344a2SDavid du Colombier 	else
139*01a344a2SDavid du Colombier 		r->msize = MSIZE;
140*01a344a2SDavid du Colombier 
141*01a344a2SDavid du Colombier 	/*
142*01a344a2SDavid du Colombier 	 * Should check the '.' stuff here.
143*01a344a2SDavid du Colombier 	 */
144*01a344a2SDavid du Colombier 	if(strcmp(f->version, VERSION9P) == 0){
145*01a344a2SDavid du Colombier 		r->version = VERSION9P;
146*01a344a2SDavid du Colombier 		chan->protocol = serve9p2;
147*01a344a2SDavid du Colombier 		chan->msize = r->msize;
148*01a344a2SDavid du Colombier 	} else
149*01a344a2SDavid du Colombier 		r->version = "unknown";
150*01a344a2SDavid du Colombier 
151*01a344a2SDavid du Colombier 	fileinit(chan);
152*01a344a2SDavid du Colombier 	return 0;
153*01a344a2SDavid du Colombier }
154*01a344a2SDavid du Colombier 
155*01a344a2SDavid du Colombier struct {
156*01a344a2SDavid du Colombier 	Lock;
157*01a344a2SDavid du Colombier 	ulong	hi;
158*01a344a2SDavid du Colombier } authpath;
159*01a344a2SDavid du Colombier 
160*01a344a2SDavid du Colombier static int
161*01a344a2SDavid du Colombier auth(Chan* chan, Fcall* f, Fcall* r)
162*01a344a2SDavid du Colombier {
163*01a344a2SDavid du Colombier 	char *aname;
164*01a344a2SDavid du Colombier 	File *file;
165*01a344a2SDavid du Colombier 	Filsys *fs;
166*01a344a2SDavid du Colombier 	int error;
167*01a344a2SDavid du Colombier 
168*01a344a2SDavid du Colombier 	if(cons.flags & authdisableflag)
169*01a344a2SDavid du Colombier 		return Eauthdisabled;
170*01a344a2SDavid du Colombier 
171*01a344a2SDavid du Colombier 	error = 0;
172*01a344a2SDavid du Colombier 	aname = f->aname;
173*01a344a2SDavid du Colombier 
174*01a344a2SDavid du Colombier 	if(strcmp(f->uname, "none") == 0)
175*01a344a2SDavid du Colombier 		return Eauthnone;
176*01a344a2SDavid du Colombier 
177*01a344a2SDavid du Colombier 	if(!aname[0])	/* default */
178*01a344a2SDavid du Colombier 		aname = "main";
179*01a344a2SDavid du Colombier 	file = filep(chan, f->afid, 1);
180*01a344a2SDavid du Colombier 	if(file == nil){
181*01a344a2SDavid du Colombier 		error = Efidinuse;
182*01a344a2SDavid du Colombier 		goto out;
183*01a344a2SDavid du Colombier 	}
184*01a344a2SDavid du Colombier 	fs = fsstr(aname);
185*01a344a2SDavid du Colombier 	if(fs == nil){
186*01a344a2SDavid du Colombier 		error = Ebadspc;
187*01a344a2SDavid du Colombier 		goto out;
188*01a344a2SDavid du Colombier 	}
189*01a344a2SDavid du Colombier 	lock(&authpath);
190*01a344a2SDavid du Colombier 	file->qid.path = authpath.hi++;
191*01a344a2SDavid du Colombier 	unlock(&authpath);
192*01a344a2SDavid du Colombier 	file->qid.type = QTAUTH;
193*01a344a2SDavid du Colombier 	file->qid.vers = 0;
194*01a344a2SDavid du Colombier 	file->fs = fs;
195*01a344a2SDavid du Colombier 	file->open = FREAD+FWRITE;
196*01a344a2SDavid du Colombier 	freewp(file->wpath);
197*01a344a2SDavid du Colombier 	file->wpath = 0;
198*01a344a2SDavid du Colombier 	file->auth = authnew(f->uname, f->aname);
199*01a344a2SDavid du Colombier 	if(file->auth == nil){
200*01a344a2SDavid du Colombier 		error = Eauthfile;
201*01a344a2SDavid du Colombier 		goto out;
202*01a344a2SDavid du Colombier 	}
203*01a344a2SDavid du Colombier 	r->aqid = file->qid;
204*01a344a2SDavid du Colombier 
205*01a344a2SDavid du Colombier out:
206*01a344a2SDavid du Colombier 	if((cons.flags & attachflag) && error)
207*01a344a2SDavid du Colombier 		print("9p2: auth %s %T SUCK EGGS --- %s\n",
208*01a344a2SDavid du Colombier 			f->uname, time(nil), errstr9p[error]);
209*01a344a2SDavid du Colombier 	if(file != nil){
210*01a344a2SDavid du Colombier 		qunlock(file);
211*01a344a2SDavid du Colombier 		if(error)
212*01a344a2SDavid du Colombier 			freefp(file);
213*01a344a2SDavid du Colombier 	}
214*01a344a2SDavid du Colombier 	return error;
215*01a344a2SDavid du Colombier }
216*01a344a2SDavid du Colombier 
217*01a344a2SDavid du Colombier int
218*01a344a2SDavid du Colombier authorize(Chan* chan, Fcall* f)
219*01a344a2SDavid du Colombier {
220*01a344a2SDavid du Colombier 	File* af;
221*01a344a2SDavid du Colombier 	int db, uid = -1;
222*01a344a2SDavid du Colombier 
223*01a344a2SDavid du Colombier 	db = cons.flags & authdebugflag;
224*01a344a2SDavid du Colombier 
225*01a344a2SDavid du Colombier 	if(strcmp(f->uname, "none") == 0){
226*01a344a2SDavid du Colombier 		uid = strtouid(f->uname);
227*01a344a2SDavid du Colombier 		if(db)
228*01a344a2SDavid du Colombier 			print("permission granted to none: uid %s = %d\n",
229*01a344a2SDavid du Colombier 				f->uname, uid);
230*01a344a2SDavid du Colombier 		return uid;
231*01a344a2SDavid du Colombier 	}
232*01a344a2SDavid du Colombier 
233*01a344a2SDavid du Colombier 	if(cons.flags & authdisableflag){
234*01a344a2SDavid du Colombier 		uid = strtouid(f->uname);
235*01a344a2SDavid du Colombier 		if(db)
236*01a344a2SDavid du Colombier 			print("permission granted by authdisable uid %s = %d\n",
237*01a344a2SDavid du Colombier 				f->uname, uid);
238*01a344a2SDavid du Colombier 		return uid;
239*01a344a2SDavid du Colombier 	}
240*01a344a2SDavid du Colombier 
241*01a344a2SDavid du Colombier 	af = filep(chan, f->afid, 0);
242*01a344a2SDavid du Colombier 	if(af == nil){
243*01a344a2SDavid du Colombier 		if(db)
244*01a344a2SDavid du Colombier 			print("authorize: af == nil\n");
245*01a344a2SDavid du Colombier 		return -1;
246*01a344a2SDavid du Colombier 	}
247*01a344a2SDavid du Colombier 	if(af->auth == nil){
248*01a344a2SDavid du Colombier 		if(db)
249*01a344a2SDavid du Colombier 			print("authorize: af->auth == nil\n");
250*01a344a2SDavid du Colombier 		goto out;
251*01a344a2SDavid du Colombier 	}
252*01a344a2SDavid du Colombier 	if(strcmp(f->uname, authuname(af->auth)) != 0){
253*01a344a2SDavid du Colombier 		if(db)
254*01a344a2SDavid du Colombier 			print("authorize: strcmp(f->uname, authuname(af->auth)) != 0\n");
255*01a344a2SDavid du Colombier 		goto out;
256*01a344a2SDavid du Colombier 	}
257*01a344a2SDavid du Colombier 	if(strcmp(f->aname, authaname(af->auth)) != 0){
258*01a344a2SDavid du Colombier 		if(db)
259*01a344a2SDavid du Colombier 			print("authorize: strcmp(f->aname, authaname(af->auth)) != 0\n");
260*01a344a2SDavid du Colombier 		goto out;
261*01a344a2SDavid du Colombier 	}
262*01a344a2SDavid du Colombier 	uid = authuid(af->auth);
263*01a344a2SDavid du Colombier 	if(db)
264*01a344a2SDavid du Colombier 		print("authorize: uid is %d\n", uid);
265*01a344a2SDavid du Colombier out:
266*01a344a2SDavid du Colombier 	qunlock(af);
267*01a344a2SDavid du Colombier 	return uid;
268*01a344a2SDavid du Colombier }
269*01a344a2SDavid du Colombier 
270*01a344a2SDavid du Colombier static int
271*01a344a2SDavid du Colombier attach(Chan* chan, Fcall* f, Fcall* r)
272*01a344a2SDavid du Colombier {
273*01a344a2SDavid du Colombier 	char *aname;
274*01a344a2SDavid du Colombier 	Iobuf *p;
275*01a344a2SDavid du Colombier 	Dentry *d;
276*01a344a2SDavid du Colombier 	File *file;
277*01a344a2SDavid du Colombier 	Filsys *fs;
278*01a344a2SDavid du Colombier 	Off raddr;
279*01a344a2SDavid du Colombier 	int error, u;
280*01a344a2SDavid du Colombier 
281*01a344a2SDavid du Colombier 	aname = f->aname;
282*01a344a2SDavid du Colombier 	if(!aname[0])	/* default */
283*01a344a2SDavid du Colombier 		aname = "main";
284*01a344a2SDavid du Colombier 	p = nil;
285*01a344a2SDavid du Colombier 	error = 0;
286*01a344a2SDavid du Colombier 	file = filep(chan, f->fid, 1);
287*01a344a2SDavid du Colombier 	if(file == nil){
288*01a344a2SDavid du Colombier 		error = Efidinuse;
289*01a344a2SDavid du Colombier 		goto out;
290*01a344a2SDavid du Colombier 	}
291*01a344a2SDavid du Colombier 
292*01a344a2SDavid du Colombier 	u = -1;
293*01a344a2SDavid du Colombier 	if(chan != cons.chan){
294*01a344a2SDavid du Colombier 		if(noattach && strcmp(f->uname, "none")) {
295*01a344a2SDavid du Colombier 			error = Enoattach;
296*01a344a2SDavid du Colombier 			goto out;
297*01a344a2SDavid du Colombier 		}
298*01a344a2SDavid du Colombier 		u = authorize(chan, f);
299*01a344a2SDavid du Colombier 		if(u < 0){
300*01a344a2SDavid du Colombier 			error = Ebadu;
301*01a344a2SDavid du Colombier 			goto out;
302*01a344a2SDavid du Colombier 		}
303*01a344a2SDavid du Colombier 	}
304*01a344a2SDavid du Colombier 	file->uid = u;
305*01a344a2SDavid du Colombier 
306*01a344a2SDavid du Colombier 	fs = fsstr(aname);
307*01a344a2SDavid du Colombier 	if(fs == nil){
308*01a344a2SDavid du Colombier 		error = Ebadspc;
309*01a344a2SDavid du Colombier 		goto out;
310*01a344a2SDavid du Colombier 	}
311*01a344a2SDavid du Colombier 	raddr = getraddr(fs->dev);
312*01a344a2SDavid du Colombier 	p = getbuf(fs->dev, raddr, Brd);
313*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPROOT)){
314*01a344a2SDavid du Colombier 		error = Ealloc;
315*01a344a2SDavid du Colombier 		goto out;
316*01a344a2SDavid du Colombier 	}
317*01a344a2SDavid du Colombier 	d = getdir(p, 0);
318*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
319*01a344a2SDavid du Colombier 		error = Ealloc;
320*01a344a2SDavid du Colombier 		goto out;
321*01a344a2SDavid du Colombier 	}
322*01a344a2SDavid du Colombier 	if (iaccess(file, d, DEXEC) ||
323*01a344a2SDavid du Colombier 	    file->uid == 0 && fs->dev->type == Devro) {
324*01a344a2SDavid du Colombier 		/*
325*01a344a2SDavid du Colombier 		 * 'none' not allowed on dump
326*01a344a2SDavid du Colombier 		 */
327*01a344a2SDavid du Colombier 		error = Eaccess;
328*01a344a2SDavid du Colombier 		goto out;
329*01a344a2SDavid du Colombier 	}
330*01a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
331*01a344a2SDavid du Colombier 	mkqid(&file->qid, d, 1);
332*01a344a2SDavid du Colombier 	file->fs = fs;
333*01a344a2SDavid du Colombier 	file->addr = raddr;
334*01a344a2SDavid du Colombier 	file->slot = 0;
335*01a344a2SDavid du Colombier 	file->open = 0;
336*01a344a2SDavid du Colombier 	freewp(file->wpath);
337*01a344a2SDavid du Colombier 	file->wpath = 0;
338*01a344a2SDavid du Colombier 
339*01a344a2SDavid du Colombier 	r->qid = file->qid;
340*01a344a2SDavid du Colombier 
341*01a344a2SDavid du Colombier 	strncpy(chan->whoname, f->uname, sizeof(chan->whoname));
342*01a344a2SDavid du Colombier 	chan->whotime = time(nil);
343*01a344a2SDavid du Colombier 	if(cons.flags & attachflag)
344*01a344a2SDavid du Colombier 		print("9p2: attach %s %T to \"%s\" C%d\n",
345*01a344a2SDavid du Colombier 			chan->whoname, chan->whotime, fs->name, chan->chan);
346*01a344a2SDavid du Colombier 
347*01a344a2SDavid du Colombier out:
348*01a344a2SDavid du Colombier 	if((cons.flags & attachflag) && error)
349*01a344a2SDavid du Colombier 		print("9p2: attach %s %T SUCK EGGS --- %s\n",
350*01a344a2SDavid du Colombier 			f->uname, time(nil), errstr9p[error]);
351*01a344a2SDavid du Colombier 	if(p != nil)
352*01a344a2SDavid du Colombier 		putbuf(p);
353*01a344a2SDavid du Colombier 	if(file != nil){
354*01a344a2SDavid du Colombier 		qunlock(file);
355*01a344a2SDavid du Colombier 		if(error)
356*01a344a2SDavid du Colombier 			freefp(file);
357*01a344a2SDavid du Colombier 	}
358*01a344a2SDavid du Colombier 	return error;
359*01a344a2SDavid du Colombier }
360*01a344a2SDavid du Colombier 
361*01a344a2SDavid du Colombier static int
362*01a344a2SDavid du Colombier flush(Chan* chan, Fcall*, Fcall*)
363*01a344a2SDavid du Colombier {
364*01a344a2SDavid du Colombier 	runlock(&chan->reflock);
365*01a344a2SDavid du Colombier 	wlock(&chan->reflock);
366*01a344a2SDavid du Colombier 	wunlock(&chan->reflock);
367*01a344a2SDavid du Colombier 	rlock(&chan->reflock);
368*01a344a2SDavid du Colombier 
369*01a344a2SDavid du Colombier 	return 0;
370*01a344a2SDavid du Colombier }
371*01a344a2SDavid du Colombier 
372*01a344a2SDavid du Colombier static void
373*01a344a2SDavid du Colombier clone(File* nfile, File* file)
374*01a344a2SDavid du Colombier {
375*01a344a2SDavid du Colombier 	Wpath *wpath;
376*01a344a2SDavid du Colombier 
377*01a344a2SDavid du Colombier 	nfile->qid = file->qid;
378*01a344a2SDavid du Colombier 
379*01a344a2SDavid du Colombier 	lock(&wpathlock);
380*01a344a2SDavid du Colombier 	nfile->wpath = file->wpath;
381*01a344a2SDavid du Colombier 	for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up)
382*01a344a2SDavid du Colombier 		wpath->refs++;
383*01a344a2SDavid du Colombier 	unlock(&wpathlock);
384*01a344a2SDavid du Colombier 
385*01a344a2SDavid du Colombier 	nfile->fs = file->fs;
386*01a344a2SDavid du Colombier 	nfile->addr = file->addr;
387*01a344a2SDavid du Colombier 	nfile->slot = file->slot;
388*01a344a2SDavid du Colombier 	nfile->uid = file->uid;
389*01a344a2SDavid du Colombier 	nfile->open = file->open & ~FREMOV;
390*01a344a2SDavid du Colombier }
391*01a344a2SDavid du Colombier 
392*01a344a2SDavid du Colombier static int
393*01a344a2SDavid du Colombier walkname(File* file, char* wname, Qid* wqid)
394*01a344a2SDavid du Colombier {
395*01a344a2SDavid du Colombier 	Wpath *w;
396*01a344a2SDavid du Colombier 	Iobuf *p, *p1;
397*01a344a2SDavid du Colombier 	Dentry *d, *d1;
398*01a344a2SDavid du Colombier 	int error, slot;
399*01a344a2SDavid du Colombier 	Off addr, qpath;
400*01a344a2SDavid du Colombier 
401*01a344a2SDavid du Colombier 	p = p1 = nil;
402*01a344a2SDavid du Colombier 
403*01a344a2SDavid du Colombier 	/*
404*01a344a2SDavid du Colombier 	 * File must not have been opened for I/O by an open
405*01a344a2SDavid du Colombier 	 * or create message and must represent a directory.
406*01a344a2SDavid du Colombier 	 */
407*01a344a2SDavid du Colombier 	if(file->open != 0){
408*01a344a2SDavid du Colombier 		error = Emode;
409*01a344a2SDavid du Colombier 		goto out;
410*01a344a2SDavid du Colombier 	}
411*01a344a2SDavid du Colombier 
412*01a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
413*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
414*01a344a2SDavid du Colombier 		error = Edir1;
415*01a344a2SDavid du Colombier 		goto out;
416*01a344a2SDavid du Colombier 	}
417*01a344a2SDavid du Colombier 	d = getdir(p, file->slot);
418*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
419*01a344a2SDavid du Colombier 		error = Ealloc;
420*01a344a2SDavid du Colombier 		goto out;
421*01a344a2SDavid du Colombier 	}
422*01a344a2SDavid du Colombier 	if(!(d->mode & DDIR)){
423*01a344a2SDavid du Colombier 		error = Edir1;
424*01a344a2SDavid du Colombier 		goto out;
425*01a344a2SDavid du Colombier 	}
426*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
427*01a344a2SDavid du Colombier 		goto out;
428*01a344a2SDavid du Colombier 
429*01a344a2SDavid du Colombier 	/*
430*01a344a2SDavid du Colombier 	 * For walked elements the implied user must
431*01a344a2SDavid du Colombier 	 * have permission to search the directory.
432*01a344a2SDavid du Colombier 	 */
433*01a344a2SDavid du Colombier 	if(file->cp != cons.chan && iaccess(file, d, DEXEC)){
434*01a344a2SDavid du Colombier 		error = Eaccess;
435*01a344a2SDavid du Colombier 		goto out;
436*01a344a2SDavid du Colombier 	}
437*01a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
438*01a344a2SDavid du Colombier 
439*01a344a2SDavid du Colombier 	if(strcmp(wname, ".") == 0){
440*01a344a2SDavid du Colombier setdot:
441*01a344a2SDavid du Colombier 		if(wqid != nil)
442*01a344a2SDavid du Colombier 			*wqid = file->qid;
443*01a344a2SDavid du Colombier 		goto out;
444*01a344a2SDavid du Colombier 	}
445*01a344a2SDavid du Colombier 	if(strcmp(wname, "..") == 0){
446*01a344a2SDavid du Colombier 		if(file->wpath == 0)
447*01a344a2SDavid du Colombier 			goto setdot;
448*01a344a2SDavid du Colombier 		putbuf(p);
449*01a344a2SDavid du Colombier 		p = nil;
450*01a344a2SDavid du Colombier 		addr = file->wpath->addr;
451*01a344a2SDavid du Colombier 		slot = file->wpath->slot;
452*01a344a2SDavid du Colombier 		p1 = getbuf(file->fs->dev, addr, Brd);
453*01a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, QPNONE)){
454*01a344a2SDavid du Colombier 			error = Edir1;
455*01a344a2SDavid du Colombier 			goto out;
456*01a344a2SDavid du Colombier 		}
457*01a344a2SDavid du Colombier 		d1 = getdir(p1, slot);
458*01a344a2SDavid du Colombier 		if(d == nil || !(d1->mode & DALLOC)){
459*01a344a2SDavid du Colombier 			error = Ephase;
460*01a344a2SDavid du Colombier 			goto out;
461*01a344a2SDavid du Colombier 		}
462*01a344a2SDavid du Colombier 		lock(&wpathlock);
463*01a344a2SDavid du Colombier 		file->wpath->refs--;
464*01a344a2SDavid du Colombier 		file->wpath = file->wpath->up;
465*01a344a2SDavid du Colombier 		unlock(&wpathlock);
466*01a344a2SDavid du Colombier 		goto found;
467*01a344a2SDavid du Colombier 	}
468*01a344a2SDavid du Colombier 
469*01a344a2SDavid du Colombier 	for(addr = 0; ; addr++){
470*01a344a2SDavid du Colombier 		if(p == nil){
471*01a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
472*01a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
473*01a344a2SDavid du Colombier 				error = Ealloc;
474*01a344a2SDavid du Colombier 				goto out;
475*01a344a2SDavid du Colombier 			}
476*01a344a2SDavid du Colombier 			d = getdir(p, file->slot);
477*01a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
478*01a344a2SDavid du Colombier 				error = Ealloc;
479*01a344a2SDavid du Colombier 				goto out;
480*01a344a2SDavid du Colombier 			}
481*01a344a2SDavid du Colombier 		}
482*01a344a2SDavid du Colombier 		qpath = d->qid.path;
483*01a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
484*01a344a2SDavid du Colombier 		p = nil;
485*01a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, qpath)){
486*01a344a2SDavid du Colombier 			error = Eentry;
487*01a344a2SDavid du Colombier 			goto out;
488*01a344a2SDavid du Colombier 		}
489*01a344a2SDavid du Colombier 		for(slot = 0; slot < DIRPERBUF; slot++){
490*01a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
491*01a344a2SDavid du Colombier 			if (!(d1->mode & DALLOC) ||
492*01a344a2SDavid du Colombier 			    strncmp(wname, d1->name, NAMELEN) != 0)
493*01a344a2SDavid du Colombier 				continue;
494*01a344a2SDavid du Colombier 			/*
495*01a344a2SDavid du Colombier 			 * update walk path
496*01a344a2SDavid du Colombier 			 */
497*01a344a2SDavid du Colombier 			if((w = newwp()) == nil){
498*01a344a2SDavid du Colombier 				error = Ewalk;
499*01a344a2SDavid du Colombier 				goto out;
500*01a344a2SDavid du Colombier 			}
501*01a344a2SDavid du Colombier 			w->addr = file->addr;
502*01a344a2SDavid du Colombier 			w->slot = file->slot;
503*01a344a2SDavid du Colombier 			w->up = file->wpath;
504*01a344a2SDavid du Colombier 			file->wpath = w;
505*01a344a2SDavid du Colombier 			slot += DIRPERBUF*addr;
506*01a344a2SDavid du Colombier 			goto found;
507*01a344a2SDavid du Colombier 		}
508*01a344a2SDavid du Colombier 		putbuf(p1);
509*01a344a2SDavid du Colombier 		p1 = nil;
510*01a344a2SDavid du Colombier 	}
511*01a344a2SDavid du Colombier 
512*01a344a2SDavid du Colombier found:
513*01a344a2SDavid du Colombier 	file->addr = p1->addr;
514*01a344a2SDavid du Colombier 	mkqid(&file->qid, d1, 1);
515*01a344a2SDavid du Colombier 	putbuf(p1);
516*01a344a2SDavid du Colombier 	p1 = nil;
517*01a344a2SDavid du Colombier 	file->slot = slot;
518*01a344a2SDavid du Colombier 	if(wqid != nil)
519*01a344a2SDavid du Colombier 		*wqid = file->qid;
520*01a344a2SDavid du Colombier 
521*01a344a2SDavid du Colombier out:
522*01a344a2SDavid du Colombier 	if(p1 != nil)
523*01a344a2SDavid du Colombier 		putbuf(p1);
524*01a344a2SDavid du Colombier 	if(p != nil)
525*01a344a2SDavid du Colombier 		putbuf(p);
526*01a344a2SDavid du Colombier 
527*01a344a2SDavid du Colombier 	return error;
528*01a344a2SDavid du Colombier }
529*01a344a2SDavid du Colombier 
530*01a344a2SDavid du Colombier static int
531*01a344a2SDavid du Colombier walk(Chan* chan, Fcall* f, Fcall* r)
532*01a344a2SDavid du Colombier {
533*01a344a2SDavid du Colombier 	int error, nwname;
534*01a344a2SDavid du Colombier 	File *file, *nfile, tfile;
535*01a344a2SDavid du Colombier 
536*01a344a2SDavid du Colombier 	/*
537*01a344a2SDavid du Colombier 	 * The file identified by f->fid must be valid in the
538*01a344a2SDavid du Colombier 	 * current session and must not have been opened for I/O
539*01a344a2SDavid du Colombier 	 * by an open or create message.
540*01a344a2SDavid du Colombier 	 */
541*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
542*01a344a2SDavid du Colombier 		return Efid;
543*01a344a2SDavid du Colombier 	if(file->open != 0){
544*01a344a2SDavid du Colombier 		qunlock(file);
545*01a344a2SDavid du Colombier 		return Emode;
546*01a344a2SDavid du Colombier 	}
547*01a344a2SDavid du Colombier 
548*01a344a2SDavid du Colombier 	/*
549*01a344a2SDavid du Colombier 	 * If newfid is not the same as fid, allocate a new file;
550*01a344a2SDavid du Colombier 	 * a side effect is checking newfid is not already in use (error);
551*01a344a2SDavid du Colombier 	 * if there are no names to walk this will be equivalent to a
552*01a344a2SDavid du Colombier 	 * simple 'clone' operation.
553*01a344a2SDavid du Colombier 	 * Otherwise, fid and newfid are the same and if there are names
554*01a344a2SDavid du Colombier 	 * to walk make a copy of 'file' to be used during the walk as
555*01a344a2SDavid du Colombier 	 * 'file' must only be updated on success.
556*01a344a2SDavid du Colombier 	 * Finally, it's a no-op if newfid is the same as fid and f->nwname
557*01a344a2SDavid du Colombier 	 * is 0.
558*01a344a2SDavid du Colombier 	 */
559*01a344a2SDavid du Colombier 	r->nwqid = 0;
560*01a344a2SDavid du Colombier 	if(f->newfid != f->fid){
561*01a344a2SDavid du Colombier 		if((nfile = filep(chan, f->newfid, 1)) == nil){
562*01a344a2SDavid du Colombier 			qunlock(file);
563*01a344a2SDavid du Colombier 			return Efidinuse;
564*01a344a2SDavid du Colombier 		}
565*01a344a2SDavid du Colombier 	} else if(f->nwname != 0){
566*01a344a2SDavid du Colombier 		nfile = &tfile;
567*01a344a2SDavid du Colombier 		memset(nfile, 0, sizeof(File));
568*01a344a2SDavid du Colombier 		nfile->cp = chan;
569*01a344a2SDavid du Colombier 		nfile->fid = ~0;
570*01a344a2SDavid du Colombier 	} else {
571*01a344a2SDavid du Colombier 		qunlock(file);
572*01a344a2SDavid du Colombier 		return 0;
573*01a344a2SDavid du Colombier 	}
574*01a344a2SDavid du Colombier 	clone(nfile, file);
575*01a344a2SDavid du Colombier 
576*01a344a2SDavid du Colombier 	/*
577*01a344a2SDavid du Colombier 	 * Should check name is not too long.
578*01a344a2SDavid du Colombier 	 */
579*01a344a2SDavid du Colombier 	error = 0;
580*01a344a2SDavid du Colombier 	for(nwname = 0; nwname < f->nwname; nwname++){
581*01a344a2SDavid du Colombier 		error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]);
582*01a344a2SDavid du Colombier 		if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid))
583*01a344a2SDavid du Colombier 			break;
584*01a344a2SDavid du Colombier 	}
585*01a344a2SDavid du Colombier 
586*01a344a2SDavid du Colombier 	if(f->nwname == 0){
587*01a344a2SDavid du Colombier 		/*
588*01a344a2SDavid du Colombier 		 * Newfid must be different to fid (see above)
589*01a344a2SDavid du Colombier 		 * so this is a simple 'clone' operation - there's
590*01a344a2SDavid du Colombier 		 * nothing to do except unlock unless there's
591*01a344a2SDavid du Colombier 		 * an error.
592*01a344a2SDavid du Colombier 		 */
593*01a344a2SDavid du Colombier 		if(error){
594*01a344a2SDavid du Colombier 			freewp(nfile->wpath);
595*01a344a2SDavid du Colombier 			qunlock(nfile);
596*01a344a2SDavid du Colombier 			freefp(nfile);
597*01a344a2SDavid du Colombier 		} else
598*01a344a2SDavid du Colombier 			qunlock(nfile);
599*01a344a2SDavid du Colombier 	} else if(r->nwqid < f->nwname){
600*01a344a2SDavid du Colombier 		/*
601*01a344a2SDavid du Colombier 		 * Didn't walk all elements, 'clunk' nfile
602*01a344a2SDavid du Colombier 		 * and leave 'file' alone.
603*01a344a2SDavid du Colombier 		 * Clear error if some of the elements were
604*01a344a2SDavid du Colombier 		 * walked OK.
605*01a344a2SDavid du Colombier 		 */
606*01a344a2SDavid du Colombier 		freewp(nfile->wpath);
607*01a344a2SDavid du Colombier 		if(nfile != &tfile){
608*01a344a2SDavid du Colombier 			qunlock(nfile);
609*01a344a2SDavid du Colombier 			freefp(nfile);
610*01a344a2SDavid du Colombier 		}
611*01a344a2SDavid du Colombier 		if(r->nwqid != 0)
612*01a344a2SDavid du Colombier 			error = 0;
613*01a344a2SDavid du Colombier 	} else {
614*01a344a2SDavid du Colombier 		/*
615*01a344a2SDavid du Colombier 		 * Walked all elements. If newfid is the same
616*01a344a2SDavid du Colombier 		 * as fid must update 'file' from the temporary
617*01a344a2SDavid du Colombier 		 * copy used during the walk.
618*01a344a2SDavid du Colombier 		 * Otherwise just unlock (when using tfile there's
619*01a344a2SDavid du Colombier 		 * no need to unlock as it's a local).
620*01a344a2SDavid du Colombier 		 */
621*01a344a2SDavid du Colombier 		if(nfile == &tfile){
622*01a344a2SDavid du Colombier 			file->qid = nfile->qid;
623*01a344a2SDavid du Colombier 			freewp(file->wpath);
624*01a344a2SDavid du Colombier 			file->wpath = nfile->wpath;
625*01a344a2SDavid du Colombier 			file->addr = nfile->addr;
626*01a344a2SDavid du Colombier 			file->slot = nfile->slot;
627*01a344a2SDavid du Colombier 		} else
628*01a344a2SDavid du Colombier 			qunlock(nfile);
629*01a344a2SDavid du Colombier 	}
630*01a344a2SDavid du Colombier 	qunlock(file);
631*01a344a2SDavid du Colombier 
632*01a344a2SDavid du Colombier 	return error;
633*01a344a2SDavid du Colombier }
634*01a344a2SDavid du Colombier 
635*01a344a2SDavid du Colombier static int
636*01a344a2SDavid du Colombier fs_open(Chan* chan, Fcall* f, Fcall* r)
637*01a344a2SDavid du Colombier {
638*01a344a2SDavid du Colombier 	Iobuf *p;
639*01a344a2SDavid du Colombier 	Dentry *d;
640*01a344a2SDavid du Colombier 	File *file;
641*01a344a2SDavid du Colombier 	Tlock *t;
642*01a344a2SDavid du Colombier 	Qid qid;
643*01a344a2SDavid du Colombier 	int error, ro, fmod, wok;
644*01a344a2SDavid du Colombier 
645*01a344a2SDavid du Colombier 	wok = 0;
646*01a344a2SDavid du Colombier 	p = nil;
647*01a344a2SDavid du Colombier 
648*01a344a2SDavid du Colombier 	if(chan == cons.chan || writeallow)
649*01a344a2SDavid du Colombier 		wok = 1;
650*01a344a2SDavid du Colombier 
651*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
652*01a344a2SDavid du Colombier 		error = Efid;
653*01a344a2SDavid du Colombier 		goto out;
654*01a344a2SDavid du Colombier 	}
655*01a344a2SDavid du Colombier 	if(file->open != 0){
656*01a344a2SDavid du Colombier 		error = Emode;
657*01a344a2SDavid du Colombier 		goto out;
658*01a344a2SDavid du Colombier 	}
659*01a344a2SDavid du Colombier 
660*01a344a2SDavid du Colombier 	/*
661*01a344a2SDavid du Colombier 	 * if remove on close, check access here
662*01a344a2SDavid du Colombier 	 */
663*01a344a2SDavid du Colombier 	ro = file->fs->dev->type == Devro;
664*01a344a2SDavid du Colombier 	if(f->mode & ORCLOSE){
665*01a344a2SDavid du Colombier 		if(ro){
666*01a344a2SDavid du Colombier 			error = Eronly;
667*01a344a2SDavid du Colombier 			goto out;
668*01a344a2SDavid du Colombier 		}
669*01a344a2SDavid du Colombier 		/*
670*01a344a2SDavid du Colombier 		 * check on parent directory of file to be deleted
671*01a344a2SDavid du Colombier 		 */
672*01a344a2SDavid du Colombier 		if(file->wpath == 0 || file->wpath->addr == file->addr){
673*01a344a2SDavid du Colombier 			error = Ephase;
674*01a344a2SDavid du Colombier 			goto out;
675*01a344a2SDavid du Colombier 		}
676*01a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->wpath->addr, Brd);
677*01a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
678*01a344a2SDavid du Colombier 			error = Ephase;
679*01a344a2SDavid du Colombier 			goto out;
680*01a344a2SDavid du Colombier 		}
681*01a344a2SDavid du Colombier 		d = getdir(p, file->wpath->slot);
682*01a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
683*01a344a2SDavid du Colombier 			error = Ephase;
684*01a344a2SDavid du Colombier 			goto out;
685*01a344a2SDavid du Colombier 		}
686*01a344a2SDavid du Colombier 		if(iaccess(file, d, DWRITE)){
687*01a344a2SDavid du Colombier 			error = Eaccess;
688*01a344a2SDavid du Colombier 			goto out;
689*01a344a2SDavid du Colombier 		}
690*01a344a2SDavid du Colombier 		putbuf(p);
691*01a344a2SDavid du Colombier 	}
692*01a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
693*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
694*01a344a2SDavid du Colombier 		error = Ealloc;
695*01a344a2SDavid du Colombier 		goto out;
696*01a344a2SDavid du Colombier 	}
697*01a344a2SDavid du Colombier 	d = getdir(p, file->slot);
698*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
699*01a344a2SDavid du Colombier 		error = Ealloc;
700*01a344a2SDavid du Colombier 		goto out;
701*01a344a2SDavid du Colombier 	}
702*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
703*01a344a2SDavid du Colombier 		goto out;
704*01a344a2SDavid du Colombier 	mkqid(&qid, d, 1);
705*01a344a2SDavid du Colombier 	switch(f->mode & 7){
706*01a344a2SDavid du Colombier 
707*01a344a2SDavid du Colombier 	case OREAD:
708*01a344a2SDavid du Colombier 		if(iaccess(file, d, DREAD) && !wok)
709*01a344a2SDavid du Colombier 			goto badaccess;
710*01a344a2SDavid du Colombier 		fmod = FREAD;
711*01a344a2SDavid du Colombier 		break;
712*01a344a2SDavid du Colombier 
713*01a344a2SDavid du Colombier 	case OWRITE:
714*01a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
715*01a344a2SDavid du Colombier 			goto badaccess;
716*01a344a2SDavid du Colombier 		if(ro){
717*01a344a2SDavid du Colombier 			error = Eronly;
718*01a344a2SDavid du Colombier 			goto out;
719*01a344a2SDavid du Colombier 		}
720*01a344a2SDavid du Colombier 		fmod = FWRITE;
721*01a344a2SDavid du Colombier 		break;
722*01a344a2SDavid du Colombier 
723*01a344a2SDavid du Colombier 	case ORDWR:
724*01a344a2SDavid du Colombier 		if((d->mode & DDIR)
725*01a344a2SDavid du Colombier 		|| (iaccess(file, d, DREAD) && !wok)
726*01a344a2SDavid du Colombier 		|| (iaccess(file, d, DWRITE) && !wok))
727*01a344a2SDavid du Colombier 			goto badaccess;
728*01a344a2SDavid du Colombier 		if(ro){
729*01a344a2SDavid du Colombier 			error = Eronly;
730*01a344a2SDavid du Colombier 			goto out;
731*01a344a2SDavid du Colombier 		}
732*01a344a2SDavid du Colombier 		fmod = FREAD+FWRITE;
733*01a344a2SDavid du Colombier 		break;
734*01a344a2SDavid du Colombier 
735*01a344a2SDavid du Colombier 	case OEXEC:
736*01a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok))
737*01a344a2SDavid du Colombier 			goto badaccess;
738*01a344a2SDavid du Colombier 		fmod = FREAD;
739*01a344a2SDavid du Colombier 		break;
740*01a344a2SDavid du Colombier 
741*01a344a2SDavid du Colombier 	default:
742*01a344a2SDavid du Colombier 		error = Emode;
743*01a344a2SDavid du Colombier 		goto out;
744*01a344a2SDavid du Colombier 	}
745*01a344a2SDavid du Colombier 	if(f->mode & OTRUNC){
746*01a344a2SDavid du Colombier 		if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok))
747*01a344a2SDavid du Colombier 			goto badaccess;
748*01a344a2SDavid du Colombier 		if(ro){
749*01a344a2SDavid du Colombier 			error = Eronly;
750*01a344a2SDavid du Colombier 			goto out;
751*01a344a2SDavid du Colombier 		}
752*01a344a2SDavid du Colombier 	}
753*01a344a2SDavid du Colombier 	t = 0;
754*01a344a2SDavid du Colombier 	if(d->mode & DLOCK){
755*01a344a2SDavid du Colombier 		if((t = tlocked(p, d)) == nil){
756*01a344a2SDavid du Colombier 			error = Elocked;
757*01a344a2SDavid du Colombier 			goto out;
758*01a344a2SDavid du Colombier 		}
759*01a344a2SDavid du Colombier 	}
760*01a344a2SDavid du Colombier 	if(f->mode & ORCLOSE)
761*01a344a2SDavid du Colombier 		fmod |= FREMOV;
762*01a344a2SDavid du Colombier 	file->open = fmod;
763*01a344a2SDavid du Colombier 	if((f->mode & OTRUNC) && !(d->mode & DAPND)){
764*01a344a2SDavid du Colombier 		dtrunc(p, d, file->uid);
765*01a344a2SDavid du Colombier 		qid.vers = d->qid.version;
766*01a344a2SDavid du Colombier 	}
767*01a344a2SDavid du Colombier 	r->qid = qid;
768*01a344a2SDavid du Colombier 	file->tlock = t;
769*01a344a2SDavid du Colombier 	if(t != nil)
770*01a344a2SDavid du Colombier 		t->file = file;
771*01a344a2SDavid du Colombier 	file->lastra = 1;
772*01a344a2SDavid du Colombier 	goto out;
773*01a344a2SDavid du Colombier 
774*01a344a2SDavid du Colombier badaccess:
775*01a344a2SDavid du Colombier 	error = Eaccess;
776*01a344a2SDavid du Colombier 	file->open = 0;
777*01a344a2SDavid du Colombier 
778*01a344a2SDavid du Colombier out:
779*01a344a2SDavid du Colombier 	if(p != nil)
780*01a344a2SDavid du Colombier 		putbuf(p);
781*01a344a2SDavid du Colombier 	if(file != nil)
782*01a344a2SDavid du Colombier 		qunlock(file);
783*01a344a2SDavid du Colombier 
784*01a344a2SDavid du Colombier 	r->iounit = chan->msize-IOHDRSZ;
785*01a344a2SDavid du Colombier 
786*01a344a2SDavid du Colombier 	return error;
787*01a344a2SDavid du Colombier }
788*01a344a2SDavid du Colombier 
789*01a344a2SDavid du Colombier static int
790*01a344a2SDavid du Colombier fs_create(Chan* chan, Fcall* f, Fcall* r)
791*01a344a2SDavid du Colombier {
792*01a344a2SDavid du Colombier 	Iobuf *p, *p1;
793*01a344a2SDavid du Colombier 	Dentry *d, *d1;
794*01a344a2SDavid du Colombier 	File *file;
795*01a344a2SDavid du Colombier 	int error, slot, slot1, fmod, wok;
796*01a344a2SDavid du Colombier 	Off addr, addr1, path;
797*01a344a2SDavid du Colombier 	Tlock *t;
798*01a344a2SDavid du Colombier 	Wpath *w;
799*01a344a2SDavid du Colombier 
800*01a344a2SDavid du Colombier 	wok = 0;
801*01a344a2SDavid du Colombier 	p = nil;
802*01a344a2SDavid du Colombier 
803*01a344a2SDavid du Colombier 	if(chan == cons.chan || writeallow)
804*01a344a2SDavid du Colombier 		wok = 1;
805*01a344a2SDavid du Colombier 
806*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
807*01a344a2SDavid du Colombier 		error = Efid;
808*01a344a2SDavid du Colombier 		goto out;
809*01a344a2SDavid du Colombier 	}
810*01a344a2SDavid du Colombier 	if(file->fs->dev->type == Devro){
811*01a344a2SDavid du Colombier 		error = Eronly;
812*01a344a2SDavid du Colombier 		goto out;
813*01a344a2SDavid du Colombier 	}
814*01a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
815*01a344a2SDavid du Colombier 		error = Emode;
816*01a344a2SDavid du Colombier 		goto out;
817*01a344a2SDavid du Colombier 	}
818*01a344a2SDavid du Colombier 
819*01a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
820*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
821*01a344a2SDavid du Colombier 		error = Ealloc;
822*01a344a2SDavid du Colombier 		goto out;
823*01a344a2SDavid du Colombier 	}
824*01a344a2SDavid du Colombier 	d = getdir(p, file->slot);
825*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
826*01a344a2SDavid du Colombier 		error = Ealloc;
827*01a344a2SDavid du Colombier 		goto out;
828*01a344a2SDavid du Colombier 	}
829*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
830*01a344a2SDavid du Colombier 		goto out;
831*01a344a2SDavid du Colombier 	if(!(d->mode & DDIR)){
832*01a344a2SDavid du Colombier 		error = Edir2;
833*01a344a2SDavid du Colombier 		goto out;
834*01a344a2SDavid du Colombier 	}
835*01a344a2SDavid du Colombier 	if(iaccess(file, d, DWRITE) && !wok) {
836*01a344a2SDavid du Colombier 		error = Eaccess;
837*01a344a2SDavid du Colombier 		goto out;
838*01a344a2SDavid du Colombier 	}
839*01a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
840*01a344a2SDavid du Colombier 
841*01a344a2SDavid du Colombier 	/*
842*01a344a2SDavid du Colombier 	 * Check the name is valid (and will fit in an old
843*01a344a2SDavid du Colombier 	 * directory entry for the moment).
844*01a344a2SDavid du Colombier 	 */
845*01a344a2SDavid du Colombier 	if(error = checkname9p2(f->name))
846*01a344a2SDavid du Colombier 		goto out;
847*01a344a2SDavid du Colombier 
848*01a344a2SDavid du Colombier 	addr1 = 0;
849*01a344a2SDavid du Colombier 	slot1 = 0;	/* set */
850*01a344a2SDavid du Colombier 	for(addr = 0; ; addr++){
851*01a344a2SDavid du Colombier 		if((p1 = dnodebuf(p, d, addr, 0, file->uid)) == nil){
852*01a344a2SDavid du Colombier 			if(addr1 != 0)
853*01a344a2SDavid du Colombier 				break;
854*01a344a2SDavid du Colombier 			p1 = dnodebuf(p, d, addr, Tdir, file->uid);
855*01a344a2SDavid du Colombier 		}
856*01a344a2SDavid du Colombier 		if(p1 == nil){
857*01a344a2SDavid du Colombier 			error = Efull;
858*01a344a2SDavid du Colombier 			goto out;
859*01a344a2SDavid du Colombier 		}
860*01a344a2SDavid du Colombier 		if(checktag(p1, Tdir, d->qid.path)){
861*01a344a2SDavid du Colombier 			putbuf(p1);
862*01a344a2SDavid du Colombier 			goto phase;
863*01a344a2SDavid du Colombier 		}
864*01a344a2SDavid du Colombier 		for(slot = 0; slot < DIRPERBUF; slot++){
865*01a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
866*01a344a2SDavid du Colombier 			if(!(d1->mode & DALLOC)){
867*01a344a2SDavid du Colombier 				if(addr1 == 0){
868*01a344a2SDavid du Colombier 					addr1 = p1->addr;
869*01a344a2SDavid du Colombier 					slot1 = slot + addr*DIRPERBUF;
870*01a344a2SDavid du Colombier 				}
871*01a344a2SDavid du Colombier 				continue;
872*01a344a2SDavid du Colombier 			}
873*01a344a2SDavid du Colombier 			if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){
874*01a344a2SDavid du Colombier 				putbuf(p1);
875*01a344a2SDavid du Colombier 				error = Eexist;
876*01a344a2SDavid du Colombier 				goto out;
877*01a344a2SDavid du Colombier 			}
878*01a344a2SDavid du Colombier 		}
879*01a344a2SDavid du Colombier 		putbuf(p1);
880*01a344a2SDavid du Colombier 	}
881*01a344a2SDavid du Colombier 
882*01a344a2SDavid du Colombier 	switch(f->mode & 7){
883*01a344a2SDavid du Colombier 	case OEXEC:
884*01a344a2SDavid du Colombier 	case OREAD:		/* seems only useful to make directories */
885*01a344a2SDavid du Colombier 		fmod = FREAD;
886*01a344a2SDavid du Colombier 		break;
887*01a344a2SDavid du Colombier 
888*01a344a2SDavid du Colombier 	case OWRITE:
889*01a344a2SDavid du Colombier 		fmod = FWRITE;
890*01a344a2SDavid du Colombier 		break;
891*01a344a2SDavid du Colombier 
892*01a344a2SDavid du Colombier 	case ORDWR:
893*01a344a2SDavid du Colombier 		fmod = FREAD+FWRITE;
894*01a344a2SDavid du Colombier 		break;
895*01a344a2SDavid du Colombier 
896*01a344a2SDavid du Colombier 	default:
897*01a344a2SDavid du Colombier 		error = Emode;
898*01a344a2SDavid du Colombier 		goto out;
899*01a344a2SDavid du Colombier 	}
900*01a344a2SDavid du Colombier 	if(f->perm & PDIR)
901*01a344a2SDavid du Colombier 		if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE))
902*01a344a2SDavid du Colombier 			goto badaccess;
903*01a344a2SDavid du Colombier 	/*
904*01a344a2SDavid du Colombier 	 * do it
905*01a344a2SDavid du Colombier 	 */
906*01a344a2SDavid du Colombier 	path = qidpathgen(file->fs->dev);
907*01a344a2SDavid du Colombier 	if((p1 = getbuf(file->fs->dev, addr1, Brd|Bimm|Bmod)) == nil)
908*01a344a2SDavid du Colombier 		goto phase;
909*01a344a2SDavid du Colombier 	d1 = getdir(p1, slot1);
910*01a344a2SDavid du Colombier 	if(d1 == nil || checktag(p1, Tdir, d->qid.path)) {
911*01a344a2SDavid du Colombier 		putbuf(p1);
912*01a344a2SDavid du Colombier 		goto phase;
913*01a344a2SDavid du Colombier 	}
914*01a344a2SDavid du Colombier 	if(d1->mode & DALLOC){
915*01a344a2SDavid du Colombier 		putbuf(p1);
916*01a344a2SDavid du Colombier 		goto phase;
917*01a344a2SDavid du Colombier 	}
918*01a344a2SDavid du Colombier 
919*01a344a2SDavid du Colombier 	strncpy(d1->name, f->name, sizeof(d1->name));
920*01a344a2SDavid du Colombier 	if(chan == cons.chan){
921*01a344a2SDavid du Colombier 		d1->uid = cons.uid;
922*01a344a2SDavid du Colombier 		d1->gid = cons.gid;
923*01a344a2SDavid du Colombier 	} else {
924*01a344a2SDavid du Colombier 		d1->uid = file->uid;
925*01a344a2SDavid du Colombier 		d1->gid = d->gid;
926*01a344a2SDavid du Colombier 		f->perm &= d->mode | ~0666;
927*01a344a2SDavid du Colombier 		if(f->perm & PDIR)
928*01a344a2SDavid du Colombier 			f->perm &= d->mode | ~0777;
929*01a344a2SDavid du Colombier 	}
930*01a344a2SDavid du Colombier 	d1->qid.path = path;
931*01a344a2SDavid du Colombier 	d1->qid.version = 0;
932*01a344a2SDavid du Colombier 	d1->mode = DALLOC | (f->perm & 0777);
933*01a344a2SDavid du Colombier 	if(f->perm & PDIR) {
934*01a344a2SDavid du Colombier 		d1->mode |= DDIR;
935*01a344a2SDavid du Colombier 		d1->qid.path |= QPDIR;
936*01a344a2SDavid du Colombier 	}
937*01a344a2SDavid du Colombier 	if(f->perm & PAPND)
938*01a344a2SDavid du Colombier 		d1->mode |= DAPND;
939*01a344a2SDavid du Colombier 	t = nil;
940*01a344a2SDavid du Colombier 	if(f->perm & PLOCK){
941*01a344a2SDavid du Colombier 		d1->mode |= DLOCK;
942*01a344a2SDavid du Colombier 		t = tlocked(p1, d1);
943*01a344a2SDavid du Colombier 		/* if nil, out of tlock structures */
944*01a344a2SDavid du Colombier 	}
945*01a344a2SDavid du Colombier 	accessdir(p1, d1, FWRITE, file->uid);
946*01a344a2SDavid du Colombier 	mkqid(&r->qid, d1, 0);
947*01a344a2SDavid du Colombier 	putbuf(p1);
948*01a344a2SDavid du Colombier 	accessdir(p, d, FWRITE, file->uid);
949*01a344a2SDavid du Colombier 
950*01a344a2SDavid du Colombier 	/*
951*01a344a2SDavid du Colombier 	 * do a walk to new directory entry
952*01a344a2SDavid du Colombier 	 */
953*01a344a2SDavid du Colombier 	if((w = newwp()) == nil){
954*01a344a2SDavid du Colombier 		error = Ewalk;
955*01a344a2SDavid du Colombier 		goto out;
956*01a344a2SDavid du Colombier 	}
957*01a344a2SDavid du Colombier 	w->addr = file->addr;
958*01a344a2SDavid du Colombier 	w->slot = file->slot;
959*01a344a2SDavid du Colombier 	w->up = file->wpath;
960*01a344a2SDavid du Colombier 	file->wpath = w;
961*01a344a2SDavid du Colombier 	file->qid = r->qid;
962*01a344a2SDavid du Colombier 	file->tlock = t;
963*01a344a2SDavid du Colombier 	if(t != nil)
964*01a344a2SDavid du Colombier 		t->file = file;
965*01a344a2SDavid du Colombier 	file->lastra = 1;
966*01a344a2SDavid du Colombier 	if(f->mode & ORCLOSE)
967*01a344a2SDavid du Colombier 		fmod |= FREMOV;
968*01a344a2SDavid du Colombier 	file->open = fmod;
969*01a344a2SDavid du Colombier 	file->addr = addr1;
970*01a344a2SDavid du Colombier 	file->slot = slot1;
971*01a344a2SDavid du Colombier 	goto out;
972*01a344a2SDavid du Colombier 
973*01a344a2SDavid du Colombier badaccess:
974*01a344a2SDavid du Colombier 	error = Eaccess;
975*01a344a2SDavid du Colombier 	goto out;
976*01a344a2SDavid du Colombier 
977*01a344a2SDavid du Colombier phase:
978*01a344a2SDavid du Colombier 	error = Ephase;
979*01a344a2SDavid du Colombier 
980*01a344a2SDavid du Colombier out:
981*01a344a2SDavid du Colombier 	if(p != nil)
982*01a344a2SDavid du Colombier 		putbuf(p);
983*01a344a2SDavid du Colombier 	if(file != nil)
984*01a344a2SDavid du Colombier 		qunlock(file);
985*01a344a2SDavid du Colombier 
986*01a344a2SDavid du Colombier 	r->iounit = chan->msize-IOHDRSZ;
987*01a344a2SDavid du Colombier 
988*01a344a2SDavid du Colombier 	return error;
989*01a344a2SDavid du Colombier }
990*01a344a2SDavid du Colombier 
991*01a344a2SDavid du Colombier static int
992*01a344a2SDavid du Colombier fs_read(Chan* chan, Fcall* f, Fcall* r, uchar* data)
993*01a344a2SDavid du Colombier {
994*01a344a2SDavid du Colombier 	Iobuf *p, *p1;
995*01a344a2SDavid du Colombier 	File *file;
996*01a344a2SDavid du Colombier 	Dentry *d, *d1;
997*01a344a2SDavid du Colombier 	Tlock *t;
998*01a344a2SDavid du Colombier 	Off addr, offset, start;
999*01a344a2SDavid du Colombier 	Timet tim;
1000*01a344a2SDavid du Colombier 	int error, iounit, nread, count, n, o, slot;
1001*01a344a2SDavid du Colombier 	Msgbuf *dmb;
1002*01a344a2SDavid du Colombier 	Dir dir;
1003*01a344a2SDavid du Colombier 
1004*01a344a2SDavid du Colombier 	p = nil;
1005*01a344a2SDavid du Colombier 
1006*01a344a2SDavid du Colombier 	error = 0;
1007*01a344a2SDavid du Colombier 	count = f->count;
1008*01a344a2SDavid du Colombier 	offset = f->offset;
1009*01a344a2SDavid du Colombier 	nread = 0;
1010*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
1011*01a344a2SDavid du Colombier 		error = Efid;
1012*01a344a2SDavid du Colombier 		goto out;
1013*01a344a2SDavid du Colombier 	}
1014*01a344a2SDavid du Colombier 	if(!(file->open & FREAD)){
1015*01a344a2SDavid du Colombier 		error = Eopen;
1016*01a344a2SDavid du Colombier 		goto out;
1017*01a344a2SDavid du Colombier 	}
1018*01a344a2SDavid du Colombier 	iounit = chan->msize-IOHDRSZ;
1019*01a344a2SDavid du Colombier 	if(count < 0 || count > iounit){
1020*01a344a2SDavid du Colombier 		error = Ecount;
1021*01a344a2SDavid du Colombier 		goto out;
1022*01a344a2SDavid du Colombier 	}
1023*01a344a2SDavid du Colombier 	if(offset < 0){
1024*01a344a2SDavid du Colombier 		error = Eoffset;
1025*01a344a2SDavid du Colombier 		goto out;
1026*01a344a2SDavid du Colombier 	}
1027*01a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
1028*01a344a2SDavid du Colombier 		nread = authread(file, (uchar*)data, count);
1029*01a344a2SDavid du Colombier 		if(nread < 0)
1030*01a344a2SDavid du Colombier 			error = Eauth2;
1031*01a344a2SDavid du Colombier 		goto out;
1032*01a344a2SDavid du Colombier 	}
1033*01a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
1034*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
1035*01a344a2SDavid du Colombier 		error = Ealloc;
1036*01a344a2SDavid du Colombier 		goto out;
1037*01a344a2SDavid du Colombier 	}
1038*01a344a2SDavid du Colombier 	d = getdir(p, file->slot);
1039*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
1040*01a344a2SDavid du Colombier 		error = Ealloc;
1041*01a344a2SDavid du Colombier 		goto out;
1042*01a344a2SDavid du Colombier 	}
1043*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
1044*01a344a2SDavid du Colombier 		goto out;
1045*01a344a2SDavid du Colombier 	if(t = file->tlock){
1046*01a344a2SDavid du Colombier 		tim = toytime();
1047*01a344a2SDavid du Colombier 		if(t->time < tim || t->file != file){
1048*01a344a2SDavid du Colombier 			error = Ebroken;
1049*01a344a2SDavid du Colombier 			goto out;
1050*01a344a2SDavid du Colombier 		}
1051*01a344a2SDavid du Colombier 		/* renew the lock */
1052*01a344a2SDavid du Colombier 		t->time = tim + TLOCK;
1053*01a344a2SDavid du Colombier 	}
1054*01a344a2SDavid du Colombier 	accessdir(p, d, FREAD, file->uid);
1055*01a344a2SDavid du Colombier 	if(d->mode & DDIR)
1056*01a344a2SDavid du Colombier 		goto dread;
1057*01a344a2SDavid du Colombier 	if(offset+count > d->size)
1058*01a344a2SDavid du Colombier 		count = d->size - offset;
1059*01a344a2SDavid du Colombier 	while(count > 0){
1060*01a344a2SDavid du Colombier 		if(p == nil){
1061*01a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
1062*01a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
1063*01a344a2SDavid du Colombier 				error = Ealloc;
1064*01a344a2SDavid du Colombier 				goto out;
1065*01a344a2SDavid du Colombier 			}
1066*01a344a2SDavid du Colombier 			d = getdir(p, file->slot);
1067*01a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
1068*01a344a2SDavid du Colombier 				error = Ealloc;
1069*01a344a2SDavid du Colombier 				goto out;
1070*01a344a2SDavid du Colombier 			}
1071*01a344a2SDavid du Colombier 		}
1072*01a344a2SDavid du Colombier 		addr = offset / BUFSIZE;
1073*01a344a2SDavid du Colombier 		file->lastra = dbufread(p, d, addr, file->lastra, file->uid);
1074*01a344a2SDavid du Colombier 		o = offset % BUFSIZE;
1075*01a344a2SDavid du Colombier 		n = BUFSIZE - o;
1076*01a344a2SDavid du Colombier 		if(n > count)
1077*01a344a2SDavid du Colombier 			n = count;
1078*01a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
1079*01a344a2SDavid du Colombier 		p = nil;
1080*01a344a2SDavid du Colombier 		if(p1 != nil){
1081*01a344a2SDavid du Colombier 			if(checktag(p1, Tfile, QPNONE)){
1082*01a344a2SDavid du Colombier 				error = Ephase;
1083*01a344a2SDavid du Colombier 				putbuf(p1);
1084*01a344a2SDavid du Colombier 				goto out;
1085*01a344a2SDavid du Colombier 			}
1086*01a344a2SDavid du Colombier 			memmove(data+nread, p1->iobuf+o, n);
1087*01a344a2SDavid du Colombier 			putbuf(p1);
1088*01a344a2SDavid du Colombier 		} else
1089*01a344a2SDavid du Colombier 			memset(data+nread, 0, n);
1090*01a344a2SDavid du Colombier 		count -= n;
1091*01a344a2SDavid du Colombier 		nread += n;
1092*01a344a2SDavid du Colombier 		offset += n;
1093*01a344a2SDavid du Colombier 	}
1094*01a344a2SDavid du Colombier 	goto out;
1095*01a344a2SDavid du Colombier 
1096*01a344a2SDavid du Colombier dread:
1097*01a344a2SDavid du Colombier 	/*
1098*01a344a2SDavid du Colombier 	 * Pick up where we left off last time if nothing has changed,
1099*01a344a2SDavid du Colombier 	 * otherwise must scan from the beginning.
1100*01a344a2SDavid du Colombier 	 */
1101*01a344a2SDavid du Colombier 	if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){
1102*01a344a2SDavid du Colombier 		addr = file->dslot/DIRPERBUF;
1103*01a344a2SDavid du Colombier 		slot = file->dslot%DIRPERBUF;
1104*01a344a2SDavid du Colombier 		start = offset;
1105*01a344a2SDavid du Colombier 	} else {
1106*01a344a2SDavid du Colombier 		addr = 0;
1107*01a344a2SDavid du Colombier 		slot = 0;
1108*01a344a2SDavid du Colombier 		start = 0;
1109*01a344a2SDavid du Colombier 	}
1110*01a344a2SDavid du Colombier 
1111*01a344a2SDavid du Colombier 	dmb = mballoc(iounit, chan, Mbreply1);
1112*01a344a2SDavid du Colombier 	for (;;) {
1113*01a344a2SDavid du Colombier 		if(p == nil){
1114*01a344a2SDavid du Colombier 			/*
1115*01a344a2SDavid du Colombier 			 * This is just a check to ensure the entry hasn't
1116*01a344a2SDavid du Colombier 			 * gone away during the read of each directory block.
1117*01a344a2SDavid du Colombier 			 */
1118*01a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd);
1119*01a344a2SDavid du Colombier 			if(p == nil || checktag(p, Tdir, QPNONE)){
1120*01a344a2SDavid du Colombier 				error = Ealloc;
1121*01a344a2SDavid du Colombier 				goto out1;
1122*01a344a2SDavid du Colombier 			}
1123*01a344a2SDavid du Colombier 			d = getdir(p, file->slot);
1124*01a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
1125*01a344a2SDavid du Colombier 				error = Ealloc;
1126*01a344a2SDavid du Colombier 				goto out1;
1127*01a344a2SDavid du Colombier 			}
1128*01a344a2SDavid du Colombier 		}
1129*01a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, 0, file->uid);
1130*01a344a2SDavid du Colombier 		p = nil;
1131*01a344a2SDavid du Colombier 		if(p1 == nil)
1132*01a344a2SDavid du Colombier 			goto out1;
1133*01a344a2SDavid du Colombier 		if(checktag(p1, Tdir, QPNONE)){
1134*01a344a2SDavid du Colombier 			error = Ephase;
1135*01a344a2SDavid du Colombier 			putbuf(p1);
1136*01a344a2SDavid du Colombier 			goto out1;
1137*01a344a2SDavid du Colombier 		}
1138*01a344a2SDavid du Colombier 
1139*01a344a2SDavid du Colombier 		for(; slot < DIRPERBUF; slot++){
1140*01a344a2SDavid du Colombier 			d1 = getdir(p1, slot);
1141*01a344a2SDavid du Colombier 			if(!(d1->mode & DALLOC))
1142*01a344a2SDavid du Colombier 				continue;
1143*01a344a2SDavid du Colombier 			mkdir9p2(&dir, d1, dmb->data);
1144*01a344a2SDavid du Colombier 			n = convD2M(&dir, data+nread, iounit - nread);
1145*01a344a2SDavid du Colombier 			if(n <= BIT16SZ){
1146*01a344a2SDavid du Colombier 				putbuf(p1);
1147*01a344a2SDavid du Colombier 				goto out1;
1148*01a344a2SDavid du Colombier 			}
1149*01a344a2SDavid du Colombier 			start += n;
1150*01a344a2SDavid du Colombier 			if(start < offset)
1151*01a344a2SDavid du Colombier 				continue;
1152*01a344a2SDavid du Colombier 			if(count < n){
1153*01a344a2SDavid du Colombier 				putbuf(p1);
1154*01a344a2SDavid du Colombier 				goto out1;
1155*01a344a2SDavid du Colombier 			}
1156*01a344a2SDavid du Colombier 			count -= n;
1157*01a344a2SDavid du Colombier 			nread += n;
1158*01a344a2SDavid du Colombier 			offset += n;
1159*01a344a2SDavid du Colombier 		}
1160*01a344a2SDavid du Colombier 		putbuf(p1);
1161*01a344a2SDavid du Colombier 		slot = 0;
1162*01a344a2SDavid du Colombier 		addr++;
1163*01a344a2SDavid du Colombier 	}
1164*01a344a2SDavid du Colombier out1:
1165*01a344a2SDavid du Colombier 	mbfree(dmb);
1166*01a344a2SDavid du Colombier 	if(error == 0){
1167*01a344a2SDavid du Colombier 		file->doffset = offset;
1168*01a344a2SDavid du Colombier 		file->dvers = file->qid.vers;
1169*01a344a2SDavid du Colombier 		file->dslot = slot+DIRPERBUF*addr;
1170*01a344a2SDavid du Colombier 	}
1171*01a344a2SDavid du Colombier 
1172*01a344a2SDavid du Colombier out:
1173*01a344a2SDavid du Colombier 	/*
1174*01a344a2SDavid du Colombier 	 * Do we need this any more?
1175*01a344a2SDavid du Colombier 	count = f->count - nread;
1176*01a344a2SDavid du Colombier 	if(count > 0)
1177*01a344a2SDavid du Colombier 		memset(data+nread, 0, count);
1178*01a344a2SDavid du Colombier 	 */
1179*01a344a2SDavid du Colombier 	if(p != nil)
1180*01a344a2SDavid du Colombier 		putbuf(p);
1181*01a344a2SDavid du Colombier 	if(file != nil)
1182*01a344a2SDavid du Colombier 		qunlock(file);
1183*01a344a2SDavid du Colombier 	r->count = nread;
1184*01a344a2SDavid du Colombier 	r->data = (char*)data;
1185*01a344a2SDavid du Colombier 
1186*01a344a2SDavid du Colombier 	return error;
1187*01a344a2SDavid du Colombier }
1188*01a344a2SDavid du Colombier 
1189*01a344a2SDavid du Colombier static int
1190*01a344a2SDavid du Colombier fs_write(Chan* chan, Fcall* f, Fcall* r)
1191*01a344a2SDavid du Colombier {
1192*01a344a2SDavid du Colombier 	Iobuf *p, *p1;
1193*01a344a2SDavid du Colombier 	Dentry *d;
1194*01a344a2SDavid du Colombier 	File *file;
1195*01a344a2SDavid du Colombier 	Tlock *t;
1196*01a344a2SDavid du Colombier 	Off offset, addr, qpath;
1197*01a344a2SDavid du Colombier 	Timet tim;
1198*01a344a2SDavid du Colombier 	int count, error, nwrite, o, n;
1199*01a344a2SDavid du Colombier 
1200*01a344a2SDavid du Colombier 	error = 0;
1201*01a344a2SDavid du Colombier 	offset = f->offset;
1202*01a344a2SDavid du Colombier 	count = f->count;
1203*01a344a2SDavid du Colombier 
1204*01a344a2SDavid du Colombier 	nwrite = 0;
1205*01a344a2SDavid du Colombier 	p = nil;
1206*01a344a2SDavid du Colombier 
1207*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil){
1208*01a344a2SDavid du Colombier 		error = Efid;
1209*01a344a2SDavid du Colombier 		goto out;
1210*01a344a2SDavid du Colombier 	}
1211*01a344a2SDavid du Colombier 	if(!(file->open & FWRITE)){
1212*01a344a2SDavid du Colombier 		error = Eopen;
1213*01a344a2SDavid du Colombier 		goto out;
1214*01a344a2SDavid du Colombier 	}
1215*01a344a2SDavid du Colombier 	if(count < 0 || count > chan->msize-IOHDRSZ){
1216*01a344a2SDavid du Colombier 		error = Ecount;
1217*01a344a2SDavid du Colombier 		goto out;
1218*01a344a2SDavid du Colombier 	}
1219*01a344a2SDavid du Colombier 	if(offset < 0) {
1220*01a344a2SDavid du Colombier 		error = Eoffset;
1221*01a344a2SDavid du Colombier 		goto out;
1222*01a344a2SDavid du Colombier 	}
1223*01a344a2SDavid du Colombier 
1224*01a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
1225*01a344a2SDavid du Colombier 		nwrite = authwrite(file, (uchar*)f->data, count);
1226*01a344a2SDavid du Colombier 		if(nwrite < 0)
1227*01a344a2SDavid du Colombier 			error = Eauth2;
1228*01a344a2SDavid du Colombier 		goto out;
1229*01a344a2SDavid du Colombier 	} else if(file->fs->dev->type == Devro){
1230*01a344a2SDavid du Colombier 		error = Eronly;
1231*01a344a2SDavid du Colombier 		goto out;
1232*01a344a2SDavid du Colombier 	}
1233*01a344a2SDavid du Colombier 
1234*01a344a2SDavid du Colombier 	if ((p = getbuf(file->fs->dev, file->addr, Brd|Bmod)) == nil ||
1235*01a344a2SDavid du Colombier 	    (d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)) {
1236*01a344a2SDavid du Colombier 		error = Ealloc;
1237*01a344a2SDavid du Colombier 		goto out;
1238*01a344a2SDavid du Colombier 	}
1239*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
1240*01a344a2SDavid du Colombier 		goto out;
1241*01a344a2SDavid du Colombier 	if(t = file->tlock) {
1242*01a344a2SDavid du Colombier 		tim = toytime();
1243*01a344a2SDavid du Colombier 		if(t->time < tim || t->file != file){
1244*01a344a2SDavid du Colombier 			error = Ebroken;
1245*01a344a2SDavid du Colombier 			goto out;
1246*01a344a2SDavid du Colombier 		}
1247*01a344a2SDavid du Colombier 		/* renew the lock */
1248*01a344a2SDavid du Colombier 		t->time = tim + TLOCK;
1249*01a344a2SDavid du Colombier 	}
1250*01a344a2SDavid du Colombier 	accessdir(p, d, FWRITE, file->uid);
1251*01a344a2SDavid du Colombier 	if(d->mode & DAPND)
1252*01a344a2SDavid du Colombier 		offset = d->size;
1253*01a344a2SDavid du Colombier 	if(offset+count > d->size)
1254*01a344a2SDavid du Colombier 		d->size = offset+count;
1255*01a344a2SDavid du Colombier 	while(count > 0){
1256*01a344a2SDavid du Colombier 		if(p == nil){
1257*01a344a2SDavid du Colombier 			p = getbuf(file->fs->dev, file->addr, Brd|Bmod);
1258*01a344a2SDavid du Colombier 			if(p == nil){
1259*01a344a2SDavid du Colombier 				error = Ealloc;
1260*01a344a2SDavid du Colombier 				goto out;
1261*01a344a2SDavid du Colombier 			}
1262*01a344a2SDavid du Colombier 			d = getdir(p, file->slot);
1263*01a344a2SDavid du Colombier 			if(d == nil || !(d->mode & DALLOC)){
1264*01a344a2SDavid du Colombier 				error = Ealloc;
1265*01a344a2SDavid du Colombier 				goto out;
1266*01a344a2SDavid du Colombier 			}
1267*01a344a2SDavid du Colombier 		}
1268*01a344a2SDavid du Colombier 		addr = offset / BUFSIZE;
1269*01a344a2SDavid du Colombier 		o = offset % BUFSIZE;
1270*01a344a2SDavid du Colombier 		n = BUFSIZE - o;
1271*01a344a2SDavid du Colombier 		if(n > count)
1272*01a344a2SDavid du Colombier 			n = count;
1273*01a344a2SDavid du Colombier 		qpath = d->qid.path;
1274*01a344a2SDavid du Colombier 		p1 = dnodebuf1(p, d, addr, Tfile, file->uid);
1275*01a344a2SDavid du Colombier 		p = nil;
1276*01a344a2SDavid du Colombier 		if(p1 == nil) {
1277*01a344a2SDavid du Colombier 			error = Efull;
1278*01a344a2SDavid du Colombier 			goto out;
1279*01a344a2SDavid du Colombier 		}
1280*01a344a2SDavid du Colombier 		if(checktag(p1, Tfile, qpath)){
1281*01a344a2SDavid du Colombier 			putbuf(p1);
1282*01a344a2SDavid du Colombier 			error = Ephase;
1283*01a344a2SDavid du Colombier 			goto out;
1284*01a344a2SDavid du Colombier 		}
1285*01a344a2SDavid du Colombier 		memmove(p1->iobuf+o, f->data+nwrite, n);
1286*01a344a2SDavid du Colombier 		p1->flags |= Bmod;
1287*01a344a2SDavid du Colombier 		putbuf(p1);
1288*01a344a2SDavid du Colombier 		count -= n;
1289*01a344a2SDavid du Colombier 		nwrite += n;
1290*01a344a2SDavid du Colombier 		offset += n;
1291*01a344a2SDavid du Colombier 	}
1292*01a344a2SDavid du Colombier 
1293*01a344a2SDavid du Colombier out:
1294*01a344a2SDavid du Colombier 	if(p != nil)
1295*01a344a2SDavid du Colombier 		putbuf(p);
1296*01a344a2SDavid du Colombier 	if(file != nil)
1297*01a344a2SDavid du Colombier 		qunlock(file);
1298*01a344a2SDavid du Colombier 	r->count = nwrite;
1299*01a344a2SDavid du Colombier 
1300*01a344a2SDavid du Colombier 	return error;
1301*01a344a2SDavid du Colombier }
1302*01a344a2SDavid du Colombier 
1303*01a344a2SDavid du Colombier static int
1304*01a344a2SDavid du Colombier _clunk(File* file, int remove, int wok)
1305*01a344a2SDavid du Colombier {
1306*01a344a2SDavid du Colombier 	Tlock *t;
1307*01a344a2SDavid du Colombier 	int error;
1308*01a344a2SDavid du Colombier 
1309*01a344a2SDavid du Colombier 	error = 0;
1310*01a344a2SDavid du Colombier 	if(t = file->tlock){
1311*01a344a2SDavid du Colombier 		if(t->file == file)
1312*01a344a2SDavid du Colombier 			t->time = 0;		/* free the lock */
1313*01a344a2SDavid du Colombier 		file->tlock = 0;
1314*01a344a2SDavid du Colombier 	}
1315*01a344a2SDavid du Colombier 	if(remove && (file->qid.type & QTAUTH) == 0)
1316*01a344a2SDavid du Colombier 		error = doremove(file, wok);
1317*01a344a2SDavid du Colombier 	file->open = 0;
1318*01a344a2SDavid du Colombier 	freewp(file->wpath);
1319*01a344a2SDavid du Colombier 	authfree(file->auth);
1320*01a344a2SDavid du Colombier 	freefp(file);
1321*01a344a2SDavid du Colombier 	qunlock(file);
1322*01a344a2SDavid du Colombier 
1323*01a344a2SDavid du Colombier 	return error;
1324*01a344a2SDavid du Colombier }
1325*01a344a2SDavid du Colombier 
1326*01a344a2SDavid du Colombier static int
1327*01a344a2SDavid du Colombier clunk(Chan* chan, Fcall* f, Fcall*)
1328*01a344a2SDavid du Colombier {
1329*01a344a2SDavid du Colombier 	File *file;
1330*01a344a2SDavid du Colombier 
1331*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
1332*01a344a2SDavid du Colombier 		return Efid;
1333*01a344a2SDavid du Colombier 
1334*01a344a2SDavid du Colombier 	_clunk(file, file->open & FREMOV, 0);
1335*01a344a2SDavid du Colombier 	return 0;
1336*01a344a2SDavid du Colombier }
1337*01a344a2SDavid du Colombier 
1338*01a344a2SDavid du Colombier static int
1339*01a344a2SDavid du Colombier fs_remove(Chan* chan, Fcall* f, Fcall*)
1340*01a344a2SDavid du Colombier {
1341*01a344a2SDavid du Colombier 	File *file;
1342*01a344a2SDavid du Colombier 
1343*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
1344*01a344a2SDavid du Colombier 		return Efid;
1345*01a344a2SDavid du Colombier 
1346*01a344a2SDavid du Colombier 	return _clunk(file, 1, chan == cons.chan);
1347*01a344a2SDavid du Colombier }
1348*01a344a2SDavid du Colombier 
1349*01a344a2SDavid du Colombier static int
1350*01a344a2SDavid du Colombier fs_stat(Chan* chan, Fcall* f, Fcall* r, uchar* data)
1351*01a344a2SDavid du Colombier {
1352*01a344a2SDavid du Colombier 	Dir dir;
1353*01a344a2SDavid du Colombier 	Iobuf *p;
1354*01a344a2SDavid du Colombier 	Dentry *d, dentry;
1355*01a344a2SDavid du Colombier 	File *file;
1356*01a344a2SDavid du Colombier 	int error, len;
1357*01a344a2SDavid du Colombier 
1358*01a344a2SDavid du Colombier 	error = 0;
1359*01a344a2SDavid du Colombier 	p = nil;
1360*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
1361*01a344a2SDavid du Colombier 		return Efid;
1362*01a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
1363*01a344a2SDavid du Colombier 		memset(&dentry, 0, sizeof dentry);
1364*01a344a2SDavid du Colombier 		d = &dentry;
1365*01a344a2SDavid du Colombier 		mkqid9p1(&d->qid, &file->qid);
1366*01a344a2SDavid du Colombier 		strcpy(d->name, "#¿");
1367*01a344a2SDavid du Colombier 		d->uid = authuid(file->auth);
1368*01a344a2SDavid du Colombier 		d->gid = d->uid;
1369*01a344a2SDavid du Colombier 		d->muid = d->uid;
1370*01a344a2SDavid du Colombier 		d->atime = time(nil);
1371*01a344a2SDavid du Colombier 		d->mtime = d->atime;
1372*01a344a2SDavid du Colombier 		d->size = 0;
1373*01a344a2SDavid du Colombier 	} else {
1374*01a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->addr, Brd);
1375*01a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
1376*01a344a2SDavid du Colombier 			error = Edir1;
1377*01a344a2SDavid du Colombier 			goto out;
1378*01a344a2SDavid du Colombier 		}
1379*01a344a2SDavid du Colombier 		d = getdir(p, file->slot);
1380*01a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
1381*01a344a2SDavid du Colombier 			error = Ealloc;
1382*01a344a2SDavid du Colombier 			goto out;
1383*01a344a2SDavid du Colombier 		}
1384*01a344a2SDavid du Colombier 		if(error = mkqidcmp(&file->qid, d))
1385*01a344a2SDavid du Colombier 			goto out;
1386*01a344a2SDavid du Colombier 
1387*01a344a2SDavid du Colombier 		if(d->qid.path == QPROOT)	/* stat of root gives time */
1388*01a344a2SDavid du Colombier 			d->atime = time(nil);
1389*01a344a2SDavid du Colombier 	}
1390*01a344a2SDavid du Colombier 	len = mkdir9p2(&dir, d, data);
1391*01a344a2SDavid du Colombier 	data += len;
1392*01a344a2SDavid du Colombier 
1393*01a344a2SDavid du Colombier 	if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0)
1394*01a344a2SDavid du Colombier 		error = Eedge;
1395*01a344a2SDavid du Colombier 	r->stat = data;
1396*01a344a2SDavid du Colombier 
1397*01a344a2SDavid du Colombier out:
1398*01a344a2SDavid du Colombier 	if(p != nil)
1399*01a344a2SDavid du Colombier 		putbuf(p);
1400*01a344a2SDavid du Colombier 	if(file != nil)
1401*01a344a2SDavid du Colombier 		qunlock(file);
1402*01a344a2SDavid du Colombier 
1403*01a344a2SDavid du Colombier 	return error;
1404*01a344a2SDavid du Colombier }
1405*01a344a2SDavid du Colombier 
1406*01a344a2SDavid du Colombier static int
1407*01a344a2SDavid du Colombier fs_wstat(Chan* chan, Fcall* f, Fcall*, char* strs)
1408*01a344a2SDavid du Colombier {
1409*01a344a2SDavid du Colombier 	Iobuf *p, *p1;
1410*01a344a2SDavid du Colombier 	Dentry *d, *d1;
1411*01a344a2SDavid du Colombier 	File *file;
1412*01a344a2SDavid du Colombier 	int error, err, gid, gl, muid, op, slot, tsync, uid;
1413*01a344a2SDavid du Colombier 	long addr;
1414*01a344a2SDavid du Colombier 	Dir dir;
1415*01a344a2SDavid du Colombier 
1416*01a344a2SDavid du Colombier 	if(convM2D(f->stat, f->nstat, &dir, strs) == 0)
1417*01a344a2SDavid du Colombier 		return Econvert;
1418*01a344a2SDavid du Colombier 
1419*01a344a2SDavid du Colombier 	/*
1420*01a344a2SDavid du Colombier 	 * Get the file.
1421*01a344a2SDavid du Colombier 	 * If user 'none' (uid == 0), can't do anything;
1422*01a344a2SDavid du Colombier 	 * if filesystem is read-only, can't change anything.
1423*01a344a2SDavid du Colombier 	 */
1424*01a344a2SDavid du Colombier 	if((file = filep(chan, f->fid, 0)) == nil)
1425*01a344a2SDavid du Colombier 		return Efid;
1426*01a344a2SDavid du Colombier 	p = p1 = nil;
1427*01a344a2SDavid du Colombier 	if(file->uid == 0){
1428*01a344a2SDavid du Colombier 		error = Eaccess;
1429*01a344a2SDavid du Colombier 		goto out;
1430*01a344a2SDavid du Colombier 	}
1431*01a344a2SDavid du Colombier 	if(file->fs->dev->type == Devro){
1432*01a344a2SDavid du Colombier 		error = Eronly;
1433*01a344a2SDavid du Colombier 		goto out;
1434*01a344a2SDavid du Colombier 	}
1435*01a344a2SDavid du Colombier 	if(file->qid.type & QTAUTH){
1436*01a344a2SDavid du Colombier 		error = Emode;
1437*01a344a2SDavid du Colombier 		goto out;
1438*01a344a2SDavid du Colombier 	}
1439*01a344a2SDavid du Colombier 
1440*01a344a2SDavid du Colombier 	/*
1441*01a344a2SDavid du Colombier 	 * Get the current entry and check it is still valid.
1442*01a344a2SDavid du Colombier 	 */
1443*01a344a2SDavid du Colombier 	p = getbuf(file->fs->dev, file->addr, Brd);
1444*01a344a2SDavid du Colombier 	if(p == nil || checktag(p, Tdir, QPNONE)){
1445*01a344a2SDavid du Colombier 		error = Ealloc;
1446*01a344a2SDavid du Colombier 		goto out;
1447*01a344a2SDavid du Colombier 	}
1448*01a344a2SDavid du Colombier 	d = getdir(p, file->slot);
1449*01a344a2SDavid du Colombier 	if(d == nil || !(d->mode & DALLOC)){
1450*01a344a2SDavid du Colombier 		error = Ealloc;
1451*01a344a2SDavid du Colombier 		goto out;
1452*01a344a2SDavid du Colombier 	}
1453*01a344a2SDavid du Colombier 	if(error = mkqidcmp(&file->qid, d))
1454*01a344a2SDavid du Colombier 		goto out;
1455*01a344a2SDavid du Colombier 
1456*01a344a2SDavid du Colombier 	/*
1457*01a344a2SDavid du Colombier 	 * Run through each of the (sub-)fields in the provided Dir
1458*01a344a2SDavid du Colombier 	 * checking for validity and whether it's a default:
1459*01a344a2SDavid du Colombier 	 * .type, .dev and .atime are completely ignored and not checked;
1460*01a344a2SDavid du Colombier 	 * .qid.path, .qid.vers and .muid are checked for validity but
1461*01a344a2SDavid du Colombier 	 * any attempt to change them is an error.
1462*01a344a2SDavid du Colombier 	 * .qid.type/.mode, .mtime, .name, .length, .uid and .gid can
1463*01a344a2SDavid du Colombier 	 * possibly be changed.
1464*01a344a2SDavid du Colombier 	 *
1465*01a344a2SDavid du Colombier 	 * 'Op' flags there are changed fields, i.e. it's not a no-op.
1466*01a344a2SDavid du Colombier 	 * 'Tsync' flags all fields are defaulted.
1467*01a344a2SDavid du Colombier 	 *
1468*01a344a2SDavid du Colombier 	 * Wstatallow and writeallow are set to allow changes during the
1469*01a344a2SDavid du Colombier 	 * fileserver bootstrap phase.
1470*01a344a2SDavid du Colombier 	 */
1471*01a344a2SDavid du Colombier 	tsync = 1;
1472*01a344a2SDavid du Colombier 	if(dir.qid.path != ~0){
1473*01a344a2SDavid du Colombier 		if(dir.qid.path != file->qid.path){
1474*01a344a2SDavid du Colombier 			error = Ewstatp;
1475*01a344a2SDavid du Colombier 			goto out;
1476*01a344a2SDavid du Colombier 		}
1477*01a344a2SDavid du Colombier 		tsync = 0;
1478*01a344a2SDavid du Colombier 	}
1479*01a344a2SDavid du Colombier 	if(dir.qid.vers != ~0){
1480*01a344a2SDavid du Colombier 		if(dir.qid.vers != file->qid.vers){
1481*01a344a2SDavid du Colombier 			error = Ewstatv;
1482*01a344a2SDavid du Colombier 			goto out;
1483*01a344a2SDavid du Colombier 		}
1484*01a344a2SDavid du Colombier 		tsync = 0;
1485*01a344a2SDavid du Colombier 	}
1486*01a344a2SDavid du Colombier 	if(dir.muid != nil && *dir.muid != '\0'){
1487*01a344a2SDavid du Colombier 		muid = strtouid(dir.muid);
1488*01a344a2SDavid du Colombier 		if(muid != d->muid && !wstatallow){
1489*01a344a2SDavid du Colombier 			error = Ewstatm;
1490*01a344a2SDavid du Colombier 			goto out;
1491*01a344a2SDavid du Colombier 		}
1492*01a344a2SDavid du Colombier 		tsync = 0;
1493*01a344a2SDavid du Colombier 	}
1494*01a344a2SDavid du Colombier 
1495*01a344a2SDavid du Colombier 	/*
1496*01a344a2SDavid du Colombier 	 * .qid.type and .mode have some bits in common. Only .mode
1497*01a344a2SDavid du Colombier 	 * is currently needed for comparisons with the old mode but
1498*01a344a2SDavid du Colombier 	 * if there are changes to the bits also encoded in .qid.type
1499*01a344a2SDavid du Colombier 	 * then file->qid must be updated appropriately later.
1500*01a344a2SDavid du Colombier 	 */
1501*01a344a2SDavid du Colombier 	if(dir.qid.type == (uchar)~0){
1502*01a344a2SDavid du Colombier 		if(dir.mode == ~0)
1503*01a344a2SDavid du Colombier 			dir.qid.type = mktype9p2(d->mode);
1504*01a344a2SDavid du Colombier 		else
1505*01a344a2SDavid du Colombier 			dir.qid.type = dir.mode>>24;
1506*01a344a2SDavid du Colombier 	} else
1507*01a344a2SDavid du Colombier 		tsync = 0;
1508*01a344a2SDavid du Colombier 	if(dir.mode == ~0)
1509*01a344a2SDavid du Colombier 		dir.mode = mkmode9p2(d->mode);
1510*01a344a2SDavid du Colombier 	else
1511*01a344a2SDavid du Colombier 		tsync = 0;
1512*01a344a2SDavid du Colombier 
1513*01a344a2SDavid du Colombier 	/*
1514*01a344a2SDavid du Colombier 	 * Check dir.qid.type and dir.mode agree, check for any unknown
1515*01a344a2SDavid du Colombier 	 * type/mode bits, check for an attempt to change the directory bit.
1516*01a344a2SDavid du Colombier 	 */
1517*01a344a2SDavid du Colombier 	if(dir.qid.type != ((dir.mode>>24) & 0xFF)){
1518*01a344a2SDavid du Colombier 		error = Ewstatq;
1519*01a344a2SDavid du Colombier 		goto out;
1520*01a344a2SDavid du Colombier 	}
1521*01a344a2SDavid du Colombier 	if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){
1522*01a344a2SDavid du Colombier 		error = Ewstatb;
1523*01a344a2SDavid du Colombier 		goto out;
1524*01a344a2SDavid du Colombier 	}
1525*01a344a2SDavid du Colombier 
1526*01a344a2SDavid du Colombier 	op = dir.mode^mkmode9p2(d->mode);
1527*01a344a2SDavid du Colombier 	if(op & DMDIR){
1528*01a344a2SDavid du Colombier 		error = Ewstatd;
1529*01a344a2SDavid du Colombier 		goto out;
1530*01a344a2SDavid du Colombier 	}
1531*01a344a2SDavid du Colombier 
1532*01a344a2SDavid du Colombier 	if(dir.mtime != ~0){
1533*01a344a2SDavid du Colombier 		if(dir.mtime != d->mtime)
1534*01a344a2SDavid du Colombier 			op = 1;
1535*01a344a2SDavid du Colombier 		tsync = 0;
1536*01a344a2SDavid du Colombier 	} else
1537*01a344a2SDavid du Colombier 		dir.mtime = d->mtime;
1538*01a344a2SDavid du Colombier 
1539*01a344a2SDavid du Colombier 	if(dir.length == ~(Off)0)
1540*01a344a2SDavid du Colombier 		dir.length = d->size;
1541*01a344a2SDavid du Colombier 	else {
1542*01a344a2SDavid du Colombier 		if (dir.length < 0) {
1543*01a344a2SDavid du Colombier 			error = Ewstatl;
1544*01a344a2SDavid du Colombier 			goto out;
1545*01a344a2SDavid du Colombier 		} else if(dir.length != d->size)
1546*01a344a2SDavid du Colombier 			op = 1;
1547*01a344a2SDavid du Colombier 		tsync = 0;
1548*01a344a2SDavid du Colombier 	}
1549*01a344a2SDavid du Colombier 
1550*01a344a2SDavid du Colombier 	/*
1551*01a344a2SDavid du Colombier 	 * Check for permission to change .mode, .mtime or .length,
1552*01a344a2SDavid du Colombier 	 * must be owner or leader of either group, for which test gid
1553*01a344a2SDavid du Colombier 	 * is needed; permission checks on gid will be done later.
1554*01a344a2SDavid du Colombier 	 * 'Gl' counts whether neither, one or both groups are led.
1555*01a344a2SDavid du Colombier 	 */
1556*01a344a2SDavid du Colombier 	if(dir.gid != nil && *dir.gid != '\0'){
1557*01a344a2SDavid du Colombier 		gid = strtouid(dir.gid);
1558*01a344a2SDavid du Colombier 		tsync = 0;
1559*01a344a2SDavid du Colombier 	} else
1560*01a344a2SDavid du Colombier 		gid = d->gid;
1561*01a344a2SDavid du Colombier 	gl = leadgroup(file->uid, gid) != 0;
1562*01a344a2SDavid du Colombier 	gl += leadgroup(file->uid, d->gid) != 0;
1563*01a344a2SDavid du Colombier 
1564*01a344a2SDavid du Colombier 	if(op && !wstatallow && d->uid != file->uid && !gl){
1565*01a344a2SDavid du Colombier 		error = Ewstato;
1566*01a344a2SDavid du Colombier 		goto out;
1567*01a344a2SDavid du Colombier 	}
1568*01a344a2SDavid du Colombier 
1569*01a344a2SDavid du Colombier 	/*
1570*01a344a2SDavid du Colombier 	 * Rename.
1571*01a344a2SDavid du Colombier 	 * Check .name is valid and different to the current.
1572*01a344a2SDavid du Colombier 	 */
1573*01a344a2SDavid du Colombier 	if(dir.name != nil && *dir.name != '\0'){
1574*01a344a2SDavid du Colombier 		if(error = checkname9p2(dir.name))
1575*01a344a2SDavid du Colombier 			goto out;
1576*01a344a2SDavid du Colombier 		if(strncmp(dir.name, d->name, NAMELEN))
1577*01a344a2SDavid du Colombier 			op = 1;
1578*01a344a2SDavid du Colombier 		else
1579*01a344a2SDavid du Colombier 			dir.name = d->name;
1580*01a344a2SDavid du Colombier 		tsync = 0;
1581*01a344a2SDavid du Colombier 	} else
1582*01a344a2SDavid du Colombier 		dir.name = d->name;
1583*01a344a2SDavid du Colombier 
1584*01a344a2SDavid du Colombier 	/*
1585*01a344a2SDavid du Colombier 	 * If the name is really to be changed check it's unique
1586*01a344a2SDavid du Colombier 	 * and there is write permission in the parent.
1587*01a344a2SDavid du Colombier 	 */
1588*01a344a2SDavid du Colombier 	if(dir.name != d->name){
1589*01a344a2SDavid du Colombier 		/*
1590*01a344a2SDavid du Colombier 		 * First get parent.
1591*01a344a2SDavid du Colombier 		 * Must drop current entry to prevent
1592*01a344a2SDavid du Colombier 		 * deadlock when searching that new name
1593*01a344a2SDavid du Colombier 		 * already exists below.
1594*01a344a2SDavid du Colombier 		 */
1595*01a344a2SDavid du Colombier 		putbuf(p);
1596*01a344a2SDavid du Colombier 		p = nil;
1597*01a344a2SDavid du Colombier 
1598*01a344a2SDavid du Colombier 		if(file->wpath == nil){
1599*01a344a2SDavid du Colombier 			error = Ephase;
1600*01a344a2SDavid du Colombier 			goto out;
1601*01a344a2SDavid du Colombier 		}
1602*01a344a2SDavid du Colombier 		p1 = getbuf(file->fs->dev, file->wpath->addr, Brd);
1603*01a344a2SDavid du Colombier 		if(p1 == nil || checktag(p1, Tdir, QPNONE)){
1604*01a344a2SDavid du Colombier 			error = Ephase;
1605*01a344a2SDavid du Colombier 			goto out;
1606*01a344a2SDavid du Colombier 		}
1607*01a344a2SDavid du Colombier 		d1 = getdir(p1, file->wpath->slot);
1608*01a344a2SDavid du Colombier 		if(d1 == nil || !(d1->mode & DALLOC)){
1609*01a344a2SDavid du Colombier 			error = Ephase;
1610*01a344a2SDavid du Colombier 			goto out;
1611*01a344a2SDavid du Colombier 		}
1612*01a344a2SDavid du Colombier 
1613*01a344a2SDavid du Colombier 		/*
1614*01a344a2SDavid du Colombier 		 * Check entries in parent for new name.
1615*01a344a2SDavid du Colombier 		 */
1616*01a344a2SDavid du Colombier 		for(addr = 0; ; addr++){
1617*01a344a2SDavid du Colombier 			if((p = dnodebuf(p1, d1, addr, 0, file->uid)) == nil)
1618*01a344a2SDavid du Colombier 				break;
1619*01a344a2SDavid du Colombier 			if(checktag(p, Tdir, d1->qid.path)){
1620*01a344a2SDavid du Colombier 				putbuf(p);
1621*01a344a2SDavid du Colombier 				continue;
1622*01a344a2SDavid du Colombier 			}
1623*01a344a2SDavid du Colombier 			for(slot = 0; slot < DIRPERBUF; slot++){
1624*01a344a2SDavid du Colombier 				d = getdir(p, slot);
1625*01a344a2SDavid du Colombier 				if(!(d->mode & DALLOC) ||
1626*01a344a2SDavid du Colombier 				   strncmp(dir.name, d->name, sizeof d->name))
1627*01a344a2SDavid du Colombier 					continue;
1628*01a344a2SDavid du Colombier 				error = Eexist;
1629*01a344a2SDavid du Colombier 				goto out;
1630*01a344a2SDavid du Colombier 			}
1631*01a344a2SDavid du Colombier 			putbuf(p);
1632*01a344a2SDavid du Colombier 		}
1633*01a344a2SDavid du Colombier 
1634*01a344a2SDavid du Colombier 		/*
1635*01a344a2SDavid du Colombier 		 * Reacquire entry and check it's still OK.
1636*01a344a2SDavid du Colombier 		 */
1637*01a344a2SDavid du Colombier 		p = getbuf(file->fs->dev, file->addr, Brd);
1638*01a344a2SDavid du Colombier 		if(p == nil || checktag(p, Tdir, QPNONE)){
1639*01a344a2SDavid du Colombier 			error = Ephase;
1640*01a344a2SDavid du Colombier 			goto out;
1641*01a344a2SDavid du Colombier 		}
1642*01a344a2SDavid du Colombier 		d = getdir(p, file->slot);
1643*01a344a2SDavid du Colombier 		if(d == nil || !(d->mode & DALLOC)){
1644*01a344a2SDavid du Colombier 			error = Ephase;
1645*01a344a2SDavid du Colombier 			goto out;
1646*01a344a2SDavid du Colombier 		}
1647*01a344a2SDavid du Colombier 
1648*01a344a2SDavid du Colombier 		/*
1649*01a344a2SDavid du Colombier 		 * Check write permission in the parent.
1650*01a344a2SDavid du Colombier 		 */
1651*01a344a2SDavid du Colombier 		if(!wstatallow && !writeallow && iaccess(file, d1, DWRITE)){
1652*01a344a2SDavid du Colombier 			error = Eaccess;
1653*01a344a2SDavid du Colombier 			goto out;
1654*01a344a2SDavid du Colombier 		}
1655*01a344a2SDavid du Colombier 	}
1656*01a344a2SDavid du Colombier 
1657*01a344a2SDavid du Colombier 	/*
1658*01a344a2SDavid du Colombier 	 * Check for permission to change owner - must be god.
1659*01a344a2SDavid du Colombier 	 */
1660*01a344a2SDavid du Colombier 	if(dir.uid != nil && *dir.uid != '\0'){
1661*01a344a2SDavid du Colombier 		uid = strtouid(dir.uid);
1662*01a344a2SDavid du Colombier 		if(uid != d->uid){
1663*01a344a2SDavid du Colombier 			if(!wstatallow){
1664*01a344a2SDavid du Colombier 				error = Ewstatu;
1665*01a344a2SDavid du Colombier 				goto out;
1666*01a344a2SDavid du Colombier 			}
1667*01a344a2SDavid du Colombier 			op = 1;
1668*01a344a2SDavid du Colombier 		}
1669*01a344a2SDavid du Colombier 		tsync = 0;
1670*01a344a2SDavid du Colombier 	} else
1671*01a344a2SDavid du Colombier 		uid = d->uid;
1672*01a344a2SDavid du Colombier 
1673*01a344a2SDavid du Colombier 	/*
1674*01a344a2SDavid du Colombier 	 * Check for permission to change group, must be
1675*01a344a2SDavid du Colombier 	 * either owner and in new group or leader of both groups.
1676*01a344a2SDavid du Colombier 	 */
1677*01a344a2SDavid du Colombier 	if(gid != d->gid){
1678*01a344a2SDavid du Colombier 		if(!(wstatallow || writeallow)
1679*01a344a2SDavid du Colombier 		&& !(d->uid == file->uid && ingroup(file->uid, gid))
1680*01a344a2SDavid du Colombier 		&& !(gl == 2)){
1681*01a344a2SDavid du Colombier 			error = Ewstatg;
1682*01a344a2SDavid du Colombier 			goto out;
1683*01a344a2SDavid du Colombier 		}
1684*01a344a2SDavid du Colombier 		op = 1;
1685*01a344a2SDavid du Colombier 	}
1686*01a344a2SDavid du Colombier 
1687*01a344a2SDavid du Colombier 	/*
1688*01a344a2SDavid du Colombier 	 * Checks all done, update if necessary.
1689*01a344a2SDavid du Colombier 	 */
1690*01a344a2SDavid du Colombier 	if(op){
1691*01a344a2SDavid du Colombier 		d->mode = mkmode9p1(dir.mode);
1692*01a344a2SDavid du Colombier 		file->qid.type = mktype9p2(d->mode);
1693*01a344a2SDavid du Colombier 		d->mtime = dir.mtime;
1694*01a344a2SDavid du Colombier 		if (dir.length < d->size) {
1695*01a344a2SDavid du Colombier 			err = dtrunclen(p, d, dir.length, uid);
1696*01a344a2SDavid du Colombier 			if (error == 0)
1697*01a344a2SDavid du Colombier 				error = err;
1698*01a344a2SDavid du Colombier 		}
1699*01a344a2SDavid du Colombier 		d->size = dir.length;
1700*01a344a2SDavid du Colombier 		if(dir.name != d->name)
1701*01a344a2SDavid du Colombier 			strncpy(d->name, dir.name, sizeof(d->name));
1702*01a344a2SDavid du Colombier 		d->uid = uid;
1703*01a344a2SDavid du Colombier 		d->gid = gid;
1704*01a344a2SDavid du Colombier 	}
1705*01a344a2SDavid du Colombier 	if(!tsync)
1706*01a344a2SDavid du Colombier 		accessdir(p, d, FREAD, file->uid);
1707*01a344a2SDavid du Colombier 
1708*01a344a2SDavid du Colombier out:
1709*01a344a2SDavid du Colombier 	if(p != nil)
1710*01a344a2SDavid du Colombier 		putbuf(p);
1711*01a344a2SDavid du Colombier 	if(p1 != nil)
1712*01a344a2SDavid du Colombier 		putbuf(p1);
1713*01a344a2SDavid du Colombier 	qunlock(file);
1714*01a344a2SDavid du Colombier 
1715*01a344a2SDavid du Colombier 	return error;
1716*01a344a2SDavid du Colombier }
1717*01a344a2SDavid du Colombier 
1718*01a344a2SDavid du Colombier int
1719*01a344a2SDavid du Colombier serve9p2(Msgbuf* mb)
1720*01a344a2SDavid du Colombier {
1721*01a344a2SDavid du Colombier 	Chan *chan;
1722*01a344a2SDavid du Colombier 	Fcall f, r;
1723*01a344a2SDavid du Colombier 	Msgbuf *data, *rmb;
1724*01a344a2SDavid du Colombier 	char ename[64];
1725*01a344a2SDavid du Colombier 	int error, n, type;
1726*01a344a2SDavid du Colombier 	static int once;
1727*01a344a2SDavid du Colombier 
1728*01a344a2SDavid du Colombier 	if(once == 0){
1729*01a344a2SDavid du Colombier 		fmtinstall('F', fcallfmt);
1730*01a344a2SDavid du Colombier 		once = 1;
1731*01a344a2SDavid du Colombier 	}
1732*01a344a2SDavid du Colombier 
1733*01a344a2SDavid du Colombier 	/*
1734*01a344a2SDavid du Colombier 	 * 0 return means i don't understand this message,
1735*01a344a2SDavid du Colombier 	 * 1 return means i dealt with it, including error
1736*01a344a2SDavid du Colombier 	 * replies.
1737*01a344a2SDavid du Colombier 	 */
1738*01a344a2SDavid du Colombier 	if(convM2S(mb->data, mb->count, &f) != mb->count)
1739*01a344a2SDavid du Colombier {
1740*01a344a2SDavid du Colombier print("didn't like %d byte message\n", mb->count);
1741*01a344a2SDavid du Colombier 		return 0;
1742*01a344a2SDavid du Colombier }
1743*01a344a2SDavid du Colombier 	type = f.type;
1744*01a344a2SDavid du Colombier 	if(type < Tversion || type >= Tmax || (type & 1) || type == Terror)
1745*01a344a2SDavid du Colombier 		return 0;
1746*01a344a2SDavid du Colombier 
1747*01a344a2SDavid du Colombier 	chan = mb->chan;
1748*01a344a2SDavid du Colombier 	if(CHAT(chan))
1749*01a344a2SDavid du Colombier 		print("9p2: f %F\n", &f);
1750*01a344a2SDavid du Colombier 	r.type = type+1;
1751*01a344a2SDavid du Colombier 	r.tag = f.tag;
1752*01a344a2SDavid du Colombier 	error = 0;
1753*01a344a2SDavid du Colombier 	data = nil;
1754*01a344a2SDavid du Colombier 
1755*01a344a2SDavid du Colombier 	switch(type){
1756*01a344a2SDavid du Colombier 	default:
1757*01a344a2SDavid du Colombier 		r.type = Rerror;
1758*01a344a2SDavid du Colombier 		snprint(ename, sizeof(ename), "unknown message: %F", &f);
1759*01a344a2SDavid du Colombier 		r.ename = ename;
1760*01a344a2SDavid du Colombier 		break;
1761*01a344a2SDavid du Colombier 	case Tversion:
1762*01a344a2SDavid du Colombier 		error = version(chan, &f, &r);
1763*01a344a2SDavid du Colombier 		break;
1764*01a344a2SDavid du Colombier 	case Tauth:
1765*01a344a2SDavid du Colombier 		error = auth(chan, &f, &r);
1766*01a344a2SDavid du Colombier 		break;
1767*01a344a2SDavid du Colombier 	case Tattach:
1768*01a344a2SDavid du Colombier 		error = attach(chan, &f, &r);
1769*01a344a2SDavid du Colombier 		break;
1770*01a344a2SDavid du Colombier 	case Tflush:
1771*01a344a2SDavid du Colombier 		error = flush(chan, &f, &r);
1772*01a344a2SDavid du Colombier 		break;
1773*01a344a2SDavid du Colombier 	case Twalk:
1774*01a344a2SDavid du Colombier 		error = walk(chan, &f, &r);
1775*01a344a2SDavid du Colombier 		break;
1776*01a344a2SDavid du Colombier 	case Topen:
1777*01a344a2SDavid du Colombier 		error = fs_open(chan, &f, &r);
1778*01a344a2SDavid du Colombier 		break;
1779*01a344a2SDavid du Colombier 	case Tcreate:
1780*01a344a2SDavid du Colombier 		error = fs_create(chan, &f, &r);
1781*01a344a2SDavid du Colombier 		break;
1782*01a344a2SDavid du Colombier 	case Tread:
1783*01a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
1784*01a344a2SDavid du Colombier 		error = fs_read(chan, &f, &r, data->data);
1785*01a344a2SDavid du Colombier 		break;
1786*01a344a2SDavid du Colombier 	case Twrite:
1787*01a344a2SDavid du Colombier 		error = fs_write(chan, &f, &r);
1788*01a344a2SDavid du Colombier 		break;
1789*01a344a2SDavid du Colombier 	case Tclunk:
1790*01a344a2SDavid du Colombier 		error = clunk(chan, &f, &r);
1791*01a344a2SDavid du Colombier 		break;
1792*01a344a2SDavid du Colombier 	case Tremove:
1793*01a344a2SDavid du Colombier 		error = fs_remove(chan, &f, &r);
1794*01a344a2SDavid du Colombier 		break;
1795*01a344a2SDavid du Colombier 	case Tstat:
1796*01a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
1797*01a344a2SDavid du Colombier 		error = fs_stat(chan, &f, &r, data->data);
1798*01a344a2SDavid du Colombier 		break;
1799*01a344a2SDavid du Colombier 	case Twstat:
1800*01a344a2SDavid du Colombier 		data = mballoc(chan->msize, chan, Mbreply1);
1801*01a344a2SDavid du Colombier 		error = fs_wstat(chan, &f, &r, (char*)data->data);
1802*01a344a2SDavid du Colombier 		break;
1803*01a344a2SDavid du Colombier 	}
1804*01a344a2SDavid du Colombier 
1805*01a344a2SDavid du Colombier 	if(error != 0){
1806*01a344a2SDavid du Colombier 		r.type = Rerror;
1807*01a344a2SDavid du Colombier 		if(error >= MAXERR){
1808*01a344a2SDavid du Colombier 			snprint(ename, sizeof(ename), "error %d", error);
1809*01a344a2SDavid du Colombier 			r.ename = ename;
1810*01a344a2SDavid du Colombier 		} else
1811*01a344a2SDavid du Colombier 			r.ename = errstr9p[error];
1812*01a344a2SDavid du Colombier 	}
1813*01a344a2SDavid du Colombier 	if(CHAT(chan))
1814*01a344a2SDavid du Colombier 		print("9p2: r %F\n", &r);
1815*01a344a2SDavid du Colombier 
1816*01a344a2SDavid du Colombier 	rmb = mballoc(chan->msize, chan, Mbreply2);
1817*01a344a2SDavid du Colombier 	n = convS2M(&r, rmb->data, chan->msize);
1818*01a344a2SDavid du Colombier 	if(data != nil)
1819*01a344a2SDavid du Colombier 		mbfree(data);
1820*01a344a2SDavid du Colombier 	if(n == 0){
1821*01a344a2SDavid du Colombier 		type = r.type;
1822*01a344a2SDavid du Colombier 		r.type = Rerror;
1823*01a344a2SDavid du Colombier 
1824*01a344a2SDavid du Colombier 		/*
1825*01a344a2SDavid du Colombier 		 * If a Tversion has not been seen on the chan then
1826*01a344a2SDavid du Colombier 		 * chan->msize will be 0. In that case craft a special
1827*01a344a2SDavid du Colombier 		 * Rerror message. It's fortunate that the mballoc above
1828*01a344a2SDavid du Colombier 		 * for rmb will have returned a Msgbuf of MAXMSG size
1829*01a344a2SDavid du Colombier 		 * when given a request with count of 0...
1830*01a344a2SDavid du Colombier 		 */
1831*01a344a2SDavid du Colombier 		if(chan->msize == 0){
1832*01a344a2SDavid du Colombier 			r.ename = "Tversion not seen";
1833*01a344a2SDavid du Colombier 			n = convS2M(&r, rmb->data, MAXMSG);
1834*01a344a2SDavid du Colombier 		} else {
1835*01a344a2SDavid du Colombier 			snprint(ename, sizeof(ename), "9p2: convS2M: type %d",
1836*01a344a2SDavid du Colombier 				type);
1837*01a344a2SDavid du Colombier 			r.ename = ename;
1838*01a344a2SDavid du Colombier 			n = convS2M(&r, rmb->data, chan->msize);
1839*01a344a2SDavid du Colombier 		}
1840*01a344a2SDavid du Colombier 		print("%s\n", r.ename);
1841*01a344a2SDavid du Colombier 		if(n == 0){
1842*01a344a2SDavid du Colombier 			/*
1843*01a344a2SDavid du Colombier 			 * What to do here, the failure notification failed?
1844*01a344a2SDavid du Colombier 			 */
1845*01a344a2SDavid du Colombier 			mbfree(rmb);
1846*01a344a2SDavid du Colombier 			return 1;
1847*01a344a2SDavid du Colombier 		}
1848*01a344a2SDavid du Colombier 	}
1849*01a344a2SDavid du Colombier 	rmb->count = n;
1850*01a344a2SDavid du Colombier 	rmb->param = mb->param;
1851*01a344a2SDavid du Colombier 
1852*01a344a2SDavid du Colombier 	/* done 9P processing, write reply to network */
1853*01a344a2SDavid du Colombier 	fs_send(chan->reply, rmb);
1854*01a344a2SDavid du Colombier 
1855*01a344a2SDavid du Colombier 	return 1;
1856*01a344a2SDavid du Colombier }
1857