bin/64990: /bin/sh unable to change directory but current dir grow anyway

swp at uni-altai.ru swp at uni-altai.ru
Wed Mar 31 04:50:02 PST 2004


>Number:         64990
>Category:       bin
>Synopsis:       /bin/sh unable to change directory but current dir grow anyway
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Mar 31 04:50:00 PST 2004
>Closed-Date:
>Last-Modified:
>Originator:     mitrohin a.s.
>Release:        FreeBSD 5.2-CURRENT i386
>Organization:
Barnaul State Pedagogical University
>Environment:
System: FreeBSD swp.bspu.secna.ru 5.2-CURRENT FreeBSD 5.2-CURRENT #1: Fri Mar 26 17:56:09 OMST 2004 swp at swp.bspu.secna.ru:/usr/obj/usr/src/sys/ag_kernel i386


	
>Description:
builtin cd unable to change work directory but curdir grow anyway.

	
>How-To-Repeat:
# mkdir -p /tmp/1/2
# chmod 770 /tmp/1/2
# cd /tmp/1
# touch 1.txt
# pwd
/tmp/1
# ls -l
total 1
drwxrwx---  2 root  wheel  512 22 ÍÁÒ 11:22 2
-rw-r--r--  1 root  wheel    0 22 ÍÁÒ 11:22 1.txt
# su -m cyrus
$ pwd
/tmp/1
$ cd 2
cd: can't cd to 2
$ pwd
/tmp/1/2
$ ls -l
total 1
drwxrwx---  2 root  wheel  512 22 ÍÁÒ 11:22 2
-rw-r--r--  1 root  wheel    0 22 ÍÁÒ 11:22 1.txt
$ cd 2
cd: can't cd to 2
$ pwd
/tmp/1/2/2
$ ls -l
total 1
drwxrwx---  2 root  wheel  512 22 ÍÁÒ 11:22 2
-rw-r--r--  1 root  wheel    0 22 ÍÁÒ 11:22 1.txt
$ cd 2
cd: can't cd to 2
$ pwd
/tmp/1/2/2/2
$ ls -l
total 1
drwxrwx---  2 root  wheel  512 22 ÍÁÒ 11:22 2
-rw-r--r--  1 root  wheel    0 22 ÍÁÒ 11:22 1.txt
$ cd .
$ pwd
/tmp/1
$ ^D

	
>Fix:
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	31 Mar 2004 12:35:06 -0000
@@ -42,13 +42,18 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/bin/sh/cd.c,v 1.33 2003/07/05 15:18:44 dds Exp $");
 
+#include <stddef.h>
+#include <stdio.h>
+#include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/param.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <limits.h>
+#include <fcntl.h>
 
 /*
  * The cd and pwd commands.
@@ -68,15 +73,305 @@
 #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;
+/*
+ * 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 *p;
+
+	while ((p = strsep(path, "/")) != 0 && !*p)
+		continue;
+	return p;
+}
+
+
+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;
+	}
+	return 0;
+}
+
+static inline
+int
+mblk_attach(mblk_t *a, mblk_t *b)
+{
+	mblk_uninit(a);
+	if (*b) {
+		(*b)->clnk++;
+		*a = *b;
+	}
+	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;
+}
+
+static
+int
+mblk_realloc(mblk_t *b, int n)
+{
+	int clnk, realsz, new_realsz;
+	struct mblkcore *q;
+
+	if (n == -1)
+		n = mblk_getn(b);
+
+	if (!n)
+		return mblk_clear(b);
+
+	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 inline
+int
+mblk_write(mblk_t *b, int off, void *buf, int bufn)
+{
+	int clnk, realsz, bn, n;
+
+	if (!buf || !bufn)
+		return 0;
+	
+	if (!*b) {
+		clnk = 1;
+		realsz = 0;
+		bn = 0;
+	} else {
+		clnk = (*b)->clnk;
+		realsz = (*b)->realsz;
+		bn = (*b)->n;
+	}
+
+	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);
+	}
+	return 0;
+}
+
+
+
+static
+void
+mblk_dump(mblk_t *b)
+{
+	int i;
+
+	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]);
+	}
+	printf("\n");
+}
+
+
+static
+int
+mblk_mkcdpath(mblk_t *b, char *dir, int phys)
+{
+	int rc, off, n;
+	char *p, *d, cdir[MAXPATHLEN];
+	mblk_t a[1];
+
+	if (phys) {
+		if (dir && *dir)
+			if (chdir(dir))
+				return -1;
+		if (!getcwd(cdir, sizeof cdir))
+			return -1;
+		n = strlen(cdir) + 1;
+		if (mblk_write(b, 0, cdir, n) || mblk_resize(b, n))
+			return -1;
+		return 0;
+	}
+
+	if (!*b) {
+		if (!getcwd(cdir, sizeof cdir)) {
+			cdir[0] = '/'; cdir[1] = 0;
+			chdir(cdir);
+		}
+		n = strlen(cdir) + 1;
+		if (mblk_write(b, 0, cdir, n) || mblk_resize(b, n))
+			return -1;
+	}
+
+	if (!dir || !*dir)
+		return 0;
+
+	assert(*b);
+	assert((*b)->n == 2 || ((*b)->n > 2 && (*b)->d[(*b)->n - 2] != '/'));
+	assert(*(*b)->d == '/');
+	assert(!(*b)->d[(*b)->n - 1]);
+
+	if (*dir && *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;
+	}
+
+	if (rc)
+		mblk_attach(b, a);
+	mblk_uninit(a);
+
+	return rc;
+}
+
+/*
+ * Actually change the directory.  In an interactive shell, print the
+ * directory name if "print" is nonzero.
+ */
+static 
+int
+docd(mblk_t *curdir, mblk_t *prevdir, char *dest, int print, int phys)
+{
+	int rc;
+	mblk_t old_curdir[1];
+
+	TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
+
+	rc = -1;
+	mblk_init(old_curdir);
+	mblk_attach(old_curdir, curdir);
+	if (!mblk_mkcdpath(curdir, dest, phys)) {
+		INTOFF;
+		if (chdir((*curdir)->d) < 0)
+			mblk_attach(curdir, old_curdir);
+		else {
+			mblk_attach(prevdir, old_curdir);
+			if (print && iflag)
+				out1fmt("%s\n", (*curdir)->d);
+			rc = 0;
+		}
+		INTON;
+	}
+	mblk_uninit(old_curdir);
+		
+	return rc;
+}
+
+static mblk_t cdir[1] = { 0 };
+static mblk_t pdir[1] = { 0 };
 
 int
 cdcmd(int argc, char **argv)
