shithub: drawterm-fdroid

Download patch

ref: 1583a55d91725ddf4166b0c0fe7623a88a5f9cd3
parent: cdb9a6e131c162f8c6f29b4fb131bb78b9ea7928
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Tue Apr 4 20:25:04 EDT 2017

kern: merge with chan.c, dev.c, pgrp.c from 9front, fix waserror() botch in devwalk()

--- a/kern/chan.c
+++ b/kern/chan.c
@@ -4,69 +4,15 @@
 #include	"fns.h"
 #include	"error.h"
 
-int chandebug=0;		/* toggled by sysr1 */
-QLock chanprint;		/* probably asking for trouble (deadlocks) -rsc */
-
-int domount(Chan**, Mhead**);
-
-void
-dumpmount(void)		/* DEBUGGING */
-{
-	Pgrp *pg;
-	Mount *t;
-	Mhead **h, **he, *f;
-
-	if(up == nil){
-		print("no process for dumpmount\n");
-		return;
-	}
-	pg = up->pgrp;
-	if(pg == nil){
-		print("no pgrp for dumpmount\n");
-		return;
-	}
-	rlock(&pg->ns);
-	if(waserror()) {
-		runlock(&pg->ns);
-		nexterror();
-	}
-
-	he = &pg->mnthash[MNTHASH];
-	for(h = pg->mnthash; h < he; h++) {
-		for(f = *h; f; f = f->hash) {
-			print("head: %p: %s 0x%llux.%lud %C %lud -> \n", f,
-				f->from->name->s, f->from->qid.path,
-				f->from->qid.vers, devtab[f->from->type]->dc,
-				f->from->dev);
-			for(t = f->mount; t; t = t->next)
-				print("\t%p: %s (umh %p) (path %.8llux dev %C %lud)\n", t, t->to->name->s, t->to->umh, t->to->qid.path, devtab[t->to->type]->dc, t->to->dev);
-		}
-	}
-	poperror();
-	runlock(&pg->ns);
-}
-
-
-char*
-chanpath(Chan *c)
-{
-	if(c == nil)
-		return "<nil chan>";
-	if(c->name == nil)
-		return "<nil name>";
-	if(c->name->s == nil)
-		return "<nil name.s>";
-	return c->name->s;
-}
-
 enum
 {
-	CNAMESLOP	= 20
+	PATHSLOP	= 20,
+	PATHMSLOP	= 20,
 };
 
