bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: PWD not made canonical on shell start


From: dualbus
Subject: Re: PWD not made canonical on shell start
Date: Mon, 29 May 2017 11:22:56 -0500
User-agent: NeoMutt/20170113 (1.7.2)

On Wed, May 24, 2017 at 11:53:45AM -0400, Chet Ramey wrote:
[...]
> It was changed back in 2015 as the result of
> 
> http://lists.gnu.org/archive/html/bug-bash/2015-09/msg00053.html

After reading POSIX's [1] (section 2.5.3 "Shell Variables"), I noticed
that bash may not be following the specification. This is what the description
of `PWD' says:

| PWD
| Set by the shell and by the cd utility. In the shell the value shall be
| initialized from the environment as follows. If a value for PWD is passed to
| the shell in the environment when it is executed, the value is an absolute
| pathname of the current working directory that is no longer than {PATH_MAX}
| bytes including the terminating null byte, and the value does not contain any
| components that are dot or dot-dot, then the shell shall set PWD to the value
| from the environment. Otherwise, if a value for PWD is passed to the shell in
| the environment when it is executed, the value is an absolute pathname of the
| current working directory, and the value does not contain any components that
| are dot or dot-dot, then it is unspecified whether the shell sets PWD to the
| value from the environment or sets PWD to the pathname that would be output by
| pwd -P. Otherwise, the sh utility sets PWD to the pathname that would be 
output
| by pwd -P. In cases where PWD is set to the value from the environment, the
| value can contain components that refer to files of type symbolic link. In
| cases where PWD is set to the pathname that would be output by pwd -P, if 
there
| is insufficient permission on the current working directory, or on any parent
| of that directory, to determine what that pathname would be, the value of PWD
| is unspecified. Assignments to this variable may be ignored. If an application
| sets or unsets the value of PWD, the behaviors of the cd and pwd utilities are
| unspecified.

My interpretation is the following:

  case 1)
  
  If PWD is passed through the environment,
  and the value is an absolute pathname of the CWD
  and the value is no longer than PATH_MAX
  and the value does not contain any components that are dot or dot-dot,
  then set PWD to the value from the environment.
  
  case 2)
  
  The same as case 1, except for the restriction on PATH_MAX. The result
  is unspecified.
  
  case 3)
  
  If case 1 and 2 do not apply, then set PWD=$(pwd -P)


Bash doesn't really do that. What bash does is:

  case 1)
  
  If PWD is passed through the environment,
  and the value is an absolute pathname of the CWD,
  then set PWD to the current value from the environment

  case 2)

  If case 1 does not apply,
  and the shell is an interactive shell,
  and the shell is a login shell,
  and $HOME is the same as the CWD,
  then set PWD=$HOME

  case 3)

  If case 2 does not apply,
  then call getcwd() and sets PWD to the return value.


The patch below makes bash behave as described by POSIX.


diff --git a/variables.c b/variables.c
index 944de86e..dbf21339 100644
--- a/variables.c
+++ b/variables.c
@@ -827,49 +827,39 @@ initialize_shell_level ()
 /* If we got PWD from the environment, update our idea of the current
    working directory.  In any case, make sure that PWD exists before
    checking it.  It is possible for getcwd () to fail on shell startup,
-   and in that case, PWD would be undefined.  If this is an interactive
-   login shell, see if $HOME is the current working directory, and if
-   that's not the same string as $PWD, set PWD=$HOME. */
-
+   and in that case, PWD would be undefined. */
 void
 set_pwd ()
 {
   SHELL_VAR *temp_var, *home_var;
-  char *temp_string, *home_string, *current_dir;
-
-  home_var = find_variable ("HOME");
-  home_string = home_var ? value_cell (home_var) : (char *)NULL;
+  char *temp_string, *current_dir, *last_component;
 
   temp_var = find_variable ("PWD");
   /* Follow posix rules for importing PWD */
   if (temp_var && imported_p (temp_var) &&
       (temp_string = value_cell (temp_var)) &&
       temp_string[0] == '/' &&
+      !strstr(temp_string, "/./") &&
+      !strstr(temp_string, "/../") &&
+      (last_component=strrchr(temp_string, '/')) &&
+      strcmp(last_component, ".") &&
+      strcmp(last_component, "..") &&
       same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
     {
-      current_dir = sh_canonpath (temp_string, 
PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
-      if (current_dir == 0)
-       current_dir = get_working_directory ("shell_init");
-      else 
-       set_working_directory (current_dir);
-      free (current_dir);
-    }
-  else if (home_string && interactive_shell && login_shell &&
-          same_file (home_string, ".", (struct stat *)NULL, (struct stat 
*)NULL))
-    {
-      set_working_directory (home_string);
-      temp_var = bind_variable ("PWD", home_string, 0);
-      set_auto_export (temp_var);
+      set_working_directory (temp_string);
     }
   else
     {
-      temp_string = get_working_directory ("shell-init");
-      if (temp_string)
-       {
-         temp_var = bind_variable ("PWD", temp_string, 0);
-         set_auto_export (temp_var);
-         free (temp_string);
-       }
+      if (current_dir=get_working_directory ("shell_init"))
+        {
+          if (temp_string=sh_physpath(current_dir, 0))
+            {
+              temp_var = bind_variable ("PWD", temp_string, 0);
+              set_auto_export (temp_var);
+              free (temp_string);
+            }
+          free(current_dir);
+        }
     }
 
   /* According to the Single Unix Specification, v2, $OLDPWD is an



[1] 
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_03

--
Eduardo Bustamante
https://dualbus.me/



reply via email to

[Prev in Thread] Current Thread [Next in Thread]