1
0
forked from aniani/vim

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)
This commit is contained in:
LemonBoy
2022-05-05 20:18:16 +01:00
committed by Bram Moolenaar
parent 365d8f76b5
commit 40fd7e6652
6 changed files with 158 additions and 4 deletions

View File

@@ -439,6 +439,27 @@ slash_adjust(char_u *p)
#define _wstat _wstat64
#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)
{
@@ -491,6 +512,61 @@ wstat_symlink_aware(const WCHAR *name, stat_T *stp)
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.
*/

View File

@@ -2127,13 +2127,27 @@ theend:
static int
executable_file(char *name, char_u **path)
{
if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
int attrs = win32_getattrs((char_u *)name);
// The file doesn't exist or is a folder.
if (attrs == -1 || (attrs & FILE_ATTRIBUTE_DIRECTORY))
return FALSE;
// Check if the file is an AppExecLink, a special alias used by Windows
// Store for its apps.
if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)
{
char_u *res = resolve_appexeclink((char_u *)name);
if (res == NULL)
return FALSE;
// The path is already absolute.
if (path != NULL)
*path = res;
else
vim_free(res);
}
else if (path != NULL)
*path = FullName_save((char_u *)name, FALSE);
return TRUE;
}
return FALSE;
}
/*

View File

@@ -126,6 +126,45 @@
#ifndef IO_REPARSE_TAG_SYMLINK
# define IO_REPARSE_TAG_SYMLINK 0xA000000C
#endif
#ifndef IO_REPARSE_TAG_APPEXECLINK
# define IO_REPARSE_TAG_APPEXECLINK 0x8000001B
#endif
/*
* Definition of the reparse point buffer.
* This is usually defined in the DDK, copy the definition here to avoid
* adding it as a dependence only for a single structure.
*/
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
struct
{
ULONG StringCount;
WCHAR StringList[1];
} AppExecLinkReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#ifdef _MSC_VER
// Support for __try / __except. All versions of MSVC are

View File

@@ -50,4 +50,5 @@ char *charset_id2name(int id);
char *quality_id2name(DWORD id);
int get_logfont(LOGFONTW *lf, char_u *name, HDC printer_dc, int verbose);
void channel_init_winsock(void);
char_u *resolve_appexeclink(char_u *fname);
/* vim: set ft=c : */

View File

@@ -1398,6 +1398,28 @@ func Test_Executable()
endif
endfunc
func Test_executable_windows_store_apps()
CheckMSWindows
" Windows Store apps install some 'decoy' .exe that require some careful
" handling as they behave similarly to symlinks.
let app_dir = expand("$LOCALAPPDATA\\Microsoft\\WindowsApps")
if !isdirectory(app_dir)
return
endif
let save_path = $PATH
let $PATH = app_dir
" Ensure executable() finds all the app .exes
for entry in readdir(app_dir)
if entry =~ '\.exe$'
call assert_true(executable(entry))
endif
endfor
let $PATH = save_path
endfunc
func Test_executable_longname()
CheckMSWindows

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4875,
/**/
4874,
/**/