summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SystemTools.cxx96
1 files changed, 86 insertions, 10 deletions
diff --git a/SystemTools.cxx b/SystemTools.cxx
index ac45b14..6a8520fe 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -34,6 +34,10 @@
#include <utility>
#include <vector>
+#ifdef _WIN32
+# include <cwchar>
+#endif
+
// Work-around CMake dependency scanning limitation. This must
// duplicate the above list of headers.
#if 0
@@ -103,6 +107,9 @@
# if defined(_MSC_VER) && _MSC_VER >= 1800
# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
# endif
+# ifndef IO_REPARSE_TAG_APPEXECLINK
+# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL)
+# endif
// from ntifs.h, which can only be used by drivers
typedef struct _REPARSE_DATA_BUFFER
{
@@ -132,8 +139,46 @@ typedef struct _REPARSE_DATA_BUFFER
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
+ struct
+ {
+ ULONG Version;
+ WCHAR StringList[1];
+ // In version 3, there are 4 NUL-terminated strings:
+ // * Package ID
+ // * Entry Point
+ // * Executable Path
+ // * Application Type
+ } AppExecLinkReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+namespace {
+WCHAR* GetAppExecLink(PREPARSE_DATA_BUFFER data, size_t& len)
+{
+ // We only know the layout of version 3.
+ if (data->AppExecLinkReparseBuffer.Version != 3) {
+ return nullptr;
+ }
+
+ WCHAR* pstr = data->AppExecLinkReparseBuffer.StringList;
+
+ // Skip the package id and entry point strings.
+ for (int i = 0; i < 2; ++i) {
+ len = std::wcslen(pstr);
+ if (len == 0) {
+ return nullptr;
+ }
+ pstr += len + 1;
+ }
+
+ // The third string is the executable path.
+ len = std::wcslen(pstr);
+ if (len == 0) {
+ return nullptr;
+ }
+ return pstr;
+}
+}
#endif
#if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H
@@ -1343,8 +1388,8 @@ bool SystemTools::FileExists(const std::string& filename)
return false;
}
#if defined(_WIN32)
- DWORD attr =
- GetFileAttributesW(Encoding::ToWindowsExtendedPath(filename).c_str());
+ const std::wstring path = Encoding::ToWindowsExtendedPath(filename);
+ DWORD attr = GetFileAttributesW(path.c_str());
if (attr == INVALID_FILE_ATTRIBUTES) {
return false;
}
@@ -1352,12 +1397,38 @@ bool SystemTools::FileExists(const std::string& filename)
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
// Using 0 instead of GENERIC_READ as it allows reading of file attributes
// even if we do not have permission to read the file itself
- HANDLE handle =
- CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(), 0, 0,
- nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ HANDLE handle = CreateFileW(path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
- return false;
+ // A reparse point may be an execution alias (Windows Store app), which
+ // is similar to a symlink but it cannot be opened as a regular file.
+ // We must look at the reparse point data explicitly.
+ handle = CreateFileW(
+ path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ DWORD bytesReturned = 0;
+
+ if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
+ nullptr)) {
+ CloseHandle(handle);
+ return false;
+ }
+
+ CloseHandle(handle);
+
+ PREPARSE_DATA_BUFFER data =
+ reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
+
+ // Assume that file exists if it is an execution alias.
+ return data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK;
}
CloseHandle(handle);
@@ -3011,11 +3082,7 @@ bool SystemTools::FileIsDirectory(const std::string& inName)
bool SystemTools::FileIsExecutable(const std::string& name)
{
-#if defined(_WIN32)
- return SystemTools::FileExists(name, true);
-#else
return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE);
-#endif
}
bool SystemTools::FileIsSymlink(const std::string& name)
@@ -3164,6 +3231,15 @@ Status SystemTools::ReadSymlink(std::string const& newName,
data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
substituteNameData = data->MountPointReparseBuffer.PathBuffer +
data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
+ } else if (data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
+ // The reparse buffer is a list of 0-terminated non-empty strings,
+ // terminated by an empty string (0-0). We need the third string.
+ size_t destLen;
+ substituteNameData = GetAppExecLink(data, destLen);
+ if (substituteNameData == nullptr || destLen == 0) {
+ return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
+ }
+ substituteNameLength = static_cast<USHORT>(destLen);
} else {
return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
}