M port/chan.c => port/chan.c +49 -39
@@ 49,7 49,7 @@ dumpmount(void) /* DEBUGGING */
char*
-c2name(Chan *c) /* DEBUGGING */
+channame(Chan *c) /* DEBUGGING */
{
if(c == nil)
return "<nil chan>";
@@ 243,6 243,7 @@ newchan(void)
c->ref = 1;
c->dev = 0;
c->offset = 0;
+ c->devoffset = 0;
c->iounit = 0;
c->umh = 0;
c->uri = 0;
@@ 253,6 254,7 @@ newchan(void)
c->mux = 0;
memset(&c->mqid, 0, sizeof(c->mqid));
c->name = 0;
+ c->ismtpt = 0;
return c;
}
@@ 327,6 329,12 @@ chanfree(Chan *c)
{
c->flag = CFREE;
+ if(c->dirrock != nil){
+ free(c->dirrock);
+ c->dirrock = 0;
+ c->nrock = 0;
+ c->mrock = 0;
+ }
if(c->umh != nil){
putmhead(c->umh);
c->umh = nil;
@@ 655,12 663,12 @@ findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
rlock(&pg->ns);
for(m = MOUNTH(pg, qid); m; m = m->hash){
rlock(&m->lock);
-if(m->from == nil){
- print("m %p m->from 0\n", m);
- runlock(&m->lock);
- continue;
-}
- if(eqchantdqid(m->from, type, dev, qid, 1)) {
+ if(m->from == nil){
+ print("m %p m->from 0\n", m);
+ runlock(&m->lock);
+ continue;
+ }
+ if(eqchantdqid(m->from, type, dev, qid, 1)){
runlock(&pg->ns);
if(mp != nil){
incref(m);
@@ 771,9 779,7 @@ walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
cclose(c);
strcpy(up->errstr, Enotdir);
if(mh != nil)
-{print("walk 1\n");
putmhead(mh);
-}
return -1;
}
ntry = nnames - nhave;
@@ 801,7 807,7 @@ walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
/* try a union mount, if any */
if(mh && !nomount){
/*
- * mh->mount == c, so start at mh->mount->next
+ * mh->mount->to == c, so start at mh->mount->next
*/
rlock(&mh->lock);
for(f = mh->mount->next; f; f = f->next)
@@ 825,14 831,14 @@ walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
}
nmh = nil;
- if(dotdot) {
+ if(dotdot){
assert(wq->nqid == 1);
assert(wq->clone != nil);
cname = addelem(cname, "..");
nc = undomount(wq->clone, cname);
n = 1;
- } else {
+ }else{
nc = nil;
if(!nomount)
for(i=0; i<wq->nqid && i<ntry-1; i++)
@@ 1023,26 1029,42 @@ memrchr(void *va, int c, long n)
return nil;
}
+void
+nameerror(char *name, char *error)
+{
+ int len;
+ char tmperr[ERRMAX], *p;
+
+ strcpy(tmperr, error); /* error might be in genbuf or tmperr */
+ len = strlen(name);
+ if(len < ERRMAX/3 || (p=strrchr(name, '/'))==nil || p==name)
+ snprint(up->genbuf, sizeof up->genbuf, "%s", name);
+ else
+ snprint(up->genbuf, sizeof up->genbuf, "...%s", p);
+ snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, tmperr);
+ nexterror();
+}
+
/*
* Turn a name into a channel.
* &name[0] is known to be a valid address. It may be a kernel address.
*
- * Opening with amode Aopen, Acreate, or Aremove guarantees
+ * Opening with amode Aopen, Acreate, Aremove, or Aaccess guarantees
* that the result will be the only reference to that particular fid.
* This is necessary since we might pass the result to
* devtab[]->remove().
*
- * Opening Atodir, Amount, or Aaccess does not guarantee this.
+ * Opening Atodir or Amount does not guarantee this.
*
- * Opening Aaccess can, under certain conditions, return a
- * correct Chan* but with an incorrect Cname attached.
- * Since the functions that open Aaccess (sysstat, syswstat, sys_stat)
- * do not use the Cname*, this avoids an unnecessary clone.
+ * Under certain circumstances, opening Aaccess will cause
+ * an unnecessary clone in order to get a cunique Chan so it
+ * can attach the correct name. Sysstat and sys_stat need the
+ * correct name so they can rewrite the stat info.
*/
Chan*
namec(char *aname, int amode, int omode, ulong perm)
{
- int n, prefix, len, t, nomount, npath;
+ int n, t, nomount, npath;
Chan *c, *cnew;
Cname *cname;
Elemlist e;
@@ 1107,7 1129,6 @@ namec(char *aname, int amode, int omode, ulong perm)
incref(c);
break;
}
- prefix = name - aname;
e.name = nil;
e.elems = nil;
@@ 1134,8 1155,7 @@ namec(char *aname, int amode, int omode, ulong perm)
/* perm must have DMDIR if last element is / or /. */
if(e.mustbedir && !(perm&DMDIR)){
npath = e.nelems;
- strcpy(tmperrbuf, "create without DMDIR");
- goto NameError;
+ nameerror(aname, "create without DMDIR");
}
/* don't try to walk the last path element just yet. */
@@ 1149,21 1169,12 @@ namec(char *aname, int amode, int omode, ulong perm)
print("namec %s walk error npath=%d\n", aname, npath);
nexterror();
}
- strcpy(tmperrbuf, up->errstr);
- NameError:
- len = prefix+e.off[npath];
- if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname)
- snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname);
- else
- snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name);
- snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf);
- nexterror();
+ nameerror(aname, up->errstr);
}
if(e.mustbedir && !(c->qid.type&QTDIR)){
npath = e.nelems;
- strcpy(tmperrbuf, "not a directory");
- goto NameError;
+ nameerror(aname, "not a directory");
}
if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
@@ 1172,11 1183,6 @@ namec(char *aname, int amode, int omode, ulong perm)
}
switch(amode){
- case Aaccess:
- if(!nomount)
- domount(&c, nil);
- break;
-
case Abind:
m = nil;
if(!nomount)
@@ 1186,6 1192,7 @@ namec(char *aname, int amode, int omode, ulong perm)
c->umh = m;
break;
+ case Aaccess:
case Aremove:
case Aopen:
Open:
@@ 1203,7 1210,11 @@ namec(char *aname, int amode, int omode, ulong perm)
cnameclose(c->name);
c->name = cname;
+ /* record whether c is on a mount point */
+ c->ismtpt = m!=nil;
+
switch(amode){
+ case Aaccess:
case Aremove:
putmhead(m);
break;
@@ 1215,7 1226,6 @@ if(c->umh != nil){
putmhead(c->umh);
c->umh = nil;
}
-
/* only save the mount head if it's a multiple element union */
if(m && m->mount && m->mount->next)
c->umh = m;
M port/devmnt.c => port/devmnt.c +2 -2
@@ 1151,12 1151,12 @@ mntchk(Chan *c)
/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
if(c->mchan == nil)
- panic("mntchk 1: nil mchan c %s\n", c2name(c));
+ panic("mntchk 1: nil mchan c %s\n", channame(c));
m = c->mchan->mux;
if(m == nil)
- print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
+ print("mntchk 2: nil mux c %s c->mchan %s \n", channame(c), channame(c->mchan));
/*
* Was it closed and reused (was error(Eshutdown); now, it can't happen)
M port/error.h => port/error.h +1 -0
@@ 1,6 1,7 @@
extern char Enoerror[]; /* no error */
extern char Emount[]; /* inconsistent mount */
extern char Eunmount[]; /* not mounted */
+extern char Eismtpt[]; /* is a mount point */
extern char Eunion[]; /* not in union */
extern char Emountrpc[]; /* mount rpc error */
extern char Eshutdown[]; /* device shut down */
M port/portdat.h => port/portdat.h +7 -1
@@ 155,7 155,8 @@ struct Chan
Ref;
Chan* next; /* allocation */
Chan* link;
- vlong offset; /* in file */
+ vlong offset; /* in fd */
+ vlong devoffset; /* in underlying device; see read */
ushort type;
ulong dev;
ushort mode; /* read/write */
@@ 168,6 169,11 @@ struct Chan
QLock umqlock; /* serialize unionreads */
int uri; /* union read index */
int dri; /* devdirread index */
+ uchar* dirrock; /* directory entry rock for translations */
+ int nrock;
+ int mrock;
+ QLock rockqlock;
+ int ismtpt;
ulong mountid;
Mntcache *mcp; /* Mount cache pointer */
Mnt *mux; /* Mnt for clients using me for messages */
M port/portfns.h => port/portfns.h +4 -1
@@ 16,7 16,7 @@ int blocklen(Block*);
void cachedel(Image*, ulong);
void cachepage(Page*, Image*);
void callwithureg(void(*)(Ureg*));
-char* c2name(Chan*);
+char* channame(Chan*);
int cangetc(void*);
int canlock(Lock*);
int canpage(Proc*);
@@ 91,6 91,7 @@ int emptystr(char*);
int encrypt(void*, void*, int);
void envcpy(Egrp*, Egrp*);
int eqchan(Chan*, Chan*, int);
+int eqchantdqid(Chan*, int, int, Qid, int);
int eqqid(Qid, Qid);
void error(char*);
long execregs(ulong, ulong, ulong);
@@ 100,6 101,7 @@ uvlong fastticks(uvlong*);
int fault(ulong, int);
void fdclose(int, int);
Chan* fdtochan(int, int, int, int);
+int findmount(Chan**, Mhead**, int, int, Qid);
int fixfault(Segment*, ulong, int, int);
void flushmmu(void);
void forkchild(Proc*, Ureg*);
@@ 180,6 182,7 @@ ulong ms2tk(ulong);
uvlong ms2fastticks(ulong);
void muxclose(Mnt*);
Chan* namec(char*, int, int, ulong);
+void nameerror(char*, char*);
#define nelem(x) (sizeof(x)/sizeof(x[0]))
Chan* newchan(void);
int newfd(Chan*);
M port/sysfile.c => port/sysfile.c +338 -35
@@ 342,9 342,9 @@ unionread(Chan *c, void *va, long n)
mount = mount->next;
nr = 0;
- while(mount != nil) {
+ while(mount != nil){
/* Error causes component of union to be skipped */
- if(mount->to && !waserror()) {
+ if(mount->to && !waserror()){
if(c->umc == nil){
c->umc = cclone(mount->to);
c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
@@ 359,7 359,7 @@ unionread(Chan *c, void *va, long n)
/* Advance to next element */
c->uri++;
- if(c->umc) {
+ if(c->umc){
cclose(c->umc);
c->umc = nil;
}
@@ 370,52 370,314 @@ unionread(Chan *c, void *va, long n)
return nr;
}
+static void
+unionrewind(Chan *c)
+{
+ qlock(&c->umqlock);
+ c->uri = 0;
+ if(c->umc){
+ cclose(c->umc);
+ c->umc = nil;
+ }
+ qunlock(&c->umqlock);
+}
+
+static void
+dirqid(uchar *p, Qid *q)
+{
+ p += BIT16SZ+BIT16SZ+BIT32SZ;
+ q->type = GBIT8(p);
+ p += BIT8SZ;
+ q->vers = GBIT32(p);
+ p += BIT32SZ;
+ q->path = GBIT64(p);
+}
+
+static char*
+dirname(uchar *p, int *n)
+{
+ p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ
+ + BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;
+ *n = GBIT16(p);
+ return (char*)p+BIT16SZ;
+}
+
+static long
+dirsetname(char *name, int len, uchar *p, long n, long maxn)
+{
+ char *oname;
+ int olen;
+ long nn;
+
+ if(n == BIT16SZ)
+ return BIT16SZ;
+
+ oname = dirname(p, &olen);
+
+ nn = n+len-olen;
+ PBIT16(p, nn-BIT16SZ);
+ if(nn > maxn)
+ return BIT16SZ;
+
+ if(len != olen)
+ memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));
+ PBIT16((uchar*)(oname-2), len);
+ memmove(oname, name, len);
+ return nn;
+}
+
+/*
+ * Mountfix might have caused the fixed results of the directory read
+ * to overflow the buffer. Catch the overflow in c->dirrock.
+ */
+static void
+mountrock(Chan *c, uchar *p, uchar **pe)
+{
+ uchar *e, *r;
+ int len, n;
+
+ e = *pe;
+
+ /* find last directory entry */
+ for(;;){
+ len = BIT16SZ+GBIT16(p);
+ if(p+len >= e)
+ break;
+ p += len;
+ }
+
+ /* save it away */
+ qlock(&c->rockqlock);
+ if(c->nrock+len > c->mrock){
+ n = ROUND(c->nrock+len, 1024);
+ r = smalloc(n);
+ memmove(r, c->dirrock, c->nrock);
+ free(c->dirrock);
+ c->dirrock = r;
+ c->mrock = n;
+ }
+ memmove(c->dirrock+c->nrock, p, len);
+ c->nrock += len;
+ qunlock(&c->rockqlock);
+
+ /* drop it */
+ *pe = p;
+}
+
+/*
+ * Satisfy a directory read with the results saved in c->dirrock.
+ */
+static int
+mountrockread(Chan *c, uchar *op, long n, long *nn)
+{
+ long dirlen;
+ uchar *rp, *erp, *ep, *p;
+
+ /* common case */
+ if(c->nrock == 0)
+ return 0;
+
+ /* copy out what we can */
+ qlock(&c->rockqlock);
+ rp = c->dirrock;
+ erp = rp+c->nrock;
+ p = op;
+ ep = p+n;
+ while(rp+BIT16SZ <= erp){
+ dirlen = BIT16SZ+GBIT16(rp);
+ if(p+dirlen > ep)
+ break;
+ memmove(p, rp, dirlen);
+ p += dirlen;
+ rp += dirlen;
+ }
+
+ if(p == op){
+ qunlock(&c->rockqlock);
+ return 0;
+ }
+
+ /* shift the rest */
+ if(rp != erp)
+ memmove(c->dirrock, rp, erp-rp);
+ c->nrock = erp - rp;
+
+ *nn = p - op;
+ qunlock(&c->rockqlock);
+ return 1;
+}
+
+static void
+mountrewind(Chan *c)
+{
+ c->nrock = 0;
+}
+
+/*
+ * Rewrite the results of a directory read to reflect current
+ * name space bindings and mounts. Specifically, replace
+ * directory entries for bind and mount points with the results
+ * of statting what is mounted there. Except leave the old names.
+ */
+static long
+mountfix(Chan *c, uchar *op, long n, long maxn)
+{
+ char *name;
+ int nbuf, nname;
+ Chan *nc;
+ Mhead *mh;
+ Mount *m;
+ uchar *p;
+ int dirlen, rest;
+ long l;
+ Qid q;
+ uchar *buf, *e;
+
+ p = op;
+ buf = nil;
+ nbuf = 0;
+ for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){
+ dirlen = BIT16SZ+GBIT16(p);
+ if(p+dirlen > e)
+ break;
+ dirqid(p, &q);
+ nc = nil;
+ mh = nil;
+ if(findmount(&nc, &mh, c->type, c->dev, q)){
+ /*
+ * If it's a union directory and the original is
+ * in the union, don't rewrite anything.
+ */
+ for(m=mh->mount; m; m=m->next)
+ if(eqchantdqid(m->to, c->type, c->dev, q, 1))
+ goto Norewrite;
+
+ name = dirname(p, &nname);
+ // print("mnted %.*s\n", utfnlen(name, nname), name);
+ /*
+ * Do the stat but fix the name. If it fails, leave old entry.
+ * BUG: If it fails because there isn't room for the entry,
+ * what can we do? Nothing, really. Might as well skip it.
+ */
+ if(buf == nil){
+ buf = smalloc(4096);
+ nbuf = 4096;
+ }
+ if(waserror())
+ goto Norewrite;
+ l = devtab[nc->type]->stat(nc, buf, nbuf);
+ l = dirsetname(name, nname, buf, l, nbuf);
+ if(l == BIT16SZ)
+ goto Norewrite;
+ poperror();
+
+ /*
+ * Shift data in buffer to accomodate new entry,
+ * possibly overflowing into rock.
+ */
+ rest = e - (p+dirlen);
+ if(l > dirlen){
+ while(p+l+rest > op+maxn){
+ mountrock(c, p, &e);
+ if(e == p){
+ dirlen = 0;
+ goto Norewrite;
+ }
+ rest = e - (p+dirlen);
+ }
+ }
+ if(l != dirlen){
+ memmove(p+l, p+dirlen, rest);
+ dirlen = l;
+ e = p+dirlen+rest;
+ }
+
+ /*
+ * Rewrite directory entry.
+ */
+ memmove(p, buf, l);
+
+ Norewrite:
+ cclose(nc);
+ putmhead(mh);
+ }
+ }
+ if(buf)
+ free(buf);
+
+ if(p != e)
+ error("oops in rockfix");
+
+ return e-op;
+}
+
static long
read(ulong *arg, vlong *offp)
{
int dir;
- long n;
+ long n, nn, nnn;
+ uchar *p;
Chan *c;
vlong off;
n = arg[2];
validaddr(arg[1], n, 1);
+ p = (void*)arg[1];
c = fdtochan(arg[0], OREAD, 1, 1);
- if(waserror()) {
+ if(waserror()){
cclose(c);
nexterror();
}
- dir = c->qid.type&QTDIR;
/*
- * The offset is passed through on directories, normally. sysseek complains but
- * pread is used by servers and e.g. exportfs that shouldn't need to worry about this issue.
+ * The offset is passed through on directories, normally.
+ * Sysseek complains, but pread is used by servers like exportfs,
+ * that shouldn't need to worry about this issue.
+ *
+ * Notice that c->devoffset is the offset that c's dev is seeing.
+ * The number of bytes read on this fd (c->offset) may be different
+ * due to rewritings in rockfix.
*/
-
if(offp == nil) /* use and maintain channel's offset */
off = c->offset;
else
off = *offp;
-
if(off < 0)
error(Enegoff);
- if(dir && c->umh)
- n = unionread(c, (void*)arg[1], n);
- else
- n = devtab[c->type]->read(c, (void*)arg[1], n, off);
+ if(off == 0){ /* rewind to the beginning of the directory */
+ if(offp == nil){
+ c->offset = 0;
+ c->devoffset = 0;
+ }
+ mountrewind(c);
+ unionrewind(c);
+ }
- if(offp == nil){
- lock(c);
- c->offset += n;
- unlock(c);
+ dir = c->qid.type&QTDIR;
+ if(dir && mountrockread(c, p, n, &nn)){
+ /* do nothing: mountrockread filled buffer */
+ }else{
+ if(dir && c->umh)
+ nn = unionread(c, p, n);
+ else
+ nn = devtab[c->type]->read(c, p, n, off);
}
+ if(dir)
+ nnn = mountfix(c, p, nn, n);
+ else
+ nnn = nn;
+
+ lock(c);
+ c->devoffset += nn;
+ c->offset += nnn;
+ unlock(c);
poperror();
cclose(c);
- return n;
+ return nnn;
}
long
@@ 635,6 897,21 @@ validstat(uchar *s, int n)
validname(buf, 0);
}
+static char*
+cnamelast(Cname *n)
+{
+ char *s;
+
+ if(n == nil)
+ return nil;
+ if(n->len == 0)
+ return nil;
+ s = strrchr(n->s, '/');
+ if(s)
+ return s+1;
+ return n->s;
+}
+
long
sysfstat(ulong *arg)
{
@@ 657,6 934,7 @@ sysfstat(ulong *arg)
long
sysstat(ulong *arg)
{
+ char *name;
Chan *c;
uint l;
@@ 669,6 947,10 @@ sysstat(ulong *arg)
nexterror();
}
l = devtab[c->type]->stat(c, (uchar*)arg[1], l);
+ name = cnamelast(c->name);
+ if(name)
+ l = dirsetname(name, strlen(name), (uchar*)arg[1], l, arg[2]);
+
poperror();
cclose(c);
return l;
@@ 859,6 1141,12 @@ sysremove(ulong *arg)
cclose(c);
nexterror();
}
+ /*
+ * Removing mount points is disallowed to avoid surprises
+ * (which should be removed: the mount point or the mounted Chan?).
+ */
+ if(c->ismtpt)
+ error(Eismtpt);
devtab[c->type]->remove(c);
/*
* Remove clunks the fid, but we need to recover the Chan
@@ 870,6 1158,31 @@ sysremove(ulong *arg)
return 0;
}
+static long
+wstat(Chan *c, uchar *d, int nd)
+{
+ long l;
+ int namelen;
+
+ if(waserror()){
+ cclose(c);
+ nexterror();
+ }
+ if(c->ismtpt){
+ /*
+ * Renaming mount points is disallowed to avoid surprises
+ * (which should be renamed: the mount point or the mounted Chan?).
+ */
+ dirname(d, &namelen);
+ if(namelen)
+ nameerror(channame(c), Eismtpt);
+ }
+ l = devtab[c->type]->wstat(c, d, nd);
+ poperror();
+ cclose(c);
+ return l;
+}
+
long
syswstat(ulong *arg)
{
@@ 881,14 1194,7 @@ syswstat(ulong *arg)
validstat((uchar*)arg[1], l);
validaddr(arg[0], 1, 0);
c = namec((char*)arg[0], Aaccess, 0, 0);
- if(waserror()){
- cclose(c);
- nexterror();
- }
- l = devtab[c->type]->wstat(c, (uchar*)arg[1], l);
- poperror();
- cclose(c);
- return l;
+ return wstat(c, (uchar*)arg[1], l);
}
long
@@ 901,14 1207,7 @@ sysfwstat(ulong *arg)
validaddr(arg[1], l, 0);
validstat((uchar*)arg[1], l);
c = fdtochan(arg[0], -1, 1, 1);
- if(waserror()) {
- cclose(c);
- nexterror();
- }
- l = devtab[c->type]->wstat(c, (uchar*)arg[1], l);
- poperror();
- cclose(c);
- return l;
+ return wstat(c, (uchar*)arg[1], l);
}
static void
@@ 980,6 1279,7 @@ long
sys_fstat(ulong *arg)
{
Chan *c;
+ char *name;
uint l;
uchar buf[128]; /* old DIRLEN plus a little should be plenty */
char strs[128];
@@ 996,6 1296,9 @@ sys_fstat(ulong *arg)
/* buf contains a new stat buf; convert to old. yuck. */
if(l <= BIT16SZ) /* buffer too small; time to face reality */
error(old);
+ name = cnamelast(c->name);
+ if(name)
+ l = dirsetname(name, strlen(name), (uchar*)arg[1], l, arg[2]);
l = convM2D(buf, l, &d, strs);
if(l == 0)
error(old);