/*
* 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->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 */
};
struct Mntalloc
{
Lock;
Mnt* list; /* Mount devices in use */
Mnt* mntfree; /* Free list */
Mntrpc* rpcfree;
int nrpcfree;
int nrpcused;
ulong id;
int rpctag;
}mntalloc;
/*
* 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(&c->umqlock); /* make sure no one else does this until we've established ourselves */
if(waserror()){
qunlock(&c->umqlock);
nexterror();
}
l = convM2S(msg, k, &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 > msize)
error("server tries to increase msize in fversion");
if(f.msize<256 || f.msize>1024*1024)
error("nonsense value of msize in fversion");
if(strncmp(f.version, v, strlen(f.version)) != 0)
error("bad 9P version returned from server");
/* now build Mnt associated with this connection */
lock(&mntalloc);
m = mntalloc.mntfree;
if(m != 0)
mntalloc.mntfree = m->list;
else {
m = malloc(sizeof(Mnt));
if(m == 0) {
unlock(&mntalloc);
exhausted("mount devices");
}
}
m->list = mntalloc.list;
mntalloc.list = m;
m->version = nil;
kstrdup(&m->version, f.version);
m->id = mntalloc.id++;
m->q = qopen(10*MAXRPC, 0, nil, nil);
m->msize = f.msize;
unlock(&mntalloc);
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 > MAXWELEM)
error("devmnt: too many name elements");
alloc = 0;
wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
if(waserror()){
if(alloc && wq->clone!=nil)
cclose(wq->clone);
free(wq);
return nil;
}
alloc = 0;
m = mntchk(c);
r = mntralloc(c, m->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->type = 0;
alloc = 1;
}
wq->clone = nc;
/* Transmit a file system rpc */
if(m->msize == 0)
panic("msize");
n = convS2M(&r->request, r->rpc, m->msize);
if(n < 0)
panic("bad message type in mountio");
if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
error(Emountrpc);
r->stime = fastticks(nil);
r->reqlen = n;
/* Gate readers onto the mount point one at a time */
for(;;) {
lock(m);
if(m->rip == 0)
break;
unlock(m);
sleep(&r->r, rpcattn, r);
if(r->done){
poperror();
mntflushfree(m, r);
return;
}
}
m->rip = up;
unlock(m);
while(r->done == 0) {
if(mntrpcread(m, r) < 0)
error(Emountrpc);
mountmux(m, r);
}
mntgate(m);
poperror();
mntflushfree(m, r);
}
int
mntrpcread(Mnt *m, Mntrpc *r)
{
int i, t, len, hlen;
Block *b, **l, *nb;
r->reply.type = 0;
r->reply.tag = 0;
/* read at least length, type, and tag and pullup to a single block */
while(qlen(m->q) < BIT32SZ+BIT8SZ+BIT16SZ){
b = devtab[m->c->type]->bread(m->c, 2*MAXRPC, 0);
if(b == nil)
return -1;
qaddlist(m->q, b);
}
nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
len = GBIT32(nb->rp);
/* read in the rest of the message */
while(qlen(m->q) < len){
b = devtab[m->c->type]->bread(m->c, 2*MAXRPC, 0);
if(b == nil)
return -1;
qaddlist(m->q, b);
}
/* pullup the header (i.e. everything except data) */
t = nb->rp[BIT32SZ];
switch(t){
case Rread:
hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
break;
default:
hlen = len;
break;
}
nb = pullupqueue(m->q, hlen);
if(convM2S(nb->rp, len, &r->reply) <= 0){
/* bad message, dump it */
print("mntrpcread: convM2S failed\n");
qdiscard(m->q, len);
return -1;
}
/* hang the data off of the fcall struct */
l = &r->b;
*l = nil;
do {
b = qremove(m->q);
if(hlen > 0){
b->rp += hlen;
len -= hlen;
hlen = 0;
}
i = BLEN(b);
if(i <= len){
len -= i;
*l = b;
l = &(b->next);
} else {
/* split block and put unused bit back */
nb = allocb(i-len);
memmove(nb->wp, b->rp+len, i-len);
b->wp = b->rp+len;
nb->wp += i-len;
qputback(m->q, nb);
*l = b;
return 0;
}
}while(len > 0);
/*
* 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;
/*
* Was it closed and reused (was error(Eshutdown); now, it can't happen)
*/
if(m->id == 0 || m->id >= c->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;