<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/quanstro/vx32devmnt.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/quanstro/vx32devmnt.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include        "u.h"
#include        "lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "error.h"

/*
* References are managed as follows:
* The channel to the server - a network connection or pipe - has one
* reference for every Chan open on the server.  The server channel has
* c-&gt;mux set to the Mnt used for muxing control to that server.  Mnts
* have no reference count; they go away when c goes away.
* Each channel derived from the mount point has mchan set to c,
* and increfs/decrefs mchan to manage references on the server
* connection.
*/

#define MAXRPC (IOHDRSZ+8192)

struct Mntrpc
{
       Chan*   c;              /* Channel for whom we are working */
       Mntrpc* list;           /* Free/pending list */
       Fcall   request;        /* Outgoing file system protocol message */
       Fcall   reply;          /* Incoming reply */
       Mnt*    m;              /* Mount device during rpc */
       Rendez  r;              /* Place to hang out */
       uchar*  rpc;            /* I/O Data buffer */
       uint    rpclen;         /* len of buffer */
       Block   *b;             /* reply blocks */
       char    done;           /* Rpc completed */
       uvlong  stime;          /* start time for mnt statistics */
       ulong   reqlen;         /* request length for mnt statistics */
       ulong   replen;         /* reply length for mnt statistics */
       Mntrpc* flushed;        /* message this one flushes */
};

enum
{
       TAGSHIFT = 5,                   /* ulong has to be 32 bits */
       TAGMASK = (1&lt;&lt;TAGSHIFT)-1,
       NMASK = (64*1024)&gt;&gt;TAGSHIFT,
};

struct Mntalloc
{
       Lock lk;
       Mnt*    list;           /* Mount devices in use */
       Mnt*    mntfree;        /* Free list */
       Mntrpc* rpcfree;
       int     nrpcfree;
       int     nrpcused;
       ulong   id;
       ulong   tagmask[NMASK];
}mntalloc;

Mnt*    mntchk(Chan*);
void    mntdirfix(uchar*, Chan*);
Mntrpc* mntflushalloc(Mntrpc*, ulong);
void    mntflushfree(Mnt*, Mntrpc*);
void    mntfree(Mntrpc*);
void    mntgate(Mnt*);
void    mntpntfree(Mnt*);
void    mntqrm(Mnt*, Mntrpc*);
Mntrpc* mntralloc(Chan*, ulong);
long    mntrdwr(int, Chan*, void*, long, vlong);
int     mntrpcread(Mnt*, Mntrpc*);
void    mountio(Mnt*, Mntrpc*);
void    mountmux(Mnt*, Mntrpc*);
void    mountrpc(Mnt*, Mntrpc*);
int     rpcattn(void*);
Chan*   mntchan(void);

char    Esbadstat[] = "invalid directory entry received from server";
char    Enoversion[] = "version not established for mount channel";


void (*mntstats)(int, Chan*, uvlong, ulong);

static void
mntreset(void)
{
       mntalloc.id = 10;       /* 1-9 are reserved for devfs */
       mntalloc.tagmask[0] = 1;                        /* don't allow 0 as a tag */
       mntalloc.tagmask[NMASK-1] = 0x80000000UL;       /* don't allow NOTAG */
       fmtinstall('F', fcallfmt);
       fmtinstall('D', dirfmt);
/* We can't install %M since eipfmt does and is used in the kernel [sape] */

       cinit();
}