-struct
+static struct Chanalloc
 {
-	Lock lk;
+	Lock	lk;
 	int	fid;
 	Chan	*free;
 	Chan	*list;
@@ -76,16 +22,30 @@
 
 struct Elemlist
 {
+	char	*aname;	/* original name */
 	char	*name;	/* copy of name, so '/' can be overwritten */
 	int	nelems;
 	char	**elems;
 	int	*off;
 	int	mustbedir;
+	int	nerror;
+	int	prefix;
 };
 
 #define SEP(c) ((c) == 0 || (c) == '/')
-void cleancname(Cname*);
 
+char*
+chanpath(Chan *c)
+{
+	if(c == nil)
+		return "<nil chan>";
+	if(c->path == nil)
+		return "<nil path>";
+	if(c->path->s == nil)
+		return "<nil path.s>";
+	return c->path->s;
+}
+
 int
 isdotdot(char *p)
 {
@@ -96,7 +56,6 @@
 incref(Ref *r)
 {
 	int x;
-
 	lock(&r->lk);
 	x = ++r->ref;
 	unlock(&r->lk);
@@ -107,13 +66,11 @@
 decref(Ref *r)
 {
 	int x;
-
 	lock(&r->lk);
 	x = --r->ref;
 	unlock(&r->lk);
 	if(x < 0)
 		panic("decref, pc=0x%p", getcallerpc(&r));
-
 	return x;
 }
 
@@ -129,20 +86,19 @@
 	int nt;
 
 	nt = strlen(t);
-	if(nt+1 <= ns){
-		memmove(s, t, nt+1);
+	if(nt < ns){
+		memmove(s, t, nt);
+		s[nt] = '\0';
 		return;
 	}
-	/* too long */
-	if(ns < 4){
-		/* but very short! */
-		strncpy(s, t, ns);
-		return;
-	}
-	/* truncate with ... at character boundary (very rare case) */
-	memmove(s, t, ns-4);
+	/* too long, truncate */
+	nt = ns-1;
+	memmove(s, t, nt);
+	s[nt] = '\0';
+	/* append ... if there is space */
 	ns -= 4;
-	s[ns] = '\0';
+	if(ns < 0)
+		return;
 	/* look for first byte of UTF-8 sequence by skipping continuation bytes */
 	while(ns>0 && (s[--ns]&0xC0)==0x80)
 		;
@@ -168,17 +124,18 @@
 	int n;
 	char *t, *prev;
 
-	n = strlen(s)+1;
+	n = strlen(s);
 	/* if it's a user, we can wait for memory; if not, something's very wrong */
-	if(up){
-		t = smalloc(n);
-		setmalloctag(t, getcallerpc(&p));
-	}else{
-		t = malloc(n);
+	if(up != nil)
+		t = smalloc(n+1);
+	else{
+		t = malloc(n+1);
 		if(t == nil)
 			panic("kstrdup: no memory");
 	}
+	setmalloctag(t, getcallerpc(&p));
 	memmove(t, s, n);
+	t[n] = '\0';
 	prev = *p;
 	*p = t;
 	free(prev);
@@ -221,18 +178,19 @@
 
 	lock(&chanalloc.lk);
 	c = chanalloc.free;
-	if(c != 0)
+	if(c != nil){
 		chanalloc.free = c->next;
-	unlock(&chanalloc.lk);
-
-	if(c == nil) {
+		c->next = nil;
+	} else {
+		unlock(&chanalloc.lk);
 		c = smalloc(sizeof(Chan));
 		lock(&chanalloc.lk);
-		c->fid = ++chanalloc.fid;
 		c->link = chanalloc.list;
 		chanalloc.list = c;
-		unlock(&chanalloc.lk);
 	}
+	if(c->fid == 0)
+		c->fid = ++chanalloc.fid;
+	unlock(&chanalloc.lk);
 
 	/* if you get an error before associating with a dev,
 	   close calls rootclose, a nop */
@@ -241,82 +199,181 @@
 	c->ref.ref = 1;
 	c->dev = 0;
 	c->offset = 0;
+	c->devoffset = 0;
 	c->iounit = 0;
-	c->umh = 0;
+	c->umh = nil;
+	c->umc = nil;
 	c->uri = 0;
 	c->dri = 0;
-	c->aux = 0;
-	c->mchan = 0;
-	c->mux = 0;
+	c->dirrock = nil;
+	c->nrock = 0;
+	c->mrock = 0;
+	c->ismtpt = 0;
+	c->mux = nil;
+	c->aux = nil;
+	c->mchan = nil;
 	memset(&c->mqid, 0, sizeof(c->mqid));
-	c->name = 0;
+	c->path = nil;
+
 	return c;
 }
 
-static Ref ncname;
-
-Cname*
-newcname(char *s)
+Path*
+newpath(char *s)
 {
-	Cname *n;
 	int i;
+	Path *p;
 
-	n = smalloc(sizeof(Cname));
+	p = smalloc(sizeof(Path));
 	i = strlen(s);
-	n->len = i;
-	n->alen = i+CNAMESLOP;
-	n->s = smalloc(n->alen);
-	memmove(n->s, s, i+1);
-	n->ref.ref = 1;
-	incref(&ncname);
-	return n;
+	p->len = i;
+	p->alen = i+PATHSLOP;
+	p->s = smalloc(p->alen);
+	memmove(p->s, s, i+1);
+	p->ref.ref = 1;
+
+	/*
+	 * Cannot use newpath for arbitrary names because the mtpt 
+	 * array will not be populated correctly.  The names #/ and / are
+	 * allowed, but other names with / in them draw warnings.
+	 */
+	if(strchr(s, '/') != nil && strcmp(s, "#/") != 0 && strcmp(s, "/") != 0)
+		print("newpath: %s from %#p\n", s, getcallerpc(&s));
+
+	p->mlen = 1;
+	p->malen = PATHMSLOP;
+	p->mtpt = smalloc(p->malen*sizeof p->mtpt[0]);
+	return p;
 }
 
+static Path*
+copypath(Path *p)
+{
+	int i;
+	Path *pp;
+	
+	pp = smalloc(sizeof(Path));
+	pp->ref.ref = 1;
+	
+	pp->len = p->len;
+	pp->alen = p->alen;
+	pp->s = smalloc(p->alen);
+	memmove(pp->s, p->s, p->len+1);
+	
+	pp->mlen = p->mlen;
+	pp->malen = p->malen;
+	pp->mtpt = smalloc(p->malen*sizeof pp->mtpt[0]);
+	for(i=0; i<pp->mlen; i++){
+		pp->mtpt[i] = p->mtpt[i];
+		if(pp->mtpt[i] != nil)
+			incref(&pp->mtpt[i]->ref);
+	}
+
+	return pp;
+}
+
 void
-cnameclose(Cname *n)
+pathclose(Path *p)
 {
-	if(n == nil)
+	int i;
+
+	if(p == nil || decref(&p->ref))
 		return;
-	if(decref(&n->ref))
-		return;
-	decref(&ncname);
-	free(n->s);
-	free(n);
+	for(i=0; i<p->mlen; i++)
+		if(p->mtpt[i] != nil)
+			cclose(p->mtpt[i]);
+	free(p->mtpt);
+	free(p->s);
+	free(p);
 }
 
-Cname*
-addelem(Cname *n, char *s)
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ * (Really only called to remove a trailing .. that has been added.
+ * Otherwise would need to update n->mtpt as well.)
+ */
+static void
+fixdotdotname(Path *p)
 {
-	int i, a;
-	char *t;
-	Cname *new;
+	char *r;
 
-	if(s[0]=='.' && s[1]=='\0')
-		return n;
+	if(p->s[0] == '#'){
+		r = strchr(p->s, '/');
+		if(r == nil)
+			return;
+		cleanname(r);
 
-	if(n->ref.ref > 1){
+		/*
+		 * The correct name is #i rather than #i/,
+		 * but the correct name of #/ is #/.
+		 */
+		if(strcmp(r, "/")==0 && p->s[1] != '/')
+			*r = '\0';
+	}else
+		cleanname(p->s);
+	p->len = strlen(p->s);
+}
+
+static Path*
+uniquepath(Path *p)
+{
+	Path *new;
+	
+	if(p->ref.ref > 1){
 		/* copy on write */
-		new = newcname(n->s);
-		cnameclose(n);
-		n = new;
+		new = copypath(p);
+		pathclose(p);
+		p = new;
 	}
+	return p;
+}
 
+static Path*
+addelem(Path *p, char *s, Chan *from)
+{
+	char *t;
+	int a, i;
+	Chan *c, **tt;
+
+	if(s[0]=='.' && s[1]=='\0')
+		return p;
+
+	p = uniquepath(p);
+
 	i = strlen(s);
-	if(n->len+1+i+1 > n->alen){
-		a = n->len+1+i+1 + CNAMESLOP;
+	a = p->len+1+i+1;
+	if(a > p->alen){
+		a += PATHSLOP;
 		t = smalloc(a);
-		memmove(t, n->s, n->len+1);
-		free(n->s);
-		n->s = t;
-		n->alen = a;
+		memmove(t, p->s, p->len+1);
+		free(p->s);
+		p->s = t;
+		p->alen = a;
 	}
-	if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/')	/* don't insert extra slash if one is present */
-		n->s[n->len++] = '/';
-	memmove(n->s+n->len, s, i+1);
-	n->len += i;
-	if(isdotdot(s))
-		cleancname(n);
-	return n;
+	/* don't insert extra slash if one is present */
+	if(p->len>0 && p->s[p->len-1]!='/' && s[0]!='/')
+		p->s[p->len++] = '/';
+	memmove(p->s+p->len, s, i+1);
+	p->len += i;
+	if(isdotdot(s)){
+		fixdotdotname(p);
+		if(p->mlen > 1 && (c = p->mtpt[--p->mlen]) != nil){
+			p->mtpt[p->mlen] = nil;
+			cclose(c);
+		}
+	}else{
+		if(p->mlen >= p->malen){
+			p->malen = p->mlen+1+PATHMSLOP;
+			tt = smalloc(p->malen*sizeof tt[0]);
+			memmove(tt, p->mtpt, p->mlen*sizeof tt[0]);
+			free(p->mtpt);
+			p->mtpt = tt;
+		}
+		p->mtpt[p->mlen++] = from;
+		if(from != nil)
+			incref(&from->ref);
+	}
+	return p;
 }
 
 void
@@ -324,6 +381,12 @@
 {
 	c->flag = CFREE;
 
+	if(c->dirrock != nil){
+		free(c->dirrock);
+		c->dirrock = nil;
+		c->nrock = 0;
+		c->mrock = 0;
+	}
 	if(c->umh != nil){
 		putmhead(c->umh);
 		c->umh = nil;
@@ -341,7 +404,8 @@
 		c->mchan = nil;
 	}
 
-	cnameclose(c->name);
+	pathclose(c->path);
+	c->path = nil;
 
 	lock(&chanalloc.lk);
 	c->next = chanalloc.free;
@@ -352,8 +416,8 @@
 void
 cclose(Chan *c)
 {
-	if(c->flag&CFREE)
-		panic("cclose %p", getcallerpc(&c));
+	if(c == nil || c->ref.ref < 1 || c->flag&CFREE)
+		panic("cclose %#p", getcallerpc(&c));
 
 	if(decref(&c->ref))
 		return;
@@ -373,7 +437,7 @@
 {
 	Chan *nc;
 
-	if(c->ref.ref != 1) {
+	if(c->ref.ref != 1){
 		nc = cclone(c);
 		cclose(c);
 		c = nc;
@@ -389,11 +453,11 @@
 }
 
 int
-eqchan(Chan *a, Chan *b, int pathonly)
+eqchan(Chan *a, Chan *b, int skipvers)
 {
 	if(a->qid.path != b->qid.path)
 		return 0;
-	if(!pathonly && a->qid.vers!=b->qid.vers)
+	if(!skipvers && a->qid.vers!=b->qid.vers)
 		return 0;
 	if(a->type != b->type)
 		return 0;
@@ -403,11 +467,11 @@
 }
 
 int
-eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int skipvers)
 {
 	if(a->qid.path != qid.path)
 		return 0;
-	if(!pathonly && a->qid.vers!=qid.vers)
+	if(!skipvers && a->qid.vers!=qid.vers)
 		return 0;
 	if(a->type != type)
 		return 0;
@@ -425,34 +489,56 @@
 	mh->ref.ref = 1;
 	mh->from = from;
 	incref(&from->ref);
+	return mh;
+}
 
 /*
-	n = from->name->len;
-	if(n >= sizeof(mh->fromname))
-		n = sizeof(mh->fromname)-1;
-	memmove(mh->fromname, from->name->s, n);
-	mh->fromname[n] = 0;
-*/
-	return mh;
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ *	- the mhead in the namespace hash table
+ *	- the mhead in chans returned from findmount:
+ *	  used in namec and then by unionread.
+ *	- the mhead in chans returned from createdir:
+ *	  used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted in cunmount() and closepgrp().
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as 
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+	if(m != nil && decref(&m->ref) == 0){
+		assert(m->mount == nil);
+		cclose(m->from);
+		free(m);
+	}
 }
 
 int
 cmount(Chan **newp, Chan *old, int flag, char *spec)
 {
-	Pgrp *pg;
 	int order, flg;
+	Chan *new;
 	Mhead *m, **l, *mh;
 	Mount *nm, *f, *um, **h;
-	Chan *new;
+	Pgrp *pg;
 
 	if(QTDIR & (old->qid.type^(*newp)->qid.type))
 		error(Emount);
 
-if(old->umh)print("cmount old extra umh\n");
+	if(old->umh != nil)
+		print("cmount: unexpected umh, caller %#p\n", getcallerpc(&newp));
 
 	order = flag&MORDER;
 
-	if((old->qid.type&QTDIR)==0 && order != MREPL)
+	if((old->qid.type&QTDIR) == 0 && order != MREPL)
 		error(Emount);
 
 	new = *newp;
@@ -459,23 +545,25 @@
 	mh = new->umh;
 
 	/*
-	 * Not allowed to bind when the old directory
-	 * is itself a union.  (Maybe it should be allowed, but I don't see
-	 * what the semantics would be.)
+	 * Not allowed to bind when the old directory is itself a union. 
+	 * (Maybe it should be allowed, but I don't see what the semantics
+	 * would be.)
 	 *
 	 * We need to check mh->mount->next to tell unions apart from
 	 * simple mount points, so that things like
 	 *	mount -c fd /root
 	 *	bind -c /root /
-	 * work.  The check of mount->mflag catches things like
+	 * work.  
+	 * 
+	 * The check of mount->mflag allows things like
 	 *	mount fd /root
 	 *	bind -c /root /
 	 * 
 	 * This is far more complicated than it should be, but I don't
-	 * see an easier way at the moment.		-rsc
+	 * see an easier way at the moment.
 	 */
-	if((flag&MCREATE) && mh && mh->mount
-	&& (mh->mount->next || !(mh->mount->mflag&MCREATE)))
+	if((flag&MCREATE) != 0 && mh != nil && mh->mount != nil
+	&& (mh->mount->next != nil || (mh->mount->mflag&MCREATE) == 0))
 		error(Emount);
 
 	pg = up->pgrp;
@@ -482,13 +570,13 @@
 	wlock(&pg->ns);
 
 	l = &MOUNTH(pg, old->qid);
-	for(m = *l; m; m = m->hash) {
+	for(m = *l; m != nil; m = m->hash){
 		if(eqchan(m->from, old, 1))
 			break;
 		l = &m->hash;
 	}
 
-	if(m == nil) {
+	if(m == nil){
 		/*
 		 *  nothing mounted here yet.  create a mount
 		 *  head and add to the hash table.
@@ -501,7 +589,7 @@
 		 *  node to the mount chain.
 		 */
 		if(order != MREPL)
-			m->mount = newmount(m, old, 0, 0);
+			m->mount = newmount(old, 0, nil);
 	}
 	wlock(&m->lock);
 	if(waserror()){
@@ -510,8 +598,8 @@
 	}
 	wunlock(&pg->ns);
 
-	nm = newmount(m, new, flag, spec);
-	if(mh != nil && mh->mount != nil) {
+	nm = newmount(new, flag, spec);
+	if(mh != nil && mh->mount != nil){
 		/*
 		 *  copy a union when binding it onto a directory
 		 */
@@ -520,33 +608,31 @@
 			flg = MAFTER;
 		h = &nm->next;
 		um = mh->mount;
-		for(um = um->next; um; um = um->next) {
-			f = newmount(m, um->to, flg, um->spec);
+		for(um = um->next; um != nil; um = um->next){
+			f = newmount(um->to, flg, um->spec);
 			*h = f;
 			h = &f->next;
 		}
 	}
 
-	if(m->mount && order == MREPL) {
+	if(m->mount != nil && order == MREPL){
 		mountfree(m->mount);
-		m->mount = 0;
+		m->mount = nil;
 	}
 
 	if(flag & MCREATE)
 		nm->mflag |= MCREATE;
 
-	if(m->mount && order == MAFTER) {
-		for(f = m->mount; f->next; f = f->next)
+	if(m->mount != nil && order == MAFTER){
+		for(f = m->mount; f->next != nil; f = f->next)
 			;
 		f->next = nm;
-	}
-	else {
-		for(f = nm; f->next; f = f->next)
+	}else{
+		for(f = nm; f->next != nil; f = f->next)
 			;
 		f->next = m->mount;
 		m->mount = nm;
 	}
-
 	wunlock(&m->lock);
 	poperror();
 	return nm->mountid;
@@ -559,7 +645,7 @@
 	Mhead *m, **l;
 	Mount *f, **p;
 
-	if(mnt->umh)	/* should not happen */
+	if(mnt->umh != nil)	/* should not happen */
 		print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
 
 	/*
@@ -575,47 +661,44 @@
 	wlock(&pg->ns);
 
 	l = &MOUNTH(pg, mnt->qid);
-	for(m = *l; m; m = m->hash) {
+	for(m = *l; m != nil; m = m->hash){
 		if(eqchan(m->from, mnt, 1))
 			break;
 		l = &m->hash;
 	}
 
-	if(m == 0) {
+	if(m == nil){
 		wunlock(&pg->ns);
 		error(Eunmount);
 	}
 
 	wlock(&m->lock);
-	if(mounted == 0) {
+	f = m->mount;
+	if(mounted == nil){
 		*l = m->hash;
-		wunlock(&pg->ns);
-		mountfree(m->mount);
 		m->mount = nil;
-		cclose(m->from);
 		wunlock(&m->lock);
+		wunlock(&pg->ns);
+		mountfree(f);
 		putmhead(m);
 		return;
 	}
-
-	p = &m->mount;
-	for(f = *p; f; f = f->next) {
-		/* BUG: Needs to be 2 pass */
+	for(p = &m->mount; f != nil; f = f->next){
 		if(eqchan(f->to, mounted, 1) ||
-		  (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+		  (f->to->mchan != nil && eqchan(f->to->mchan, mounted, 1))){
 			*p = f->next;
-			f->next = 0;
-			mountfree(f);
-			if(m->mount == nil) {
+			f->next = nil;
+			if(m->mount == nil){
 				*l = m->hash;
-				cclose(m->from);
 				wunlock(&m->lock);
 				wunlock(&pg->ns);
+				mountfree(f);
 				putmhead(m);
 				return;
 			}
 			wunlock(&m->lock);
 			wunlock(&pg->ns);
+			mountfree(f);
 			return;
 		}
 		p = &f->next;
@@ -631,105 +714,119 @@
 	Chan *nc;
 	Walkqid *wq;
 
+	if(c == nil || c->ref.ref < 1 || c->flag&CFREE)
+		panic("cclone: %#p", getcallerpc(&c));
 	wq = devtab[c->type]->walk(c, nil, nil, 0);
 	if(wq == nil)
 		error("clone failed");
 	nc = wq->clone;
 	free(wq);
-	nc->name = c->name;
-	if(c->name)
-		incref(&c->name->ref);
+	if((nc->path = c->path) != nil)
+		incref(&c->path->ref);
 	return nc;
 }
 
+/* also used by sysfile.c:/^mountfix */
 int
 findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
 {
+	Chan *to;
 	Pgrp *pg;
 	Mhead *m;
 
 	pg = up->pgrp;
 	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)) {
+	for(m = MOUNTH(pg, qid); m != nil; m = m->hash){
+		if(eqchantdqid(m->from, type, dev, qid, 1)){
+			rlock(&m->lock);
 			runlock(&pg->ns);
-			if(mp != nil){
+			if(mp != nil)
 				incref(&m->ref);
-				if(*mp != nil)
-					putmhead(*mp);
+			to = m->mount->to;
+			incref(&to->ref);
+			runlock(&m->lock);
+			if(mp != nil){
+				putmhead(*mp);
 				*mp = m;
 			}
 			if(*cp != nil)
 				cclose(*cp);
-			incref(&m->mount->to->ref);
-			*cp = m->mount->to;
-			runlock(&m->lock);
+			*cp = to;
 			return 1;
 		}
-		runlock(&m->lock);
 	}
-
 	runlock(&pg->ns);
 	return 0;
 }
 
-int
-domount(Chan **cp, Mhead **mp)
+/*
+ * Calls findmount but also updates path.
+ */
+static int
+domount(Chan **cp, Mhead **mp, Path **path)
 {
-	return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid);
+	Chan **lc, *from;
+	Path *p;
+
+	if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0)
+		return 0;
+
+	if(path != nil){
+		p = *path;
+		p = uniquepath(p);
+		if(p->mlen <= 0)
+			print("domount: path %s has mlen==%d\n", p->s, p->mlen);
+		else{
+			from = (*mp)->from;
+			incref(&from->ref);
+			lc = &p->mtpt[p->mlen-1];
+			if(*lc != nil)
+				cclose(*lc);
+			*lc = from;
+		}
+		*path = p;
+	}
+	return 1;
 }
 
-Chan*
-undomount(Chan *c, Cname *name)
+/*
+ * If c is the right-hand-side of a mount point, returns the left hand side.
+ * Changes name to reflect the fact that we've uncrossed the mountpoint,
+ * so name had better be ours to change!
+ */
+static Chan*
+undomount(Chan *c, Path *path)
 {
 	Chan *nc;
-	Pgrp *pg;
-	Mount *t;
-	Mhead **h, **he, *f;
 
-	pg = up->pgrp;
-	rlock(&pg->ns);
-	if(waserror()) {
-		runlock(&pg->ns);
-		nexterror();
-	}
+	if(path->ref.ref != 1 || path->mlen == 0)
+		print("undomount: path %s ref %ld mlen %d caller %#p\n",
+			path->s, path->ref, path->mlen, getcallerpc(&c));
 
-	he = &pg->mnthash[MNTHASH];
-	for(h = pg->mnthash; h < he; h++) {
-		for(f = *h; f; f = f->hash) {
-			if(strcmp(f->from->name->s, name->s) != 0)
-				continue;
-			for(t = f->mount; t; t = t->next) {
-				if(eqchan(c, t->to, 1)) {
-					/*
-					 * We want to come out on the left hand side of the mount
-					 * point using the element of the union that we entered on.
-					 * To do this, find the element that has a from name of
-					 * c->name->s.
-					 */
-					if(strcmp(t->head->from->name->s, name->s) != 0)
-						continue;
-					nc = t->head->from;
-					incref(&nc->ref);
-					cclose(c);
-					c = nc;
-					break;
-				}
-			}
-		}
+	if(path->mlen > 0 && (nc = path->mtpt[path->mlen-1]) != nil){
+		cclose(c);
+		path->mtpt[path->mlen-1] = nil;
+		c = nc;
 	}
-	poperror();
-	runlock(&pg->ns);
 	return c;
 }
 
 /*
+ * Call dev walk but catch errors.
+ */
+static Walkqid*
+ewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	Walkqid *wq;
+
+	if(waserror())
+		return nil;
+	wq = devtab[c->type]->walk(c, nc, name, nname);
+	poperror();
+	return wq;
+}
+
+/*
  * Either walks all the way or not at all.  No partial results in *cp.
  * *nerror is the number of names to display in an error message.
  */
@@ -737,17 +834,17 @@
 int
 walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
 {
-	int dev, dotdot, i, n, nhave, ntry, type;
-	Chan *c, *nc;
-	Cname *cname;
-	Mount *f;
+	int dev, didmount, dotdot, i, n, nhave, ntry, type;
+	Chan *c, *nc, *mtpt;
+	Path *path;
 	Mhead *mh, *nmh;
+	Mount *f;
 	Walkqid *wq;
 
 	c = *cp;
 	incref(&c->ref);
-	cname = c->name;
-	incref(&cname->ref);
+	path = c->path;
+	incref(&path->ref);
 	mh = nil;
 
 	/*
@@ -757,20 +854,21 @@
 	 *    3. move to the first mountpoint along the way.
 	 *    4. repeat.
 	 *
-	 * An invariant is that each time through the loop, c is on the undomount
-	 * side of the mount point, and c's name is cname.
+	 * Each time through the loop:
+	 *
+	 *	If didmount==0, c is on the undomount side of the mount point.
+	 *	If didmount==1, c is on the domount side of the mount point.
+	 * 	Either way, c's full path is path.
 	 */
+	didmount = 0;
 	for(nhave=0; nhave<nnames; nhave+=n){
-		if((c->qid.type&QTDIR)==0){
+		if((c->qid.type&QTDIR) == 0){
 			if(nerror)
 				*nerror = nhave;
-			cnameclose(cname);
+			pathclose(path);
 			cclose(c);
-			strcpy(up->errstr, Enotdir);
-			if(mh != nil)
-{print("walk 1\n");
-				putmhead(mh);
-}
+			kstrcpy(up->errstr, Enotdir, ERRMAX);
+			putmhead(mh);
 			return -1;
 		}
 		ntry = nnames - nhave;
@@ -779,83 +877,89 @@
 		dotdot = 0;
 		for(i=0; i<ntry; i++){
 			if(isdotdot(names[nhave+i])){
-				if(i==0) {
+				if(i==0){
 					dotdot = 1;
 					ntry = 1;
-				} else
+				}else
 					ntry = i;
 				break;
 			}
 		}
 
-		if(!dotdot && !nomount)
-			domount(&c, &mh);
-
+		if(!dotdot && !nomount && !didmount)
+			domount(&c, &mh, &path);
+		
 		type = c->type;
 		dev = c->dev;
 
-		if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){
+		if((wq = ewalk(c, nil, names+nhave, ntry)) == nil){
 			/* try a union mount, if any */
-			if(mh && !nomount){
+			if(mh != nil && !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)
-					if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil)
+				if((f = mh->mount) != nil)
+					f = f->next;
+				for(; f != nil; f = f->next)
+					if((wq = ewalk(f->to, nil, names+nhave, ntry)) != nil){
+						type = f->to->type;
+						dev = f->to->dev;
 						break;
+					}
 				runlock(&mh->lock);
-				if(f != nil){
-					type = f->to->type;
-					dev = f->to->dev;
-				}
 			}
 			if(wq == nil){
 				cclose(c);
-				cnameclose(cname);
+				pathclose(path);
 				if(nerror)
 					*nerror = nhave+1;
-				if(mh != nil)
-					putmhead(mh);
+				putmhead(mh);
 				return -1;
 			}
 		}
 
-		nmh = nil;
-		if(dotdot) {
+		didmount = 0;
+		if(dotdot){
 			assert(wq->nqid == 1);
 			assert(wq->clone != nil);
 
-			cname = addelem(cname, "..");
-			nc = undomount(wq->clone, cname);
+			path = addelem(path, "..", nil);
+			nc = undomount(wq->clone, path);
+			nmh = nil;
 			n = 1;
-		} else {
+		}else{
 			nc = nil;
-			if(!nomount)
-				for(i=0; i<wq->nqid && i<ntry-1; i++)
-					if(findmount(&nc, &nmh, type, dev, wq->qid[i]))
+			nmh = nil;
+			if(!nomount){
+				for(i=0; i<wq->nqid && i<ntry-1; i++){
+					if(findmount(&nc, &nmh, type, dev, wq->qid[i])){
+						didmount = 1;
 						break;
+					}
+				}
+			}
 			if(nc == nil){	/* no mount points along path */
 				if(wq->clone == nil){
 					cclose(c);
-					cnameclose(cname);
-					if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){
+					pathclose(path);
+					if(wq->nqid == 0 || (wq->qid[wq->nqid-1].type&QTDIR) != 0){
 						if(nerror)
 							*nerror = nhave+wq->nqid+1;
-						strcpy(up->errstr, Edoesnotexist);
+						kstrcpy(up->errstr, Edoesnotexist, ERRMAX);
 					}else{
 						if(nerror)
 							*nerror = nhave+wq->nqid;
-						strcpy(up->errstr, Enotdir);
+						kstrcpy(up->errstr, Enotdir, ERRMAX);
 					}
 					free(wq);
-					if(mh != nil)
-						putmhead(mh);
+					putmhead(mh);
 					return -1;
 				}
 				n = wq->nqid;
 				nc = wq->clone;
 			}else{		/* stopped early, at a mount point */
+				didmount = 1;
 				if(wq->clone != nil){
 					cclose(wq->clone);
 					wq->clone = nil;
@@ -862,8 +966,12 @@
 				}
 				n = i+1;
 			}
-			for(i=0; i<n; i++)
-				cname = addelem(cname, names[nhave+i]);
+			for(i=0; i<n; i++){
+				mtpt = nil;
+				if(i==n-1 && nmh!=nil)
+					mtpt = nmh->from;
+				path = addelem(path, names[nhave+i], mtpt);
+			}
 		}
 		cclose(c);
 		c = nc;
@@ -871,11 +979,8 @@
 		mh = nmh;
 		free(wq);
 	}
-
 	putmhead(mh);
-
 	c = cunique(c);
-
 	if(c->umh != nil){	//BUG
 		print("walk umh\n");
 		putmhead(c->umh);
@@ -882,13 +987,13 @@
 		c->umh = nil;
 	}
 
-	cnameclose(c->name);
-	c->name = cname;
+	pathclose(c->path);
+	c->path = path;
 
 	cclose(*cp);
 	*cp = c;
 	if(nerror)
-		*nerror = 0;
+		*nerror = nhave;
 	return 0;
 }
 
@@ -902,12 +1007,12 @@
 	Mount *f;
 
 	rlock(&m->lock);
-	if(waserror()) {
+	if(waserror()){
 		runlock(&m->lock);
 		nexterror();
 	}
-	for(f = m->mount; f; f = f->next) {
-		if(f->mflag&MCREATE) {
+	for(f = m->mount; f != nil; f = f->next){
+		if((f->mflag&MCREATE) != 0){
 			nc = cclone(f->to);
 			runlock(&m->lock);
 			poperror();
@@ -924,31 +1029,6 @@
 {
 }
 
-/*
- * In place, rewrite name to compress multiple /, eliminate ., and process ..
- */
-void
-cleancname(Cname *n)
-{
-	char *p;
-
-	if(n->s[0] == '#'){
-		p = strchr(n->s, '/');
-		if(p == nil)
-			return;
-		cleanname(p);
-
-		/*
-		 * The correct name is #i rather than #i/,
-		 * but the correct name of #/ is #/.
-		 */
-		if(strcmp(p, "/")==0 && n->s[1] != '/')
-			*p = '\0';
-	}else
-		cleanname(n->s);
-	n->len = strlen(n->s);
-}
-
 static void
 growparse(Elemlist *e)
 {
@@ -956,13 +1036,13 @@
 	int *inew;
 	enum { Delta = 8 };
 
-	if(e->nelems % Delta == 0){
+	if((e->nelems % Delta) == 0){
 		new = smalloc((e->nelems+Delta) * sizeof(char*));
 		memmove(new, e->elems, e->nelems*sizeof(char*));
 		free(e->elems);
 		e->elems = new;
 		inew = smalloc((e->nelems+Delta+1) * sizeof(int));
-		memmove(inew, e->off, e->nelems*sizeof(int));
+		memmove(inew, e->off, (e->nelems+1)*sizeof(int));
 		free(e->off);
 		e->off = inew;
 	}
@@ -978,11 +1058,11 @@
  * rather than a directory.
  */
 static void
-parsename(char *name, Elemlist *e)
+parsename(char *aname, Elemlist *e)
 {
-	char *slash;
+	char *name, *slash;
 
-	kstrdup(&e->name, name);
+	kstrdup(&e->name, aname);
 	name = e->name;
 	e->nelems = 0;
 	e->elems = nil;
@@ -990,7 +1070,8 @@
 	e->off[0] = skipslash(name) - name;
 	for(;;){
 		name = skipslash(name);
-		if(*name=='\0'){
+		if(*name == '\0'){
+			e->off[e->nelems] = name+strlen(name) - e->name;
 			e->mustbedir = 1;
 			break;
 		}
@@ -1009,7 +1090,7 @@
 }
 
 void*
-mymemrchr(void *va, int c, long n)
+memrchr(void *va, int c, long n)
 {
 	uchar *a, *e;
 
@@ -1020,28 +1101,79 @@
 	return nil;
 }
 
+void
+namelenerror(char *aname, int len, char *err)
+{
+	char *ename, *name, *next;
+	int i, errlen;
+
+	/*
+	 * If the name is short enough, just use the whole thing.
+	 */
+	errlen = strlen(err);
+	if(len < ERRMAX/3 || len+errlen < 2*ERRMAX/3)
+		snprint(up->genbuf, sizeof up->genbuf, "%.*s", 
+			utfnlen(aname, len), aname);
+	else{
+		/*
+		 * Print a suffix of the name, but try to get a little info.
+		 */
+		ename = aname+len;
+		next = ename;
+		do{
+			name = next;
+			next = memrchr(aname, '/', name-aname);
+			if(next == nil)
+				next = aname;
+			len = ename-next;
+		}while(len < ERRMAX/3 || len + errlen < 2*ERRMAX/3);
+
+		/*
+		 * If the name is ridiculously long, chop it.
+		 */
+		if(name == ename){
+			name = ename-ERRMAX/4;
+			if(name <= aname)
+				panic("bad math in namelenerror");
+			/* walk out of current UTF sequence */
+			for(i=0; (*name&0xC0)==0x80 && i<UTFmax; i++)
+				name++;
+		}
+		snprint(up->genbuf, sizeof up->genbuf, "...%.*s",
+			utfnlen(name, ename-name), name);
+	}				
+	snprint(up->errstr, ERRMAX, "%#q %s", up->genbuf, err);
+	nexterror();
+}
+
+void
+nameerror(char *name, char *err)
+{
+	namelenerror(name, strlen(name), err);
+}
+
 /*
  * 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 len, n, t, nomount;
 	Chan *c, *cnew;
-	Cname *cname;
+	Path *path;
 	Elemlist e;
 	Rune r;
 	Mhead *m;
@@ -1048,10 +1180,14 @@
 	char *createerr, tmperrbuf[ERRMAX];
 	char *name;
 
-	name = aname;
-	if(name[0] == '\0')
+	if(aname[0] == '\0')
 		error("empty file name");
-	validname(name, 1);
+	aname = validnamedup(aname, 1);
+	if(waserror()){
+		free(aname);
+		nexterror();
+	}
+	name = aname;
 
 	/*
 	 * Find the starting off point (the current slash, the root of
@@ -1069,7 +1205,7 @@
 		nomount = 1;
 		up->genbuf[0] = '\0';
 		n = 0;
-		while(*name!='\0' && (*name != '/' || n < 2)){
+		while(*name != '\0' && (*name != '/' || n < 2)){
 			if(n >= sizeof(up->genbuf)-1)
 				error(Efilename);
 			up->genbuf[n++] = *name++;
@@ -1088,9 +1224,6 @@
 		 *	   any others left unprotected)
 		 */
 		n = chartorune(&r, up->genbuf+1)+1;
-		/* actually / is caught by parsing earlier */
-		if(utfrune("M", r))
-			error(Enoattach);
 		if(up->pgrp->noattach && utfrune("|decp", r)==nil)
 			error(Enoattach);
 		t = devno(r, 1);
@@ -1104,23 +1237,34 @@
 		incref(&c->ref);
 		break;
 	}
-	prefix = name - aname;
 
+	e.aname = aname;
+	e.prefix = name - aname;
 	e.name = nil;
 	e.elems = nil;
 	e.off = nil;
 	e.nelems = 0;
+	e.nerror = 0;
 	if(waserror()){
 		cclose(c);
 		free(e.name);
 		free(e.elems);
+		/*
+		 * Prepare nice error, showing first e.nerror elements of name.
+		 */
+		if(e.nerror == 0)
+			nexterror();
+		strcpy(tmperrbuf, up->errstr);
+		if(e.off[e.nerror]==0)
+			print("nerror=%d but off=%d\n",
+				e.nerror, e.off[e.nerror]);
+		len = e.prefix+e.off[e.nerror];
 		free(e.off);
-//dumpmount();
-		nexterror();
+		namelenerror(aname, len, tmperrbuf);
 	}
 
 	/*
-	 * Build a list of elements in the path.
+	 * Build a list of elements in the name.
 	 */
 	parsename(name, &e);
 
@@ -1130,9 +1274,8 @@
 	if(amode == Acreate){
 		/* perm must have DMDIR if last element is / or /. */
 		if(e.mustbedir && !(perm&DMDIR)){
-			npath = e.nelems;
-			strcpy(tmperrbuf, "create without DMDIR");
-			goto NameError;
+			e.nerror = e.nelems;
+			error("create without DMDIR");
 		}
 
 		/* don't try to walk the last path element just yet. */
@@ -1141,66 +1284,58 @@
 		e.nelems--;
 	}
 
-	if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){
-		if(npath < 0 || npath > e.nelems){
-			print("namec %s walk error npath=%d\n", aname, npath);
-			nexterror();
+	if(walk(&c, e.elems, e.nelems, nomount, &e.nerror) < 0){
+		if(e.nerror < 0 || e.nerror > e.nelems){
+			print("namec %s walk error nerror=%d\n", aname, e.nerror);
+			e.nerror = 0;
 		}
-		strcpy(tmperrbuf, up->errstr);
-	NameError:
-		len = prefix+e.off[npath];
-		if(len < ERRMAX/3 || (name=mymemrchr(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();
 	}
 
-	if(e.mustbedir && !(c->qid.type&QTDIR)){
-		npath = e.nelems;
-		strcpy(tmperrbuf, "not a directory");
-		goto NameError;
-	}
+	if(e.mustbedir && (c->qid.type&QTDIR) == 0)
+		error("not a directory");
 
-	if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
-		npath = e.nelems;
+	if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR) != 0)
 		error("cannot exec directory");
-	}
 
 	switch(amode){
-	case Aaccess:
-		if(!nomount)
-			domount(&c, nil);
-		break;
-
 	case Abind:
+		/* no need to maintain path - cannot dotdot an Abind */
 		m = nil;
 		if(!nomount)
-			domount(&c, &m);
-		if(c->umh != nil)
-			putmhead(c->umh);
+			domount(&c, &m, nil);
+		putmhead(c->umh);
 		c->umh = m;
 		break;
 
+	case Aaccess:
 	case Aremove:
 	case Aopen:
 	Open:
-		/* save the name; domount might change c */
-		cname = c->name;
-		incref(&cname->ref);
+		/* save&update the name; domount might change c */
+		path = c->path;
+		incref(&path->ref);
+		if(waserror()){
+			pathclose(path);
+			nexterror();
+		}
 		m = nil;
 		if(!nomount)
-			domount(&c, &m);
+			domount(&c, &m, &path);
 
 		/* our own copy to open or remove */
 		c = cunique(c);
 
 		/* now it's our copy anyway, we can put the name back */
-		cnameclose(c->name);
-		c->name = cname;
+		pathclose(c->path);
+		c->path = path;
+		poperror();
 
+		/* record whether c is on a mount point */
+		c->ismtpt = m!=nil;
+
 		switch(amode){
+		case Aaccess:
 		case Aremove:
 			putmhead(m);
 			break;
@@ -1207,14 +1342,13 @@
 
 		case Aopen:
 		case Acreate:
-if(c->umh != nil){
-	print("cunique umh Open\n");
-	putmhead(c->umh);
-	c->umh = nil;
-}
-
+			if(c->umh != nil){
+				print("cunique umh Open\n");
+				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)
+			if(m != nil && m->mount != nil && m->mount->next != nil)
 				c->umh = m;
 			else
 				putmhead(m);
@@ -1222,9 +1356,6 @@
 			/* save registers else error() in open has wrong value of c saved */
 			saveregisters();
 
-			if(omode == OEXEC)
-				c->flag &= ~CCACHE;
-
 			c = devtab[c->type]->open(c, omode&~OCEXEC);
 
 			if(omode & OCEXEC)
@@ -1240,7 +1371,7 @@
 		 * Directories (e.g. for cd) are left before the mount point,
 		 * so one may mount on / or . and see the effect.
 		 */
-		if(!(c->qid.type & QTDIR))
+		if((c->qid.type&QTDIR) == 0)
 			error(Enotdir);
 		break;
 
@@ -1259,6 +1390,7 @@
 		 * If omode&OEXCL is set, just give up.
 		 */
 		e.nelems++;
+		e.nerror++;
 		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
 			if(omode&OEXCL)
 				error(Eexist);
@@ -1323,9 +1455,9 @@
 			 * if findmount gave us a new Chan.
 			 */
 			cnew = cunique(cnew);
-			cnameclose(cnew->name);
-			cnew->name = c->name;
-			incref(&cnew->name->ref);
+			pathclose(cnew->path);
+			cnew->path = c->path;
+			incref(&cnew->path->ref);
 
 			cnew = devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
 			poperror();
@@ -1333,38 +1465,34 @@
 				cnew->flag |= CCEXEC;
 			if(omode & ORCLOSE)
 				cnew->flag |= CRCLOSE;
-			if(m)
-				putmhead(m);
+			putmhead(m);
 			cclose(c);
 			c = cnew;
-			c->name = addelem(c->name, e.elems[e.nelems-1]);
+			c->path = addelem(c->path, e.elems[e.nelems-1], nil);
 			break;
-		}else{		/* create failed */
-			cclose(cnew);
-			if(m)
-				putmhead(m);
-			if(omode & OEXCL)
-				nexterror();
-			/* save error */
-			createerr = up->errstr;
-			up->errstr = tmperrbuf;
-			/* note: we depend that walk does not error */
-			if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
-				up->errstr = createerr;
-				error(createerr);	/* report true error */
-			}
+		}
+
+		/* create failed */
+		cclose(cnew);
+		putmhead(m);
+		if(omode & OEXCL)
+			nexterror();
+		/* save error */
+		createerr = up->errstr;
+		up->errstr = tmperrbuf;
+		/* note: we depend that walk does not error */
+		if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
 			up->errstr = createerr;
-			omode |= OTRUNC;
-			goto Open;
+			error(createerr);	/* report true error */
 		}
-		panic("namec: not reached");				
+		up->errstr = createerr;
+		omode |= OTRUNC;
+		goto Open;
 
 	default:
-		panic("unknown namec access %d\n", amode);
+		panic("unknown namec access %d", amode);
 	}
 
-	poperror();
-
 	/* place final element in genbuf for e.g. exec */
 	if(e.nelems > 0)
 		kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf);
@@ -1373,6 +1501,9 @@
 	free(e.name);
 	free(e.elems);
 	free(e.off);
+	poperror();	/* e c */
+	free(aname);
+	poperror();	/* aname */
 
 	return c;
 }
@@ -1389,22 +1520,12 @@
 }
 
 char isfrog[256]={
-	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,	/* 0 */
-	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1, /* 0x08 */
-	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1, /* 0x10 */
-	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1, /* 0x18 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
-		0, 0, 0, 0, 0, 0, 0, 1, /* 0x28 (1 is '/', 0x2F) */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x48 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x68 */
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */
-		0, 0, 0, 0, 0, 0, 0, 1, /* 0x78 (1 is DEL, 0x7F) */
+	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
+	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
+	['/']	1,
+	[0x7f]	1,
 };
 
 /*
@@ -1416,30 +1537,36 @@
  * routine works for kernel and user memory both.
  * The parameter slashok flags whether a slash character is an error
  * or a valid character.
+ *
+ * The parameter dup flags whether the string should be copied
+ * out of user space before being scanned the second time.
+ * (Otherwise a malicious thread could remove the NUL, causing us
+ * to access unchecked addresses.) 
  */
-void
-validname(char *aname, int slashok)
+static char*
+validname0(char *aname, int slashok, int dup, uintptr pc)
 {
-	char *ename, *name;
-	int c;
+	char *ename, *name, *s;
+	int c, n;
 	Rune r;
 
 	name = aname;
-/*
-	if(((ulong)name & KZERO) != KZERO) {
-		p = name;
-		t = BY2PG-((ulong)p&(BY2PG-1));
-		while((ename=vmemchr(p, 0, t)) == nil) {
-			p += t;
-			t = BY2PG;
-		}
-	}else
-*/
-		ename = memchr(name, 0, (1<<16));
+	ename = memchr(name, 0, (1<<16));
 
 	if(ename==nil || ename-name>=(1<<16))
-		error("name too long");
+		error(Etoolong);
 
+	s = nil;
+	if(dup){
+		n = ename-name;
+		s = smalloc(n+1);
+		memmove(s, name, n);
+		s[n] = 0;
+		aname = s;
+		name = s;
+		setmalloctag(s, pc);
+	}
+	
 	while(*name){
 		/* all characters above '~' are ok */
 		c = *(uchar*)name;
@@ -1447,47 +1574,33 @@
 			name += chartorune(&r, name);
 		else{
 			if(isfrog[c])
-				if(!slashok || c!='/'){
-					snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
-					error(up->genbuf);
+			if(!slashok || c!='/'){
+				snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
+				free(s);
+				error(up->genbuf);
 			}
 			name++;
 		}
 	}
+	return s;
 }
 
 void
-isdir(Chan *c)
+validname(char *aname, int slashok)
 {
-	if(c->qid.type & QTDIR)
-		return;
-	error(Enotdir);
+	validname0(aname, slashok, 0, getcallerpc(&aname));
 }
 
-/*
- * This is necessary because there are many
- * pointers to the top of a given mount list:
- *
- *	- the mhead in the namespace hash table
- *	- the mhead in chans returned from findmount:
- *	  used in namec and then by unionread.
- *	- the mhead in chans returned from createdir:
- *	  used in the open/create race protect, which is gone.
- *
- * The RWlock in the Mhead protects the mount list it contains.
- * The mount list is deleted when we cunmount.
- * The RWlock ensures that nothing is using the mount list at that time.
- *
- * It is okay to replace c->mh with whatever you want as 
- * long as you are sure you have a unique reference to it.
- *
- * This comment might belong somewhere else.
- */
+char*
+validnamedup(char *aname, int slashok)
+{
+	return validname0(aname, slashok, 1, getcallerpc(&aname));
+}
+
 void
-putmhead(Mhead *m)
+isdir(Chan *c)
 {
-	if(m && decref(&m->ref) == 0){
-		m->mount = (Mount*)0xCafeBeef;
-		free(m);
-	}
+	if(c->qid.type & QTDIR)
+		return;
+	error(Enotdir);
 }
--- a/kern/dat.h
+++ b/kern/dat.h
@@ -6,7 +6,6 @@
 typedef struct Chan	Chan;
 typedef struct Cmdbuf	Cmdbuf;
 typedef struct Cmdtab	Cmdtab;
-typedef struct Cname	Cname;
 typedef struct Conf	Conf;
 typedef struct Dev	Dev;
 typedef struct Dirtab	Dirtab;
@@ -22,6 +21,7 @@
 typedef struct Mntwalk	Mntwalk;
 typedef struct Mnt	Mnt;
 typedef struct Mhead	Mhead;
+typedef struct Path	Path;
 typedef struct Pgrps	Pgrps;
 typedef struct Pgrp	Pgrp;
 typedef struct Proc	Proc;
@@ -135,10 +135,12 @@
 
 struct Chan
 {
-	Ref ref;
+	Ref	ref;
+	Lock	lk;
 	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 */
@@ -145,28 +147,37 @@
 	ushort	flag;
 	Qid	qid;
 	int	fid;			/* for devmnt */
-	ulong	iounit;	/* chunk size for i/o; 0==default */
+	ulong	iounit;			/* chunk size for i/o; 0==default */
 	Mhead*	umh;			/* mount point that derived Chan; used in unionread */
 	Chan*	umc;			/* channel in union; held for union read */
 	QLock	umqlock;		/* serialize unionreads */
 	int	uri;			/* union read index */
 	int	dri;			/* devdirread index */
-	ulong	mountid;
-	Mnt		*mux;		/* Mnt for clients using me for messages */
-	void*	aux;
-	Qid	pgrpid;		/* for #p/notepg */
-	ulong	mid;		/* for ns in devproc */
+	uchar*	dirrock;		/* directory entry rock for translations */
+	int	nrock;
+	int	mrock;
+	QLock	rockqlock;
+	int	ismtpt;
+	Mnt*	mux;			/* Mnt for clients using me for messages */
+	union {
+		void*	aux;
+		Qid	pgrpid;		/* for #p/notepg */
+		ulong	mid;		/* for ns in devproc */
+	};
 	Chan*	mchan;			/* channel to mounted server */
 	Qid	mqid;			/* qid of root of mount point */
-	Cname	*name;
+	Path*	path;
 };
 
-struct Cname
+struct Path
 {
-	Ref ref;
-	int	alen;			/* allocated length */
-	int	len;			/* strlen(s) */
+	Ref	ref;
 	char	*s;
+	Chan	**mtpt;			/* mtpt history */
+	int	len;			/* strlen(s) */
+	int	alen;			/* allocated length of s */
+	int	mlen;			/* number of path elements */
+	int	malen;			/* allocated length of mtpt */
 };
 
 struct Dev
@@ -198,7 +209,7 @@
 	char	name[KNAMELEN];
 	Qid	qid;
 	vlong length;
-	ulong	perm;
+	long	perm;
 };
 
 struct Walkqid
@@ -315,7 +326,7 @@
 
 struct Fgrp
 {
-	Ref ref;
+	Ref	ref;
 	Chan	**fd;
 	int	nfd;			/* number allocated */
 	int	maxfd;			/* highest fd in use */
--- a/kern/dev.c
+++ b/kern/dev.c
@@ -24,7 +24,7 @@
 			return i;
 	}
 	if(user == 0)
-		panic("devno %C 0x%ux", c, c);
+		panic("devno %C %#ux", c, c);
 
 	return -1;
 }
@@ -124,6 +124,7 @@
 Chan*
 devattach(int tc, char *spec)
 {
+	int n;
 	Chan *c;
 	char *buf;
 
@@ -132,9 +133,10 @@
 	c->type = devno(tc, 0);
 	if(spec == nil)
 		spec = "";
-	buf = smalloc(4+strlen(spec)+1);
-	sprint(buf, "#%C%s", tc, spec);
-	c->name = newcname(buf);
+	n = 1+UTFmax+strlen(spec)+1;
+	buf = smalloc(n);
+	snprint(buf, n, "#%C%s", tc, spec);
+	c->path = newpath(buf);
 	free(buf);
 	return c;
 }
@@ -146,7 +148,7 @@
 	Chan *nc;
 
 	if(c->flag & COPEN)
-		panic("clone of open file type %C\n", devtab[c->type]->dc);
+		panic("clone of open file type %C", devtab[c->type]->dc);
 
 	nc = newchan();
 
@@ -156,10 +158,7 @@
 	nc->qid = c->qid;
 	nc->offset = c->offset;
 	nc->umh = nil;
-	nc->mountid = c->mountid;
 	nc->aux = c->aux;
-	nc->pgrpid = c->pgrpid;
-	nc->mid = c->mid;
 	nc->mqid = c->mqid;
 	return nc;
 }
@@ -175,18 +174,17 @@
 	if(nname > 0)
 		isdir(c);
 
-	alloc = 0;
+	alloc = (nc == nil);
 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
 	if(waserror()){
-		if(alloc && wq->clone!=nil)
+		if(alloc && wq->clone != nil)
 			cclose(wq->clone);
 		free(wq);
 		return nil;
 	}
-	if(nc == nil){
+	if(alloc){
 		nc = devclone(c);
 		nc->type = 0;	/* device doesn't know about this channel yet */
-		alloc = 1;
 	}
 	wq->clone = nc;
 
@@ -251,7 +249,7 @@
 		if(alloc)
 			cclose(wq->clone);
 		wq->clone = nil;
-	}else if(wq->clone){
+	}else if(wq->clone != nil){
 		/* attach cloned channel to same device */
 		wq->clone->type = c->type;
 	}
@@ -265,16 +263,16 @@
 	Dir dir;
 	char *p, *elem;
 
-	for(i=0;; i++)
+	for(i=0;; i++){
 		switch((*gen)(c, nil, tab, ntab, i, &dir)){
 		case -1:
 			if(c->qid.type & QTDIR){
-				if(c->name == nil)
+				if(c->path == nil)
 					elem = "???";
-				else if(strcmp(c->name->s, "/") == 0)
+				else if(strcmp(c->path->s, "/") == 0)
 					elem = "/";
 				else
-					for(elem=p=c->name->s; *p; p++)
+					for(elem=p=c->path->s; *p; p++)
 						if(*p == '/')
 							elem = p+1;
 				devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
@@ -283,7 +281,6 @@
 					error(Ebadarg);
 				return n;
 			}
-			print("devstat %C %llux\n", devtab[c->type]->dc, c->qid.path);
 
 			error(Enonexist);
 		case 0:
@@ -299,8 +296,7 @@
 			}
 			break;
 		}
-	error(Egreg);	/* not reached? */
-	return -1;
+	}
 }
 
 long
@@ -307,13 +303,10 @@
 devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
 {
 	long m, dsz;
-	struct{
-		Dir d;
-		char slop[100];
-	}dir;
+	Dir dir;
 
 	for(m=0; m<n; c->dri++) {
-		switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){
+		switch((*gen)(c, nil, tab, ntab, c->dri, &dir)){
 		case -1:
 			return m;
 
@@ -321,7 +314,7 @@
 			break;
 
 		case 1:
-			dsz = convD2M(&dir.d, (uchar*)d, n-m);
+			dsz = convD2M(&dir, (uchar*)d, n-m);
 			if(dsz <= BIT16SZ){	/* <= not < because this isn't stat; read is stuck */
 				if(m == 0)
 					error(Eshort);
@@ -396,7 +389,7 @@
 	USED(perm);
 
 	error(Eperm);
-	return nil;
+	return 0;
 }
 
 Block*
--- a/kern/devlfd.c
+++ b/kern/devlfd.c
@@ -16,7 +16,7 @@
 	c = newchan();
 	c->type = devno('L', 0);
 	c->aux = (void*)(uintptr)fd;
-	c->name = newcname("fd");
+	c->path = newpath("fd");
 	c->mode = ORDWR;
 	c->qid.type = 0;
 	c->qid.path = 0;
--- a/kern/error.c
+++ b/kern/error.c
@@ -48,3 +48,4 @@
 char Ebadstat[] = "malformed stat buffer";
 char Enegoff[] = "negative i/o offset";
 char Ecmdargs[] = "wrong #args in control message";
+char Etoolong[] = "name too long";
--- a/kern/error.h
+++ b/kern/error.h
@@ -48,3 +48,4 @@
 extern char Ebadstat[];		/* malformed stat buffer */
 extern char Enegoff[];		/* negative i/o offset */
 extern char Ecmdargs[];		/* wrong #args in control message */
+extern char Etoolong[];		/* name too long */
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -26,7 +26,6 @@
 void		closergrp(Rgrp*);
 void		cmderror(Cmdbuf*, char*);
 int		cmount(Chan**, Chan*, int, char*);
-void		cnameclose(Cname*);
 Block*		concatblock(Block*);
 Block*		copyblock(Block*, int);
 void		cunmount(Chan*, Chan*);
@@ -107,13 +106,13 @@
 Chan*		newchan(void);
 int		newfd(Chan*);
 Mhead*		newmhead(Chan*);
-Mount*		newmount(Mhead*, Chan*, int, char*);
+Mount*		newmount(Chan*, int, char*);
+Path*		newpath(char*);
 Pgrp*		newpgrp(void);
 Rgrp*		newrgrp(void);
 Proc*		newproc(void);
 char*		nextelem(char*, char*);
 void		nexterror(void);
-Cname*		newcname(char*);
 int		openmode(ulong);
 void		oserrstr(void);
 void		oserror(void);
@@ -121,6 +120,7 @@
 Block*		padblock(Block*, int);
 void		panic(char*, ...);
 Cmdbuf*		parsecmd(char *a, int n);
+void		pathclose(Path*);
 void		pexit(char*, int);
 void		printinit(void);
 int		procindex(ulong);
@@ -201,6 +201,7 @@
 long		userwrite(char*, int);
 #define	validaddr(a, b, c)
 void		validname(char*, int);
+char*		validnamedup(char*, int);
 void		validstat(uchar*, int);
 void*		vmemchr(void*, int, int);
 Proc*		wakeup(Rendez*);
--- a/kern/pgrp.c
+++ b/kern/pgrp.c
@@ -7,39 +7,6 @@
 static Ref pgrpid;
 static Ref mountid;
 
-#ifdef NOTDEF
-void
-pgrpnote(ulong noteid, char *a, long n, int flag)
-{
-	Proc *p, *ep;
-	char buf[ERRMAX];
-
-	if(n >= ERRMAX-1)
-		error(Etoobig);
-
-	memmove(buf, a, n);
-	buf[n] = 0;
-	p = proctab(0);
-	ep = p+conf.nproc;
-	for(; p < ep; p++) {
-		if(p->state == Dead)
-			continue;
-		if(up != p && p->noteid == noteid && p->kp == 0) {
-			qlock(&p->debug);
-			if(p->pid == 0 || p->noteid != noteid){
-				qunlock(&p->debug);
-				continue;
-			}
-			if(!waserror()) {
-				postnote(p, 0, buf, flag);
-				poperror();
-			}
-			qunlock(&p->debug);
-		}
-	}
-}
-#endif
-
 Pgrp*
 newpgrp(void)
 {
@@ -71,29 +38,24 @@
 void
 closepgrp(Pgrp *p)
 {
-	Mhead **h, **e, *f, *next;
+	Mhead **h, **e, *f;
+	Mount *m;
 
-	if(decref(&p->ref) != 0)
+	if(decref(&p->ref))
 		return;
 
-	qlock(&p->debug);
-	wlock(&p->ns);
-	p->pgrpid = -1;
-
 	e = &p->mnthash[MNTHASH];
 	for(h = p->mnthash; h < e; h++) {
-		for(f = *h; f; f = next) {
+		while((f = *h) != nil){
+			*h = f->hash;
 			wlock(&f->lock);
-			cclose(f->from);
-			mountfree(f->mount);
+			m = f->mount;
 			f->mount = nil;
-			next = f->hash;
 			wunlock(&f->lock);
+			mountfree(m);
 			putmhead(f);
 		}
 	}
-	wunlock(&p->ns);
-	qunlock(&p->debug);
 	free(p);
 }
 
@@ -102,12 +64,12 @@
 {
 	Mount *f;
 
-	m->order = 0;
-	if(*order == 0) {
+	m->order = nil;
+	if(*order == nil) {
 		*order = m;
 		return;
 	}
-	for(f = *order; f; f = f->order) {
+	for(f = *order; f != nil; f = f->order) {
 		if(m->mountid < f->mountid) {
 			m->order = f;
 			*order = m;
@@ -124,25 +86,31 @@
 void
 pgrpcpy(Pgrp *to, Pgrp *from)
 {
-	int i;
 	Mount *n, *m, **link, *order;
 	Mhead *f, **tom, **l, *mh;
+	int i;
 
-	wlock(&from->ns);
-	order = 0;
+	wlock(&to->ns);
+	rlock(&from->ns);
+	order = nil;
 	tom = to->mnthash;
 	for(i = 0; i < MNTHASH; i++) {
 		l = tom++;
-		for(f = from->mnthash[i]; f; f = f->hash) {
+		for(f = from->mnthash[i]; f != nil; f = f->hash) {
 			rlock(&f->lock);
 			mh = newmhead(f->from);
 			*l = mh;
 			l = &mh->hash;
 			link = &mh->mount;
-			for(m = f->mount; m; m = m->next) {
-				n = newmount(mh, m->to, m->mflag, m->spec);
-				m->copy = n;
-				pgrpinsert(&order, m);
+			for(m = f->mount; m != nil; m = m->next) {
+				n = smalloc(sizeof(Mount));
+				n->mountid = m->mountid;
+				n->mflag = m->mflag;
+				n->to = m->to;
+				incref(&n->to->ref);
+				if(m->spec != nil)
+					kstrdup(&n->spec, m->spec);
+				pgrpinsert(&order, n);
 				*link = n;
 				link = &n->next;
 			}
@@ -152,11 +120,10 @@
 	/*
 	 * Allocate mount ids in the same sequence as the parent group
 	 */
-	lock(&mountid.lk);
-	for(m = order; m; m = m->order)
-		m->copy->mountid = mountid.ref++;
-	unlock(&mountid.lk);
-	wunlock(&from->ns);
+	for(m = order; m != nil; m = m->order)
+		m->mountid = incref(&mountid);
+	runlock(&from->ns);
+	wunlock(&to->ns);
 }
 
 Fgrp*
@@ -181,8 +148,9 @@
 	if(i != 0)
 		new->nfd += DELTAFD - i;
 	new->fd = malloc(new->nfd*sizeof(Chan*));
-	if(new->fd == 0){
+	if(new->fd == nil){
 		unlock(&f->ref.lk);
+		free(new);
 		error("no memory for fgrp");
 	}
 	new->ref.ref = 1;
@@ -189,7 +157,7 @@
 
 	new->maxfd = f->maxfd;
 	for(i = 0; i <= f->maxfd; i++) {
-		if((c = f->fd[i])){
+		if((c = f->fd[i]) != nil){
 			incref(&c->ref);
 			new->fd[i] = c;
 		}
@@ -205,15 +173,14 @@
 	int i;
 	Chan *c;
 
-	if(f == 0)
+	if(f == nil || decref(&f->ref))
 		return;
 
-	if(decref(&f->ref) != 0)
-		return;
-
 	for(i = 0; i <= f->maxfd; i++)
-		if((c = f->fd[i]))
+		if((c = f->fd[i]) != nil){
+			f->fd[i] = nil;
 			cclose(c);
+		}
 
 	free(f->fd);
 	free(f);
@@ -220,17 +187,16 @@
 }
 
 Mount*
-newmount(Mhead *mh, Chan *to, int flag, char *spec)
+newmount(Chan *to, int flag, char *spec)
 {
 	Mount *m;
 
 	m = smalloc(sizeof(Mount));
 	m->to = to;
-	m->head = mh;
 	incref(&to->ref);
 	m->mountid = incref(&mountid);
 	m->mflag = flag;
-	if(spec != 0)
+	if(spec != nil)
 		kstrdup(&m->spec, spec);
 
 	return m;
@@ -241,12 +207,10 @@
 {
 	Mount *f;
 
-	while(m) {
-		f = m->next;
-		cclose(m->to);
-		m->mountid = 0;
-		free(m->spec);
-		free(m);
-		m = f;
+	while((f = m) != nil) {
+		m = m->next;
+		cclose(f->to);
+		free(f->spec);
+		free(f);
 	}
 }
--- a/kern/procinit.c
+++ b/kern/procinit.c
@@ -18,8 +18,8 @@
 	_setproc(p);
 
 	up->slash = namec("#/", Atodir, 0, 0);
-	cnameclose(up->slash->name);
-	up->slash->name = newcname("/");
+	pathclose(up->slash->path);
+	up->slash->path = newpath("/");
 	up->dot = cclone(up->slash);
 }
 
--- a/kern/sysfile.c
+++ b/kern/sysfile.c
@@ -190,10 +190,10 @@
 
 	c = fdtochan(fd, -1, 0, 1);
 
-	if(c->name == nil)
+	if(c->path == nil)
 		snprint(buf, nbuf, "<null>");
 	else
-		snprint(buf, nbuf, "%s", c->name->s);
+		snprint(buf, nbuf, "%s", c->path->s);
 	cclose(c);
 	return 0;
 }