Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <regexp.h>
5 #include <thread.h>
6 #include <fcall.h>
7 #include <plumb.h>
8 #include "plumber.h"
10 enum
11 {
12 Stack = 32*1024
13 };
15 typedef struct Dirtab Dirtab;
16 typedef struct Fid Fid;
17 typedef struct Holdq Holdq;
18 typedef struct Readreq Readreq;
19 typedef struct Sendreq Sendreq;
21 struct Dirtab
22 {
23 char *name;
24 uchar type;
25 uint qid;
26 uint perm;
27 int nopen; /* #fids open on this port */
28 Fid *fopen;
29 Holdq *holdq;
30 Readreq *readq;
31 Sendreq *sendq;
32 };
34 struct Fid
35 {
36 int fid;
37 int busy;
38 int open;
39 int mode;
40 Qid qid;
41 Dirtab *dir;
42 long offset; /* zeroed at beginning of each message, read or write */
43 char *writebuf; /* partial message written so far; offset tells how much */
44 Fid *next;
45 Fid *nextopen;
46 };
48 struct Readreq
49 {
50 Fid *fid;
51 Fcall *fcall;
52 uchar *buf;
53 Readreq *next;
54 };
56 struct Sendreq
57 {
58 int nfid; /* number of fids that should receive this message */
59 int nleft; /* number left that haven't received it */
60 Fid **fid; /* fid[nfid] */
61 Plumbmsg *msg;
62 char *pack; /* plumbpack()ed message */
63 int npack; /* length of pack */
64 Sendreq *next;
65 };
67 struct Holdq
68 {
69 Plumbmsg *msg;
70 Holdq *next;
71 };
73 struct /* needed because incref() doesn't return value */
74 {
75 Lock lk;
76 int ref;
77 } rulesref;
79 enum
80 {
81 NDIR = 50,
82 Nhash = 16,
84 Qdir = 0,
85 Qrules = 1,
86 Qsend = 2,
87 Qport = 3,
88 NQID = Qport
89 };
91 static Dirtab dir[NDIR] =
92 {
93 { ".", QTDIR, Qdir, 0500|DMDIR },
94 { "rules", QTFILE, Qrules, 0600 },
95 { "send", QTFILE, Qsend, 0200 },
96 };
97 static int ndir = NQID;
99 static int srvfd;
100 static int clock;
101 static Fid *fids[Nhash];
102 static QLock readlock;
103 static QLock queue;
104 static int messagesize = 8192+IOHDRSZ; /* good start */
106 static void fsysproc(void*);
107 static void fsysrespond(Fcall*, uchar*, char*);
108 static Fid* newfid(int);
110 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
111 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
112 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
113 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
114 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
115 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
116 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
117 static Fcall* fsysread(Fcall*, uchar*, Fid*);
118 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
119 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
120 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
121 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
122 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
124 Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) =
126 [Tflush] = fsysflush,
127 [Tversion] = fsysversion,
128 [Tauth] = fsysauth,
129 [Tattach] = fsysattach,
130 [Twalk] = fsyswalk,
131 [Topen] = fsysopen,
132 [Tcreate] = fsyscreate,
133 [Tread] = fsysread,
134 [Twrite] = fsyswrite,
135 [Tclunk] = fsysclunk,
136 [Tremove]= fsysremove,
137 [Tstat] = fsysstat,
138 [Twstat] = fsyswstat,
139 };
141 char Ebadfcall[] = "bad fcall type";
142 char Eperm[] = "permission denied";
143 char Enomem[] = "malloc failed for buffer";
144 char Enotdir[] = "not a directory";
145 char Enoexist[] = "plumb file does not exist";
146 char Eisdir[] = "file is a directory";
147 char Ebadmsg[] = "bad plumb message format";
148 char Enosuchport[] ="no such plumb port";
149 char Enoport[] = "couldn't find destination for message";
150 char Einuse[] = "file already open";
152 /*
153 * Add new port. A no-op if port already exists or is the null string
154 */
155 void
156 addport(char *port)
158 int i;
160 if(port == nil)
161 return;
162 for(i=NQID; i<ndir; i++)
163 if(strcmp(port, dir[i].name) == 0)
164 return;
165 if(i == NDIR){
166 fprint(2, "plumb: too many ports; max %d\n", NDIR);
167 return;
169 ndir++;
170 dir[i].name = estrdup(port);
171 dir[i].qid = i;
172 dir[i].perm = 0400;
173 nports++;
174 ports = erealloc(ports, nports*sizeof(char*));
175 ports[nports-1] = dir[i].name;
178 static ulong
179 getclock(void)
181 return time(0);
184 void
185 startfsys(void)
187 int p[2];
189 fmtinstall('F', fcallfmt);
190 clock = getclock();
191 if(pipe(p) < 0)
192 error("can't create pipe: %r");
193 /* 0 will be server end, 1 will be client end */
194 srvfd = p[0];
195 if(post9pservice(p[1], "plumb") < 0)
196 sysfatal("post9pservice plumb: %r");
197 close(p[1]);
198 threadcreate(fsysproc, nil, Stack);
201 static void
202 fsysproc(void *v)
204 int n;
205 Fcall *t;
206 Fid *f;
207 uchar *buf;
209 USED(v);
210 t = nil;
211 for(;;){
212 buf = malloc(messagesize); /* avoid memset of emalloc */
213 if(buf == nil)
214 error("malloc failed: %r");
215 qlock(&readlock);
216 n = threadread9pmsg(srvfd, buf, messagesize);
217 if(n <= 0){
218 if(n < 0)
219 error("i/o error on server channel");
220 threadexitsall("unmounted");
222 if(readlock.head == nil) /* no other processes waiting to read; start one */
223 threadcreate(fsysproc, nil, Stack);
224 qunlock(&readlock);
225 if(t == nil)
226 t = emalloc(sizeof(Fcall));
227 if(convM2S(buf, n, t) != n)
228 error("convert error in convM2S");
229 if(debug)
230 fprint(2, "<= %F\n", t);
231 if(fcall[t->type] == nil)
232 fsysrespond(t, buf, Ebadfcall);
233 else{
234 if(t->type==Tversion || t->type==Tauth)
235 f = nil;
236 else
237 f = newfid(t->fid);
238 t = (*fcall[t->type])(t, buf, f);
243 static void
244 fsysrespond(Fcall *t, uchar *buf, char *err)
246 int n;
248 if(err){
249 t->type = Rerror;
250 t->ename = err;
251 }else
252 t->type++;
253 if(buf == nil)
254 buf = emalloc(messagesize);
255 n = convS2M(t, buf, messagesize);
256 if(n < 0)
257 error("convert error in convS2M");
258 if(write(srvfd, buf, n) != n)
259 error("write error in respond");
260 if(debug)
261 fprint(2, "=> %F\n", t);
262 free(buf);
265 static
266 Fid*
267 newfid(int fid)
269 Fid *f, *ff, **fh;
271 qlock(&queue);
272 ff = nil;
273 fh = &fids[fid&(Nhash-1)];
274 for(f=*fh; f; f=f->next)
275 if(f->fid == fid)
276 goto Return;
277 else if(ff==nil && !f->busy)
278 ff = f;
279 if(ff){
280 ff->fid = fid;
281 f = ff;
282 goto Return;
284 f = emalloc(sizeof *f);
285 f->fid = fid;
286 f->next = *fh;
287 *fh = f;
288 Return:
289 qunlock(&queue);
290 return f;
293 static uint
294 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
296 Dir d;
298 d.qid.type = dir->type;
299 d.qid.path = dir->qid;
300 d.qid.vers = 0;
301 d.mode = dir->perm;
302 d.length = 0; /* would be nice to do better */
303 d.name = dir->name;
304 d.uid = user;
305 d.gid = user;
306 d.muid = user;
307 d.atime = clock;
308 d.mtime = clock;
309 return convD2M(&d, buf, nbuf);
312 static void
313 queuesend(Dirtab *d, Plumbmsg *m)
315 Sendreq *s, *t;
316 Fid *f;
317 int i;
319 s = emalloc(sizeof(Sendreq));
320 s->nfid = d->nopen;
321 s->nleft = s->nfid;
322 s->fid = emalloc(s->nfid*sizeof(Fid*));
323 i = 0;
324 /* build array of fids open on this channel */
325 for(f=d->fopen; f!=nil; f=f->nextopen)
326 s->fid[i++] = f;
327 s->msg = m;
328 s->next = nil;
329 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
330 for(t=d->sendq; t!=nil; t=t->next)
331 if(t->next == nil)
332 break;
333 if(t == nil)
334 d->sendq = s;
335 else
336 t->next = s;
339 static void
340 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
342 Readreq *r;
344 r = emalloc(sizeof(Readreq));
345 r->fcall = t;
346 r->buf = buf;
347 r->fid = f;
348 r->next = d->readq;
349 d->readq = r;
352 static void
353 drainqueue(Dirtab *d)
355 Readreq *r, *nextr, *prevr;
356 Sendreq *s, *nexts, *prevs;
357 int i, n;
359 prevs = nil;
360 for(s=d->sendq; s!=nil; s=nexts){
361 nexts = s->next;
362 for(i=0; i<s->nfid; i++){
363 prevr = nil;
364 for(r=d->readq; r!=nil; r=nextr){
365 nextr = r->next;
366 if(r->fid == s->fid[i]){
367 /* pack the message if necessary */
368 if(s->pack == nil)
369 s->pack = plumbpack(s->msg, &s->npack);
370 /* exchange the stuff... */
371 r->fcall->data = s->pack+r->fid->offset;
372 n = s->npack - r->fid->offset;
373 if(n > messagesize-IOHDRSZ)
374 n = messagesize-IOHDRSZ;
375 if(n > r->fcall->count)
376 n = r->fcall->count;
377 r->fcall->count = n;
378 fsysrespond(r->fcall, r->buf, nil);
379 r->fid->offset += n;
380 if(r->fid->offset >= s->npack){
381 /* message transferred; delete this fid from send queue */
382 r->fid->offset = 0;
383 s->fid[i] = nil;
384 s->nleft--;
386 /* delete read request from queue */
387 if(prevr)
388 prevr->next = r->next;
389 else
390 d->readq = r->next;
391 free(r->fcall);
392 free(r);
393 break;
394 }else
395 prevr = r;
398 /* if no fids left, delete this send from queue */
399 if(s->nleft == 0){
400 free(s->fid);
401 plumbfree(s->msg);
402 free(s->pack);
403 if(prevs)
404 prevs->next = s->next;
405 else
406 d->sendq = s->next;
407 free(s);
408 }else
409 prevs = s;
413 /* can't flush a send because they are always answered synchronously */
414 static void
415 flushqueue(Dirtab *d, int oldtag)
417 Readreq *r, *prevr;
419 prevr = nil;
420 for(r=d->readq; r!=nil; r=r->next){
421 if(oldtag == r->fcall->tag){
422 /* delete read request from queue */
423 if(prevr)
424 prevr->next = r->next;
425 else
426 d->readq = r->next;
427 free(r->fcall);
428 free(r->buf);
429 free(r);
430 return;
432 prevr = r;
436 /* remove messages awaiting delivery to now-closing fid */
437 static void
438 removesenders(Dirtab *d, Fid *fid)
440 Sendreq *s, *nexts, *prevs;
441 int i;
443 prevs = nil;
444 for(s=d->sendq; s!=nil; s=nexts){
445 nexts = s->next;
446 for(i=0; i<s->nfid; i++)
447 if(fid == s->fid[i]){
448 /* delete this fid from send queue */
449 s->fid[i] = nil;
450 s->nleft--;
451 break;
453 /* if no fids left, delete this send from queue */
454 if(s->nleft == 0){
455 free(s->fid);
456 plumbfree(s->msg);
457 free(s->pack);
458 if(prevs)
459 prevs->next = s->next;
460 else
461 d->sendq = s->next;
462 free(s);
463 }else
464 prevs = s;
468 static void
469 hold(Plumbmsg *m, Dirtab *d)
471 Holdq *h, *q;
473 h = emalloc(sizeof(Holdq));
474 h->msg = m;
475 /* add to end of queue */
476 if(d->holdq == nil)
477 d->holdq = h;
478 else{
479 for(q=d->holdq; q->next!=nil; q=q->next)
481 q->next = h;
485 static void
486 queueheld(Dirtab *d)
488 Holdq *h;
490 while(d->holdq != nil){
491 h = d->holdq;
492 d->holdq = h->next;
493 queuesend(d, h->msg);
494 /* no need to drain queue because we know no-one is reading yet */
495 free(h);
499 static void
500 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
502 int i;
503 char *err;
505 qlock(&queue);
506 err = nil;
507 if(m->dst==nil || m->dst[0]=='\0'){
508 err = Enoport;
509 if(rs != nil)
510 err = startup(rs, e);
511 plumbfree(m);
512 }else
513 for(i=NQID; i<ndir; i++)
514 if(strcmp(m->dst, dir[i].name) == 0){
515 if(dir[i].nopen == 0){
516 err = startup(rs, e);
517 if(e!=nil && e->holdforclient)
518 hold(m, &dir[i]);
519 else
520 plumbfree(m);
521 }else{
522 queuesend(&dir[i], m);
523 drainqueue(&dir[i]);
525 break;
527 freeexec(e);
528 qunlock(&queue);
529 fsysrespond(t, buf, err);
530 free(t);
533 static Fcall*
534 fsysversion(Fcall *t, uchar *buf, Fid *fid)
536 USED(fid);
538 if(t->msize < 256){
539 fsysrespond(t, buf, "version: message size too small");
540 return t;
542 if(t->msize < messagesize)
543 messagesize = t->msize;
544 t->msize = messagesize;
545 if(strncmp(t->version, "9P2000", 6) != 0){
546 fsysrespond(t, buf, "unrecognized 9P version");
547 return t;
549 t->version = "9P2000";
550 fsysrespond(t, buf, nil);
551 return t;
554 static Fcall*
555 fsysauth(Fcall *t, uchar *buf, Fid *fid)
557 USED(fid);
558 fsysrespond(t, buf, "plumber: authentication not required");
559 return t;
562 static Fcall*
563 fsysattach(Fcall *t, uchar *buf, Fid *f)
565 Fcall out;
567 if(strcmp(t->uname, user) != 0){
568 fsysrespond(&out, buf, Eperm);
569 return t;
571 f->busy = 1;
572 f->open = 0;
573 f->qid.type = QTDIR;
574 f->qid.path = Qdir;
575 f->qid.vers = 0;
576 f->dir = dir;
577 memset(&out, 0, sizeof(Fcall));
578 out.type = t->type;
579 out.tag = t->tag;
580 out.fid = f->fid;
581 out.qid = f->qid;
582 fsysrespond(&out, buf, nil);
583 return t;
586 static Fcall*
587 fsysflush(Fcall *t, uchar *buf, Fid *fid)
589 int i;
591 USED(fid);
592 qlock(&queue);
593 for(i=NQID; i<ndir; i++)
594 flushqueue(&dir[i], t->oldtag);
595 qunlock(&queue);
596 fsysrespond(t, buf, nil);
597 return t;
600 static Fcall*
601 fsyswalk(Fcall *t, uchar *buf, Fid *f)
603 Fcall out;
604 Fid *nf;
605 ulong path;
606 Dirtab *d, *dir;
607 Qid q;
608 int i;
609 uchar type;
610 char *err;
612 if(f->open){
613 fsysrespond(t, buf, "clone of an open fid");
614 return t;
617 nf = nil;
618 if(t->fid != t->newfid){
619 nf = newfid(t->newfid);
620 if(nf->busy){
621 fsysrespond(t, buf, "clone to a busy fid");
622 return t;
624 nf->busy = 1;
625 nf->open = 0;
626 nf->dir = f->dir;
627 nf->qid = f->qid;
628 f = nf; /* walk f */
631 out.nwqid = 0;
632 err = nil;
633 dir = f->dir;
634 q = f->qid;
636 if(t->nwname > 0){
637 for(i=0; i<t->nwname; i++){
638 if((q.type & QTDIR) == 0){
639 err = Enotdir;
640 break;
642 if(strcmp(t->wname[i], "..") == 0){
643 type = QTDIR;
644 path = Qdir;
645 Accept:
646 q.type = type;
647 q.vers = 0;
648 q.path = path;
649 out.wqid[out.nwqid++] = q;
650 continue;
652 d = dir;
653 d++; /* skip '.' */
654 for(; d->name; d++)
655 if(strcmp(t->wname[i], d->name) == 0){
656 type = d->type;
657 path = d->qid;
658 dir = d;
659 goto Accept;
661 err = Enoexist;
662 break;
666 out.type = t->type;
667 out.tag = t->tag;
668 if(err!=nil || out.nwqid<t->nwname){
669 if(nf)
670 nf->busy = 0;
671 }else if(out.nwqid == t->nwname){
672 f->qid = q;
673 f->dir = dir;
676 fsysrespond(&out, buf, err);
677 return t;
680 static Fcall*
681 fsysopen(Fcall *t, uchar *buf, Fid *f)
683 int m, clearrules, mode;
685 clearrules = 0;
686 if(t->mode & OTRUNC){
687 if(f->qid.path != Qrules)
688 goto Deny;
689 clearrules = 1;
691 /* can't truncate anything, so just disregard */
692 mode = t->mode & ~(OTRUNC|OCEXEC);
693 /* can't execute or remove anything */
694 if(mode==OEXEC || (mode&ORCLOSE))
695 goto Deny;
696 switch(mode){
697 default:
698 goto Deny;
699 case OREAD:
700 m = 0400;
701 break;
702 case OWRITE:
703 m = 0200;
704 break;
705 case ORDWR:
706 m = 0600;
707 break;
709 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
710 goto Deny;
711 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
712 lock(&rulesref.lk);
713 if(rulesref.ref++ != 0){
714 rulesref.ref--;
715 unlock(&rulesref.lk);
716 fsysrespond(t, buf, Einuse);
717 return t;
719 unlock(&rulesref.lk);
721 if(clearrules){
722 writerules(nil, 0);
723 rules[0] = nil;
725 t->qid = f->qid;
726 t->iounit = 0;
727 qlock(&queue);
728 f->mode = mode;
729 f->open = 1;
730 f->dir->nopen++;
731 f->nextopen = f->dir->fopen;
732 f->dir->fopen = f;
733 queueheld(f->dir);
734 qunlock(&queue);
735 fsysrespond(t, buf, nil);
736 return t;
738 Deny:
739 fsysrespond(t, buf, Eperm);
740 return t;
743 static Fcall*
744 fsyscreate(Fcall *t, uchar *buf, Fid *fid)
746 USED(fid);
747 fsysrespond(t, buf, Eperm);
748 return t;
751 static Fcall*
752 fsysreadrules(Fcall *t, uchar *buf)
754 char *p;
755 int n;
757 p = printrules();
758 n = strlen(p);
759 t->data = p;
760 if(t->offset >= n)
761 t->count = 0;
762 else{
763 t->data = p+t->offset;
764 if(t->offset+t->count > n)
765 t->count = n-t->offset;
767 fsysrespond(t, buf, nil);
768 free(p);
769 return t;
772 static Fcall*
773 fsysread(Fcall *t, uchar *buf, Fid *f)
775 uchar *b;
776 int i, n, o, e;
777 uint len;
778 Dirtab *d;
779 uint clock;
781 if(f->qid.path != Qdir){
782 if(f->qid.path == Qrules)
783 return fsysreadrules(t, buf);
784 /* read from port */
785 if(f->qid.path < NQID){
786 fsysrespond(t, buf, "internal error: unknown read port");
787 return t;
789 qlock(&queue);
790 queueread(f->dir, t, buf, f);
791 drainqueue(f->dir);
792 qunlock(&queue);
793 return nil;
795 o = t->offset;
796 e = t->offset+t->count;
797 clock = getclock();
798 b = malloc(messagesize-IOHDRSZ);
799 if(b == nil){
800 fsysrespond(t, buf, Enomem);
801 return t;
803 n = 0;
804 d = dir;
805 d++; /* first entry is '.' */
806 for(i=0; d->name!=nil && i<e; i+=len){
807 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
808 if(len <= BIT16SZ)
809 break;
810 if(i >= o)
811 n += len;
812 d++;
814 t->data = (char*)b;
815 t->count = n;
816 fsysrespond(t, buf, nil);
817 free(b);
818 return t;
821 static Fcall*
822 fsyswrite(Fcall *t, uchar *buf, Fid *f)
824 Plumbmsg *m;
825 int i, n;
826 long count;
827 char *data;
828 Exec *e;
830 switch((int)f->qid.path){
831 case Qdir:
832 fsysrespond(t, buf, Eisdir);
833 return t;
834 case Qrules:
835 clock = getclock();
836 fsysrespond(t, buf, writerules(t->data, t->count));
837 return t;
838 case Qsend:
839 if(f->offset == 0){
840 data = t->data;
841 count = t->count;
842 }else{
843 /* partial message already assembled */
844 f->writebuf = erealloc(f->writebuf, f->offset + t->count);
845 memmove(f->writebuf+f->offset, t->data, t->count);
846 data = f->writebuf;
847 count = f->offset+t->count;
849 m = plumbunpackpartial(data, count, &n);
850 if(m == nil){
851 if(n == 0){
852 f->offset = 0;
853 free(f->writebuf);
854 f->writebuf = nil;
855 fsysrespond(t, buf, Ebadmsg);
856 return t;
858 /* can read more... */
859 if(f->offset == 0){
860 f->writebuf = emalloc(t->count);
861 memmove(f->writebuf, t->data, t->count);
863 /* else buffer has already been grown */
864 f->offset += t->count;
865 fsysrespond(t, buf, nil);
866 return t;
868 /* release partial buffer */
869 f->offset = 0;
870 free(f->writebuf);
871 f->writebuf = nil;
872 for(i=0; rules[i]; i++)
873 if((e=matchruleset(m, rules[i])) != nil){
874 dispose(t, buf, m, rules[i], e);
875 return nil;
877 if(m->dst != nil){
878 dispose(t, buf, m, nil, nil);
879 return nil;
881 fsysrespond(t, buf, "no matching plumb rule");
882 return t;
884 fsysrespond(t, buf, "internal error: write to unknown file");
885 return t;
888 static Fcall*
889 fsysstat(Fcall *t, uchar *buf, Fid *f)
891 t->stat = emalloc(messagesize-IOHDRSZ);
892 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
893 fsysrespond(t, buf, nil);
894 free(t->stat);
895 t->stat = nil;
896 return t;
899 static Fcall*
900 fsyswstat(Fcall *t, uchar *buf, Fid *fid)
902 USED(fid);
903 fsysrespond(t, buf, Eperm);
904 return t;
907 static Fcall*
908 fsysremove(Fcall *t, uchar *buf, Fid *fid)
910 USED(fid);
911 fsysrespond(t, buf, Eperm);
912 return t;
915 static Fcall*
916 fsysclunk(Fcall *t, uchar *buf, Fid *f)
918 Fid *prev, *p;
919 Dirtab *d;
921 qlock(&queue);
922 if(f->open){
923 d = f->dir;
924 d->nopen--;
925 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
926 /*
927 * just to be sure last rule is parsed; error messages will be lost, though,
928 * unless last write ended with a blank line
929 */
930 writerules(nil, 0);
931 lock(&rulesref.lk);
932 rulesref.ref--;
933 unlock(&rulesref.lk);
935 prev = nil;
936 for(p=d->fopen; p; p=p->nextopen){
937 if(p == f){
938 if(prev)
939 prev->nextopen = f->nextopen;
940 else
941 d->fopen = f->nextopen;
942 removesenders(d, f);
943 break;
945 prev = p;
948 f->busy = 0;
949 f->open = 0;
950 f->offset = 0;
951 if(f->writebuf != nil){
952 free(f->writebuf);
953 f->writebuf = nil;
955 qunlock(&queue);
956 fsysrespond(t, buf, nil);
957 return t;