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