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