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