patch 8.2.4875: MS-Windows: some .exe files are not recognized
Problem: MS-Windows: some .exe files are not recognized.
Solution: Parse APPEXECLINK junctions. (closes #10302)
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 57ac582..fb9a3be 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -440,6 +440,27 @@
#define _fstat _fstat64
static int
+read_reparse_point(const WCHAR *name, char_u *buf, DWORD *buf_len)
+{
+ HANDLE h;
+ BOOL ok;
+
+ h = CreateFileW(name, FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return FAIL;
+
+ ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, *buf_len,
+ buf_len, NULL);
+ CloseHandle(h);
+
+ return ok ? OK : FAIL;
+}
+
+ static int
wstat_symlink_aware(const WCHAR *name, stat_T *stp)
{
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__MINGW32__)
@@ -491,6 +512,61 @@
return _wstat(name, (struct _stat *)stp);
}
+ char_u *
+resolve_appexeclink(char_u *fname)
+{
+ DWORD attr = 0;
+ int idx;
+ WCHAR *p, *end, *wname;
+ // The buffer size is arbitrarily chosen to be "big enough" (TM), the
+ // ceiling should be around 16k.
+ char_u buf[4096];
+ DWORD buf_len = sizeof(buf);
+ REPARSE_DATA_BUFFER *rb = (REPARSE_DATA_BUFFER *)buf;
+
+ wname = enc_to_utf16(fname, NULL);
+ if (wname == NULL)
+ return NULL;
+
+ attr = GetFileAttributesW(wname);
+ if (attr == INVALID_FILE_ATTRIBUTES ||
+ (attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+ {
+ vim_free(wname);
+ return NULL;
+ }
+
+ // The applinks are similar to symlinks but with a huge difference: they can
+ // only be executed, any other I/O operation on them is bound to fail with
+ // ERROR_FILE_NOT_FOUND even though the file exists.
+ if (read_reparse_point(wname, buf, &buf_len) == FAIL)
+ {
+ vim_free(wname);
+ return NULL;
+ }
+ vim_free(wname);
+
+ if (rb->ReparseTag != IO_REPARSE_TAG_APPEXECLINK)
+ return NULL;
+
+ // The (undocumented) reparse buffer contains a set of N null-terminated
+ // Unicode strings, the application path is stored in the third one.
+ if (rb->AppExecLinkReparseBuffer.StringCount < 3)
+ return NULL;
+
+ p = rb->AppExecLinkReparseBuffer.StringList;
+ end = p + rb->ReparseDataLength / sizeof(WCHAR);
+ for (idx = 0; p < end
+ && idx < (int)rb->AppExecLinkReparseBuffer.StringCount
+ && idx != 2; )
+ {
+ if ((*p++ == L'\0'))
+ ++idx;
+ }
+
+ return utf16_to_enc(p, NULL);
+}
+
/*
* stat() can't handle a trailing '/' or '\', remove it first.
*/