Module Name:    src
Committed By:   kre
Date:           Sun Oct 31 02:12:01 UTC 2021

Modified Files:
        src/bin/sh: cd.c

Log Message:
PR bin/45390 - fix for folly four

In the pwd builtin, verify that curdir names '.' before
simply printing it.   Never alter PWD or OLDPWD in the
pwd command.

Also while here, implement the (new: coming in POSIX, but has existed
for a while in several other shells) -e option to cd (with -e, cd -P
will exit(1) if the chdir() succeeds, but PWD cannot be discovered).

cd now prints the directory name used (if different from that given,
or cdprint is on) if interactive or (the new bit)in posix mode.

Some additional/changed comments added, and a DEBUG mode trace call
that was accidentally put inside an #if 0 block moved to where it
can do some good.

XXX pullup -9


To generate a diff of this commit:
cvs rdiff -u -r1.50 -r1.51 src/bin/sh/cd.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/cd.c
diff -u src/bin/sh/cd.c:1.50 src/bin/sh/cd.c:1.51
--- src/bin/sh/cd.c:1.50	Wed Jul  5 20:00:27 2017
+++ src/bin/sh/cd.c	Sun Oct 31 02:12:01 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $	*/
+/*	$NetBSD: cd.c,v 1.51 2021/10/31 02:12:01 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,12 +37,13 @@
 #if 0
 static char sccsid[] = "@(#)cd.c	8.2 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $");
+__RCSID("$NetBSD: cd.c,v 1.51 2021/10/31 02:12:01 kre Exp $");
 #endif
 #endif /* not lint */
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -67,10 +68,11 @@ __RCSID("$NetBSD: cd.c,v 1.50 2017/07/05
 #include "show.h"
 #include "cd.h"
 
-STATIC int docd(const char *, int);
+STATIC int docd(const char *, bool, bool);
 STATIC char *getcomponent(void);
-STATIC void updatepwd(const char *);
+STATIC bool updatepwd(const char *);
 STATIC void find_curdir(int noerror);
+STATIC bool is_curdir(const char *);
 
 char *curdir = NULL;		/* current working directory */
 char *prevdir;			/* previous working directory */
@@ -84,10 +86,13 @@ cdcmd(int argc, char **argv)
 	char *p;
 	char *d;
 	struct stat statb;
+	char opt;
+	bool eopt = false;
 	int print = cdprint;	/* set -o cdprint to enable */
 
-	while (nextopt("P") != '\0')
-		;
+	while ((opt = nextopt("Pe")) != '\0')
+		if (opt == 'e')
+			eopt = true;
 
 	/*
 	 * Try (quite hard) to have 'curdir' defined, nothing has set
@@ -128,19 +133,13 @@ cdcmd(int argc, char **argv)
 		stunalloc(p);
 		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
 			int dopr = print;
+			int x;
 
-			if (!print) {
-				/*
-				 * XXX - rethink
-				 */
-				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
-					dopr = strcmp(p + 2, dest);
-				else
-					dopr = strcmp(p, dest);
-			}
-			if (docd(p, dopr) >= 0)
-				return 0;
+			if (!print)
+				dopr = strcmp(p, dest);
 
+			if ((x = docd(p, dopr != 0, eopt)) >= 0)
+				return x;
 		}
 	}
 	error("can't cd to %s", dest);
@@ -154,8 +153,10 @@ cdcmd(int argc, char **argv)
  */
 
 STATIC int
-docd(const char *dest, int print)
+docd(const char *dest, bool print, bool eopt)
 {
+	bool gotpwd;
+
 #if 0		/* no "cd -L" (ever) so all this is just a waste of time ... */
 	char *p;
 	char *q;
@@ -164,8 +165,6 @@ docd(const char *dest, int print)
 	int first;
 	int badstat;
 
-	CTRACE(DBG_CMDS, ("docd(\"%s\", %d) called\n", dest, print));
-
 	/*
 	 *  Check each component of the path. If we find a symlink or
 	 *  something we can't stat, clear curdir to force a getcwd()
@@ -199,16 +198,19 @@ docd(const char *dest, int print)
 	}
 #endif
 
+	CTRACE(DBG_CMDS, ("docd(\"%s\", %s, %s) called\n", dest,
+	    print ? "true" : "false", eopt ? "true" : "false"));
+
 	INTOFF;
 	if (chdir(dest) < 0) {
 		INTON;
 		return -1;
 	}
-	updatepwd(NULL);	/* only do cd -P, no "pretend" -L mode */
+	gotpwd = updatepwd(NULL);   /* only do cd -P, no "pretend" -L mode */
 	INTON;
-	if (print && iflag == 1 && curdir)
-		out1fmt("%s\n", curdir);
-	return 0;
+	if (print && (iflag || posix))
+		out1fmt("%s\n", gotpwd ? curdir : dest);
+	return gotpwd || !eopt ? 0 : 1;
 }
 
 
