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