/*
* Version is not multiplexed: message sent only once per connection.
*/
long
mntversion(Chan *c, char *version, int msize, int returnlen)
{
       Fcall f;
       uchar *msg;
       Mnt *m;
       char *v;
       long k, l;
       uvlong oo;
       char buf[128];

       qlock(&amp;c-&gt;umqlock);      /* make sure no one else does this until we've established ourselves */
       if(waserror()){
               qunlock(&amp;c-&gt;umqlock);
               nexterror();
       }

       /* defaults */
       if(msize == 0)
               msize = MAXRPC;
       if(msize &gt; c-&gt;iounit &amp;&amp; c-&gt;iounit != 0)
               msize = c-&gt;iounit;
       v = version;
       if(v == nil || v[0] == '\0')
               v = VERSION9P;

       /* validity */
       if(msize &lt; 0)
               error("bad iounit in version call");
       if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
               error("bad 9P version specification");

       m = c-&gt;mux;

       if(m != nil){
               qunlock(&amp;c-&gt;umqlock);
               poperror();

               strecpy(buf, buf+sizeof buf, m-&gt;version);
               k = strlen(buf);
               if(strncmp(buf, v, k) != 0){
                       snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m-&gt;version, v);
                       error(buf);
               }
               if(returnlen &gt; 0){
                       if(returnlen &lt; k)
                               error(Eshort);
                       memmove(version, buf, k);
               }
               return k;
       }

       f.type = Tversion;
       f.tag = NOTAG;
       f.msize = msize;
       f.version = v;
       msg = malloc(8192+IOHDRSZ);
       if(msg == nil)
               exhausted("version memory");
       if(waserror()){
               free(msg);
               nexterror();
       }
       k = convS2M(&amp;f, msg, 8192+IOHDRSZ);
       if(k == 0)
               error("bad fversion conversion on send");

       lock(&amp;c-&gt;ref.lk);
       oo = c-&gt;offset;
       c-&gt;offset += k;
       unlock(&amp;c-&gt;ref.lk);

       l = devtab[c-&gt;type]-&gt;write(c, msg, k, oo);

       if(l &lt; k){
               lock(&amp;c-&gt;ref.lk);
               c-&gt;offset -= k - l;
               unlock(&amp;c-&gt;ref.lk);
               error("short write in fversion");
       }

       /* message sent; receive and decode reply */
       k = devtab[c-&gt;type]-&gt;read(c, msg, 8192+IOHDRSZ, c-&gt;offset);
       if(k &lt;= 0)
               error("EOF receiving fversion reply");

       lock(&amp;c-&gt;ref.lk);
       c-&gt;offset += k;
       unlock(&amp;c-&gt;ref.lk);

       l = convM2S(msg, k, &amp;f);
       if(l != k)
               error("bad fversion conversion on reply");
       if(f.type != Rversion){
               if(f.type == Rerror)
                       error(f.ename);
               error("unexpected reply type in fversion");
       }
       if(f.msize &gt; msize)
               error("server tries to increase msize in fversion");
       if(f.msize&lt;256 || f.msize&gt;1024*1024)
               error("nonsense value of msize in fversion");
       k = strlen(f.version);
       if(strncmp(f.version, v, k) != 0)
               error("bad 9P version returned from server");

       /* now build Mnt associated with this connection */
       lock(&amp;mntalloc.lk);
       m = mntalloc.mntfree;
       if(m != 0)
               mntalloc.mntfree = m-&gt;list;
       else {
               m = malloc(sizeof(Mnt));
               if(m == 0) {
                       unlock(&amp;mntalloc.lk);
                       exhausted("mount devices");
               }
       }
       m-&gt;list = mntalloc.list;
       mntalloc.list = m;
       m-&gt;version = nil;
       kstrdup(&amp;m-&gt;version, f.version);
       m-&gt;id = mntalloc.id++;
       m-&gt;q = qopen(10*MAXRPC, 0, nil, nil);
       m-&gt;msize = f.msize;
       unlock(&amp;mntalloc.lk);

       if(returnlen &gt; 0){
               if(returnlen &lt; k)
                       error(Eshort);
               memmove(version, f.version, k);
       }

       poperror();     /* msg */
       free(msg);

       lock(&amp;m-&gt;lk);
       m-&gt;queue = 0;
       m-&gt;rip = 0;

       c-&gt;flag |= CMSG;
       c-&gt;mux = m;
       m-&gt;c = c;
       unlock(&amp;m-&gt;lk);

       poperror();     /* c */
       qunlock(&amp;c-&gt;umqlock);

       return k;
}

Chan*
mntauth(Chan *c, char *spec)
{
       Mnt *m;
       Mntrpc *r;

       m = c-&gt;mux;

       if(m == nil){
               mntversion(c, VERSION9P, MAXRPC, 0);
               m = c-&gt;mux;
               if(m == nil)
                       error(Enoversion);
       }

       c = mntchan();
       if(waserror()) {
               /* Close must not be called since it will
                * call mnt recursively
                */
               chanfree(c);
               nexterror();
       }

       r = mntralloc(0, m-&gt;msize);

       if(waserror()) {
               mntfree(r);
               nexterror();
       }

       r-&gt;request.type = Tauth;
       r-&gt;request.afid = c-&gt;fid;
       r-&gt;request.uname = up-&gt;user;
       r-&gt;request.aname = spec;
       mountrpc(m, r);

       c-&gt;qid = r-&gt;reply.aqid;
       c-&gt;mchan = m-&gt;c;
       incref(&amp;m-&gt;c-&gt;ref);
       c-&gt;mqid = c-&gt;qid;
       c-&gt;mode = ORDWR;

       poperror();     /* r */
       mntfree(r);

       poperror();     /* c */

       return c;

}

