From b951adce93f8ef1bcdbc06fc9f93ac7af5ee7d11 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Wed, 2 Dec 2020 16:12:58 +0100 Subject: [PATCH] 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 --- 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 aaa549f21..7ec784e8b 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);}