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 <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2020-12-02 16:12:58 +01:00
parent e9bc4cccef
commit aec6479820
2 changed files with 16 additions and 4 deletions

View File

@ -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;

View File

@ -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);}