Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <9p.h>
7 int chatty9p;
9 /* static char Ebadattach[] = "unknown specifier in attach"; */
10 static char Ebadoffset[] = "bad offset";
11 /* static char Ebadcount[] = "bad count"; */
12 static char Ebotch[] = "9P protocol botch";
13 static char Ecreatenondir[] = "create in non-directory";
14 static char Edupfid[] = "duplicate fid";
15 static char Eduptag[] = "duplicate tag";
16 static char Eisdir[] = "is a directory";
17 static char Enocreate[] = "create prohibited";
18 /* static char Enomem[] = "out of memory"; */
19 static char Enoremove[] = "remove prohibited";
20 static char Enostat[] = "stat prohibited";
21 static char Enotfound[] = "file not found";
22 /* static char Enowrite[] = "write prohibited"; */
23 static char Enowstat[] = "wstat prohibited";
24 static char Eperm[] = "permission denied";
25 static char Eunknownfid[] = "unknown fid";
26 static char Ebaddir[] = "bad directory in wstat";
27 static char Ewalknodir[] = "walk in non-directory";
29 static void
30 setfcallerror(Fcall *f, char *err)
31 {
32 f->ename = err;
33 f->type = Rerror;
34 }
36 static void
37 changemsize(Srv *srv, int msize)
38 {
39 if(srv->rbuf && srv->wbuf && srv->msize == msize)
40 return;
41 qlock(&srv->rlock);
42 qlock(&srv->wlock);
43 srv->msize = msize;
44 free(srv->rbuf);
45 free(srv->wbuf);
46 srv->rbuf = emalloc9p(msize);
47 srv->wbuf = emalloc9p(msize);
48 qunlock(&srv->rlock);
49 qunlock(&srv->wlock);
50 }
52 static Req*
53 getreq(Srv *s)
54 {
55 long n;
56 uchar *buf;
57 Fcall f;
58 Req *r;
60 qlock(&s->rlock);
61 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
62 qunlock(&s->rlock);
63 return nil;
64 }
66 buf = emalloc9p(n+1); /* +1 for NUL in swrite */
67 memmove(buf, s->rbuf, n);
68 qunlock(&s->rlock);
70 if(convM2Su(buf, n, &f, s->dotu) != n){
71 free(buf);
72 return nil;
73 }
75 if((r=allocreq(s->rpool, f.tag)) == nil){ /* duplicate tag: cons up a fake Req */
76 r = emalloc9p(sizeof *r);
77 incref(&r->ref);
78 r->tag = f.tag;
79 r->ifcall = f;
80 r->error = Eduptag;
81 r->buf = buf;
82 r->responded = 0;
83 r->type = 0;
84 r->srv = s;
85 r->pool = nil;
86 if(chatty9p)
87 fprint(2, "<-%d- %F: dup tag\n", s->infd, &f);
88 return r;
89 }
91 r->srv = s;
92 r->responded = 0;
93 r->buf = buf;
94 r->ifcall = f;
95 memset(&r->ofcall, 0, sizeof r->ofcall);
96 r->type = r->ifcall.type;
98 if(chatty9p)
99 if(r->error)
100 fprint(2, "<-%d- %F: %s\n", s->infd, &r->ifcall, r->error);
101 else
102 fprint(2, "<-%d- %F\n", s->infd, &r->ifcall);
104 return r;
107 static void
108 filewalk(Req *r)
110 int i;
111 File *f;
113 f = r->fid->file;
114 assert(f != nil);
116 incref(&f->ref);
117 for(i=0; i<r->ifcall.nwname; i++)
118 if(f = walkfile(f, r->ifcall.wname[i]))
119 r->ofcall.wqid[i] = f->dir.qid;
120 else
121 break;
123 r->ofcall.nwqid = i;
124 if(f){
125 r->newfid->file = f;
126 r->newfid->qid = r->newfid->file->dir.qid;
128 respond(r, nil);
131 void
132 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
134 int i;
135 char *e;
137 if(r->fid == r->newfid && r->ifcall.nwname > 1){
138 respond(r, "lib9p: unused documented feature not implemented");
139 return;
142 if(r->fid != r->newfid){
143 r->newfid->qid = r->fid->qid;
144 if(clone && (e = clone(r->fid, r->newfid, arg))){
145 respond(r, e);
146 return;
150 e = nil;
151 for(i=0; i<r->ifcall.nwname; i++){
152 if(e = walk1(r->newfid, r->ifcall.wname[i], arg))
153 break;
154 r->ofcall.wqid[i] = r->newfid->qid;
157 r->ofcall.nwqid = i;
158 if(e && i==0)
159 respond(r, e);
160 else
161 respond(r, nil);
164 static void
165 sversion(Srv *srv, Req *r)
167 USED(srv);
169 if(strncmp(r->ifcall.version, "9P", 2) != 0){
170 r->ofcall.version = "unknown";
171 respond(r, nil);
172 return;
175 if(strncmp(r->ifcall.version, "9P2000.u", 8) == 0){
176 r->ofcall.version = "9P2000.u";
177 srv->dotu = 1;
178 }else{
179 r->ofcall.version = "9P2000";
180 srv->dotu = 0;
182 r->ofcall.msize = r->ifcall.msize;
183 respond(r, nil);
185 static void
186 rversion(Req *r, char *error)
188 assert(error == nil);
189 changemsize(r->srv, r->ofcall.msize);
192 static void
193 sauth(Srv *srv, Req *r)
195 char e[ERRMAX];
197 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
198 respond(r, Edupfid);
199 return;
201 if(srv->auth)
202 srv->auth(r);
203 else{
204 snprint(e, sizeof e, "%s: authentication not required", argv0);
205 respond(r, e);
208 static void
209 rauth(Req *r, char *error)
211 if(error && r->afid)
212 closefid(removefid(r->srv->fpool, r->afid->fid));
215 static void
216 sattach(Srv *srv, Req *r)
218 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
219 respond(r, Edupfid);
220 return;
222 r->afid = nil;
223 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
224 respond(r, Eunknownfid);
225 return;
227 r->fid->uid = estrdup9p(r->ifcall.uname);
228 if(srv->tree){
229 r->fid->file = srv->tree->root;
230 /* BUG? incref(r->fid->file) ??? */
231 r->ofcall.qid = r->fid->file->dir.qid;
232 r->fid->qid = r->ofcall.qid;
234 if(srv->attach)
235 srv->attach(r);
236 else
237 respond(r, nil);
238 return;
240 static void
241 rattach(Req *r, char *error)
243 if(error && r->fid)
244 closefid(removefid(r->srv->fpool, r->fid->fid));
247 static void
248 sflush(Srv *srv, Req *r)
250 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
251 if(r->oldreq == nil || r->oldreq == r)
252 respond(r, nil);
253 else if(srv->flush)
254 srv->flush(r);
255 else
256 respond(r, nil);
258 static int
259 rflush(Req *r, char *error)
261 Req *or;
263 assert(error == nil);
264 or = r->oldreq;
265 if(or){
266 qlock(&or->lk);
267 if(or->responded == 0){
268 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
269 or->flush[or->nflush++] = r;
270 qunlock(&or->lk);
271 return -1; /* delay response until or is responded */
273 qunlock(&or->lk);
274 closereq(or);
276 r->oldreq = nil;
277 return 0;
280 static char*
281 oldwalk1(Fid *fid, char *name, void *arg)
283 char *e;
284 Qid qid;
285 Srv *srv;
287 srv = arg;
288 e = srv->walk1(fid, name, &qid);
289 if(e)
290 return e;
291 fid->qid = qid;
292 return nil;
295 static char*
296 oldclone(Fid *fid, Fid *newfid, void *arg)
298 Srv *srv;
300 srv = arg;
301 if(srv->clone == nil)
302 return nil;
303 return srv->clone(fid, newfid);
306 static void
307 swalk(Srv *srv, Req *r)
309 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
310 respond(r, Eunknownfid);
311 return;
313 if(r->fid->omode != -1){
314 respond(r, "cannot clone open fid");
315 return;
317 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
318 respond(r, Ewalknodir);
319 return;
321 if(r->ifcall.fid != r->ifcall.newfid){
322 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
323 respond(r, Edupfid);
324 return;
326 r->newfid->uid = estrdup9p(r->fid->uid);
327 }else{
328 incref(&r->fid->ref);
329 r->newfid = r->fid;
331 if(r->fid->file){
332 filewalk(r);
333 }else if(srv->walk1)
334 walkandclone(r, oldwalk1, oldclone, srv);
335 else if(srv->walk)
336 srv->walk(r);
337 else
338 sysfatal("no walk function, no file trees");
340 static void
341 rwalk(Req *r, char *error)
343 if(error || r->ofcall.nwqid < r->ifcall.nwname){
344 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
345 closefid(removefid(r->srv->fpool, r->newfid->fid));
346 if (r->ofcall.nwqid==0){
347 if(error==nil && r->ifcall.nwname!=0)
348 r->error = Enotfound;
349 }else
350 r->error = nil; /* No error on partial walks */
351 }else{
352 if(r->ofcall.nwqid == 0){
353 /* Just a clone */
354 r->newfid->qid = r->fid->qid;
355 }else{
356 /* if file trees are in use, filewalk took care of the rest */
357 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
362 static void
363 sopen(Srv *srv, Req *r)
365 int p;
367 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
368 respond(r, Eunknownfid);
369 return;
371 if(r->fid->omode != -1){
372 respond(r, Ebotch);
373 return;
375 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
376 respond(r, Eisdir);
377 return;
379 r->ofcall.qid = r->fid->qid;
380 switch(r->ifcall.mode&3){
381 default:
382 assert(0);
383 case OREAD:
384 p = AREAD;
385 break;
386 case OWRITE:
387 p = AWRITE;
388 break;
389 case ORDWR:
390 p = AREAD|AWRITE;
391 break;
392 case OEXEC:
393 p = AEXEC;
394 break;
396 if(r->ifcall.mode&OTRUNC)
397 p |= AWRITE;
398 if((r->fid->qid.type&QTDIR) && p!=AREAD){
399 respond(r, Eperm);
400 return;
402 if(r->fid->file){
403 if(!hasperm(r->fid->file, r->fid->uid, p)){
404 respond(r, Eperm);
405 return;
407 /* BUG RACE */
408 if((r->ifcall.mode&ORCLOSE)
409 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
410 respond(r, Eperm);
411 return;
413 r->ofcall.qid = r->fid->file->dir.qid;
414 if((r->ofcall.qid.type&QTDIR)
415 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
416 respond(r, "opendirfile failed");
417 return;
420 if(srv->open)
421 srv->open(r);
422 else
423 respond(r, nil);
425 static void
426 ropen(Req *r, char *error)
428 char errbuf[ERRMAX];
429 if(error)
430 return;
431 if(chatty9p){
432 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
433 write(2, errbuf, strlen(errbuf));
435 r->fid->omode = r->ifcall.mode;
436 r->fid->qid = r->ofcall.qid;
437 if(r->ofcall.qid.type&QTDIR)
438 r->fid->diroffset = 0;
441 static void
442 screate(Srv *srv, Req *r)
444 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
445 respond(r, Eunknownfid);
446 else if(r->fid->omode != -1)
447 respond(r, Ebotch);
448 else if(!(r->fid->qid.type&QTDIR))
449 respond(r, Ecreatenondir);
450 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
451 respond(r, Eperm);
452 else if(srv->create)
453 srv->create(r);
454 else
455 respond(r, Enocreate);
457 static void
458 rcreate(Req *r, char *error)
460 if(error)
461 return;
462 r->fid->omode = r->ifcall.mode;
463 r->fid->qid = r->ofcall.qid;
466 static void
467 sread(Srv *srv, Req *r)
469 int o;
471 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
472 respond(r, Eunknownfid);
473 return;
475 if(r->ifcall.count < 0){
476 respond(r, Ebotch);
477 return;
479 if(r->ifcall.offset < 0
480 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
481 respond(r, Ebadoffset);
482 return;
485 if(r->ifcall.count > srv->msize - IOHDRSZ)
486 r->ifcall.count = srv->msize - IOHDRSZ;
487 r->rbuf = emalloc9p(r->ifcall.count);
488 r->ofcall.data = r->rbuf;
489 o = r->fid->omode & 3;
490 if(o != OREAD && o != ORDWR && o != OEXEC){
491 respond(r, Ebotch);
492 return;
494 if((r->fid->qid.type&QTDIR) && r->fid->file){
495 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
496 respond(r, nil);
497 return;
499 if(srv->read)
500 srv->read(r);
501 else
502 respond(r, "no srv->read");
504 static void
505 rread(Req *r, char *error)
507 if(error==nil && (r->fid->qid.type&QTDIR))
508 r->fid->diroffset += r->ofcall.count;
511 static void
512 swrite(Srv *srv, Req *r)
514 int o;
515 char e[ERRMAX];
517 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
518 respond(r, Eunknownfid);
519 return;
521 if(r->ifcall.count < 0){
522 respond(r, Ebotch);
523 return;
525 if(r->ifcall.offset < 0){
526 respond(r, Ebotch);
527 return;
529 if(r->ifcall.count > srv->msize - IOHDRSZ)
530 r->ifcall.count = srv->msize - IOHDRSZ;
531 o = r->fid->omode & 3;
532 if(o != OWRITE && o != ORDWR){
533 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
534 respond(r, e);
535 return;
537 if(srv->write){
538 r->ifcall.data[r->ifcall.count] = 0; /* enough room - see getreq */
539 srv->write(r);
540 }else
541 respond(r, "no srv->write");
543 static void
544 rwrite(Req *r, char *error)
546 if(error)
547 return;
548 if(r->fid->file)
549 r->fid->file->dir.qid.vers++;
552 static void
553 sclunk(Srv *srv, Req *r)
555 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
556 respond(r, Eunknownfid);
557 else
558 respond(r, nil);
560 static void
561 rclunk(Req *r, char *msg)
563 USED(r);
564 USED(msg);
567 static void
568 sremove(Srv *srv, Req *r)
570 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
571 respond(r, Eunknownfid);
572 return;
574 /* BUG RACE */
575 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
576 respond(r, Eperm);
577 return;
579 if(srv->remove)
580 srv->remove(r);
581 else
582 respond(r, r->fid->file ? nil : Enoremove);
584 static void
585 rremove(Req *r, char *error, char *errbuf)
587 if(error)
588 return;
589 if(r->fid->file){
590 if(removefile(r->fid->file) < 0){
591 snprint(errbuf, ERRMAX, "remove %s: %r",
592 r->fid->file->dir.name);
593 r->error = errbuf;
595 r->fid->file = nil;
599 static void
600 sstat(Srv *srv, Req *r)
602 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
603 respond(r, Eunknownfid);
604 return;
606 if(r->fid->file){
607 r->d = r->fid->file->dir;
608 if(r->d.name)
609 r->d.name = estrdup9p(r->d.name);
610 if(r->d.uid)
611 r->d.uid = estrdup9p(r->d.uid);
612 if(r->d.gid)
613 r->d.gid = estrdup9p(r->d.gid);
614 if(r->d.muid)
615 r->d.muid = estrdup9p(r->d.muid);
617 if(srv->stat)
618 srv->stat(r);
619 else if(r->fid->file)
620 respond(r, nil);
621 else
622 respond(r, Enostat);
624 static void
625 rstat(Req *r, char *error)
627 int n;
628 uchar *statbuf;
629 uchar tmp[BIT16SZ];
631 if(error)
632 return;
633 if(convD2Mu(&r->d, tmp, BIT16SZ, r->srv->dotu) != BIT16SZ){
634 r->error = "convD2Mu(_,_,BIT16SZ,_) did not return BIT16SZ";
635 return;
637 n = GBIT16(tmp)+BIT16SZ;
638 statbuf = emalloc9p(n);
639 if(statbuf == nil){
640 r->error = "out of memory";
641 return;
643 r->ofcall.nstat = convD2Mu(&r->d, statbuf, n, r->srv->dotu);
644 r->ofcall.stat = statbuf; /* freed in closereq */
645 if(r->ofcall.nstat <= BIT16SZ){
646 r->error = "convD2Mu fails";
647 free(statbuf);
648 return;
652 static void
653 swstat(Srv *srv, Req *r)
655 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
656 respond(r, Eunknownfid);
657 return;
659 if(srv->wstat == nil){
660 respond(r, Enowstat);
661 return;
663 if(convM2Du(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat, srv->dotu) != r->ifcall.nstat){
664 respond(r, Ebaddir);
665 return;
667 if((ushort)~r->d.type){
668 respond(r, "wstat -- attempt to change type");
669 return;
671 if((uint)~r->d.dev){
672 respond(r, "wstat -- attempt to change dev");
673 return;
675 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
676 respond(r, "wstat -- attempt to change qid");
677 return;
679 if(r->d.muid && r->d.muid[0]){
680 respond(r, "wstat -- attempt to change muid");
681 return;
683 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
684 respond(r, "wstat -- attempt to change DMDIR bit");
685 return;
687 srv->wstat(r);
689 static void
690 rwstat(Req *r, char *msg)
692 USED(r);
693 USED(msg);
696 void
697 srv(Srv *srv)
699 Req *r;
701 fmtinstall('D', dirfmt);
702 fmtinstall('F', fcallfmt);
704 if(srv->fpool == nil)
705 srv->fpool = allocfidpool(srv->destroyfid);
706 if(srv->rpool == nil)
707 srv->rpool = allocreqpool(srv->destroyreq);
708 if(srv->msize == 0)
709 srv->msize = 8192+IOHDRSZ;
711 changemsize(srv, srv->msize);
713 srv->fpool->srv = srv;
714 srv->rpool->srv = srv;
716 if(srv->start)
717 srv->start(srv);
719 while(r = getreq(srv)){
720 if(r->error){
721 respond(r, r->error);
722 continue;
724 switch(r->ifcall.type){
725 default:
726 respond(r, "unknown message");
727 break;
728 case Tversion: sversion(srv, r); break;
729 case Tauth: sauth(srv, r); break;
730 case Tattach: sattach(srv, r); break;
731 case Tflush: sflush(srv, r); break;
732 case Twalk: swalk(srv, r); break;
733 case Topen: sopen(srv, r); break;
734 case Tcreate: screate(srv, r); break;
735 case Tread: sread(srv, r); break;
736 case Twrite: swrite(srv, r); break;
737 case Tclunk: sclunk(srv, r); break;
738 case Tremove: sremove(srv, r); break;
739 case Tstat: sstat(srv, r); break;
740 case Twstat: swstat(srv, r); break;
744 if(srv->end)
745 srv->end(srv);
748 void
749 respond(Req *r, char *error)
751 int i, m, n;
752 char errbuf[ERRMAX];
753 Srv *srv;
755 srv = r->srv;
756 assert(srv != nil);
758 if(r->responded){
759 assert(r->pool);
760 goto free;
763 assert(r->responded == 0);
764 r->error = error;
766 switch(r->ifcall.type){
767 default:
768 assert(0);
769 /*
770 * Flush is special. If the handler says so, we return
771 * without further processing. Respond will be called
772 * again once it is safe.
773 */
774 case Tflush:
775 if(rflush(r, error)<0)
776 return;
777 break;
778 case Tversion: rversion(r, error); break;
779 case Tauth: rauth(r, error); break;
780 case Tattach: rattach(r, error); break;
781 case Twalk: rwalk(r, error); break;
782 case Topen: ropen(r, error); break;
783 case Tcreate: rcreate(r, error); break;
784 case Tread: rread(r, error); break;
785 case Twrite: rwrite(r, error); break;
786 case Tclunk: rclunk(r, error); break;
787 case Tremove: rremove(r, error, errbuf); break;
788 case Tstat: rstat(r, error); break;
789 case Twstat: rwstat(r, error); break;
792 r->ofcall.tag = r->ifcall.tag;
793 r->ofcall.type = r->ifcall.type+1;
794 if(r->error)
795 setfcallerror(&r->ofcall, r->error);
797 if(chatty9p)
798 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
800 qlock(&srv->wlock);
801 n = convS2Mu(&r->ofcall, srv->wbuf, srv->msize, srv->dotu);
802 if(n <= 0){
803 fprint(2, "n = %d %F\n", n, &r->ofcall);
804 abort();
806 assert(n > 2);
807 /*
808 * There is a race here - we must remove the entry before
809 * the write, so that if the client is very fast and reuses the
810 * tag, the read loop won't think it is still in use.
812 * By removing the entry before the write, we open up a
813 * race with incoming Tflush messages. Specifically, an
814 * incoming Tflush might not see r even though it has not
815 * yet been responded to. It would then send an Rflush
816 * immediately, potentially before we do the write. This can't
817 * happen because we already old srv->wlock, so nothing
818 * is going out on the wire before this write.
819 */
820 if(r->pool) /* not a fake */
821 closereq(removereq(r->pool, r->ifcall.tag));
823 qlock(&r->lk);
824 r->responded = 1;
825 if(r->pool)
826 if(r->ref.ref == 1+r->nflush)
827 if(r->fid){
828 /*
829 * There are no references other than in our r->flush array,
830 * so no one else should be accessing r concurrently.
831 * Close the fid now, before responding to the message.
833 * If the client is behaving (there are no outstanding T-messages
834 * that reference r->fid) and the message is a Tclunk or Tremove,
835 * then this closefid will call destroyfid.
837 * This means destroyfid can't piddle around
838 * indefinitely (we're holding srv->wlock!), but it provides
839 * for tighter semantics as to when destroyfid is called.
841 * LANL has observed cases where waiting until after the write
842 * can delay a closefid on a Twrite for many 9P transactions,
843 * so that a handful of transactions can happen including a Tclunk
844 * and a Topen, and the original fid will still not be destroyed.
845 */
846 closefid(r->fid);
847 r->fid = nil;
849 qunlock(&r->lk);
850 m = write(srv->outfd, srv->wbuf, n);
851 if(m != n)
852 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
853 qunlock(&srv->wlock);
855 free:
856 qlock(&r->lk); /* no one will add flushes now */
858 for(i=0; i<r->nflush; i++){
859 r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */
860 respond(r->flush[i], nil);
862 free(r->flush);
863 r->nflush = 0;
864 r->flush = nil;
865 qunlock(&r->lk);
867 if(r->pool)
868 closereq(r);
869 else
870 free(r);
873 int
874 postfd(char *name, int pfd)
876 int fd;
877 char buf[80];
879 snprint(buf, sizeof buf, "/srv/%s", name);
880 if(chatty9p)
881 fprint(2, "postfd %s\n", buf);
882 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
883 if(fd < 0){
884 if(chatty9p)
885 fprint(2, "create fails: %r\n");
886 return -1;
888 if(fprint(fd, "%d", pfd) < 0){
889 if(chatty9p)
890 fprint(2, "write fails: %r\n");
891 close(fd);
892 return -1;
894 if(chatty9p)
895 fprint(2, "postfd successful\n");
896 return 0;