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

a.s. mitrohin swp at uni-altai.ru
Sat Apr 3 06:20:32 PST 2004


The following reply was made to PR bin/64990; it has been noted by GNATS.

From: "a.s. mitrohin" <swp at uni-altai.ru>
To: freebsd-gnats-submit at FreeBSD.org, swp at 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 <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 <stdlib.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,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--


More information about the freebsd-bugs mailing list