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