Strange behavior of su
Paul Borman
prb at cray.UUCP
Sat Sep 28 13:22:55 AEST 1985
No one is crazy. This is a bug. It can be fixed (without chdiring
to / (which wont work on a system which has / as 711)).
Solution: change sh.dir.c so that if you are not superuser it will
call /bin/pwd to find your current working directory.
make /bin/pwd suid to root. This is NOT a security problem.
Enhancement:
put your current working dir into a environ var called CWD
and your inode/device into CWDINO. Then, when the csh starts
up, it stats . and sees if CWDINO is right, if so, then just
use CWD, else call pwd.
Hell, I'll just put the sources in here....
The #ifdef SYSV is the stuff you have to add to make it work.
I ported 4.2 csh to System V and fixed the bug (again) while
I was doing it. The 3rd file is just along for the ride.
-Paul R Borman
Cray Research, Inc.
ihnp4!cray!prb
----sh.dir.c----
static char *sccsid = "@(#)sh.dir.c 4.2 2/3/83";
#include "sh.h"
#include "sh.dir.h"
/*
* C Shell - directory management
*/
struct directory *dfind();
char *dfollow();
struct directory dhead; /* "head" of loop */
int printd; /* force name to be printed */
static char *fakev[] = { "dirs", NOSTR };
/*
* dinit - initialize current working directory
*/
dinit(hp)
char *hp;
{
register char *cp;
register struct directory *dp;
char path[BUFSIZ];
if (loginsh && hp)
cp = hp;
else {
#ifdef SYSV
cp = getwd_up(path);
#else
cp = getwd(path);
#endif
if (cp == NULL) {
write(2, path, strlen(path));
exit(1);
}
}
dp = (struct directory *)calloc(sizeof (struct directory), 1);
dp->di_name = savestr(cp);
dp->di_count = 0;
dhead.di_next = dhead.di_prev = dp;
dp->di_next = dp->di_prev = &dhead;
printd = 0;
dnewcwd(dp);
}
/*
* dodirs - list all directories in directory loop
*/
dodirs(v)
char **v;
{
register struct directory *dp;
bool lflag;
char *hp = value("home");
if (*hp == '\0')
hp = NOSTR;
if (*++v != NOSTR)
if (eq(*v, "-l") && *++v == NOSTR)
lflag = 1;
else
error("Usage: dirs [ -l ]");
else
lflag = 0;
dp = dcwd;
do {
if (dp == &dhead)
continue;
if (!lflag && hp != NOSTR) {
dtildepr(hp, dp->di_name);
} else
printf("%s", dp->di_name);
printf(" ");
} while ((dp = dp->di_prev) != dcwd);
printf("\n");
}
dtildepr(home, dir)
register char *home, *dir;
{
if (!eq(home, "/") && prefix(home, dir))
printf("~%s", dir + strlen(home));
else
printf("%s", dir);
}
/*
* dochngd - implement chdir command.
*/
dochngd(v)
char **v;
{
register char *cp;
register struct directory *dp;
printd = 0;
if (*++v == NOSTR) {
if ((cp = value("home")) == NOSTR || *cp == 0)
bferr("No home directory");
if (chdir(cp) < 0)
bferr("Can't change to home directory");
cp = savestr(cp);
} else if ((dp = dfind(*v)) != 0) {
printd = 1;
if (chdir(dp->di_name) < 0)
Perror(dp->di_name);
dcwd->di_prev->di_next = dcwd->di_next;
dcwd->di_next->di_prev = dcwd->di_prev;
goto flushcwd;
} else
cp = dfollow(*v);
dp = (struct directory *)calloc(sizeof (struct directory), 1);
dp->di_name = cp;
dp->di_count = 0;
dp->di_next = dcwd->di_next;
dp->di_prev = dcwd->di_prev;
dp->di_prev->di_next = dp;
dp->di_next->di_prev = dp;
flushcwd:
dfree(dcwd);
dnewcwd(dp);
}
/*
* dfollow - change to arg directory; fall back on cdpath if not valid
*/
char *
dfollow(cp)
register char *cp;
{
register char **cdp;
struct varent *c;
cp = globone(cp);
if (chdir(cp) == 0)
goto gotcha;
if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp)
&& (c = adrof("cdpath"))) {
for (cdp = c->vec; *cdp; cdp++) {
char buf[BUFSIZ];
strcpy(buf, *cdp);
strcat(buf, "/");
strcat(buf, cp);
if (chdir(buf) >= 0) {
printd = 1;
xfree(cp);
cp = savestr(buf);
goto gotcha;
}
}
}
if (adrof(cp)) {
char *dp = value(cp);
if (dp[0] == '/' || dp[0] == '.')
if (chdir(dp) >= 0) {
xfree(cp);
cp = savestr(dp);
printd = 1;
goto gotcha;
}
}
xfree(cp);
Perror(cp);
gotcha:
if (*cp != '/') {
char *dp = calloc(strlen(cp) + strlen(dcwd->di_name) + 2, 1);
strcpy(dp, dcwd->di_name);
strcat(dp, "/");
strcat(dp, cp);
xfree(cp);
cp = dp;
}
dcanon(cp);
return (cp);
}
/*
* dopushd - push new directory onto directory stack.
* with no arguments exchange top and second.
* with numeric argument (+n) bring it to top.
*/
dopushd(v)
char **v;
{
register struct directory *dp;
printd = 1;
if (*++v == NOSTR) {
if ((dp = dcwd->di_prev) == &dhead)
dp = dhead.di_prev;
if (dp == dcwd)
bferr("No other directory");
if (chdir(dp->di_name) < 0)
Perror(dp->di_name);
dp->di_prev->di_next = dp->di_next;
dp->di_next->di_prev = dp->di_prev;
dp->di_next = dcwd->di_next;
dp->di_prev = dcwd;
dcwd->di_next->di_prev = dp;
dcwd->di_next = dp;
} else if (dp = dfind(*v)) {
if (chdir(dp->di_name) < 0)
Perror(dp->di_name);
} else {
register char *cp;
cp = dfollow(*v);
dp = (struct directory *)calloc(sizeof (struct directory), 1);
dp->di_name = cp;
dp->di_count = 0;
dp->di_prev = dcwd;
dp->di_next = dcwd->di_next;
dcwd->di_next = dp;
dp->di_next->di_prev = dp;
}
dnewcwd(dp);
}
/*
* dfind - find a directory if specified by numeric (+n) argument
*/
struct directory *
dfind(cp)
register char *cp;
{
register struct directory *dp;
register int i;
register char *ep;
if (*cp++ != '+')
return (0);
for (ep = cp; digit(*ep); ep++)
continue;
if (*ep)
return (0);
i = getn(cp);
if (i <= 0)
return (0);
for (dp = dcwd; i != 0; i--) {
if ((dp = dp->di_prev) == &dhead)
dp = dp->di_prev;
if (dp == dcwd)
bferr("Directory stack not that deep");
}
return (dp);
}
/*
* dopopd - pop a directory out of the directory stack
* with a numeric argument just discard it.
*/
dopopd(v)
char **v;
{
register struct directory *dp, *p;
printd = 1;
if (*++v == NOSTR)
dp = dcwd;
else if ((dp = dfind(*v)) == 0)
bferr("Bad directory");
if (dp->di_prev == &dhead && dp->di_next == &dhead)
bferr("Directory stack empty");
if (dp == dcwd) {
if ((p = dp->di_prev) == &dhead)
p = dhead.di_prev;
if (chdir(p->di_name) < 0)
Perror(p->di_name);
}
dp->di_prev->di_next = dp->di_next;
dp->di_next->di_prev = dp->di_prev;
if (dp == dcwd)
dnewcwd(p);
else
dodirs(fakev);
dfree(dp);
}
/*
* dfree - free the directory (or keep it if it still has ref count)
*/
dfree(dp)
register struct directory *dp;
{
if (dp->di_count != 0)
dp->di_next = dp->di_prev = 0;
else
xfree(dp->di_name), xfree((char *)dp);
}
/*
* dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
* we are of course assuming that the file system is standardly
* constructed (always have ..'s, directories have links)
*/
dcanon(cp)
char *cp;
{
register char *p, *sp;
register bool slash;
if (*cp != '/')
abort();
for (p = cp; *p; ) { /* for each component */
sp = p; /* save slash address */
while(*++p == '/') /* flush extra slashes */
;
if (p != ++sp)
strcpy(sp, p);
p = sp; /* save start of component */
slash = 0;
while(*++p) /* find next slash or end of path */
if (*p == '/') {
slash = 1;
*p = 0;
break;
}
if (*sp == '\0') /* if component is null */
if (--sp == cp) /* if path is one char (i.e. /) */
break;
else
*sp = '\0';
else if (eq(".", sp)) {
if (slash) {
strcpy(sp, ++p);
p = --sp;
} else if (--sp != cp)
*sp = '\0';
} else if (eq("..", sp)) {
if (--sp != cp)
while (*--sp != '/')
;
if (slash) {
strcpy(++sp, ++p);
p = --sp;
} else if (cp == sp)
*++sp = '\0';
else
*sp = '\0';
} else if (slash)
*p = '/';
}
}
/*
* dnewcwd - make a new directory in the loop the current one
*/
dnewcwd(dp)
register struct directory *dp;
{
dcwd = dp;
set("cwd", savestr(dcwd->di_name));
if (printd)
dodirs(fakev);
#ifdef SYSV
dnewCWD();
#endif
}
----sh.getwd_up.c----
#ifdef SYSV
#include "sh.h"
#define NULL 0
#define STREQ(a,b) (!strcmp(a,b))
/*
* returns the current working directory ala pwd(1)
* exits if it can't find it. the actual getwd(3J)
* spits out a error message on stderr, but since csh(1)
* doesn't have filedes 2 open, it makes no sense.
*
* up => unprivilaged
*/
char *
getwd_up(s)
char *s;
{
int p[2];
int ppid = getpid();
int cpid;
int status;
int r;
char *s1;
if (checkCWD(s))
return(s);
if (pipe(p) < 0)
exit(1);
switch(cpid = fork()) {
case -1:
exit(1);
case 0:
close(p[0]);
/*
* This is actually the case in the csh(1).
* csh(1) doesn't have 0 or 1 open, so when you do
* a pipe, you end up with p[0] == 0 && p[1] == 1.
*/
if (p[1] != 1) {
if (dup2(p[1], 1) < 0)
exit(1);
close(p[1]);
}
execl("/bin/pwd", "pwd", 0);
exit(1);
default:
close(p[1]);
/*
* make sure we get the right kid
*/
while ((r = wait(&status)) != -1 && r != cpid)
;
if ( r == -1)
exit(1);
break;
}
/*
* we exit if the kid exited un-nornal like
*/
if (status & 0377)
exit(1);
if ((r = read(p[0], s, BUFSIZ)) <= 0)
exit(1);
close(p[0]);
s[r] = '\0';
s1 = s;
/*
* Also, we only want up to and not including the first newline
*/
while (*s1)
if (*s1++ == '\n') {
*--s1 = '\0';
break;
}
return(s);
}
dnewCWD()
{
struct stat st;
if (stat(".", &st) < 0)
return;
setenv("CWD", savestr(value("cwd")));
setenv("CWDINO", savestr(ltoo(((long)st.st_dev << 16) |
((long)st.st_ino))));
}
char *
checkCWD(s)
char *s;
{
char *CWD;
char *CWDINO;
struct stat st;
if (((CWD = getenv("CWD")) == NULL)
|| ((CWDINO = getenv("CWDINO")) == NULL)
|| (stat(".", &st) < 0))
return(NULL);
if (STREQ(ltoo(((long)st.st_dev << 16) | (long)st.st_ino),CWDINO))
return(strcpy(s, CWD));
return(NULL);
}
char *
ltoo(v)
long v;
{
static char rbuf[16];
char *p = rbuf;
while (v > 0) {
*p++ = (v & 07) + '0';
v = v >> 3;
}
return(reverse(rbuf));
}
char *
reverse(s)
char *s;
{
char *os = s;
char *p = s;
char c;
while (*p)
++p;
--p;
while (s < p) {
c = *s;
*s++ = *p;
*p-- = c;
}
return(os);
}
#endif
----sh.which.c---
#ifdef MOD_WHICH
#include "sh.h"
dowhich(v)
char **v;
{
struct varent *vp;
struct biltins *bp;
int found;
while (*++v) {
found = 0;
for (vp = aliases.link; vp; vp = vp->link) {
if (!strcmp(vp->name, *v)) {
palias(vp);
++found;
}
}
#ifdef MOD_ASSIGN
for (vp = assigns.link; vp; vp = vp->link) {
if (!strcmp(vp->name, *v)) {
passign(vp);
++found;
}
}
#endif MOD_ASSIGN
if (!found )
for (bp = bfunc; bp->bname; ++bp) {
if (!strcmp(bp->bname, *v)) {
printf("%s: is built into the csh\n", *v);
++found;
}
}
if (!found)
which(*v);
}
}
palias(vp)
struct varent *vp;
{
printf(vp->name);
printf(": aliased to ");
blkpr(vp->vec);
printf("\n");
}
#ifdef MOD_ASSIGN
passign(vp)
struct varent *vp;
{
printf(vp->name);
printf(": assigned to ");
blkpr(vp->vec);
printf("\n");
}
#endif MOD_ASSIGN
which(file)
char *file;
{
char tmp[100];
char *t = tmp;
struct varent *vp;
char **p;
if (vp = adrof("path")) {
p = vp->vec;
for (p = vp->vec ; *p; ++p) {
strcpy(tmp, *p);
strcat(tmp, "/");
strcat(tmp, file);
if (access(tmp, 1) == 0) {
printf("%s\n",tmp);
return;
}
}
}
printf("no %s in ", file);
blkpr(adrof("path")->vec);
printf("\n");
}
#endif
--
-Paul R Borman
Cray Research, Inc.
...ihnp4!cray!prb
More information about the Comp.unix.wizards
mailing list