~kris/9p

9hist

1f37cd7a5dd0fcee32702101f6bb3966761ff814 — David du Colombier 23 years ago 9b4ec8b
Plan 9 from Bell Labs 2003-05-09
6 files changed, 401 insertions(+), 78 deletions(-)

M port/chan.c
M port/devmnt.c
M port/error.h
M port/portdat.h
M port/portfns.h
M port/sysfile.c
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);