Subject: symlinks to directories and executables
From: [email-masked] (Marcus Daniels)

10 Jan 1997 18:40:11 -0800
Sender: [email-masked]
Approved: [email-masked]
Distribution: cygnus
Message-ID: <[email-masked]>
Original-To: [email-masked]
X-Mailer: Gnus v5.3/Emacs 19.34
Original-Sender: [email-masked]

The FAQ mentions (and experience confirms) that paths containing
symlinked directories or paths to executables don't work.

It appears to me that all that is needed to fix this is to check and expand
symlinked directory names before using a path, and to suppress
this behavior in those cases where it is not desired (e.g. unlinks and
lstat).

The following patch implements this; I've tested it with a package that
makes heavy use of both features during its build procedure (CLISP).

Is there any chance a patch like this could be installed? 
If so, what, if anything, needs to be improved?

diff --ignore-all-space -c -r winsup/fhandler.cc /src/winsup/fhandler.cc
*** winsup/fhandler.cc	Tue Dec 10 14:18:50 1996
--- /src/winsup/fhandler.cc	Thu Jan  9 06:27:19 1997
***************
*** 298,304 ****
        syml_p = 0;
      }
  
!   path_conv win32_path (real_path);
    if (win32_path.error)
      {
        set_errno (win32_path.error);
--- 298,304 ----
        syml_p = 0;
      }
  
!   path_conv win32_path (real_path, 0);
    if (win32_path.error)
      {
        set_errno (win32_path.error);
diff --ignore-all-space -c -r winsup/path.cc /src/winsup/path.cc
*** winsup/path.cc	Tue Dec  3 19:23:01 1996
--- /src/winsup/path.cc	Fri Jan 10 00:26:02 1997
***************
*** 136,141 ****
--- 136,192 ----
    return 0;
  }
  
+ 
+ static const char *
+ skip_n_slashes (const char *path, int ind)
+ {
+   int i;
+   const char *ptr = path;
+   for (i = 0 ; i < ind && ptr != NULL; i++)
+     {
+       ptr = strchr (ptr+1, '/');
+       if (ptr)
+         if (ptr[1] == '/')
+           ptr++;
+     }
+   return ptr;
+ }
+ 
+ static int symlink_check_worker (const char *path, char *buf, int buflen, int *syml, int *exec, int follow_mode);
+ 
+ static void
+ symlink_expand (const char *path, char *buf)
+ {
+ 
+   char in[MAX_PATH];
+   char tmp[MAX_PATH];
+   char *next; 
+   int si, tmplen;
+   int exec, syml;
+ 
+   strcpy (in, path);
+   for (si=1; next = (char *)skip_n_slashes (in, si); si++)
+     {
+       *next = '\0';
+       tmplen = symlink_check_worker (in, buf, MAX_PATH - 1, &syml, &exec, -1);
+       *next = '/';
+       if (tmplen > 0)
+         {
+           strcpy (tmp, next);
+           buf[tmplen]='\0';
+ 	  if (buf[0] == '/' || si == 1)
+ 	    strcpy (in, buf);
+ 	  else
+ 	    {
+ 	      const char *prev = skip_n_slashes (in, si - 1);
+ 	      strcpy ((char *)prev + 1, (const char *)buf);
+ 	    }
+ 	  strcat (in, tmp);
+         }
+     }
+   strcpy (buf, in);
+ }
+ 
  /* Convert an arbitrary path SRC to a pure WIN32 path, suitable for passing to
     Win32 API routines.
  
***************
*** 145,157 ****
     If an error occurs, `error' is set to the errno value.
     Otherwise it is set to 0.  */
  
