M bitsy/etherwavelan.c => bitsy/etherwavelan.c +1 -1
@@ 948,7 948,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
ltv.len = 4;
if (w_inltv(ctlr, <v))
print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
- l += snprint(p+l, READSTR-l, "Base station: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+ l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
}
PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
M ip/devip.c => ip/devip.c +34 -8
@@ 66,6 66,8 @@ ip3gen(Chan *c, int i, Dir *dp)
char *p;
cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)];
+ if(cv->owner[0] == 0)
+ memmove(cv->owner, eve, NAMELEN);
switch(i) {
default:
return -1;
@@ 282,6 284,24 @@ ipgetfs(int dev)
return ipfs[dev];
}
+IPaux*
+newipaux(char *owner, char *tag)
+{
+ IPaux *a;
+ int n;
+
+ a = smalloc(sizeof(*a));
+ memmove(a->owner, owner, NAMELEN);
+ memset(a->tag, ' ', sizeof(a->tag));
+ n = strlen(tag);
+ if(n > sizeof(a->tag))
+ n = sizeof(a->tag);
+ memmove(a->tag, tag, n);
+ return a;
+}
+
+#define ATTACHER(c) (((IPaux*)((c)->aux))->owner)
+
static Chan*
ipattach(char* spec)
{
@@ 297,13 317,19 @@ ipattach(char* spec)
c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
c->dev = dev;
+ c->aux = newipaux(commonuser(), "none");
+
return c;
}
static Chan*
ipclone(Chan* c, Chan* nc)
{
- return devclone(c, nc);
+ IPaux *a = c->aux;
+
+ nc = devclone(c, nc);
+ nc->aux = newipaux(a->owner, a->tag);
+ return nc;
}
static int
@@ 359,7 385,6 @@ ipopen(Chan* c, int omode)
netlogopen(f);
break;
case Qiproute:
- memmove(c->tag, "none", sizeof(c->tag));
break;
case Qtopdir:
case Qprotodir:
@@ 380,7 405,7 @@ ipopen(Chan* c, int omode)
qunlock(p);
nexterror();
}
- cv = Fsprotoclone(p, commonuser());
+ cv = Fsprotoclone(p, ATTACHER(c));
qunlock(p);
poperror();
if(cv == nil) {
@@ 402,7 427,7 @@ ipopen(Chan* c, int omode)
nexterror();
}
if((perm & (cv->perm>>6)) != perm) {
- if(strcmp(commonuser(), cv->owner) != 0)
+ if(strcmp(ATTACHER(c), cv->owner) != 0)
error(Eperm);
if((perm & cv->perm) != perm)
error(Eperm);
@@ 410,7 435,7 @@ ipopen(Chan* c, int omode)
}
cv->inuse++;
if(cv->inuse == 1){
- memmove(cv->owner, commonuser(), NAMELEN);
+ memmove(cv->owner, ATTACHER(c), NAMELEN);
cv->perm = 0660;
}
qunlock(cv);
@@ 420,7 445,7 @@ ipopen(Chan* c, int omode)
case Qlisten:
cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)];
if((perm & (cv->perm>>6)) != perm) {
- if(strcmp(commonuser(), cv->owner) != 0)
+ if(strcmp(ATTACHER(c), cv->owner) != 0)
error(Eperm);
if((perm & cv->perm) != perm)
error(Eperm);
@@ 450,7 475,7 @@ ipopen(Chan* c, int omode)
if(nc != nil){
cv->incall = nc->next;
c->qid = (Qid){QID(PROTO(c->qid), nc->x, Qctl), 0};
- memmove(cv->owner, commonuser(), NAMELEN);
+ memmove(nc->owner, ATTACHER(c), NAMELEN);
}
qunlock(cv);
@@ 499,7 524,7 @@ ipwstat(Chan *c, char *dp)
p = f->p[PROTO(c->qid)];
cv = p->conv[CONV(c->qid)];
- if(!iseve() && strcmp(commonuser(), cv->owner) != 0)
+ if(!iseve() && strcmp(ATTACHER(c), cv->owner) != 0)
error(Eperm);
if(d.uid[0]){
strncpy(cv->owner, d.uid, sizeof(cv->owner));
@@ 558,6 583,7 @@ ipclose(Chan* c)
if(c->flag & COPEN)
closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]);
}
+ free(c->aux);
}
enum
M ip/ip.h => ip/ip.h +17 -0
@@ 3,6 3,7 @@ typedef struct Fs Fs;
typedef union Hwaddr Hwaddr;
typedef struct Ifcconv Ifcconv;
typedef struct IP IP;
+typedef struct IPaux IPaux;
typedef struct Ipself Ipself;
typedef struct Ipselftab Ipselftab;
typedef struct Iplink Iplink;
@@ 418,6 419,22 @@ extern void ipwalkroutes(Fs*, Routewalk*);
extern void convroute(Route*, uchar*, uchar*, uchar*, char*, int*);
/*
+ * devip.c
+ */
+
+/*
+ * Hanging off every ip channel's ->aux is the following structure.
+ * It maintains the state used by devip and iproute.
+ */
+struct IPaux
+{
+ char owner[NAMELEN]; /* the user that did the attach */
+ char tag[4];
+};
+
+extern IPaux* newipaux(char*, char*);
+
+/*
* arp.c
*/
struct Arpent
M ip/iproute.c => ip/iproute.c +9 -8
@@ 760,6 760,7 @@ routewrite(Fs *f, Chan *c, char *p, int n)
uchar addr[IPaddrlen];
uchar mask[IPaddrlen];
uchar gate[IPaddrlen];
+ IPaux *a, *na;
cb = parsecmd(p, n);
if(waserror()){
@@ 797,8 798,10 @@ routewrite(Fs *f, Chan *c, char *p, int n)
parseipmask(mask, cb->f[2]);
parseip(gate, cb->f[3]);
tag = "none";
- if(c != nil)
- tag = c->tag;
+ if(c != nil){
+ a = c->aux;
+ tag = a->tag;
+ }
if(memcmp(addr, v4prefix, IPv4off) == 0)
v4addroute(f, tag, addr+IPv4off, mask+IPv4off, gate+IPv4off, 0);
else
@@ 807,12 810,10 @@ routewrite(Fs *f, Chan *c, char *p, int n)
if(cb->nf < 2)
error(Ebadarg);
- h = strlen(cb->f[1]);
- if(h > sizeof(c->tag))
- h = sizeof(c->tag);
- strncpy(c->tag, cb->f[1], h);
- if(h < 4)
- memset(c->tag+h, ' ', sizeof(c->tag)-h);
+ a = c->aux;
+ na = newipaux(a->owner, cb->f[1]);
+ c->aux = na;
+ free(a);
}
poperror();
M pc/etherwavelan.c => pc/etherwavelan.c +10 -3
@@ 114,6 114,7 @@ enum
WTyp_Prom = 0xfc85,
WTyp_Keys = 0xfcb0,
WTyp_TxKey = 0xfcb1,
+ WTyp_StationID = 0xfd20,
WTyp_CurName = 0xfd41,
WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station
WTyp_CurTxRate = 0xfd44, // Current TX rate
@@ 942,8 943,14 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
if(i == 3)
PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
else {
+ Wltv ltv;
PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
-// PRINTSTAT("Base station: %s\n", ltv_inname(ctlr, WTyp_BaseID));
+ ltv.type = WTyp_BaseID;
+ ltv.len = 4;
+ if (w_inltv(ctlr, <v))
+ print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
+ l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
+ ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
}
PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
@@ 1192,8 1199,8 @@ reset(Ether* ether)
DEBUG("no wavelan found\n");
goto abort;
}
-// DEBUG("#l%d: port=0x%lx irq=%ld\n",
-// ether->ctlrno, ether->port, ether->irq);
+ // DEBUG("#l%d: port=0x%lx irq=%ld\n",
+ // ether->ctlrno, ether->port, ether->irq);
w_intdis(ctlr);
if (w_cmd(ctlr,WCmdIni,0)){
M pc/trap.c => pc/trap.c +5 -8
@@ 390,15 390,12 @@ _dumpstack(Ureg *ureg)
v = *(ulong*)l;
if(KTZERO < v && v < (ulong)&etext){
/*
- * Pick off general CALL (0xE8) and CALL indirect
- * through AX (0xFFD0).
+ * we could Pick off general CALL (((uchar*)v)[-5] == 0xE8)
+ * and CALL indirect through AX (((uchar*)v)[-2] == 0xFF && ((uchar*)v)[-2] == 0xD0),
+ * but this is too clever and misses faulting address.
*/
- p = (uchar*)v;
- if(*(p-5) == 0xE8
- || (*(p-2) == 0xFF && *(p-1) == 0xD0)){
- print("%.8lux=%.8lux ", l, v);
- i++;
- }
+ print("%.8lux=%.8lux ", l, v);
+ i++;
}
if(i == 4){
i = 0;
M pc/vgas3.c => pc/vgas3.c +12 -1
@@ 100,7 100,18 @@ s3linear(VGAscr* scr, int* size, int* align)
mmiosize = 0;
mmiobase = 0;
mmioname = nil;
- if(p = pcimatch(nil, PCIS3, 0)){
+
+ /*
+ * S3 makes cards other than display controllers, so
+ * look for the first S3 display controller (device class 3)
+ * and not one of their sound cards.
+ */
+ p = nil;
+ while(p = pcimatch(p, PCIS3, 0)){
+ if(p->ccrb == 0x03)
+ break;
+ }
+ if(p != nil){
for(i=0; i<nelem(p->mem); i++){
if(p->mem[i].size >= *size
&& ((p->mem[i].bar & ~0x0F) & (*align-1)) == 0)
M port/devtls.c => port/devtls.c +122 -98
@@ 15,6 15,10 @@ typedef struct Secret Secret;
enum {
MaxRecLen = 1<<14, /* max payload length of a record layer message */
+ MaxCipherRecLen = MaxRecLen + 2048,
+
+//ZZZ
+ Maxdstate = 64,
RecHdrLen = 5,
TLSVersion = 0x0301,
SSL3Version = 0x0300,
@@ 102,17 106,20 @@ struct TlsRec
void (*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
/* input side -- protected by in.q */
- OneWay in;
- Queue *handq; /* queue of handshake messages */
- Block *processed; /* next bunch of application data */
- Block *unprocessed; /* data read from c but not parsed into records */
+ OneWay in;
+ Block *processed; /* next bunch of application data */
+ Block *unprocessed; /* data read from c but not parsed into records */
+
+ Lock hqlock;
+ int hqref;
+ Queue *handq; /* queue of handshake messages */
/* output side */
- OneWay out;
+ OneWay out;
/* protections */
- char user[NAMELEN];
- int perm;
+ char user[NAMELEN];
+ int perm;
};
typedef struct TlsErrs TlsErrs;
@@ 183,13 190,6 @@ static TlsRec** dstate;
static char *encalgs;
static char *hashalgs;
-enum
-{
-//ZZZ
- Maxdmsg= 1<<16,
- Maxdstate= 64
-};
-
enum{
Qtopdir = 1, /* top level directory */
Qprotodir,
@@ 351,27 351,6 @@ tlsstat(Chan *c, char *db)
devstat(c, db, 0, 0, tlsgen);
}
-static void
-closedown(Chan *c, TlsRec *tr)
-{
- lock(&dslock);
- if(--tr->ref > 0) {
- unlock(&dslock);
- return;
- }
- dstate[CONV(c->qid)] = nil;
- unlock(&dslock);
-
- tlshangup(tr);
- if(tr->c)
- cclose(tr->c);
- free(tr->in.sec);
- free(tr->in.new);
- free(tr->out.sec);
- free(tr->out.new);
- free(tr);
-}
-
static Chan*
tlsopen(Chan *c, int omode)
{
@@ 421,29 400,29 @@ tlsopen(Chan *c, int omode)
dsnew(c, pp);
else {
if((perm & (tr->perm>>6)) != perm
- && (strcmp(up->user, tr->user) != 0
- || (perm & tr->perm) != perm))
+ && (strcmp(up->user, tr->user) != 0
+ || (perm & tr->perm) != perm))
error(Eperm);
-
+ if(t == Qhand){
+ if(waserror()){
+ unlock(&tr->hqlock);
+ nexterror();
+ }
+ lock(&tr->hqlock);
+ if(tr->handq != nil)
+ error(Einuse);
+//ZZZ what is the correct buffering here?
+ tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
+ if(tr->handq == nil)
+ error("can't allocate handshake queue");
+ tr->hqref = 1;
+ unlock(&tr->hqlock);
+ poperror();
+ }
tr->ref++;
}
unlock(&dslock);
poperror();
- if(t == Qhand){
- if(waserror()){
- qunlock(&tr->in.io);
- closedown(c, tr);
- }
- qlock(&tr->in.io);
- if(tr->handq != nil)
- error(Einuse);
-//ZZZ what is the correct buffering here?
- tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
- if(tr->handq == nil)
- error("can't allocate handshake queue");
- qunlock(&tr->in.io);
- poperror();
- }
break;
case Qencalgs:
case Qhashalgs:
@@ 471,11 450,23 @@ tlswstat(Chan *c, char *dp)
if(strcmp(tr->user, up->user) != 0)
error(Eperm);
+//ZZZ propagate chown to tr->datafd?
memmove(tr->user, d.uid, NAMELEN);
tr->perm = d.mode;
}
static void
+dechandq(TlsRec *tr)
+{
+ lock(&tr->hqlock);
+ if(--tr->hqref == 0 && tr->handq != nil){
+ qfree(tr->handq);
+ tr->handq = nil;
+ }
+ unlock(&tr->hqlock);
+}
+
+static void
tlsclose(Chan *c)
{
TlsRec *tr;
@@ 493,15 484,25 @@ tlsclose(Chan *c)
if(tr == nil)
break;
- if(t == Qhand){
- qlock(&tr->in.io);
- if(tr->handq != nil){
- qfree(tr->handq);
- tr->handq = nil;
- }
- qunlock(&tr->in.io);
+ if(t == Qhand)
+ dechandq(tr);
+
+ lock(&dslock);
+ if(--tr->ref > 0) {
+ unlock(&dslock);
+ return;
}
- closedown(c, tr);
+ dstate[CONV(c->qid)] = nil;
+ unlock(&dslock);
+
+ tlshangup(tr);
+ if(tr->c)
+ cclose(tr->c);
+ free(tr->in.sec);
+ free(tr->in.new);
+ free(tr->out.sec);
+ free(tr->out.new);
+ free(tr);
break;
}
}
@@ 524,7 525,7 @@ ensure(TlsRec *s, Block **l, int n)
}
while(sofar < n){
- bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0);
+ bl = devtab[s->c->type]->bread(s->c, MaxCipherRecLen + RecHdrLen, 0);
if(bl == 0)
error(Ehungup);
*l = bl;
@@ 704,6 705,7 @@ tlsrecread(TlsRec *tr)
(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
if(memcmp(hmac, p+len, in->sec->maclen) != 0)
rcvError(tr, EBadRecordMac, "record mac mismatch");
+ b->wp -= in->sec->maclen;
}
qunlock(&in->seclock);
poperror();
@@ 733,13 735,19 @@ tlsrecread(TlsRec *tr)
rcvError(tr, EDecodeError, "invalid alert");
if(p[0] == 1) {
if(p[1] == ECloseNotify) {
- rcvError(tr, ECloseNotify, "remote close");
tlsSetState(tr, SRemoteClosed);
+// handclose(tr, "remote close");
+ error("remote close");
}
/*
- * ignore EUserCancelled, it's meaningless
- * need to handle ENoRenegotiation
+ * propate messages to handshaker
+ * EUserCancelled ENoRenegotiation
+ZZZ better comment, better thoughts about this
*/
+// if(p[1] == ENoRenegotiation)
+// handclose(tr, "no renegotiation");
+// if(p[1] == EUserCancelled)
+// handclose(tr, "user cancelled");
} else {
rcvAlert(tr, p[1]);
}
@@ 752,9 760,13 @@ tlsrecread(TlsRec *tr)
* if there isn't any handshaker, ignore the request,
* but notify the other side we are doing so.
*/
+ lock(&tr->hqlock);
if(tr->handq != nil){
+ tr->hqref++;
+ unlock(&tr->hqlock);
qbwrite(tr->handq, b);
b = nil;
+ dechandq(tr);
}else if(tr->verset && tr->version != SSL3Version)
sendAlert(tr, ENoRenegotiation);
break;
@@ 801,7 813,7 @@ tlsbread(Chan *c, long n, ulong offset)
}
qlock(&tr->in.io);
if(TYPE(c->qid) == Qdata){
-//ZZZ race setting state
+//ZZZ race on state
if(tr->state != SOpen)
error(Ebadusefd);
while(tr->processed == nil)
@@ 812,7 824,7 @@ tlsbread(Chan *c, long n, ulong offset)
qunlock(&tr->in.io);
poperror();
}else{
-//ZZZ race setting state
+//ZZZ race on state
while(tr->state == SHandshake && !qcanread(tr->handq))
tlsrecread(tr);
qunlock(&tr->in.io);
@@ 906,7 918,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
while(bb != nil){
//ZZZ race on state
- if(tr->state != SHandshake && tr->state != SOpen)
+ if(tr->state != SHandshake && tr->state != SOpen && tr->state != SRemoteClosed)
error(Ebadusefd);
/*
@@ 949,7 961,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
put64(seq, out->seq);
out->seq++;
(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp);
- b->wp += maclen;
+ nb->wp += maclen;
n += maclen;
/* update length */
@@ 958,6 970,14 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
/* encrypt */
rc4(&out->sec->rc4, p+RecHdrLen, n);
}
+ if(type == RChangeCipherSpec){
+ if(out->new == nil)
+ error("change cipher without a new cipher");
+ free(out->sec);
+ out->sec = out->new;
+ out->new = nil;
+ out->seq = 0;
+ }
qunlock(&out->seclock);
poperror();
@@ 987,13 1007,10 @@ tlsbwrite(Chan *c, Block *b, ulong offset)
default:
return devbwrite(c, b, offset);
case Qhand:
-//ZZZ race setting state
-// if(tr->state != SHandshake && tr->state != SOpen)
-// error(Ebadusefd);
tlsrecwrite(tr, RHandshake, b);
break;
case Qdata:
-//ZZZ race setting state
+//ZZZ race on state
if(tr->state != SOpen)
error(Ebadusefd);
tlsrecwrite(tr, RApplication, b);
@@ 1172,7 1189,14 @@ tlswrite(Chan *c, void *a, long n, vlong off)
}else if(strcmp(cb->f[0], "opened") == 0){
if(cb->nf != 1)
error("usage: opened");
- tlsSetState(tr, SOpen);
+ lock(&tr->statelk);
+ if(tr->state != SHandshake && tr->state != SOpen){
+ unlock(&tr->statelk);
+//ZZZ bad error message
+ error("can't set open state");
+ }
+ tr->state = SOpen;
+ unlock(&tr->statelk);
}else if(strcmp(cb->f[0], "alert") == 0){
if(cb->nf != 2)
error("usage: alert n");
@@ 1195,24 1219,19 @@ tlswrite(Chan *c, void *a, long n, vlong off)
if(tr->out.new == nil)
error("can't change cipher spec without setting secret");
- toc = tr->out.new;
- tr->out.new = nil;
-
-//ZZZ minor race; worth fixing?
qunlock(&tr->in.seclock);
qunlock(&tr->out.seclock);
poperror();
free(cb);
poperror();
-
+
+ /*
+ * the real work is done as the message is written
+ * so the stream is encrypted in sync.
+ */
b = allocb(1);
*b->wp++ = 1;
tlsrecwrite(tr, RChangeCipherSpec, b);
-
- qlock(&tr->out.seclock);
- free(tr->out.sec);
- tr->out.sec = toc;
- qunlock(&tr->out.seclock);
return n;
}else if(strcmp(cb->f[0], "secret") == 0){
if(cb->nf != 5)
@@ 1379,24 1398,26 @@ sendAlert(TlsRec *tr, int err)
tlsSetState(tr, SError);
}
+/*
+ * got a fatal alert message
+ */
static void
rcvAlert(TlsRec *tr, int err)
{
char *s;
- int i, fatal;
+ int i;
s = "unknown error";
- fatal = 1;
for(i=0; i < nelem(tlserrs); i++){
if(tlserrs[i].err == err){
s = tlserrs[i].msg;
- fatal = tlserrs[i].fatal;
break;
}
}
//ZZZ need to kill session if fatal error
- if(fatal)
- tlsSetState(tr, SError);
+// handclose(tr, err);
+ tlshangup(tr);
+ tlsSetState(tr, SError);
error(s);
}
@@ 1424,21 1445,24 @@ tlsSetState(TlsRec *tr, int newstate)
/* hand up a digest connection */
static void
-tlshangup(TlsRec *s)
+tlshangup(TlsRec *tr)
{
Block *b;
- qlock(&s->in.io);
- for(b = s->processed; b; b = s->processed){
- s->processed = b->next;
+ qlock(&tr->in.io);
+ for(b = tr->processed; b; b = tr->processed){
+ tr->processed = b->next;
freeb(b);
}
- if(s->unprocessed != nil){
- freeb(s->unprocessed);
- s->unprocessed = nil;
+ if(tr->unprocessed != nil){
+ freeb(tr->unprocessed);
+ tr->unprocessed = nil;
}
- s->state = SClosed;
- qunlock(&s->in.io);
+ qunlock(&tr->in.io);
+
+ tlsrecwrite(tr, RAlert, ECloseNotify);
+
+ tlsSetState(tr, SClosed);
}
static TlsRec*
M port/parse.c => port/parse.c +10 -3
@@ 13,17 13,24 @@ parsecmd(char *p, int n)
{
Cmdbuf *volatile cb;
- cb = smalloc(sizeof(*cb));
+ if(up != nil)
+ cb = smalloc(sizeof(*cb));
+ else{
+ cb = malloc(sizeof(*cb));
+ if(cb == nil)
+ return nil;
+ }
if(n > sizeof(cb->buf)-1)
n = sizeof(cb->buf)-1;
- if(waserror()){
+ if(up != nil && waserror()){
free(cb);
nexterror();
}
memmove(cb->buf, p, n);
- poperror();
+ if(up != nil)
+ poperror();
if(n > 0 && cb->buf[n-1] == '\n')
n--;
M port/portdat.h => port/portdat.h +0 -1
@@ 159,7 159,6 @@ struct Chan
Qid pgrpid; /* for #p/notepg */
Mnt* mntptr; /* for devmnt */
ulong mid; /* for ns in devproc */
- char tag[4]; /* for iproute */
};
Chan* mchan; /* channel to mounted server */
Qid mqid; /* qid of root of mount point */