https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=aec6479820fee5f71d50930bf0dde2bbf386bd4b

commit aec6479820fee5f71d50930bf0dde2bbf386bd4b
Author: Corinna Vinschen <[email protected]>
Date:   Wed Dec 2 16:12:58 2020 +0100

    Cygwin: add flag to indicate reparse points unknown to WinAPI
    
    https://cygwin.com/pipermail/cygwin/2020-December/246938.html
    reports a problem where, when adding a Cygwin default symlink
    to $PATH since Cygwin 3.1.5, $PATH handling appears to be broken.
    
    3.1.5 switched to WSL symlinks as Cygwin default symlinks.
    
    A piece of code in path handling skips resolving reparse points
    if they are the last component in the path.  Thus a reparse point
    in $PATH is not resolved but converted to Windows path syntax
    verbatim.
    
    If you do this with a WSL symlink, certain WinAPI functions fail.
    The underlying $PATH handling fails to recognize the reparse
    point in $PATH and returns with STATUS_IO_REPARSE_TAG_NOT_HANDLED.
    As a result, the calling WinAPI function fails, most prominently
    so CreateProcess.
    
    Fix this problem by adding a PATH_REP_NOAPI bit to path_types
    and a matching method path_conv::is_winapi_reparse_point().
    
    Right now this flag is set for WSL symlinks and Cygwin AF_UNIX
    sockets (new type implemented as reparse points).
    
    The aforementioned code skipping repare point path resolution calls
    is_winapi_reparse_point() rather than is_known_reparse_point(),
    so now path resolution is only skipped for reparse points known
    to WinAPI.
    
    Signed-off-by: Corinna Vinschen <[email protected]>

Diff:
---
 winsup/cygwin/path.cc |  8 ++++----
 winsup/cygwin/path.h  | 12 ++++++++++++
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 7e6243d32..abd3687df 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -1017,7 +1017,7 @@ path_conv::check (const char *src, unsigned opt,
                {
                  if (component == 0
                      && (!(opt & PC_SYM_FOLLOW)
-                         || (is_known_reparse_point ()
+                         || (is_winapi_reparse_point ()
                              && (opt & PC_SYM_NOFOLLOW_REP))))
                    {
                      /* Usually a trailing slash requires to follow a symlink,
@@ -2622,7 +2622,7 @@ check_reparse_point_target (HANDLE h, bool remote, 
PREPARSE_DATA_BUFFER rp,
            }
          RtlInitCountedUnicodeString (psymbuf, utf16_buf,
                                       utf16_bufsize * sizeof (WCHAR));
-         return PATH_SYMLINK | PATH_REP;
+         return PATH_SYMLINK | PATH_REP | PATH_REP_NOAPI;
        }
       return -EIO;
     }
@@ -2632,10 +2632,10 @@ check_reparse_point_target (HANDLE h, bool remote, 
PREPARSE_DATA_BUFFER rp,
 
       if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
 #ifdef __WITH_AF_UNIX
-       return PATH_SOCKET | PATH_REP;
+       return PATH_SOCKET | PATH_REP | PATH_REP_NOAPI;
 #else
         /* Recognize this as a reparse point but not as a socket.  */
-        return PATH_REP;
+        return PATH_REP | PATH_REP_NOAPI;
 #endif
     }
   return 0;
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
index 45a047ad3..62bd5ddd5 100644
--- a/winsup/cygwin/path.h
+++ b/winsup/cygwin/path.h
@@ -71,6 +71,7 @@ enum path_types
   PATH_SYMLINK         = _BIT ( 4),    /* symlink understood by Cygwin */
   PATH_SOCKET          = _BIT ( 5),    /* AF_UNIX socket file */
   PATH_RESOLVE_PROCFD  = _BIT ( 6),    /* fd symlink via /proc */
+  PATH_REP_NOAPI       = _BIT ( 7),    /* rep. point unknown to WinAPI */
   PATH_DONT_USE                = _BIT (31)     /* conversion to signed 
happens. */
 };
 
@@ -179,7 +180,18 @@ class path_conv
   }
   int issymlink () const {return path_flags & PATH_SYMLINK;}
   int is_lnk_symlink () const {return path_flags & PATH_LNK;}
+  /* This indicates any known reparse point */
   int is_known_reparse_point () const {return path_flags & PATH_REP;}
+  /* This indicates any known reparse point, handled sanely by WinAPI.
+     The difference is crucial: WSL symlinks, for instance, are known
+     reparse points, so we want to open them as reparse points usually.
+     However they are foreign to WinAPI and not handled sanely.  If one
+     is part of $PATH, WinAPI functions may fail under the hood with
+     STATUS_IO_REPARSE_TAG_NOT_HANDLED. */
+  int is_winapi_reparse_point () const
+  {
+    return (path_flags & (PATH_REP | PATH_REP_NOAPI)) == PATH_REP;
+  }
   int isdevice () const {return dev.not_device (FH_FS) && dev.not_device 
(FH_FIFO);}
   int isfifo () const {return dev.is_device (FH_FIFO);}
   int isspecial () const {return dev.not_device (FH_FS);}

Reply via email to