diff options
author | tegge <tegge@FreeBSD.org> | 1998-09-06 21:01:57 +0000 |
---|---|---|
committer | tegge <tegge@FreeBSD.org> | 1998-09-06 21:01:57 +0000 |
commit | 59e6a57bc195e6145db20a1292466fa6df067e24 (patch) | |
tree | 931490c491e36266d9b1957ad05ddaf559ac7993 /bin | |
parent | 705eaef8b847e2fe924e76b18cb4d5064650d3ce (diff) | |
download | FreeBSD-src-59e6a57bc195e6145db20a1292466fa6df067e24.zip FreeBSD-src-59e6a57bc195e6145db20a1292466fa6df067e24.tar.gz |
Don't blindly eliminate `..' and the previous pathname component.
PR: 2541
Obtained from: NetBSD
Diffstat (limited to 'bin')
-rw-r--r-- | bin/sh/cd.c | 173 | ||||
-rw-r--r-- | bin/sh/cd.h | 6 |
2 files changed, 160 insertions, 19 deletions
diff --git a/bin/sh/cd.c b/bin/sh/cd.c index 78f7192..12e78bf 100644 --- a/bin/sh/cd.c +++ b/bin/sh/cd.c @@ -39,7 +39,7 @@ static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; #endif static const char rcsid[] = - "$Id$"; + "$Id: cd.c,v 1.17 1998/05/18 06:43:30 charnier Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -106,7 +106,7 @@ cdcmd(argc, argv) /* * XXX - rethink */ - if (p[0] == '.' && p[1] == '/') + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') p += 2; print = strcmp(p, dest); } @@ -130,22 +130,58 @@ docd(dest, print) char *dest; int print; { + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * 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) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; + } + } + INTOFF; - updatepwd(dest); - if (chdir(stackblock()) < 0) { + if (chdir(dest) < 0) { INTON; return -1; } - hashcd(); /* update command hash table */ - if (prevdir) - ckfree(prevdir); - prevdir = curdir; - curdir = savestr(stackblock()); + updatepwd(badstat ? NULL : dest); INTON; - if (print && iflag) - out1fmt("%s\n", stackblock()); + if (print && iflag && curdir) + out1fmt("%s\n", curdir); return 0; } @@ -176,8 +212,9 @@ getcomponent() /* - * Determine the new working directory, but don't actually enforce - * any changes. + * 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. */ STATIC void updatepwd(dir) @@ -186,12 +223,30 @@ updatepwd(dir) char *new; char *p; + hashcd(); /* update command hash table */ + + /* + * 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); + INTOFF; + prevdir = curdir; + curdir = NULL; + if (getpwd() == NULL) + error("getcwd() failed: %s", strerror(errno)); + setvar("PWD", curdir, VEXPORT | VTEXTFIXED); + setvar("OLDPWD", prevdir, VEXPORT | VTEXTFIXED); + INTON; + return; + } cdcomppath = stalloc(strlen(dir) + 1); scopy(dir, cdcomppath); STARTSTACKSTR(new); if (*dir != '/') { - if (curdir == NULL) - return; p = curdir; while (*p) STPUTC(*p++, new); @@ -210,6 +265,14 @@ updatepwd(dir) if (new == stackblock()) STPUTC('/', new); STACKSTRNUL(new); + INTOFF; + if (prevdir) + ckfree(prevdir); + prevdir = curdir; + curdir = savestr(stackblock()); + setvar("PWD", curdir, VEXPORT | VTEXTFIXED); + setvar("OLDPWD", prevdir, VEXPORT | VTEXTFIXED); + INTON; } @@ -226,6 +289,10 @@ pwdcmd(argc, argv) } + + +#define MAXPWD 256 + /* * Find out what the current directory is. If we already know the current * directory, this routine returns immediately. @@ -233,7 +300,79 @@ pwdcmd(argc, argv) char * getpwd() { + char buf[MAXPWD]; + if (curdir) - return (curdir); - return ((curdir = getcwd(NULL, 0))); + return curdir; + /* + * Things are a bit complicated here; we could have just used + * getcwd, but traditionally getcwd is implemented using popen + * to /bin/pwd. This creates a problem for us, since we cannot + * keep track of the job if it is being ran behind our backs. + * So we re-implement getcwd(), and we suppress interrupts + * throughout the process. This is not completely safe, since + * the user can still break out of it by killing the pwd program. + * We still try to use getcwd for systems that we know have a + * c implementation of getcwd, that does not open a pipe to + * /bin/pwd. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__SVR4) + + 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); +#else + { + char *p; + int i; + int status; + struct job *jp; + int pip[2]; + + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + (void) close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + (void) execl("/bin/pwd", "pwd", (char *)0); + error("Cannot exec /bin/pwd"); + } + (void) close(pip[1]); + pip[1] = -1; + p = buf; + while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 + || (i == -1 && errno == EINTR)) { + if (i > 0) + p += i; + } + (void) close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == buf || p[-1] != '\n') + error("pwd command failed"); + p[-1] = '\0'; + } + curdir = savestr(buf); + INTON; +#endif + return curdir; } diff --git a/bin/sh/cd.h b/bin/sh/cd.h index feb6d81..673ccdc 100644 --- a/bin/sh/cd.h +++ b/bin/sh/cd.h @@ -30,7 +30,9 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: cd.h,v 1.3 1997/12/29 00:00:10 alex Exp $ */ -char *getpwd __P((void)); +char *getpwd __P((void)); +int cdcmd __P((int, char **)); +int pwdcmd __P((int, char **)); |