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 #define clock plumbclock /* SunOS name clash */
101 static int clock;
102 static Fid *fids[Nhash];
103 static QLock readlock;
104 static QLock queue;
105 static int messagesize = 8192+IOHDRSZ; /* good start */
107 static void fsysproc(void*);
108 static void fsysrespond(Fcall*, uchar*, char*);
109 static Fid* newfid(int);
111 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
112 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
113 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
114 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
115 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
116 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
117 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
118 static Fcall* fsysread(Fcall*, uchar*, Fid*);
119 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
120 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
121 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
122 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
123 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
125 Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*);
127 static void
128 initfcall(void)
130 fcall[Tflush] = fsysflush;
131 fcall[Tversion] = fsysversion;
132 fcall[Tauth] = fsysauth;
133 fcall[Tattach] = fsysattach;
134 fcall[Twalk] = fsyswalk;
135 fcall[Topen] = fsysopen;
136 fcall[Tcreate] = fsyscreate;
137 fcall[Tread] = fsysread;
138 fcall[Twrite] = fsyswrite;
139 fcall[Tclunk] = fsysclunk;
140 fcall[Tremove]= fsysremove;
141 fcall[Tstat] = fsysstat;
142 fcall[Twstat] = fsyswstat;
145 char Ebadfcall[] = "bad fcall type";
146 char Eperm[] = "permission denied";
147 char Enomem[] = "malloc failed for buffer";
148 char Enotdir[] = "not a directory";
149 char Enoexist[] = "plumb file does not exist";
150 char Eisdir[] = "file is a directory";
151 char Ebadmsg[] = "bad plumb message format";
152 char Enosuchport[] ="no such plumb port";
153 char Enoport[] = "couldn't find destination for message";
154 char Einuse[] = "file already open";
156 /*
157 * Add new port. A no-op if port already exists or is the null string
158 */
159 void
160 addport(char *port)
162 int i;
164 if(port == nil)
165 return;
166 for(i=NQID; i<ndir; i++)
167 if(strcmp(port, dir[i].name) == 0)
168 return;
169 if(i == NDIR){
170 fprint(2, "plumb: too many ports; max %d\n", NDIR);
171 return;
173 ndir++;
174 dir[i].name = estrdup(port);
175 dir[i].qid = i;
176 dir[i].perm = 0400;
177 nports++;
178 ports = erealloc(ports, nports*sizeof(char*));
179 ports[nports-1] = dir[i].name;
182 static ulong
183 getclock(void)
185 return time(0);
188 void
189 startfsys(int foreground)
191 int p[2];
193 fmtinstall('F', fcallfmt);
194 clock = getclock();
195 if(pipe(p) < 0)
196 error("can't create pipe: %r");
197 /* 0 will be server end, 1 will be client end */
198 srvfd = p[0];
199 if(post9pservice(p[1], "plumb", nil) < 0)
200 sysfatal("post9pservice plumb: %r");
201 close(p[1]);
202 if(foreground)
203 fsysproc(nil);
204 else
205 proccreate(fsysproc, nil, Stack);
208 static void
209 fsysproc(void *v)
211 int n;
212 Fcall *t;
213 Fid *f;
214 uchar *buf;
216 USED(v);
217 initfcall();
218 t = nil;
219 for(;;){
220 buf = malloc(messagesize); /* avoid memset of emalloc */
221 if(buf == nil)
222 error("malloc failed: %r");
223 qlock(&readlock);
224 n = read9pmsg(srvfd, buf, messagesize);
225 if(n <= 0){
226 if(n < 0)
227 error("i/o error on server channel");
228 threadexitsall("unmounted");
230 /*
231 * can give false positive (create an extra fsysproc) once in a while,
232 * but no false negatives, so good enough. once we have one extra
233 * we'll never have more.
234 */
235 if(readlock.waiting.head == nil) /* no other processes waiting to read; start one */
236 proccreate(fsysproc, nil, Stack);
237 qunlock(&readlock);
238 if(t == nil)
239 t = emalloc(sizeof(Fcall));
240 if(convM2S(buf, n, t) != n)
241 error("convert error in convM2S");
242 if(debug)
243 fprint(2, "<= %F\n", t);
244 if(fcall[t->type] == 0)
245 fsysrespond(t, buf, Ebadfcall);
246 else{
247 if(t->type==Tversion || t->type==Tauth)
248 f = nil;
249 else
250 f = newfid(t->fid);
251 t = (*fcall[t->type])(t, buf, f);
256 static void
257 fsysrespond(Fcall *t, uchar *buf, char *err)
259 int n;
261 if(err){
262 t->type = Rerror;
263 t->ename = err;
264 }else
265 t->type++;
266 if(buf == nil)
267 buf = emalloc(messagesize);
268 n = convS2M(t, buf, messagesize);
269 if(n < 0)
270 error("convert error in convS2M");
271 if(write(srvfd, buf, n) != n)
272 error("write error in respond");
273 if(debug)
274 fprint(2, "=> %F\n", t);
275 free(buf);
278 static
279 Fid*
280 newfid(int fid)
282 Fid *f, *ff, **fh;
284 qlock(&queue);
285 ff = nil;
286 fh = &fids[fid&(Nhash-1)];
287 for(f=*fh; f; f=f->next)
288 if(f->fid == fid)
289 goto Return;
290 else if(ff==nil && !f->busy)
291 ff = f;
292 if(ff){
293 ff->fid = fid;
294 f = ff;
295 goto Return;
297 f = emalloc(sizeof *f);
298 f->fid = fid;
299 f->next = *fh;
300 *fh = f;
301 Return:
302 qunlock(&queue);
303 return f;
306 static uint
307 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
309 Dir d;
311 d.qid.type = dir->type;
312 d.qid.path = dir->qid;
313 d.qid.vers = 0;
314 d.mode = dir->perm;
315 d.length = 0; /* would be nice to do better */
316 d.name = dir->name;
317 d.uid = user;
318 d.gid = user;
319 d.muid = user;
320 d.atime = clock;
321 d.mtime = clock;
322 return convD2M(&d, buf, nbuf);
325 static void
326 queuesend(Dirtab *d, Plumbmsg *m)
328 Sendreq *s, *t;
329 Fid *f;
330 int i;
332 s = emalloc(sizeof(Sendreq));
333 s->nfid = d->nopen;
334 s->nleft = s->nfid;
335 s->fid = emalloc(s->nfid*sizeof(Fid*));
336 i = 0;
337 /* build array of fids open on this channel */
338 for(f=d->fopen; f!=nil; f=f->nextopen)
339 s->fid[i++] = f;
340 s->msg = m;
341 s->next = nil;
342 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
343 for(t=d->sendq; t!=nil; t=t->next)
344 if(t->next == nil)
345 break;
346 if(t == nil)
347 d->sendq = s;
348 else
349 t->next = s;
352 static void
353 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
355 Readreq *r;
357 r = emalloc(sizeof(Readreq));
358 r->fcall = t;
359 r->buf = buf;
360 r->fid = f;
361 r->next = d->readq;
362 d->readq = r;
365 static void
366 drainqueue(Dirtab *d)
368 Readreq *r, *nextr, *prevr;
369 Sendreq *s, *nexts, *prevs;
370 int i, n;
372 prevs = nil;
373 for(s=d->sendq; s!=nil; s=nexts){
374 nexts = s->next;
375 for(i=0; i<s->nfid; i++){
376 prevr = nil;
377 for(r=d->readq; r!=nil; r=nextr){
378 nextr = r->next;
379 if(r->fid == s->fid[i]){
380 /* pack the message if necessary */
381 if(s->pack == nil)
382 s->pack = plumbpack(s->msg, &s->npack);
383 /* exchange the stuff... */
384 r->fcall->data = s->pack+r->fid->offset;
385 n = s->npack - r->fid->offset;
386 if(n > messagesize-IOHDRSZ)
387 n = messagesize-IOHDRSZ;
388 if(n > r->fcall->count)
389 n = r->fcall->count;
390 r->fcall->count = n;
391 fsysrespond(r->fcall, r->buf, nil);
392 r->fid->offset += n;
393 if(r->fid->offset >= s->npack){
394 /* message transferred; delete this fid from send queue */
395 r->fid->offset = 0;
396 s->fid[i] = nil;
397 s->nleft--;
399 /* delete read request from queue */
400 if(prevr)
401 prevr->next = r->next;
402 else
403 d->readq = r->next;
404 free(r->fcall);
405 free(r);
406 break;
407 }else
408 prevr = r;
411 /* if no fids left, delete this send from queue */
412 if(s->nleft == 0){
413 free(s->fid);
414 plumbfree(s->msg);
415 free(s->pack);
416 if(prevs)
417 prevs->next = s->next;
418 else
419 d->sendq = s->next;
420 free(s);
421 }else
422 prevs = s;
426 /* can't flush a send because they are always answered synchronously */
427 static void
428 flushqueue(Dirtab *d, int oldtag)
430 Readreq *r, *prevr;
432 prevr = nil;
433 for(r=d->readq; r!=nil; r=r->next){
434 if(oldtag == r->fcall->tag){
435 /* delete read request from queue */
436 if(prevr)
437 prevr->next = r->next;
438 else
439 d->readq = r->next;
440 free(r->fcall);
441 free(r->buf);
442 free(r);
443 return;
445 prevr = r;
449 /* remove messages awaiting delivery to now-closing fid */
450 static void
451 removesenders(Dirtab *d, Fid *fid)
453 Sendreq *s, *nexts, *prevs;
454 int i;
456 prevs = nil;
457 for(s=d->sendq; s!=nil; s=nexts){
458 nexts = s->next;
459 for(i=0; i<s->nfid; i++)
460 if(fid == s->fid[i]){
461 /* delete this fid from send queue */
462 s->fid[i] = nil;
463 s->nleft--;
464 break;
466 /* if no fids left, delete this send from queue */
467 if(s->nleft == 0){
468 free(s->fid);
469 plumbfree(s->msg);
470 free(s->pack);
471 if(prevs)
472 prevs->next = s->next;
473 else
474 d->sendq = s->next;
475 free(s);
476 }else
477 prevs = s;
481 static void
482 hold(Plumbmsg *m, Dirtab *d)
484 Holdq *h, *q;
486 h = emalloc(sizeof(Holdq));
487 h->msg = m;
488 /* add to end of queue */
489 if(d->holdq == nil)
490 d->holdq = h;
491 else{
492 for(q=d->holdq; q->next!=nil; q=q->next)
494 q->next = h;
498 static void
499 queueheld(Dirtab *d)
501 Holdq *h;
503 while(d->holdq != nil){
504 h = d->holdq;
505 d->holdq = h->next;
506 queuesend(d, h->msg);
507 /* no need to drain queue because we know no-one is reading yet */
508 free(h);
512 static void
513 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
515 int i;
516 char *err;
518 qlock(&queue);
519 err = nil;
520 if(m->dst==nil || m->dst[0]=='\0'){
521 err = Enoport;
522 if(rs != nil)
523 err = startup(rs, e);
524 plumbfree(m);
525 }else
526 for(i=NQID; i<ndir; i++)
527 if(strcmp(m->dst, dir[i].name) == 0){
528 if(dir[i].nopen == 0){
529 err = startup(rs, e);
530 if(e!=nil && e->holdforclient)
531 hold(m, &dir[i]);
532 else
533 plumbfree(m);
534 }else{
535 queuesend(&dir[i], m);
536 drainqueue(&dir[i]);
538 break;
540 freeexec(e);
541 qunlock(&queue);
542 fsysrespond(t, buf, err);
543 free(t);
546 static Fcall*
547 fsysversion(Fcall *t, uchar *buf, Fid *fid)
549 USED(fid);
551 if(t->msize < 256){
552 fsysrespond(t, buf, "version: message size too small");
553 return t;
555 if(t->msize < messagesize)
556 messagesize = t->msize;
557 t->msize = messagesize;
558 if(strncmp(t->version, "9P2000", 6) != 0){
559 fsysrespond(t, buf, "unrecognized 9P version");
560 return t;
562 t->version = "9P2000";
563 fsysrespond(t, buf, nil);
564 return t;
567 static Fcall*
568 fsysauth(Fcall *t, uchar *buf, Fid *fid)
570 USED(fid);
571 fsysrespond(t, buf, "plumber: authentication not required");
572 return t;
575 static Fcall*
576 fsysattach(Fcall *t, uchar *buf, Fid *f)
578 Fcall out;
580 /*
581 if(strcmp(t->uname, user) != 0){
582 fsysrespond(&out, buf, Eperm);
583 return t;
585 */
586 f->busy = 1;
587 f->open = 0;
588 f->qid.type = QTDIR;
589 f->qid.path = Qdir;
590 f->qid.vers = 0;
591 f->dir = dir;
592 memset(&out, 0, sizeof(Fcall));
593 out.type = t->type;
594 out.tag = t->tag;
595 out.fid = f->fid;
596 out.qid = f->qid;
597 fsysrespond(&out, buf, nil);
598 return t;
601 static Fcall*
602 fsysflush(Fcall *t, uchar *buf, Fid *fid)
604 int i;
606 USED(fid);
607 qlock(&queue);
608 for(i=NQID; i<ndir; i++)
609 flushqueue(&dir[i], t->oldtag);
610 qunlock(&queue);
611 fsysrespond(t, buf, nil);
612 return t;
615 static Fcall*
616 fsyswalk(Fcall *t, uchar *buf, Fid *f)
618 Fcall out;
619 Fid *nf;
620 ulong path;
621 Dirtab *d, *dir;
622 Qid q;
623 int i;
624 uchar type;
625 char *err;
627 if(f->open){
628 fsysrespond(t, buf, "clone of an open fid");
629 return t;
632 nf = nil;
633 if(t->fid != t->newfid){
634 nf = newfid(t->newfid);
635 if(nf->busy){
636 fsysrespond(t, buf, "clone to a busy fid");
637 return t;
639 nf->busy = 1;
640 nf->open = 0;
641 nf->dir = f->dir;
642 nf->qid = f->qid;
643 f = nf; /* walk f */
646 out.nwqid = 0;
647 err = nil;
648 dir = f->dir;
649 q = f->qid;
651 if(t->nwname > 0){
652 for(i=0; i<t->nwname; i++){
653 if((q.type & QTDIR) == 0){
654 err = Enotdir;
655 break;
657 if(strcmp(t->wname[i], "..") == 0){
658 type = QTDIR;
659 path = Qdir;
660 Accept:
661 q.type = type;
662 q.vers = 0;
663 q.path = path;
664 out.wqid[out.nwqid++] = q;
665 continue;
667 d = dir;
668 d++; /* skip '.' */
669 for(; d->name; d++)
670 if(strcmp(t->wname[i], d->name) == 0){
671 type = d->type;
672 path = d->qid;
673 dir = d;
674 goto Accept;
676 err = Enoexist;
677 break;
681 out.type = t->type;
682 out.tag = t->tag;
683 if(err!=nil || out.nwqid<t->nwname){
684 if(nf)
685 nf->busy = 0;
686 }else if(out.nwqid == t->nwname){
687 f->qid = q;
688 f->dir = dir;
691 fsysrespond(&out, buf, err);
692 return t;
695 static Fcall*
696 fsysopen(Fcall *t, uchar *buf, Fid *f)
698 int m, clearrules, mode;
700 clearrules = 0;
701 if(t->mode & OTRUNC){
702 if(f->qid.path != Qrules)
703 goto Deny;
704 clearrules = 1;
706 /* can't truncate anything, so just disregard */
707 mode = t->mode & ~(OTRUNC|OCEXEC);
708 /* can't execute or remove anything */
709 if(mode==OEXEC || (mode&ORCLOSE))
710 goto Deny;
711 switch(mode){
712 default:
713 goto Deny;
714 case OREAD:
715 m = 0400;
716 break;
717 case OWRITE:
718 m = 0200;
719 break;
720 case ORDWR:
721 m = 0600;
722 break;
724 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
725 goto Deny;
726 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
727 lock(&rulesref.lk);
728 if(rulesref.ref++ != 0){
729 rulesref.ref--;
730 unlock(&rulesref.lk);
731 fsysrespond(t, buf, Einuse);
732 return t;
734 unlock(&rulesref.lk);
736 if(clearrules){
737 writerules(nil, 0);
738 rules[0] = nil;
740 t->qid = f->qid;
741 t->iounit = 0;
742 qlock(&queue);
743 f->mode = mode;
744 f->open = 1;
745 f->dir->nopen++;
746 f->nextopen = f->dir->fopen;
747 f->dir->fopen = f;
748 queueheld(f->dir);
749 qunlock(&queue);
750 fsysrespond(t, buf, nil);
751 return t;
753 Deny:
754 fsysrespond(t, buf, Eperm);
755 return t;
758 static Fcall*
759 fsyscreate(Fcall *t, uchar *buf, Fid *fid)
761 USED(fid);
762 fsysrespond(t, buf, Eperm);
763 return t;
766 static Fcall*
767 fsysreadrules(Fcall *t, uchar *buf)
769 char *p;
770 int n;
772 p = printrules();
773 n = strlen(p);
774 t->data = p;
775 if(t->offset >= n)
776 t->count = 0;
777 else{
778 t->data = p+t->offset;
779 if(t->offset+t->count > n)
780 t->count = n-t->offset;
782 fsysrespond(t, buf, nil);
783 free(p);
784 return t;
787 static Fcall*
788 fsysread(Fcall *t, uchar *buf, Fid *f)
790 uchar *b;
791 int i, n, o, e;
792 uint len;
793 Dirtab *d;
794 uint clock;
796 if(f->qid.path != Qdir){
797 if(f->qid.path == Qrules)
798 return fsysreadrules(t, buf);
799 /* read from port */
800 if(f->qid.path < NQID){
801 fsysrespond(t, buf, "internal error: unknown read port");
802 return t;
804 qlock(&queue);
805 queueread(f->dir, t, buf, f);
806 drainqueue(f->dir);
807 qunlock(&queue);
808 return nil;
810 o = t->offset;
811 e = t->offset+t->count;
812 clock = getclock();
813 b = malloc(messagesize-IOHDRSZ);
814 if(b == nil){
815 fsysrespond(t, buf, Enomem);
816 return t;
818 n = 0;
819 d = dir;
820 d++; /* first entry is '.' */
821 for(i=0; d->name!=nil && i<e; i+=len){
822 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
823 if(len <= BIT16SZ)
824 break;
825 if(i >= o)
826 n += len;
827 d++;
829 t->data = (char*)b;
830 t->count = n;
831 fsysrespond(t, buf, nil);
832 free(b);
833 return t;
836 static Fcall*
837 fsyswrite(Fcall *t, uchar *buf, Fid *f)
839 Plumbmsg *m;
840 int i, n;
841 long count;
842 char *data;
843 Exec *e;
845 switch((int)f->qid.path){
846 case Qdir:
847 fsysrespond(t, buf, Eisdir);
848 return t;
849 case Qrules:
850 clock = getclock();
851 fsysrespond(t, buf, writerules(t->data, t->count));
852 return t;
853 case Qsend:
854 if(f->offset == 0){
855 data = t->data;
856 count = t->count;
857 }else{
858 /* partial message already assembled */
859 f->writebuf = erealloc(f->writebuf, f->offset + t->count);
860 memmove(f->writebuf+f->offset, t->data, t->count);
861 data = f->writebuf;
862 count = f->offset+t->count;
864 m = plumbunpackpartial(data, count, &n);
865 if(m == nil){
866 if(n == 0){
867 f->offset = 0;
868 free(f->writebuf);
869 f->writebuf = nil;
870 fsysrespond(t, buf, Ebadmsg);
871 return t;
873 /* can read more... */
874 if(f->offset == 0){
875 f->writebuf = emalloc(t->count);
876 memmove(f->writebuf, t->data, t->count);
878 /* else buffer has already been grown */
879 f->offset += t->count;
880 fsysrespond(t, buf, nil);
881 return t;
883 /* release partial buffer */
884 f->offset = 0;
885 free(f->writebuf);
886 f->writebuf = nil;
887 for(i=0; rules[i]; i++)
888 if((e=matchruleset(m, rules[i])) != nil){
889 dispose(t, buf, m, rules[i], e);
890 return nil;
892 if(m->dst != nil){
893 dispose(t, buf, m, nil, nil);
894 return nil;
896 fsysrespond(t, buf, "no matching plumb rule");
897 return t;
899 fsysrespond(t, buf, "internal error: write to unknown file");
900 return t;
903 static Fcall*
904 fsysstat(Fcall *t, uchar *buf, Fid *f)
906 t->stat = emalloc(messagesize-IOHDRSZ);
907 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
908 fsysrespond(t, buf, nil);
909 free(t->stat);
910 t->stat = nil;
911 return t;
914 static Fcall*
915 fsyswstat(Fcall *t, uchar *buf, Fid *fid)
917 USED(fid);
918 fsysrespond(t, buf, Eperm);
919 return t;
922 static Fcall*
923 fsysremove(Fcall *t, uchar *buf, Fid *fid)
925 USED(fid);
926 fsysrespond(t, buf, Eperm);
927 return t;
930 static Fcall*
931 fsysclunk(Fcall *t, uchar *buf, Fid *f)
933 Fid *prev, *p;
934 Dirtab *d;
936 qlock(&queue);
937 if(f->open){
938 d = f->dir;
939 d->nopen--;
940 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
941 /*
942 * just to be sure last rule is parsed; error messages will be lost, though,
943 * unless last write ended with a blank line
944 */
945 writerules(nil, 0);
946 lock(&rulesref.lk);
947 rulesref.ref--;
948 unlock(&rulesref.lk);
950 prev = nil;
951 for(p=d->fopen; p; p=p->nextopen){
952 if(p == f){
953 if(prev)
954 prev->nextopen = f->nextopen;
955 else
956 d->fopen = f->nextopen;
957 removesenders(d, f);
958 break;
960 prev = p;
963 f->busy = 0;
964 f->open = 0;
965 f->offset = 0;
966 if(f->writebuf != nil){
967 free(f->writebuf);
968 f->writebuf = nil;
970 qunlock(&queue);
971 fsysrespond(t, buf, nil);
972 return t;