! path_conv::path_conv (const char *src)
  {
    mixed_p = 0;
    silent_p = 0;
    binary_p = 0;
    error = 0;
  
    if (! s->mount.posix_path_p ())
      {
        if (strlen (src) >= MAX_PATH)
--- 196,226 ----
     If an error occurs, `error' is set to the errno value.
     Otherwise it is set to 0.  */
  
! path_conv::path_conv (const char *in_src, int follow_mode)
  {
+   const char *src;
+   char src_[MAX_PATH];
+ 
    mixed_p = 0;
    silent_p = 0;
    binary_p = 0;
    error = 0;
  
+   if (follow_mode > 0)
+     {
+       int exec;
+       symlink_follow (in_src, src_, &exec);
+       src = src_;
+     }
+   else if (follow_mode == 0)
+     {
+       symlink_expand (in_src, src_);
+       src = src_;
+     }
+   else
+     src = in_src;
+ 
+ 
    if (! s->mount.posix_path_p ())
      {
        if (strlen (src) >= MAX_PATH)
***************
*** 1021,1032 ****
  
     If an error occurs -1 is returned and errno is set.  */
  
! int
! symlink_check (const char *path, char *buf, int buflen, int *syml, int *exec)
  {
    int res = -1;
  
!   path_conv pathbuf (path);
    if (pathbuf.error)
      {
        set_errno (pathbuf.error);
--- 1090,1101 ----
  
     If an error occurs -1 is returned and errno is set.  */
  
! static int
! symlink_check_worker (const char *path, char *buf, int buflen, int *syml, int *exec, int follow_mode)
  {
    int res = -1;
  
!   path_conv pathbuf (path, follow_mode);
    if (pathbuf.error)
      {
        set_errno (pathbuf.error);
***************
*** 1110,1115 ****
--- 1179,1191 ----
    return res;
  }
  
+ int
+ symlink_check (const char *path, char *buf, int buflen, int *syml, int *exec)
+ {
+   return symlink_check_worker (path, buf, buflen, syml, exec, 0);
+ }
+ 
+ 
  /* Traverse PATH to its ultimate destination and store that in REALPATH.
     PATH needn't be a symlink (in which case it is copied to REALPATH).
     REALPATH is assumed to be large enough (i.e. MAX_PATH bytes).
***************
*** 1125,1135 ****
     up later but the priority now is on simplicity and correctness.  */
  
  int
! symlink_follow (const char *path, char *realpath, int *exec)
  {
    char buf[MAX_PATH];
    int len;
  
    if (strlen (path) >= MAX_PATH)
      {
        set_errno (ENAMETOOLONG);
--- 1201,1213 ----
     up later but the priority now is on simplicity and correctness.  */
  
  int
! symlink_follow (const char *in_path, char *realpath, int *exec)
  {
    char buf[MAX_PATH];
+   char path[MAX_PATH];
    int len;
  
+   symlink_expand (in_path, path);
    if (strlen (path) >= MAX_PATH)
      {
        set_errno (ENAMETOOLONG);
***************
*** 1147,1152 ****
--- 1225,1232 ----
        /* If that wasn't a symlink, we're done.  */
        if (len == -1 || ! syml_p)
  	{
+           symlink_expand (realpath, buf);
+           strcpy (realpath, buf);
  	  /* We really don't care if an error occurs here.  We just assume
  	     PATH isn't a symlink and let the caller deal with it.  */
  	  if (exec)
diff --ignore-all-space -c -r winsup/path.h /src/winsup/path.h
*** winsup/path.h	Tue Dec  3 19:23:01 1996
--- /src/winsup/path.h	Thu Jan  9 00:58:41 1997
***************
*** 27,33 ****
  
    int error;
  
!   path_conv (const char * const);
    inline char *get_win32 () { return path; }
  };
  
--- 27,33 ----
  
    int error;
  
!   path_conv (const char * const, int follow_mode = 1);
    inline char *get_win32 () { return path; }
  };
  
diff --ignore-all-space -c -r winsup/syscalls.cc /src/winsup/syscalls.cc
*** winsup/syscalls.cc	Tue Dec  3 19:23:03 1996
--- /src/winsup/syscalls.cc	Thu Jan  9 06:15:39 1997
***************
*** 168,174 ****
  {
    int res;
  
!   path_conv win32_name (ourname);
    
    syscall_printf ("_unlink (%s)\n", win32_name.get_win32 ());
  
--- 168,174 ----
  {
    int res;
  
!   path_conv win32_name (ourname, 0);
    
    syscall_printf ("_unlink (%s)\n", win32_name.get_win32 ());
  
***************
*** 477,484 ****
  _link (const char *a, const char *b)
  {
    int res = -1;
!   path_conv real_a (a);
!   path_conv real_b (b);
    
    /* do this with a copy */
    if (CopyFileA (real_a.get_win32 (), real_b.get_win32 (), 1))
--- 477,484 ----
  _link (const char *a, const char *b)
  {
    int res = -1;
!   path_conv real_a (a, 0);
!   path_conv real_b (b, 0);
    
    /* do this with a copy */
    if (CopyFileA (real_a.get_win32 (), real_b.get_win32 (), 1))
***************
*** 496,502 ****
  {
    int res = -1;
  
!   path_conv real_dir (dir);
    
    if (CreateDirectoryA (real_dir.get_win32 (), 0))
      res = 0;
--- 496,502 ----
  {
    int res = -1;
  
!   path_conv real_dir (dir, 0);
    
    if (CreateDirectoryA (real_dir.get_win32 (), 0))
      res = 0;
***************
*** 513,519 ****
  {
    int res = -1;
  
!   path_conv real_dir (dir);
    
    if (RemoveDirectoryA (real_dir.get_win32 ()))
      res = 0;
--- 513,519 ----
  {
    int res = -1;
  
!   path_conv real_dir (dir, 0);
    
    if (RemoveDirectoryA (real_dir.get_win32 ()))
      res = 0;
***************
*** 708,714 ****
  
    debug_printf ("%s (%s, %p)\n", caller, name, buf);
    
!   path_conv real_path (name);
    if (real_path.error)
      {
        set_errno (real_path.error);
--- 708,714 ----
  
    debug_printf ("%s (%s, %p)\n", caller, name, buf);
    
!   path_conv real_path (name, !nofollow);
    if (real_path.error)
      {
        set_errno (real_path.error);
***************
*** 814,821 ****
  int
  _rename (const char *oldpath, const char *newpath)
  {
!   path_conv real_old (oldpath);
!   path_conv real_new (newpath);
    int res;
    int oldatts = GetFileAttributesA (real_old.get_win32 ());
    int newatts = GetFileAttributesA (real_new.get_win32 ());
--- 814,821 ----
  int
  _rename (const char *oldpath, const char *newpath)
  {
!   path_conv real_old (oldpath, 0);
!   path_conv real_new (newpath, 0);
    int res;
    int oldatts = GetFileAttributesA (real_old.get_win32 ());
    int newatts = GetFileAttributesA (real_new.get_win32 ());

-
For help on using this list, send a message to
"[email-masked]" with one line of text: "help".