# HG changeset patch # User Bram Moolenaar # Date 1380129218 -7200 # Node ID 4dfba3df303c51fe31efd1255338e9fcbedc5401 # Parent 5409c616bfd0f7be1da231f0949b386136e5858a updated for version 7.4.039 Problem: MS-Windows: MSCV10 and earlier can't handle symlinks to a directory properly. Solution: Add stat_symlink_aware() and wstat_symlink_aware(). (Ken Takata) diff --git a/src/os_mswin.c b/src/os_mswin.c --- a/src/os_mswin.c +++ b/src/os_mswin.c @@ -498,6 +498,98 @@ slash_adjust(p) } } + static int +stat_symlink_aware(const char *name, struct stat *stp) +{ +#if defined(_MSC_VER) && _MSC_VER < 1700 + /* Work around for VC10 or earlier. stat() can't handle symlinks properly. + * VC9 or earlier: stat() doesn't support a symlink at all. It retrieves + * status of a symlink itself. + * VC10: stat() supports a symlink to a normal file, but it doesn't support + * a symlink to a directory (always returns an error). */ + WIN32_FIND_DATA findData; + HANDLE hFind, h; + DWORD attr = 0; + BOOL is_symlink = FALSE; + + hFind = FindFirstFile(name, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + attr = findData.dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) + && (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) + is_symlink = TRUE; + FindClose(hFind); + } + if (is_symlink) + { + h = CreateFile(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, n; + + fd = _open_osfhandle((intptr_t)h, _O_RDONLY); + n = _fstat(fd, (struct _stat*)stp); + _close(fd); + return n; + } + } +#endif + return stat(name, stp); +} + +#ifdef FEAT_MBYTE + static int +wstat_symlink_aware(const WCHAR *name, struct _stat *stp) +{ +# if defined(_MSC_VER) && _MSC_VER < 1700 + /* Work around for VC10 or earlier. _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). */ + 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, stp); + _close(fd); + return n; + } + } +# endif + return _wstat(name, stp); +} +#endif /* * stat() can't handle a trailing '/' or '\', remove it first. @@ -534,7 +626,7 @@ vim_stat(const char *name, struct stat * if (wp != NULL) { - n = _wstat(wp, (struct _stat *)stp); + n = wstat_symlink_aware(wp, (struct _stat *)stp); vim_free(wp); if (n >= 0) return n; @@ -544,7 +636,7 @@ vim_stat(const char *name, struct stat * } } #endif - return stat(buf, stp); + return stat_symlink_aware(buf, stp); } #if defined(FEAT_GUI_MSWIN) || defined(PROTO) diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -78,16 +78,6 @@ # endif #endif -/* - * Reparse Point - */ -#ifndef FILE_ATTRIBUTE_REPARSE_POINT -# define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 -#endif -#ifndef IO_REPARSE_TAG_SYMLINK -# define IO_REPARSE_TAG_SYMLINK 0xA000000C -#endif - /* Record all output and all keyboard & mouse input */ /* #define MCH_WRITE_DUMP */ diff --git a/src/os_win32.h b/src/os_win32.h --- a/src/os_win32.h +++ b/src/os_win32.h @@ -130,6 +130,19 @@ # define DFLT_MAXMEMTOT (5*1024) /* use up to 5 Mbyte for Vim */ #endif +/* + * Reparse Point + */ +#ifndef FILE_ATTRIBUTE_REPARSE_POINT +# define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 +#endif +#ifndef IO_REPARSE_TAG_MOUNT_POINT +# define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#endif +#ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK 0xA000000C +#endif + #if defined(_MSC_VER) || defined(__BORLANDC__) /* Support for __try / __except. All versions of MSVC and Borland C are * expected to have this. Any other compilers that support it? */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 39, +/**/ 38, /**/ 37,