Commit de83c4d4 authored by Ben Boeckel's avatar Ben Boeckel Committed by Brad King
Browse files

SystemTools: support deleting junction points

Junction points can exist in directories which may be manipulated within
CMake. The problem is that junction points are currently completely
unsupported within CMake. Since junctions need special API calls to
manipulate, DeleteFileW does not work and instead DeviceIoControl must
be used. Token privilege API calls must also be used to be able to
actually open the junction points.

Change-Id: I3e4ff3b7a600146ac3574d2b17890eef07d06496
parent 3e1b7395
......@@ -35,6 +35,7 @@
#include <fstream>
#include <sstream>
#include <set>
#include <vector>
// Work-around CMake dependency scanning limitation. This must
// duplicate the above list of headers.
......@@ -88,6 +89,7 @@
// Windows API.
#if defined(_WIN32)
# include <windows.h>
# include <winioctl.h>
# ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
# endif
......@@ -2755,6 +2757,106 @@ std::string SystemTools::GetLastSystemError()
return strerror(e);
}
#ifdef _WIN32
static bool IsJunction(const std::wstring& source)
{
#ifdef FSCTL_GET_REPARSE_POINT
const DWORD JUNCTION_ATTRS = FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_REPARSE_POINT;
DWORD attrs = GetFileAttributesW(source.c_str());
if (attrs == INVALID_FILE_ATTRIBUTES)
{
return false;
}
if ((attrs & JUNCTION_ATTRS) != JUNCTION_ATTRS)
{
return false;
}
// Adjust privileges so that we can succefully open junction points.
HANDLE token;
TOKEN_PRIVILEGES privs;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token);
LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &privs.Privileges[0].Luid);
privs.PrivilegeCount = 1;
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &privs, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
CloseHandle(token);
HANDLE dir = CreateFileW(source.c_str(), GENERIC_READ,
0, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dir == INVALID_HANDLE_VALUE)
{
return false;
}
// Query whether this is a reparse point or not.
BYTE buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_GUID_DATA_BUFFER *reparse_buffer =
(REPARSE_GUID_DATA_BUFFER*) buffer;
DWORD sentinel;
BOOL success = DeviceIoControl(
dir, FSCTL_GET_REPARSE_POINT,
NULL, 0,
reparse_buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
&sentinel, NULL);
CloseHandle(dir);
return (success && (reparse_buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT));
#else
return false;
#endif
}
static bool DeleteJunction(const std::wstring& source)
{
#ifdef FSCTL_DELETE_REPARSE_POINT
// Adjust privileges so that we can succefully open junction points as
// read/write.
HANDLE token;
TOKEN_PRIVILEGES privs;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token);
LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &privs.Privileges[0].Luid);
privs.PrivilegeCount = 1;
privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(token, FALSE, &privs, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
CloseHandle(token);
HANDLE dir = CreateFileW(source.c_str(), GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dir == INVALID_HANDLE_VALUE)
{
return false;
}
// Set up the structure so that we can delete the junction.
std::vector<BYTE> buffer(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 0);
REPARSE_GUID_DATA_BUFFER *reparse_buffer =
(REPARSE_GUID_DATA_BUFFER*) &buffer[0];
DWORD sentinel;
reparse_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
BOOL success = DeviceIoControl(
dir, FSCTL_DELETE_REPARSE_POINT,
reparse_buffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
NULL, 0,
&sentinel, NULL);
CloseHandle(dir);
return !!success;
#else
return false;
#endif
}
#endif
bool SystemTools::RemoveFile(const std::string& source)
{
#ifdef _WIN32
......@@ -2782,6 +2884,10 @@ bool SystemTools::RemoveFile(const std::string& source)
SetLastError(err);
return false;
}
if (IsJunction(ws) && !DeleteJunction(ws))
{
return false;
}
if (DeleteFileW(ws.c_str()) ||
GetLastError() == ERROR_FILE_NOT_FOUND ||
GetLastError() == ERROR_PATH_NOT_FOUND)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment