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