static Chan*
mntattach(char *muxattach)
{
       Mnt *m;
       Chan *c;
       Mntrpc *r;
       struct bogus{
               Chan    *chan;
               Chan    *authchan;
               char    *spec;
               int     flags;
       }bogus;

       bogus = *((struct bogus *)muxattach);
       c = bogus.chan;

       { // Plan 9 VX addition
               extern Dev mntloopdevtab;
               Chan *mc;
               if(devtab[c-&gt;type] == &amp;mntloopdevtab){
                       if(bogus.authchan || (bogus.spec &amp;&amp; bogus.spec[0]))
                               error(Ebadarg);
                       mc = c-&gt;aux;
                       incref(&amp;mc-&gt;ref);
                       return mc;
               }
       }


       m = c-&gt;mux;

       if(m == nil){
               mntversion(c, nil, 0, 0);
               m = c-&gt;mux;
               if(m == nil)
                       error(Enoversion);
       }

       c = mntchan();
       if(waserror()) {
               /* Close must not be called since it will
                * call mnt recursively
                */
               chanfree(c);
               nexterror();
       }

       r = mntralloc(0, m-&gt;msize);

       if(waserror()) {
               mntfree(r);
               nexterror();
       }

       r-&gt;request.type = Tattach;
       r-&gt;request.fid = c-&gt;fid;
       if(bogus.authchan == nil)
               r-&gt;request.afid = NOFID;
       else
               r-&gt;request.afid = bogus.authchan-&gt;fid;
       r-&gt;request.uname = up-&gt;user;
       r-&gt;request.aname = bogus.spec;
       mountrpc(m, r);

       c-&gt;qid = r-&gt;reply.qid;
       c-&gt;mchan = m-&gt;c;
       incref(&amp;m-&gt;c-&gt;ref);
       c-&gt;mqid = c-&gt;qid;

       poperror();     /* r */
       mntfree(r);

       poperror();     /* c */

       if(bogus.flags&amp;MCACHE)
               c-&gt;flag |= CCACHE;
       return c;
}

Chan*
mntchan(void)
{
       Chan *c;

       c = devattach('M', 0);
       lock(&amp;mntalloc.lk);
       c-&gt;dev = mntalloc.id++;
       unlock(&amp;mntalloc.lk);

       if(c-&gt;mchan)
               panic("mntchan non-zero %p", c-&gt;mchan);
       return c;
}

static Walkqid*
mntwalk(Chan *c, Chan *nc, char **name, int nname)
{
       int i, alloc;
       Mnt *m;
       Mntrpc *r;
       Walkqid *wq;

       if(nc != nil)
               print("mntwalk: nc != nil\n");
       if(nname &gt; MAXWELEM)
               error("devmnt: too many name elements");
       alloc = 0;
       wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
       if(waserror()){
               if(alloc &amp;&amp; wq-&gt;clone!=nil)
                       cclose(wq-&gt;clone);
               free(wq);
               return nil;
       }

       alloc = 0;
       m = mntchk(c);
       r = mntralloc(c, m-&gt;msize);
       if(nc == nil){
               nc = devclone(c);
               /*
                * Until the other side accepts this fid, we can't mntclose it.
                * Therefore set type to 0 for now; rootclose is known to be safe.
                */
               nc-&gt;type = 0;
               alloc = 1;
       }
       wq-&gt;clone = nc;

       if(waserror()) {
               mntfree(r);
               nexterror();
       }
       r-&gt;request.type = Twalk;
       r-&gt;request.fid = c-&gt;fid;
       r-&gt;request.newfid = nc-&gt;fid;
       r-&gt;request.nwname = nname;
       memmove(r-&gt;request.wname, name, nname*sizeof(char*));

       mountrpc(m, r);

       if(r-&gt;reply.nwqid &gt; nname)
               error("too many QIDs returned by walk");
       if(r-&gt;reply.nwqid &lt; nname){
               if(alloc)
                       cclose(nc);
               wq-&gt;clone = nil;
               if(r-&gt;reply.nwqid == 0){
                       free(wq);
                       wq = nil;
                       goto Return;
               }
       }

       /* move new fid onto mnt device and update its qid */
       if(wq-&gt;clone != nil){
               if(wq-&gt;clone != c){
                       wq-&gt;clone-&gt;type = c-&gt;type;
                       wq-&gt;clone-&gt;mchan = c-&gt;mchan;
                       incref(&amp;c-&gt;mchan-&gt;ref);
               }
               if(r-&gt;reply.nwqid &gt; 0)
                       wq-&gt;clone-&gt;qid = r-&gt;reply.wqid[r-&gt;reply.nwqid-1];
       }
       wq-&gt;nqid = r-&gt;reply.nwqid;
       for(i=0; i&lt;wq-&gt;nqid; i++)
               wq-&gt;qid[i] = r-&gt;reply.wqid[i];

   Return:
       poperror();
       mntfree(r);
       poperror();
       return wq;
}

