// Copyright 2015 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http ://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "stdafx.h" #include "ReparsePoint.h" #include "ScopedHandle.h" #include "typed_buffer.h" #include #include // Taken from ntifs.h #define SYMLINK_FLAG_RELATIVE 1 typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; } DUMMYUNIONNAME; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #define REPARSE_DATA_BUFFER_HEADER_LENGTH FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) #define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) // winnt #define IO_REPARSE_TAG_HSM (0xC0000004L) // winnt #define IO_REPARSE_TAG_DRIVE_EXTENDER (0x80000005L) #define IO_REPARSE_TAG_HSM2 (0x80000006L) // winnt #define IO_REPARSE_TAG_SIS (0x80000007L) // winnt #define IO_REPARSE_TAG_WIM (0x80000008L) // winnt #define IO_REPARSE_TAG_CSV (0x80000009L) // winnt #define IO_REPARSE_TAG_DFS (0x8000000AL) // winnt #define IO_REPARSE_TAG_FILTER_MANAGER (0x8000000BL) #define IO_REPARSE_TAG_SYMLINK (0xA000000CL) // winnt #define IO_REPARSE_TAG_IIS_CACHE (0xA0000010L) #define IO_REPARSE_TAG_DFSR (0x80000012L) // winnt #define IO_REPARSE_TAG_DEDUP (0x80000013L) // winnt #define IO_REPARSE_TAG_APPXSTRM (0xC0000014L) #define IO_REPARSE_TAG_NFS (0x80000014L) // winnt #define IO_REPARSE_TAG_FILE_PLACEHOLDER (0x80000015L) // winnt #define IO_REPARSE_TAG_DFM (0x80000016L) #define IO_REPARSE_TAG_WOF (0x80000017L) // winnt static int g_last_error = 0; int ReparsePoint::GetLastError() { return g_last_error; } ScopedHandle OpenReparsePoint(const std::wstring& path, bool writable) { HANDLE h = CreateFile(path.c_str(), GENERIC_READ | (writable ? GENERIC_WRITE : 0), 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0); if (h == INVALID_HANDLE_VALUE) { g_last_error = GetLastError(); char buffer[50]; sprintf_s(buffer, "%d", g_last_error); MessageBoxA(NULL, buffer, "OpenReparsePoint() Failure", MB_OK); } return ScopedHandle(h, false); } static bool SetReparsePoint(const ScopedHandle& handle, typed_buffer_ptr& reparse_buffer) { DWORD cb; if (!handle.IsValid()) { return false; } bool ret = DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, reparse_buffer, reparse_buffer.size(), nullptr, 0, &cb, nullptr) == TRUE; if (!ret) { g_last_error = GetLastError(); } return ret; } static bool DeleteReparsePoint(const ScopedHandle& handle, PREPARSE_GUID_DATA_BUFFER reparse_buffer) { DWORD cb; if (!handle.IsValid()) { return false; } bool ret = DeviceIoControl(handle, FSCTL_DELETE_REPARSE_POINT, reparse_buffer, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, nullptr, 0, &cb, 0) == TRUE; if (!ret) { g_last_error = GetLastError(); char buffer[50]; sprintf_s(buffer, "%d", g_last_error); MessageBoxA(NULL, buffer, "DeleteReparsePoint() Failure", MB_OK); } return ret; } typed_buffer_ptr BuildMountPoint(const std::wstring& target, const std::wstring& printname) { const size_t target_byte_size = target.size() * 2; const size_t printname_byte_size = printname.size() * 2; const size_t path_buffer_size = target_byte_size + printname_byte_size + 8 + 4; const size_t total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH; typed_buffer_ptr buffer(total_size); buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; buffer->ReparseDataLength = static_cast(path_buffer_size); buffer->Reserved = 0; buffer->MountPointReparseBuffer.SubstituteNameOffset = 0; buffer->MountPointReparseBuffer.SubstituteNameLength = static_cast(target_byte_size); memcpy(buffer->MountPointReparseBuffer.PathBuffer, target.c_str(), target_byte_size + 2); buffer->MountPointReparseBuffer.PrintNameOffset = static_cast(target_byte_size + 2); buffer->MountPointReparseBuffer.PrintNameLength = static_cast(printname_byte_size); memcpy(buffer->MountPointReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printname_byte_size + 2); return buffer; } typed_buffer_ptr BuildSymlink(const std::wstring& target, const std::wstring& printname, bool relative) { const size_t target_byte_size = target.size() * 2; const size_t printname_byte_size = printname.size() * 2; const size_t path_buffer_size = target_byte_size + printname_byte_size + 12 + 4; const size_t total_size = path_buffer_size + REPARSE_DATA_BUFFER_HEADER_LENGTH; typed_buffer_ptr buffer(total_size); buffer->ReparseTag = IO_REPARSE_TAG_SYMLINK; buffer->ReparseDataLength = static_cast(path_buffer_size); buffer->Reserved = 0; buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0; buffer->SymbolicLinkReparseBuffer.SubstituteNameLength = static_cast(target_byte_size); memcpy(buffer->SymbolicLinkReparseBuffer.PathBuffer, target.c_str(), target_byte_size + 2); buffer->SymbolicLinkReparseBuffer.PrintNameOffset = static_cast(target_byte_size + 2); buffer->SymbolicLinkReparseBuffer.PrintNameLength = static_cast(printname_byte_size); memcpy(buffer->SymbolicLinkReparseBuffer.PathBuffer + target.size() + 1, printname.c_str(), printname_byte_size + 2); buffer->SymbolicLinkReparseBuffer.Flags = relative ? SYMLINK_FLAG_RELATIVE : 0; return buffer; } static bool CreateMountPointInternal(const std::wstring& path, typed_buffer_ptr& buffer) { ScopedHandle handle = OpenReparsePoint(path, true); if (!handle.IsValid()) { return false; } return SetReparsePoint(handle, buffer); } static bool CreateMountPointInternal(const ScopedHandle& handle, typed_buffer_ptr& buffer) { return SetReparsePoint(handle, buffer); } std::wstring FixupPath(std::wstring str) { if (str[0] != '\\') { return L"\\??\\" + str; } return str; } bool ReparsePoint::CreateMountPoint(const std::wstring& path, const std::wstring& target, const std::wstring& printname) { if (target.length() == 0) { return false; } return CreateMountPointInternal(path, BuildMountPoint(FixupPath(target), printname)); } bool ReparsePoint::CreateSymlink(const std::wstring& path, const std::wstring& target, const std::wstring& printname, bool relative) { if (target.length() == 0) { return false; } return CreateMountPointInternal(path, BuildSymlink(!relative ? FixupPath(target) : target, printname, relative)); } bool ReparsePoint::CreateSymlink(HANDLE h, const std::wstring& target, const std::wstring& printname, bool relative) { ScopedHandle handle(h, true); if (!handle.IsValid()) { return false; } return CreateMountPointInternal(handle, BuildSymlink(!relative ? FixupPath(target) : target, printname, relative)); } bool ReparsePoint::DeleteMountPoint(const std::wstring& path) { REPARSE_GUID_DATA_BUFFER reparse_buffer = { 0 }; reparse_buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; ScopedHandle handle = OpenReparsePoint(path, true); return DeleteReparsePoint(handle, &reparse_buffer); } bool ReparsePoint::CreateRawMountPoint(const std::wstring& path, DWORD reparse_tag, const std::vector& buffer) { typed_buffer_ptr reparse_buffer(8 + buffer.size()); reparse_buffer->ReparseTag = reparse_tag; reparse_buffer->ReparseDataLength = static_cast(buffer.size()); reparse_buffer->Reserved = 0; memcpy(reparse_buffer->GenericReparseBuffer.DataBuffer, &buffer[0], buffer.size()); return CreateMountPointInternal(path, reparse_buffer); } static typed_buffer_ptr GetReparsePointData(ScopedHandle handle) { typed_buffer_ptr buf(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); DWORD dwBytesReturned; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID)buf, buf.size(), &dwBytesReturned, 0) ) { g_last_error = GetLastError(); buf.reset(0); } return buf; } std::wstring ReparsePoint::GetMountPointTarget(const std::wstring& path) { ScopedHandle handle = OpenReparsePoint(path, false); if (!handle.IsValid()) { return L""; } typed_buffer_ptr buf = GetReparsePointData(handle); if (buf.size() == 0) { return L""; } if (buf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { g_last_error = ERROR_REPARSE_TAG_MISMATCH; return L""; } WCHAR* base = &buf->MountPointReparseBuffer.PathBuffer[buf->MountPointReparseBuffer.SubstituteNameOffset / 2]; return std::wstring(base, base + (buf->MountPointReparseBuffer.SubstituteNameLength / 2)); } bool ReparsePoint::IsReparsePoint(const std::wstring& path) { ScopedHandle handle = OpenReparsePoint(path, false); BY_HANDLE_FILE_INFORMATION file_info = { 0 }; return handle.IsValid() && GetFileInformationByHandle(handle, &file_info) && file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT; } static bool ReadReparsePoint(const std::wstring& path, typed_buffer_ptr& reparse_buffer) { ScopedHandle handle = OpenReparsePoint(path, false); reparse_buffer.reset(4096); DWORD dwSize; bool ret = DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, reparse_buffer, reparse_buffer.size(), &dwSize, nullptr) == TRUE; if (!ret) { g_last_error = GetLastError(); return false; } else { reparse_buffer.resize(dwSize); return true; } } static bool IsReparseTag(const std::wstring& path, DWORD reparse_tag) { typed_buffer_ptr buffer; if (ReadReparsePoint(path, buffer)) { return buffer->ReparseTag == reparse_tag; } else { return false; } } bool ReparsePoint::IsMountPoint(const std::wstring& path) { return IsReparseTag(path, IO_REPARSE_TAG_MOUNT_POINT); } bool ReparsePoint::IsSymlink(const std::wstring& path) { return IsReparseTag(path, IO_REPARSE_TAG_SYMLINK); } bool ReparsePoint::ReadMountPoint(const std::wstring& path, std::wstring& target, std::wstring& printname) { typed_buffer_ptr buffer; if (ReadReparsePoint(path, buffer) && buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { WCHAR* target_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.SubstituteNameOffset / 2]; WCHAR* display_name = &buffer->MountPointReparseBuffer.PathBuffer[buffer->MountPointReparseBuffer.PrintNameOffset / 2]; target.assign(target_name, target_name + buffer->MountPointReparseBuffer.SubstituteNameLength / 2); printname.assign(display_name, display_name + buffer->MountPointReparseBuffer.PrintNameLength / 2); return true; } else { return false; } } bool ReparsePoint::ReadSymlink(const std::wstring& path, std::wstring& target, std::wstring& printname, unsigned int* flags) { typed_buffer_ptr buffer; if (ReadReparsePoint(path, buffer) && buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) { WCHAR* target_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset / 2]; WCHAR* display_name = &buffer->SymbolicLinkReparseBuffer.PathBuffer[buffer->SymbolicLinkReparseBuffer.PrintNameOffset / 2]; target.assign(target_name, target_name + buffer->SymbolicLinkReparseBuffer.SubstituteNameLength / 2); printname.assign(display_name, display_name + buffer->SymbolicLinkReparseBuffer.PrintNameLength / 2); *flags = buffer->SymbolicLinkReparseBuffer.Flags; return true; } else { return false; } } bool ReparsePoint::ReadRaw(const std::wstring& path, unsigned int* reparse_tag, std::vector& raw_data) { typed_buffer_ptr buffer; if (ReadReparsePoint(path, buffer)) { *reparse_tag = buffer->ReparseTag; raw_data.resize(buffer->ReparseDataLength); memcpy(&raw_data[0], buffer->GenericReparseBuffer.DataBuffer, buffer->ReparseDataLength); return true; } else { return false; } return false; }