@@ -113,7 +408,7 @@
 	if (*dest == '\0')
 		dest = ".";
 	if (dest[0] == '-' && dest[1] == '\0') {
-		dest = prevdir ? prevdir : curdir;
+		dest = *pdir ? (*pdir)->d : (*cdir)->d;
 		if (dest)
 			print = 1;
 		else
@@ -131,7 +426,7 @@
 					p += 2;
 				print = strcmp(p, dest);
 			}
-			if (docd(p, print, phys) >= 0)
+			if (!docd(cdir, pdir, p, print, phys))
 				return 0;
 		}
 	}
@@ -141,189 +436,13 @@
 }
 
 
-/*
- * 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)
-{
-
-	TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
-
-	/* If logical cd fails, fall back to physical. */
-	if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0)
-		return (-1);
-
-	if (print && iflag && curdir)
-		out1fmt("%s\n", curdir);
-
-	return 0;
-}
-
-STATIC int
-cdlogical(char *dest)
-{
-	char *p;
-	char *q;
-	char *component;
-	struct stat statb;
-	int first;
-	int badstat;
-
-	/*
-	 *  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;
-		}
-	}
-
-	INTOFF;
-	if (updatepwd(badstat ? NULL : dest) < 0 || chdir(curdir) < 0) {
-		INTON;
-		return (-1);
-	}
-	INTON;
-	return (0);
-}
-
-STATIC int
-cdphysical(char *dest)
-{
-
-	INTOFF;
-	if (chdir(dest) < 0 || updatepwd(NULL) < 0) {
-		INTON;
-		return (-1);
-	}
-	INTON;
-	return (0);
-}
-
-/*
- * 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)
-{
-	char *p;
-	char *start;
-
-	if ((p = cdcomppath) == NULL)
-		return NULL;
-	start = cdcomppath;
-	while (*p != '/' && *p != '\0')
-		p++;
-	if (*p == '\0') {
-		cdcomppath = NULL;
-	} else {
-		*p++ = '\0';
-		cdcomppath = p;
-	}
-	return start;
-}
-
-
-/*
- * 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 int
-updatepwd(char *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) {
-			INTON;
-			return (-1);
-		}
-		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);
-		}
-	}
-	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;
-
-	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 +463,12 @@
 	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 (phys)
+		if (mblk_mkcdpath(cdir, 0, phys))
+			return -1;
+
+	out1str((*cdir)->d);
+	out1c('\n');
 
 	return 0;
 }
@@ -364,24 +480,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 (mblk_mkcdpath(cdir, 0, 1))
+			return 0;
+	return (*cdir)->d;
 }
	


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list