static int
mntstat(Chan *c, uchar *dp, int n)
{
       Mnt *m;
       Mntrpc *r;

       if(n &lt; BIT16SZ)
               error(Eshortstat);
       m = mntchk(c);
       r = mntralloc(c, m-&gt;msize);
       if(waserror()) {
               mntfree(r);
               nexterror();
       }
       r-&gt;request.type = Tstat;
       r-&gt;request.fid = c-&gt;fid;
       mountrpc(m, r);

       if(r-&gt;reply.nstat &gt; n){
               n = BIT16SZ;
               PBIT16((uchar*)dp, r-&gt;reply.nstat-2);
       }else{
               n = r-&gt;reply.nstat;
               memmove(dp, r-&gt;reply.stat, n);
               validstat(dp, n);
               mntdirfix(dp, c);
       }
       poperror();
       mntfree(r);
       return n;
}

static Chan*
mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
{
       Mnt *m;
       Mntrpc *r;

       m = mntchk(c);
       r = mntralloc(c, m-&gt;msize);
       if(waserror()) {
               mntfree(r);
               nexterror();
       }
       r-&gt;request.type = type;
       r-&gt;request.fid = c-&gt;fid;
       r-&gt;request.mode = omode;
       if(type == Tcreate){
               r-&gt;request.perm = perm;
               r-&gt;request.name = name;
       }
       mountrpc(m, r);

       c-&gt;qid = r-&gt;reply.qid;
       c-&gt;offset = 0;
       c-&gt;mode = openmode(omode);
       c-&gt;iounit = r-&gt;reply.iounit;
       if(c-&gt;iounit == 0 || c-&gt;iounit &gt; m-&gt;msize-IOHDRSZ)
               c-&gt;iounit = m-&gt;msize-IOHDRSZ;
       c-&gt;flag |= COPEN;
       poperror();
       mntfree(r);

       if(c-&gt;flag &amp; CCACHE)
               copen(c);

       return c;
}

static Chan*
mntopen(Chan *c, int omode)
{
       return mntopencreate(Topen, c, nil, omode, 0);
}

static void
mntcreate(Chan *c, char *name, int omode, ulong perm)
{
       mntopencreate(Tcreate, c, name, omode, perm);
}

static void
mntclunk(Chan *c, int t)
{
       Mnt *m;
       Mntrpc *r;

       m = mntchk(c);
       r = mntralloc(c, m-&gt;msize);
       if(waserror()){
               mntfree(r);
               nexterror();
       }

       r-&gt;request.type = t;
       r-&gt;request.fid = c-&gt;fid;
       mountrpc(m, r);
       mntfree(r);
       poperror();
}

void
muxclose(Mnt *m)
{
       Mntrpc *q, *r;

       for(q = m-&gt;queue; q; q = r) {
               r = q-&gt;list;
               mntfree(q);
       }
       m-&gt;id = 0;
       free(m-&gt;version);
       m-&gt;version = nil;
       mntpntfree(m);
}

void
mntpntfree(Mnt *m)
{
       Mnt *f, **l;
       Queue *q;

       lock(&amp;mntalloc.lk);
       l = &amp;mntalloc.list;
       for(f = *l; f; f = f-&gt;list) {
               if(f == m) {
                       *l = m-&gt;list;
                       break;
               }
               l = &amp;f-&gt;list;
       }
       m-&gt;list = mntalloc.mntfree;
       mntalloc.mntfree = m;
       q = m-&gt;q;
       unlock(&amp;mntalloc.lk);

       qfree(q);
}

