diff --git a/SystemTools.cxx b/SystemTools.cxx index c635b6b2b72863497c3d254faf9d9568532559b3..cbdfe11759c563905b0481432ceb9322e19feee2 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -3005,10 +3005,36 @@ bool SystemTools::FileIsDirectory(const std::string& inName) bool SystemTools::FileIsSymlink(const std::string& name) { #if defined(_WIN32) - DWORD attr = - GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); + std::wstring path = Encoding::ToWindowsExtendedPath(name); + DWORD attr = GetFileAttributesW(path.c_str()); if (attr != INVALID_FILE_ATTRIBUTES) { - return (attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + // FILE_ATTRIBUTE_REPARSE_POINT means: + // * a file or directory that has an associated reparse point, or + // * a file that is a symbolic link. + HANDLE hFile = CreateFileW( + path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + DWORD bytesReturned = 0; + if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, + MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, + NULL)) { + CloseHandle(hFile); + // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be + // a symbolic link if it is not a reparse point. + return GetLastError() == ERROR_NOT_A_REPARSE_POINT; + } + CloseHandle(hFile); + ULONG reparseTag = + reinterpret_cast<PREPARSE_GUID_DATA_BUFFER>(&buffer[0])->ReparseTag; + return (reparseTag == IO_REPARSE_TAG_SYMLINK) || + (reparseTag == IO_REPARSE_TAG_MOUNT_POINT); + } + return false; } else { return false; }