Patch 8.2.4875
Problem:    MS-Windows: some .exe files are not recognized.
Solution:   Parse APPEXECLINK junctions. (closes #10302)
Files:      src/os_mswin.c, src/proto/os_mswin.pro, src/os_win32.c,
            src/os_win32.h, src/testdir/test_functions.vim


*** ../vim-8.2.4874/src/os_mswin.c      2022-01-24 11:23:59.859900461 +0000
--- src/os_mswin.c      2022-05-05 20:15:19.984347430 +0100
***************
*** 440,445 ****
--- 440,466 ----
  #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,496 ****
--- 512,572 ----
      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.
   */
*** ../vim-8.2.4874/src/proto/os_mswin.pro      2020-05-17 13:06:07.313201564 
+0100
--- src/proto/os_mswin.pro      2022-05-05 20:15:19.984347430 +0100
***************
*** 50,53 ****
--- 50,54 ----
  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 : */
*** ../vim-8.2.4874/src/os_win32.c      2022-05-03 11:01:59.058826963 +0100
--- src/os_win32.c      2022-05-05 20:15:19.984347430 +0100
***************
*** 2127,2139 ****
      static int
  executable_file(char *name, char_u **path)
  {
!     if (mch_getperm((char_u *)name) != -1 && !mch_isdir((char_u *)name))
      {
        if (path != NULL)
!           *path = FullName_save((char_u *)name, FALSE);
!       return TRUE;
      }
!     return FALSE;
  }
  
  /*
--- 2127,2153 ----
      static int
  executable_file(char *name, char_u **path)
  {
!     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;
  }
  
  /*
*** ../vim-8.2.4874/src/os_win32.h      2022-02-04 10:45:34.944240854 +0000
--- src/os_win32.h      2022-05-05 20:15:19.984347430 +0100
***************
*** 126,131 ****
--- 126,170 ----
  #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
*** ../vim-8.2.4874/src/testdir/test_functions.vim      2022-05-03 
11:01:59.058826963 +0100
--- src/testdir/test_functions.vim      2022-05-05 20:15:19.984347430 +0100
***************
*** 1398,1403 ****
--- 1398,1425 ----
    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
  
*** ../vim-8.2.4874/src/version.c       2022-05-05 19:23:03.123905358 +0100
--- src/version.c       2022-05-05 20:17:13.140209924 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4875,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
104. When people ask about the Presidential Election you ask "Which country?"

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220505191905.AE0F01C0854%40moolenaar.net.

Raspunde prin e-mail lui