static void
mntclose(Chan *c)
{
       mntclunk(c, Tclunk);
}

static void
mntremove(Chan *c)
{
       mntclunk(c, Tremove);
}

static int
mntwstat(Chan *c, uchar *dp, int n)
{
       Mnt *m;
       Mntrpc *r;

       m = mntchk(c);
       r = mntralloc(c, m-&gt;msize);
       if(waserror()) {
               mntfree(r);
               nexterror();
       }
       r-&gt;request.type = Twstat;
       r-&gt;request.fid = c-&gt;fid;
       r-&gt;request.nstat = n;
       r-&gt;request.stat = dp;
       mountrpc(m, r);
       poperror();
       mntfree(r);
       return n;
}

static long
mntread(Chan *c, void *buf, long n, vlong off)
{
       uchar *p, *e;
       int nc, cache, isdir, dirlen;

       isdir = 0;
       cache = c-&gt;flag &amp; CCACHE;
       if(c-&gt;qid.type &amp; QTDIR) {
               cache = 0;
               isdir = 1;
       }

       p = buf;
       if(cache) {
               nc = cread(c, buf, n, off);
               if(nc &gt; 0) {
                       n -= nc;
                       if(n == 0)
                               return nc;
                       p += nc;
                       off += nc;
               }
               n = mntrdwr(Tread, c, p, n, off);
               cupdate(c, p, n, off);
               return n + nc;
       }

       n = mntrdwr(Tread, c, buf, n, off);
       if(isdir) {
               for(e = &amp;p[n]; p+BIT16SZ &lt; e; p += dirlen){
                       dirlen = BIT16SZ+GBIT16(p);
                       if(p+dirlen &gt; e)
                               break;
                       validstat(p, dirlen);
                       mntdirfix(p, c);
               }
               if(p != e)
                       error(Esbadstat);
       }
       return n;
}

static long
mntwrite(Chan *c, void *buf, long n, vlong off)
{
       return mntrdwr(Twrite, c, buf, n, off);
}

long
mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
{
       Mnt *m;
       Mntrpc *r;
       char *uba;
       int cache;
       ulong cnt, nr, nreq;

       m = mntchk(c);
       uba = buf;
       cnt = 0;
       cache = c-&gt;flag &amp; CCACHE;
       if(c-&gt;qid.type &amp; QTDIR)
               cache = 0;
       for(;;) {
               r = mntralloc(c, m-&gt;msize);
               if(waserror()) {
                       mntfree(r);
                       nexterror();
               }
               r-&gt;request.type = type;
               r-&gt;request.fid = c-&gt;fid;
               r-&gt;request.offset = off;
               r-&gt;request.data = uba;
               nr = n;
               if(nr &gt; m-&gt;msize-IOHDRSZ)
                       nr = m-&gt;msize-IOHDRSZ;
               r-&gt;request.count = nr;
               mountrpc(m, r);
               nreq = r-&gt;request.count;
               nr = r-&gt;reply.count;
               if(nr &gt; nreq)
                       nr = nreq;

               if(type == Tread)
                       r-&gt;b = bl2mem((uchar*)uba, r-&gt;b, nr);
               else if(cache)
                       cwrite(c, (uchar*)uba, nr, off);

               poperror();
               mntfree(r);
               off += nr;
               uba += nr;
               cnt += nr;
               n -= nr;
               if(nr != nreq || n == 0 || up-&gt;nnote)
                       break;
       }
       return cnt;
}

void
mountrpc(Mnt *m, Mntrpc *r)
{
       char *sn, *cn;
       int t;

       r-&gt;reply.tag = 0;
       r-&gt;reply.type = Tmax;        /* can't ever be a valid message type */

       mountio(m, r);

       t = r-&gt;reply.type;
       switch(t) {
       case Rerror:
               error(r-&gt;reply.ename);
       case Rflush:
               error(Eintr);
       default:
               if(t == r-&gt;request.type+1)
                       break;
               sn = "?";
               if(m-&gt;c-&gt;path != nil)
                       sn = m-&gt;c-&gt;path-&gt;s;
               cn = "?";
               if(r-&gt;c != nil &amp;&amp; r-&gt;c-&gt;path != nil)
                       cn = r-&gt;c-&gt;path-&gt;s;
               print("mnt: proc %s %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
                       up-&gt;text, up-&gt;pid, sn, cn,
                       r, r-&gt;request.tag, r-&gt;request.fid, r-&gt;request.type,
                       r-&gt;reply.type, r-&gt;reply.tag);
               error(Emountrpc);
       }
}

