From 1f37cd7a5dd0fcee32702101f6bb3966761ff814 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 9 May 2003 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2003-05-09 --- port/chan.c | 88 ++++++------ port/devmnt.c | 4 +- port/error.h | 1 + port/portdat.h | 8 +- port/portfns.h | 5 +- port/sysfile.c | 373 ++++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 401 insertions(+), 78 deletions(-) diff --git a/port/chan.c b/port/chan.c index 47a508ea18190dae9bd684ff010b373fcf765b8b..53173789c4ed387ea2937d49253844147e132d30 100644 --- a/port/chan.c +++ b/port/chan.c @@ -49,7 +49,7 @@ dumpmount(void) /* DEBUGGING */ char* -c2name(Chan *c) /* DEBUGGING */ +channame(Chan *c) /* DEBUGGING */ { if(c == nil) return ""; @@ -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; inqid && igenbuf, 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; diff --git a/port/devmnt.c b/port/devmnt.c index b1ff46708488b2fcb4b7b1c3d1e1ceb72338ee5a..22d1325b256f15d594949b501d79031aaf73922e 100644 --- a/port/devmnt.c +++ b/port/devmnt.c @@ -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) diff --git a/port/error.h b/port/error.h index 6bb87622e669ebd0cab6947ac021eb51d3f622a9..4401189ba3c7798034779632cd8b39a53ce7bcc5 100644 --- a/port/error.h +++ b/port/error.h @@ -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 */ diff --git a/port/portdat.h b/port/portdat.h index 077f157cb147cb56a44917a23e594b69230b735f..95b1ef0a42ddc1e0b9eb2f45c01300a4700e4ab3 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -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 */ diff --git a/port/portfns.h b/port/portfns.h index 431d410ae8c2f09e384322e62e1cb1abd8d05f68..f216c7335f477aec622f13de34e20878b1071f96 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -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*); diff --git a/port/sysfile.c b/port/sysfile.c index 3393be56ae2424180c81f599af668befc34dfe2d..ba5a0e308e03278728d73812b3c7769f12387fe0 100644 --- a/port/sysfile.c +++ b/port/sysfile.c @@ -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) + 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);