plan9port

[fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port # fast
git clone https://src.adamsgaard.dk/plan9port.git # slow
Log | Files | Refs | README | LICENSE Back to index

srv.c (18737B)


      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
     30 setfcallerror(Fcall *f, char *err)
     31 {
     32 	f->ename = err;
     33 	f->type = Rerror;
     34 }
     35 
     36 static void
     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*
     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
    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
    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
    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
    180 rversion(Req *r, char *error)
    181 {
    182 	assert(error == nil);
    183 	changemsize(r->srv, r->ofcall.msize);
    184 }
    185 
    186 static void
    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
    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
    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
    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
    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
    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*
    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*
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    561 rclunk(Req *r, char *msg)
    562 {
    563 	USED(r);
    564 	USED(msg);
    565 }
    566 
    567 static void
    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
    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
    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
    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
    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
    691 rwstat(Req *r, char *msg)
    692 {
    693 	USED(r);
    694 	USED(msg);
    695 }
    696 
    697 void
    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
    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
    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 }