void
mountio(Mnt *m, Mntrpc *r)
{
       int n;

       while(waserror()) {
               if(m-&gt;rip == up)
                       mntgate(m);
               if(strcmp(up-&gt;errstr, Eintr) != 0){
                       mntflushfree(m, r);
                       nexterror();
               }
               r = mntflushalloc(r, m-&gt;msize);
       }

       lock(&amp;m-&gt;lk);
       r-&gt;m = m;
       r-&gt;list = m-&gt;queue;
       m-&gt;queue = r;
       unlock(&amp;m-&gt;lk);

       /* Transmit a file system rpc */
       if(m-&gt;msize == 0)
               panic("msize");
       n = convS2M(&amp;r-&gt;request, r-&gt;rpc, m-&gt;msize);
       if(n &lt; 0)
               panic("bad message type in mountio");
       if(devtab[m-&gt;c-&gt;type]-&gt;write(m-&gt;c, r-&gt;rpc, n, 0) != n)
               error(Emountrpc);
       r-&gt;stime = fastticks(nil);
       r-&gt;reqlen = n;

       /* Gate readers onto the mount point one at a time */
       for(;;) {
               lock(&amp;m-&gt;lk);
               if(m-&gt;rip == 0)
                       break;
               unlock(&amp;m-&gt;lk);
               sleep(&amp;r-&gt;r, rpcattn, r);
               if(r-&gt;done){
                       poperror();
                       mntflushfree(m, r);
                       return;
               }
       }
       m-&gt;rip = up;
       unlock(&amp;m-&gt;lk);
       while(r-&gt;done == 0) {
               if(mntrpcread(m, r) &lt; 0)
                       error(Emountrpc);
               mountmux(m, r);
       }
       mntgate(m);
       poperror();
       mntflushfree(m, r);
}

static int
doread(Mnt *m, int len)
{
       Block *b;

       while(qlen(m-&gt;q) &lt; len){
               b = devtab[m-&gt;c-&gt;type]-&gt;bread(m-&gt;c, m-&gt;msize, 0);
               if(b == nil)
                       return -1;
               if(blocklen(b) == 0){
                       freeblist(b);
                       return -1;
               }
               qaddlist(m-&gt;q, b);
       }
       return 0;
}

int
mntrpcread(Mnt *m, Mntrpc *r)
{
       int i, t, len, hlen;
       Block *b, **l, *nb;

       r-&gt;reply.type = 0;
       r-&gt;reply.tag = 0;

       /* read at least length, type, and tag and pullup to a single block */
       if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) &lt; 0)
               return -1;
       nb = pullupqueue(m-&gt;q, BIT32SZ+BIT8SZ+BIT16SZ);

       /* read in the rest of the message, avoid ridiculous (for now) message sizes */
       len = GBIT32(nb-&gt;rp);
       if(len &gt; m-&gt;msize){
               qdiscard(m-&gt;q, qlen(m-&gt;q));
               return -1;
       }
       if(doread(m, len) &lt; 0)
               return -1;

       /* pullup the header (i.e. everything except data) */
       t = nb-&gt;rp[BIT32SZ];
       switch(t){
       case Rread:
               hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
               break;
       default:
               hlen = len;
               break;
       }
       nb = pullupqueue(m-&gt;q, hlen);

       if(convM2S(nb-&gt;rp, len, &amp;r-&gt;reply) &lt;= 0){
               /* bad message, dump it */
               print("mntrpcread: convM2S failed\n");
               qdiscard(m-&gt;q, len);
               return -1;
       }

       /* hang the data off of the fcall struct */
       l = &amp;r-&gt;b;
       *l = nil;
       do {
               b = qremove(m-&gt;q);
               if(hlen &gt; 0){
                       b-&gt;rp += hlen;
                       len -= hlen;
                       hlen = 0;
               }
               i = BLEN(b);
               if(i &lt;= len){
                       len -= i;
                       *l = b;
                       l = &amp;(b-&gt;next);
               } else {
                       /* split block and put unused bit back */
                       nb = allocb(i-len);
                       memmove(nb-&gt;wp, b-&gt;rp+len, i-len);
                       b-&gt;wp = b-&gt;rp+len;
                       nb-&gt;wp += i-len;
                       qputback(m-&gt;q, nb);
                       *l = b;
                       return 0;
               }
       }while(len &gt; 0);

       return 0;
}

