]> git.lizzy.rs Git - plan9front.git/blob - sys/src/lib9p/srv.c
ndb/dns: lookup *all* entries in dblookup(), v4 and v6 queries in parallel, remove...
[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, r->ifcall.offset);
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->ifcall.offset + 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                 if(srv->sref.ref > 8 && srv->spid != getpid()){
752                         decref(&srv->sref);
753                         qunlock(&srv->slock);
754                         return;
755                 }
756                 qunlock(&srv->slock);
757         }
758
759         if(srv->end && srv->sref.ref == 1)
760                 srv->end(srv);
761         if(decref(&srv->sref) == 0)
762                 srvclose(srv);
763 }
764
765 static void
766 srvclose(Srv *srv)
767 {
768         if(srv->rref.ref || srv->sref.ref)
769                 return;
770
771         if(chatty9p)
772                 fprint(2, "srvclose\n");
773
774         free(srv->rbuf);
775         srv->rbuf = nil;
776         free(srv->wbuf);
777         srv->wbuf = nil;
778         srv->msize = 0;
779         freefidpool(srv->fpool);
780         srv->fpool = nil;
781         freereqpool(srv->rpool);
782         srv->rpool = nil;
783
784         if(srv->free)
785                 srv->free(srv);
786 }
787
788 void
789 srvacquire(Srv *srv)
790 {
791         incref(&srv->sref);
792         qlock(&srv->slock);
793 }
794
795 void
796 srvrelease(Srv *srv)
797 {
798         if(decref(&srv->sref) == 0){
799                 incref(&srv->sref);
800                 _forker(srvwork, srv, 0);
801         }
802         qunlock(&srv->slock);
803 }
804
805 void
806 srv(Srv *srv)
807 {
808         fmtinstall('D', dirfmt);
809         fmtinstall('F', fcallfmt);
810
811         srv->spid = getpid();
812         srv->sref.ref = 0;
813         srv->rref.ref = 0;
814
815         if(srv->fpool == nil)
816                 srv->fpool = allocfidpool(srv->destroyfid);
817         if(srv->rpool == nil)
818                 srv->rpool = allocreqpool(srv->destroyreq);
819         if(srv->msize == 0)
820                 srv->msize = 8192+IOHDRSZ;
821
822         changemsize(srv, srv->msize);
823
824         srv->fpool->srv = srv;
825         srv->rpool->srv = srv;
826
827         if(srv->start)
828                 srv->start(srv);
829
830         incref(&srv->sref);
831         srvwork(srv);
832 }
833
834 void
835 respond(Req *r, char *error)
836 {
837         int i, m, n;
838         char errbuf[ERRMAX];
839         Srv *srv;
840
841         srv = r->srv;
842         assert(srv != nil);
843
844         assert(r->responded == 0);
845         r->error = error;
846
847         switch(r->ifcall.type){
848         /*
849          * Flush is special.  If the handler says so, we return
850          * without further processing.  Respond will be called
851          * again once it is safe.
852          */
853         case Tflush:
854                 if(rflush(r, error)<0)
855                         return;
856                 break;
857         case Tversion:  rversion(r, error);     break;
858         case Tauth:     rauth(r, error);        break;
859         case Tattach:   rattach(r, error);      break;
860         case Twalk:     rwalk(r, error);        break;
861         case Topen:     ropen(r, error);        break;
862         case Tcreate:   rcreate(r, error);      break;
863         case Tread:     rread(r, error);        break;
864         case Twrite:    rwrite(r, error);       break;
865         case Tclunk:    rclunk(r, error);       break;
866         case Tremove:   rremove(r, error, errbuf);      break;
867         case Tstat:     rstat(r, error);        break;
868         case Twstat:    rwstat(r, error);       break;
869         }
870
871         r->ofcall.tag = r->ifcall.tag;
872         r->ofcall.type = r->ifcall.type+1;
873         if(r->error)
874                 setfcallerror(&r->ofcall, r->error);
875
876 if(chatty9p)
877         fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
878
879         qlock(&srv->wlock);
880         n = convS2M(&r->ofcall, srv->wbuf, srv->msize);
881         if(n <= 0){
882                 fprint(2, "msize = %d n = %d %F\n", srv->msize, n, &r->ofcall);
883                 abort();
884         }
885         assert(n > 2);
886         if(r->pool)     /* not a fake */
887                 closereq(removereq(r->pool, r->ifcall.tag));
888         m = write(srv->outfd, srv->wbuf, n);
889         if(m != n)
890                 fprint(2, "lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
891         qunlock(&srv->wlock);
892
893         qlock(&r->lk);  /* no one will add flushes now */
894         r->responded = 1;
895         qunlock(&r->lk);
896
897         for(i=0; i<r->nflush; i++)
898                 respond(r->flush[i], nil);
899         free(r->flush);
900         r->flush = nil;
901         r->nflush = 0;
902
903         if(r->pool)
904                 closereq(r);
905         else
906                 free(r);
907
908         if(decref(&srv->rref) == 0)
909                 srvclose(srv);
910 }
911
912 void
913 responderror(Req *r)
914 {
915         char errbuf[ERRMAX];
916         
917         rerrstr(errbuf, sizeof errbuf);
918         respond(r, errbuf);
919 }
920
921 int
922 postfd(char *name, int pfd)
923 {
924         int fd;
925         char buf[80];
926
927         snprint(buf, sizeof buf, "/srv/%s", name);
928         if(chatty9p)
929                 fprint(2, "postfd %s\n", buf);
930         fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
931         if(fd < 0){
932                 if(chatty9p)
933                         fprint(2, "create fails: %r\n");
934                 return -1;
935         }
936         if(fprint(fd, "%d", pfd) < 0){
937                 if(chatty9p)
938                         fprint(2, "write fails: %r\n");
939                 close(fd);
940                 return -1;
941         }
942         if(chatty9p)
943                 fprint(2, "postfd successful\n");
944         return 0;
945 }
946
947 int
948 sharefd(char *name, char *desc, int pfd)
949 {
950         int fd;
951         char buf[80];
952
953         snprint(buf, sizeof buf, "#σc/%s", name);
954         if((fd = create(buf, OREAD, 0700|DMDIR)) >= 0)
955                 close(fd);
956         snprint(buf, sizeof buf, "#σc/%s/%s", name, desc);
957         if(chatty9p)
958                 fprint(2, "sharefd %s\n", buf);
959         fd = create(buf, OWRITE, 0600);
960         if(fd < 0){
961                 if(chatty9p)
962                         fprint(2, "create fails: %r\n");
963                 return -1;
964         }
965         if(fprint(fd, "%d\n", pfd) < 0){
966                 if(chatty9p)
967                         fprint(2, "write fails: %r\n");
968                 close(fd);
969                 return -1;
970         }
971         close(fd);
972         if(chatty9p)
973                 fprint(2, "sharefd successful\n");
974         return 0;
975 }