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;
27 int nopen; /* #fids open on this port */
42 long offset; /* zeroed at beginning of each message, read or write */
43 char *writebuf; /* partial message written so far; offset tells how much */
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] */
62 char *pack; /* plumbpack()ed message */
63 int npack; /* length of pack */
73 struct /* needed because incref() doesn't return value */
91 static Dirtab dir[NDIR] =
93 { ".", QTDIR, Qdir, 0500|DMDIR },
94 { "rules", QTFILE, Qrules, 0600 },
95 { "send", QTFILE, Qsend, 0200 }
97 static int ndir = NQID;
100 #define clock plumbclock /* SunOS name clash */
102 static Fid *fids[Nhash];
103 static QLock readlock;
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*);
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";
157 * Add new port. A no-op if port already exists or is the null string
166 for(i=NQID; i<ndir; i++)
167 if(strcmp(port, dir[i].name) == 0)
170 fprint(2, "plumb: too many ports; max %d\n", NDIR);
174 dir[i].name = estrdup(port);
178 ports = erealloc(ports, nports*sizeof(char*));
179 ports[nports-1] = dir[i].name;
193 fmtinstall('F', fcallfmt);
196 error("can't create pipe: %r");
197 /* 0 will be server end, 1 will be client end */
199 if(post9pservice(p[1], "plumb", nil) < 0)
200 sysfatal("post9pservice plumb: %r");
202 proccreate(fsysproc, nil, Stack);
217 buf = malloc(messagesize); /* avoid memset of emalloc */
219 error("malloc failed: %r");
221 n = read9pmsg(srvfd, buf, messagesize);
224 error("i/o error on server channel");
225 threadexitsall("unmounted");
228 * can give false positive (create an extra fsysproc) once in a while,
229 * but no false negatives, so good enough. once we have one extra
230 * we'll never have more.
232 if(readlock.waiting.head == nil) /* no other processes waiting to read; start one */
233 proccreate(fsysproc, nil, Stack);
236 t = emalloc(sizeof(Fcall));
237 if(convM2S(buf, n, t) != n)
238 error("convert error in convM2S");
240 fprint(2, "<= %F\n", t);
241 if(fcall[t->type] == 0)
242 fsysrespond(t, buf, Ebadfcall);
244 if(t->type==Tversion || t->type==Tauth)
248 t = (*fcall[t->type])(t, buf, f);
254 fsysrespond(Fcall *t, uchar *buf, char *err)
264 buf = emalloc(messagesize);
265 n = convS2M(t, buf, messagesize);
267 error("convert error in convS2M");
268 if(write(srvfd, buf, n) != n)
269 error("write error in respond");
271 fprint(2, "=> %F\n", t);
283 fh = &fids[fid&(Nhash-1)];
284 for(f=*fh; f; f=f->next)
287 else if(ff==nil && !f->busy)
294 f = emalloc(sizeof *f);
304 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
308 d.qid.type = dir->type;
309 d.qid.path = dir->qid;
312 d.length = 0; /* would be nice to do better */
319 return convD2M(&d, buf, nbuf);
323 queuesend(Dirtab *d, Plumbmsg *m)
329 s = emalloc(sizeof(Sendreq));
332 s->fid = emalloc(s->nfid*sizeof(Fid*));
334 /* build array of fids open on this channel */
335 for(f=d->fopen; f!=nil; f=f->nextopen)
339 /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
340 for(t=d->sendq; t!=nil; t=t->next)
350 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
354 r = emalloc(sizeof(Readreq));
363 drainqueue(Dirtab *d)
365 Readreq *r, *nextr, *prevr;
366 Sendreq *s, *nexts, *prevs;
370 for(s=d->sendq; s!=nil; s=nexts){
372 for(i=0; i<s->nfid; i++){
374 for(r=d->readq; r!=nil; r=nextr){
376 if(r->fid == s->fid[i]){
377 /* pack the message if necessary */
379 s->pack = plumbpack(s->msg, &s->npack);
380 /* exchange the stuff... */
381 r->fcall->data = s->pack+r->fid->offset;
382 n = s->npack - r->fid->offset;
383 if(n > messagesize-IOHDRSZ)
384 n = messagesize-IOHDRSZ;
385 if(n > r->fcall->count)
388 fsysrespond(r->fcall, r->buf, nil);
390 if(r->fid->offset >= s->npack){
391 /* message transferred; delete this fid from send queue */
396 /* delete read request from queue */
398 prevr->next = r->next;
408 /* if no fids left, delete this send from queue */
414 prevs->next = s->next;
423 /* can't flush a send because they are always answered synchronously */
425 flushqueue(Dirtab *d, int oldtag)
430 for(r=d->readq; r!=nil; r=r->next){
431 if(oldtag == r->fcall->tag){
432 /* delete read request from queue */
434 prevr->next = r->next;
446 /* remove messages awaiting delivery to now-closing fid */
448 removesenders(Dirtab *d, Fid *fid)
450 Sendreq *s, *nexts, *prevs;
454 for(s=d->sendq; s!=nil; s=nexts){
456 for(i=0; i<s->nfid; i++)
457 if(fid == s->fid[i]){
458 /* delete this fid from send queue */
463 /* if no fids left, delete this send from queue */
469 prevs->next = s->next;
479 hold(Plumbmsg *m, Dirtab *d)
483 h = emalloc(sizeof(Holdq));
485 /* add to end of queue */
489 for(q=d->holdq; q->next!=nil; q=q->next)
500 while(d->holdq != nil){
503 queuesend(d, h->msg);
504 /* no need to drain queue because we know no-one is reading yet */
510 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
517 if(m->dst==nil || m->dst[0]=='\0'){
520 err = startup(rs, e);
523 for(i=NQID; i<ndir; i++)
524 if(strcmp(m->dst, dir[i].name) == 0){
525 if(dir[i].nopen == 0){
526 err = startup(rs, e);
527 if(e!=nil && e->holdforclient)
532 queuesend(&dir[i], m);
539 fsysrespond(t, buf, err);
544 fsysversion(Fcall *t, uchar *buf, Fid *fid)
549 fsysrespond(t, buf, "version: message size too small");
552 if(t->msize < messagesize)
553 messagesize = t->msize;
554 t->msize = messagesize;
555 if(strncmp(t->version, "9P2000", 6) != 0){
556 fsysrespond(t, buf, "unrecognized 9P version");
559 t->version = "9P2000";
560 fsysrespond(t, buf, nil);
565 fsysauth(Fcall *t, uchar *buf, Fid *fid)
568 fsysrespond(t, buf, "plumber: authentication not required");
573 fsysattach(Fcall *t, uchar *buf, Fid *f)
577 if(strcmp(t->uname, user) != 0){
578 fsysrespond(&out, buf, Eperm);
587 memset(&out, 0, sizeof(Fcall));
592 fsysrespond(&out, buf, nil);
597 fsysflush(Fcall *t, uchar *buf, Fid *fid)
603 for(i=NQID; i<ndir; i++)
604 flushqueue(&dir[i], t->oldtag);
606 fsysrespond(t, buf, nil);
611 fsyswalk(Fcall *t, uchar *buf, Fid *f)
623 fsysrespond(t, buf, "clone of an open fid");
628 if(t->fid != t->newfid){
629 nf = newfid(t->newfid);
631 fsysrespond(t, buf, "clone to a busy fid");
647 for(i=0; i<t->nwname; i++){
648 if((q.type & QTDIR) == 0){
652 if(strcmp(t->wname[i], "..") == 0){
659 out.wqid[out.nwqid++] = q;
665 if(strcmp(t->wname[i], d->name) == 0){
678 if(err!=nil || out.nwqid<t->nwname){
681 }else if(out.nwqid == t->nwname){
686 fsysrespond(&out, buf, err);
691 fsysopen(Fcall *t, uchar *buf, Fid *f)
693 int m, clearrules, mode;
696 if(t->mode & OTRUNC){
697 if(f->qid.path != Qrules)
701 /* can't truncate anything, so just disregard */
702 mode = t->mode & ~(OTRUNC|OCEXEC);
703 /* can't execute or remove anything */
704 if(mode==OEXEC || (mode&ORCLOSE))
719 if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
721 if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
723 if(rulesref.ref++ != 0){
725 unlock(&rulesref.lk);
726 fsysrespond(t, buf, Einuse);
729 unlock(&rulesref.lk);
741 f->nextopen = f->dir->fopen;
745 fsysrespond(t, buf, nil);
749 fsysrespond(t, buf, Eperm);
754 fsyscreate(Fcall *t, uchar *buf, Fid *fid)
757 fsysrespond(t, buf, Eperm);
762 fsysreadrules(Fcall *t, uchar *buf)
773 t->data = p+t->offset;
774 if(t->offset+t->count > n)
775 t->count = n-t->offset;
777 fsysrespond(t, buf, nil);
783 fsysread(Fcall *t, uchar *buf, Fid *f)
791 if(f->qid.path != Qdir){
792 if(f->qid.path == Qrules)
793 return fsysreadrules(t, buf);
795 if(f->qid.path < NQID){
796 fsysrespond(t, buf, "internal error: unknown read port");
800 queueread(f->dir, t, buf, f);
806 e = t->offset+t->count;
808 b = malloc(messagesize-IOHDRSZ);
810 fsysrespond(t, buf, Enomem);
815 d++; /* first entry is '.' */
816 for(i=0; d->name!=nil && i<e; i+=len){
817 len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
826 fsysrespond(t, buf, nil);
832 fsyswrite(Fcall *t, uchar *buf, Fid *f)
840 switch((int)f->qid.path){
842 fsysrespond(t, buf, Eisdir);
846 fsysrespond(t, buf, writerules(t->data, t->count));
853 /* partial message already assembled */
854 f->writebuf = erealloc(f->writebuf, f->offset + t->count);
855 memmove(f->writebuf+f->offset, t->data, t->count);
857 count = f->offset+t->count;
859 m = plumbunpackpartial(data, count, &n);
865 fsysrespond(t, buf, Ebadmsg);
868 /* can read more... */
870 f->writebuf = emalloc(t->count);
871 memmove(f->writebuf, t->data, t->count);
873 /* else buffer has already been grown */
874 f->offset += t->count;
875 fsysrespond(t, buf, nil);
878 /* release partial buffer */
882 for(i=0; rules[i]; i++)
883 if((e=matchruleset(m, rules[i])) != nil){
884 dispose(t, buf, m, rules[i], e);
888 dispose(t, buf, m, nil, nil);
891 fsysrespond(t, buf, "no matching plumb rule");
894 fsysrespond(t, buf, "internal error: write to unknown file");
899 fsysstat(Fcall *t, uchar *buf, Fid *f)
901 t->stat = emalloc(messagesize-IOHDRSZ);
902 t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
903 fsysrespond(t, buf, nil);
910 fsyswstat(Fcall *t, uchar *buf, Fid *fid)
913 fsysrespond(t, buf, Eperm);
918 fsysremove(Fcall *t, uchar *buf, Fid *fid)
921 fsysrespond(t, buf, Eperm);
926 fsysclunk(Fcall *t, uchar *buf, Fid *f)
935 if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
937 * just to be sure last rule is parsed; error messages will be lost, though,
938 * unless last write ended with a blank line
943 unlock(&rulesref.lk);
946 for(p=d->fopen; p; p=p->nextopen){
949 prev->nextopen = f->nextopen;
951 d->fopen = f->nextopen;
961 if(f->writebuf != nil){
966 fsysrespond(t, buf, nil);