void
mntgate(Mnt *m)
{
       Mntrpc *q;

       lock(&amp;m-&gt;lk);
       m-&gt;rip = 0;
       for(q = m-&gt;queue; q; q = q-&gt;list) {
               if(q-&gt;done == 0)
               if(wakeup(&amp;q-&gt;r))
                       break;
       }
       unlock(&amp;m-&gt;lk);
}

void
mountmux(Mnt *m, Mntrpc *r)
{
       int bad;
       Mntrpc **l, *q;
       int tagallocd(int);
       void freetag(int);

       lock(&amp;m-&gt;lk);
       l = &amp;m-&gt;queue;
       for(q = *l; q; q = q-&gt;list) {
               /* look for a reply to a message */
               if(q-&gt;request.tag == r-&gt;reply.tag) {
                       *l = q-&gt;list;
                       if(q != r) {
                               /*
                                * Completed someone else.
                                * Trade pointers to receive buffer.
                                */
                               q-&gt;reply = r-&gt;reply;
                               q-&gt;b = r-&gt;b;
                               r-&gt;b = nil;
                       }
                       q-&gt;done = 1;
                       unlock(&amp;m-&gt;lk);
                       if(mntstats != nil)
                               (*mntstats)(q-&gt;request.type,
                                       m-&gt;c, q-&gt;stime,
                                       q-&gt;reqlen + r-&gt;replen);
                       if(q != r)
                               wakeup(&amp;q-&gt;r);
                       return;
               }
               l = &amp;q-&gt;list;
       }
       bad = 1;
       if(tagallocd(r-&gt;reply.tag)){
               freetag(r-&gt;reply.tag);
               bad = 0;
       }
       unlock(&amp;m-&gt;lk);
       if(bad)
               print("unexpected reply tag %ud; type %d\n", r-&gt;reply.tag, r-&gt;reply.type);
       else
               print("WOOT! caught stale reply %ud; type %d\n", r-&gt;reply.tag, r-&gt;reply.type);
}

/*
* Create a new flush request and chain the previous
* requests from it
*/
Mntrpc*
mntflushalloc(Mntrpc *r, ulong iounit)
{
       Mntrpc *fr;

       fr = mntralloc(0, iounit);

       fr-&gt;request.type = Tflush;
       if(r-&gt;request.type == Tflush)
               fr-&gt;request.oldtag = r-&gt;request.oldtag;
       else
               fr-&gt;request.oldtag = r-&gt;request.tag;
       fr-&gt;flushed = r;

       return fr;
}

/*
*  Free a chain of flushes.  Remove each unanswered
*  flush and the original message from the unanswered
*  request queue.  Mark the original message as done
*  and if it hasn't been answered set the reply to to
*  Rflush.
*/
void
mntflushfree(Mnt *m, Mntrpc *r)
{
       Mntrpc *fr;

       while(r){
               fr = r-&gt;flushed;
               if(!r-&gt;done){
                       r-&gt;reply.type = Rflush;
                       mntqrm(m, r);
               }
               if(fr)
                       mntfree(r);
               r = fr;
       }
}

int
alloctag(void)
{
       int i, j;
       ulong v;

       for(i = 0; i &lt; NMASK; i++){
               v = mntalloc.tagmask[i];
               if(v == ~0UL)
                       continue;
               for(j = 0; j &lt; 1&lt;&lt;TAGSHIFT; j++)
                       if((v &amp; (1&lt;&lt;j)) == 0){
                               mntalloc.tagmask[i] |= 1&lt;&lt;j;
                               return (i&lt;&lt;TAGSHIFT) + j;
                       }
       }
       panic("no friggin tags left");
       return NOTAG;
}

int
tagallocd(int t)
{
       return mntalloc.tagmask[t&gt;&gt;TAGSHIFT] &amp; 1&lt;&lt;(t&amp;TAGMASK);
}

void
freetag(int t)
{
       mntalloc.tagmask[t&gt;&gt;TAGSHIFT] &amp;= ~(1&lt;&lt;(t&amp;TAGMASK));
}

