summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2021-03-22 16:51:41 +0100
committerCorinna Vinschen <corinna@vinschen.de>2021-03-23 16:54:37 +0100
commit0631c6644e6398c051dcda1a53c0fc0e40c814a3 (patch)
tree1599f4a2e0786ea7ecccc17d3e8ab5af9f1146d3
parent2533912fc76c08af9e386a371fc6b3eb194d56be (diff)
downloadcygnal-0631c6644e6398c051dcda1a53c0fc0e40c814a3.tar.gz
cygnal-0631c6644e6398c051dcda1a53c0fc0e40c814a3.tar.bz2
cygnal-0631c6644e6398c051dcda1a53c0fc0e40c814a3.zip
Cygwin: Treat Windows Store's "app execution aliases" as symbolic links
When the Windows Store version of Python is installed, so-called "app execution aliases" are put into the `PATH`. These are reparse points under the hood, with an undocumented format. We do know a bit about this format, though, as per the excellent analysis: https://www.tiraniddo.dev/2019/09/overview-of-windows-execution-aliases.html The first 4 bytes is the reparse tag, in this case it's 0x8000001B which is documented in the Windows SDK as IO_REPARSE_TAG_APPEXECLINK. Unfortunately there doesn't seem to be a corresponding structure, but with a bit of reverse engineering we can work out the format is as follows: Version: <4 byte integer> Package ID: <NUL Terminated Unicode String> Entry Point: <NUL Terminated Unicode String> Executable: <NUL Terminated Unicode String> Application Type: <NUL Terminated Unicode String> Let's treat them as symbolic links. For example, in this developer's setup, this will result in the following nice output: $ cd $LOCALAPPDATA/Microsoft/WindowsApps/ $ ls -l python3.exe lrwxrwxrwx 1 me 4096 105 Aug 23 2020 python3.exe -> '/c/Program Files/WindowsApps/PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0/python.exe' Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
-rw-r--r--winsup/cygwin/path.cc40
1 files changed, 40 insertions, 0 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 9d2184d6a..1d6bcbe48 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -2459,6 +2459,22 @@ symlink_info::check_sysfile (HANDLE h)
return res;
}
+typedef struct _REPARSE_APPEXECLINK_BUFFER
+{
+ DWORD ReparseTag;
+ WORD ReparseDataLength;
+ WORD Reserved;
+ struct {
+ DWORD Version; /* Take member name with a grain of salt. */
+ WCHAR Strings[1]; /* Four serialized, NUL-terminated WCHAR strings:
+ - Package ID
+ - Entry Point
+ - Executable Path
+ - Application Type
+ We're only interested in the Executable Path */
+ } AppExecLinkReparseBuffer;
+} REPARSE_APPEXECLINK_BUFFER,*PREPARSE_APPEXECLINK_BUFFER;
+
static bool
check_reparse_point_string (PUNICODE_STRING subst)
{
@@ -2558,6 +2574,30 @@ check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
if (check_reparse_point_string (psymbuf))
return PATH_SYMLINK | PATH_REP;
}
+ else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
+ {
+ /* App execution aliases are commonly used by Windows Store apps. */
+ PREPARSE_APPEXECLINK_BUFFER rpl = (PREPARSE_APPEXECLINK_BUFFER) rp;
+ WCHAR *buf = rpl->AppExecLinkReparseBuffer.Strings;
+ DWORD size = rp->ReparseDataLength / sizeof (WCHAR), n;
+
+ /* It seems that app execution aliases have a payload of four
+ NUL-separated wide string: package id, entry point, executable
+ and application type. We're interested in the executable. */
+ for (int i = 0; i < 3 && size > 0; i++)
+ {
+ n = wcsnlen (buf, size - 1);
+ if (i == 2 && n > 0 && n < size)
+ {
+ RtlInitCountedUnicodeString (psymbuf, buf, n * sizeof (WCHAR));
+ return PATH_SYMLINK | PATH_REP;
+ }
+ if (i == 2)
+ break;
+ buf += n + 1;
+ size -= n + 1;
+ }
+ }
else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
{
/* WSL symlink. Problem: We have to convert the path to UTF-16 for