2 #include "../port/lib.h"
6 #include "../port/error.h"
9 * References are managed as follows:
10 * The channel to the server - a network connection or pipe - has one
11 * reference for every Chan open on the server. The server channel has
12 * c->mux set to the Mnt used for muxing control to that server. Mnts
13 * have no reference count; they go away when c goes away.
14 * Each channel derived from the mount point has mchan set to c,
15 * and increfs/decrefs mchan to manage references on the server
19 #define MAXRPC (IOHDRSZ+8192)
23 Chan* c; /* Channel for whom we are working */
24 Mntrpc* list; /* Free/pending list */
25 Fcall request; /* Outgoing file system protocol message */
26 Fcall reply; /* Incoming reply */
27 Mnt* m; /* Mount device during rpc */
28 Rendez* z; /* Place to hang out */
29 uchar* rpc; /* I/O Data buffer */
30 uint rpclen; /* len of buffer */
31 Block* b; /* reply blocks */
32 uvlong stime; /* start time for mnt statistics */
33 ulong reqlen; /* request length for mnt statistics */
34 ulong replen; /* reply length for mnt statistics */
35 Mntrpc* flushed; /* message this one flushes */
36 char done; /* Rpc completed */
41 TAGSHIFT = 5, /* ulong has to be 32 bits */
42 TAGMASK = (1<<TAGSHIFT)-1,
43 NMASK = (64*1024)>>TAGSHIFT,
49 Mnt* list; /* Mount devices in use */
50 Mnt* mntfree; /* Free list */
59 void mntdirfix(uchar*, Chan*);
60 Mntrpc* mntflushalloc(Mntrpc*, ulong);
61 void mntflushfree(Mnt*, Mntrpc*);
62 void mntfree(Mntrpc*);
64 void mntqrm(Mnt*, Mntrpc*);
65 Mntrpc* mntralloc(Chan*, ulong);
66 long mntrdwr(int, Chan*, void*, long, vlong);
67 int mntrpcread(Mnt*, Mntrpc*);
68 void mountio(Mnt*, Mntrpc*);
69 void mountmux(Mnt*, Mntrpc*);
70 void mountrpc(Mnt*, Mntrpc*);
74 char Esbadstat[] = "invalid directory entry received from server";
75 char Enoversion[] = "version not established for mount channel";
77 void (*mntstats)(int, Chan*, uvlong, ulong);
84 mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */
85 mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */
86 fmtinstall('F', fcallfmt);
87 fmtinstall('D', dirfmt);
88 /* We can't install %M since eipfmt does and is used in the kernel [sape] */
94 * Version is not multiplexed: message sent only once per connection.
97 mntversion(Chan *c, char *version, int msize, int returnlen)
108 eqlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */
110 qunlock(&c->umqlock);
117 if(msize > c->iounit && c->iounit != 0)
120 if(v == nil || v[0] == '\0')
125 error("bad iounit in version call");
126 if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
127 error("bad 9P version specification");
132 qunlock(&c->umqlock);
135 strecpy(buf, buf+sizeof buf, m->version);
137 if(strncmp(buf, v, k) != 0){
138 snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
144 memmove(version, buf, k);
153 msg = malloc(8192+IOHDRSZ);
155 exhausted("version memory");
160 k = convS2M(&f, msg, 8192+IOHDRSZ);
162 error("bad fversion conversion on send");
169 l = devtab[c->type]->write(c, msg, k, oo);
175 error("short write in fversion");
178 /* message sent; receive and decode reply */
179 k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
181 error("EOF receiving fversion reply");
187 l = convM2S(msg, k, &f);
189 error("bad fversion conversion on reply");
190 if(f.type != Rversion){
193 error("unexpected reply type in fversion");
196 error("server tries to increase msize in fversion");
197 if(f.msize<256 || f.msize>1024*1024)
198 error("nonsense value of msize in fversion");
199 k = strlen(f.version);
200 if(strncmp(f.version, v, k) != 0)
201 error("bad 9P version returned from server");
202 if(returnlen > 0 && returnlen < k)
206 kstrdup(&v, f.version);
207 q = qopen(10*MAXRPC, 0, nil, nil);
210 exhausted("mount queues");
213 /* now build Mnt associated with this connection */
215 m = mntalloc.mntfree;
217 mntalloc.mntfree = m->list;
219 m = malloc(sizeof(Mnt));
224 exhausted("mount devices");
227 m->list = mntalloc.list;
230 m->id = mntalloc.id++;
236 memmove(version, f.version, k); /* length was checked above */
238 poperror(); /* msg */
251 qunlock(&c->umqlock);
257 mntauth(Chan *c, char *spec)
265 mntversion(c, VERSION9P, MAXRPC, 0);
273 /* Close must not be called since it will
274 * call mnt recursively
280 r = mntralloc(0, m->msize);
287 r->request.type = Tauth;
288 r->request.afid = c->fid;
289 r->request.uname = up->user;
290 r->request.aname = spec;
293 c->qid = r->reply.aqid;
309 mntattach(char *muxattach)
321 bogus = *((struct bogus *)muxattach);
327 mntversion(c, nil, 0, 0);
335 /* Close must not be called since it will
336 * call mnt recursively
342 r = mntralloc(0, m->msize);
349 r->request.type = Tattach;
350 r->request.fid = c->fid;
351 if(bogus.authchan == nil)
352 r->request.afid = NOFID;
354 r->request.afid = bogus.authchan->fid;
355 r->request.uname = up->user;
356 r->request.aname = bogus.spec;
359 c->qid = r->reply.qid;
369 if(bogus.flags&MCACHE)
379 c = devattach('M', 0);
381 c->dev = mntalloc.id++;
385 panic("mntchan non-zero %p", c->mchan);
390 mntwalk(Chan *c, Chan *nc, char **name, int nname)
398 print("mntwalk: nc != nil\n");
400 error("devmnt: too many name elements");
402 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
404 if(alloc && wq->clone!=nil)
412 r = mntralloc(c, m->msize);
416 * Until the other side accepts this fid, we can't mntclose it.
417 * Therefore set type to 0 for now; rootclose is known to be safe.
420 nc->flag |= (c->flag & CCACHE);
429 r->request.type = Twalk;
430 r->request.fid = c->fid;
431 r->request.newfid = nc->fid;
432 r->request.nwname = nname;
433 memmove(r->request.wname, name, nname*sizeof(char*));
437 if(r->reply.nwqid > nname)
438 error("too many QIDs returned by walk");
439 if(r->reply.nwqid < nname){
443 if(r->reply.nwqid == 0){
450 /* move new fid onto mnt device and update its qid */
451 if(wq->clone != nil){
453 wq->clone->type = c->type;
454 wq->clone->mchan = c->mchan;
457 if(r->reply.nwqid > 0)
458 wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
460 wq->nqid = r->reply.nwqid;
461 for(i=0; i<wq->nqid; i++)
462 wq->qid[i] = r->reply.wqid[i];
472 mntstat(Chan *c, uchar *dp, int n)
480 r = mntralloc(c, m->msize);
485 r->request.type = Tstat;
486 r->request.fid = c->fid;
489 if(r->reply.nstat > n){
491 PBIT16((uchar*)dp, r->reply.nstat-2);
494 memmove(dp, r->reply.stat, n);
504 mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
510 r = mntralloc(c, m->msize);
515 r->request.type = type;
516 r->request.fid = c->fid;
517 r->request.mode = omode;
519 r->request.perm = perm;
520 r->request.name = name;
524 c->qid = r->reply.qid;
526 c->mode = openmode(omode);
527 c->iounit = r->reply.iounit;
528 if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
529 c->iounit = m->msize-IOHDRSZ;
541 mntopen(Chan *c, int omode)
543 return mntopencreate(Topen, c, nil, omode, 0);
547 mntcreate(Chan *c, char *name, int omode, ulong perm)
549 return mntopencreate(Tcreate, c, name, omode, perm);
553 mntclunk(Chan *c, int t)
559 r = mntralloc(c, m->msize);
566 r->request.fid = c->fid;
578 while((r = m->queue) != nil){
590 for(f = *l; f; f = f->list) {
597 m->list = mntalloc.mntfree;
598 mntalloc.mntfree = m;
611 mntclunk(c, Tremove);
615 mntwstat(Chan *c, uchar *dp, int n)
621 r = mntralloc(c, m->msize);
626 r->request.type = Twstat;
627 r->request.fid = c->fid;
628 r->request.nstat = n;
629 r->request.stat = dp;
637 mntread(Chan *c, void *buf, long n, vlong off)
640 int nc, cache, isdir, dirlen;
643 cache = c->flag & CCACHE;
644 if(c->qid.type & QTDIR) {
651 nc = cread(c, buf, n, off);
659 n = mntrdwr(Tread, c, p, n, off);
660 cupdate(c, p, n, off);
664 n = mntrdwr(Tread, c, buf, n, off);
666 for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
667 dirlen = BIT16SZ+GBIT16(p);
670 validstat(p, dirlen);
680 mntwrite(Chan *c, void *buf, long n, vlong off)
682 return mntrdwr(Twrite, c, buf, n, off);
686 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
697 cache = c->flag & CCACHE;
698 if(c->qid.type & QTDIR)
701 r = mntralloc(c, m->msize);
706 r->request.type = type;
707 r->request.fid = c->fid;
708 r->request.offset = off;
709 r->request.data = uba;
711 if(nr > m->msize-IOHDRSZ)
712 nr = m->msize-IOHDRSZ;
713 r->request.count = nr;
715 nreq = r->request.count;
721 r->b = bl2mem((uchar*)uba, r->b, nr);
723 cwrite(c, (uchar*)uba, nr, off);
731 if(nr != nreq || n == 0 || up->nnote)
738 mountrpc(Mnt *m, Mntrpc *r)
744 r->reply.type = Tmax; /* can't ever be a valid message type */
751 error(r->reply.ename);
755 if(t == r->request.type+1)
758 if(m->c->path != nil)
761 if(r->c != nil && r->c->path != nil)
763 print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
764 up->text, up->pid, sn, cn,
765 r, r->request.tag, r->request.fid, r->request.type,
766 r->reply.type, r->reply.tag);
772 mountio(Mnt *m, Mntrpc *r)
779 if(strcmp(up->errstr, Eintr) != 0){
780 switch(r->request.type){
783 /* botch, abandon fid */
784 if(strcmp(up->errstr, Ehungup) != 0)
790 r = mntflushalloc(r, m->msize);
800 /* Transmit a file system rpc */
803 n = convS2M(&r->request, r->rpc, m->msize);
805 print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
806 up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
810 if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
812 r->stime = fastticks(nil);
815 /* Gate readers onto the mount point one at a time */
821 sleep(r->z, rpcattn, r);
830 while(r->done == 0) {
831 if(mntrpcread(m, r) < 0)
841 doread(Mnt *m, int len)
845 while(qlen(m->q) < len){
846 b = devtab[m->c->type]->bread(m->c, m->msize, 0);
849 if(blocklen(b) == 0){
859 mntrpcread(Mnt *m, Mntrpc *r)
867 /* read at least length, type, and tag and pullup to a single block */
868 if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
870 nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
872 /* read in the rest of the message, avoid ridiculous (for now) message sizes */
873 len = GBIT32(nb->rp);
875 qdiscard(m->q, qlen(m->q));
878 if(doread(m, len) < 0)
881 /* pullup the header (i.e. everything except data) */
885 hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
891 nb = pullupqueue(m->q, hlen);
893 if(convM2S(nb->rp, len, &r->reply) <= 0){
894 /* bad message, dump it */
895 print("mntrpcread: convM2S failed\n");
900 /* hang the data off of the fcall struct */
916 /* split block and put unused bit back */
918 memmove(nb->wp, b->rp+len, i-len);
937 for(q = m->queue; q; q = q->list) {
946 mountmux(Mnt *m, Mntrpc *r)
953 for(q = *l; q; q = q->list) {
954 /* look for a reply to a message */
955 if(q->request.tag == r->reply.tag) {
958 (*mntstats)(q->request.type,
960 q->reqlen + r->replen);
963 * Completed someone else.
964 * Trade pointers to receive buffer.
972 q->done = 1; /* hands off */
981 print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
985 * Create a new flush request and chain the previous
989 mntflushalloc(Mntrpc *r, ulong iounit)
993 fr = mntralloc(0, iounit);
995 fr->request.type = Tflush;
996 if(r->request.type == Tflush)
997 fr->request.oldtag = r->request.oldtag;
999 fr->request.oldtag = r->request.tag;
1006 * Free a chain of flushes. Remove each unanswered
1007 * flush and the original message from the unanswered
1008 * request queue. Mark the original message as done
1009 * and if it hasn't been answered set the reply to to
1013 mntflushfree(Mnt *m, Mntrpc *r)
1020 r->reply.type = Rflush;
1035 for(i = 0; i < NMASK; i++){
1036 v = mntalloc.tagmask[i];
1039 for(j = 0; j < 1<<TAGSHIFT; j++)
1040 if((v & (1<<j)) == 0){
1041 mntalloc.tagmask[i] |= 1<<j;
1042 return (i<<TAGSHIFT) + j;
1045 panic("no friggin tags left");
1052 mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1056 mntralloc(Chan *c, ulong msize)
1061 new = mntalloc.rpcfree;
1063 new = malloc(sizeof(Mntrpc));
1066 exhausted("mount rpc header");
1069 * The header is split from the data buffer as
1070 * mountmux may swap the buffer with another header.
1072 new->rpc = mallocz(msize, 0);
1073 if(new->rpc == nil){
1076 exhausted("mount rpc buffer");
1078 new->rpclen = msize;
1079 new->request.tag = alloctag();
1082 mntalloc.rpcfree = new->list;
1083 mntalloc.nrpcfree--;
1084 if(new->rpclen < msize){
1086 new->rpc = mallocz(msize, 0);
1087 if(new->rpc == nil){
1089 mntalloc.nrpcused--;
1091 exhausted("mount rpc buffer");
1093 new->rpclen = msize;
1096 mntalloc.nrpcused++;
1111 if(mntalloc.nrpcfree >= 10){
1113 freetag(r->request.tag);
1117 r->list = mntalloc.rpcfree;
1118 mntalloc.rpcfree = r;
1119 mntalloc.nrpcfree++;
1121 mntalloc.nrpcused--;
1126 mntqrm(Mnt *m, Mntrpc *r)
1134 for(f = *l; f; f = f->list) {
1149 /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1152 panic("mntchk 1: nil mchan c %s", chanpath(c));
1157 print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1160 * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1162 if(m->id == 0 || m->id >= c->dev)
1163 panic("mntchk 3: can't happen");
1169 * Rewrite channel type and dev for in-flight data to
1170 * reflect local values. These entries are known to be
1171 * the first two in the Dir encoding after the count.
1174 mntdirfix(uchar *dirbuf, Chan *c)
1178 r = devtab[c->type]->dc;
1179 dirbuf += BIT16SZ; /* skip count */
1182 PBIT32(dirbuf, c->dev);
1191 return r->done || r->m->rip == 0;