Mntrpc*
mntralloc(Chan *c, ulong msize)
{
       Mntrpc *new;

       lock(&amp;mntalloc.lk);
       new = mntalloc.rpcfree;
       if(new == nil){
               new = malloc(sizeof(Mntrpc));
               if(new == nil) {
                       unlock(&amp;mntalloc.lk);
                       exhausted("mount rpc header");
               }
               /*
                * The header is split from the data buffer as
                * mountmux may swap the buffer with another header.
                */
               new-&gt;rpc = mallocz(msize, 0);
               if(new-&gt;rpc == nil){
                       free(new);
                       unlock(&amp;mntalloc.lk);
                       exhausted("mount rpc buffer");
               }
               new-&gt;rpclen = msize;
               new-&gt;request.tag = alloctag();
       }
       else {
               mntalloc.rpcfree = new-&gt;list;
               mntalloc.nrpcfree--;
               if(new-&gt;rpclen &lt; msize){
                       free(new-&gt;rpc);
                       new-&gt;rpc = mallocz(msize, 0);
                       if(new-&gt;rpc == nil){
                               free(new);
                               mntalloc.nrpcused--;
                               unlock(&amp;mntalloc.lk);
                               exhausted("mount rpc buffer");
                       }
                       new-&gt;rpclen = msize;
               }
       }
       mntalloc.nrpcused++;
       unlock(&amp;mntalloc.lk);
       new-&gt;c = c;
       new-&gt;done = 0;
       new-&gt;flushed = nil;
       new-&gt;b = nil;
       return new;
}

void
mntfree(Mntrpc *r)
{
       if(r-&gt;b != nil)
               freeblist(r-&gt;b);
       lock(&amp;mntalloc.lk);
       if(mntalloc.nrpcfree &gt;= 10){
               free(r-&gt;rpc);
               free(r);
               if(r-&gt;done != 2)
                       freetag(r-&gt;request.tag);
       }
       else{
               if(r-&gt;done == 2)
                       r-&gt;request.tag = alloctag();
               r-&gt;list = mntalloc.rpcfree;
               mntalloc.rpcfree = r;
               mntalloc.nrpcfree++;
       }
       mntalloc.nrpcused--;
       unlock(&amp;mntalloc.lk);
}

void
mntqrm(Mnt *m, Mntrpc *r)
{
       Mntrpc **l, *f;

       lock(&amp;m-&gt;lk);
       r-&gt;done = 2;

       l = &amp;m-&gt;queue;
       for(f = *l; f; f = f-&gt;list) {
               if(f == r) {
                       *l = r-&gt;list;
                       break;
               }
               l = &amp;f-&gt;list;
       }
       unlock(&amp;m-&gt;lk);
}

Mnt*
mntchk(Chan *c)
{
       Mnt *m;

       /* This routine is mostly vestiges of prior lives; now it's just sanity checking */

       if(c-&gt;mchan == nil)
               panic("mntchk 1: nil mchan c %s\n", chanpath(c));

       m = c-&gt;mchan-&gt;mux;

       if(m == nil)
               print("mntchk 2: nil mux c %s c-&gt;mchan %s \n", chanpath(c), chanpath(c-&gt;mchan));

       /*
        * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
        */
       if(m-&gt;id == 0 || m-&gt;id &gt;= c-&gt;dev)
               panic("mntchk 3: can't happen");

       return m;
}

/*
* Rewrite channel type and dev for in-flight data to
* reflect local values.  These entries are known to be
* the first two in the Dir encoding after the count.
*/
void
mntdirfix(uchar *dirbuf, Chan *c)
{
       uint r;

       r = devtab[c-&gt;type]-&gt;dc;
       dirbuf += BIT16SZ;      /* skip count */
       PBIT16(dirbuf, r);
       dirbuf += BIT16SZ;
       PBIT32(dirbuf, c-&gt;dev);
}

int
rpcattn(void *v)
{
       Mntrpc *r;

       r = v;
       return r-&gt;done || r-&gt;m-&gt;rip == 0;
}

Dev mntdevtab = {
       'M',
       "mnt",

       mntreset,
       devinit,
       devshutdown,
       mntattach,
       mntwalk,
       mntstat,
       mntopen,
       mntcreate,
       mntclose,
       mntread,
       devbread,
       mntwrite,
       devbwrite,
       mntremove,
       mntwstat,
};
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>