7a1c11ed4d5737258ae3e4ec2aaf69f6010050a3
[akaros.git] / kern / drivers / dev / mnt.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15 #include <smallidpool.h>
16
17 struct dev mntdevtab;
18
19 static char *devname(void)
20 {
21         return mntdevtab.name;
22 }
23
24 /*
25  * References are managed as follows:
26  * The channel to the server - a network connection or pipe - has one
27  * reference for every Chan open on the server.  The server channel has
28  * c->mux set to the Mnt used for muxing control to that server.  Mnts
29  * have no reference count; they go away when c goes away.
30  * Each channel derived from the mount point has mchan set to c,
31  * and increfs/decrefs mchan to manage references on the server
32  * connection.
33  */
34
35 #define MAXRPC (IOHDRSZ+8192)
36 #define MAXTAG MAX_U16_POOL_SZ
37
38 static __inline int isxdigit(int c)
39 {
40         if ((c >= '0') && (c <= '9'))
41                 return 1;
42         if ((c >= 'a') && (c <= 'f'))
43                 return 1;
44         if ((c >= 'A') && (c <= 'F'))
45                 return 1;
46         return 0;
47 }
48
49 struct mntrpc {
50         struct chan *c;                         /* Channel for whom we are working */
51         struct mntrpc *list;            /* Free/pending list */
52         struct fcall request;           /* Outgoing file system protocol message */
53         struct fcall reply;                     /* Incoming reply */
54         struct mnt *m;                          /* Mount device during rpc */
55         struct rendez r;                        /* Place to hang out */
56         uint8_t *rpc;                           /* I/O Data buffer */
57         unsigned int rpclen;            /* len of buffer */
58         struct block *b;                        /* reply blocks */
59         char done;                                      /* Rpc completed */
60         uint64_t stime;                         /* start time for mnt statistics */
61         uint32_t reqlen;                        /* request length for mnt statistics */
62         uint32_t replen;                        /* reply length for mnt statistics */
63         struct mntrpc *flushed;         /* message this one flushes */
64 };
65
66 /* Our TRUNC and remove on close differ from 9ps, so we'll need to translate.
67  * I got these flags from http://man.cat-v.org/plan_9/5/open */
68 #define MNT_9P_OPEN_OTRUNC              0x10
69 #define MNT_9P_OPEN_ORCLOSE             0x40
70
71 struct Mntalloc {
72         spinlock_t l;
73         struct mnt *list;                       /* Mount devices in use */
74         struct mnt *mntfree;            /* Free list */
75         struct mntrpc *rpcfree;
76         int nrpcfree;
77         int nrpcused;
78         uint32_t id;
79         struct u16_pool *tags;
80 } mntalloc;
81
82 void mattach(struct mnt *, struct chan *, char *unused_char_p_t);
83 struct mnt *mntchk(struct chan *);
84 void mntdirfix(uint8_t * unused_uint8_p_t, struct chan *);
85 struct mntrpc *mntflushalloc(struct mntrpc *, uint32_t);
86 void mntflushfree(struct mnt *, struct mntrpc *);
87 void mntfree(struct mntrpc *);
88 void mntgate(struct mnt *);
89 void mntpntfree(struct mnt *);
90 void mntqrm(struct mnt *, struct mntrpc *);
91 struct mntrpc *mntralloc(struct chan *, uint32_t);
92 long mntrdwr(int unused_int, struct chan *, void *, long, int64_t);
93 int mntrpcread(struct mnt *, struct mntrpc *);
94 void mountio(struct mnt *, struct mntrpc *);
95 void mountmux(struct mnt *, struct mntrpc *);
96 void mountrpc(struct mnt *, struct mntrpc *);
97 int rpcattn(void *);
98 struct chan *mntchan(void);
99
100 char Esbadstat[] = "invalid directory entry received from server";
101 char Enoversion[] = "version not established for mount channel";
102
103 void (*mntstats) (int unused_int, struct chan *, uint64_t, uint32_t);
104
105 static void mntinit(void)
106 {
107         mntalloc.id = 1;
108         mntalloc.tags = create_u16_pool(MAXTAG);
109         (void) get_u16(mntalloc.tags);  /* don't allow 0 as a tag */
110         //fmtinstall('F', fcallfmt);
111 /*      fmtinstall('D', dirfmt); */
112 /*      fmtinstall('M', dirmodefmt);  */
113
114         cinit();
115 }
116
117 /*
118  * Version is not multiplexed: message sent only once per connection.
119  */
120 long mntversion(struct chan *c, char *version, int msize, int returnlen)
121 {
122         ERRSTACK(2);
123         struct fcall f;
124         uint8_t *msg;
125         struct mnt *m;
126         char *v;
127         long k, l;
128         uint64_t oo;
129         char buf[128];
130
131         qlock(&c->umqlock);     /* make sure no one else does this until we've established ourselves */
132         if (waserror()) {
133                 qunlock(&c->umqlock);
134                 nexterror();
135         }
136
137         /* defaults */
138         if (msize == 0)
139                 msize = MAXRPC;
140         if (msize > c->iounit && c->iounit != 0)
141                 msize = c->iounit;
142         v = version;
143         if (v == NULL || v[0] == '\0')
144                 v = VERSION9P;
145
146         /* validity */
147         if (msize < 0)
148                 error("bad iounit in version call");
149         if (strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
150                 error("bad 9P version specification");
151
152         m = c->mux;
153
154         if (m != NULL) {
155                 qunlock(&c->umqlock);
156                 poperror();
157
158                 strncpy(buf, m->version, sizeof buf);
159                 k = strlen(buf);
160                 if (strncmp(buf, v, k) != 0) {
161                         snprintf(buf, sizeof buf, "incompatible 9P versions %s %s",
162                                          m->version, v);
163                         error(buf);
164                 }
165                 if (returnlen > 0) {
166                         if (returnlen < k)
167                                 error(Eshort);
168                         memmove(version, buf, k);
169                 }
170                 return k;
171         }
172
173         f.type = Tversion;
174         f.tag = NOTAG;
175         f.msize = msize;
176         f.version = v;
177         msg = kzmalloc(8192 + IOHDRSZ, 0);
178         if (msg == NULL)
179                 exhausted("version memory");
180         if (waserror()) {
181                 kfree(msg);
182                 nexterror();
183         }
184         k = convS2M(&f, msg, 8192 + IOHDRSZ);
185         if (k == 0)
186                 error("bad fversion conversion on send");
187
188         spin_lock(&c->lock);
189         oo = c->offset;
190         c->offset += k;
191         spin_unlock(&c->lock);
192
193         l = devtab[c->type].write(c, msg, k, oo);
194
195         if (l < k) {
196                 spin_lock(&c->lock);
197                 c->offset -= k - l;
198                 spin_unlock(&c->lock);
199                 error("short write in fversion");
200         }
201
202         /* message sent; receive and decode reply */
203         k = devtab[c->type].read(c, msg, 8192 + IOHDRSZ, c->offset);
204         if (k <= 0)
205                 error("EOF receiving fversion reply");
206
207         spin_lock(&c->lock);
208         c->offset += k;
209         spin_unlock(&c->lock);
210
211         l = convM2S(msg, k, &f);
212         if (l != k)
213                 error("bad fversion conversion on reply");
214         if (f.type != Rversion) {
215                 if (f.type == Rerror)
216                         error(f.ename);
217                 error("unexpected reply type in fversion");
218         }
219         if (f.msize > msize)
220                 error("server tries to increase msize in fversion");
221         if (f.msize < 256 || f.msize > 1024 * 1024)
222                 error("nonsense value of msize in fversion");
223         if (strncmp(f.version, v, strlen(f.version)) != 0)
224                 error("bad 9P version returned from server");
225
226         /* now build Mnt associated with this connection */
227         spin_lock(&mntalloc.l);
228         m = mntalloc.mntfree;
229         if (m != 0)
230                 mntalloc.mntfree = m->list;
231         else {
232                 m = kzmalloc(sizeof(struct mnt), 0);
233                 if (m == 0) {
234                         spin_unlock(&mntalloc.l);
235                         exhausted("mount devices");
236                 }
237         }
238         m->list = mntalloc.list;
239         mntalloc.list = m;
240         m->version = NULL;
241         kstrdup(&m->version, f.version);
242         m->id = mntalloc.id++;
243         m->q = qopen(10 * MAXRPC, 0, NULL, NULL);
244         m->msize = f.msize;
245         spin_unlock(&mntalloc.l);
246
247         poperror();     /* msg */
248         kfree(msg);
249
250         spin_lock(&m->lock);
251         m->queue = 0;
252         m->rip = 0;
253
254         c->flag |= CMSG;
255         c->mux = m;
256         m->c = c;
257         spin_unlock(&m->lock);
258
259         poperror();     /* c */
260         qunlock(&c->umqlock);
261         k = strlen(f.version);
262         if (returnlen > 0) {
263                 if (returnlen < k)
264                         error(Eshort);
265                 memmove(version, f.version, k);
266         }
267
268         return k;
269 }
270
271 struct chan *mntauth(struct chan *c, char *spec)
272 {
273         ERRSTACK(2);
274         struct mnt *m;
275         struct mntrpc *r;
276
277         m = c->mux;
278
279         if (m == NULL) {
280                 mntversion(c, VERSION9P, MAXRPC, 0);
281                 m = c->mux;
282                 if (m == NULL)
283                         error(Enoversion);
284         }
285
286         c = mntchan();
287         if (waserror()) {
288                 /* Close must not be called since it will
289                  * call mnt recursively
290                  */
291                 chanfree(c);
292                 nexterror();
293         }
294
295         r = mntralloc(0, m->msize);
296
297         if (waserror()) {
298                 mntfree(r);
299                 nexterror();
300         }
301
302         r->request.type = Tauth;
303         r->request.afid = c->fid;
304         r->request.uname = current->user;
305         r->request.aname = spec;
306         mountrpc(m, r);
307
308         c->qid = r->reply.aqid;
309         c->mchan = m->c;
310         chan_incref(m->c);
311         c->mqid = c->qid;
312         c->mode = O_RDWR;
313
314         poperror();     /* r */
315         mntfree(r);
316
317         poperror();     /* c */
318
319         return c;
320
321 }
322
323 static struct chan *mntattach(char *muxattach)
324 {
325         ERRSTACK(2);
326         struct mnt *m;
327         struct chan *c;
328         struct mntrpc *r;
329         struct bogus {
330                 struct chan *chan;
331                 struct chan *authchan;
332                 char *spec;
333                 int flags;
334         } bogus;
335
336         bogus = *((struct bogus *)muxattach);
337         c = bogus.chan;
338
339         m = c->mux;
340
341         if (m == NULL) {
342                 mntversion(c, NULL, 0, 0);
343                 m = c->mux;
344                 if (m == NULL)
345                         error(Enoversion);
346         }
347
348         c = mntchan();
349         if (waserror()) {
350                 /* Close must not be called since it will
351                  * call mnt recursively
352                  */
353                 chanfree(c);
354                 nexterror();
355         }
356
357         r = mntralloc(0, m->msize);
358
359         if (waserror()) {
360                 mntfree(r);
361                 nexterror();
362         }
363
364         r->request.type = Tattach;
365         r->request.fid = c->fid;
366         if (bogus.authchan == NULL)
367                 r->request.afid = NOFID;
368         else
369                 r->request.afid = bogus.authchan->fid;
370         r->request.uname = current->user;
371         r->request.aname = bogus.spec;
372         mountrpc(m, r);
373
374         c->qid = r->reply.qid;
375         c->mchan = m->c;
376         chan_incref(m->c);
377         c->mqid = c->qid;
378
379         poperror();     /* r */
380         mntfree(r);
381
382         poperror();     /* c */
383
384         if (bogus.flags & MCACHE)
385                 c->flag |= CCACHE;
386         return c;
387 }
388
389 struct chan *mntchan(void)
390 {
391         struct chan *c;
392
393         c = devattach(devname(), 0);
394         spin_lock(&mntalloc.l);
395         c->dev = mntalloc.id++;
396         spin_unlock(&mntalloc.l);
397
398         if (c->mchan)
399                 panic("mntchan non-zero %p", c->mchan);
400         return c;
401 }
402
403 static struct walkqid *mntwalk(struct chan *c, struct chan *nc, char **name,
404                                                            int nname)
405 {
406         ERRSTACK(2);
407         volatile int alloc;
408         int i;
409         struct mnt *m;
410         struct mntrpc *r;
411         struct walkqid *wq;
412
413         if (nc != NULL)
414                 printd("mntwalk: nc != NULL\n");
415         if (nname > MAXWELEM)
416                 error("devmnt: too many name elements");
417         alloc = 0;
418         wq = kzmalloc(sizeof(struct walkqid) + nname * sizeof(struct qid),
419                                   KMALLOC_WAIT);
420         if (waserror()) {
421                 if (alloc && wq->clone != NULL)
422                         cclose(wq->clone);
423                 kfree(wq);
424                 poperror();
425                 return NULL;
426         }
427
428         alloc = 0;
429         m = mntchk(c);
430         r = mntralloc(c, m->msize);
431         if (nc == NULL) {
432                 nc = devclone(c);
433                 /* Until the other side accepts this fid, we can't mntclose it.
434                  * Therefore set type to -1 for now.  inferno was setting this to 0,
435                  * assuming it was devroot.  lining up with chanrelease and newchan */
436                 nc->type = -1;
437                 alloc = 1;
438         }
439         wq->clone = nc;
440
441         if (waserror()) {
442                 mntfree(r);
443                 nexterror();
444         }
445         r->request.type = Twalk;
446         r->request.fid = c->fid;
447         r->request.newfid = nc->fid;
448         r->request.nwname = nname;
449         memmove(r->request.wname, name, nname * sizeof(char *));
450
451         mountrpc(m, r);
452
453         if (r->reply.nwqid > nname)
454                 error("too many QIDs returned by walk");
455         if (r->reply.nwqid < nname) {
456                 if (alloc)
457                         cclose(nc);
458                 wq->clone = NULL;
459                 if (r->reply.nwqid == 0) {
460                         kfree(wq);
461                         wq = NULL;
462                         goto Return;
463                 }
464         }
465
466         /* move new fid onto mnt device and update its qid */
467         if (wq->clone != NULL) {
468                 if (wq->clone != c) {
469                         wq->clone->type = c->type;
470                         wq->clone->mchan = c->mchan;
471                         chan_incref(c->mchan);
472                 }
473                 if (r->reply.nwqid > 0)
474                         wq->clone->qid = r->reply.wqid[r->reply.nwqid - 1];
475         }
476         wq->nqid = r->reply.nwqid;
477         for (i = 0; i < wq->nqid; i++)
478                 wq->qid[i] = r->reply.wqid[i];
479
480 Return:
481         poperror();
482         mntfree(r);
483         poperror();
484         return wq;
485 }
486
487 static int mntstat(struct chan *c, uint8_t * dp, int n)
488 {
489         ERRSTACK(1);
490         struct mnt *m;
491         struct mntrpc *r;
492
493         if (n < BIT16SZ)
494                 error(Eshortstat);
495         m = mntchk(c);
496         r = mntralloc(c, m->msize);
497         if (waserror()) {
498                 mntfree(r);
499                 nexterror();
500         }
501         r->request.type = Tstat;
502         r->request.fid = c->fid;
503         mountrpc(m, r);
504
505         if (r->reply.nstat > n) {
506                 /* doesn't fit; just patch the count and return */
507                 PBIT16((uint8_t *) dp, r->reply.nstat);
508                 n = BIT16SZ;
509         } else {
510                 n = r->reply.nstat;
511                 memmove(dp, r->reply.stat, n);
512                 validstat(dp, n, 0);
513                 mntdirfix(dp, c);
514         }
515         poperror();
516         mntfree(r);
517         return n;
518 }
519
520 static struct chan *mntopencreate(int type, struct chan *c, char *name,
521                                                                   int omode, uint32_t perm)
522 {
523         ERRSTACK(1);
524         struct mnt *m;
525         struct mntrpc *r;
526
527         m = mntchk(c);
528         r = mntralloc(c, m->msize);
529         if (waserror()) {
530                 mntfree(r);
531                 nexterror();
532         }
533         r->request.type = type;
534         r->request.fid = c->fid;
535         r->request.mode = omode_to_9p_accmode(omode);
536         if (omode & O_TRUNC)
537                 r->request.mode |= MNT_9P_OPEN_OTRUNC;
538         if (omode & O_REMCLO)
539                 r->request.mode |= MNT_9P_OPEN_ORCLOSE;
540         if (type == Tcreate) {
541                 r->request.perm = perm;
542                 r->request.name = name;
543         }
544         mountrpc(m, r);
545
546         c->qid = r->reply.qid;
547         c->offset = 0;
548         c->mode = openmode(omode);
549         c->iounit = r->reply.iounit;
550         if (c->iounit == 0 || c->iounit > m->msize - IOHDRSZ)
551                 c->iounit = m->msize - IOHDRSZ;
552         c->flag |= COPEN;
553         poperror();
554         mntfree(r);
555
556         if (c->flag & CCACHE)
557                 copen(c);
558
559         return c;
560 }
561
562 static struct chan *mntopen(struct chan *c, int omode)
563 {
564         return mntopencreate(Topen, c, NULL, omode, 0);
565 }
566
567 static void mntcreate(struct chan *c, char *name, int omode, uint32_t perm)
568 {
569         mntopencreate(Tcreate, c, name, omode, perm);
570 }
571
572 static void mntclunk(struct chan *c, int t)
573 {
574         ERRSTACK(1);
575         struct mnt *m;
576         struct mntrpc *r;
577
578         m = mntchk(c);
579         r = mntralloc(c, m->msize);
580         if (waserror()) {
581                 mntfree(r);
582                 nexterror();
583         }
584
585         r->request.type = t;
586         r->request.fid = c->fid;
587         mountrpc(m, r);
588         mntfree(r);
589         poperror();
590 }
591
592 void muxclose(struct mnt *m)
593 {
594         struct mntrpc *q, *r;
595
596         for (q = m->queue; q; q = r) {
597                 r = q->list;
598                 mntfree(q);
599         }
600         m->id = 0;
601         kfree(m->version);
602         m->version = NULL;
603         mntpntfree(m);
604 }
605
606 void mntpntfree(struct mnt *m)
607 {
608         struct mnt *f, **l;
609         struct queue *q;
610
611         spin_lock(&mntalloc.l);
612         l = &mntalloc.list;
613         for (f = *l; f; f = f->list) {
614                 if (f == m) {
615                         *l = m->list;
616                         break;
617                 }
618                 l = &f->list;
619         }
620         m->list = mntalloc.mntfree;
621         mntalloc.mntfree = m;
622         q = m->q;
623         spin_unlock(&mntalloc.l);
624
625         qfree(q);
626 }
627
628 static void mntclose(struct chan *c)
629 {
630         mntclunk(c, Tclunk);
631 }
632
633 static void mntremove(struct chan *c)
634 {
635         mntclunk(c, Tremove);
636 }
637
638 static int mntwstat(struct chan *c, uint8_t * dp, int n)
639 {
640         ERRSTACK(1);
641         struct mnt *m;
642         struct mntrpc *r;
643
644         m = mntchk(c);
645         r = mntralloc(c, m->msize);
646         if (waserror()) {
647                 mntfree(r);
648                 nexterror();
649         }
650         r->request.type = Twstat;
651         r->request.fid = c->fid;
652         r->request.nstat = n;
653         r->request.stat = dp;
654         mountrpc(m, r);
655         poperror();
656         mntfree(r);
657         return n;
658 }
659
660 /* the servers should either return units of whole directory entries
661  * OR support seeking to an arbitrary place. One or other.
662  * Both are fine, but at least one is a minimum.
663  * If the return a partial result, but more than one result,
664  * we'll return a shorter read and the next offset will be aligned
665  */
666 static long mntread(struct chan *c, void *buf, long n, int64_t off)
667 {
668         uint8_t *p, *e;
669         int nc, cache, isdir, dirlen;
670         int numdirent = 0;
671
672         isdir = 0;
673         cache = c->flag & CCACHE;
674         if (c->qid.type & QTDIR) {
675                 cache = 0;
676                 isdir = 1;
677         }
678
679         p = buf;
680         if (cache) {
681                 nc = cread(c, buf, n, off);
682                 if (nc > 0) {
683                         n -= nc;
684                         if (n == 0)
685                                 return nc;
686                         p += nc;
687                         off += nc;
688                 }
689                 n = mntrdwr(Tread, c, p, n, off);
690                 cupdate(c, p, n, off);
691                 return n + nc;
692         }
693
694         n = mntrdwr(Tread, c, buf, n, off);
695
696         if (isdir) {
697                 for (e = &p[n]; p + BIT16SZ < e; p += dirlen) {
698                         dirlen = BIT16SZ + GBIT16(p);
699                         if (p + dirlen > e){
700                                 break;
701                         }
702                         validstat(p, dirlen, 0);
703                         mntdirfix(p, c);
704                         numdirent += dirlen;
705                 }
706                 if (p != e) {
707                         //error(Esbadstat);
708                         /* not really. Maybe the server supports
709                          * arbitrary seek like go9p now does.
710                          */
711                         n = numdirent;
712                 }
713         }
714         return n;
715 }
716
717 static long mntwrite(struct chan *c, void *buf, long n, int64_t off)
718 {
719         return mntrdwr(Twrite, c, buf, n, off);
720 }
721
722 long mntrdwr(int type, struct chan *c, void *buf, long n, int64_t off)
723 {
724         ERRSTACK(1);
725         struct mnt *m;
726         struct mntrpc *r;                       /* TO DO: volatile struct { Mntrpc *r; } r; */
727         char *uba;
728         int cache;
729         uint32_t cnt, nr, nreq;
730
731         m = mntchk(c);
732         uba = buf;
733         cnt = 0;
734         cache = c->flag & CCACHE;
735         if (c->qid.type & QTDIR)
736                 cache = 0;
737         for (;;) {
738                 r = mntralloc(c, m->msize);
739                 if (waserror()) {
740                         mntfree(r);
741                         nexterror();
742                 }
743                 r->request.type = type;
744                 r->request.fid = c->fid;
745                 r->request.offset = off;
746                 r->request.data = uba;
747                 nr = n;
748                 if (nr > m->msize - IOHDRSZ)
749                         nr = m->msize - IOHDRSZ;
750                 r->request.count = nr;
751                 mountrpc(m, r);
752                 nreq = r->request.count;
753                 nr = r->reply.count;
754                 if (nr > nreq)
755                         nr = nreq;
756
757                 if (type == Tread)
758                         r->b = bl2mem((uint8_t *) uba, r->b, nr);
759                 else if (cache)
760                         cwrite(c, (uint8_t *) uba, nr, off);
761
762                 poperror();
763                 mntfree(r);
764                 off += nr;
765                 uba += nr;
766                 cnt += nr;
767                 n -= nr;
768                 if (nr != nreq || n == 0 /*|| current->killed */ )
769                         break;
770         }
771         return cnt;
772 }
773
774 void mountrpc(struct mnt *m, struct mntrpc *r)
775 {
776         char *sn, *cn;
777         int t;
778         char *e;
779
780         r->reply.tag = 0;
781         r->reply.type = Tmax;   /* can't ever be a valid message type */
782
783         mountio(m, r);
784
785         t = r->reply.type;
786         switch (t) {
787                 case Rerror:
788                         /* in Akaros mode, first four characters
789                          * are errno.
790                          */
791                         e = r->reply.ename;
792                         /* If it is in the format "XXXX <at least one char>" */
793                         if ((strlen(e) > 5) && isxdigit(e[0]) &&
794                                 isxdigit(e[1]) &&
795                                 isxdigit(e[2]) &&
796                                 isxdigit(e[3])) {
797                                 int errno = strtoul(e, NULL, 16);
798                                 set_errno(errno);
799                                 error(&r->reply.ename[5]);
800                         } else
801                                 error(r->reply.ename);
802                 case Rflush:
803                         error(Eintr);
804                 default:
805                         if (t == r->request.type + 1)
806                                 break;
807                         sn = "?";
808                         if (m->c->name != NULL)
809                                 sn = m->c->name->s;
810                         cn = "?";
811                         if (r->c != NULL && r->c->name != NULL)
812                                 cn = r->c->name->s;
813                         printd
814                                 ("mnt: proc %s %lu: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n",
815                                  "current->text", "current->pid", sn, cn, r, r->request.tag,
816                                  r->request.fid, r->request.type, r->reply.type, r->reply.tag);
817                         error(Emountrpc);
818         }
819 }
820
821 void mountio(struct mnt *m, struct mntrpc *r)
822 {
823         ERRSTACK(1);
824         int n;
825
826         while (waserror()) {
827                 if (m->rip == current)
828                         mntgate(m);
829                 /* Syscall aborts are like Plan 9 Eintr.  For those, we need to change
830                  * the old request to a flsh (mntflushalloc) and try again.  We'll
831                  * always try to flush, and you can't get out until the flush either
832                  * succeeds or errors out with a non-abort/Eintr error. */
833                 if (strcmp(current_errstr(), "syscall aborted") &&
834                     strcmp(current_errstr(), Eintr)) {
835                         /* all other errors (not abort or Eintr) */
836                         mntflushfree(m, r);
837                         nexterror();
838                 }
839                 r = mntflushalloc(r, m->msize);
840                 /* need one for every waserror call (so this plus one outside) */
841                 poperror();
842         }
843
844         spin_lock(&m->lock);
845         r->m = m;
846         r->list = m->queue;
847         m->queue = r;
848         spin_unlock(&m->lock);
849
850         /* Transmit a file system rpc */
851         if (m->msize == 0)
852                 panic("msize");
853         n = convS2M(&r->request, r->rpc, m->msize);
854         if (n < 0)
855                 panic("bad message type in mountio");
856         if (devtab[m->c->type].write(m->c, r->rpc, n, 0) != n)
857                 error(Emountrpc);
858 /*      r->stime = fastticks(NULL); */
859         r->reqlen = n;
860
861         /* Gate readers onto the mount point one at a time */
862         for (;;) {
863                 spin_lock(&m->lock);
864                 if (m->rip == 0)
865                         break;
866                 spin_unlock(&m->lock);
867                 rendez_sleep(&r->r, rpcattn, r);
868                 if (r->done) {
869                         poperror();
870                         mntflushfree(m, r);
871                         return;
872                 }
873         }
874         m->rip = current;
875         spin_unlock(&m->lock);
876         while (r->done == 0) {
877                 if (mntrpcread(m, r) < 0)
878                         error(Emountrpc);
879                 mountmux(m, r);
880         }
881         mntgate(m);
882         poperror();
883         mntflushfree(m, r);
884 }
885
886 static int doread(struct mnt *m, int len)
887 {
888         struct block *b;
889
890         while (qlen(m->q) < len) {
891                 b = devtab[m->c->type].bread(m->c, m->msize, 0);
892                 if (b == NULL)
893                         return -1;
894                 if (blocklen(b) == 0) {
895                         freeblist(b);
896                         return -1;
897                 }
898                 qaddlist(m->q, b);
899         }
900         return 0;
901 }
902
903 int mntrpcread(struct mnt *m, struct mntrpc *r)
904 {
905         int i, t, len, hlen;
906         struct block *b, **l, *nb;
907
908         r->reply.type = 0;
909         r->reply.tag = 0;
910
911         /* read at least length, type, and tag and pullup to a single block */
912         if (doread(m, BIT32SZ + BIT8SZ + BIT16SZ) < 0)
913                 return -1;
914         nb = pullupqueue(m->q, BIT32SZ + BIT8SZ + BIT16SZ);
915
916         /* read in the rest of the message, avoid ridiculous (for now) message sizes */
917         len = GBIT32(nb->rp);
918         if (len > m->msize) {
919                 qdiscard(m->q, qlen(m->q));
920                 return -1;
921         }
922         if (doread(m, len) < 0)
923                 return -1;
924
925         /* pullup the header (i.e. everything except data) */
926         t = nb->rp[BIT32SZ];
927         switch (t) {
928                 case Rread:
929                         hlen = BIT32SZ + BIT8SZ + BIT16SZ + BIT32SZ;
930                         break;
931                 default:
932                         hlen = len;
933                         break;
934         }
935         nb = pullupqueue(m->q, hlen);
936
937         if (convM2S(nb->rp, len, &r->reply) <= 0) {
938                 /* bad message, dump it */
939                 printd("mntrpcread: convM2S failed\n");
940                 qdiscard(m->q, len);
941                 return -1;
942         }
943
944         /* hang the data off of the fcall struct */
945         l = &r->b;
946         *l = NULL;
947         do {
948                 b = qremove(m->q);
949                 /* TODO: have better block helpers for this and the memmove below */
950                 b = linearizeblock(b);
951                 if (hlen > 0) {
952                         b->rp += hlen;
953                         len -= hlen;
954                         hlen = 0;
955                 }
956                 i = BLEN(b);
957                 if (i <= len) {
958                         len -= i;
959                         *l = b;
960                         l = &(b->next);
961                 } else {
962                         /* split block and put unused bit back */
963                         nb = allocb(i - len);
964                         memmove(nb->wp, b->rp + len, i - len);
965                         b->wp = b->rp + len;
966                         nb->wp += i - len;
967                         qputback(m->q, nb);
968                         *l = b;
969                         return 0;
970                 }
971         } while (len > 0);
972
973         return 0;
974 }
975
976 void mntgate(struct mnt *m)
977 {
978         struct mntrpc *q;
979
980         spin_lock(&m->lock);
981         m->rip = 0;
982         for (q = m->queue; q; q = q->list) {
983                 if (q->done == 0)
984                         if (rendez_wakeup(&q->r))
985                                 break;
986         }
987         spin_unlock(&m->lock);
988 }
989
990 void mountmux(struct mnt *m, struct mntrpc *r)
991 {
992         struct mntrpc **l, *q;
993
994         spin_lock(&m->lock);
995         l = &m->queue;
996         for (q = *l; q; q = q->list) {
997                 /* look for a reply to a message */
998                 if (q->request.tag == r->reply.tag) {
999                         *l = q->list;
1000                         if (q != r) {
1001                                 /*
1002                                  * Completed someone else.
1003                                  * Trade pointers to receive buffer.
1004                                  */
1005                                 q->reply = r->reply;
1006                                 q->b = r->b;
1007                                 r->b = NULL;
1008                         }
1009                         q->done = 1;
1010                         spin_unlock(&m->lock);
1011                         if (mntstats != NULL)
1012                                 (*mntstats) (q->request.type,
1013                                                          m->c, q->stime, q->reqlen + r->replen);
1014                         if (q != r)
1015                                 rendez_wakeup(&q->r);
1016                         return;
1017                 }
1018                 l = &q->list;
1019         }
1020         spin_unlock(&m->lock);
1021         if (r->reply.type == Rerror) {
1022                 printd("unexpected reply tag %u; type %d (error %q)\n", r->reply.tag,
1023                            r->reply.type, r->reply.ename);
1024         } else {
1025                 printd("unexpected reply tag %u; type %d\n", r->reply.tag,
1026                            r->reply.type);
1027         }
1028 }
1029
1030 /*
1031  * Create a new flush request and chain the previous
1032  * requests from it
1033  */
1034 struct mntrpc *mntflushalloc(struct mntrpc *r, uint32_t iounit)
1035 {
1036         struct mntrpc *fr;
1037
1038         fr = mntralloc(0, iounit);
1039
1040         fr->request.type = Tflush;
1041         if (r->request.type == Tflush)
1042                 fr->request.oldtag = r->request.oldtag;
1043         else
1044                 fr->request.oldtag = r->request.tag;
1045         fr->flushed = r;
1046
1047         return fr;
1048 }
1049
1050 /*
1051  *  Free a chain of flushes.  Remove each unanswered
1052  *  flush and the original message from the unanswered
1053  *  request queue.  Mark the original message as done
1054  *  and if it hasn't been answered set the reply to to
1055  *  Rflush.
1056  */
1057 void mntflushfree(struct mnt *m, struct mntrpc *r)
1058 {
1059         struct mntrpc *fr;
1060
1061         while (r) {
1062                 fr = r->flushed;
1063                 if (!r->done) {
1064                         r->reply.type = Rflush;
1065                         mntqrm(m, r);
1066                 }
1067                 if (fr)
1068                         mntfree(r);
1069                 r = fr;
1070         }
1071 }
1072
1073 static int alloctag(void)
1074 {
1075         return get_u16(mntalloc.tags);
1076 }
1077
1078 static void freetag(int t)
1079 {
1080         put_u16(mntalloc.tags, t);
1081 }
1082
1083 struct mntrpc *mntralloc(struct chan *c, uint32_t msize)
1084 {
1085         struct mntrpc *new;
1086
1087         spin_lock(&mntalloc.l);
1088         new = mntalloc.rpcfree;
1089         if (new == NULL) {
1090                 new = kzmalloc(sizeof(struct mntrpc), 0);
1091                 if (new == NULL) {
1092                         spin_unlock(&mntalloc.l);
1093                         exhausted("mount rpc header");
1094                 }
1095                 rendez_init(&new->r);
1096                 /*
1097                  * The header is split from the data buffer as
1098                  * mountmux may swap the buffer with another header.
1099                  */
1100                 new->rpc = kzmalloc(msize, KMALLOC_WAIT);
1101                 if (new->rpc == NULL) {
1102                         kfree(new);
1103                         spin_unlock(&mntalloc.l);
1104                         exhausted("mount rpc buffer");
1105                 }
1106                 new->rpclen = msize;
1107                 new->request.tag = alloctag();
1108                 if (new->request.tag == NOTAG) {
1109                         kfree(new);
1110                         spin_unlock(&mntalloc.l);
1111                         exhausted("rpc tags");
1112                 }
1113         } else {
1114                 mntalloc.rpcfree = new->list;
1115                 mntalloc.nrpcfree--;
1116                 if (new->rpclen < msize) {
1117                         kfree(new->rpc);
1118                         new->rpc = kzmalloc(msize, KMALLOC_WAIT);
1119                         if (new->rpc == NULL) {
1120                                 kfree(new);
1121                                 mntalloc.nrpcused--;
1122                                 spin_unlock(&mntalloc.l);
1123                                 exhausted("mount rpc buffer");
1124                         }
1125                         new->rpclen = msize;
1126                 }
1127         }
1128         mntalloc.nrpcused++;
1129         spin_unlock(&mntalloc.l);
1130         new->c = c;
1131         new->done = 0;
1132         new->flushed = NULL;
1133         new->b = NULL;
1134         return new;
1135 }
1136
1137 void mntfree(struct mntrpc *r)
1138 {
1139         if (r->b != NULL)
1140                 freeblist(r->b);
1141         spin_lock(&mntalloc.l);
1142         if (mntalloc.nrpcfree >= 10) {
1143                 kfree(r->rpc);
1144                 freetag(r->request.tag);
1145                 kfree(r);
1146         } else {
1147                 r->list = mntalloc.rpcfree;
1148                 mntalloc.rpcfree = r;
1149                 mntalloc.nrpcfree++;
1150         }
1151         mntalloc.nrpcused--;
1152         spin_unlock(&mntalloc.l);
1153 }
1154
1155 void mntqrm(struct mnt *m, struct mntrpc *r)
1156 {
1157         struct mntrpc **l, *f;
1158
1159         spin_lock(&m->lock);
1160         r->done = 1;
1161
1162         l = &m->queue;
1163         for (f = *l; f; f = f->list) {
1164                 if (f == r) {
1165                         *l = r->list;
1166                         break;
1167                 }
1168                 l = &f->list;
1169         }
1170         spin_unlock(&m->lock);
1171 }
1172
1173 struct mnt *mntchk(struct chan *c)
1174 {
1175         struct mnt *m;
1176
1177         /* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1178
1179         if (c->mchan == NULL)
1180                 panic("mntchk 1: NULL mchan c %s\n", /*c2name(c) */ "channame?");
1181
1182         m = c->mchan->mux;
1183
1184         if (m == NULL)
1185                 printd("mntchk 2: NULL mux c %s c->mchan %s \n", c2name(c),
1186                            c2name(c->mchan));
1187
1188         /*
1189          * Was it closed and reused (was error(Eshutdown); now, it can't happen)
1190          */
1191         if (m->id == 0 || m->id >= c->dev)
1192                 panic("mntchk 3: can't happen");
1193
1194         return m;
1195 }
1196
1197 /*
1198  * Rewrite channel type and dev for in-flight data to
1199  * reflect local values.  These entries are known to be
1200  * the first two in the Dir encoding after the count.
1201  */
1202 void mntdirfix(uint8_t * dirbuf, struct chan *c)
1203 {
1204         /* TODO: We used to use the device's char (dc), instead of the type.  not
1205          * sure about the effects one way or the other.  This might be the type[2]
1206          * and dev[4] in a D (struct dir, see 9p's stat
1207          * (http://man.cat-v.org/plan_9/5/stat).  In which case, those should be for
1208          * the kernel's use.  Hopefully our kernel. */
1209         dirbuf += BIT16SZ;      /* skip count */
1210         PBIT16(dirbuf, c->type);
1211         dirbuf += BIT16SZ;
1212         PBIT32(dirbuf, c->dev);
1213 }
1214
1215 int rpcattn(void *v)
1216 {
1217         struct mntrpc *r;
1218
1219         r = v;
1220         return r->done || r->m->rip == 0;
1221 }
1222
1223 struct dev mntdevtab __devtab = {
1224         "mnt",
1225
1226         devreset,
1227         mntinit,
1228         devshutdown,
1229         mntattach,
1230         mntwalk,
1231         mntstat,
1232         mntopen,
1233         mntcreate,
1234         mntclose,
1235         mntread,
1236         devbread,
1237         mntwrite,
1238         devbwrite,
1239         mntremove,
1240         mntwstat,
1241         devpower,
1242         devchaninfo,
1243 };