Files
2026-04-29 17:10:47 +02:00

1584 lines
59 KiB
C++

/*****************************************************************************/
/* Page10Links.cpp Copyright (c) Ladislav Zezula 2009 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 19.10.09 1.00 Lad The first version of Page10Links.cpp */
/*****************************************************************************/
#include "FileTest.h"
#include "ReparseDataHsm.h"
#include "resource.h"
//-----------------------------------------------------------------------------
// Local defines
#define MAX_TREE_ITEM_LENGTH 0x400
#define REPARSE_HEADER_LENGTH 0x08 // sizeof(REPARSE_DATA_BUFFER::ReparseTag, ReparseDataLength, Reserved)
#define ITEM_TYPE_REPARSE_TAG 0x0001
#define ITEM_TYPE_SUBSTNAME_MP 0x0002
#define ITEM_TYPE_PRINTNAME_MP 0x0003
#define ITEM_TYPE_SUBSTNAME_LNK 0x0004
#define ITEM_TYPE_PRINTNAME_LNK 0x0005
static TFlagInfo ReparseTags[] =
{
FLAGINFO_NUMV(IO_REPARSE_TAG_MOUNT_POINT),
FLAGINFO_NUMV(IO_REPARSE_TAG_HSM),
FLAGINFO_NUMV(IO_REPARSE_TAG_DRIVE_EXTENDER),
FLAGINFO_NUMV(IO_REPARSE_TAG_HSM2),
FLAGINFO_NUMV(IO_REPARSE_TAG_SIS),
FLAGINFO_NUMV(IO_REPARSE_TAG_WIM),
FLAGINFO_NUMV(IO_REPARSE_TAG_CSV),
FLAGINFO_NUMV(IO_REPARSE_TAG_DFS),
FLAGINFO_NUMV(IO_REPARSE_TAG_FILTER_MANAGER),
FLAGINFO_NUMV(IO_REPARSE_TAG_SYMLINK),
FLAGINFO_NUMV(IO_REPARSE_TAG_IIS_CACHE),
FLAGINFO_NUMV(IO_REPARSE_TAG_DFSR),
FLAGINFO_NUMV(IO_REPARSE_TAG_DEDUP),
FLAGINFO_NUMV(IO_REPARSE_TAG_APPXSTRM),
FLAGINFO_NUMV(IO_REPARSE_TAG_NFS),
FLAGINFO_NUMV(IO_REPARSE_TAG_FILE_PLACEHOLDER),
FLAGINFO_NUMV(IO_REPARSE_TAG_DFM),
FLAGINFO_NUMV(IO_REPARSE_TAG_WOF),
FLAGINFO_NUMV(IO_REPARSE_TAG_WCI),
FLAGINFO_NUMV(IO_REPARSE_TAG_WCI_1),
FLAGINFO_NUMV(IO_REPARSE_TAG_GLOBAL_REPARSE),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_1),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_2),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_3),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_4),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_5),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_6),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_7),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_8),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_9),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_A),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_B),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_C),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_D),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_E),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_F),
FLAGINFO_NUMV(IO_REPARSE_TAG_CLOUD_MASK),
FLAGINFO_NUMV(IO_REPARSE_TAG_APPEXECLINK),
FLAGINFO_NUMV(IO_REPARSE_TAG_PROJFS),
FLAGINFO_NUMV(IO_REPARSE_TAG_LX_SYMLINK),
FLAGINFO_NUMV(IO_REPARSE_TAG_STORAGE_SYNC),
FLAGINFO_NUMV(IO_REPARSE_TAG_WCI_TOMBSTONE),
FLAGINFO_NUMV(IO_REPARSE_TAG_UNHANDLED),
FLAGINFO_NUMV(IO_REPARSE_TAG_ONEDRIVE),
FLAGINFO_NUMV(IO_REPARSE_TAG_PROJFS_TOMBSTONE),
FLAGINFO_NUMV(IO_REPARSE_TAG_AF_UNIX),
FLAGINFO_NUMV(IO_REPARSE_TAG_LX_FIFO),
FLAGINFO_NUMV(IO_REPARSE_TAG_LX_CHR),
FLAGINFO_NUMV(IO_REPARSE_TAG_LX_BLK),
FLAGINFO_END()
};
static LPCWSTR AppExecLinkParts[] =
{
L"AppPackageID",
L"AppUserModelID",
L"TargetPath",
};
//-----------------------------------------------------------------------------
// Support for REPARSE_DATA_BUFFER
//
// - Total data length must be REPARSE_DATA_BUFFER_HEADER_SIZE + sizeof(MountPointReparseBuffer) - sizeof(WCHAR) + filenames
// - ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE must be equal to total data length
// - There must be both NT name and DOS name. Both names will be zero-terminated
//
static ULONG GetTotalDataLength(PREPARSE_DATA_BUFFER ReparseData)
{
ULONG TotalLength = 0;
if(ReparseData != NULL)
TotalLength = FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) + ReparseData->ReparseDataLength;
return TotalLength;
}
static int UpdateReparseDataLength(PREPARSE_DATA_BUFFER ReparseData, LPTSTR szBaseBuffer)
{
size_t BufferOffset = (LPBYTE)szBaseBuffer - (LPBYTE)&ReparseData->MountPointReparseBuffer;
ReparseData->ReparseDataLength = (USHORT)(BufferOffset +
ReparseData->MountPointReparseBuffer.PrintNameOffset +
ReparseData->MountPointReparseBuffer.PrintNameLength +
sizeof(WCHAR));
return ERROR_SUCCESS;
}
static int SetReparseDataTag(PREPARSE_DATA_BUFFER ReparseData, ULONG ReparseTag, ULONG DataLength)
{
// Always clear everything before changing tag
memset(ReparseData, 0, DataLength);
ReparseData->ReparseTag = ReparseTag;
// For a few known reparse formats, set empty values
switch(ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
ReparseData->ReparseDataLength = sizeof(ReparseData->MountPointReparseBuffer);
ReparseData->MountPointReparseBuffer.PrintNameOffset = sizeof(WCHAR);
break;
case IO_REPARSE_TAG_SYMLINK:
ReparseData->ReparseDataLength = sizeof(ReparseData->SymbolicLinkReparseBuffer);
ReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = sizeof(WCHAR);
break;
case IO_REPARSE_TAG_WIM:
ReparseData->ReparseDataLength = sizeof(ReparseData->WimImageReparseBuffer);
break;
}
return ERROR_SUCCESS;
}
static int SetReparseDataSubstName(PREPARSE_DATA_BUFFER ReparseData, LPTSTR szBaseBuffer, LPCTSTR szSubstName)
{
LPTSTR szOldPrintName;
LPTSTR szNewPrintName;
size_t cchSubstName = _tcslen(szSubstName);
// Fix the print name offset and size
szOldPrintName = szBaseBuffer + ReparseData->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR);
szNewPrintName = szBaseBuffer + cchSubstName + 1;
memmove(szNewPrintName, szOldPrintName, ReparseData->MountPointReparseBuffer.PrintNameLength + sizeof(WCHAR));
ReparseData->MountPointReparseBuffer.PrintNameOffset = (USHORT)((LPBYTE)szNewPrintName - (LPBYTE)szBaseBuffer);
// Copy the subst name
memmove(szBaseBuffer, szSubstName, (cchSubstName + 1) * sizeof(WCHAR));
ReparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;
ReparseData->MountPointReparseBuffer.SubstituteNameLength = (USHORT)(cchSubstName * sizeof(WCHAR));
// Fix the reparse data size
return UpdateReparseDataLength(ReparseData, szBaseBuffer);
}
static int SetReparseDataPrintName(PREPARSE_DATA_BUFFER ReparseData, LPTSTR szBaseBuffer, LPCTSTR szPrintName)
{
size_t cbPrintName = _tcslen(szPrintName) * sizeof(WCHAR);
// Copy the print name
memmove((LPBYTE)szBaseBuffer + ReparseData->MountPointReparseBuffer.PrintNameOffset, szPrintName, cbPrintName + sizeof(WCHAR));
ReparseData->MountPointReparseBuffer.PrintNameLength = (USHORT)cbPrintName;
// Fix the reparse data size
return UpdateReparseDataLength(ReparseData, szBaseBuffer);
}
static void InitializeReparseData(TFileTestData * pData)
{
PREPARSE_DATA_BUFFER ReparseData;
if(pData->ReparseData == NULL)
{
// Allocate the data buffer for the reparse
ReparseData = (PREPARSE_DATA_BUFFER)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
// Fill-in the IO_REPARSE_TAG_MOUNT_POINT
if(ReparseData != NULL)
{
// Initialize the reparse data
ReparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
SetReparseDataSubstName(ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, L"\\??\\C:\\Windows");
SetReparseDataPrintName(ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, L"C:\\Windows");
// Set the reparse data to the dialog data
pData->ReparseData = ReparseData;
pData->ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
}
}
}
static bool ReparseTargetIsDirectory(PREPARSE_DATA_BUFFER ReparseData)
{
FILE_BASIC_INFORMATION BasicInfo;
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING FileName;
NTSTATUS Status;
LPTSTR szBaseBuffer = 0;
bool bResult = false;
// Determine the pointer to the target name
switch(ReparseData->ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
szBaseBuffer = ReparseData->MountPointReparseBuffer.PathBuffer;
break;
case IO_REPARSE_TAG_SYMLINK:
szBaseBuffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer;
break;
}
// Only for mount point/symbolic link
if(szBaseBuffer != NULL)
{
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
FileName.MaximumLength =
FileName.Length = ReparseData->MountPointReparseBuffer.SubstituteNameLength;
FileName.Buffer = szBaseBuffer + ReparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
Status = NtQueryAttributesFile(&ObjAttr, &BasicInfo);
bResult = (NT_SUCCESS(Status) && (BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY));
}
return bResult;
}
//-----------------------------------------------------------------------------
// Helper functions
static void BinaryToString(LPBYTE pbData, ULONG cbData, LPTSTR szBuffer, size_t cchBuffer)
{
LPTSTR szSaveBuffer = szBuffer;
LPTSTR szBufferEnd = szBuffer + cchBuffer;
LPBYTE pbDataEnd = pbData + cbData;
while((szBuffer + 3) <= szBufferEnd && pbData < pbDataEnd)
{
// Put space there, if any
if(szBuffer > szSaveBuffer)
*szBuffer++ = _T(' ');
// Printf the hexa digit
*szBuffer++ = HexaAlphabetLower[pbData[0] >> 0x04];
*szBuffer++ = HexaAlphabetLower[pbData[0] & 0x0F];
pbData++;
}
// Terminate the buffer with zero
szBuffer[0] = 0;
}
static LPTSTR FormatReparseTag(ULONG ReparseTag, LPTSTR szBuffer, size_t cchBuffer)
{
LPCSTR szReparseTag = "unknown";
// Try to find the reparse tag
for(size_t i = 0; i < _countof(ReparseTags); i++)
{
if(ReparseTags[i].dwValue == ReparseTag)
{
szReparseTag = ReparseTags[i].szFlagText;
break;
}
}
// Format the string
StringCchPrintf(szBuffer, cchBuffer, _T("%08X (%s)"), ReparseTag, TWideString(szReparseTag).GetStr());
return szBuffer;
}
static HTREEITEM TreeView_InsertString(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szItemText,
LPARAM lParam = 0)
{
TVINSERTSTRUCT tvi;
// Insert the item
ZeroMemory(&tvi, sizeof(TVITEM));
tvi.hParent = hParent;
tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
tvi.item.pszText = (LPTSTR)szItemText;
tvi.item.lParam = lParam;
return TreeView_InsertItem(hWndTree, &tvi);
}
static HTREEITEM TreeView_InsertNameAndValue(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szValueName,
LPCTSTR szValueText,
LPARAM lParam = 0)
{
TCHAR szItemText[MAX_TREE_ITEM_LENGTH];
// Prepare the item text
StringCchPrintf(szItemText, _countof(szItemText), _T("%s: %s"), szValueName, szValueText);
return TreeView_InsertString(hWndTree, hParent, szItemText, lParam);
}
static HTREEITEM TreeView_InsertInteger(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szValueName,
LPCTSTR szFormat,
ULONG IntValue)
{
TCHAR szValueText[0x20];
// Format the value
StringCchPrintf(szValueText, _countof(szValueText), szFormat, IntValue);
return TreeView_InsertNameAndValue(hWndTree, hParent, szValueName, szValueText);
}
static HTREEITEM TreeView_InsertGuid(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szValueName,
GUID & Guid,
LPARAM lParam = 0)
{
TCHAR szValueText[0x40];
// Format the value
GuidToString(&Guid, szValueText, _countof(szValueText));
return TreeView_InsertNameAndValue(hWndTree, hParent, szValueName, szValueText, lParam);
}
static HTREEITEM TreeView_InsertBinary(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szValueName,
PVOID pvData,
ULONG cbData,
LPARAM lParam = 0)
{
HTREEITEM hNewItem = NULL;
LPTSTR szValueText;
size_t cchLength;
// Limit the maximum length
if(cbData > (MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 8))
cbData = (MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 8);
cchLength = (cbData * 3) + 1;
// Allocate buffer for the item text
szValueText = new TCHAR[cchLength];
if(szValueText != NULL)
{
// Format the value
BinaryToString((LPBYTE)pvData, cbData, szValueText, cchLength);
// Apply to the tree item
hNewItem = TreeView_InsertNameAndValue(hWndTree, hParent, szValueName, szValueText, lParam);
delete [] szValueText;
}
return hNewItem;
}
static HTREEITEM TreeView_InsertStrOffs(
HWND hWndTree,
HTREEITEM hParent,
LPCTSTR szValueName,
LPVOID BeginOfStruct,
USHORT StringOffset,
USHORT StringLength,
LPARAM lParam = 0)
{
WCHAR szValueText[MAX_TREE_ITEM_LENGTH];
size_t cchStringLength = (StringLength / sizeof(WCHAR));
// Put the prefix
StringCchPrintf(szValueText, MAX_TREE_ITEM_LENGTH, _T("0x%02X \""), StringOffset);
StringCchCatN(szValueText, MAX_TREE_ITEM_LENGTH, (LPWSTR)((LPBYTE)BeginOfStruct + StringOffset), cchStringLength);
StringCchCat(szValueText, MAX_TREE_ITEM_LENGTH, _T("\""));
// Insert as tree item
return TreeView_InsertNameAndValue(hWndTree, hParent, szValueName, szValueText, lParam);
}
static void TreeView_InsertSubstName(
HWND hWndTree,
HTREEITEM hParent,
PREPARSE_DATA_BUFFER ReparseData,
LPTSTR szBaseBuffer,
LPARAM lParam = 0)
{
TreeView_InsertStrOffs(hWndTree, hParent, _T("SubstituteNameOffset"),
szBaseBuffer,
ReparseData->MountPointReparseBuffer.SubstituteNameOffset,
ReparseData->MountPointReparseBuffer.SubstituteNameLength,
lParam);
TreeView_InsertInteger(hWndTree, hParent, _T("SubstituteNameLength"),
_T("0x%02X"),
ReparseData->MountPointReparseBuffer.SubstituteNameLength);
}
static void TreeView_InsertPrintName(
HWND hWndTree,
HTREEITEM hParent,
PREPARSE_DATA_BUFFER ReparseData,
LPTSTR szBaseBuffer,
LPARAM lParam = 0)
{
TreeView_InsertStrOffs(hWndTree, hParent, _T("PrintNameOffset"),
szBaseBuffer,
ReparseData->MountPointReparseBuffer.PrintNameOffset,
ReparseData->MountPointReparseBuffer.PrintNameLength,
lParam);
TreeView_InsertInteger(hWndTree, hParent, _T("PrintNameLength"),
_T("0x%02X"),
ReparseData->MountPointReparseBuffer.PrintNameLength);
}
static bool TreeView_EditString(HWND hWndEdit, LPTSTR szBaseBuffer, USHORT NameOffset, USHORT NameLength)
{
LPTSTR szString;
// We need to allocate the string
szString = (LPTSTR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, NameLength + sizeof(WCHAR));
if(szString != NULL)
{
memcpy(szString, (LPBYTE)szBaseBuffer + NameOffset, NameLength);
SetWindowText(hWndEdit, szString);
HeapFree(g_hHeap, 0, szString);
return true;
}
return false;
}
LPTSTR GetFullHardLinkName(PFILE_LINK_ENTRY_INFORMATION pLinkInfo, LPTSTR szFileName)
{
PFILE_NAME_INFORMATION pNameInfo = NULL;
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatus;
UNICODE_STRING FileName;
NTSTATUS Status;
LPTSTR szHardLinkName = NULL;
LPTSTR szTemp;
HANDLE hVolume = NULL;
HANDLE hParent;
TCHAR szVolumeName[MAX_PATH];
ULONG BackSlashCount = 0;
ULONG BackSlashMax = 1;
ULONG CharIndex = 0;
ULONG NameLength;
// Extract volume name from the file name
if(szFileName[0] == _T('\\'))
BackSlashMax = 3;
while(BackSlashCount < BackSlashMax)
{
if(szFileName[CharIndex] == _T('\\'))
BackSlashCount++;
szVolumeName[CharIndex] = szFileName[CharIndex];
CharIndex++;
}
szVolumeName[CharIndex] = 0;
// Open the volume
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szVolumeName);
if(NT_SUCCESS(Status))
{
Status = NtOpenFile(&hVolume,
SYNCHRONIZE,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_ALERT);
FreeFileNameString(&FileName);
}
if(NT_SUCCESS(Status))
{
// Now open the target directory by ID
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, hVolume, NULL);
FileName.MaximumLength = sizeof(LARGE_INTEGER);
FileName.Length = sizeof(LARGE_INTEGER);
FileName.Buffer = (PWSTR)&pLinkInfo->ParentFileId;
Status = NtOpenFile(&hParent,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_BY_FILE_ID | FILE_SYNCHRONOUS_IO_ALERT);
// Now query name of the directory
if(NT_SUCCESS(Status))
{
pNameInfo = (PFILE_NAME_INFORMATION)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, 0x1000);
if(pNameInfo != NULL)
{
Status = NtQueryInformationFile(hParent,
&IoStatus,
pNameInfo,
0x1000,
FileNameInformation);
if(NT_SUCCESS(Status))
{
NameLength = (ULONG)_tcslen(szVolumeName) * sizeof(WCHAR) +
pNameInfo->FileNameLength +
(pLinkInfo->FileNameLength + 1) * sizeof(WCHAR);
szHardLinkName = szTemp = (LPTSTR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, NameLength);
if(szHardLinkName != NULL)
{
// Copy the volume name
StringCchCopy(szTemp, NameLength / sizeof(WCHAR), szVolumeName);
szTemp = _tcsrchr(szTemp, _T('\\'));
// Copy the directory name
memcpy(szTemp, pNameInfo->FileName, pNameInfo->FileNameLength);
szTemp += pNameInfo->FileNameLength / sizeof(WCHAR);
// Append backslash
if(pNameInfo->FileNameLength > 2)
*szTemp++ = _T('\\');
// Copy the link name
memcpy(szTemp, pLinkInfo->FileName, (pLinkInfo->FileNameLength * sizeof(WCHAR)));
szTemp[pLinkInfo->FileNameLength] = 0;
}
}
HeapFree(g_hHeap, 0, pNameInfo);
}
NtClose(hParent);
}
NtClose(hVolume);
}
return szHardLinkName;
}
static void TreeView_InsertHsmData(HWND hWndChild, HTREEITEM hParent, PHSM_DATA HsmData, ULONG BitmapMask)
{
ULONG BitmapBit = 1;
// Insert the base data
TreeView_InsertInteger(hWndChild, hParent, _T("Magic"), _T("0x%08X"), HsmData->Magic);
TreeView_InsertInteger(hWndChild, hParent, _T("Crc32"), _T("0x%08X"), HsmData->Crc32);
TreeView_InsertInteger(hWndChild, hParent, _T("Length"), _T("0x%08X"), HsmData->Length);
TreeView_InsertInteger(hWndChild, hParent, _T("Flags"), _T("0x%04X"), HsmData->Flags);
TreeView_InsertInteger(hWndChild, hParent, _T("NumberOfElements"), _T("0x%04X"), HsmData->NumberOfElements);
// Insert the page for elements
hParent = TreeView_InsertString(hWndChild, hParent, _T("Elements"));
// Insert each element
for (USHORT i = 0; i < HsmData->NumberOfElements; i++, BitmapBit <<= 1)
{
PHSM_ELEMENT_INFO pElementInfo = &HsmData->ElementInfos[i];
HTREEITEM hBitmap;
HTREEITEM hItem;
LPBYTE ElementData;
TCHAR szElement[0x10];
// Insert the "[x]" part
StringCchPrintf(szElement, _countof(szElement), _T("[%u]"), i);
hItem = TreeView_InsertString(hWndChild, hParent, szElement);
// Insert the element info
TreeView_InsertInteger(hWndChild, hItem, _T("Type"), _T("%04X"), pElementInfo->Type);
TreeView_InsertInteger(hWndChild, hItem, _T("Length"), _T("%04X"), pElementInfo->Length);
TreeView_InsertInteger(hWndChild, hItem, _T("Offset"), _T("%08X"), pElementInfo->Offset);
// Insert element data
if(pElementInfo->Offset && pElementInfo->Length)
{
// Retrieve the element data
ElementData = HsmGetElementData(HsmData, i);
// Can the data be a bitmap?
if(BitmapMask & BitmapBit)
{
hBitmap = TreeView_InsertString(hWndChild, hItem, _T("HSM_BITMAP"));
TreeView_InsertHsmData(hWndChild, hBitmap, (PHSM_DATA)ElementData, 0);
}
else
{
TreeView_InsertBinary(hWndChild, hItem, _T("Data"), ElementData, pElementInfo->Length);
}
}
}
}
static NTSTATUS TreeView_InsertHsm(HWND hWndChild, HTREEITEM hParent, PREPARSE_DATA_BUFFER ReparseData, ULONG ReparseDataLength)
{
PREPARSE_DATA_BUFFER HsmReparseData = NULL;
HTREEITEM hItem;
NTSTATUS Status;
// Decompress the reparse data
Status = HsmUncompressData(ReparseData, ReparseDataLength, &HsmReparseData);
if(NT_SUCCESS(Status))
{
hItem = TreeView_InsertString(hWndChild, hParent, _T("HSM_REPARSE_DATA"));
if(hItem != NULL)
{
// Insert the flags and length
TreeView_InsertInteger(hWndChild, hItem, _T("Flags"), _T("0x%04X"), HsmReparseData->HsmReparseBufferRaw.Flags);
TreeView_InsertInteger(hWndChild, hItem, _T("Length"), _T("0x%04X"), HsmReparseData->HsmReparseBufferRaw.Length);
// Insert the FileData substructure
hItem = TreeView_InsertString(hWndChild, hItem, _T("HSM_DATA"));
// Get the pointer to the HSM_DATA. If validated OK, insert the substructure
Status = HsmValidateReparseData(HsmReparseData);
if(NT_SUCCESS(Status))
{
PHSM_REPARSE_DATA HsmRpData = (PHSM_REPARSE_DATA)(&HsmReparseData->HsmReparseBufferRaw);
PHSM_DATA HsmData = &HsmRpData->FileData;
TreeView_InsertHsmData(hWndChild, hItem, HsmData, 0);
Status = STATUS_SUCCESS;
}
else
{
TreeView_InsertBinary(hWndChild, hItem, _T("RawData (invalid)"), ReparseData->HsmReparseBufferRaw.RawData, HsmReparseData->HsmReparseBufferRaw.Length);
Status = STATUS_SUCCESS;
}
}
// Free the uncompressed reparse data buffer
if(HsmReparseData != NULL && HsmReparseData != ReparseData)
{
HeapFree(g_hHeap, 0, HsmReparseData);
}
}
return Status;
}
//-----------------------------------------------------------------------------
// Message handlers
static TAnchors * pAnchors = NULL;
static int OnInitDialog(HWND hDlg, LPARAM lParam)
{
PROPSHEETPAGE * pPage = (PROPSHEETPAGE *)lParam;
TFileTestData * pData = (TFileTestData *)pPage->lParam;
LPCTSTR szReparseName;
HWND hWndChild;
// Apply the data to the dialog
SetDialogData(hDlg, pPage->lParam);
// Initialize the reparse data
InitializeReparseData(pData);
// Configure dialog resizing
if(pData->bEnableResizing)
{
pAnchors = new TAnchors(hDlg);
pAnchors->AddAnchor(hDlg, IDC_SYMLINK_FRAME, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_SYMLINK_TARGET, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_SYMLINK_QUERY, akLeft | akTop);
pAnchors->AddAnchor(hDlg, IDC_SYMLINK_CREATE, akLeftCenter | akTop);
pAnchors->AddAnchor(hDlg, IDC_SYMLINK_DELETE, akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_HARDLINK_FRAME, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_HARDLINK_LIST, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_NEW_HARDLINK, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_HARDLINK_CREATE, akLeft | akTop);
pAnchors->AddAnchor(hDlg, IDC_HARDLINK_QUERY, akLeftCenter | akTop);
pAnchors->AddAnchor(hDlg, IDC_HARDLINK_DELETE, akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_REPARSE_FRAME, akAll);
pAnchors->AddAnchor(hDlg, IDC_REPARSE, akLeft | akTop | akRight);
pAnchors->AddAnchor(hDlg, IDC_REPARSE_DATA, akAll);
pAnchors->AddAnchor(hDlg, IDC_REPARSE_CREATE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_REPARSE_QUERY, akLeftCenter | akBottom);
pAnchors->AddAnchor(hDlg, IDC_REPARSE_DELETE, akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_RESULT_FRAME, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_ERROR_CODE, akLeft | akRight | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION_TITLE, akLeft | akBottom);
pAnchors->AddAnchor(hDlg, IDC_INFORMATION, akLeft | akRight | akBottom);
}
// We need this in order to create symbolic link
EnablePrivilege(_T("SeCreateSymbolicLinkPrivilege"));
// Congigure the right-arrow image
AttachIconToEdit(hDlg, GetDlgItem(hDlg, IDC_SYMLINK_TARGET), MAKEINTRESOURCE(IDI_RIGHT_ARROW));
// Default number of characters for combo box's edit field is 46.
// We have to increase it.
hWndChild = GetDlgItem(hDlg, IDC_HARDLINK_LIST);
ComboBox_LimitText(hWndChild, MAX_PATH);
// Pre-fill the dialog
SetDlgItemText(hDlg, IDC_SYMLINK, _T("\\??\\C:"));
SetDlgItemText(hDlg, IDC_HARDLINK_LIST, _T("C:\\TestFile.bin"));
SetDlgItemText(hDlg, IDC_NEW_HARDLINK, _T("C:\\TestFile_HardLink.bin"));
szReparseName = (pData->IsDefaultFileName1) ? _T("C:\\Windows_Reparse") : pData->szFileName1;
SetDlgItemText(hDlg, IDC_REPARSE, szReparseName);
// Initiate updating of the view
PostMessage(hDlg, WM_UPDATE_VIEW, 0, 0);
return TRUE;
}
static int OnSetActive(HWND /* hDlg */)
{
return FALSE;
}
static int OnKillActive(HWND /* hDlg */)
{
return FALSE;
}
static int OnBeginLabelEdit(HWND hDlg, LPNMTVDISPINFO pNMDispInfo)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData = GetDialogData(hDlg);
TCHAR szItemText[0x200];
HWND hTreeView = GetDlgItem(hDlg, IDC_REPARSE_DATA);
HWND hWndEdit;
BOOL bStartEditing = FALSE;
// Retrieve the edit control
hWndEdit = TreeView_GetEditControl(hTreeView);
if(hWndEdit != NULL)
{
// Get the reparse data
ReparseData = pData->ReparseData;
// Perform the item-specific editing
switch(pNMDispInfo->item.lParam)
{
case ITEM_TYPE_REPARSE_TAG:
StringCchPrintf(szItemText, _countof(szItemText), _T("%08X"), ReparseData->ReparseTag);
SetWindowText(hWndEdit, szItemText);
bStartEditing = TRUE;
break;
case ITEM_TYPE_SUBSTNAME_MP:
bStartEditing = TreeView_EditString(hWndEdit,
ReparseData->MountPointReparseBuffer.PathBuffer,
ReparseData->MountPointReparseBuffer.SubstituteNameOffset,
ReparseData->MountPointReparseBuffer.SubstituteNameLength);
case ITEM_TYPE_PRINTNAME_MP:
bStartEditing = TreeView_EditString(hWndEdit,
ReparseData->MountPointReparseBuffer.PathBuffer,
ReparseData->MountPointReparseBuffer.PrintNameOffset,
ReparseData->MountPointReparseBuffer.PrintNameLength);
break;
case ITEM_TYPE_SUBSTNAME_LNK:
bStartEditing = TreeView_EditString(hWndEdit,
ReparseData->SymbolicLinkReparseBuffer.PathBuffer,
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset,
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength);
break;
case ITEM_TYPE_PRINTNAME_LNK:
bStartEditing = TreeView_EditString(hWndEdit,
ReparseData->SymbolicLinkReparseBuffer.PathBuffer,
ReparseData->SymbolicLinkReparseBuffer.PrintNameOffset,
ReparseData->SymbolicLinkReparseBuffer.PrintNameLength);
break;
}
}
// By default, the item is not editable
if(bStartEditing == FALSE)
SetResultInfo(hDlg, RSI_NTSTATUS, STATUS_CANNOT_EDIT_THIS);
// Setup the close dialog and start editing (or not)
DisableCloseDialog(hDlg, bStartEditing);
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bStartEditing ? FALSE : TRUE);
return TRUE;
}
static int OnEndLabelEdit(HWND hDlg, NMTVDISPINFO * pNMDispInfo)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData = GetDialogData(hDlg);
DWORD IntValue32 = 0;
DWORD dwErrCode = ERROR_NOT_SUPPORTED;
// Only do something when the test has been changed
if(pNMDispInfo->item.pszText)
{
// Get the pointer to the reparse data
ReparseData = pData->ReparseData;
// Perform the item-specific editing
switch(pNMDispInfo->item.lParam)
{
case ITEM_TYPE_REPARSE_TAG:
dwErrCode = Text2Hex32(pNMDispInfo->item.pszText, &IntValue32);
if(dwErrCode == ERROR_SUCCESS && IntValue32 != ReparseData->ReparseTag)
dwErrCode = SetReparseDataTag(ReparseData, IntValue32, pData->ReparseDataLength);
break;
case ITEM_TYPE_SUBSTNAME_MP:
if(ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
dwErrCode = SetReparseDataSubstName(ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, pNMDispInfo->item.pszText);
break;
case ITEM_TYPE_PRINTNAME_MP:
if(ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
dwErrCode = SetReparseDataPrintName(ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, pNMDispInfo->item.pszText);
break;
case ITEM_TYPE_SUBSTNAME_LNK:
if(ReparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK)
dwErrCode = SetReparseDataSubstName(ReparseData, ReparseData->SymbolicLinkReparseBuffer.PathBuffer, pNMDispInfo->item.pszText);
break;
case ITEM_TYPE_PRINTNAME_LNK:
if(ReparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK)
dwErrCode = SetReparseDataPrintName(ReparseData, ReparseData->SymbolicLinkReparseBuffer.PathBuffer, pNMDispInfo->item.pszText);
break;
}
// If we are going to accept changes, we need to update the view
if(dwErrCode == ERROR_SUCCESS)
{
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
PostMessage(hDlg, WM_UPDATE_VIEW, 0, 0);
}
}
DisableCloseDialog(hDlg, FALSE);
return TRUE;
}
static int OnDoubleClick(HWND hDlg, LPNMHDR pNMHDR)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData;
LPARAM lParam;
ULONG ReparseTag;
// Was it the tree view?
if(pNMHDR->idFrom == IDC_REPARSE_DATA)
{
// Retrieve the item param
lParam = TreeView_GetItemParam(pNMHDR->hwndFrom, TreeView_GetSelection(pNMHDR->hwndFrom));
// If reparse tag, we can give the user a choice
if(lParam == ITEM_TYPE_REPARSE_TAG)
{
// Retrieve the reparse data
pData = GetDialogData(hDlg);
ReparseData = pData->ReparseData;
ReparseTag = ReparseData->ReparseTag;
// Let the user choose a new reparse tag
if(FlagsDialog(hDlg, IDS_CHOOSE_REPARSE_TAG, ReparseTags, ReparseTag) == IDOK)
{
SetReparseDataTag(ReparseData, ReparseTag, pData->ReparseDataLength);
PostMessage(hDlg, WM_UPDATE_VIEW, 0, 0);
}
}
}
return TRUE;
}
static int OnSymlinkCreate(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING SymlinkName;
UNICODE_STRING TargetName;
NTSTATUS Status;
TCHAR szSymlinkName[MAX_PATH];
TCHAR szTargetName[MAX_PATH];
// Get the name of the symbolic link
GetDlgItemText(hDlg, IDC_SYMLINK, szSymlinkName, _countof(szSymlinkName));
GetDlgItemText(hDlg, IDC_SYMLINK_TARGET, szTargetName, _countof(szTargetName));
// Query the symbolic link
InitializeObjectAttributes(&ObjAttr,
&SymlinkName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
RtlInitUnicodeString(&SymlinkName, szSymlinkName);
RtlInitUnicodeString(&TargetName, szTargetName);
// Create the symbolic link
Status = NtCreateSymbolicLinkObject(&pData->hSymLink,
SYMBOLIC_LINK_ALL_ACCESS,
&ObjAttr,
&TargetName);
if(NT_SUCCESS(Status))
{
EnableDlgItems(hDlg, TRUE, IDC_SYMLINK_DELETE, 0);
}
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status);
return TRUE;
}
static int OnSymlinkQuery(HWND hDlg)
{
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING SymlinkName;
UNICODE_STRING TargetName;
NTSTATUS Status;
HANDLE Handle;
TCHAR szSymlinkName[MAX_PATH];
ULONG Length = 0;
// Get the name of the symbolic link
GetDlgItemText(hDlg, IDC_SYMLINK, szSymlinkName, _countof(szSymlinkName));
// Query the symbolic link
InitializeObjectAttributes(&ObjAttr, &SymlinkName, OBJ_CASE_INSENSITIVE, NULL, NULL);
RtlInitUnicodeString(&SymlinkName, szSymlinkName);
Status = NtOpenSymbolicLinkObject(&Handle, SYMBOLIC_LINK_QUERY, &ObjAttr);
if(NT_SUCCESS(Status))
{
TargetName.MaximumLength = 0x1000;
TargetName.Length = 0;
TargetName.Buffer = (PWSTR)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, TargetName.MaximumLength + 2);
if(TargetName.Buffer != NULL)
{
Status = NtQuerySymbolicLinkObject(Handle, &TargetName, &Length);
if(NT_SUCCESS(Status))
{
SetDlgItemText(hDlg, IDC_SYMLINK_TARGET, TargetName.Buffer);
}
HeapFree(g_hHeap, 0, TargetName.Buffer);
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
NtClose(Handle);
}
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFO_INT32, Status, Length);
return TRUE;
}
static int OnSymlinkDelete(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
NTSTATUS Status;
Status = NtClose(pData->hSymLink);
pData->hSymLink = NULL;
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status);
EnableDlgItems(hDlg, FALSE, IDC_SYMLINK_DELETE, 0);
return TRUE;
}
static void OnShowHardlinks(HWND hDlg)
{
HWND hCombo = GetDlgItem(hDlg, IDC_HARDLINK_LIST);
ComboBox_ShowDropdown(hCombo, TRUE);
}
static void OnUpdateView(HWND hDlg)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData = GetDialogData(hDlg);
HTREEITEM hItem = TVI_ROOT;
TCHAR szItemText[MAX_PATH];
HWND hWndChild = GetDlgItem(hDlg, IDC_REPARSE_DATA);
// Free all items in the tree view
TreeView_DeleteAllItems(hWndChild);
ReparseData = pData->ReparseData;
// Insert the three common fields from the REPARSE_DATA
TreeView_InsertNameAndValue(hWndChild, hItem, _T("Tag"), FormatReparseTag(ReparseData->ReparseTag, szItemText, _countof(szItemText)), ITEM_TYPE_REPARSE_TAG);
TreeView_InsertInteger(hWndChild, hItem, _T("ReparseDataLength"), _T("0x%02X"), ReparseData->ReparseDataLength);
TreeView_InsertInteger(hWndChild, hItem, _T("Reserved"), _T("0x%02X"), ReparseData->Reserved);
// Insert the sub-structure
switch(ReparseData->ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
hItem = TreeView_InsertString(hWndChild, hItem, _T("MountPointReparseBuffer"));
if(hItem != NULL)
{
TreeView_InsertSubstName(hWndChild, hItem, ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, ITEM_TYPE_SUBSTNAME_MP);
TreeView_InsertPrintName(hWndChild, hItem, ReparseData, ReparseData->MountPointReparseBuffer.PathBuffer, ITEM_TYPE_PRINTNAME_MP);
}
break;
case IO_REPARSE_TAG_SYMLINK:
hItem = TreeView_InsertString(hWndChild, hItem, _T("SymbolicLinkReparseBuffer"));
if(hItem != NULL)
{
TreeView_InsertSubstName(hWndChild, hItem, ReparseData, ReparseData->SymbolicLinkReparseBuffer.PathBuffer, ITEM_TYPE_SUBSTNAME_LNK);
TreeView_InsertPrintName(hWndChild, hItem, ReparseData, ReparseData->SymbolicLinkReparseBuffer.PathBuffer, ITEM_TYPE_PRINTNAME_LNK);
TreeView_InsertInteger (hWndChild, hItem, _T("Flags"), _T("0x%04X"), ReparseData->SymbolicLinkReparseBuffer.Flags);
}
break;
case IO_REPARSE_TAG_WIM:
hItem = TreeView_InsertString(hWndChild, hItem, _T("WimImageReparseBuffer"));
if(hItem != NULL)
{
TreeView_InsertGuid(hWndChild, hItem, _T("ImageGuid"),
ReparseData->WimImageReparseBuffer.ImageGuid);
TreeView_InsertBinary(hWndChild, hItem, _T("ImagePathHash"),
ReparseData->WimImageReparseBuffer.ImagePathHash,
sizeof(ReparseData->WimImageReparseBuffer.ImagePathHash));
}
break;
case IO_REPARSE_TAG_WOF:
hItem = TreeView_InsertString(hWndChild, hItem, _T("WofReparsePointBuffer"));
if(hItem != NULL)
{
TreeView_InsertInteger(hWndChild, hItem, _T("Wof.Version"), _T("0x%04X"), ReparseData->WofReparseBuffer.Wof_Version);
TreeView_InsertInteger(hWndChild, hItem, _T("Wof.Provider"), _T("0x%04X"), ReparseData->WofReparseBuffer.Wof_Provider);
TreeView_InsertInteger(hWndChild, hItem, _T("Version"), _T("0x%04X"), ReparseData->WofReparseBuffer.FileInfo_Version);
TreeView_InsertInteger(hWndChild, hItem, _T("Algorithm"), _T("0x%04X"), ReparseData->WofReparseBuffer.FileInfo_Algorithm);
}
break;
case IO_REPARSE_TAG_APPEXECLINK:
hItem = TreeView_InsertString(hWndChild, hItem, _T("AppExecLinkReparseBuffer"));
if(hItem != NULL)
{
// Insert the string count
TreeView_InsertInteger(hWndChild, hItem, _T("StringCount"), _T("0x%04X"), ReparseData->AppExecLinkReparseBuffer.StringCount);
// Insert the strings
if(ReparseData->AppExecLinkReparseBuffer.StringCount != 0)
{
LPWSTR szString = (LPWSTR)ReparseData->AppExecLinkReparseBuffer.StringList;
WCHAR szValue[0x300];
for(ULONG i = 0; i < ReparseData->AppExecLinkReparseBuffer.StringCount; i++)
{
StringCchPrintf(szValue, _countof(szValue), _T("%s: %s"), AppExecLinkParts[i], szString);
TreeView_InsertString(hWndChild, hItem, szValue);
szString += wcslen(szString) + 1;
}
}
}
break;
case IO_REPARSE_TAG_WCI:
hItem = TreeView_InsertString(hWndChild, hItem, _T("WcifsReparseBuffer"));
if(hItem != NULL)
{
TreeView_InsertInteger(hWndChild, hItem, _T("Version"), _T("0x%08X"), ReparseData->WcifsReparseBuffer.Version);
TreeView_InsertInteger(hWndChild, hItem, _T("Reserved"), _T("0x%08X"), ReparseData->WcifsReparseBuffer.Reserved);
TreeView_InsertGuid(hWndChild, hItem, _T("LookupGuid"), ReparseData->WcifsReparseBuffer.LookupGuid);
TreeView_InsertInteger(hWndChild, hItem, _T("WciNameLength"), _T("0x%04X"), ReparseData->WcifsReparseBuffer.WciNameLength);
TreeView_InsertStrOffs(hWndChild, hItem, _T("WciName"), ReparseData, FIELD_OFFSET(REPARSE_DATA_BUFFER, WcifsReparseBuffer.WciName), ReparseData->WcifsReparseBuffer.WciNameLength, 0);
}
break;
default:
if((ReparseData->ReparseTag & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD)
{
// Attemnpt to unpack and format the HSM data. If fails, fall back to the generic reparse buffer
if(NT_SUCCESS(TreeView_InsertHsm(hWndChild, hItem, ReparseData, pData->ReparseDataValid)))
break;
}
hItem = TreeView_InsertString(hWndChild, hItem, _T("GenericReparseBuffer"));
if(hItem != NULL)
{
if(ReparseData->ReparseDataLength != 0)
{
TreeView_InsertBinary(hWndChild, hItem, _T("DataBuffer"), ReparseData->GenericReparseBuffer.DataBuffer, ReparseData->ReparseDataLength);
}
else
{
TreeView_InsertString(hWndChild, hItem, _T("DataBuffer: (no data)"));
}
}
break;
}
// Expand the tree view
TreeView_Expand(hWndChild, hItem, TVE_EXPAND);
}
static int OnHardlinkCreate(HWND hDlg)
{
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatus = { 0 };
UNICODE_STRING FileName;
NTSTATUS Status = STATUS_SUCCESS;
LPTSTR szPlainName;
HANDLE hDirectory = NULL;
HANDLE hFile = NULL;
TCHAR szHardlinkName[MAX_PATH];
TCHAR szFileName[MAX_PATH];
TCHAR szDirectory[MAX_PATH];
BYTE LinkInfoBuff[0x500];
// Get the name of the symbolic link
GetDlgItemText(hDlg, IDC_HARDLINK_LIST, szFileName, _countof(szFileName));
GetDlgItemText(hDlg, IDC_NEW_HARDLINK, szHardlinkName, _countof(szHardlinkName));
// Get the plain file name of the hardlink
szPlainName = _tcsrchr(szHardlinkName, _T('\\'));
if(szPlainName != NULL)
{
// Extract directory name from the hardlink path
StringCchCopyN(szDirectory, _countof(szDirectory), szHardlinkName, (szPlainName - szHardlinkName + 1));
szDirectory[szPlainName - szHardlinkName + 1] = 0;
// Open the directory
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szDirectory);
if(NT_SUCCESS(Status))
{
Status = NtOpenFile(&hDirectory,
SYNCHRONIZE,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_ALERT);
FreeFileNameString(&FileName);
}
}
if(NT_SUCCESS(Status))
{
// Open the file in order to set symlink
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szFileName);
if(NT_SUCCESS(Status))
{
Status = NtOpenFile(&hFile,
MAXIMUM_ALLOWED,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0);
FreeFileNameString(&FileName);
}
// Set the new hardlink to the file
if(NT_SUCCESS(Status))
{
PFILE_LINK_INFORMATION pLinkInfo = (PFILE_LINK_INFORMATION)LinkInfoBuff;
// Get the plain name of the directory
szPlainName = GetPlainName(szHardlinkName);
// Prepare the structure for creating hardlink
pLinkInfo->ReplaceIfExists = FALSE;
pLinkInfo->RootDirectory = hDirectory;
pLinkInfo->FileNameLength = (ULONG)(_tcslen(szPlainName) * sizeof(TCHAR));
StringCchCopy(pLinkInfo->FileName,
(sizeof(LinkInfoBuff) - sizeof(FILE_LINK_INFORMATION)) / sizeof(WCHAR),
szPlainName);
// Create the hard link
Status = NtSetInformationFile(hFile,
&IoStatus,
pLinkInfo,
sizeof(FILE_LINK_INFORMATION) + pLinkInfo->FileNameLength,
FileLinkInformation);
NtClose(hFile);
}
}
if(hDirectory != NULL)
NtClose(hDirectory);
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFORMATION, Status, &IoStatus);
return TRUE;
}
static int OnHardlinkQuery(HWND hDlg)
{
PFILE_LINK_ENTRY_INFORMATION pLinkInfo = NULL;
PFILE_LINKS_INFORMATION pLinksInfo = NULL;
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatus = { 0 };
UNICODE_STRING FileName;
NTSTATUS Status = STATUS_SUCCESS;
LPTSTR szHardLinkName;
HANDLE hFile = NULL;
TCHAR szFileName[MAX_PATH];
ULONG HardLinkCount = 0;
ULONG Length = 0x100;
HWND hCombo = GetDlgItem(hDlg, IDC_HARDLINK_LIST);
// Get the name of the symbolic link
GetDlgItemText(hDlg, IDC_HARDLINK_LIST, szFileName, _countof(szFileName));
// Open the hardlink as if it was file
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szFileName);
if(NT_SUCCESS(Status))
{
Status = NtOpenFile(&hFile,
FILE_READ_ATTRIBUTES,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0);
FreeFileNameString(&FileName);
}
// Query the file name
if(NT_SUCCESS(Status))
{
for(;;)
{
// Allocate a chink of memory, that (we hope) will be enough
pLinksInfo = (PFILE_LINKS_INFORMATION)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, Length);
if(pLinksInfo == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
// Query all hardlinks
Status = NtQueryInformationFile(hFile,
&IoStatus,
pLinksInfo,
Length,
FileHardLinkInformation);
if(Status != STATUS_INFO_LENGTH_MISMATCH && Status != STATUS_BUFFER_OVERFLOW)
break;
// Free the buffer and double the length
HeapFree(g_hHeap, 0, pLinksInfo);
Length *= 2;
}
if(NT_SUCCESS(Status))
{
ComboBox_ResetContent(hCombo);
if(pLinksInfo->EntriesReturned != 0)
{
pLinkInfo = &pLinksInfo->Entry;
for(;;)
{
szHardLinkName = GetFullHardLinkName(pLinkInfo, szFileName);
if(szHardLinkName != NULL)
{
// First hardlink will also be added to the combo box main field
if(HardLinkCount == 0)
SetWindowText(hCombo, szHardLinkName);
// Add the hardlink to the list
ComboBox_InsertString(hCombo, -1, szHardLinkName);
HeapFree(g_hHeap, 0, szHardLinkName);
HardLinkCount++;
}
if(pLinkInfo->NextEntryOffset == 0)
break;
pLinkInfo = (PFILE_LINK_ENTRY_INFORMATION)((LPBYTE)pLinkInfo + pLinkInfo->NextEntryOffset);
}
}
}
// Free the buffer and close file handle
if(pLinksInfo != NULL)
HeapFree(g_hHeap, 0, pLinksInfo);
NtClose(hFile);
}
// Open the drop list to show all links
if(HardLinkCount != 0)
PostMessage(hDlg, WM_SHOW_HARDLINKS, 0, 0);
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFORMATION, Status, &IoStatus);
return TRUE;
}
static int OnHardlinkDelete(HWND hDlg)
{
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING FileName;
NTSTATUS Status = STATUS_SUCCESS;
TCHAR szHardlinkName[MAX_PATH];
// Get the name of the symbolic link
GetDlgItemText(hDlg, IDC_HARDLINK_LIST, szHardlinkName, _countof(szHardlinkName));
// Open the symlink and delete it
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szHardlinkName);
if(NT_SUCCESS(Status))
{
Status = NtDeleteFile(&ObjAttr);
FreeFileNameString(&FileName);
}
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status);
return TRUE;
}
static int OnReparseCreate(HWND hDlg)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData = GetDialogData(hDlg);
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatus = { 0 };
UNICODE_STRING FileName;
NTSTATUS Status;
HANDLE hReparse = NULL;
TCHAR szReparseName[MAX_PATH];
ULONG CreateOptions = FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_REPARSE_POINT;
// Get the name of the reparse point
GetDlgItemText(hDlg, IDC_REPARSE, szReparseName, _countof(szReparseName));
ReparseData = pData->ReparseData;
// Prepare the name of the reparse point
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szReparseName);
if(NT_SUCCESS(Status))
{
// Get the name of the target. If it doesn't exist, we create new
if(ReparseTargetIsDirectory(ReparseData))
CreateOptions |= FILE_DIRECTORY_FILE;
// Create/open the reparse point
Status = NtCreateFile(&hReparse,
GENERIC_WRITE | SYNCHRONIZE,
&ObjAttr,
&IoStatus,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
CreateOptions,
NULL,
0);
if(NT_SUCCESS(Status))
{
// Fire the IOCTL
Status = NtFsControlFile(hReparse,
NULL,
NULL,
NULL,
&IoStatus,
FSCTL_SET_REPARSE_POINT,
ReparseData,
GetTotalDataLength(ReparseData),
NULL,
0);
// Close the handle
NtClose(hReparse);
}
FreeFileNameString(&FileName);
}
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFORMATION, Status, &IoStatus);
return TRUE;
}
static int OnReparseQuery(HWND hDlg)
{
PREPARSE_DATA_BUFFER ReparseData;
TFileTestData * pData = GetDialogData(hDlg);
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatus;
UNICODE_STRING FileName;
NTSTATUS Status;
HANDLE hFile = NULL;
TCHAR szReparseName[MAX_PATH];
// Get the name of the reparse point
GetDlgItemText(hDlg, IDC_REPARSE, szReparseName, _countof(szReparseName));
ReparseData = pData->ReparseData;
// Open the reparse point
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = FileNameToUnicodeString(&FileName, szReparseName);
if(NT_SUCCESS(Status))
{
// Open the reparse point
Status = NtOpenFile(&hFile,
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&ObjAttr,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_ALERT);
if(NT_SUCCESS(Status))
{
// Reset the data of the reparse point, so we don't have any artifacts from the past
memset(pData->ReparseData, 0, pData->ReparseDataLength);
pData->ReparseDataValid = 0;
// Query the reparse point
Status = NtFsControlFile(hFile,
NULL,
NULL,
NULL,
&IoStatus,
FSCTL_GET_REPARSE_POINT,
NULL,
0,
pData->ReparseData,
pData->ReparseDataLength);
if(NT_SUCCESS(Status))
{
// Update the view
pData->ReparseDataValid = (ULONG)IoStatus.Information;
PostMessage(hDlg, WM_UPDATE_VIEW, Status, 0);
}
NtClose(hFile);
}
FreeFileNameString(&FileName);
}
// Show the result to the dialog
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_INFORMATION, Status, &IoStatus);
return TRUE;
}
static int OnReparseDelete(HWND hDlg)
{
OBJECT_ATTRIBUTES ObjAttr;
UNICODE_STRING FileName;
NTSTATUS Status;
TCHAR szReparseName[MAX_PATH];
// Get the name of the reparse point
GetDlgItemText(hDlg, IDC_REPARSE, szReparseName, _countof(szReparseName));
// Open the reparse point
Status = FileNameToUnicodeString(&FileName, szReparseName);
if(NT_SUCCESS(Status))
{
InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtDeleteReparsePoint(&ObjAttr);
FreeFileNameString(&FileName);
}
SetResultInfo(hDlg, RSI_NTSTATUS | RSI_NOINFO, Status);
return TRUE;
}
static int OnCommand(HWND hDlg, UINT nNotify, UINT nIDCtrl)
{
if(nNotify == BN_CLICKED || nNotify == 1)
{
switch(nIDCtrl)
{
case ID_EDIT_LABEL:
return TreeView_EditLabel_ID(hDlg, IDC_REPARSE_DATA);
case IDC_SYMLINK_CREATE:
return OnSymlinkCreate(hDlg);
case IDC_SYMLINK_QUERY:
return OnSymlinkQuery(hDlg);
case IDC_SYMLINK_DELETE:
return OnSymlinkDelete(hDlg);
case IDC_HARDLINK_CREATE:
return OnHardlinkCreate(hDlg);
case IDC_HARDLINK_QUERY:
return OnHardlinkQuery(hDlg);
case IDC_HARDLINK_DELETE:
return OnHardlinkDelete(hDlg);
case IDC_REPARSE_CREATE:
return OnReparseCreate(hDlg);
case IDC_REPARSE_QUERY:
return OnReparseQuery(hDlg);
case IDC_REPARSE_DELETE:
return OnReparseDelete(hDlg);
}
}
return FALSE;
}
static int OnNotify(HWND hDlg, LPNMHDR pNMHDR)
{
switch(pNMHDR->code)
{
case PSN_SETACTIVE:
return OnSetActive(hDlg);
case PSN_KILLACTIVE:
return OnKillActive(hDlg);
case TVN_BEGINLABELEDIT:
return OnBeginLabelEdit(hDlg, (LPNMTVDISPINFO)pNMHDR);
case TVN_ENDLABELEDIT:
return OnEndLabelEdit(hDlg, (LPNMTVDISPINFO)pNMHDR);
case TVN_KEYDOWN:
return OnTVKeyDown_CopyToClipboard(hDlg, (LPNMTVKEYDOWN)pNMHDR);
case NM_DBLCLK:
return OnDoubleClick(hDlg, pNMHDR);
}
return FALSE;
}
static void OnDestroy(HWND hDlg)
{
TFileTestData * pData = GetDialogData(hDlg);
if(pData != NULL)
{
if(pAnchors != NULL)
delete pAnchors;
pAnchors = NULL;
if(pData->ReparseData != NULL)
HeapFree(g_hHeap, 0, pData->ReparseData);
pData->ReparseData = NULL;
}
}
//-----------------------------------------------------------------------------
// Public functions
INT_PTR CALLBACK PageProc10(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
return OnInitDialog(hDlg, lParam);
case WM_SIZE:
case WM_GETMINMAXINFO:
return pAnchors->OnMessage(uMsg, wParam, lParam);
case WM_SHOW_HARDLINKS:
OnShowHardlinks(hDlg);
return FALSE;
case WM_UPDATE_VIEW:
OnUpdateView(hDlg);
return TRUE;
case WM_COMMAND:
return OnCommand(hDlg, HIWORD(wParam), LOWORD(wParam));
case WM_NOTIFY:
return OnNotify(hDlg, (NMHDR *)lParam);
case WM_DESTROY:
OnDestroy(hDlg);
return FALSE;
}
return FALSE;
}