mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 9.1.0499: MS-Windows: doesn't handle symlinks properly
Problem: MS-Windows: doesn't handle symlinks properly (Timothy Madden) Solution: Implement lstat() on MS-Windows (author) lstat() differs from stat() in how it handles symbolic links, the former doesn't resolve the symlink while the latter does so. Implement a simple yet effective fallback using Win32 APIs. fixes #14933 closes: #15014 Co-authored-by: K.Takata <kentkt@csc.jp> Signed-off-by: LemonBoy <thatlemon@gmail.com> Signed-off-by: K.Takata <kentkt@csc.jp> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
a821b609f9
commit
23c5ebeb95
@@ -3645,11 +3645,15 @@ dos_expandpath(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
stat_T sb;
|
||||||
|
|
||||||
// no more wildcards, check if there is a match
|
// no more wildcards, check if there is a match
|
||||||
// remove backslashes for the remaining components only
|
// remove backslashes for the remaining components only
|
||||||
if (*path_end != 0)
|
if (*path_end != 0)
|
||||||
backslash_halve(buf + len + 1);
|
backslash_halve(buf + len + 1);
|
||||||
if (mch_getperm(buf) >= 0) // add existing file
|
// add existing file
|
||||||
|
if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
|
||||||
|
: mch_getperm(buf) >= 0)
|
||||||
addfile(gap, buf, flags);
|
addfile(gap, buf, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -194,7 +194,11 @@
|
|||||||
#ifdef HAVE_LSTAT
|
#ifdef HAVE_LSTAT
|
||||||
# define mch_lstat(n, p) lstat((n), (p))
|
# define mch_lstat(n, p) lstat((n), (p))
|
||||||
#else
|
#else
|
||||||
# define mch_lstat(n, p) mch_stat((n), (p))
|
# ifdef MSWIN
|
||||||
|
# define mch_lstat(n, p) vim_lstat((n), (p))
|
||||||
|
# else
|
||||||
|
# define mch_lstat(n, p) mch_stat((n), (p))
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef VMS
|
#ifdef VMS
|
||||||
|
145
src/os_mswin.c
145
src/os_mswin.c
@@ -430,16 +430,6 @@ slash_adjust(char_u *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use 64-bit stat functions.
|
|
||||||
#undef stat
|
|
||||||
#undef _stat
|
|
||||||
#undef _wstat
|
|
||||||
#undef _fstat
|
|
||||||
#define stat _stat64
|
|
||||||
#define _stat _stat64
|
|
||||||
#define _wstat _wstat64
|
|
||||||
#define _fstat _fstat64
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_reparse_point(const WCHAR *name, char_u *buf, DWORD *buf_len)
|
read_reparse_point(const WCHAR *name, char_u *buf, DWORD *buf_len)
|
||||||
{
|
{
|
||||||
@@ -461,58 +451,6 @@ read_reparse_point(const WCHAR *name, char_u *buf, DWORD *buf_len)
|
|||||||
return ok ? OK : FAIL;
|
return ok ? OK : FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
wstat_symlink_aware(const WCHAR *name, stat_T *stp)
|
|
||||||
{
|
|
||||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__MINGW32__)
|
|
||||||
// Work around for VC12 or earlier (and MinGW). _wstat() can't handle
|
|
||||||
// symlinks properly.
|
|
||||||
// VC9 or earlier: _wstat() doesn't support a symlink at all. It retrieves
|
|
||||||
// status of a symlink itself.
|
|
||||||
// VC10: _wstat() supports a symlink to a normal file, but it doesn't
|
|
||||||
// support a symlink to a directory (always returns an error).
|
|
||||||
// VC11 and VC12: _wstat() doesn't return an error for a symlink to a
|
|
||||||
// directory, but it doesn't set S_IFDIR flag.
|
|
||||||
// MinGW: Same as VC9.
|
|
||||||
int n;
|
|
||||||
BOOL is_symlink = FALSE;
|
|
||||||
HANDLE hFind, h;
|
|
||||||
DWORD attr = 0;
|
|
||||||
WIN32_FIND_DATAW findDataW;
|
|
||||||
|
|
||||||
hFind = FindFirstFileW(name, &findDataW);
|
|
||||||
if (hFind != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
attr = findDataW.dwFileAttributes;
|
|
||||||
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
|
|
||||||
&& (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
|
|
||||||
is_symlink = TRUE;
|
|
||||||
FindClose(hFind);
|
|
||||||
}
|
|
||||||
if (is_symlink)
|
|
||||||
{
|
|
||||||
h = CreateFileW(name, FILE_READ_ATTRIBUTES,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
(attr & FILE_ATTRIBUTE_DIRECTORY)
|
|
||||||
? FILE_FLAG_BACKUP_SEMANTICS : 0,
|
|
||||||
NULL);
|
|
||||||
if (h != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
fd = _open_osfhandle((intptr_t)h, _O_RDONLY);
|
|
||||||
n = _fstat(fd, (struct _stat *)stp);
|
|
||||||
if ((n == 0) && (attr & FILE_ATTRIBUTE_DIRECTORY))
|
|
||||||
stp->st_mode = (stp->st_mode & ~S_IFREG) | S_IFDIR;
|
|
||||||
_close(fd);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return _wstat(name, (struct _stat *)stp);
|
|
||||||
}
|
|
||||||
|
|
||||||
char_u *
|
char_u *
|
||||||
resolve_appexeclink(char_u *fname)
|
resolve_appexeclink(char_u *fname)
|
||||||
{
|
{
|
||||||
@@ -568,11 +506,76 @@ resolve_appexeclink(char_u *fname)
|
|||||||
return utf16_to_enc(p, NULL);
|
return utf16_to_enc(p, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use 64-bit stat functions.
|
||||||
|
#undef stat
|
||||||
|
#undef _stat
|
||||||
|
#undef _wstat
|
||||||
|
#undef _fstat
|
||||||
|
#define stat _stat64
|
||||||
|
#define _stat _stat64
|
||||||
|
#define _wstat _wstat64
|
||||||
|
#define _fstat _fstat64
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implements lstat() and stat() that can handle symlinks properly.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mswin_stat_impl(const WCHAR *name, stat_T *stp, const int resolve)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
int fd;
|
||||||
|
BOOL is_symlink = FALSE;
|
||||||
|
HANDLE hFind, h;
|
||||||
|
DWORD attr = 0;
|
||||||
|
DWORD flag = 0;
|
||||||
|
WIN32_FIND_DATAW findDataW;
|
||||||
|
|
||||||
|
#ifdef _UCRT
|
||||||
|
if (resolve)
|
||||||
|
// Universal CRT can handle symlinks properly.
|
||||||
|
return _wstat(name, stp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
hFind = FindFirstFileW(name, &findDataW);
|
||||||
|
if (hFind != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
attr = findDataW.dwFileAttributes;
|
||||||
|
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||||
|
&& (findDataW.dwReserved0 == IO_REPARSE_TAG_SYMLINK))
|
||||||
|
is_symlink = TRUE;
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the plain old stat() whenever it's possible.
|
||||||
|
if (!is_symlink)
|
||||||
|
return _wstat(name, stp);
|
||||||
|
|
||||||
|
if (!resolve && is_symlink)
|
||||||
|
flag = FILE_FLAG_OPEN_REPARSE_POINT;
|
||||||
|
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
flag |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||||
|
|
||||||
|
h = CreateFileW(name, FILE_READ_ATTRIBUTES,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, flag,
|
||||||
|
NULL);
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fd = _open_osfhandle((intptr_t)h, _O_RDONLY);
|
||||||
|
n = _fstat(fd, (struct _stat *)stp);
|
||||||
|
if ((n == 0) && (attr & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
stp->st_mode = (stp->st_mode & ~S_IFMT) | S_IFDIR;
|
||||||
|
_close(fd);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* stat() can't handle a trailing '/' or '\', remove it first.
|
* stat() can't handle a trailing '/' or '\', remove it first.
|
||||||
|
* When 'resolve' is true behave as lstat() wrt symlinks.
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
vim_stat(const char *name, stat_T *stp)
|
stat_impl(const char *name, stat_T *stp, const int resolve)
|
||||||
{
|
{
|
||||||
// WinNT and later can use _MAX_PATH wide characters for a pathname, which
|
// WinNT and later can use _MAX_PATH wide characters for a pathname, which
|
||||||
// means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
|
// means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is
|
||||||
@@ -607,11 +610,23 @@ vim_stat(const char *name, stat_T *stp)
|
|||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
n = wstat_symlink_aware(wp, stp);
|
n = mswin_stat_impl(wp, stp, resolve);
|
||||||
vim_free(wp);
|
vim_free(wp);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vim_lstat(const char *name, stat_T *stp)
|
||||||
|
{
|
||||||
|
return stat_impl(name, stp, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vim_stat(const char *name, stat_T *stp)
|
||||||
|
{
|
||||||
|
return stat_impl(name, stp, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
#if (defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)) || defined(PROTO)
|
#if (defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)) || defined(PROTO)
|
||||||
void
|
void
|
||||||
mch_settmode(tmode_T tmode UNUSED)
|
mch_settmode(tmode_T tmode UNUSED)
|
||||||
|
@@ -11,6 +11,7 @@ int mch_isFullName(char_u *fname);
|
|||||||
void slash_adjust(char_u *p);
|
void slash_adjust(char_u *p);
|
||||||
char_u *resolve_appexeclink(char_u *fname);
|
char_u *resolve_appexeclink(char_u *fname);
|
||||||
int vim_stat(const char *name, stat_T *stp);
|
int vim_stat(const char *name, stat_T *stp);
|
||||||
|
int vim_lstat(const char *name, stat_T *stp);
|
||||||
void mch_settmode(tmode_T tmode);
|
void mch_settmode(tmode_T tmode);
|
||||||
int mch_get_shellsize(void);
|
int mch_get_shellsize(void);
|
||||||
void mch_set_shellsize(void);
|
void mch_set_shellsize(void);
|
||||||
|
@@ -3822,6 +3822,29 @@ func Test_glob2()
|
|||||||
endif
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_glob_symlinks()
|
||||||
|
call writefile([], 'Xglob1')
|
||||||
|
|
||||||
|
if has("win32")
|
||||||
|
silent !mklink XglobBad DoesNotExist
|
||||||
|
if v:shell_error
|
||||||
|
throw 'Skipped: cannot create symlinks'
|
||||||
|
endif
|
||||||
|
silent !mklink XglobOk Xglob1
|
||||||
|
else
|
||||||
|
silent !ln -s DoesNotExist XglobBad
|
||||||
|
silent !ln -s Xglob1 XglobOk
|
||||||
|
endif
|
||||||
|
|
||||||
|
" The broken symlink is excluded when alllinks is false.
|
||||||
|
call assert_equal(['Xglob1', 'XglobBad', 'XglobOk'], sort(glob('Xglob*', 0, 1, 1)))
|
||||||
|
call assert_equal(['Xglob1', 'XglobOk'], sort(glob('Xglob*', 0, 1, 0)))
|
||||||
|
|
||||||
|
call delete('Xglob1')
|
||||||
|
call delete('XglobBad')
|
||||||
|
call delete('XglobOk')
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Test for browse()
|
" Test for browse()
|
||||||
func Test_browse()
|
func Test_browse()
|
||||||
CheckFeature browse
|
CheckFeature browse
|
||||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
499,
|
||||||
/**/
|
/**/
|
||||||
498,
|
498,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user