Subject: bin/1776: /bin/sh prints pathname after 'cd' through symlink.
To: None <gnats-bugs@gnats.netbsd.org>
From: Chris G. Demetriou <cgd@NetBSD.ORG>
List: netbsd-bugs
Date: 11/20/1995 21:39:48
>Number: 1776
>Category: bin
>Synopsis: /bin/sh doesn't act like other [k]shs, cd'ing thru symlinks
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: bin-bug-people (Utility Bug People)
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Mon Nov 20 22:05:03 1995
>Last-Modified:
>Originator: Chris G. Demetriou
>Organization:
Kernel Hackers 'r' Us
>Release: NetBSD-current (trunk) as of 11/20/95
>Environment:
System: NetBSD sun-lamp.pc.cs.cmu.edu 1.0A NetBSD 1.0A (SUN_LAMP) #4: Mon Nov 20 19:31:01 EST 1995 cgd@sun-lamp.pc.cs.cmu.edu:/usr/src/sys/arch/i386/compile/SUN_LAMP i386
>Description:
NetBSD's /bin/sh is a bit more verbose when cd'ing through symlinks
than on other systems. In particular, when cd'ing through a symlink,
NetBSD's /bin/sh:
(1) prints out the destination of the symlink, and
(2) sets the internal notion of the pwd to be
the 'real' destination of the symlink.
This contrasts with (apparently) standard sh/ksh, which:
(1) is silent, and
(2) doesn't ferret out the 'real' destination of
the symlink.
The difference in output from the 'norm' is annoying enough, but
the way it picks the real symlink destination can be problematic:
it reads each symlink, and applies its own interpretation to
the contents (via what it thinks are the 'normal' file system
methods). If for some reason a file system interprets
symlinks specially (for instance, so that "cd @foo" doesn't
work the same as "cd symlink" where symlink contains "@foo"),
the NetBSD sh will break. (This could happen because a
special NFS server is being tricky, or for a variety of
reasons.)
>How-To-Repeat:
Do:
cd /tmp
ln -s /tmp bar
cd bar
pwd
/bin/pwd
on NetBSD, and using sh or ksh on other systems.
With NetBSD's /bin/sh, the results will end up like:
$ cd /tmp
$ ln -s /tmp bar
$ cd bar
/tmp
$ pwd
/tmp
$ /bin/pwd
/tmp
But, using the SunOS sh or ksh, the results end up like:
$ cd /tmp
$ ln -s /tmp bar
$ cd bar
$ pwd
/tmp/bar
$ /bin/pwd
/tmp
>Fix:
Apply the following diff. If pwd should really report
the 'correct current directory', it should probably get
it via getcwd(), not by translating the pathname itself.
Index: cd.c
===================================================================
RCS file: /a/cvsroot/src/bin/sh/cd.c,v
retrieving revision 1.14
diff -c -r1.14 cd.c
*** cd.c 1995/11/19 23:27:37 1.14
--- cd.c 1995/11/21 02:25:07
***************
*** 118,241 ****
/*
! * Actually do the chdir. If the name refers to symbolic links, we
! * compute the actual directory name before doing the cd. In an
! * interactive shell, print the directory name if "print" is nonzero
! * or if the name refers to a symbolic link. We also print the name
! * if "/u/logname" was expanded in it, since this is similar to a
! * symbolic link. (The check for this breaks if the user gives the
! * cd command some additional, unused arguments.)
*/
- #if SYMLINKS == 0
STATIC int
docd(dest, print)
char *dest;
- {
- INTOFF;
- if (chdir(dest) < 0) {
- INTON;
- return -1;
- }
- updatepwd(dest);
- INTON;
- if (print && iflag)
- out1fmt("%s\n", stackblock());
- return 0;
- }
-
- #else
-
-
-
- STATIC int
- docd(dest, print)
- char *dest;
- int print;
{
- register char *p;
- register char *q;
- char *symlink;
- char *component;
- struct stat statb;
- int first;
- int i;
TRACE(("docd(\"%s\", %d) called\n", dest, print));
-
- top:
- cdcomppath = dest;
- 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)
- error("lstat %s failed", stackblock());
- if (!S_ISLNK(statb.st_mode))
- continue;
-
- /* Hit a symbolic link. We have to start all over again. */
- print = 1;
- STPUTC('\0', p);
- symlink = grabstackstr(p);
- i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */
- if (cdcomppath != NULL)
- i += strlen(cdcomppath);
- p = stalloc(i);
- if (readlink(symlink, p, (int)statb.st_size) < 0) {
- error("readlink %s failed", stackblock());
- }
- if (cdcomppath != NULL) {
- p[(int)statb.st_size] = '/';
- scopy(cdcomppath, p + (int)statb.st_size + 1);
- } else {
- p[(int)statb.st_size] = '\0';
- }
- if (p[0] != '/') { /* relative path name */
- char *r;
- q = r = symlink;
- while (*q) {
- if (*q++ == '/')
- r = q;
- }
- *r = '\0';
- dest = stalloc(strlen(symlink) + strlen(p) + 1);
- scopy(symlink, dest);
- strcat(dest, p);
- } else {
- dest = p;
- }
- goto top;
- }
- STPUTC('\0', p);
- p = grabstackstr(p);
INTOFF;
! if (chdir(p) < 0) {
INTON;
return -1;
}
! updatepwd(p);
INTON;
if (print && iflag)
! out1fmt("%s\n", p);
return 0;
}
- #endif /* SYMLINKS */
-
/*
--- 118,144 ----
/*
! * Actually do the chdir. In an interactive shell, print the
! * directory name if "print" is nonzero.
*/
STATIC int
docd(dest, print)
char *dest;
{
TRACE(("docd(\"%s\", %d) called\n", dest, print));
INTOFF;
! if (chdir(dest) < 0) {
INTON;
return -1;
}
! updatepwd(dest);
INTON;
if (print && iflag)
! out1fmt("%s\n", stackblock());
return 0;
}
/*
Index: shell.h
===================================================================
RCS file: /a/cvsroot/src/bin/sh/shell.h,v
retrieving revision 1.8
diff -c -r1.8 shell.h
*** shell.h 1995/05/11 21:30:22 1.8
--- shell.h 1995/11/21 02:25:08
***************
*** 41,47 ****
/*
* The follow should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
- * SYMLINKS -> 1 if your system includes symbolic links, 0 otherwise.
* SHORTNAMES -> 1 if your linker cannot handle long names.
* define BSD if you are running 4.2 BSD or later.
* define SYSV if you are running under System V.
--- 41,46 ----
***************
*** 54,60 ****
#define JOBS 1
- #define SYMLINKS 1
#ifndef BSD
#define BSD 1
#endif
--- 53,58 ----
>Audit-Trail:
>Unformatted: