From owner-freebsd-bugs@FreeBSD.ORG Sat Apr 3 06:20:31 2004 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id EDE9D16A4CE for ; Sat, 3 Apr 2004 06:20:31 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id E24F243D5C for ; Sat, 3 Apr 2004 06:20:31 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) i33EKQbv042442 for ; Sat, 3 Apr 2004 06:20:26 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.10/8.12.10/Submit) id i33EKP09042441; Sat, 3 Apr 2004 06:20:25 -0800 (PST) (envelope-from gnats) Date: Sat, 3 Apr 2004 06:20:25 -0800 (PST) Message-Id: <200404031420.i33EKP09042441@freefall.freebsd.org> To: freebsd-bugs@FreeBSD.org From: "a.s. mitrohin" Subject: Re: bin/64990: /bin/sh unable to change directory but current dir grow anyway X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: "a.s. mitrohin" List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 03 Apr 2004 14:20:32 -0000 The following reply was made to PR bin/64990; it has been noted by GNATS. From: "a.s. mitrohin" To: freebsd-gnats-submit@FreeBSD.org, swp@uni-altai.ru Cc: Subject: Re: bin/64990: /bin/sh unable to change directory but current dir grow anyway Date: Sat, 03 Apr 2004 21:18:42 +0700 This is a multi-part message in MIME format. --------------050700010807040401070403 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit sorry... patch broken ;( please review new version this patch fixed another bug yet. sh change directory incorrect (for logical paths) if current dir was maked from symlink. for example: # ln -fs /usr/ports/distfiles /home/ftp/distfiles # cd /usr/ports/distfiles # pwd /usr/ports/distfiles # cd ../x11 cd: can't cd to ../x11 /swp --------------050700010807040401070403 Content-Type: text/plain; name="patch-bin::sh::cd.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="patch-bin::sh::cd.c" Index: bin/sh/cd.c =================================================================== RCS file: /usr/cvs/freebsd/ncvs/src/bin/sh/cd.c,v retrieving revision 1.33 diff -u -r1.33 cd.c --- bin/sh/cd.c 5 Jul 2003 15:18:44 -0000 1.33 +++ bin/sh/cd.c 3 Apr 2004 11:30:51 -0000 @@ -42,13 +42,19 @@ #include __FBSDID("$FreeBSD: src/bin/sh/cd.c,v 1.33 2003/07/05 15:18:44 dds Exp $"); +#include +#include +#include +#include #include #include +#include #include #include #include #include #include +#include /* * The cd and pwd commands. @@ -68,262 +74,381 @@ #include "show.h" #include "cd.h" -STATIC int cdlogical(char *); -STATIC int cdphysical(char *); -STATIC int docd(char *, int, int); -STATIC char *getcomponent(void); -STATIC int updatepwd(char *); - -STATIC char *curdir = NULL; /* current working directory */ -STATIC char *prevdir; /* previous working directory */ -STATIC char *cdcomppath; +#define malloc ckmalloc +#define realloc ckrealloc -int -cdcmd(int argc, char **argv) +/* + * Get the next component of the path name pointed to by path. + * This routine overwrites the string pointed to by path. + */ +static inline +char * +getcomponent(char **path) { - char *dest; - char *path; char *p; - struct stat statb; - int ch, phys, print = 0; - optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ - phys = Pflag; - while ((ch = getopt(argc, argv, "LP")) != -1) { - switch (ch) { - case 'L': - phys = 0; - break; - case 'P': - phys = 1; - break; - default: - error("unknown option: -%c", optopt); - break; - } - } - argc -= optind; - argv += optind; + while ((p = strsep(path, "/")) != 0 && !*p) + continue; + return p; +} - if (argc > 1) - error("too many arguments"); - if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) - error("HOME not set"); - if (*dest == '\0') - dest = "."; - if (dest[0] == '-' && dest[1] == '\0') { - dest = prevdir ? prevdir : curdir; - if (dest) - print = 1; - else - dest = "."; +struct mblkcore { + int clnk; + int realsz; + int n; + char d[1]; +}; +typedef struct mblkcore *mblk_t; + +#define BLKSZ 0x1000 +#define MBLK_GETMEMSIZE(n) \ + ((n)?(((n)-1)/BLKSZ+1)*BLKSZ + offsetof(struct mblkcore, d) : 0) + +static inline int mblk_init(mblk_t *); +static inline int mblk_uninit(mblk_t *); +static inline int mblk_clear(mblk_t *); +static inline int mblk_attach(mblk_t *, mblk_t *); +static inline int mblk_getn(mblk_t *); +static inline char *mblk_getp(mblk_t *); +static inline int mblk_realloc(mblk_t *, int); +static inline int mblk_write(mblk_t *, int, void *, int); + +#define mblk_resize mblk_realloc + +static inline +int +mblk_init(mblk_t *b) +{ + *b = 0; + return 0; +} + +static inline +int +mblk_uninit(mblk_t *b) +{ + return mblk_clear(b); +} + +static inline +int +mblk_clear(mblk_t *b) +{ + if (*b) { + if (!--(*b)->clnk) + free(*b); + *b = 0; } - if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) - path = nullstr; - while ((p = padvance(&path, dest)) != NULL) { - if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { - if (!print) { - /* - * XXX - rethink - */ - if (p[0] == '.' && p[1] == '/' && p[2] != '\0') - p += 2; - print = strcmp(p, dest); - } - if (docd(p, print, phys) >= 0) - return 0; - } + return 0; +} + +static inline +int +mblk_attach(mblk_t *a, mblk_t *b) +{ + mblk_uninit(a); + if (*b) { + (*b)->clnk++; + *a = *b; } - error("can't cd to %s", dest); - /*NOTREACHED*/ return 0; } +static inline +int +mblk_getn(mblk_t *b) +{ + return (*b) ? (*b)->n : 0; +} +static inline +char * +mblk_getp(mblk_t *b) +{ + return (*b) ? (*b)->d : 0; +} -/* - * Actually change the directory. In an interactive shell, print the - * directory name if "print" is nonzero. - */ -STATIC int -docd(char *dest, int print, int phys) +static +int +mblk_realloc(mblk_t *b, int n) { + int clnk, realsz, new_realsz; + struct mblkcore *q; - TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); + if (n == -1) + n = mblk_getn(b); - /* If logical cd fails, fall back to physical. */ - if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0) - return (-1); + if (!n) + return mblk_clear(b); - if (print && iflag && curdir) - out1fmt("%s\n", curdir); + if (!*b) { + clnk = 1; + realsz = 0; + } else { + clnk = (*b)->clnk; + realsz = (*b)->realsz; + assert(realsz > 0); + } + + new_realsz = MBLK_GETMEMSIZE(n); + if (clnk > 1) { + if (!(q = malloc(new_realsz))) + return -1; + memcpy(q->d, (*b)->d, (*b)->n < n ? (*b)->n : n); + (*b)->clnk--; + *b = q; + } else if (new_realsz != realsz) { + if (!(q = realloc(*b, new_realsz))) + return -1; + *b = q; + } + (*b)->clnk = 1; + (*b)->realsz = new_realsz; + (*b)->n = n; return 0; } -STATIC int -cdlogical(char *dest) +static +int +mblk_write(mblk_t *b, int off, void *buf, int bufn) { - char *p; - char *q; - char *component; - struct stat statb; - int first; - int badstat; + int clnk, realsz, bn, n; - /* - * Check each component of the path. If we find a symlink or - * something we can't stat, clear curdir to force a getcwd() - * next time we get the value of the current directory. - */ - badstat = 0; - cdcomppath = stalloc(strlen(dest) + 1); - scopy(dest, cdcomppath); - STARTSTACKSTR(p); - if (*dest == '/') { - STPUTC('/', p); - cdcomppath++; - } - first = 1; - while ((q = getcomponent()) != NULL) { - if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) - continue; - if (! first) - STPUTC('/', p); - first = 0; - component = q; - while (*q) - STPUTC(*q++, p); - if (equal(component, "..")) - continue; - STACKSTRNUL(p); - if (lstat(stackblock(), &statb) < 0) { - badstat = 1; - break; - } + if (!buf || !bufn) + return 0; + + if (!*b) { + clnk = 1; + realsz = 0; + bn = 0; + } else { + clnk = (*b)->clnk; + realsz = (*b)->realsz; + bn = (*b)->n; } - INTOFF; - if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) { - INTON; - return (-1); + if (off <= realsz) { + if (off == -1) + off = bn; + n = off + bufn; + if (n < bn) + n = bn; + if (mblk_realloc(b, n)) + return -1; + memcpy((*b)->d + off, buf, bufn); } - INTON; - return (0); + return 0; } -STATIC int -cdphysical(char *dest) + +static +void +mblk_dump(mblk_t *b) { + int i; - INTOFF; - if (chdir(dest) < 0 || updatepwd(NULL) < 0) { - INTON; - return (-1); + printf("blk = %p", *b); + if (*b) { + printf("clnk: %d, realsz: %d, n: %d,\n d: ", + (*b)->clnk, (*b)->realsz, (*b)->n); + for (i = 0; i < (*b)->n; i++) + printf("%d:%c ", i, (*b)->d[i]); } - INTON; - return (0); + printf("\n"); } -/* - * Get the next component of the path name pointed to by cdcomppath. - * This routine overwrites the string pointed to by cdcomppath. - */ -STATIC char * -getcomponent(void) + + +static +int +mkcdpath(mblk_t *b, char *dir, int phys) { - char *p; - char *start; + int rc, off, n; + char *p, cdir[MAXPATHLEN < PATH_MAX ? PATH_MAX : MAXPATHLEN]; + mblk_t a[1]; + + if (!*b) { + if (!getcwd(cdir, sizeof cdir)) + memcpy(cdir, "/", 2); + n = strlen(cdir) + 1; + if (mblk_write(b, 0, cdir, n) || mblk_resize(b, n)) + return -1; + } + + assert(*b); + assert((*b)->n == 2 || ((*b)->n > 2 && (*b)->d[(*b)->n - 2] != '/')); + assert(*(*b)->d == '/'); + assert(!(*b)->d[(*b)->n - 1]); + + if (phys) { + char *pathname; + + if (dir && *dir) { + if (*dir == '/') + pathname = dir; + else { + off = 1; + if ((*b)->n != 2) { + off = (*b)->n; + if (mblk_write(b, off-1, "/", 1)) + return -1; + } + if (mblk_write(b, off, dir, strlen(dir)+1)) + return -1; + pathname = (*b)->d; + } + } else + pathname = (*b)->d; - if ((p = cdcomppath) == NULL) - return NULL; - start = cdcomppath; - while (*p != '/' && *p != '\0') - p++; - if (*p == '\0') { - cdcomppath = NULL; - } else { - *p++ = '\0'; - cdcomppath = p; + if (!realpath(pathname, cdir)) + return -1; + if (mblk_write(b, 0, cdir, n = strlen(cdir) + 1) || + mblk_resize(b, n)) + return -1; + return 0; + } + + if (!dir || !*dir) + return 0; + + if (*dir == '/') { + if (mblk_write(b, 0, "/", 2) || mblk_resize(b, 2)) + return -1; + dir++; + } + + mblk_init(a); + mblk_attach(a, b); + + rc = 0; + while ((p = getcomponent(&dir)) != 0) { + if (*p == '.') { + if (!p[1]) + continue; + if (p[1] == '.' && !p[2]) { + for (p = (*b)->d + (*b)->n - 1;;) + if (p-1 == (*b)->d || *--p == '/') + break; + if ((rc = mblk_resize(b, p-(*b)->d+1)) != 0) + break; + if ((rc = mblk_write(b,(*b)->n-1,"",1)) != 0) + break; + continue; + } + } + + off = 1; + if ((*b)->n != 2) { + off = (*b)->n; + if ((rc = mblk_write(b, off-1, "/", 1)) != 0) + break; + } + if ((rc = mblk_write(b, off, p, strlen(p)+1)) != 0) + break; } - return start; -} + if (rc) + mblk_attach(b, a); + mblk_uninit(a); + + return rc; +} /* - * Update curdir (the name of the current directory) in response to a - * cd command. We also call hashcd to let the routines in exec.c know - * that the current directory has changed. + * Actually change the directory. In an interactive shell, print the + * directory name if "print" is nonzero. */ -STATIC int -updatepwd(char *dir) +static +int +docd(mblk_t *curdir, mblk_t *prevdir, char *dest, int phys) { - char *new; - char *p; + int rc; + mblk_t old_curdir[1]; - hashcd(); /* update command hash table */ + TRACE(("docd(\"%s\", %d) called\n", dest, phys)); - /* - * If our argument is NULL, we don't know the current directory - * any more because we traversed a symbolic link or something - * we couldn't stat(). - */ - if (dir == NULL || curdir == NULL) { - if (prevdir) - ckfree(prevdir); + rc = -1; + mblk_init(old_curdir); + mblk_attach(old_curdir, curdir); + if (!mkcdpath(curdir, dest, phys)) { INTOFF; - prevdir = curdir; - curdir = NULL; - if (getpwd() == NULL) { - INTON; - return (-1); + if (chdir((*curdir)->d) < 0) + mblk_attach(curdir, old_curdir); + else { + mblk_attach(prevdir, old_curdir); + if (iflag) + out1fmt("%s\n", (*curdir)->d); + rc = 0; } - setvar("PWD", curdir, VEXPORT); - setvar("OLDPWD", prevdir, VEXPORT); INTON; - return (0); } - cdcomppath = stalloc(strlen(dir) + 1); - scopy(dir, cdcomppath); - STARTSTACKSTR(new); - if (*dir != '/') { - p = curdir; - while (*p) - STPUTC(*p++, new); - if (p[-1] == '/') - STUNPUTC(new); - } - while ((p = getcomponent()) != NULL) { - if (equal(p, "..")) { - while (new > stackblock() && (STUNPUTC(new), *new) != '/'); - } else if (*p != '\0' && ! equal(p, ".")) { - STPUTC('/', new); - while (*p) - STPUTC(*p++, new); + mblk_uninit(old_curdir); + + return rc; +} + +static mblk_t cdir[1] = { 0 }; +static mblk_t pdir[1] = { 0 }; + +int +cdcmd(int argc, char **argv) +{ + char *dest; + char *path; + char *p; + struct stat statb; + int ch, phys; + + optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ + phys = Pflag; + while ((ch = getopt(argc, argv, "LP")) != -1) { + switch (ch) { + case 'L': + phys = 0; + break; + case 'P': + phys = 1; + break; + default: + error("unknown option: -%c", optopt); + break; } } - if (new == stackblock()) - STPUTC('/', new); - STACKSTRNUL(new); - INTOFF; - if (prevdir) - ckfree(prevdir); - prevdir = curdir; - curdir = savestr(stackblock()); - setvar("PWD", curdir, VEXPORT); - setvar("OLDPWD", prevdir, VEXPORT); - INTON; + argc -= optind; + argv += optind; - return (0); + if (argc > 1) + error("too many arguments"); + + if ((dest = *argv) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + else if (dest[0] == '-' && dest[1] == '\0') { + dest = *pdir ? (*pdir)->d : (*cdir)->d; + if (!dest) + dest = "."; + } + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + if ((p = padvance(&path, dest)) != NULL) { + if (!docd(cdir, pdir, p, phys)) + return 0; + } + error("can't cd to %s", dest); + /*NOTREACHED*/ + return 0; } + int pwdcmd(int argc, char **argv) { char buf[PATH_MAX]; int ch, phys; - optreset = 1; optind = 1; opterr = 0; /* initialize getopt */ + optreset = optind = 1; opterr = 0; /* initialize getopt */ phys = Pflag; while ((ch = getopt(argc, argv, "LP")) != -1) { switch (ch) { @@ -344,15 +469,11 @@ if (argc != 0) error("too many arguments"); - if (!phys && getpwd()) { - out1str(curdir); - out1c('\n'); - } else { - if (getcwd(buf, sizeof(buf)) == NULL) - error(".: %s", strerror(errno)); - out1str(buf); - out1c('\n'); - } + if (mkcdpath(cdir, 0, phys)) + return -1; + + out1str((*cdir)->d); + out1c('\n'); return 0; } @@ -364,24 +485,8 @@ char * getpwd(void) { - char buf[PATH_MAX]; - - if (curdir) - return curdir; - if (getcwd(buf, sizeof(buf)) == NULL) { - char *pwd = getenv("PWD"); - struct stat stdot, stpwd; - - if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && - stat(pwd, &stpwd) != -1 && - stdot.st_dev == stpwd.st_dev && - stdot.st_ino == stpwd.st_ino) { - curdir = savestr(pwd); - return curdir; - } - return NULL; - } - curdir = savestr(buf); - - return curdir; + if (!*cdir) + if (mkcdpath(cdir, 0, 1)) + return 0; + return (*cdir)->d; } --------------050700010807040401070403--