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