URI:
       tsrv.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tsrv.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 }