xref: /plan9/sys/src/cmd/cwfs/9p1.c (revision 223a035810d484657ee1a815f68faa8211009d6d)
1 #include "all.h"
2 #include "9p1.h"
3 
4 extern Nvrsafe	nvr;
5 
6 typedef struct {
7 	uchar	chal[CHALLEN];		/* locally generated challenge */
8 	uchar	rchal[CHALLEN];		/* remotely generated challenge */
9 	Lock	idlock;
10 	ulong	idoffset;		/* offset of id vector */
11 	ulong	idvec;			/* vector of acceptable id's */
12 } Authinfo;
13 
14 static void
f_nop(Chan * cp,Fcall *,Fcall *)15 f_nop(Chan *cp, Fcall*, Fcall*)
16 {
17 	if(CHAT(cp))
18 		print("c_nop %d\n", cp->chan);
19 }
20 
21 static void
f_flush(Chan * cp,Fcall *,Fcall *)22 f_flush(Chan *cp, Fcall*, Fcall*)
23 {
24 	if(CHAT(cp))
25 		print("c_flush %d\n", cp->chan);
26 	runlock(&cp->reflock);
27 	wlock(&cp->reflock);
28 	wunlock(&cp->reflock);
29 	rlock(&cp->reflock);
30 }
31 
32 /*
33  *  create a challenge for a fid space
34  */
35 static void
mkchallenge(Authinfo * aip)36 mkchallenge(Authinfo *aip)
37 {
38 	int i;
39 
40 	srand((uintptr)aip + time(nil));
41 	for(i = 0; i < CHALLEN; i++)
42 		aip->chal[i] = nrand(256);
43 
44 	aip->idoffset = 0;
45 	aip->idvec = 0;
46 }
47 
48 static void
f_session(Chan * cp,Fcall * in,Fcall * ou)49 f_session(Chan *cp, Fcall *in, Fcall *ou)
50 {
51 	Authinfo *aip;
52 
53 	aip = (Authinfo*)cp->authinfo;
54 
55 	if(CHAT(cp))
56 		print("c_session %d\n", cp->chan);
57 	memmove(aip->rchal, in->chal, sizeof(aip->rchal));
58 	mkchallenge(aip);
59 	memmove(ou->chal, aip->chal, sizeof(ou->chal));
60 	if(noauth || wstatallow)
61 		memset(ou->authid, 0, sizeof(ou->authid));
62 	else
63 		memmove(ou->authid, nvr.authid, sizeof(ou->authid));
64 
65 	sprint(ou->authdom, "%s.%s", service, nvr.authdom);
66 	fileinit(cp);
67 }
68 
69 /*
70  *  match a challenge from an attach
71  */
72 static int
authorize(Chan * cp,Fcall * in,Fcall * ou)73 authorize(Chan *cp, Fcall *in, Fcall *ou)
74 {
75 	Ticket t;
76 	Authenticator a;
77 	int x;
78 	ulong bit;
79 	Authinfo *aip;
80 
81 	if(noauth || wstatallow)	/* set to allow entry during boot */
82 		return 1;
83 
84 	if(strcmp(in->uname, "none") == 0)
85 		return 1;
86 
87 	if(in->type == Toattach)
88 		return 0;
89 
90 	/* decrypt and unpack ticket */
91 	convM2T9p1(in->ticket, &t, nvr.machkey);
92 	if(t.num != AuthTs){
93 		print("9p1: bad AuthTs num\n");
94 		return 0;
95 	}
96 
97 	/* decrypt and unpack authenticator */
98 	convM2A9p1(in->auth, &a, t.key);
99 	if(a.num != AuthAc){
100 		print("9p1: bad AuthAc num\n");
101 		return 0;
102 	}
103 
104 	/* challenges must match */
105 	aip = (Authinfo*)cp->authinfo;
106 	if(memcmp(a.chal, aip->chal, sizeof(a.chal)) != 0){
107 		print("9p1: bad challenge\n");
108 		return 0;
109 	}
110 
111 	/*
112 	 *  the id must be in a valid range.  the range is specified by a
113 	 *  lower bound (idoffset) and a bit vector (idvec) where a
114 	 *  bit set to 1 means unusable
115 	 */
116 	lock(&aip->idlock);
117 	x = a.id - aip->idoffset;
118 	bit = 1<<x;
119 	if(x < 0 || x > 31 || (bit&aip->idvec)){
120 		unlock(&aip->idlock);
121 		print("9p1: id out of range: idoff %ld idvec %lux id %ld\n",
122 		   aip->idoffset, aip->idvec, a.id);
123 		return 0;
124 	}
125 	aip->idvec |= bit;
126 
127 	/* normalize the vector */
128 	while(aip->idvec&0xffff0001){
129 		aip->idvec >>= 1;
130 		aip->idoffset++;
131 	}
132 	unlock(&aip->idlock);
133 
134 	/* ticket name and attach name must match */
135 	if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){
136 		print("9p1: names don't match\n");
137 		return 0;
138 	}
139 
140 	/* copy translated name into input record */
141 	memmove(in->uname, t.suid, sizeof(in->uname));
142 
143 	/* craft a reply */
144 	a.num = AuthAs;
145 	memmove(a.chal, aip->rchal, CHALLEN);
146 	convA2M9p1(&a, ou->rauth, t.key);
147 
148 	return 1;
149 }
150 
151 /*
152  * buggery to give false qid for
153  * the top 2 levels of the dump fs
154  */
155 void
mkqid(Qid * qid,Dentry * d,int buggery)156 mkqid(Qid* qid, Dentry *d, int buggery)
157 {
158 	int c;
159 
160 	if(buggery && d->qid.path == (QPDIR|QPROOT)){
161 		c = d->name[0];
162 		if(isascii(c) && isdigit(c)){
163 			qid->path = 3;
164 			qid->vers = d->qid.version;
165 			qid->type = QTDIR;
166 
167 			c = (c-'0')*10 + (d->name[1]-'0');
168 			if(c >= 1 && c <= 12)
169 				qid->path = 4;
170 			return;
171 		}
172 	}
173 
174 	mkqid9p2(qid, &d->qid, d->mode);
175 }
176 
177 int
mkqidcmp(Qid * qid,Dentry * d)178 mkqidcmp(Qid* qid, Dentry *d)
179 {
180 	Qid tmp;
181 
182 	mkqid(&tmp, d, 1);
183 	if(qid->path == tmp.path && qid->type == tmp.type)
184 		return 0;
185 	return Eqid;
186 }
187 
188 static void
f_attach(Chan * cp,Fcall * in,Fcall * ou)189 f_attach(Chan *cp, Fcall *in, Fcall *ou)
190 {
191 	Iobuf *p;
192 	Dentry *d;
193 	File *f;
194 	int u;
195 	Filsys *fs;
196 	Off raddr;
197 
198 	if(CHAT(cp)) {
199 		print("c_attach %d\n", cp->chan);
200 		print("\tfid = %d\n", in->fid);
201 		print("\tuid = %s\n", in->uname);
202 		print("\targ = %s\n", in->aname);
203 	}
204 
205 	ou->qid = QID9P1(0,0);
206 	ou->fid = in->fid;
207 	if(!in->aname[0])	/* default */
208 		strncpy(in->aname, "main", sizeof(in->aname));
209 	p = 0;
210 	f = filep(cp, in->fid, 1);
211 	if(!f) {
212 		ou->err = Efid;
213 		goto out;
214 	}
215 
216 	u = -1;
217 	if(cp != cons.chan) {
218 		if(noattach && strcmp(in->uname, "none")) {
219 			ou->err = Enoattach;
220 			goto out;
221 		}
222 		if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0) {
223 			ou->err = Eauth;
224 			goto out;
225 		}
226 		u = strtouid(in->uname);
227 		if(u < 0) {
228 			ou->err = Ebadu;
229 			goto out;
230 		}
231 	}
232 	f->uid = u;
233 
234 	fs = fsstr(in->aname);
235 	if(fs == 0) {
236 		ou->err = Ebadspc;
237 		goto out;
238 	}
239 	raddr = getraddr(fs->dev);
240 	p = getbuf(fs->dev, raddr, Brd);
241 	d = getdir(p, 0);
242 	if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) {
243 		ou->err = Ealloc;
244 		goto out;
245 	}
246 	if (iaccess(f, d, DEXEC) ||
247 	    f->uid == 0 && fs->dev->type == Devro) {
248 		/*
249 		 * 'none' not allowed on dump
250 		 */
251 		ou->err = Eaccess;
252 		goto out;
253 	}
254 	accessdir(p, d, FREAD, f->uid);
255 	mkqid(&f->qid, d, 1);
256 	f->fs = fs;
257 	f->addr = raddr;
258 	f->slot = 0;
259 	f->open = 0;
260 	freewp(f->wpath);
261 	f->wpath = 0;
262 
263 	mkqid9p1(&ou->qid, &f->qid);
264 
265 	strncpy(cp->whoname, in->uname, sizeof(cp->whoname));
266 	cp->whotime = time(nil);
267 	if(cons.flags & attachflag)
268 		print("9p1: attach %s %T to \"%s\" C%d\n",
269 			cp->whoname, cp->whotime, fs->name, cp->chan);
270 
271 out:
272 	if((cons.flags & attachflag) && ou->err)
273 		print("9p1: attach %s %T SUCK EGGS --- %s\n",
274 			in->uname, time(nil), errstr9p[ou->err]);
275 	if(p)
276 		putbuf(p);
277 	if(f) {
278 		qunlock(f);
279 		if(ou->err)
280 			freefp(f);
281 	}
282 }
283 
284 static void
f_clone(Chan * cp,Fcall * in,Fcall * ou)285 f_clone(Chan *cp, Fcall *in, Fcall *ou)
286 {
287 	File *f1, *f2;
288 	Wpath *p;
289 	int fid, fid1;
290 
291 	if(CHAT(cp)) {
292 		print("c_clone %d\n", cp->chan);
293 		print("\told fid = %d\n", in->fid);
294 		print("\tnew fid = %d\n", in->newfid);
295 	}
296 
297 	fid = in->fid;
298 	fid1 = in->newfid;
299 
300 	f1 = 0;
301 	f2 = 0;
302 	if(fid < fid1) {
303 		f1 = filep(cp, fid, 0);
304 		f2 = filep(cp, fid1, 1);
305 	} else
306 	if(fid1 < fid) {
307 		f2 = filep(cp, fid1, 1);
308 		f1 = filep(cp, fid, 0);
309 	}
310 	if(!f1 || !f2) {
311 		ou->err = Efid;
312 		goto out;
313 	}
314 
315 
316 	f2->fs = f1->fs;
317 	f2->addr = f1->addr;
318 	f2->open = f1->open & ~FREMOV;
319 	f2->uid = f1->uid;
320 	f2->slot = f1->slot;
321 	f2->qid = f1->qid;
322 
323 	freewp(f2->wpath);
324 	lock(&wpathlock);
325 	f2->wpath = f1->wpath;
326 	for(p = f2->wpath; p; p = p->up)
327 		p->refs++;
328 	unlock(&wpathlock);
329 
330 out:
331 	ou->fid = fid;
332 	if(f1)
333 		qunlock(f1);
334 	if(f2) {
335 		qunlock(f2);
336 		if(ou->err)
337 			freefp(f2);
338 	}
339 }
340 
341 static void
f_walk(Chan * cp,Fcall * in,Fcall * ou)342 f_walk(Chan *cp, Fcall *in, Fcall *ou)
343 {
344 	Iobuf *p, *p1;
345 	Dentry *d, *d1;
346 	File *f;
347 	Wpath *w;
348 	int slot;
349 	Off addr, qpath;
350 
351 	if(CHAT(cp)) {
352 		print("c_walk %d\n", cp->chan);
353 		print("\tfid = %d\n", in->fid);
354 		print("\tname = %s\n", in->name);
355 	}
356 
357 	ou->fid = in->fid;
358 	ou->qid = QID9P1(0,0);
359 	p = 0;
360 	f = filep(cp, in->fid, 0);
361 	if(!f) {
362 		ou->err = Efid;
363 		goto out;
364 	}
365 	p = getbuf(f->fs->dev, f->addr, Brd);
366 	d = getdir(p, f->slot);
367 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
368 		ou->err = Ealloc;
369 		goto out;
370 	}
371 	if(!(d->mode & DDIR)) {
372 		ou->err = Edir1;
373 		goto out;
374 	}
375 	if(ou->err = mkqidcmp(&f->qid, d))
376 		goto out;
377 	if(cp != cons.chan && iaccess(f, d, DEXEC)) {
378 		ou->err = Eaccess;
379 		goto out;
380 	}
381 	accessdir(p, d, FREAD, f->uid);
382 	if(strcmp(in->name, ".") == 0)
383 		goto setdot;
384 	if(strcmp(in->name, "..") == 0) {
385 		if(f->wpath == 0)
386 			goto setdot;
387 		putbuf(p);
388 		p = 0;
389 		addr = f->wpath->addr;
390 		slot = f->wpath->slot;
391 		p1 = getbuf(f->fs->dev, addr, Brd);
392 		d1 = getdir(p1, slot);
393 		if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
394 			if(p1)
395 				putbuf(p1);
396 			ou->err = Ephase;
397 			goto out;
398 		}
399 		lock(&wpathlock);
400 		f->wpath->refs--;
401 		f->wpath = f->wpath->up;
402 		unlock(&wpathlock);
403 		goto found;
404 	}
405 	for(addr=0;; addr++) {
406 		if(p == 0) {
407 			p = getbuf(f->fs->dev, f->addr, Brd);
408 			d = getdir(p, f->slot);
409 			if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
410 				ou->err = Ealloc;
411 				goto out;
412 			}
413 		}
414 		qpath = d->qid.path;
415 		p1 = dnodebuf1(p, d, addr, 0, f->uid);
416 		p = 0;
417 		if(!p1 || checktag(p1, Tdir, qpath) ) {
418 			if(p1)
419 				putbuf(p1);
420 			ou->err = Eentry;
421 			goto out;
422 		}
423 		for(slot=0; slot<DIRPERBUF; slot++) {
424 			d1 = getdir(p1, slot);
425 			if(!(d1->mode & DALLOC))
426 				continue;
427 			if(strncmp(in->name, d1->name, sizeof(in->name)) != 0)
428 				continue;
429 			/*
430 			 * update walk path
431 			 */
432 			w = newwp();
433 			if(!w) {
434 				ou->err = Ewalk;
435 				putbuf(p1);
436 				goto out;
437 			}
438 			w->addr = f->addr;
439 			w->slot = f->slot;
440 			w->up = f->wpath;
441 			f->wpath = w;
442 			slot += DIRPERBUF*addr;
443 			goto found;
444 		}
445 		putbuf(p1);
446 	}
447 
448 found:
449 	f->addr = p1->addr;
450 	mkqid(&f->qid, d1, 1);
451 	putbuf(p1);
452 	f->slot = slot;
453 
454 setdot:
455 	mkqid9p1(&ou->qid, &f->qid);
456 	f->open = 0;
457 
458 out:
459 	if(p)
460 		putbuf(p);
461 	if(f)
462 		qunlock(f);
463 }
464 
465 static void
f_open(Chan * cp,Fcall * in,Fcall * ou)466 f_open(Chan *cp, Fcall *in, Fcall *ou)
467 {
468 	Iobuf *p;
469 	Dentry *d;
470 	File *f;
471 	Tlock *t;
472 	Qid qid;
473 	int ro, fmod, wok;
474 
475 	if(CHAT(cp)) {
476 		print("c_open %d\n", cp->chan);
477 		print("\tfid = %d\n", in->fid);
478 		print("\tmode = %o\n", in->mode);
479 	}
480 
481 	wok = 0;
482 	if(cp == cons.chan || writeallow)
483 		wok = 1;
484 
485 	p = 0;
486 	f = filep(cp, in->fid, 0);
487 	if(!f) {
488 		ou->err = Efid;
489 		goto out;
490 	}
491 
492 	/*
493 	 * if remove on close, check access here
494 	 */
495 	ro = f->fs->dev->type == Devro;
496 	if(in->mode & ORCLOSE) {
497 		if(ro) {
498 			ou->err = Eronly;
499 			goto out;
500 		}
501 		/*
502 		 * check on parent directory of file to be deleted
503 		 */
504 		if(f->wpath == 0 || f->wpath->addr == f->addr) {
505 			ou->err = Ephase;
506 			goto out;
507 		}
508 		p = getbuf(f->fs->dev, f->wpath->addr, Brd);
509 		d = getdir(p, f->wpath->slot);
510 		if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
511 			ou->err = Ephase;
512 			goto out;
513 		}
514 		if(iaccess(f, d, DWRITE)) {
515 			ou->err = Eaccess;
516 			goto out;
517 		}
518 		putbuf(p);
519 	}
520 	p = getbuf(f->fs->dev, f->addr, Brd);
521 	d = getdir(p, f->slot);
522 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
523 		ou->err = Ealloc;
524 		goto out;
525 	}
526 	if(ou->err = mkqidcmp(&f->qid, d))
527 		goto out;
528 	mkqid(&qid, d, 1);
529 	switch(in->mode & 7) {
530 
531 	case OREAD:
532 		if(iaccess(f, d, DREAD) && !wok)
533 			goto badaccess;
534 		fmod = FREAD;
535 		break;
536 
537 	case OWRITE:
538 		if((d->mode & DDIR) ||
539 		   (iaccess(f, d, DWRITE) && !wok))
540 			goto badaccess;
541 		if(ro) {
542 			ou->err = Eronly;
543 			goto out;
544 		}
545 		fmod = FWRITE;
546 		break;
547 
548 	case ORDWR:
549 		if((d->mode & DDIR) ||
550 		   (iaccess(f, d, DREAD) && !wok) ||
551 		   (iaccess(f, d, DWRITE) && !wok))
552 			goto badaccess;
553 		if(ro) {
554 			ou->err = Eronly;
555 			goto out;
556 		}
557 		fmod = FREAD+FWRITE;
558 		break;
559 
560 	case OEXEC:
561 		if((d->mode & DDIR) ||
562 		   (iaccess(f, d, DEXEC) && !wok))
563 			goto badaccess;
564 		fmod = FREAD;
565 		break;
566 
567 	default:
568 		ou->err = Emode;
569 		goto out;
570 	}
571 	if(in->mode & OTRUNC) {
572 		if((d->mode & DDIR) ||
573 		   (iaccess(f, d, DWRITE) && !wok))
574 			goto badaccess;
575 		if(ro) {
576 			ou->err = Eronly;
577 			goto out;
578 		}
579 	}
580 	t = 0;
581 	if(d->mode & DLOCK) {
582 		t = tlocked(p, d);
583 		if(t == nil) {
584 			ou->err = Elocked;
585 			goto out;
586 		}
587 	}
588 	if(in->mode & ORCLOSE)
589 		fmod |= FREMOV;
590 	f->open = fmod;
591 	if(in->mode & OTRUNC)
592 		if(!(d->mode & DAPND)) {
593 			dtrunc(p, d, f->uid);
594 			qid.vers = d->qid.version;
595 		}
596 	f->tlock = t;
597 	if(t)
598 		t->file = f;
599 	f->lastra = 1;
600 	mkqid9p1(&ou->qid, &qid);
601 	goto out;
602 
603 badaccess:
604 	ou->err = Eaccess;
605 	f->open = 0;
606 
607 out:
608 	if(p)
609 		putbuf(p);
610 	if(f)
611 		qunlock(f);
612 	ou->fid = in->fid;
613 }
614 
615 static void
f_create(Chan * cp,Fcall * in,Fcall * ou)616 f_create(Chan *cp, Fcall *in, Fcall *ou)
617 {
618 	Iobuf *p, *p1;
619 	Dentry *d, *d1;
620 	File *f;
621 	int slot, slot1, fmod, wok;
622 	Off addr, addr1, path;
623 	Qid qid;
624 	Tlock *t;
625 	Wpath *w;
626 
627 	if(CHAT(cp)) {
628 		print("c_create %d\n", cp->chan);
629 		print("\tfid = %d\n", in->fid);
630 		print("\tname = %s\n", in->name);
631 		print("\tperm = %lx+%lo\n", (in->perm>>28)&0xf,
632 				in->perm&0777);
633 		print("\tmode = %o\n", in->mode);
634 	}
635 
636 	wok = 0;
637 	if(cp == cons.chan || writeallow)
638 		wok = 1;
639 
640 	p = 0;
641 	f = filep(cp, in->fid, 0);
642 	if(!f) {
643 		ou->err = Efid;
644 		goto out;
645 	}
646 	if(f->fs->dev->type == Devro) {
647 		ou->err = Eronly;
648 		goto out;
649 	}
650 
651 	p = getbuf(f->fs->dev, f->addr, Brd);
652 	d = getdir(p, f->slot);
653 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
654 		ou->err = Ealloc;
655 		goto out;
656 	}
657 	if(ou->err = mkqidcmp(&f->qid, d))
658 		goto out;
659 	if(!(d->mode & DDIR)) {
660 		ou->err = Edir2;
661 		goto out;
662 	}
663 	if(iaccess(f, d, DWRITE) && !wok) {
664 		ou->err = Eaccess;
665 		goto out;
666 	}
667 	accessdir(p, d, FREAD, f->uid);
668 	if(!strncmp(in->name, ".", sizeof(in->name)) ||
669 	   !strncmp(in->name, "..", sizeof(in->name))) {
670 		ou->err = Edot;
671 		goto out;
672 	}
673 	if(checkname(in->name)) {
674 		ou->err = Ename;
675 		goto out;
676 	}
677 	addr1 = 0;
678 	slot1 = 0;	/* set */
679 	for(addr=0;; addr++) {
680 		p1 = dnodebuf(p, d, addr, 0, f->uid);
681 		if(!p1) {
682 			if(addr1)
683 				break;
684 			p1 = dnodebuf(p, d, addr, Tdir, f->uid);
685 		}
686 		if(p1 == 0) {
687 			ou->err = Efull;
688 			goto out;
689 		}
690 		if(checktag(p1, Tdir, d->qid.path)) {
691 			putbuf(p1);
692 			goto phase;
693 		}
694 		for(slot=0; slot<DIRPERBUF; slot++) {
695 			d1 = getdir(p1, slot);
696 			if(!(d1->mode & DALLOC)) {
697 				if(!addr1) {
698 					addr1 = p1->addr;
699 					slot1 = slot + addr*DIRPERBUF;
700 				}
701 				continue;
702 			}
703 			if(!strncmp(in->name, d1->name, sizeof(in->name))) {
704 				putbuf(p1);
705 				ou->err = Eexist;
706 				goto out;
707 			}
708 		}
709 		putbuf(p1);
710 	}
711 	switch(in->mode & 7) {
712 	case OEXEC:
713 	case OREAD:		/* seems only useful to make directories */
714 		fmod = FREAD;
715 		break;
716 
717 	case OWRITE:
718 		fmod = FWRITE;
719 		break;
720 
721 	case ORDWR:
722 		fmod = FREAD+FWRITE;
723 		break;
724 
725 	default:
726 		ou->err = Emode;
727 		goto out;
728 	}
729 	if(in->perm & PDIR)
730 		if((in->mode & OTRUNC) || (in->perm & PAPND) || (fmod & FWRITE))
731 			goto badaccess;
732 	/*
733 	 * do it
734 	 */
735 	path = qidpathgen(f->fs->dev);
736 	p1 = getbuf(f->fs->dev, addr1, Brd|Bimm|Bmod);
737 	d1 = getdir(p1, slot1);
738 	if(!d1 || checktag(p1, Tdir, d->qid.path)) {
739 		if(p1)
740 			putbuf(p1);
741 		goto phase;
742 	}
743 	if(d1->mode & DALLOC) {
744 		putbuf(p1);
745 		goto phase;
746 	}
747 
748 	strncpy(d1->name, in->name, sizeof(in->name));
749 	if(cp == cons.chan) {
750 		d1->uid = cons.uid;
751 		d1->gid = cons.gid;
752 	} else {
753 		d1->uid = f->uid;
754 		d1->gid = d->gid;
755 		in->perm &= d->mode | ~0666;
756 		if(in->perm & PDIR)
757 			in->perm &= d->mode | ~0777;
758 	}
759 	d1->qid.path = path;
760 	d1->qid.version = 0;
761 	d1->mode = DALLOC | (in->perm & 0777);
762 	if(in->perm & PDIR) {
763 		d1->mode |= DDIR;
764 		d1->qid.path |= QPDIR;
765 	}
766 	if(in->perm & PAPND)
767 		d1->mode |= DAPND;
768 	t = 0;
769 	if(in->perm & PLOCK) {
770 		d1->mode |= DLOCK;
771 		t = tlocked(p1, d1);
772 		/* if nil, out of tlock structures */
773 	}
774 	accessdir(p1, d1, FWRITE, f->uid);
775 	mkqid(&qid, d1, 0);
776 	putbuf(p1);
777 	accessdir(p, d, FWRITE, f->uid);
778 
779 	/*
780 	 * do a walk to new directory entry
781 	 */
782 	w = newwp();
783 	if(!w) {
784 		ou->err = Ewalk;
785 		goto out;
786 	}
787 	w->addr = f->addr;
788 	w->slot = f->slot;
789 	w->up = f->wpath;
790 	f->wpath = w;
791 	f->qid = qid;
792 	f->tlock = t;
793 	if(t)
794 		t->file = f;
795 	f->lastra = 1;
796 	if(in->mode & ORCLOSE)
797 		fmod |= FREMOV;
798 	f->open = fmod;
799 	f->addr = addr1;
800 	f->slot = slot1;
801 	mkqid9p1(&ou->qid, &qid);
802 	goto out;
803 
804 badaccess:
805 	ou->err = Eaccess;
806 	goto out;
807 
808 phase:
809 	ou->err = Ephase;
810 
811 out:
812 	if(p)
813 		putbuf(p);
814 	if(f)
815 		qunlock(f);
816 	ou->fid = in->fid;
817 }
818 
819 static void
f_read(Chan * cp,Fcall * in,Fcall * ou)820 f_read(Chan *cp, Fcall *in, Fcall *ou)
821 {
822 	Iobuf *p, *p1;
823 	File *f;
824 	Dentry *d, *d1;
825 	Tlock *t;
826 	Off addr, offset;
827 	Timet tim;
828 	int nread, count, n, o, slot;
829 
830 	if(CHAT(cp)) {
831 		print("c_read %d\n", cp->chan);
832 		print("\tfid = %d\n", in->fid);
833 		print("\toffset = %lld\n", (Wideoff)in->offset);
834 		print("\tcount = %ld\n", in->count);
835 	}
836 
837 	p = 0;
838 	count = in->count;
839 	offset = in->offset;
840 	nread = 0;
841 	f = filep(cp, in->fid, 0);
842 	if(!f) {
843 		ou->err = Efid;
844 		goto out;
845 	}
846 	if(!(f->open & FREAD)) {
847 		ou->err = Eopen;
848 		goto out;
849 	}
850 	if(count < 0 || count > MAXDAT) {
851 		ou->err = Ecount;
852 		goto out;
853 	}
854 	if(offset < 0) {
855 		ou->err = Eoffset;
856 		goto out;
857 	}
858 	p = getbuf(f->fs->dev, f->addr, Brd);
859 	d = getdir(p, f->slot);
860 	if(!d || !(d->mode & DALLOC)) {
861 		ou->err = Ealloc;
862 		goto out;
863 	}
864 	if(ou->err = mkqidcmp(&f->qid, d))
865 		goto out;
866 	if(t = f->tlock) {
867 		tim = toytime();
868 		if(t->time < tim || t->file != f) {
869 			ou->err = Ebroken;
870 			goto out;
871 		}
872 		/* renew the lock */
873 		t->time = tim + TLOCK;
874 	}
875 	accessdir(p, d, FREAD, f->uid);
876 	if(d->mode & DDIR) {
877 		addr = 0;
878 		goto dread;
879 	}
880 
881 	/* XXXX terrible hack to get at raw data XXXX */
882 	if(rawreadok && strncmp(d->name, "--raw--", 7) == 0) {
883 		Device *dev;
884 		Devsize boff, bsize;
885 
886 		dev = p->dev;
887 		putbuf(p);
888 		p = 0;
889 
890 		boff = number(d->name + 7, 0, 10) * 100000;
891 		if(boff < 0)
892 			boff = 0;
893 		if(boff > devsize(dev))
894 			boff = devsize(dev);
895 		bsize = devsize(dev) - boff;
896 
897 		if(offset+count >= 100000*RBUFSIZE)
898 			count = 100000*RBUFSIZE - offset;
899 
900 		if((offset+count)/RBUFSIZE >= bsize)
901 			/* will not overflow */
902 			count = bsize*RBUFSIZE - offset;
903 
904 		while(count > 0) {
905 			addr = offset / RBUFSIZE;
906 			addr += boff;
907 			o = offset % RBUFSIZE;
908 			n = RBUFSIZE - o;
909 			if(n > count)
910 				n = count;
911 
912 			p1 = getbuf(dev, addr, Brd);
913 			if(p1) {
914 				memmove(ou->data+nread, p1->iobuf+o, n);
915 				putbuf(p1);
916 			} else
917 				memset(ou->data+nread, 0, n);
918 			count -= n;
919 			nread += n;
920 			offset += n;
921 		}
922 		goto out;
923 	}
924 
925 	if(offset+count > d->size)
926 		count = d->size - offset;
927 	while(count > 0) {
928 		if(p == 0) {
929 			p = getbuf(f->fs->dev, f->addr, Brd);
930 			d = getdir(p, f->slot);
931 			if(!d || !(d->mode & DALLOC)) {
932 				ou->err = Ealloc;
933 				goto out;
934 			}
935 		}
936 		addr = offset / BUFSIZE;
937 		f->lastra = dbufread(p, d, addr, f->lastra, f->uid);
938 		o = offset % BUFSIZE;
939 		n = BUFSIZE - o;
940 		if(n > count)
941 			n = count;
942 		p1 = dnodebuf1(p, d, addr, 0, f->uid);
943 		p = 0;
944 		if(p1) {
945 			if(checktag(p1, Tfile, QPNONE)) {
946 				ou->err = Ephase;
947 				putbuf(p1);
948 				goto out;
949 			}
950 			memmove(ou->data+nread, p1->iobuf+o, n);
951 			putbuf(p1);
952 		} else
953 			memset(ou->data+nread, 0, n);
954 		count -= n;
955 		nread += n;
956 		offset += n;
957 	}
958 	goto out;
959 
960 dread:
961 	for (;;) {
962 		if(p == 0) {
963 			p = getbuf(f->fs->dev, f->addr, Brd);
964 			d = getdir(p, f->slot);
965 			if(!d || !(d->mode & DALLOC)) {
966 				ou->err = Ealloc;
967 				goto out;
968 			}
969 		}
970 		p1 = dnodebuf1(p, d, addr, 0, f->uid);
971 		p = 0;
972 		if(!p1)
973 			goto out;
974 		if(checktag(p1, Tdir, QPNONE)) {
975 			ou->err = Ephase;
976 			putbuf(p1);
977 			goto out;
978 		}
979 		n = DIRREC;
980 		for(slot=0; slot<DIRPERBUF; slot++) {
981 			d1 = getdir(p1, slot);
982 			if(!(d1->mode & DALLOC))
983 				continue;
984 			if(offset >= n) {
985 				offset -= n;
986 				continue;
987 			}
988 			if(count < n) {
989 				putbuf(p1);
990 				goto out;
991 			}
992 			if(convD2M9p1(d1, ou->data+nread) != n)
993 				print("9p1: dirread convD2M1990\n");
994 			nread += n;
995 			count -= n;
996 		}
997 		putbuf(p1);
998 		addr++;
999 	}
1000 out:
1001 	count = in->count - nread;
1002 	if(count > 0)
1003 		memset(ou->data+nread, 0, count);
1004 	if(p)
1005 		putbuf(p);
1006 	if(f)
1007 		qunlock(f);
1008 	ou->fid = in->fid;
1009 	ou->count = nread;
1010 	if(CHAT(cp))
1011 		print("\tnread = %d\n", nread);
1012 }
1013 
1014 static void
f_write(Chan * cp,Fcall * in,Fcall * ou)1015 f_write(Chan *cp, Fcall *in, Fcall *ou)
1016 {
1017 	Iobuf *p, *p1;
1018 	Dentry *d;
1019 	File *f;
1020 	Tlock *t;
1021 	Off offset, addr, qpath;
1022 	Timet tim;
1023 	int count, nwrite, o, n;
1024 
1025 	if(CHAT(cp)) {
1026 		print("c_write %d\n", cp->chan);
1027 		print("\tfid = %d\n", in->fid);
1028 		print("\toffset = %lld\n", (Wideoff)in->offset);
1029 		print("\tcount = %ld\n", in->count);
1030 	}
1031 
1032 	offset = in->offset;
1033 	count = in->count;
1034 	nwrite = 0;
1035 	p = 0;
1036 	f = filep(cp, in->fid, 0);
1037 	if(!f) {
1038 		ou->err = Efid;
1039 		goto out;
1040 	}
1041 	if(!(f->open & FWRITE)) {
1042 		ou->err = Eopen;
1043 		goto out;
1044 	}
1045 	if(f->fs->dev->type == Devro) {
1046 		ou->err = Eronly;
1047 		goto out;
1048 	}
1049 	if(count < 0 || count > MAXDAT) {
1050 		ou->err = Ecount;
1051 		goto out;
1052 	}
1053 	if(offset < 0) {
1054 		ou->err = Eoffset;
1055 		goto out;
1056 	}
1057 	p = getbuf(f->fs->dev, f->addr, Brd|Bmod);
1058 	d = getdir(p, f->slot);
1059 	if(!d || !(d->mode & DALLOC)) {
1060 		ou->err = Ealloc;
1061 		goto out;
1062 	}
1063 	if(ou->err = mkqidcmp(&f->qid, d))
1064 		goto out;
1065 	if(t = f->tlock) {
1066 		tim = toytime();
1067 		if(t->time < tim || t->file != f) {
1068 			ou->err = Ebroken;
1069 			goto out;
1070 		}
1071 		/* renew the lock */
1072 		t->time = tim + TLOCK;
1073 	}
1074 	accessdir(p, d, FWRITE, f->uid);
1075 	if(d->mode & DAPND)
1076 		offset = d->size;
1077 	if(offset+count > d->size)
1078 		d->size = offset+count;
1079 	while(count > 0) {
1080 		if(p == 0) {
1081 			p = getbuf(f->fs->dev, f->addr, Brd|Bmod);
1082 			d = getdir(p, f->slot);
1083 			if(!d || !(d->mode & DALLOC)) {
1084 				ou->err = 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 		qpath = d->qid.path;
1094 		p1 = dnodebuf1(p, d, addr, Tfile, f->uid);
1095 		p = 0;
1096 		if(p1 == 0) {
1097 			ou->err = Efull;
1098 			goto out;
1099 		}
1100 		if(checktag(p1, Tfile, qpath)) {
1101 			putbuf(p1);
1102 			ou->err = Ephase;
1103 			goto out;
1104 		}
1105 		memmove(p1->iobuf+o, in->data+nwrite, n);
1106 		p1->flags |= Bmod;
1107 		putbuf(p1);
1108 		count -= n;
1109 		nwrite += n;
1110 		offset += n;
1111 	}
1112 	if(CHAT(cp))
1113 		print("\tnwrite = %d\n", nwrite);
1114 
1115 out:
1116 	if(p)
1117 		putbuf(p);
1118 	if(f)
1119 		qunlock(f);
1120 	ou->fid = in->fid;
1121 	ou->count = nwrite;
1122 }
1123 
1124 int
doremove(File * f,int wok)1125 doremove(File *f, int wok)
1126 {
1127 	Iobuf *p, *p1;
1128 	Dentry *d, *d1;
1129 	Off addr;
1130 	int slot, err;
1131 
1132 	p = 0;
1133 	p1 = 0;
1134 	if(f->fs->dev->type == Devro) {
1135 		err = Eronly;
1136 		goto out;
1137 	}
1138 	/*
1139 	 * check on parent directory of file to be deleted
1140 	 */
1141 	if(f->wpath == 0 || f->wpath->addr == f->addr) {
1142 		err = Ephase;
1143 		goto out;
1144 	}
1145 	p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1146 	d1 = getdir(p1, f->wpath->slot);
1147 	if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1148 		err = Ephase;
1149 		goto out;
1150 	}
1151 	if(iaccess(f, d1, DWRITE) && !wok) {
1152 		err = Eaccess;
1153 		goto out;
1154 	}
1155 	accessdir(p1, d1, FWRITE, f->uid);
1156 	putbuf(p1);
1157 	p1 = 0;
1158 
1159 	/*
1160 	 * check on file to be deleted
1161 	 */
1162 	p = getbuf(f->fs->dev, f->addr, Brd);
1163 	d = getdir(p, f->slot);
1164 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1165 		err = Ealloc;
1166 		goto out;
1167 	}
1168 	if(err = mkqidcmp(&f->qid, d))
1169 		goto out;
1170 
1171 	/*
1172 	 * if deleting a directory, make sure it is empty
1173 	 */
1174 	if((d->mode & DDIR))
1175 	for(addr=0;; addr++) {
1176 		p1 = dnodebuf(p, d, addr, 0, f->uid);
1177 		if(!p1)
1178 			break;
1179 		if(checktag(p1, Tdir, d->qid.path)) {
1180 			err = Ephase;
1181 			goto out;
1182 		}
1183 		for(slot=0; slot<DIRPERBUF; slot++) {
1184 			d1 = getdir(p1, slot);
1185 			if(!(d1->mode & DALLOC))
1186 				continue;
1187 			err = Eempty;
1188 			goto out;
1189 		}
1190 		putbuf(p1);
1191 	}
1192 
1193 	/*
1194 	 * do it
1195 	 */
1196 	dtrunc(p, d, f->uid);
1197 	memset(d, 0, sizeof(Dentry));
1198 	settag(p, Tdir, QPNONE);
1199 
1200 out:
1201 	if(p1)
1202 		putbuf(p1);
1203 	if(p)
1204 		putbuf(p);
1205 	return err;
1206 }
1207 
1208 static int
doclunk(File * f,int remove,int wok)1209 doclunk(File* f, int remove, int wok)
1210 {
1211 	Tlock *t;
1212 	int err;
1213 
1214 	err = 0;
1215 	if(t = f->tlock) {
1216 		if(t->file == f)
1217 			t->time = 0;	/* free the lock */
1218 		f->tlock = 0;
1219 	}
1220 	if(remove)
1221 		err = doremove(f, wok);
1222 	f->open = 0;
1223 	freewp(f->wpath);
1224 	freefp(f);
1225 
1226 	return err;
1227 }
1228 
1229 static void
f_clunk(Chan * cp,Fcall * in,Fcall * ou)1230 f_clunk(Chan *cp, Fcall *in, Fcall *ou)
1231 {
1232 	File *f;
1233 
1234 	if(CHAT(cp)) {
1235 		print("c_clunk %d\n", cp->chan);
1236 		print("\tfid = %d\n", in->fid);
1237 	}
1238 
1239 	f = filep(cp, in->fid, 0);
1240 	if(!f)
1241 		ou->err = Efid;
1242 	else {
1243 		doclunk(f, f->open & FREMOV, 0);
1244 		qunlock(f);
1245 	}
1246 	ou->fid = in->fid;
1247 }
1248 
1249 static void
f_remove(Chan * cp,Fcall * in,Fcall * ou)1250 f_remove(Chan *cp, Fcall *in, Fcall *ou)
1251 {
1252 	File *f;
1253 
1254 	if(CHAT(cp)) {
1255 		print("c_remove %d\n", cp->chan);
1256 		print("\tfid = %d\n", in->fid);
1257 	}
1258 
1259 	f = filep(cp, in->fid, 0);
1260 	if(!f)
1261 		ou->err = Efid;
1262 	else {
1263 		ou->err = doclunk(f, 1, cp==cons.chan);
1264 		qunlock(f);
1265 	}
1266 	ou->fid = in->fid;
1267 }
1268 
1269 static void
f_stat(Chan * cp,Fcall * in,Fcall * ou)1270 f_stat(Chan *cp, Fcall *in, Fcall *ou)
1271 {
1272 	Iobuf *p;
1273 	Dentry *d;
1274 	File *f;
1275 
1276 	if(CHAT(cp)) {
1277 		print("c_stat %d\n", cp->chan);
1278 		print("\tfid = %d\n", in->fid);
1279 	}
1280 
1281 	p = 0;
1282 	memset(ou->stat, 0, sizeof(ou->stat));
1283 	f = filep(cp, in->fid, 0);
1284 	if(!f) {
1285 		ou->err = Efid;
1286 		goto out;
1287 	}
1288 	p = getbuf(f->fs->dev, f->addr, Brd);
1289 	d = getdir(p, f->slot);
1290 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1291 		ou->err = Ealloc;
1292 		goto out;
1293 	}
1294 	if(ou->err = mkqidcmp(&f->qid, d))
1295 		goto out;
1296 	if(d->qid.path == QPROOT)	/* stat of root gives time */
1297 		d->atime = time(nil);
1298 	if(convD2M9p1(d, ou->stat) != DIRREC)
1299 		print("9p1: stat convD2M\n");
1300 
1301 out:
1302 	if(p)
1303 		putbuf(p);
1304 	if(f)
1305 		qunlock(f);
1306 	ou->fid = in->fid;
1307 }
1308 
1309 static void
f_wstat(Chan * cp,Fcall * in,Fcall * ou)1310 f_wstat(Chan *cp, Fcall *in, Fcall *ou)
1311 {
1312 	Iobuf *p, *p1;
1313 	Dentry *d, *d1, xd;
1314 	File *f;
1315 	int slot;
1316 	Off addr;
1317 
1318 	if(CHAT(cp)) {
1319 		print("c_wstat %d\n", cp->chan);
1320 		print("\tfid = %d\n", in->fid);
1321 	}
1322 
1323 	p = 0;
1324 	p1 = 0;
1325 	d1 = 0;
1326 	f = filep(cp, in->fid, 0);
1327 	if(!f) {
1328 		ou->err = Efid;
1329 		goto out;
1330 	}
1331 	if(f->fs->dev->type == Devro) {
1332 		ou->err = Eronly;
1333 		goto out;
1334 	}
1335 
1336 	/*
1337 	 * first get parent
1338 	 */
1339 	if(f->wpath) {
1340 		p1 = getbuf(f->fs->dev, f->wpath->addr, Brd);
1341 		d1 = getdir(p1, f->wpath->slot);
1342 		if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
1343 			ou->err = Ephase;
1344 			goto out;
1345 		}
1346 	}
1347 
1348 	p = getbuf(f->fs->dev, f->addr, Brd);
1349 	d = getdir(p, f->slot);
1350 	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1351 		ou->err = Ealloc;
1352 		goto out;
1353 	}
1354 	if(ou->err = mkqidcmp(&f->qid, d))
1355 		goto out;
1356 
1357 	convM2D9p1(in->stat, &xd);
1358 	if(CHAT(cp)) {
1359 		print("\td.name = %s\n", xd.name);
1360 		print("\td.uid  = %d\n", xd.uid);
1361 		print("\td.gid  = %d\n", xd.gid);
1362 		print("\td.mode = %o\n", xd.mode);
1363 	}
1364 
1365 	/*
1366 	 * if user none,
1367 	 * cant do anything
1368 	 */
1369 	if(f->uid == 0) {
1370 		ou->err = Eaccess;
1371 		goto out;
1372 	}
1373 
1374 	/*
1375 	 * if chown,
1376 	 * must be god
1377 	 */
1378 	if(xd.uid != d->uid && !wstatallow) { /* set to allow chown during boot */
1379 		ou->err = Ewstatu;
1380 		goto out;
1381 	}
1382 
1383 	/*
1384 	 * if chgroup,
1385 	 * must be either
1386 	 *	a) owner and in new group
1387 	 *	b) leader of both groups
1388 	 */
1389 	if (xd.gid != d->gid &&
1390 	    (!wstatallow && !writeallow &&  /* set to allow chgrp during boot */
1391 	     (d->uid != f->uid || !ingroup(f->uid, xd.gid)) &&
1392 	     (!leadgroup(f->uid, xd.gid) || !leadgroup(f->uid, d->gid)))) {
1393 		ou->err = Ewstatg;
1394 		goto out;
1395 	}
1396 
1397 	/*
1398 	 * if rename,
1399 	 * must have write permission in parent
1400 	 */
1401 	if (strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
1402 		if (checkname(xd.name) || !d1 ||
1403 		    strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) {
1404 			ou->err = Ename;
1405 			goto out;
1406 		}
1407 
1408 		/*
1409 		 * drop entry to prevent lock, then
1410 		 * check that destination name is unique,
1411 		 */
1412 		putbuf(p);
1413 		for(addr=0;; addr++) {
1414 			p = dnodebuf(p1, d1, addr, 0, f->uid);
1415 			if(!p)
1416 				break;
1417 			if(checktag(p, Tdir, d1->qid.path)) {
1418 				putbuf(p);
1419 				continue;
1420 			}
1421 			for(slot=0; slot<DIRPERBUF; slot++) {
1422 				d = getdir(p, slot);
1423 				if(!(d->mode & DALLOC))
1424 					continue;
1425 				if(!strncmp(xd.name, d->name, sizeof(xd.name))) {
1426 					ou->err = Eexist;
1427 					goto out;
1428 				}
1429 			}
1430 			putbuf(p);
1431 		}
1432 
1433 		/*
1434 		 * reacquire entry
1435 		 */
1436 		p = getbuf(f->fs->dev, f->addr, Brd);
1437 		d = getdir(p, f->slot);
1438 		if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
1439 			ou->err = Ephase;
1440 			goto out;
1441 		}
1442 
1443 		if (!wstatallow && !writeallow && /* set to allow rename during boot */
1444 		    (!d1 || iaccess(f, d1, DWRITE))) {
1445 			ou->err = Eaccess;
1446 			goto out;
1447 		}
1448 	}
1449 
1450 	/*
1451 	 * if mode/time, either
1452 	 *	a) owner
1453 	 *	b) leader of either group
1454 	 */
1455 	if (d->mtime != xd.mtime ||
1456 	    ((d->mode^xd.mode) & (DAPND|DLOCK|0777)))
1457 		if (!wstatallow &&	/* set to allow chmod during boot */
1458 		    d->uid != f->uid &&
1459 		    !leadgroup(f->uid, xd.gid) &&
1460 		    !leadgroup(f->uid, d->gid)) {
1461 			ou->err = Ewstatu;
1462 			goto out;
1463 		}
1464 	d->mtime = xd.mtime;
1465 	d->uid = xd.uid;
1466 	d->gid = xd.gid;
1467 	d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
1468 
1469 	strncpy(d->name, xd.name, sizeof(d->name));
1470 	accessdir(p, d, FREAD, f->uid);
1471 
1472 out:
1473 	if(p)
1474 		putbuf(p);
1475 	if(p1)
1476 		putbuf(p1);
1477 	if(f)
1478 		qunlock(f);
1479 	ou->fid = in->fid;
1480 }
1481 
1482 static void
f_clwalk(Chan * cp,Fcall * in,Fcall * ou)1483 f_clwalk(Chan *cp, Fcall *in, Fcall *ou)
1484 {
1485 	int er, fid;
1486 
1487 	if(CHAT(cp))
1488 		print("c_clwalk macro\n");
1489 
1490 	f_clone(cp, in, ou);		/* sets tag, fid */
1491 	if(ou->err)
1492 		return;
1493 	fid = in->fid;
1494 	in->fid = in->newfid;
1495 	f_walk(cp, in, ou);		/* sets tag, fid, qid */
1496 	er = ou->err;
1497 	if(er == Eentry) {
1498 		/*
1499 		 * if error is "no entry"
1500 		 * return non error and fid
1501 		 */
1502 		ou->err = 0;
1503 		f_clunk(cp, in, ou);	/* sets tag, fid */
1504 		ou->err = 0;
1505 		ou->fid = fid;
1506 		if(CHAT(cp))
1507 			print("\terror: %s\n", errstr9p[er]);
1508 	} else if(er) {
1509 		/*
1510 		 * if any other error
1511 		 * return an error
1512 		 */
1513 		ou->err = 0;
1514 		f_clunk(cp, in, ou);	/* sets tag, fid */
1515 		ou->err = er;
1516 	}
1517 	/*
1518 	 * non error
1519 	 * return newfid
1520 	 */
1521 }
1522 
1523 void (*call9p1[MAXSYSCALL])(Chan*, Fcall*, Fcall*) =
1524 {
1525 	[Tnop]		f_nop,
1526 	[Tosession]	f_session,
1527 	[Tsession]	f_session,
1528 	[Tflush]	f_flush,
1529 	[Toattach]	f_attach,
1530 	[Tattach]	f_attach,
1531 	[Tclone]	f_clone,
1532 	[Twalk]		f_walk,
1533 	[Topen]		f_open,
1534 	[Tcreate]	f_create,
1535 	[Tread]		f_read,
1536 	[Twrite]	f_write,
1537 	[Tclunk]	f_clunk,
1538 	[Tremove]	f_remove,
1539 	[Tstat]		f_stat,
1540 	[Twstat]	f_wstat,
1541 	[Tclwalk]	f_clwalk,
1542 };
1543 
1544 int
error9p1(Chan * cp,Msgbuf * mb)1545 error9p1(Chan* cp, Msgbuf* mb)
1546 {
1547 	Msgbuf *mb1;
1548 
1549 	print("type=%d count=%d\n", mb->data[0], mb->count);
1550 	print(" %.2x %.2x %.2x %.2x\n",
1551 		mb->data[1]&0xff, mb->data[2]&0xff,
1552 		mb->data[3]&0xff, mb->data[4]&0xff);
1553 	print(" %.2x %.2x %.2x %.2x\n",
1554 		mb->data[5]&0xff, mb->data[6]&0xff,
1555 		mb->data[7]&0xff, mb->data[8]&0xff);
1556 	print(" %.2x %.2x %.2x %.2x\n",
1557 		mb->data[9]&0xff, mb->data[10]&0xff,
1558 		mb->data[11]&0xff, mb->data[12]&0xff);
1559 
1560 	mb1 = mballoc(3, cp, Mbreply4);
1561 	mb1->data[0] = Rnop;	/* your nop was ok */
1562 	mb1->data[1] = ~0;
1563 	mb1->data[2] = ~0;
1564 	mb1->count = 3;
1565 	mb1->param = mb->param;
1566 	fs_send(cp->reply, mb1);
1567 
1568 	return 1;
1569 }
1570 
1571 int
serve9p1(Msgbuf * mb)1572 serve9p1(Msgbuf* mb)
1573 {
1574 	int t, n;
1575 	Chan *cp;
1576 	Msgbuf *mb1;
1577 	Fcall fi, fo;
1578 
1579 	assert(mb != nil);
1580 	cp = mb->chan;
1581 	assert(mb->data != nil);
1582 	if(convM2S9p1(mb->data, &fi, mb->count) == 0){
1583 		assert(cp != nil);
1584 		if(cp->protocol == nil)
1585 			return 0;
1586 		print("9p1: bad M2S conversion\n");
1587 		return error9p1(cp, mb);
1588 	}
1589 
1590 	t = fi.type;
1591 	if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) {
1592 		print("9p1: bad message type\n");
1593 		return error9p1(cp, mb);
1594 	}
1595 
1596 	/*
1597 	 * allocate reply message
1598 	 */
1599 	if(t == Tread) {
1600 		mb1 = mballoc(MAXMSG+MAXDAT, cp, Mbreply2);
1601 		fo.data = (char*)(mb1->data + 8);
1602 	} else
1603 		mb1 = mballoc(MAXMSG, cp, Mbreply3);
1604 
1605 	/*
1606 	 * call the file system
1607 	 */
1608 	assert(cp != nil);
1609 	fo.err = 0;
1610 
1611 	(*call9p1[t])(cp, &fi, &fo);
1612 
1613 	fo.type = t+1;
1614 	fo.tag = fi.tag;
1615 
1616 	if(fo.err) {
1617 		if(cons.flags&errorflag)
1618 			print("\ttype %d: error: %s\n", t, errstr9p[fo.err]);
1619 		if(CHAT(cp))
1620 			print("\terror: %s\n", errstr9p[fo.err]);
1621 		fo.type = Rerror;
1622 		strncpy(fo.ename, errstr9p[fo.err], sizeof(fo.ename));
1623 	}
1624 
1625 	n = convS2M9p1(&fo, mb1->data);
1626 	if(n == 0) {
1627 		print("9p1: bad S2M conversion\n");
1628 		mbfree(mb1);
1629 		return error9p1(cp, mb);
1630 	}
1631 	mb1->count = n;
1632 	mb1->param = mb->param;
1633 	fs_send(cp->reply, mb1);
1634 
1635 	return 1;
1636 }
1637