Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <regexp.h>
5 #include <thread.h>
6 #include <auth.h>
7 #include <fcall.h>
8 #include <plumb.h>
9 #include "plumber.h"
11 enum
12 {
13 Stack = 8*1024
14 };
16 typedef struct Dirtab Dirtab;
17 typedef struct Fid Fid;
18 typedef struct Holdq Holdq;
19 typedef struct Readreq Readreq;
20 typedef struct Sendreq Sendreq;
22 struct Dirtab
23 {
24 char *name;
25 uchar type;
26 uint qid;
27 uint perm;
28 int nopen; /* #fids open on this port */
29 Fid *fopen;
30 Holdq *holdq;
31 Readreq *readq;
32 Sendreq *sendq;
33 };
35 struct Fid
36 {
37 int fid;
38 int busy;
39 int open;
40 int mode;
41 Qid qid;
42 Dirtab *dir;
43 long offset; /* zeroed at beginning of each message, read or write */
44 char *writebuf; /* partial message written so far; offset tells how much */
45 Fid *next;
46 Fid *nextopen;
47 };
49 struct Readreq
50 {
51 Fid *fid;
52 Fcall *fcall;
53 uchar *buf;
54 Readreq *next;
55 };
57 struct Sendreq
58 {
59 int nfid; /* number of fids that should receive this message */
60 int nleft; /* number left that haven't received it */
61 Fid **fid; /* fid[nfid] */
62 Plumbmsg *msg;
63 char *pack; /* plumbpack()ed message */
64 int npack; /* length of pack */
65 Sendreq *next;
66 };
68 struct Holdq
69 {
70 Plumbmsg *msg;
71 Holdq *next;
72 };
74 struct /* needed because incref() doesn't return value */
75 {
76 Lock;
77 int ref;
78 } rulesref;
80 enum
81 {
82 DEBUG = 0,
83 NDIR = 50,
84 Nhash = 16,
86 Qdir = 0,
87 Qrules = 1,
88 Qsend = 2,
89 Qport = 3,
90 NQID = Qport
91 };
93 static Dirtab dir[NDIR] =
94 {
95 { ".", QTDIR, Qdir, 0500|DMDIR },
96 { "rules", QTFILE, Qrules, 0600 },
97 { "send", QTFILE, Qsend, 0200 },
98 };
99 static int ndir = NQID;
101 static int srvfd;
102 static int srvclosefd; /* rock for end of pipe to close */
103 static int clockfd;
104 static int clock;
105 static Fid *fids[Nhash];
106 static QLock readlock;
107 static QLock queue;
108 static char srvfile[128];
109 static int messagesize = 8192+IOHDRSZ; /* good start */
111 static void fsysproc(void*);
112 static void fsysrespond(Fcall*, uchar*, char*);
113 static Fid* newfid(int);
115 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
116 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
117 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
118 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
119 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
120 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
121 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
122 static Fcall* fsysread(Fcall*, uchar*, Fid*);
123 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
124 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
125 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
126 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
127 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
129 Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) =
131 [Tflush] = fsysflush,
132 [Tversion] = fsysversion,
133 [Tauth] = fsysauth,
134 [Tattach] = fsysattach,
135 [Twalk] = fsyswalk,
136 [Topen] = fsysopen,
137 [Tcreate] = fsyscreate,
138 [Tread] = fsysread,
139 [Twrite] = fsyswrite,
140 [Tclunk] = fsysclunk,
141 [Tremove]= fsysremove,
142 [Tstat] = fsysstat,
143 [Twstat] = fsyswstat,
144 };
146 char Ebadfcall[] = "bad fcall type";
147 char Eperm[] = "permission denied";
148 char Enomem[] = "malloc failed for buffer";
149 char Enotdir[] = "not a directory";
150 char Enoexist[] = "plumb file does not exist";
151 char Eisdir[] = "file is a directory";
152 char Ebadmsg[] = "bad plumb message format";
153 char Enosuchport[] ="no such plumb port";
154 char Enoport[] = "couldn't find destination for message";
155 char Einuse[] = "file already open";
157 /*
158 * Add new port. A no-op if port already exists or is the null string
159 */
160 void
161 addport(char *port)
163 int i;
165 if(port == nil)
166 return;
167 for(i=NQID; i<ndir; i++)
168 if(strcmp(port, dir[i].name) == 0)
169 return;
170 if(i == NDIR){
171 fprint(2, "plumb: too many ports; max %d\n", NDIR);
172 return;
174 ndir++;
175 dir[i].name = estrdup(port);
176 dir[i].qid = i;
177 dir[i].perm = 0400;
178 nports++;
179 ports = erealloc(ports, nports*sizeof(char*));
180 ports[nports-1] = dir[i].name;
183 static ulong
184 getclock(void)
186 char buf[32];
188 seek(clockfd, 0, 0);
189 read(clockfd, buf, sizeof buf);
190 return atoi(buf);
193 void
194 startfsys(void)
196 int p[2], fd;
198 fmtinstall('F', fcallfmt);
199 clockfd = open("/dev/time", OREAD|OCEXEC);
200 clock = getclock();
201 if(pipe(p) < 0)
202 error("can't create pipe: %r");
203 /* 0 will be server end, 1 will be client end */
204 srvfd = p[0];
205 srvclosefd = p[1];
206 sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
207 if(putenv("plumbsrv", srvfile) < 0)
208 error("can't write $plumbsrv: %r");
209 fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
210 if(fd < 0)
211 error("can't create /srv file: %r");
212 if(fprint(fd, "%d", p[1]) <= 0)
213 error("can't write /srv/file: %r");
214 /* leave fd open; ORCLOSE will take care of it */
216 procrfork(fsysproc, nil, Stack, RFFDG);
218 close(p[0]);
219 if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
220 error("can't mount /mnt/plumb: %r");
221 close(p[1]);
224 static void
225 fsysproc(void*)
227 int n;
228 Fcall *t;
229 Fid *f;
230 uchar *buf;
232 close(srvclosefd);
233 srvclosefd = -1;
234 t = nil;
235 for(;;){
236 buf = malloc(messagesize); /* avoid memset of emalloc */
237 if(buf == nil)
238 error("malloc failed: %r");
239 qlock(&readlock);
240 n = read9pmsg(srvfd, buf, messagesize);
241 if(n <= 0){
242 if(n < 0)
243 error("i/o error on server channel");
244 threadexitsall("unmounted");
246 if(readlock.head == nil) /* no other processes waiting to read; start one */
247 proccreate(fsysproc, nil, Stack);
248 qunlock(&readlock);
249 if(t == nil)
250 t = emalloc(sizeof(Fcall));
251 if(convM2S(buf, n, t) != n)
252 error("convert error in convM2S");
253 if(DEBUG)
254 fprint(2, "<= %F\n", t);
255 if(fcall[t->type] == nil)
256 fsysrespond(t, buf, Ebadfcall);
257 else{
258 if(t->type==Tversion || t->type==Tauth)
259 f = nil;
260 else
261 f = newfid(t->fid);
262 t = (*fcall[t->type])(t, buf, f);
267 static void
268 fsysrespond(Fcall *t, uchar *buf, char *err)
270 int n;
272 if(err){
273 t->type = Rerror;
274 t->ename = err;
275 }else
276 t->type++;
277 if(buf == nil)
278 buf = emalloc(messagesize);
279 n = convS2M(t, buf, messagesize);
280 if(n < 0)
281 error("convert error in convS2M");
282 if(write(srvfd, buf, n) != n)
283 error("write error in respond");
284 if(DEBUG)
285 fprint(2, "=> %F\n", t);
286 free(buf);
289 static
290 Fid*
291 newfid(int fid)
293 Fid *f, *ff, **fh;
295 qlock(&queue);
296 ff = nil;
297 fh = &fids[fid&(Nhash-1)];
298 for(f=*fh; f; f=f->next)
299 if(f->fid == fid)
300 goto Return;
301 else if(ff==nil && !f->busy)
302 ff = f;
303 if(ff){
304 ff->fid = fid;
305 f = ff;
306 goto Return;
308 f = emalloc(sizeof *f);
309 f->fid = fid;
310 f->next = *fh;
311 *fh = f;
312 Return:
313 qunlock(&queue);
314 return f;
317 static uint
318 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
320 Dir d;
322 d.qid.type = dir->type;
323 d.qid.path = dir->qid;
324 d.qid.vers = 0;
325 d.mode = dir->perm;
326 d.length = 0; /* would be nice to do better */
327 d.name = dir->name;
328 d.uid = user;
329 d.gid = user;
330 d.muid = user;
331 d.atime = clock;
332 d.mtime = clock;
333 return convD2M(&d, buf, nbuf);
336 static void
337 queuesend(Dirtab *d, Plumbmsg *m)
339 Sendreq *s, *t;
340 Fid *f;
341 int i;
343 s = emalloc(sizeof(Sendreq));
344 s->nfid = d->nopen;
345 s->nleft = s->nfid;
346 s->fid = emalloc(s->nfid*sizeof(Fid*));
347 i = 0;
348 /* build array of fids open on this channel */
349 for(f=d->fopen; f!=nil; f=f->nextopen)
350 s->fid[i++] = f;
351 s->msg = m;
352 s->next = nil;
353 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
354 for(t=d->sendq; t!=nil; t=t->next)
355 if(t->next == nil)
356 break;
357 if(t == nil)
358 d->sendq = s;
359 else
360 t->next = s;
363 static void
364 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
366 Readreq *r;
368 r = emalloc(sizeof(Readreq));
369 r->fcall = t;
370 r->buf = buf;
371 r->fid = f;
372 r->next = d->readq;
373 d->readq = r;
376 static void
377 drainqueue(Dirtab *d)
379 Readreq *r, *nextr, *prevr;
380 Sendreq *s, *nexts, *prevs;
381 int i, n;
383 prevs = nil;
384 for(s=d->sendq; s!=nil; s=nexts){
385 nexts = s->next;
386 for(i=0; i<s->nfid; i++){
387 prevr = nil;
388 for(r=d->readq; r!=nil; r=nextr){
389 nextr = r->next;
390 if(r->fid == s->fid[i]){
391 /* pack the message if necessary */
392 if(s->pack == nil)
393 s->pack = plumbpack(s->msg, &s->npack);
394 /* exchange the stuff... */
395 r->fcall->data = s->pack+r->fid->offset;
396 n = s->npack - r->fid->offset;
397 if(n > messagesize-IOHDRSZ)
398 n = messagesize-IOHDRSZ;
399 if(n > r->fcall->count)
400 n = r->fcall->count;
401 r->fcall->count = n;
402 fsysrespond(r->fcall, r->buf, nil);
403 r->fid->offset += n;
404 if(r->fid->offset >= s->npack){
405 /* message transferred; delete this fid from send queue */
406 r->fid->offset = 0;
407 s->fid[i] = nil;
408 s->nleft--;
410 /* delete read request from queue */
411 if(prevr)
412 prevr->next = r->next;
413 else
414 d->readq = r->next;
415 free(r->fcall);
416 free(r);
417 break;
418 }else
419 prevr = r;
422 /* if no fids left, delete this send from queue */
423 if(s->nleft == 0){
424 free(s->fid);
425 plumbfree(s->msg);
426 free(s->pack);
427 if(prevs)
428 prevs->next = s->next;
429 else
430 d->sendq = s->next;
431 free(s);
432 }else
433 prevs = s;
437 /* can't flush a send because they are always answered synchronously */
438 static void
439 flushqueue(Dirtab *d, int oldtag)
441 Readreq *r, *prevr;
443 prevr = nil;
444 for(r=d->readq; r!=nil; r=r->next){
445 if(oldtag == r->fcall->tag){
446 /* delete read request from queue */
447 if(prevr)
448 prevr->next = r->next;
449 else
450 d->readq = r->next;
451 free(r->fcall);
452 free(r->buf);
453 free(r);
454 return;
456 prevr = r;
460 /* remove messages awaiting delivery to now-closing fid */
461 static void
462 removesenders(Dirtab *d, Fid *fid)
464 Sendreq *s, *nexts, *prevs;
465 int i;
467 prevs = nil;
468 for(s=d->sendq; s!=nil; s=nexts){
469 nexts = s->next;
470 for(i=0; i<s->nfid; i++)
471 if(fid == s->fid[i]){
472 /* delete this fid from send queue */
473 s->fid[i] = nil;
474 s->nleft--;
475 break;
477 /* if no fids left, delete this send from queue */
478 if(s->nleft == 0){
479 free(s->fid);
480 plumbfree(s->msg);
481 free(s->pack);
482 if(prevs)
483 prevs->next = s->next;
484 else
485 d->sendq = s->next;
486 free(s);
487 }else
488 prevs = s;
492 static void
493 hold(Plumbmsg *m, Dirtab *d)
495 Holdq *h, *q;
497 h = emalloc(sizeof(Holdq));
498 h->msg = m;
499 /* add to end of queue */
500 if(d->holdq == nil)
501 d->holdq = h;
502 else{
503 for(q=d->holdq; q->next!=nil; q=q->next)
505 q->next = h;
509 static void
510 queueheld(Dirtab *d)
512 Holdq *h;
514 while(d->holdq != nil){
515 h = d->holdq;
516 d->holdq = h->next;
517 queuesend(d, h->msg);
518 /* no need to drain queue because we know no-one is reading yet */
519 free(h);
523 static void
524 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
526 int i;
527 char *err;
529 qlock(&queue);
530 err = nil;
531 if(m->dst==nil || m->dst[0]=='\0'){
532 err = Enoport;
533 if(rs != nil)
534 err = startup(rs, e);
535 plumbfree(m);
536 }else
537 for(i=NQID; i<ndir; i++)
538 if(strcmp(m->dst, dir[i].name) == 0){
539 if(dir[i].nopen == 0){
540 err = startup(rs, e);
541 if(e!=nil && e->holdforclient)
542 hold(m, &dir[i]);
543 else
544 plumbfree(m);
545 }else{
546 queuesend(&dir[i], m);
547 drainqueue(&dir[i]);
549 break;
551 freeexec(e);
552 qunlock(&queue);
553 fsysrespond(t, buf, err);
554 free(t);
557 static Fcall*
558 fsysversion(Fcall *t, uchar *buf, Fid*)
560 if(t->msize < 256){
561 fsysrespond(t, buf, "version: message size too small");
562 return t;
564 if(t->msize < messagesize)
565 messagesize = t->msize;
566 t->msize = messagesize;
567 if(strncmp(t->version, "9P2000", 6) != 0){
568 fsysrespond(t, buf, "unrecognized 9P version");
569 return t;
571 t->version = "9P2000";
572 fsysrespond(t, buf, nil);
573 return t;
576 static Fcall*
577 fsysauth(Fcall *t, uchar *buf, Fid*)
579 fsysrespond(t, buf, "plumber: authentication not required");
580 return t;
583 static Fcall*
584 fsysattach(Fcall *t, uchar *buf, Fid *f)
586 Fcall out;
588 if(strcmp(t->uname, user) != 0){
589 fsysrespond(&out, buf, Eperm);
590 return t;
592 f->busy = 1;
593 f->open = 0;
594 f->qid.type = QTDIR;
595 f->qid.path = Qdir;
596 f->qid.vers = 0;
597 f->dir = dir;
598 memset(&out, 0, sizeof(Fcall));
599 out.type = t->type;
600 out.tag = t->tag;
601 out.fid = f->fid;
602 out.qid = f->qid;
603 fsysrespond(&out, buf, nil);
604 return t;
607 static Fcall*
608 fsysflush(Fcall *t, uchar *buf, Fid*)
610 int i;
612 qlock(&queue);
613 for(i=NQID; i<ndir; i++)
614 flushqueue(&dir[i], t->oldtag);
615 qunlock(&queue);
616 fsysrespond(t, buf, nil);
617 return t;
620 static Fcall*
621 fsyswalk(Fcall *t, uchar *buf, Fid *f)
623 Fcall out;
624 Fid *nf;
625 ulong path;
626 Dirtab *d, *dir;
627 Qid q;
628 int i;
629 uchar type;
630 char *err;
632 if(f->open){
633 fsysrespond(t, buf, "clone of an open fid");
634 return t;
637 nf = nil;
638 if(t->fid != t->newfid){
639 nf = newfid(t->newfid);
640 if(nf->busy){
641 fsysrespond(t, buf, "clone to a busy fid");
642 return t;
644 nf->busy = 1;
645 nf->open = 0;
646 nf->dir = f->dir;
647 nf->qid = f->qid;
648 f = nf; /* walk f */
651 out.nwqid = 0;
652 err = nil;
653 dir = f->dir;
654 q = f->qid;
656 if(t->nwname > 0){
657 for(i=0; i<t->nwname; i++){
658 if((q.type & QTDIR) == 0){
659 err = Enotdir;
660 break;
662 if(strcmp(t->wname[i], "..") == 0){
663 type = QTDIR;
664 path = Qdir;
665 Accept:
666 q.type = type;
667 q.vers = 0;
668 q.path = path;
669 out.wqid[out.nwqid++] = q;
670 continue;
672 d = dir;
673 d++; /* skip '.' */
674 for(; d->name; d++)
675 if(strcmp(t->wname[i], d->name) == 0){
676 type = d->type;
677 path = d->qid;
678 dir = d;
679 goto Accept;
681 err = Enoexist;
682 break;
686 out.type = t->type;
687 out.tag = t->tag;
688 if(err!=nil || out.nwqid<t->nwname){
689 if(nf)
690 nf->busy = 0;
691 }else if(out.nwqid == t->nwname){
692 f->qid = q;
693 f->dir = dir;
696 fsysrespond(&out, buf, err);
697 return t;
700 static Fcall*
701 fsysopen(Fcall *t, uchar *buf, Fid *f)
703 int m, clearrules, mode;
705 clearrules = 0;
706 if(t->mode & OTRUNC){
707 if(f->qid.path != Qrules)
708 goto Deny;
709 clearrules = 1;
711 /* can't truncate anything, so just disregard */
712 mode = t->mode & ~(OTRUNC|OCEXEC);
713 /* can't execute or remove anything */
714 if(mode==OEXEC || (mode&ORCLOSE))
715 goto Deny;
716 switch(mode){
717 default:
718 goto Deny;
719 case OREAD:
720 m = 0400;
721 break;
722 case OWRITE:
723 m = 0200;
724 break;
725 case ORDWR:
726 m = 0600;
727 break;
729 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
730 goto Deny;
731 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
732 lock(&rulesref);
733 if(rulesref.ref++ != 0){
734 rulesref.ref--;
735 unlock(&rulesref);
736 fsysrespond(t, buf, Einuse);
737 return t;
739 unlock(&rulesref);
741 if(clearrules){
742 writerules(nil, 0);
743 rules[0] = nil;
745 t->qid = f->qid;
746 t->iounit = 0;
747 qlock(&queue);
748 f->mode = mode;
749 f->open = 1;
750 f->dir->nopen++;
751 f->nextopen = f->dir->fopen;
752 f->dir->fopen = f;
753 queueheld(f->dir);
754 qunlock(&queue);
755 fsysrespond(t, buf, nil);
756 return t;
758 Deny:
759 fsysrespond(t, buf, Eperm);
760 return t;
763 static Fcall*
764 fsyscreate(Fcall *t, uchar *buf, Fid*)
766 fsysrespond(t, buf, Eperm);
767 return t;
770 static Fcall*
771 fsysreadrules(Fcall *t, uchar *buf)
773 char *p;
774 int n;
776 p = printrules();
777 n = strlen(p);
778 t->data = p;
779 if(t->offset >= n)
780 t->count = 0;
781 else{
782 t->data = p+t->offset;
783 if(t->offset+t->count > n)
784 t->count = n-t->offset;
786 fsysrespond(t, buf, nil);
787 free(p);
788 return t;
791 static Fcall*
792 fsysread(Fcall *t, uchar *buf, Fid *f)
794 uchar *b;
795 int i, n, o, e;
796 uint len;
797 Dirtab *d;
798 uint clock;
800 if(f->qid.path != Qdir){
801 if(f->qid.path == Qrules)
802 return fsysreadrules(t, buf);
803 /* read from port */
804 if(f->qid.path < NQID){
805 fsysrespond(t, buf, "internal error: unknown read port");
806 return t;
808 qlock(&queue);
809 queueread(f->dir, t, buf, f);
810 drainqueue(f->dir);
811 qunlock(&queue);
812 return nil;
814 o = t->offset;
815 e = t->offset+t->count;
816 clock = getclock();
817 b = malloc(messagesize-IOHDRSZ);
818 if(b == nil){
819 fsysrespond(t, buf, Enomem);
820 return t;
822 n = 0;
823 d = dir;
824 d++; /* first entry is '.' */
825 for(i=0; d->name!=nil && i<e; i+=len){
826 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
827 if(len <= BIT16SZ)
828 break;
829 if(i >= o)
830 n += len;
831 d++;
833 t->data = (char*)b;
834 t->count = n;
835 fsysrespond(t, buf, nil);
836 free(b);
837 return t;
840 static Fcall*
841 fsyswrite(Fcall *t, uchar *buf, Fid *f)
843 Plumbmsg *m;
844 int i, n;
845 long count;
846 char *data;
847 Exec *e;
849 switch((int)f->qid.path){
850 case Qdir:
851 fsysrespond(t, buf, Eisdir);
852 return t;
853 case Qrules:
854 clock = getclock();
855 fsysrespond(t, buf, writerules(t->data, t->count));
856 return t;
857 case Qsend:
858 if(f->offset == 0){
859 data = t->data;
860 count = t->count;
861 }else{
862 /* partial message already assembled */
863 f->writebuf = erealloc(f->writebuf, f->offset + t->count);
864 memmove(f->writebuf+f->offset, t->data, t->count);
865 data = f->writebuf;
866 count = f->offset+t->count;
868 m = plumbunpackpartial(data, count, &n);
869 if(m == nil){
870 if(n == 0){
871 f->offset = 0;
872 free(f->writebuf);
873 f->writebuf = nil;
874 fsysrespond(t, buf, Ebadmsg);
875 return t;
877 /* can read more... */
878 if(f->offset == 0){
879 f->writebuf = emalloc(t->count);
880 memmove(f->writebuf, t->data, t->count);
882 /* else buffer has already been grown */
883 f->offset += t->count;
884 fsysrespond(t, buf, nil);
885 return t;
887 /* release partial buffer */
888 f->offset = 0;
889 free(f->writebuf);
890 f->writebuf = nil;
891 for(i=0; rules[i]; i++)
892 if((e=matchruleset(m, rules[i])) != nil){
893 dispose(t, buf, m, rules[i], e);
894 return nil;
896 if(m->dst != nil){
897 dispose(t, buf, m, nil, nil);
898 return nil;
900 fsysrespond(t, buf, "no matching plumb rule");
901 return t;
903 fsysrespond(t, buf, "internal error: write to unknown file");
904 return t;
907 static Fcall*
908 fsysstat(Fcall *t, uchar *buf, Fid *f)
910 t->stat = emalloc(messagesize-IOHDRSZ);
911 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
912 fsysrespond(t, buf, nil);
913 free(t->stat);
914 t->stat = nil;
915 return t;
918 static Fcall*
919 fsyswstat(Fcall *t, uchar *buf, Fid*)
921 fsysrespond(t, buf, Eperm);
922 return t;
925 static Fcall*
926 fsysremove(Fcall *t, uchar *buf, Fid*)
928 fsysrespond(t, buf, Eperm);
929 return t;
932 static Fcall*
933 fsysclunk(Fcall *t, uchar *buf, Fid *f)
935 Fid *prev, *p;
936 Dirtab *d;
938 qlock(&queue);
939 if(f->open){
940 d = f->dir;
941 d->nopen--;
942 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
943 /*
944 * just to be sure last rule is parsed; error messages will be lost, though,
945 * unless last write ended with a blank line
946 */
947 writerules(nil, 0);
948 lock(&rulesref);
949 rulesref.ref--;
950 unlock(&rulesref);
952 prev = nil;
953 for(p=d->fopen; p; p=p->nextopen){
954 if(p == f){
955 if(prev)
956 prev->nextopen = f->nextopen;
957 else
958 d->fopen = f->nextopen;
959 removesenders(d, f);
960 break;
962 prev = p;
965 f->busy = 0;
966 f->open = 0;
967 f->offset = 0;
968 if(f->writebuf != nil){
969 free(f->writebuf);
970 f->writebuf = nil;
972 qunlock(&queue);
973 fsysrespond(t, buf, nil);
974 return t;