@@ -245,7 +247,7 @@ getcomponent(void)
  * that the current directory has changed.
  */
 
-STATIC void
+STATIC bool
 updatepwd(const char *dir)
 {
 	char *new;
@@ -256,7 +258,7 @@ updatepwd(const char *dir)
 	/*
 	 * 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().
+	 * we couldn't stat().   Or we simply don't trust what we had.
 	 */
 	if (dir == NULL || curdir == NULL)  {
 		if (prevdir)
@@ -269,10 +271,14 @@ updatepwd(const char *dir)
 		if (curdir) {
 			setvar("OLDPWD", prevdir, VEXPORT);
 			setvar("PWD", curdir, VEXPORT);
+			return true;
 		} else
 			unsetvar("PWD", 0);
-		return;
+		return false;
 	}
+
+	/* XXX none of the following code is ever executed any more */
+
 	cdcomppath = stalloc(strlen(dir) + 1);
 	scopy(dir, cdcomppath);
 	STARTSTACKSTR(new);
@@ -303,6 +309,25 @@ updatepwd(const char *dir)
 	setvar("OLDPWD", prevdir, VEXPORT);
 	setvar("PWD", curdir, VEXPORT);
 	INTON;
+	return true;
+}
+
+/*
+ * Test whether we are currently in the direcory given
+ * (provided it is given, and is absolute)
+ * ie: determine if path is fully qualified pathname of "."
+ */
+STATIC bool
+is_curdir(const char *path)
+{
+	struct stat stdot, stpath;
+
+	return	path != NULL &&
+		*path == '/' &&
+		stat(".", &stdot) != -1 &&
+		stat(path, &stpath) != -1 &&
+		stdot.st_dev == stpath.st_dev &&
+		stdot.st_ino == stpath.st_ino;
 }
 
 /*
@@ -329,8 +354,16 @@ pwdcmd(int argc, char **argv)
 	else
 		find_curdir(0);
 
+#if 0	/* posix has been changed to forbid this */
 	setvar("OLDPWD", prevdir, VEXPORT);
 	setvar("PWD", curdir, VEXPORT);
+#endif
+
+	if (!is_curdir(curdir)) {
+		find_curdir(1);
+		if (curdir == NULL)
+			error("Unable to find current directory");
+	}
 	out1str(curdir);
 	out1c('\n');
 	return 0;
@@ -358,7 +391,6 @@ void
 getpwd(int noerror)
 {
 	char *pwd;
-	struct stat stdot, stpwd;
 	static int first = 1;
 
 	if (curdir)
@@ -367,10 +399,7 @@ getpwd(int noerror)
 	if (first) {
 		first = 0;
 		pwd = getenv("PWD");
-		if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
-		    stat(pwd, &stpwd) != -1 &&
-		    stdot.st_dev == stpwd.st_dev &&
-		    stdot.st_ino == stpwd.st_ino) {
+		if (is_curdir(pwd)) {
 			curdir = savestr(pwd);
 			return;
 		}
@@ -392,6 +421,13 @@ find_curdir(int noerror)
 	 * getcwd, but traditionally getcwd is implemented using popen
 	 * to /bin/pwd. This creates a problem for us, since we cannot
 	 * keep track of the job if it is being ran behind our backs.
+	 * XXX That's not actually the problem, a process created and
+	 * XXX destroyed that we know nothing about is harmless.  The
+	 * XXX problem is that old popen() implementations would use
+	 * XXX wait(2) to await completion of the command, and that might
+	 * XXX collect (and ignore) our children.   As long as we are
+	 * XXX confident that popen() uses waitpid() (or the equv) there
+	 * XXX would not be a problem.   But how do we know that?
 	 * So we re-implement getcwd(), and we suppress interrupts
 	 * throughout the process. This is not completely safe, since
 	 * the user can still break out of it by killing the pwd program.

Reply via email to