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