Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <9p.h>
8 int chatty9p;
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";
30 static void
31 setfcallerror(Fcall *f, char *err)
32 {
33 f->ename = err;
34 f->type = Rerror;
35 }
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 }
53 static Req*
54 getreq(Srv *s)
55 {
56 long n;
57 uchar *buf;
58 Fcall f;
59 Req *r;
61 qlock(&s->rlock);
62 if((n = read9pmsg(s->infd, s->rbuf, s->msize)) <= 0){
63 qunlock(&s->rlock);
64 return nil;
65 }
67 buf = emalloc9p(n+1); /* +1 for NUL in swrite */
68 memmove(buf, s->rbuf, n);
69 qunlock(&s->rlock);
71 if(convM2Su(buf, n, &f, s->dotu) != n){
72 free(buf);
73 return nil;
74 }
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 }
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;
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);
105 return r;
108 static void
109 filewalk(Req *r)
111 int i;
112 File *f;
114 f = r->fid->file;
115 assert(f != nil);
117 incref(&f->ref);
118 for(i=0; i<r->ifcall.nwname; i++)
119 if(f = walkfile(f, r->ifcall.wname[i]))
120 r->ofcall.wqid[i] = f->dir.qid;
121 else
122 break;
124 r->ofcall.nwqid = i;
125 if(f){
126 r->newfid->file = f;
127 r->newfid->qid = r->newfid->file->dir.qid;
129 respond(r, nil);
132 void
133 walkandclone(Req *r, char *(*walk1)(Fid*, char*, void*), char *(*clone)(Fid*, Fid*, void*), void *arg)
135 int i;
136 char *e;
138 if(r->fid == r->newfid && r->ifcall.nwname > 1){
139 respond(r, "lib9p: unused documented feature not implemented");
140 return;
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;
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;
158 r->ofcall.nwqid = i;
159 if(e && i==0)
160 respond(r, e);
161 else
162 respond(r, nil);
165 static void
166 sversion(Srv *srv, Req *r)
168 USED(srv);
170 if(strncmp(r->ifcall.version, "9P", 2) != 0){
171 r->ofcall.version = "unknown";
172 respond(r, nil);
173 return;
176 if(strncmp(r->ifcall.version, "9P2000.u", 8) == 0){
177 r->ofcall.version = "9P2000.u";
178 srv->dotu = 1;
179 }else{
180 r->ofcall.version = "9P2000";
181 srv->dotu = 0;
183 r->ofcall.msize = r->ifcall.msize;
184 respond(r, nil);
186 static void
187 rversion(Req *r, char *error)
189 assert(error == nil);
190 changemsize(r->srv, r->ofcall.msize);
193 static void
194 sauth(Srv *srv, Req *r)
196 char e[ERRMAX];
198 if((r->afid = allocfid(srv->fpool, r->ifcall.afid)) == nil){
199 respond(r, Edupfid);
200 return;
202 if(srv->auth)
203 srv->auth(r);
204 else{
205 snprint(e, sizeof e, "%s: authentication not required", argv0);
206 respond(r, e);
209 static void
210 rauth(Req *r, char *error)
212 if(error && r->afid)
213 closefid(removefid(r->srv->fpool, r->afid->fid));
216 static void
217 sattach(Srv *srv, Req *r)
219 if((r->fid = allocfid(srv->fpool, r->ifcall.fid)) == nil){
220 respond(r, Edupfid);
221 return;
223 r->afid = nil;
224 if(r->ifcall.afid != NOFID && (r->afid = lookupfid(srv->fpool, r->ifcall.afid)) == nil){
225 respond(r, Eunknownfid);
226 return;
228 r->fid->uid = estrdup9p(r->ifcall.uname);
229 if(srv->tree){
230 r->fid->file = srv->tree->root;
231 /* BUG? incref(r->fid->file) ??? */
232 r->ofcall.qid = r->fid->file->dir.qid;
233 r->fid->qid = r->ofcall.qid;
235 if(srv->attach)
236 srv->attach(r);
237 else
238 respond(r, nil);
239 return;
241 static void
242 rattach(Req *r, char *error)
244 if(error && r->fid)
245 closefid(removefid(r->srv->fpool, r->fid->fid));
248 static void
249 sflush(Srv *srv, Req *r)
251 r->oldreq = lookupreq(srv->rpool, r->ifcall.oldtag);
252 if(r->oldreq == nil || r->oldreq == r)
253 respond(r, nil);
254 else if(srv->flush)
255 srv->flush(r);
256 else
257 respond(r, nil);
259 static int
260 rflush(Req *r, char *error)
262 Req *or;
264 assert(error == nil);
265 or = r->oldreq;
266 if(or){
267 qlock(&or->lk);
268 if(or->responded == 0){
269 or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0]));
270 or->flush[or->nflush++] = r;
271 qunlock(&or->lk);
272 return -1; /* delay response until or is responded */
274 qunlock(&or->lk);
275 closereq(or);
277 r->oldreq = nil;
278 return 0;
281 static char*
282 oldwalk1(Fid *fid, char *name, void *arg)
284 char *e;
285 Qid qid;
286 Srv *srv;
288 srv = arg;
289 e = srv->walk1(fid, name, &qid);
290 if(e)
291 return e;
292 fid->qid = qid;
293 return nil;
296 static char*
297 oldclone(Fid *fid, Fid *newfid, void *arg)
299 Srv *srv;
301 srv = arg;
302 if(srv->clone == nil)
303 return nil;
304 return srv->clone(fid, newfid);
307 static void
308 swalk(Srv *srv, Req *r)
310 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
311 respond(r, Eunknownfid);
312 return;
314 if(r->fid->omode != -1){
315 respond(r, "cannot clone open fid");
316 return;
318 if(r->ifcall.nwname && !(r->fid->qid.type&QTDIR)){
319 respond(r, Ewalknodir);
320 return;
322 if(r->ifcall.fid != r->ifcall.newfid){
323 if((r->newfid = allocfid(srv->fpool, r->ifcall.newfid)) == nil){
324 respond(r, Edupfid);
325 return;
327 r->newfid->uid = estrdup9p(r->fid->uid);
328 }else{
329 incref(&r->fid->ref);
330 r->newfid = r->fid;
332 if(r->fid->file){
333 filewalk(r);
334 }else if(srv->walk1)
335 walkandclone(r, oldwalk1, oldclone, srv);
336 else if(srv->walk)
337 srv->walk(r);
338 else
339 sysfatal("no walk function, no file trees");
341 static void
342 rwalk(Req *r, char *error)
344 if(error || r->ofcall.nwqid < r->ifcall.nwname){
345 if(r->ifcall.fid != r->ifcall.newfid && r->newfid)
346 closefid(removefid(r->srv->fpool, r->newfid->fid));
347 if (r->ofcall.nwqid==0){
348 if(error==nil && r->ifcall.nwname!=0)
349 r->error = Enotfound;
350 }else
351 r->error = nil; /* No error on partial walks */
352 }else{
353 if(r->ofcall.nwqid == 0){
354 /* Just a clone */
355 r->newfid->qid = r->fid->qid;
356 }else{
357 /* if file trees are in use, filewalk took care of the rest */
358 r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1];
363 static void
364 sopen(Srv *srv, Req *r)
366 int p;
368 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
369 respond(r, Eunknownfid);
370 return;
372 if(r->fid->omode != -1){
373 respond(r, Ebotch);
374 return;
376 if((r->fid->qid.type&QTDIR) && (r->ifcall.mode&~ORCLOSE) != OREAD){
377 respond(r, Eisdir);
378 return;
380 r->ofcall.qid = r->fid->qid;
381 switch(r->ifcall.mode&3){
382 default:
383 assert(0);
384 case OREAD:
385 p = AREAD;
386 break;
387 case OWRITE:
388 p = AWRITE;
389 break;
390 case ORDWR:
391 p = AREAD|AWRITE;
392 break;
393 case OEXEC:
394 p = AEXEC;
395 break;
397 if(r->ifcall.mode&OTRUNC)
398 p |= AWRITE;
399 if((r->fid->qid.type&QTDIR) && p!=AREAD){
400 respond(r, Eperm);
401 return;
403 if(r->fid->file){
404 if(!hasperm(r->fid->file, r->fid->uid, p)){
405 respond(r, Eperm);
406 return;
408 /* BUG RACE */
409 if((r->ifcall.mode&ORCLOSE)
410 && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
411 respond(r, Eperm);
412 return;
414 r->ofcall.qid = r->fid->file->dir.qid;
415 if((r->ofcall.qid.type&QTDIR)
416 && (r->fid->rdir = opendirfile(r->fid->file)) == nil){
417 respond(r, "opendirfile failed");
418 return;
421 if(srv->open)
422 srv->open(r);
423 else
424 respond(r, nil);
426 static void
427 ropen(Req *r, char *error)
429 char errbuf[ERRMAX];
430 if(error)
431 return;
432 if(chatty9p){
433 snprint(errbuf, sizeof errbuf, "fid mode is 0x%ux\n", r->ifcall.mode);
434 write(2, errbuf, strlen(errbuf));
436 r->fid->omode = r->ifcall.mode;
437 r->fid->qid = r->ofcall.qid;
438 if(r->ofcall.qid.type&QTDIR)
439 r->fid->diroffset = 0;
442 static void
443 screate(Srv *srv, Req *r)
445 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil)
446 respond(r, Eunknownfid);
447 else if(r->fid->omode != -1)
448 respond(r, Ebotch);
449 else if(!(r->fid->qid.type&QTDIR))
450 respond(r, Ecreatenondir);
451 else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
452 respond(r, Eperm);
453 else if(srv->create)
454 srv->create(r);
455 else
456 respond(r, Enocreate);
458 static void
459 rcreate(Req *r, char *error)
461 if(error)
462 return;
463 r->fid->omode = r->ifcall.mode;
464 r->fid->qid = r->ofcall.qid;
467 static void
468 sread(Srv *srv, Req *r)
470 int o;
472 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
473 respond(r, Eunknownfid);
474 return;
476 if(r->ifcall.count < 0){
477 respond(r, Ebotch);
478 return;
480 if(r->ifcall.offset < 0
481 || ((r->fid->qid.type&QTDIR) && r->ifcall.offset != 0 && r->ifcall.offset != r->fid->diroffset)){
482 respond(r, Ebadoffset);
483 return;
486 if(r->ifcall.count > srv->msize - IOHDRSZ)
487 r->ifcall.count = srv->msize - IOHDRSZ;
488 r->rbuf = emalloc9p(r->ifcall.count);
489 r->ofcall.data = r->rbuf;
490 o = r->fid->omode & 3;
491 if(o != OREAD && o != ORDWR && o != OEXEC){
492 respond(r, Ebotch);
493 return;
495 if((r->fid->qid.type&QTDIR) && r->fid->file){
496 r->ofcall.count = readdirfile(r->fid->rdir, r->rbuf, r->ifcall.count);
497 respond(r, nil);
498 return;
500 if(srv->read)
501 srv->read(r);
502 else
503 respond(r, "no srv->read");
505 static void
506 rread(Req *r, char *error)
508 if(error==nil && (r->fid->qid.type&QTDIR))
509 r->fid->diroffset += r->ofcall.count;
512 static void
513 swrite(Srv *srv, Req *r)
515 int o;
516 char e[ERRMAX];
518 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
519 respond(r, Eunknownfid);
520 return;
522 if(r->ifcall.count < 0){
523 respond(r, Ebotch);
524 return;
526 if(r->ifcall.offset < 0){
527 respond(r, Ebotch);
528 return;
530 if(r->ifcall.count > srv->msize - IOHDRSZ)
531 r->ifcall.count = srv->msize - IOHDRSZ;
532 o = r->fid->omode & 3;
533 if(o != OWRITE && o != ORDWR){
534 snprint(e, sizeof e, "write on fid with open mode 0x%ux", r->fid->omode);
535 respond(r, e);
536 return;
538 if(srv->write){
539 r->ifcall.data[r->ifcall.count] = 0; /* enough room - see getreq */
540 srv->write(r);
541 }else
542 respond(r, "no srv->write");
544 static void
545 rwrite(Req *r, char *error)
547 if(error)
548 return;
549 if(r->fid->file)
550 r->fid->file->dir.qid.vers++;
553 static void
554 sclunk(Srv *srv, Req *r)
556 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil)
557 respond(r, Eunknownfid);
558 else
559 respond(r, nil);
561 static void
562 rclunk(Req *r, char *msg)
564 USED(r);
565 USED(msg);
568 static void
569 sremove(Srv *srv, Req *r)
571 if((r->fid = removefid(srv->fpool, r->ifcall.fid)) == nil){
572 respond(r, Eunknownfid);
573 return;
575 /* BUG RACE */
576 if(r->fid->file && !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
577 respond(r, Eperm);
578 return;
580 if(srv->remove)
581 srv->remove(r);
582 else
583 respond(r, r->fid->file ? nil : Enoremove);
585 static void
586 rremove(Req *r, char *error, char *errbuf)
588 if(error)
589 return;
590 if(r->fid->file){
591 if(removefile(r->fid->file) < 0){
592 snprint(errbuf, ERRMAX, "remove %s: %r",
593 r->fid->file->dir.name);
594 r->error = errbuf;
596 r->fid->file = nil;
600 static void
601 sstat(Srv *srv, Req *r)
603 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
604 respond(r, Eunknownfid);
605 return;
607 if(r->fid->file){
608 r->d = r->fid->file->dir;
609 if(r->d.name)
610 r->d.name = estrdup9p(r->d.name);
611 if(r->d.uid)
612 r->d.uid = estrdup9p(r->d.uid);
613 if(r->d.gid)
614 r->d.gid = estrdup9p(r->d.gid);
615 if(r->d.muid)
616 r->d.muid = estrdup9p(r->d.muid);
618 if(srv->stat)
619 srv->stat(r);
620 else if(r->fid->file)
621 respond(r, nil);
622 else
623 respond(r, Enostat);
625 static void
626 rstat(Req *r, char *error)
628 int n;
629 uchar *statbuf;
630 uchar tmp[BIT16SZ];
632 if(error)
633 return;
634 if(convD2Mu(&r->d, tmp, BIT16SZ, r->srv->dotu) != BIT16SZ){
635 r->error = "convD2Mu(_,_,BIT16SZ,_) did not return BIT16SZ";
636 return;
638 n = GBIT16(tmp)+BIT16SZ;
639 statbuf = emalloc9p(n);
640 if(statbuf == nil){
641 r->error = "out of memory";
642 return;
644 r->ofcall.nstat = convD2Mu(&r->d, statbuf, n, r->srv->dotu);
645 r->ofcall.stat = statbuf; /* freed in closereq */
646 if(r->ofcall.nstat <= BIT16SZ){
647 r->error = "convD2Mu fails";
648 free(statbuf);
649 return;
653 static void
654 swstat(Srv *srv, Req *r)
656 if((r->fid = lookupfid(srv->fpool, r->ifcall.fid)) == nil){
657 respond(r, Eunknownfid);
658 return;
660 if(srv->wstat == nil){
661 respond(r, Enowstat);
662 return;
664 if(convM2Du(r->ifcall.stat, r->ifcall.nstat, &r->d, (char*)r->ifcall.stat, srv->dotu) != r->ifcall.nstat){
665 respond(r, Ebaddir);
666 return;
668 if((ushort)~r->d.type){
669 respond(r, "wstat -- attempt to change type");
670 return;
672 if((uint)~r->d.dev){
673 respond(r, "wstat -- attempt to change dev");
674 return;
676 if((uchar)~r->d.qid.type || (ulong)~r->d.qid.vers || (uvlong)~r->d.qid.path){
677 respond(r, "wstat -- attempt to change qid");
678 return;
680 if(r->d.muid && r->d.muid[0]){
681 respond(r, "wstat -- attempt to change muid");
682 return;
684 if((ulong)~r->d.mode && ((r->d.mode&DMDIR)>>24) != (r->fid->qid.type&QTDIR)){
685 respond(r, "wstat -- attempt to change DMDIR bit");
686 return;
688 srv->wstat(r);
690 static void
691 rwstat(Req *r, char *msg)
693 USED(r);
694 USED(msg);
697 void
698 srv(Srv *srv)
700 Req *r;
702 fmtinstall('D', dirfmt);
703 fmtinstall('F', fcallfmt);
705 if(srv->fpool == nil)
706 srv->fpool = allocfidpool(srv->destroyfid);
707 if(srv->rpool == nil)
708 srv->rpool = allocreqpool(srv->destroyreq);
709 if(srv->msize == 0)
710 srv->msize = 8192+IOHDRSZ;
712 changemsize(srv, srv->msize);
714 srv->fpool->srv = srv;
715 srv->rpool->srv = srv;
717 if(srv->start)
718 srv->start(srv);
720 while(r = getreq(srv)){
721 if(r->error){
722 respond(r, r->error);
723 continue;
725 switch(r->ifcall.type){
726 default:
727 respond(r, "unknown message");
728 break;
729 case Tversion: sversion(srv, r); break;
730 case Tauth: sauth(srv, r); break;
731 case Tattach: sattach(srv, r); break;
732 case Tflush: sflush(srv, r); break;
733 case Twalk: swalk(srv, r); break;
734 case Topen: sopen(srv, r); break;
735 case Tcreate: screate(srv, r); break;
736 case Tread: sread(srv, r); break;
737 case Twrite: swrite(srv, r); break;
738 case Tclunk: sclunk(srv, r); break;
739 case Tremove: sremove(srv, r); break;
740 case Tstat: sstat(srv, r); break;
741 case Twstat: swstat(srv, r); break;
745 if(srv->end)
746 srv->end(srv);
749 void
750 respond(Req *r, char *error)
752 int i, m, n;
753 char errbuf[ERRMAX];
754 Srv *srv;
756 srv = r->srv;
757 assert(srv != nil);
759 if(r->responded){
760 assert(r->pool);
761 goto free;
764 assert(r->responded == 0);
765 r->error = error;
767 switch(r->ifcall.type){
768 default:
769 assert(0);
770 /*
771 * Flush is special. If the handler says so, we return
772 * without further processing. Respond will be called
773 * again once it is safe.
774 */
775 case Tflush:
776 if(rflush(r, error)<0)
777 return;
778 break;
779 case Tversion: rversion(r, error); break;
780 case Tauth: rauth(r, error); break;
781 case Tattach: rattach(r, error); break;
782 case Twalk: rwalk(r, error); break;
783 case Topen: ropen(r, error); break;
784 case Tcreate: rcreate(r, error); break;
785 case Tread: rread(r, error); break;
786 case Twrite: rwrite(r, error); break;
787 case Tclunk: rclunk(r, error); break;
788 case Tremove: rremove(r, error, errbuf); break;
789 case Tstat: rstat(r, error); break;
790 case Twstat: rwstat(r, error); break;
793 r->ofcall.tag = r->ifcall.tag;
794 r->ofcall.type = r->ifcall.type+1;
795 if(r->error)
796 setfcallerror(&r->ofcall, r->error);
798 if(chatty9p)
799 fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall);
801 qlock(&srv->wlock);
802 n = convS2Mu(&r->ofcall, srv->wbuf, srv->msize, srv->dotu);
803 if(n <= 0){
804 fprint(2, "n = %d %F\n", n, &r->ofcall);
805 abort();
807 assert(n > 2);
808 /*
809 * There is a race here - we must remove the entry before
810 * the write, so that if the client is very fast and reuses the
811 * tag, the read loop won't think it is still in use.
813 * By removing the entry before the write, we open up a
814 * race with incoming Tflush messages. Specifically, an
815 * incoming Tflush might not see r even though it has not
816 * yet been responded to. It would then send an Rflush
817 * immediately, potentially before we do the write. This can't
818 * happen because we already old srv->wlock, so nothing
819 * is going out on the wire before this write.
820 */
821 if(r->pool) /* not a fake */
822 closereq(removereq(r->pool, r->ifcall.tag));
824 qlock(&r->lk);
825 r->responded = 1;
826 if(r->pool)
827 if(r->ref.ref == 1+r->nflush)
828 if(r->fid){
829 /*
830 * There are no references other than in our r->flush array,
831 * so no one else should be accessing r concurrently.
832 * Close the fid now, before responding to the message.
834 * If the client is behaving (there are no outstanding T-messages
835 * that reference r->fid) and the message is a Tclunk or Tremove,
836 * then this closefid will call destroyfid.
838 * This means destroyfid can't piddle around
839 * indefinitely (we're holding srv->wlock!), but it provides
840 * for tighter semantics as to when destroyfid is called.
842 * LANL has observed cases where waiting until after the write
843 * can delay a closefid on a Twrite for many 9P transactions,
844 * so that a handful of transactions can happen including a Tclunk
845 * and a Topen, and the original fid will still not be destroyed.
846 */
847 closefid(r->fid);
848 r->fid = nil;
850 qunlock(&r->lk);
851 m = write(srv->outfd, srv->wbuf, n);
852 if(m != n)
853 sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd);
854 qunlock(&srv->wlock);
856 free:
857 qlock(&r->lk); /* no one will add flushes now */
859 for(i=0; i<r->nflush; i++){
860 r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */
861 respond(r->flush[i], nil);
863 free(r->flush);
864 r->nflush = 0;
865 r->flush = nil;
866 qunlock(&r->lk);
868 if(r->pool)
869 closereq(r);
870 else
871 free(r);
874 int
875 postfd(char *name, int pfd)
877 int fd;
878 char buf[80];
880 snprint(buf, sizeof buf, "/srv/%s", name);
881 if(chatty9p)
882 fprint(2, "postfd %s\n", buf);
883 fd = create(buf, OWRITE|ORCLOSE|OCEXEC, 0600);
884 if(fd < 0){
885 if(chatty9p)
886 fprint(2, "create fails: %r\n");
887 return -1;
889 if(fprint(fd, "%d", pfd) < 0){
890 if(chatty9p)
891 fprint(2, "write fails: %r\n");
892 close(fd);
893 return -1;
895 if(chatty9p)
896 fprint(2, "postfd successful\n");
897 return 0;