Process Hacker
db.c
Go to the documentation of this file.
1 /*
2  * Process Hacker User Notes -
3  * database functions
4  *
5  * Copyright (C) 2011-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phdk.h>
24 #include <mxml.h>
25 #include <shlobj.h>
26 #include "db.h"
27 
28 BOOLEAN NTAPI ObjectDbCompareFunction(
29  _In_ PVOID Entry1,
30  _In_ PVOID Entry2
31  );
32 
33 ULONG NTAPI ObjectDbHashFunction(
34  _In_ PVOID Entry
35  );
36 
40 
42  VOID
43  )
44 {
45  ObjectDb = PhCreateHashtable(
46  sizeof(PDB_OBJECT),
49  64
50  );
51 }
52 
54  _In_ PVOID Entry1,
55  _In_ PVOID Entry2
56  )
57 {
58  PDB_OBJECT object1 = *(PDB_OBJECT *)Entry1;
59  PDB_OBJECT object2 = *(PDB_OBJECT *)Entry2;
60 
61  return object1->Tag == object2->Tag && PhEqualStringRef(&object1->Key, &object2->Key, TRUE);
62 }
63 
65  _In_ PVOID Entry
66  )
67 {
68  PDB_OBJECT object = *(PDB_OBJECT *)Entry;
69 
70  return object->Tag + PhHashStringRef(&object->Key, TRUE);
71 }
72 
74  VOID
75  )
76 {
77  return ObjectDb->Count;
78 }
79 
81  VOID
82  )
83 {
84  PhAcquireQueuedLockExclusive(&ObjectDbLock);
85 }
86 
88  VOID
89  )
90 {
91  PhReleaseQueuedLockExclusive(&ObjectDbLock);
92 }
93 
95  _In_ ULONG Tag,
96  _In_ PPH_STRINGREF Name
97  )
98 {
99  DB_OBJECT lookupObject;
100  PDB_OBJECT lookupObjectPtr;
101  PDB_OBJECT *objectPtr;
102 
103  lookupObject.Tag = Tag;
104  lookupObject.Key = *Name;
105  lookupObjectPtr = &lookupObject;
106 
107  objectPtr = PhFindEntryHashtable(ObjectDb, &lookupObjectPtr);
108 
109  if (objectPtr)
110  return *objectPtr;
111  else
112  return NULL;
113 }
114 
116  _In_ ULONG Tag,
117  _In_ PPH_STRINGREF Name,
118  _In_opt_ PPH_STRING Comment
119  )
120 {
121  PDB_OBJECT object;
122  BOOLEAN added;
123  PDB_OBJECT *realObject;
124 
125  object = PhAllocate(sizeof(DB_OBJECT));
126  memset(object, 0, sizeof(DB_OBJECT));
127  object->Tag = Tag;
128  object->Key = *Name;
129 
130  realObject = PhAddEntryHashtableEx(ObjectDb, &object, &added);
131 
132  if (added)
133  {
134  object->Name = PhCreateStringEx(Name->Buffer, Name->Length);
135  object->Key = object->Name->sr;
136 
137  if (Comment)
138  PhSetReference(&object->Comment, Comment);
139  else
140  object->Comment = PhReferenceEmptyString();
141  }
142  else
143  {
144  PhFree(object);
145  object = *realObject;
146 
147  if (Comment)
148  PhSwapReference(&object->Comment, Comment);
149  }
150 
151  return object;
152 }
153 
155  _In_ PDB_OBJECT Object
156  )
157 {
158  PhRemoveEntryHashtable(ObjectDb, &Object);
159 
160  PhDereferenceObject(Object->Name);
161  PhDereferenceObject(Object->Comment);
162  PhFree(Object);
163 }
164 
166  _In_ PPH_STRING Path
167  )
168 {
169  PhSwapReference(&ObjectDbPath, Path);
170 }
171 
173  _In_ mxml_node_t *node
174  )
175 {
176  return MXML_OPAQUE;
177 }
178 
180  _In_ mxml_node_t *node
181  )
182 {
183  if (node->child && node->child->type == MXML_OPAQUE && node->child->value.opaque)
184  {
185  return PhConvertUtf8ToUtf16(node->child->value.opaque);
186  }
187  else
188  {
189  return PhReferenceEmptyString();
190  }
191 }
192 
193 NTSTATUS LoadDb(
194  VOID
195  )
196 {
197  NTSTATUS status;
198  HANDLE fileHandle;
199  LARGE_INTEGER fileSize;
200  mxml_node_t *topNode;
201  mxml_node_t *currentNode;
202 
203  status = PhCreateFileWin32(
204  &fileHandle,
205  ObjectDbPath->Buffer,
206  FILE_GENERIC_READ,
207  0,
208  FILE_SHARE_READ | FILE_SHARE_DELETE,
209  FILE_OPEN,
211  );
212 
213  if (!NT_SUCCESS(status))
214  return status;
215 
216  if (NT_SUCCESS(PhGetFileSize(fileHandle, &fileSize)) && fileSize.QuadPart == 0)
217  {
218  // A blank file is OK. There are no objects to load.
219  NtClose(fileHandle);
220  return status;
221  }
222 
223  topNode = mxmlLoadFd(NULL, fileHandle, MxmlLoadCallback);
224  NtClose(fileHandle);
225 
226  if (!topNode)
227  return STATUS_FILE_CORRUPT_ERROR;
228 
229  if (topNode->type != MXML_ELEMENT)
230  {
231  mxmlDelete(topNode);
232  return STATUS_FILE_CORRUPT_ERROR;
233  }
234 
235  LockDb();
236 
237  currentNode = topNode->child;
238 
239  while (currentNode)
240  {
241  PPH_STRING tag = NULL;
242  PPH_STRING name = NULL;
243  PPH_STRING priorityClass = NULL;
244  PPH_STRING ioPriorityPlusOne = NULL;
245  PPH_STRING comment = NULL;
246 
247  if (currentNode->type == MXML_ELEMENT &&
248  currentNode->value.element.num_attrs >= 2)
249  {
250  ULONG i;
251 
252  for (i = 0; i < (ULONG)currentNode->value.element.num_attrs; i++)
253  {
254  if (stricmp(currentNode->value.element.attrs[i].name, "tag") == 0)
256  else if (stricmp(currentNode->value.element.attrs[i].name, "name") == 0)
257  PhMoveReference(&name, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value));
258  else if (stricmp(currentNode->value.element.attrs[i].name, "priorityclass") == 0)
259  PhMoveReference(&priorityClass, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value));
260  else if (stricmp(currentNode->value.element.attrs[i].name, "iopriorityplusone") == 0)
261  PhMoveReference(&ioPriorityPlusOne, PhConvertUtf8ToUtf16(currentNode->value.element.attrs[i].value));
262  }
263  }
264 
265  comment = GetOpaqueXmlNodeText(currentNode);
266 
267  if (tag && name && comment)
268  {
269  PDB_OBJECT object;
270  ULONG64 tagInteger;
271  ULONG64 priorityClassInteger = 0;
272  ULONG64 ioPriorityPlusOneInteger = 0;
273 
274  PhStringToInteger64(&tag->sr, 10, &tagInteger);
275 
276  if (priorityClass)
277  PhStringToInteger64(&priorityClass->sr, 10, &priorityClassInteger);
278  if (ioPriorityPlusOne)
279  PhStringToInteger64(&ioPriorityPlusOne->sr, 10, &ioPriorityPlusOneInteger);
280 
281  object = CreateDbObject((ULONG)tagInteger, &name->sr, comment);
282  object->PriorityClass = (ULONG)priorityClassInteger;
283  object->IoPriorityPlusOne = (ULONG)ioPriorityPlusOneInteger;
284  }
285 
286  PhClearReference(&tag);
287  PhClearReference(&name);
288  PhClearReference(&priorityClass);
289  PhClearReference(&ioPriorityPlusOne);
290  PhClearReference(&comment);
291 
292  currentNode = currentNode->next;
293  }
294 
295  UnlockDb();
296 
297  mxmlDelete(topNode);
298 
299  return STATUS_SUCCESS;
300 }
301 
303  _In_ mxml_node_t *node,
304  _In_ int position
305  )
306 {
307  if (PhEqualBytesZ(node->value.element.name, "object", TRUE))
308  {
309  if (position == MXML_WS_BEFORE_OPEN)
310  return " ";
311  else if (position == MXML_WS_AFTER_CLOSE)
312  return "\r\n";
313  }
314  else if (PhEqualBytesZ(node->value.element.name, "objects", TRUE))
315  {
316  if (position == MXML_WS_AFTER_OPEN)
317  return "\r\n";
318  }
319 
320  return NULL;
321 }
322 
324  _Inout_ mxml_node_t *ParentNode,
325  _In_ PPH_STRINGREF Tag,
326  _In_ PPH_STRINGREF Name,
327  _In_ PPH_STRINGREF PriorityClass,
328  _In_ PPH_STRINGREF IoPriorityPlusOne,
329  _In_ PPH_STRINGREF Comment
330  )
331 {
332  mxml_node_t *objectNode;
333  mxml_node_t *textNode;
334  PPH_BYTES tagUtf8;
335  PPH_BYTES nameUtf8;
336  PPH_BYTES priorityClassUtf8;
337  PPH_BYTES ioPriorityPlusOneUtf8;
338  PPH_BYTES valueUtf8;
339 
340  // Create the setting element.
341 
342  objectNode = mxmlNewElement(ParentNode, "object");
343 
344  tagUtf8 = PhConvertUtf16ToUtf8Ex(Tag->Buffer, Tag->Length);
345  mxmlElementSetAttr(objectNode, "tag", tagUtf8->Buffer);
346  PhDereferenceObject(tagUtf8);
347 
348  nameUtf8 = PhConvertUtf16ToUtf8Ex(Name->Buffer, Name->Length);
349  mxmlElementSetAttr(objectNode, "name", nameUtf8->Buffer);
350  PhDereferenceObject(nameUtf8);
351 
352  priorityClassUtf8 = PhConvertUtf16ToUtf8Ex(PriorityClass->Buffer, PriorityClass->Length);
353  mxmlElementSetAttr(objectNode, "priorityclass", priorityClassUtf8->Buffer);
354  PhDereferenceObject(priorityClassUtf8);
355 
356  ioPriorityPlusOneUtf8 = PhConvertUtf16ToUtf8Ex(IoPriorityPlusOne->Buffer, IoPriorityPlusOne->Length);
357  mxmlElementSetAttr(objectNode, "iopriorityplusone", ioPriorityPlusOneUtf8->Buffer);
358  PhDereferenceObject(ioPriorityPlusOneUtf8);
359 
360  // Set the value.
361 
362  valueUtf8 = PhConvertUtf16ToUtf8Ex(Comment->Buffer, Comment->Length);
363  textNode = mxmlNewOpaque(objectNode, valueUtf8->Buffer);
364  PhDereferenceObject(valueUtf8);
365 
366  return objectNode;
367 }
368 
369 NTSTATUS SaveDb(
370  VOID
371  )
372 {
373  NTSTATUS status;
374  HANDLE fileHandle;
375  mxml_node_t *topNode;
376  ULONG enumerationKey = 0;
377  PDB_OBJECT *object;
378 
379  topNode = mxmlNewElement(MXML_NO_PARENT, "objects");
380 
381  LockDb();
382 
383  while (PhEnumHashtable(ObjectDb, (PVOID *)&object, &enumerationKey))
384  {
385  PPH_STRING tagString;
386  PPH_STRING priorityClassString;
387  PPH_STRING ioPriorityPlusOneString;
388 
389  tagString = PhIntegerToString64((*object)->Tag, 10, FALSE);
390  priorityClassString = PhIntegerToString64((*object)->PriorityClass, 10, FALSE);
391  ioPriorityPlusOneString = PhIntegerToString64((*object)->IoPriorityPlusOne, 10, FALSE);
392 
393  CreateObjectElement(topNode, &tagString->sr, &(*object)->Name->sr, &priorityClassString->sr, &ioPriorityPlusOneString->sr, &(*object)->Comment->sr);
394 
395  PhDereferenceObject(tagString);
396  PhDereferenceObject(priorityClassString);
397  PhDereferenceObject(ioPriorityPlusOneString);
398  }
399 
400  UnlockDb();
401 
402  // Create the directory if it does not exist.
403  {
404  PPH_STRING fullPath;
405  ULONG indexOfFileName;
406  PPH_STRING directoryName;
407 
408  fullPath = PhGetFullPath(ObjectDbPath->Buffer, &indexOfFileName);
409 
410  if (fullPath)
411  {
412  if (indexOfFileName != -1)
413  {
414  directoryName = PhSubstring(fullPath, 0, indexOfFileName);
415  SHCreateDirectoryEx(NULL, directoryName->Buffer, NULL);
416  PhDereferenceObject(directoryName);
417  }
418 
419  PhDereferenceObject(fullPath);
420  }
421  }
422 
423  status = PhCreateFileWin32(
424  &fileHandle,
425  ObjectDbPath->Buffer,
426  FILE_GENERIC_WRITE,
427  0,
428  FILE_SHARE_READ,
431  );
432 
433  if (!NT_SUCCESS(status))
434  {
435  mxmlDelete(topNode);
436  return status;
437  }
438 
439  mxmlSaveFd(topNode, fileHandle, MxmlSaveCallback);
440  mxmlDelete(topNode);
441  NtClose(fileHandle);
442 
443  return STATUS_SUCCESS;
444 }