xref: /plan9/sys/src/lib9p/srv.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
7 
8 static char Ebadattach[] = "unknown specifier in attach";
9 static char Ebadoffset[] = "bad offset";
10 static char Ebadcount[] = "bad count";
11 static char Ebotch[] = "9P protocol botch";
12 static char Ecreatenondir[] = "create in non-directory";
13 static char Edupfid[] = "duplicate fid";
14 static char Eduptag[] = "duplicate tag";
15 static char Eisdir[] = "is a directory";
16 static char Enocreate[] = "create prohibited";
17 static char Enomem[] = "out of memory";
18 static char Enoremove[] = "remove prohibited";
19 static char Enostat[] = "stat prohibited";
20 static char Enotfound[] = "file not found";
21 static char Enowrite[] = "write prohibited";
22 static char Enowstat[] = "wstat prohibited";
23 static char Eperm[] = "permission denied";
24 static char Eunknownfid[] = "unknown fid";
25 static char Ebaddir[] = "bad directory in wstat";
26 static char Ewalknodir[] = "walk in non-directory";
27 
28 static void
29 setfcallerror(Fcall *f, char *err)
30 {
31 	f->ename = err;
32 	f->type = Rerror;
33 }
34 
35 static void
36 changemsize(Srv *srv, int msize)
37 {
38 	if(srv->rbuf && srv->wbuf && srv->msize == msize)
39 		return;
40 	qlock(&srv->rlock);
41 	qlock(&srv->wlock);
42 	srv->msize = msize;
43 	free(srv->rbuf);
44 	free(srv->wbuf);
45 	srv->rbuf = emalloc9p(msize);
46 	srv->wbuf = emalloc9p(msize);
47 	qunlock(&srv->rlock);
48 	qunlock(&srv->wlock);
49 }
50 
51 static Req*
52 getreq(Srv *s)
53 {
54 	long n;
55 	uchar *buf;
56 	Fcall f;
57 	Req *r;
58 
59 	qlock(&s->rlock);
60 	if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
61 		qunlock(&s->rlock);
62 		return nil;
63 	}
64 
65 	buf = emalloc9p(n);
66 	memmove(buf, s->rbuf, n);
67 	qunlock(&s->rlock);
68 
69 	if(convM2S(buf, n, &f) != n){
70 		free(buf);
71 		return nil;
72 	}
73 
74 	if((r=allocreq(s->rpool, f.tag)) == nil){	/* duplicate tag: cons up a fake Req */
75 		r = emalloc9p(sizeof *r);
76 		incref(&r->ref);
77 		r->tag = f.tag;
78 		r->ifcall = f;
79 		r->error = Eduptag;
80 		r->buf = buf;
81 		r->responded = 0;
82 		r->type = 0;
83 		r->srv = s;
84 		r->pool = nil;
85 if(chatty9p)
86 	fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
87 		return r;
88 	}
89 
90 	r->srv = s;
91 	r->responded = 0;
92 	r->buf = buf;
93 	r->ifcall = f;
94 	memset(&r->ofcall, 0, sizeof r->ofcall);
95 	r->type = r->ifcall.type;
96 
97 if(chatty9p)
98 	if(r->error)
99 		fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
100 	else
101 		fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
102 
103 	return r;
104 }
105 
106 static void
107 filewalk(Req *r)
108 {
109 	int i;
110 	File *f;
111 
112 	f = r->fid->file;
113 	assert(f != nil);
114 
115 	incref(f);
116 	for(i=0; i<r->ifcall.nwname; i++)
117 		if(f = walkfile(f, r->ifcall.wname[i]))
118 			r->ofcall.wqid[i] = f->qid;
119 		else
120 			break;
121 
122 	r->ofcall.nwqid = i;
123 	if(f){
124 		r->newfid->file = f;
125 		r->newfid->qid = r->newfid->file->qid;
126 	}
127 	respond(r, nil);
128 }
129 
130 void
131 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
132 {
133 	int i;
134 	char *e;
135 
136 	if(r->fid == r->newfid && r->ifcall.nwname > 1){
137 		respond(r, "lib9p: unused documented feature not implemented");
138 		return;
139 	}
140 
141 	if(r->fid != r->newfid){
142 		r->newfid->qid = r->fid->qid;
143 		if(clone && (e = clone(r->fid, r->newfid, arg))){
144 			respond(r, e);
145 			return;
146 		}
147 	}
148 
149 	e = nil;
150 	for(i=0; i<r->ifcall.nwname; i++){
151 		if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
152 			break;
153 		r->ofcall.wqid[i] = r->newfid->qid;
154 	}
155 
156 	r->ofcall.nwqid = i;
157 	if(e && i==0)
158 		respond(r, e);
159 	else
160 		respond(r, nil);
161 }
162 
163 static void
164 sversion(Srv*, Req *r)
165 {
166 	if(strncmp(r->ifcall.version, "9P", 2) != 0){
167 		r->ofcall.version = "unknown";
168 		respond(r, nil);
169 		return;
170 	}
171 
172 	r->ofcall.version = "9P2000";
173 	r->ofcall.msize = r->ifcall.msize;
174 	respond(r, nil);
175 }
176 static void
177 rversion(Req *r, char *error)
178 {
179 	assert(error == nil);
180 	changemsize(r->srv, r->ofcall.msize);
181 }
182 
183 static void
184 sauth(Srv *srv, Req *r)
185 {
186 	char e[ERRMAX];
187 
188 	if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
189 		respond(r, Edupfid);
190 		return;
191 	}
192 	if(srv->auth)
193 		srv->auth(r);
194 	else{
195 		snprint(e, sizeof e, "%s: authentication not required", argv0);
196 		respond(r, e);
197 	}
198 }
199 static void
200 rauth(Req *r, char *error)
201 {
202 	if(error && r->afid)
203 		closefid(removefid(r->srv->fpool, r->afid->fid));
204 }
205 
206 static void
207 sattach(Srv *srv, Req *r)
208 {
209 	if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
210 		respond(r, Edupfid);
211 		return;
212 	}
213 	r->afid = nil;
214 	if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
215 		respond(r, Eunknownfid);
216 		return;
217 	}
218 	r->fid->uid = estrdup9p(r->ifcall.uname);
219 	if(srv->tree){
220 		r->fid->file = srv->tree->root;
221 		/* BUG? incref(r->fid->file) ??? */
222 		r->ofcall.qid = r->fid->file->qid;
223 		r->fid->qid = r->ofcall.qid;
224 	}
225 	if(srv->attach)
226 		srv->attach(r);
227 	else
228 		respond(r, nil);
229 	return;
230 }
231 static void
232 rattach(Req *r, char *error)
233 {
234 	if(error && r->fid)
235 		closefid(removefid(r->srv->fpool, r->fid->fid));
236 }
237 
238 static void
239 sflush(Srv *srv, Req *r)
240 {
241 	r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
242 	if(r->oldreq == nil || r->oldreq == r)
243 		respond(r, nil);
244 	else if(srv->flush)
245 		srv->flush(r);
246 	else
247 		respond(r, nil);
248 }
249 static int
250 rflush(Req *r, char *error)
251 {
252 	Req *or;
253 
254 	assert(error == nil);
255 	or = r->oldreq;
256 	if(or){
257 		qlock(&or->lk);
258 		if(or->responded == 0){
259 			or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
260 			or->flush[or->nflush++] = r;
261 			qunlock(&or->lk);
262 			return -1;		/* delay response until or is responded */
263 		}
264 		qunlock(&or->lk);
265 		closereq(or);
266 	}
267 	r->oldreq = nil;
268 	return 0;
269 }
270 
271 static char*
272 oldwalk1(Fid *fid, char *name, void *arg)
273 {
274 	char *e;
275 	Qid qid;
276 	Srv *srv;
277 
278 	srv = arg;
279 	e = srv->walk1(fid, name, &qid);
280 	if(e)
281 		return e;
282 	fid->qid = qid;
283 	return nil;
284 }
285 
286 static char*
287 oldclone(Fid *fid, Fid *newfid, void *arg)
288 {
289 	Srv *srv;
290 
291 	srv = arg;
292 	if(srv->clone == nil)
293 		return nil;
294 	return srv->clone(fid, newfid);
295 }
296 
297 static void
298 swalk(Srv *srv, Req *r)
299 {
300 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
301 		respond(r, Eunknownfid);
302 		return;
303 	}
304 	if(r->fid->omode != -1){
305 		respond(r, "cannot clone open fid");
306 		return;
307 	}
308 	if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
309 		respond(r, Ewalknodir);
310 		return;
311 	}
312 	if(r->ifcall.fid != r->ifcall.newfid){
313 		if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
314 			respond(r, Edupfid);
315 			return;
316 		}
317 		r->newfid->uid = estrdup9p(r->fid->uid);
318 	}else{
319 		incref(&r->fid->ref);
320 		r->newfid = r->fid;
321 	}
322 	if(r->fid->file){
323 		filewalk(r);
324 	}else if(srv->walk1)
325 		walkandclone(r, oldwalk1, oldclone, srv);
326 	else if(srv->walk)
327 		srv->walk(r);
328 	else
329 		sysfatal("no walk function, no file trees");
330 }
331 static void
332 rwalk(Req *r, char *error)
333 {
334 	if(error || r->ofcall.nwqid < r->ifcall.nwname){
335 		if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
336 			closefid(removefid(r->srv->fpool, r->newfid->fid));
337 		if (r->ofcall.nwqid==0){
338 			if(error==nil && r->ifcall.nwname!=0)
339 				r->error = Enotfound;
340 		}else
341 			r->error = nil;	// No error on partial walks
342 	}else{
343 		if(r->ofcall.nwqid == 0){
344 			/* Just a clone */
345 			r->newfid->qid = r->fid->qid;
346 		}else{
347 			/* if file trees are in use, filewalk took care of the rest */
348 			r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
349 		}
350 	}
351 }
352 
353 static void
354 sopen(Srv *srv, Req *r)
355 {
356 	int p;
357 
358 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
359 		respond(r, Eunknownfid);
360 		return;
361 	}
362 	if(r->fid->omode != -1){
363 		respond(r, Ebotch);
364 		return;
365 	}
366 	if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
367 		respond(r, Eisdir);
368 		return;
369 	}
370 	r->ofcall.qid = r->fid->qid;
371 	switch(r->ifcall.mode&3){
372 	default:
373 		assert(0);
374 	case OREAD:
375 		p = AREAD;
376 		break;
377 	case OWRITE:
378 		p = AWRITE;
379 		break;
380 	case ORDWR:
381 		p = AREAD|AWRITE;
382 		break;
383 	case OEXEC:
384 		p = AEXEC;
385 		break;
386 	}
387 	if(r->ifcall.mode&OTRUNC)
388 		p |= AWRITE;
389 	if((r->fid->qid.type&QTDIR) && p!=AREAD){
390 		respond(r, Eperm);
391 		return;
392 	}
393 	if(r->fid->file){
394 		if(!hasperm(r->fid->file, r->fid->uid, p)){
395 			respond(r, Eperm);
396 			return;
397 		}
398 	/* BUG RACE */
399 		if((r->ifcall.mode&ORCLOSE)
400 		&& !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
401 			respond(r, Eperm);
402 			return;
403 		}
404 		r->ofcall.qid = r->fid->file->qid;
405 		if((r->ofcall.qid.type&QTDIR)
406 		&& (r->fid->rdir = opendirfile(r->fid->file)) == nil){
407 			respond(r, "opendirfile failed");
408 			return;
409 		}
410 	}
411 	if(srv->open)
412 		srv->open(r);
413 	else
414 		respond(r, nil);
415 }
416 static void
417 ropen(Req *r, char *error)
418 {
419 	char errbuf[ERRMAX];
420 	if(error)
421 		return;
422 	if(chatty9p){
423 		snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
424 		write(2, errbuf, strlen(errbuf));
425 	}
426 	r->fid->omode = r->ifcall.mode;
427 	r->fid->qid = r->ofcall.qid;
428 	if(r->ofcall.qid.type&QTDIR)
429 		r->fid->diroffset = 0;
430 }
431 
432 static void
433 screate(Srv *srv, Req *r)
434 {
435 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
436 		respond(r, Eunknownfid);
437 	else if(r->fid->omode != -1)
438 		respond(r, Ebotch);
439 	else if(!(r->fid->qid.type&QTDIR))
440 		respond(r, Ecreatenondir);
441 	else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
442 		respond(r, Eperm);
443 	else if(srv->create)
444 		srv->create(r);
445 	else
446 		respond(r, Enocreate);
447 }
448 static void
449 rcreate(Req *r, char *error)
450 {
451 	if(error)
452 		return;
453 	r->fid->omode = r->ifcall.mode;
454 	r->fid->qid = r->ofcall.qid;
455 }
456 
457 static void
458 sread(Srv *srv, Req *r)
459 {
460 	int o;
461 
462 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
463 		respond(r, Eunknownfid);
464 		return;
465 	}
466 	if(r->ifcall.count < 0){
467 		respond(r, Ebotch);
468 		return;
469 	}
470 	if(r->ifcall.offset < 0
471 	|| ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
472 		respond(r, Ebadoffset);
473 		return;
474 	}
475 
476 	if(r->ifcall.count > srv->msize - IOHDRSZ)
477 		r->ifcall.count = srv->msize - IOHDRSZ;
478 	r->rbuf = emalloc9p(r->ifcall.count);
479 	r->ofcall.data = r->rbuf;
480 	o = r->fid->omode & 3;
481 	if(o != OREAD && o != ORDWR && o != OEXEC){
482 		respond(r, Ebotch);
483 		return;
484 	}
485 	if((r->fid->qid.type&QTDIR) && r->fid->file){
486 		r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
487 		respond(r, nil);
488 		return;
489 	}
490 	if(srv->read)
491 		srv->read(r);
492 	else
493 		respond(r, "no srv->read");
494 }
495 static void
496 rread(Req *r, char *error)
497 {
498 	if(error==nil && (r->fid->qid.type&QTDIR))
499 		r->fid->diroffset += r->ofcall.count;
500 }
501 
502 static void
503 swrite(Srv *srv, Req *r)
504 {
505 	int o;
506 	char e[ERRMAX];
507 
508 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
509 		respond(r, Eunknownfid);
510 		return;
511 	}
512 	if(r->ifcall.count < 0){
513 		respond(r, Ebotch);
514 		return;
515 	}
516 	if(r->ifcall.offset < 0){
517 		respond(r, Ebotch);
518 		return;
519 	}
520 	if(r->ifcall.count > srv->msize - IOHDRSZ)
521 		r->ifcall.count = srv->msize - IOHDRSZ;
522 	o = r->fid->omode & 3;
523 	if(o != OWRITE && o != ORDWR){
524 		snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
525 		respond(r, e);
526 		return;
527 	}
528 	if(srv->write)
529 		srv->write(r);
530 	else
531 		respond(r, "no srv->write");
532 }
533 static void
534 rwrite(Req *r, char *error)
535 {
536 	if(error)
537 		return;
538 	if(r->fid->file)
539 		r->fid->file->qid.vers++;
540 }
541 
542 static void
543 sclunk(Srv *srv, Req *r)
544 {
545 	if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
546 		respond(r, Eunknownfid);
547 	else
548 		respond(r, nil);
549 }
550 static void
551 rclunk(Req*, char*)
552 {
553 }
554 
555 static void
556 sremove(Srv *srv, Req *r)
557 {
558 	if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
559 		respond(r, Eunknownfid);
560 		return;
561 	}
562 	/* BUG RACE */
563 	if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
564 		respond(r, Eperm);
565 		return;
566 	}
567 	if(srv->remove)
568 		srv->remove(r);
569 	else
570 		respond(r, r->fid->file ? nil : Enoremove);
571 }
572 static void
573 rremove(Req *r, char *error, char *errbuf)
574 {
575 	if(error)
576 		return;
577 	if(r->fid->file){
578 		if(removefile(r->fid->file) < 0){
579 			snprint(errbuf, ERRMAX, "remove %s: %r",
580 				r->fid->file->name);
581 			r->error = errbuf;
582 		}
583 		r->fid->file = nil;
584 	}
585 }
586 
587 static void
588 sstat(Srv *srv, Req *r)
589 {
590 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
591 		respond(r, Eunknownfid);
592 		return;
593 	}
594 	if(r->fid->file){
595 		r->d = r->fid->file->Dir;
596 		if(r->d.name)
597 			r->d.name = estrdup9p(r->d.name);
598 		if(r->d.uid)
599 			r->d.uid = estrdup9p(r->d.uid);
600 		if(r->d.gid)
601 			r->d.gid = estrdup9p(r->d.gid);
602 		if(r->d.muid)
603 			r->d.muid = estrdup9p(r->d.muid);
604 	}
605 	if(srv->stat)
606 		srv->stat(r);
607 	else if(r->fid->file)
608 		respond(r, nil);
609 	else
610 		respond(r, Enostat);
611 }
612 static void
613 rstat(Req *r, char *error)
614 {
615 	int n;
616 	uchar *statbuf;
617 	uchar tmp[BIT16SZ];
618 
619 	if(error)
620 		return;
621 	if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
622 		r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
623 		return;
624 	}
625 	n = GBIT16(tmp)+BIT16SZ;
626 	statbuf = emalloc9p(n);
627 	if(statbuf == nil){
628 		r->error = "out of memory";
629 		return;
630 	}
631 	r->ofcall.nstat = convD2M(&r->d, statbuf, n);
632 	r->ofcall.stat = statbuf;	/* freed in closereq */
633 	if(r->ofcall.nstat < 0){
634 		r->error = "convD2M fails";
635 		free(statbuf);
636 		return;
637 	}
638 }
639 
640 static void
641 swstat(Srv *srv, Req *r)
642 {
643 	if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
644 		respond(r, Eunknownfid);
645 		return;
646 	}
647 	if(srv->wstat == nil){
648 		respond(r, Enowstat);
649 		return;
650 	}
651 	if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
652 		respond(r, Ebaddir);
653 		return;
654 	}
655 	if((ushort)~r->d.type){
656 		respond(r, "wstat -- attempt to change type");
657 		return;
658 	}
659 	if((uint)~r->d.dev){
660 		respond(r, "wstat -- attempt to change dev");
661 		return;
662 	}
663 	if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
664 		respond(r, "wstat -- attempt to change qid");
665 		return;
666 	}
667 	if(r->d.muid && r->d.muid[0]){
668 		respond(r, "wstat -- attempt to change muid");
669 		return;
670 	}
671 	if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
672 		respond(r, "wstat -- attempt to change DMDIR bit");
673 		return;
674 	}
675 	srv->wstat(r);
676 }
677 static void
678 rwstat(Req*, char*)
679 {
680 }
681 
682 void
683 srv(Srv *srv)
684 {
685 	Req *r;
686 
687 	fmtinstall('D', dirfmt);
688 	fmtinstall('F', fcallfmt);
689 
690 	if(srv->fpool == nil)
691 		srv->fpool = allocfidpool(srv->destroyfid);
692 	if(srv->rpool == nil)
693 		srv->rpool = allocreqpool(srv->destroyreq);
694 	if(srv->msize == 0)
695 		srv->msize = 8192+IOHDRSZ;
696 
697 	changemsize(srv, srv->msize);
698 
699 	srv->fpool->srv = srv;
700 	srv->rpool->srv = srv;
701 
702 	while(r = getreq(srv)){
703 		if(r->error){
704 			respond(r, r->error);
705 			continue;
706 		}
707 		switch(r->ifcall.type){
708 		default:
709 			respond(r, "unknown message");
710 			break;
711 		case Tversion:	sversion(srv, r);	break;
712 		case Tauth:	sauth(srv, r);	break;
713 		case Tattach:	sattach(srv, r);	break;
714 		case Tflush:	sflush(srv, r);	break;
715 		case Twalk:	swalk(srv, r);	break;
716 		case Topen:	sopen(srv, r);	break;
717 		case Tcreate:	screate(srv, r);	break;
718 		case Tread:	sread(srv, r);	break;
719 		case Twrite:	swrite(srv, r);	break;
720 		case Tclunk:	sclunk(srv, r);	break;
721 		case Tremove:	sremove(srv, r);	break;
722 		case Tstat:	sstat(srv, r);	break;
723 		case Twstat:	swstat(srv, r);	break;
724 		}
725 	}
726 
727 	if(srv->end)
728 		srv->end(srv);
729 }
730 
731 void
732 respond(Req *r, char *error)
733 {
734 	int i, m, n;
735 	char errbuf[ERRMAX];
736 	Srv *srv;
737 
738 	srv = r->srv;
739 	assert(srv != nil);
740 
741 	assert(r->responded == 0);
742 	r->error = error;
743 
744 	switch(r->ifcall.type){
745 	default:
746 		assert(0);
747 	/*
748 	 * Flush is special.  If the handler says so, we return
749 	 * without further processing.  Respond will be called
750 	 * again once it is safe.
751 	 */
752 	case Tflush:
753 		if(rflush(r, error)<0)
754 			return;
755 		break;
756 	case Tversion:	rversion(r, error);	break;
757 	case Tauth:	rauth(r, error);	break;
758 	case Tattach:	rattach(r, error);	break;
759 	case Twalk:	rwalk(r, error);	break;
760 	case Topen:	ropen(r, error);	break;
761 	case Tcreate:	rcreate(r, error);	break;
762 	case Tread:	rread(r, error);	break;
763 	case Twrite:	rwrite(r, error);	break;
764 	case Tclunk:	rclunk(r, error);	break;
765 	case Tremove:	rremove(r, error, errbuf);	break;
766 	case Tstat:	rstat(r, error);	break;
767 	case Twstat:	rwstat(r, error);	break;
768 	}
769 
770 	r->ofcall.tag = r->ifcall.tag;
771 	r->ofcall.type = r->ifcall.type+1;
772 	if(r->error)
773 		setfcallerror(&r->ofcall, r->error);
774 
775 if(chatty9p)
776 	fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
777 
778 	qlock(&srv->wlock);
779 	n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
780 	if(n <= 0){
781 		fprint(2, "n = %d %F\n", n, &r->ofcall);
782 		abort();
783 	}
784 	assert(n > 2);
785 	if(r->pool)	/* not a fake */
786 		closereq(removereq(r->pool, r->ifcall.tag));
787 	m = write(srv->outfd, srv->wbuf, n);
788 	if(m != n)
789 		sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
790 	qunlock(&srv->wlock);
791 
792 	qlock(&r->lk);	/* no one will add flushes now */
793 	r->responded = 1;
794 	qunlock(&r->lk);
795 
796 	for(i=0; i<r->nflush; i++)
797 		respond(r->flush[i], nil);
798 	free(r->flush);
799 
800 	if(r->pool)
801 		closereq(r);
802 	else
803 		free(r);
804 }
805 
806 int
807 postfd(char *name, int pfd)
808 {
809 	int fd;
810 	char buf[80];
811 
812 	snprint(buf, sizeof buf, "/srv/%s", name);
813 	if(chatty9p)
814 		fprint(2, "postfd %s\n", buf);
815 	fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
816 	if(fd < 0){
817 		if(chatty9p)
818 			fprint(2, "create fails: %r\n");
819 		return -1;
820 	}
821 	if(fprint(fd, "%d", pfd) < 0){
822 		if(chatty9p)
823 			fprint(2, "write fails: %r\n");
824 		close(fd);
825 		return -1;
826 	}
827 	if(chatty9p)
828 		fprint(2, "postfd successful\n");
829 	return 0;
830 }
831 
832