This is probably a bit risky to apply at this point; I have used it for only one day. This patch gets rid of ".." machinery in the kernel by rewriting paths early in namec. It also fixes libc cleanname. The most notable difference is that paths that start with ".." will know walk from the root of the process and not from ".". At the very least this patch is useful to document what could change if ".." is handled this way. At most it simplifies the Path structure, and the code in chan.c. If the new cleanname code is used, but not the kernel changes, then a call to cleanname in the kernel must check after cleanname returns if the cleaned name is "", and in that case, copy "." into it. The old libc cleanname did that, which is probably wrong, and the supplied cleanname does not. hth PS: This is a second version of the patch. The previous version had a cleanname that did not preserver optional runes on kernel paths and did not return "." for empty names. Notes: Sun Feb 23 02:22:21 EST 2014 geoff see the version that made it into nix ultimately. Reference: /n/sources/patch/saved/no-dot-dot Date: Tue Apr 9 22:19:22 CES 2013 Signed-off-by: nemo@lsub.org Reviewed-by: geoff --- /sys/src/9/port/chan.c Mon Apr 8 17:46:48 2013 +++ /sys/src/9/port/chan.c Mon Apr 8 17:46:43 2013 @@ -11,7 +11,6 @@ enum { PATHSLOP = 20, - PATHMSLOP = 20, }; struct @@ -301,16 +300,12 @@ if(strchr(s, '/') && 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)); @@ -323,39 +318,20 @@ 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; imlen; i++){ - pp->mtpt[i] = p->mtpt[i]; - if(pp->mtpt[i]) - incref(pp->mtpt[i]); - } - return pp; } void pathclose(Path *p) { - int i; - if(p == nil) return; -//XXX - DBG("pathclose %p %s ref=%ld =>", p, p->s, p->ref); - for(i=0; imlen; i++) - DBG(" %p", p->mtpt[i]); - DBG("\n"); + DBG("pathclose %p %s ref=%ld\n", p, p->s, p->ref); if(decref(p)) return; decref(&npath); free(p->s); - for(i=0; imlen; i++) - if(p->mtpt[i]) - cclose(p->mtpt[i]); - free(p->mtpt); free(p); } @@ -374,6 +350,8 @@ if(r == nil) return; cleanname(r); + if(r[0] == 0) /* compat with old cleannames */ + strcpy(r, "."); /* * The correct name is #i rather than #i/, @@ -381,8 +359,11 @@ */ if(strcmp(r, "/")==0 && p->s[1] != '/') *r = '\0'; - }else + }else{ cleanname(p->s); + if(p->s[0] == 0) /* compat with old cleannames */ + strcpy(p->s, "."); + } p->len = strlen(p->s); } @@ -401,11 +382,10 @@ } static Path* -addelem(Path *p, char *s, Chan *from) +addelem(Path *p, char *s) { char *t; int a, i; - Chan *c, **tt; if(s[0]=='.' && s[1]=='\0') return p; @@ -426,26 +406,11 @@ p->s[p->len++] = '/'; memmove(p->s+p->len, s, i+1); p->len += i; - if(isdotdot(s)){ - fixdotdotname(p); - DBG("addelem %s .. => rm %p\n", p->s, p->mtpt[p->mlen-1]); - if(p->mlen>1 && (c = p->mtpt[--p->mlen])){ - 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; - } - DBG("addelem %s %s => add %p\n", p->s, s, from); - p->mtpt[p->mlen++] = from; - if(from) - incref(from); - } + +if(isdotdot(s)) +panic("DOTDOT: addelem\n"); + + DBG("addelem %s %s \n", p->s, s); return p; } @@ -890,58 +855,22 @@ /* * Calls findmount but also updates path. + * NB: Kept because it also makes the path unique, but + * could probably go now that we don't track mount points + * within Paths. */ static int domount(Chan **cp, Mhead **mp, Path **path) { - Chan **lc; - Path *p; - if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0) return 0; - if(path){ - p = *path; - p = uniquepath(p); - if(p->mlen <= 0) - print("domount: path %s has mlen==%d\n", p->s, p->mlen); - else{ - lc = &p->mtpt[p->mlen-1]; -DBG("domount %p %s => add %p (was %p)\n", p, p->s, (*mp)->from, p->mtpt[p->mlen-1]); - incref((*mp)->from); - if(*lc) - cclose(*lc); - *lc = (*mp)->from; - } - *path = p; - } + if(path) + *path = uniquepath(*path); return 1; } /* - * 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; - - if(path->ref != 1 || path->mlen == 0) - print("undomount: path %s ref %ld mlen %d caller %#p\n", - path->s, path->ref, path->mlen, getcallerpc(&c)); - - if(path->mlen>0 && (nc=path->mtpt[path->mlen-1]) != nil){ -DBG("undomount %p %s => remove %p\n", path, path->s, nc); - cclose(c); - path->mtpt[path->mlen-1] = nil; - c = nc; - } - return c; -} - -/* * Call dev walk but catch errors. */ static Walkqid* @@ -964,8 +893,8 @@ int walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) { - int dev, didmount, dotdot, i, n, nhave, ntry, type; - Chan *c, *nc, *mtpt; + int dev, didmount, i, n, nhave, ntry, type; + Chan *c, *nc; Path *path; Mhead *mh, *nmh; Mount *f; @@ -980,13 +909,12 @@ /* * While we haven't gotten all the way down the path: * 1. step through a mount point, if any - * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 2. send a walk request for initial prefix without dotdot * 3. move to the first mountpoint along the way. * 4. repeat. * * 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. */ @@ -1005,19 +933,12 @@ ntry = nnames - nhave; if(ntry > MAXWELEM) ntry = MAXWELEM; - dotdot = 0; - for(i=0; itype; @@ -1052,60 +973,46 @@ } didmount = 0; - if(dotdot){ - assert(wq->nqid == 1); - assert(wq->clone != nil); - - path = addelem(path, "..", nil); - nc = undomount(wq->clone, path); - nmh = nil; - n = 1; - }else{ - nc = nil; - nmh = nil; - if(!nomount){ - for(i=0; inqid && iqid[i])){ - didmount = 1; - break; - } + nc = nil; + nmh = nil; + if(!nomount){ + for(i=0; inqid && iqid[i])){ + didmount = 1; + break; } } - if(nc == nil){ /* no mount points along path */ - if(wq->clone == nil){ - cclose(c); - pathclose(path); - if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ - if(nerror) - *nerror = nhave+wq->nqid+1; - strcpy(up->errstr, Edoesnotexist); - }else{ - if(nerror) - *nerror = nhave+wq->nqid; - strcpy(up->errstr, Enotdir); - } - free(wq); - if(mh != nil) - 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; + } + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + pathclose(path); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->errstr, Enotdir); } - n = i+1; + free(wq); + if(mh != nil) + putmhead(mh); + return -1; } - for(i=0; ifrom; - path = addelem(path, names[nhave+i], mtpt); + 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; } + n = i+1; } + for(i=0; idot. + * No other portion of code should ever see a "..". + */ + dotdotname = nil; + if(up->dot == nil) /* init0 called */ + goto pathok; + cleanname(name); + if(name[0] == '.' && name[1] == '.' && (name[2] == '/' || name[2] == 0)){ + path = up->dot->path; + dotdotname = smalloc(path->len + 1 + strlen(name) + 1); + memmove(dotdotname, path->s, path->len); + s = dotdotname+path->len; + if(s[-1] != '/') + *s++ = '/'; + strcpy(s, name); + cleanname(dotdotname); + DBG("namec dotdot %s\n", dotdotname); + name = dotdotname; + } +pathok: + if(name[0] == 0) + strcpy(name, "."); + if(waserror()){ + free(dotdotname); + nexterror(); + } + /* * Find the starting off point (the current slash, the root of * a device tree, or the current dot) as well as the name to @@ -1623,7 +1561,7 @@ putmhead(m); cclose(c); c = cnew; - c->path = addelem(c->path, e.elems[e.nelems-1], nil); + c->path = addelem(c->path, e.elems[e.nelems-1]); break; } @@ -1658,6 +1596,8 @@ free(e.elems); free(e.off); poperror(); /* e c */ + free(dotdotname); + poperror(); /* dotdotname */ free(aname); poperror(); /* aname */ --- /sys/src/9/port/portdat.h Mon Apr 8 17:46:55 2013 +++ /sys/src/9/port/portdat.h Mon Apr 8 17:46:51 2013 @@ -222,11 +222,8 @@ { 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 --- /sys/src/libc/port/cleanname.c Mon Apr 8 17:47:00 2013 +++ /sys/src/libc/port/cleanname.c Sun Apr 14 13:42:09 2013 @@ -8,56 +8,90 @@ char* cleanname(char *name) { - char *p, *q, *dotdot; - int rooted, erasedprefix; + char *s; /* source of copy */ + char *d; /* destination of copy */ + char *d0; /* start of path afer the root name */ + Rune r; + int rooted; - rooted = name[0] == '/'; - erasedprefix = 0; + if(name[0] == 0) + return strcpy(name, "."); + rooted = 0; + d0 = name; + if(d0[0] == '#'){ + if(d0[1] == 0) + return d0; + d0 += 1 + chartorune(&r, d0+1); /* ignore slash: #/ */ + while(!SEP(*d0)) + d0 += chartorune(&r, d0); + if(d0 == 0) + return name; + d0++; /* keep / after # */ + rooted = 1; + }else if(d0[0] == '/'){ + rooted = 1; + d0++; + } + + s = d0; + if(rooted){ + /* skip extra '/' at root name */ + for(; *s == '/'; s++) + ; + } + /* remove dup slashes */ + for(d = d0; *s != 0; s++){ + *d++ = *s; + if(*s == '/') + while(s[1] == '/') + s++; + } + *d = 0; - /* - * invariants: - * p points at beginning of path element we're considering. - * q points just past the last path element we wrote (no slash). - * dotdot points just past the point where .. cannot backtrack - * any further (no slash). - */ - p = q = dotdot = name+rooted; - while(*p) { - if(p[0] == '/') /* null element */ - p++; - else if(p[0] == '.' && SEP(p[1])) { - if(p == name) - erasedprefix = 1; - p += 1; /* don't count the separator in case it is nul */ - } else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) { - p += 2; - if(q > dotdot) { /* can backtrack */ - while(--q > dotdot && *q != '/') - ; - } else if(!rooted) { /* /.. is / but ./../ is .. */ - if(q != name) - *q++ = '/'; - *q++ = '.'; - *q++ = '.'; - dotdot = q; + d = d0; + s = d0; + while(*s != 0){ + if(s[0] == '.' && SEP(s[1])){ + if(s[1] == 0) + break; + s+= 2; + continue; + } + if(s[0] == '.' && s[1] == '.' && SEP(s[2])){ + if(d == d0){ + if(rooted){ + /* /../x -> /x */ + if(s[2] == 0) + break; + s += 3; + continue; + }else{ + /* ../x -> ../x; and never collect ../ */ + d0 += 3; + } + } + if(d > d0){ + /* a/../x -> x */ + assert(d-2 >= d0 && d[-1] == '/'); + for(d -= 2; d > d0 && d[-1] != '/'; d--) + ; + if(s[2] == 0) + break; + s += 3; + continue; } - if(q == name) - erasedprefix = 1; /* erased entire path via dotdot */ - } else { /* real path element */ - if(q != name+rooted) - *q++ = '/'; - while((*q = *p) != '/' && *q != 0) - p++, q++; } + while(!SEP(*s)) + *d++ = *s++; + if(*s == 0) + break; + + *d++ = *s++; } - if(q == name) /* empty string is really ``.'' */ - *q++ = '.'; - *q = '\0'; - if(erasedprefix && name[0] == '#'){ - /* this was not a #x device path originally - make it not one now */ - memmove(name+2, name, strlen(name)+1); - name[0] = '.'; - name[1] = '/'; - } + *d = 0; + if(d-1 > name && d[-1] == '/') /* thanks to #/ */ + *--d = 0; + if(name[0] == 0) + strcpy(name, "."); return name; }