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