]> git.lizzy.rs Git - plan9front.git/blob - sys/src/lib9p/srv.c
merge
[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 != 2){
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 void
369 sopen(Srv *srv, Req *r)
370 {
371         int p;
372
373         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
374                 respond(r, Eunknownfid);
375                 return;
376         }
377         if(r->fid->omode != -1){
378                 respond(r, Ebotch);
379                 return;
380         }
381         if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
382                 respond(r, Eisdir);
383                 return;
384         }
385         r->ofcall.qid = r->fid->qid;
386         switch(r->ifcall.mode&3){
387         default:
388                 assert(0);
389         case OREAD:
390                 p = AREAD;      
391                 break;
392         case OWRITE:
393                 p = AWRITE;
394                 break;
395         case ORDWR:
396                 p = AREAD|AWRITE;
397                 break;
398         case OEXEC:
399                 p = AEXEC;      
400                 break;
401         }
402         if(r->ifcall.mode&OTRUNC)
403                 p |= AWRITE;
404         if((r->fid->qid.type&QTDIR) && p!=AREAD){
405                 respond(r, Eperm);
406                 return;
407         }
408         if(r->fid->file){
409                 if(!hasperm(r->fid->file, r->fid->uid, p)){
410                         respond(r, Eperm);
411                         return;
412                 }
413         /* BUG RACE */
414                 if((r->ifcall.mode&ORCLOSE)
415                 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
416                         respond(r, Eperm);
417                         return;
418                 }
419                 r->ofcall.qid = r->fid->file->qid;
420                 if((r->ofcall.qid.type&QTDIR)
421                 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
422                         respond(r, "opendirfile failed");
423                         return;
424                 }
425         }
426         if(srv->open)
427                 srv->open(r);
428         else
429                 respond(r, nil);
430 }
431 static void
432 ropen(Req *r, char *error)
433 {
434         char errbuf[ERRMAX];
435         if(error)
436                 return;
437         if(chatty9p){
438                 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
439                 write(2, errbuf, strlen(errbuf));
440         }
441         r->fid->omode = r->ifcall.mode;
442         r->fid->qid = r->ofcall.qid;
443         if(r->ofcall.qid.type&QTDIR)
444                 r->fid->diroffset = 0;
445 }
446
447 static void
448 screate(Srv *srv, Req *r)
449 {
450         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
451                 respond(r, Eunknownfid);
452         else if(r->fid->omode != -1)
453                 respond(r, Ebotch);
454         else if(!(r->fid->qid.type&QTDIR))
455                 respond(r, Ecreatenondir);
456         else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
457                 respond(r, Eperm);
458         else if(srv->create)
459                 srv->create(r);
460         else
461                 respond(r, Enocreate);
462 }
463 static void
464 rcreate(Req *r, char *error)
465 {
466         if(error)
467                 return;
468         r->fid->omode = r->ifcall.mode;
469         r->fid->qid = r->ofcall.qid;
470 }
471
472 static void
473 sread(Srv *srv, Req *r)
474 {
475         int o;
476
477         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
478                 respond(r, Eunknownfid);
479                 return;
480         }
481         if((int)r->ifcall.count < 0){
482                 respond(r, Ebotch);
483                 return;
484         }
485         if(r->ifcall.offset < 0
486         || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
487                 respond(r, Ebadoffset);
488                 return;
489         }
490
491         if(r->ifcall.count > srv->msize - IOHDRSZ)
492                 r->ifcall.count = srv->msize - IOHDRSZ;
493         r->rbuf = emalloc9p(r->ifcall.count);
494         r->ofcall.data = r->rbuf;
495         o = r->fid->omode & 3;
496         if(o != OREAD && o != ORDWR && o != OEXEC){
497                 respond(r, Ebotch);
498                 return;
499         }
500         if((r->fid->qid.type&QTDIR) && r->fid->file){
501                 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
502                 respond(r, nil);
503                 return;
504         }
505         if(srv->read)
506                 srv->read(r);
507         else
508                 respond(r, "no srv->read");
509 }
510 static void
511 rread(Req *r, char *error)
512 {
513         if(error==nil && (r->fid->qid.type&QTDIR))
514                 r->fid->diroffset += r->ofcall.count;
515 }
516
517 static void
518 swrite(Srv *srv, Req *r)
519 {
520         int o;
521         char e[ERRMAX];
522
523         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
524                 respond(r, Eunknownfid);
525                 return;
526         }
527         if((int)r->ifcall.count < 0){
528                 respond(r, Ebotch);
529                 return;
530         }
531         if(r->ifcall.offset < 0){
532                 respond(r, Ebotch);
533                 return;
534         }
535         if(r->ifcall.count > srv->msize - IOHDRSZ)
536                 r->ifcall.count = srv->msize - IOHDRSZ;
537         o = r->fid->omode & 3;
538         if(o != OWRITE && o != ORDWR){
539                 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
540                 respond(r, e);
541                 return;
542         }
543         if(srv->write)
544                 srv->write(r);
545         else
546                 respond(r, "no srv->write");
547 }
548 static void
549 rwrite(Req *r, char *error)
550 {
551         if(error)
552                 return;
553         if(r->fid->file)
554                 r->fid->file->qid.vers++;
555 }
556
557 static void
558 sclunk(Srv *srv, Req *r)
559 {
560         if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
561                 respond(r, Eunknownfid);
562         else
563                 respond(r, nil);
564 }
565 static void
566 rclunk(Req*, char*)
567 {
568 }
569
570 static void
571 sremove(Srv *srv, Req *r)
572 {
573         if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
574                 respond(r, Eunknownfid);
575                 return;
576         }
577         /* BUG RACE */
578         if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
579                 respond(r, Eperm);
580                 return;
581         }
582         if(srv->remove)
583                 srv->remove(r);
584         else
585                 respond(r, r->fid->file ? nil : Enoremove);
586 }
587 static void
588 rremove(Req *r, char *error, char *errbuf)
589 {
590         if(error)
591                 return;
592         if(r->fid->file){
593                 if(removefile(r->fid->file) < 0){
594                         snprint(errbuf, ERRMAX, "remove %s: %r", 
595                                 r->fid->file->name);
596                         r->error = errbuf;
597                 }
598                 r->fid->file = nil;
599         }
600 }
601
602 static void
603 sstat(Srv *srv, Req *r)
604 {
605         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
606                 respond(r, Eunknownfid);
607                 return;
608         }
609         if(r->fid->file){
610                 /* should we rlock the file? */
611                 r->d = r->fid->file->Dir;
612                 if(r->d.name)
613                         r->d.name = estrdup9p(r->d.name);
614                 if(r->d.uid)
615                         r->d.uid = estrdup9p(r->d.uid);
616                 if(r->d.gid)
617                         r->d.gid = estrdup9p(r->d.gid);
618                 if(r->d.muid)
619                         r->d.muid = estrdup9p(r->d.muid);
620         }
621         if(srv->stat)   
622                 srv->stat(r);   
623         else if(r->fid->file)
624                 respond(r, nil);
625         else
626                 respond(r, Enostat);
627 }
628 static void
629 rstat(Req *r, char *error)
630 {
631         int n;
632         uchar *statbuf;
633         uchar tmp[BIT16SZ];
634
635         if(error)
636                 return;
637         if(convD2M(&r->d, tmp, BIT16SZ) != BIT16SZ){
638                 r->error = "convD2M(_,_,BIT16SZ) did not return BIT16SZ";
639                 return;
640         }
641         n = GBIT16(tmp)+BIT16SZ;
642         statbuf = emalloc9p(n);
643         if(statbuf == nil){
644                 r->error = "out of memory";
645                 return;
646         }
647         r->ofcall.nstat = convD2M(&r->d, statbuf, n);
648         r->ofcall.stat = statbuf;       /* freed in closereq */
649         if(r->ofcall.nstat <= BIT16SZ){
650                 r->error = "convD2M fails";
651                 free(statbuf);
652                 return;
653         }
654 }
655
656 static void
657 swstat(Srv *srv, Req *r)
658 {
659         if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
660                 respond(r, Eunknownfid);
661                 return;
662         }
663         if(srv->wstat == nil){
664                 respond(r, Enowstat);
665                 return;
666         }
667         if(convM2D(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat) != r->ifcall.nstat){
668                 respond(r, Ebaddir);
669                 return;
670         }
671         if((ushort)~r->d.type){
672                 respond(r, "wstat -- attempt to change type");
673                 return;
674         }
675         if((uint)~r->d.dev){
676                 respond(r, "wstat -- attempt to change dev");
677                 return;
678         }
679         if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
680                 respond(r, "wstat -- attempt to change qid");
681                 return;
682         }
683         if(r->d.muid && r->d.muid[0]){
684                 respond(r, "wstat -- attempt to change muid");
685                 return;
686         }
687         if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
688                 respond(r, "wstat -- attempt to change DMDIR bit");
689                 return;
690         }
691         srv->wstat(r);
692 }
693 static void
694 rwstat(Req*, char*)
695 {
696 }
697
698 static void srvclose(Srv *);
699
700 static void
701 srvwork(void *v)
702 {
703         Srv *srv = v;
704         Req *r;
705
706         incref(&srv->rref);
707         incref(&srv->sref);
708         while(r = getreq(srv)){
709                 incref(&srv->rref);
710                 if(r->error){
711                         respond(r, r->error);
712                         continue;       
713                 }
714                 qlock(&srv->slock);
715                 switch(r->ifcall.type){
716                 default:
717                         respond(r, "unknown message");
718                         break;
719                 case Tversion:  sversion(srv, r);       break;
720                 case Tauth:     sauth(srv, r);  break;
721                 case Tattach:   sattach(srv, r);        break;
722                 case Tflush:    sflush(srv, r); break;
723                 case Twalk:     swalk(srv, r);  break;
724                 case Topen:     sopen(srv, r);  break;
725                 case Tcreate:   screate(srv, r);        break;
726                 case Tread:     sread(srv, r);  break;
727                 case Twrite:    swrite(srv, r); break;
728                 case Tclunk:    sclunk(srv, r); break;
729                 case Tremove:   sremove(srv, r);        break;
730                 case Tstat:     sstat(srv, r);  break;
731                 case Twstat:    swstat(srv, r); break;
732                 }
733                 qunlock(&srv->slock);
734         }
735         decref(&srv->sref);
736         srvclose(srv);
737 }
738
739 static void
740 srvclose(Srv *srv)
741 {
742         if(decref(&srv->rref))
743                 return;
744
745         if(chatty9p)
746                 fprint(2, "srvclose\n");
747
748         free(srv->rbuf);
749         srv->rbuf = nil;
750         free(srv->wbuf);
751         srv->wbuf = nil;
752         srv->msize = 0;
753         freefidpool(srv->fpool);
754         srv->fpool = nil;
755         freereqpool(srv->rpool);
756         srv->rpool = nil;
757
758         if(srv->end)
759                 srv->end(srv);
760 }
761
762 void
763 srvacquire(Srv *srv)
764 {
765         incref(&srv->sref);
766         qlock(&srv->slock);
767 }
768
769 void
770 srvrelease(Srv *srv)
771 {
772         if(decref(&srv->sref) == 0)
773                 _forker(srvwork, srv, 0);
774         qunlock(&srv->slock);
775 }
776
777 void
778 srv(Srv *srv)
779 {
780         fmtinstall('D', dirfmt);
781         fmtinstall('F', fcallfmt);
782
783         srv->sref.ref = 0;
784         srv->rref.ref = 0;
785
786         if(srv->fpool == nil)
787                 srv->fpool = allocfidpool(srv->destroyfid);
788         if(srv->rpool == nil)
789                 srv->rpool = allocreqpool(srv->destroyreq);
790         if(srv->msize == 0)
791                 srv->msize = 8192+IOHDRSZ;
792
793         changemsize(srv, srv->msize);
794
795         srv->fpool->srv = srv;
796         srv->rpool->srv = srv;
797
798         if(srv->start)
799                 srv->start(srv);
800
801         srvwork(srv);
802 }
803
804 void
805 respond(Req *r, char *error)
806 {
807         int i, m, n;
808         char errbuf[ERRMAX];
809         Srv *srv;
810
811         srv = r->srv;
812         assert(srv != nil);
813
814         assert(r->responded == 0);
815         r->error = error;
816
817         switch(r->ifcall.type){
818         /*
819          * Flush is special.  If the handler says so, we return
820          * without further processing.  Respond will be called
821          * again once it is safe.
822          */
823         case Tflush:
824                 if(rflush(r, error)<0)
825                         return;
826                 break;
827         case Tversion:  rversion(r, error);     break;
828         case Tauth:     rauth(r, error);        break;
829         case Tattach:   rattach(r, error);      break;
830         case Twalk:     rwalk(r, error);        break;
831         case Topen:     ropen(r, error);        break;
832         case Tcreate:   rcreate(r, error);      break;
833         case Tread:     rread(r, error);        break;
834         case Twrite:    rwrite(r, error);       break;
835         case Tclunk:    rclunk(r, error);       break;
836         case Tremove:   rremove(r, error, errbuf);      break;
837         case Tstat:     rstat(r, error);        break;
838         case Twstat:    rwstat(r, error);       break;
839         }
840
841         r->ofcall.tag = r->ifcall.tag;
842         r->ofcall.type = r->ifcall.type+1;
843         if(r->error)
844                 setfcallerror(&r->ofcall, r->error);
845
846 if(chatty9p)
847         fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
848
849         qlock(&srv->wlock);
850         n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
851         if(n <= 0){
852                 fprint(2, "msize = %d n = %d %F\n", srv->msize, n, &r->ofcall);
853                 abort();
854         }
855         assert(n > 2);
856         if(r->pool)     /* not a fake */
857                 closereq(removereq(r->pool, r->ifcall.tag));
858         m = write(srv->outfd, srv->wbuf, n);
859         if(m != n)
860                 fprint(2, "lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
861         qunlock(&srv->wlock);
862
863         qlock(&r->lk);  /* no one will add flushes now */
864         r->responded = 1;
865         qunlock(&r->lk);
866
867         for(i=0; i<r->nflush; i++)
868                 respond(r->flush[i], nil);
869         free(r->flush);
870         r->flush = nil;
871         r->nflush = 0;
872
873         if(r->pool)
874                 closereq(r);
875         else
876                 free(r);
877
878         srvclose(srv);
879 }
880
881 void
882 responderror(Req *r)
883 {
884         char errbuf[ERRMAX];
885         
886         rerrstr(errbuf, sizeof errbuf);
887         respond(r, errbuf);
888 }
889
890 int
891 postfd(char *name, int pfd)
892 {
893         int fd;
894         char buf[80];
895
896         snprint(buf, sizeof buf, "/srv/%s", name);
897         if(chatty9p)
898                 fprint(2, "postfd %s\n", buf);
899         fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
900         if(fd < 0){
901                 if(chatty9p)
902                         fprint(2, "create fails: %r\n");
903                 return -1;
904         }
905         if(fprint(fd, "%d", pfd) < 0){
906                 if(chatty9p)
907                         fprint(2, "write fails: %r\n");
908                 close(fd);
909                 return -1;
910         }
911         if(chatty9p)
912                 fprint(2, "postfd successful\n");
913         return 0;
914 }
915
916 int
917 sharefd(char *name, char *desc, int pfd)
918 {
919         int fd;
920         char buf[80];
921
922         snprint(buf, sizeof buf, "#σc/%s", name);
923         if((fd = create(buf, OREAD, 0700|DMDIR)) >= 0)
924                 close(fd);
925         snprint(buf, sizeof buf, "#σc/%s/%s", name, desc);
926         if(chatty9p)
927                 fprint(2, "sharefd %s\n", buf);
928         fd = create(buf, OWRITE, 0600);
929         if(fd < 0){
930                 if(chatty9p)
931                         fprint(2, "create fails: %r\n");
932                 return -1;
933         }
934         if(fprint(fd, "%d\n", pfd) < 0){
935                 if(chatty9p)
936                         fprint(2, "write fails: %r\n");
937                 close(fd);
938                 return -1;
939         }
940         close(fd);
941         if(chatty9p)
942                 fprint(2, "sharefd successful\n");
943         return 0;
944 }