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