mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
57e77756e7
* added UserNotes plugin git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4774 21ef857c-d57f-4fe0-8362-d861dc6d29cd
398 lines
9.0 KiB
C
398 lines
9.0 KiB
C
#include <phdk.h>
|
|
#include <mxml.h>
|
|
#include <shlobj.h>
|
|
#include "db.h"
|
|
|
|
BOOLEAN NTAPI ObjectDbCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
);
|
|
|
|
ULONG NTAPI ObjectDbHashFunction(
|
|
__in PVOID Entry
|
|
);
|
|
|
|
PPH_HASHTABLE ObjectDb;
|
|
PH_QUEUED_LOCK ObjectDbLock = PH_QUEUED_LOCK_INIT;
|
|
PPH_STRING ObjectDbPath;
|
|
|
|
VOID InitializeDb(
|
|
VOID
|
|
)
|
|
{
|
|
ObjectDb = PhCreateHashtable(
|
|
sizeof(PDB_OBJECT),
|
|
ObjectDbCompareFunction,
|
|
ObjectDbHashFunction,
|
|
64
|
|
);
|
|
}
|
|
|
|
// Copied from srvprv.c
|
|
/**
|
|
* Generates a hash code for a string, case-insensitive.
|
|
*
|
|
* \param String The string.
|
|
* \param Count The number of characters to hash.
|
|
*/
|
|
FORCEINLINE ULONG HashStringIgnoreCase(
|
|
__in PWSTR String,
|
|
__in ULONG Count
|
|
)
|
|
{
|
|
ULONG hash = Count;
|
|
|
|
if (Count == 0)
|
|
return 0;
|
|
|
|
do
|
|
{
|
|
hash = RtlUpcaseUnicodeChar(*String) + (hash << 6) + (hash << 16) - hash;
|
|
String++;
|
|
} while (--Count != 0);
|
|
|
|
return hash;
|
|
}
|
|
|
|
BOOLEAN NTAPI ObjectDbCompareFunction(
|
|
__in PVOID Entry1,
|
|
__in PVOID Entry2
|
|
)
|
|
{
|
|
PDB_OBJECT object1 = *(PDB_OBJECT *)Entry1;
|
|
PDB_OBJECT object2 = *(PDB_OBJECT *)Entry2;
|
|
|
|
return object1->Tag == object2->Tag && PhEqualStringRef(&object1->Key, &object2->Key, TRUE);
|
|
}
|
|
|
|
ULONG NTAPI ObjectDbHashFunction(
|
|
__in PVOID Entry
|
|
)
|
|
{
|
|
PDB_OBJECT object = *(PDB_OBJECT *)Entry;
|
|
|
|
return object->Tag + HashStringIgnoreCase(object->Key.Buffer, object->Key.Length / sizeof(WCHAR));
|
|
}
|
|
|
|
VOID LockDb(
|
|
VOID
|
|
)
|
|
{
|
|
PhAcquireQueuedLockExclusive(&ObjectDbLock);
|
|
}
|
|
|
|
VOID UnlockDb(
|
|
VOID
|
|
)
|
|
{
|
|
PhReleaseQueuedLockExclusive(&ObjectDbLock);
|
|
}
|
|
|
|
PDB_OBJECT FindDbObject(
|
|
__in ULONG Tag,
|
|
__in PPH_STRINGREF Name
|
|
)
|
|
{
|
|
DB_OBJECT lookupObject;
|
|
PDB_OBJECT lookupObjectPtr;
|
|
PDB_OBJECT *objectPtr;
|
|
|
|
lookupObject.Tag = Tag;
|
|
lookupObject.Key = *Name;
|
|
lookupObjectPtr = &lookupObject;
|
|
|
|
objectPtr = PhFindEntryHashtable(ObjectDb, &lookupObjectPtr);
|
|
|
|
if (objectPtr)
|
|
return *objectPtr;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
PDB_OBJECT CreateDbObject(
|
|
__in ULONG Tag,
|
|
__in PPH_STRINGREF Name,
|
|
__in PPH_STRING Comment
|
|
)
|
|
{
|
|
PDB_OBJECT object;
|
|
BOOLEAN added;
|
|
PDB_OBJECT *realObject;
|
|
|
|
object = PhAllocate(sizeof(DB_OBJECT));
|
|
object->Tag = Tag;
|
|
object->Key = *Name;
|
|
|
|
realObject = PhAddEntryHashtableEx(ObjectDb, &object, &added);
|
|
|
|
if (added)
|
|
{
|
|
object->Name = PhCreateStringEx(Name->Buffer, Name->Length);
|
|
object->Key = object->Name->sr;
|
|
object->Comment = Comment;
|
|
PhReferenceObject(Comment);
|
|
}
|
|
else
|
|
{
|
|
PhFree(object);
|
|
object = *realObject;
|
|
|
|
PhSwapReference(&object->Comment, Comment);
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
VOID DeleteDbObject(
|
|
__in PDB_OBJECT Object
|
|
)
|
|
{
|
|
PhRemoveEntryHashtable(ObjectDb, &Object);
|
|
|
|
PhDereferenceObject(Object->Name);
|
|
PhDereferenceObject(Object->Comment);
|
|
PhFree(Object);
|
|
}
|
|
|
|
VOID SetDbPath(
|
|
__in PPH_STRING Path
|
|
)
|
|
{
|
|
PhSwapReference(&ObjectDbPath, Path);
|
|
}
|
|
|
|
mxml_type_t MxmlLoadCallback(
|
|
__in mxml_node_t *node
|
|
)
|
|
{
|
|
return MXML_OPAQUE;
|
|
}
|
|
|
|
PPH_STRING GetOpaqueXmlNodeText(
|
|
__in mxml_node_t *node
|
|
)
|
|
{
|
|
if (node->child && node->child->type == MXML_OPAQUE && node->child->value.opaque)
|
|
{
|
|
return PhCreateStringFromAnsi(node->child->value.opaque);
|
|
}
|
|
else
|
|
{
|
|
return PhReferenceEmptyString();
|
|
}
|
|
}
|
|
|
|
NTSTATUS LoadDb(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE fileHandle;
|
|
LARGE_INTEGER fileSize;
|
|
mxml_node_t *topNode;
|
|
mxml_node_t *currentNode;
|
|
|
|
status = PhCreateFileWin32(
|
|
&fileHandle,
|
|
ObjectDbPath->Buffer,
|
|
FILE_GENERIC_READ,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0)
|
|
{
|
|
// A blank file is OK. There are no objects to load.
|
|
NtClose(fileHandle);
|
|
return status;
|
|
}
|
|
|
|
topNode = mxmlLoadFd(NULL, fileHandle, MxmlLoadCallback);
|
|
NtClose(fileHandle);
|
|
|
|
if (!topNode)
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
|
|
if (topNode->type != MXML_ELEMENT)
|
|
{
|
|
mxmlDelete(topNode);
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
}
|
|
|
|
LockDb();
|
|
|
|
currentNode = topNode->child;
|
|
|
|
while (currentNode)
|
|
{
|
|
PPH_STRING tag = NULL;
|
|
PPH_STRING name = NULL;
|
|
PPH_STRING comment = NULL;
|
|
|
|
if (currentNode->type == MXML_ELEMENT &&
|
|
currentNode->value.element.num_attrs >= 2)
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < (ULONG)currentNode->value.element.num_attrs; i++)
|
|
{
|
|
if (stricmp(currentNode->value.element.attrs[i].name, "tag") == 0)
|
|
PhSwapReference2(&tag, PhCreateStringFromAnsi(currentNode->value.element.attrs[i].value));
|
|
else if (stricmp(currentNode->value.element.attrs[i].name, "name") == 0)
|
|
PhSwapReference2(&name, PhCreateStringFromAnsi(currentNode->value.element.attrs[i].value));
|
|
}
|
|
}
|
|
|
|
comment = GetOpaqueXmlNodeText(currentNode);
|
|
|
|
if (tag && name && comment)
|
|
{
|
|
ULONG64 tagInteger;
|
|
|
|
PhStringToInteger64(&tag->sr, 10, &tagInteger);
|
|
CreateDbObject((ULONG)tagInteger, &name->sr, comment);
|
|
}
|
|
|
|
PhSwapReference(&tag, NULL);
|
|
PhSwapReference(&name, NULL);
|
|
PhSwapReference(&comment, NULL);
|
|
|
|
currentNode = currentNode->next;
|
|
}
|
|
|
|
UnlockDb();
|
|
|
|
mxmlDelete(topNode);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
char *MxmlSaveCallback(
|
|
__in mxml_node_t *node,
|
|
__in int position
|
|
)
|
|
{
|
|
if (STR_IEQUAL(node->value.element.name, "object"))
|
|
{
|
|
if (position == MXML_WS_BEFORE_OPEN)
|
|
return " ";
|
|
else if (position == MXML_WS_AFTER_CLOSE)
|
|
return "\r\n";
|
|
}
|
|
else if (STR_IEQUAL(node->value.element.name, "objects"))
|
|
{
|
|
if (position == MXML_WS_AFTER_OPEN)
|
|
return "\r\n";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
mxml_node_t *CreateObjectElement(
|
|
__inout mxml_node_t *ParentNode,
|
|
__in PPH_STRINGREF Tag,
|
|
__in PPH_STRINGREF Name,
|
|
__in PPH_STRINGREF Comment
|
|
)
|
|
{
|
|
mxml_node_t *objectNode;
|
|
mxml_node_t *textNode;
|
|
PPH_ANSI_STRING tagAnsi;
|
|
PPH_ANSI_STRING nameAnsi;
|
|
PPH_ANSI_STRING valueAnsi;
|
|
|
|
// Create the setting element.
|
|
|
|
objectNode = mxmlNewElement(ParentNode, "object");
|
|
|
|
tagAnsi = PhCreateAnsiStringFromUnicodeEx(Tag->Buffer, Tag->Length);
|
|
mxmlElementSetAttr(objectNode, "tag", tagAnsi->Buffer);
|
|
PhDereferenceObject(tagAnsi);
|
|
|
|
nameAnsi = PhCreateAnsiStringFromUnicodeEx(Name->Buffer, Name->Length);
|
|
mxmlElementSetAttr(objectNode, "name", nameAnsi->Buffer);
|
|
PhDereferenceObject(nameAnsi);
|
|
|
|
// Set the value.
|
|
|
|
valueAnsi = PhCreateAnsiStringFromUnicodeEx(Comment->Buffer, Comment->Length);
|
|
textNode = mxmlNewOpaque(objectNode, valueAnsi->Buffer);
|
|
PhDereferenceObject(valueAnsi);
|
|
|
|
return objectNode;
|
|
}
|
|
|
|
NTSTATUS SaveDb(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE fileHandle;
|
|
mxml_node_t *topNode;
|
|
ULONG enumerationKey = 0;
|
|
PDB_OBJECT *object;
|
|
|
|
topNode = mxmlNewElement(MXML_NO_PARENT, "objects");
|
|
|
|
LockDb();
|
|
|
|
while (PhEnumHashtable(ObjectDb, (PVOID *)&object, &enumerationKey))
|
|
{
|
|
PPH_STRING tagString;
|
|
|
|
tagString = PhIntegerToString64((*object)->Tag, 10, FALSE);
|
|
CreateObjectElement(topNode, &tagString->sr, &(*object)->Name->sr, &(*object)->Comment->sr);
|
|
PhDereferenceObject(tagString);
|
|
}
|
|
|
|
UnlockDb();
|
|
|
|
// Create the directory if it does not exist.
|
|
{
|
|
PPH_STRING fullPath;
|
|
ULONG indexOfFileName;
|
|
PPH_STRING directoryName;
|
|
|
|
fullPath = PhGetFullPath(ObjectDbPath->Buffer, &indexOfFileName);
|
|
|
|
if (fullPath)
|
|
{
|
|
if (indexOfFileName != -1)
|
|
{
|
|
directoryName = PhSubstring(fullPath, 0, indexOfFileName);
|
|
SHCreateDirectoryEx(NULL, directoryName->Buffer, NULL);
|
|
PhDereferenceObject(directoryName);
|
|
}
|
|
|
|
PhDereferenceObject(fullPath);
|
|
}
|
|
}
|
|
|
|
status = PhCreateFileWin32(
|
|
&fileHandle,
|
|
ObjectDbPath->Buffer,
|
|
FILE_GENERIC_WRITE,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
mxmlDelete(topNode);
|
|
return status;
|
|
}
|
|
|
|
mxmlSaveFd(topNode, fileHandle, MxmlSaveCallback);
|
|
mxmlDelete(topNode);
|
|
NtClose(fileHandle);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|