[MKHIVE] Minor code formatting.
[reactos.git] / sdk / tools / mkhive / registry.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2006 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS hive maker
22 * FILE: tools/mkhive/registry.c
23 * PURPOSE: Registry code
24 * PROGRAMMERS: Hervé Poussineau
25 * Hermès Bélusca-Maïto
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #define NDEBUG
31 #include "mkhive.h"
32
33 /* DEFINITIONS AND DATA *****************************************************/
34
35 #define STATUS_NO_LOG_SPACE ((NTSTATUS)0xC000017D)
36 #define STATUS_CANNOT_DELETE ((NTSTATUS)0xC0000121)
37
38 typedef struct _REPARSE_POINT
39 {
40 LIST_ENTRY ListEntry;
41 PCMHIVE SourceHive;
42 HCELL_INDEX SourceKeyCellOffset;
43 PCMHIVE DestinationHive;
44 HCELL_INDEX DestinationKeyCellOffset;
45 } REPARSE_POINT, *PREPARSE_POINT;
46
47 typedef struct _MEMKEY
48 {
49 /* Information on hard disk structure */
50 HCELL_INDEX KeyCellOffset;
51 PCMHIVE RegistryHive;
52 } MEMKEY, *PMEMKEY;
53
54 #define HKEY_TO_MEMKEY(hKey) ((PMEMKEY)(hKey))
55 #define MEMKEY_TO_HKEY(memKey) ((HKEY)(memKey))
56
57 static CMHIVE RootHive;
58 static PMEMKEY RootKey;
59
60 static CMHIVE SystemHive; /* \Registry\Machine\SYSTEM */
61 static CMHIVE SoftwareHive; /* \Registry\Machine\SOFTWARE */
62 static CMHIVE DefaultHive; /* \Registry\User\.DEFAULT */
63 static CMHIVE SamHive; /* \Registry\Machine\SAM */
64 static CMHIVE SecurityHive; /* \Registry\Machine\SECURITY */
65 static CMHIVE BcdHive; /* \Registry\Machine\BCD00000000 */
66
67 //
68 // TODO: Write these values in a more human-readable form.
69 // See http://amnesia.gtisc.gatech.edu/~moyix/suzibandit.ltd.uk/MSc/Registry%20Structure%20-%20Appendices%20V4.pdf
70 // Appendix 12 "The Registry NT Security Descriptor" for more information.
71 //
72 // Those SECURITY_DESCRIPTORs were obtained by dumping the security block "sk"
73 // of registry hives created by setting their permissions to be the same as
74 // the ones of the BCD, SOFTWARE, or SYSTEM, SAM and .DEFAULT system hives.
75 // A cross-check was subsequently done with the system hives to verify that
76 // the security descriptors were the same.
77 //
78 static UCHAR BcdSecurity[] =
79 {
80 // SECURITY_DESCRIPTOR_RELATIVE
81 0x01, // Revision
82 0x00, // Sbz1
83 0x04, 0x94, // Control: SE_SELF_RELATIVE (0x8000) |
84 // SE_DACL_PROTECTED (0x1000) |
85 // SE_DACL_AUTO_INHERITED (0x0400) |
86 // SE_DACL_PRESENT (0x0004)
87 0x48, 0x00, 0x00, 0x00, // Owner
88 0x58, 0x00, 0x00, 0x00, // Group
89 0x00, 0x00, 0x00, 0x00, // Sacl (None)
90 0x14, 0x00, 0x00, 0x00, // Dacl
91
92 // DACL
93 0x02, // AclRevision
94 0x00, // Sbz1
95 0x34, 0x00, // AclSize
96 0x02, 0x00, // AceCount
97 0x00, 0x00, // Sbz2
98
99 // (1st ACE)
100 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
101 0x02, // AceFlags: CONTAINER_INHERIT_ACE
102 0x18, 0x00, // AceSize
103 0x19, 0x00, 0x06, 0x00, // ACCESS_MASK: "Write DAC" (0x00040000) |
104 // "Read Control" (0x00020000) |
105 // "Notify" (0x00000010) |
106 // "Enumerate Subkeys" (0x00000008) |
107 // "Query Value" (0x00000001)
108 // (SidStart: S-1-5-32-544 "Administrators")
109 0x01, 0x02, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x05,
111 0x20, 0x00, 0x00, 0x00,
112 0x20, 0x02, 0x00, 0x00,
113
114 // (2nd ACE)
115 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
116 0x02, // AceFlags: CONTAINER_INHERIT_ACE
117 0x14, 0x00, // AceSize
118 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
119 // (SidStart: S-1-5-18 "Local System")
120 0x01, 0x01, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x05,
122 0x12, 0x00, 0x00, 0x00,
123
124 // Owner SID (S-1-5-32-544 "Administrators")
125 0x01, 0x02, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x05,
127 0x20, 0x00, 0x00, 0x00,
128 0x20, 0x02, 0x00, 0x00,
129
130 // Group SID (S-1-5-21-domain-513 "Domain Users")
131 0x01, 0x05, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x05,
133 0x15, 0x00, 0x00, 0x00,
134 0xAC, 0xD0, 0x49, 0xCB,
135 0xE6, 0x52, 0x47, 0x9C,
136 0xE4, 0x31, 0xDB, 0x5C,
137 0x01, 0x02, 0x00, 0x00
138 };
139
140 static UCHAR SoftwareSecurity[] =
141 {
142 // SECURITY_DESCRIPTOR_RELATIVE
143 0x01, // Revision
144 0x00, // Sbz1
145 0x04, 0x94, // Control: SE_SELF_RELATIVE (0x8000) |
146 // SE_DACL_PROTECTED (0x1000) |
147 // SE_DACL_AUTO_INHERITED (0x0400) |
148 // SE_DACL_PRESENT (0x0004)
149 0xA0, 0x00, 0x00, 0x00, // Owner
150 0xB0, 0x00, 0x00, 0x00, // Group
151 0x00, 0x00, 0x00, 0x00, // Sacl (None)
152 0x14, 0x00, 0x00, 0x00, // Dacl
153
154 // DACL
155 0x02, // AclRevision
156 0x00, // Sbz1
157 0x8C, 0x00, // AclSize
158 0x06, 0x00, // AceCount
159 0x00, 0x00, // Sbz2
160
161 // (1st ACE)
162 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
163 0x02, // AceFlags: CONTAINER_INHERIT_ACE
164 0x18, 0x00, // AceSize
165 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
166 // (SidStart: S-1-5-32-544 "Administrators")
167 0x01, 0x02, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x05,
169 0x20, 0x00, 0x00, 0x00,
170 0x20, 0x02, 0x00, 0x00,
171
172 // (2nd ACE)
173 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
174 0x0A, // AceFlags: INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE
175 0x14, 0x00, // AceSize
176 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
177 // (SidStart: S-1-3-0 "Creator Owner")
178 0x01, 0x01, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x03,
180 0x00, 0x00, 0x00, 0x00,
181
182 // (3rd ACE)
183 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
184 0x02, // AceFlags: CONTAINER_INHERIT_ACE
185 0x14, 0x00, // AceSize
186 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
187 // (SidStart: S-1-5-18 "Local System")
188 0x01, 0x01, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x05,
190 0x12, 0x00, 0x00, 0x00,
191
192 // (4th ACE)
193 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
194 0x02, // AceFlags: CONTAINER_INHERIT_ACE
195 0x14, 0x00, // AceSize
196 0x1F, 0x00, 0x03, 0x00, // ACCESS_MASK: "Read Control" (0x00020000) |
197 // "Delete" (0x00010000) |
198 // "Notify" (0x00000010) |
199 // "Enumerate Subkeys" (0x00000008) |
200 // "Create Subkey" (0x00000004) |
201 // "Set Value" (0x00000002) |
202 // "Query Value" (0x00000001)
203 // (SidStart: S-1-5-13 "Terminal Server Users")
204 0x01, 0x01, 0x00, 0x00,
205 0x00, 0x00, 0x00, 0x05,
206 0x0D, 0x00, 0x00, 0x00,
207
208 // (5th ACE)
209 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
210 0x02, // AceFlags: CONTAINER_INHERIT_ACE
211 0x18, 0x00, // AceSize
212 0x19, 0x00, 0x02, 0x00, // ACCESS_MASK: "Read Control" (0x00020000) |
213 // "Notify" (0x00000010) |
214 // "Enumerate Subkeys" (0x00000008) |
215 // "Query Value" (0x00000001)
216 // (SidStart: S-1-5-32-545 "Users")
217 0x01, 0x02, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x05,
219 0x20, 0x00, 0x00, 0x00,
220 0x21, 0x02, 0x00, 0x00,
221
222 // (6th ACE)
223 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
224 0x02, // AceFlags: CONTAINER_INHERIT_ACE
225 0x18, 0x00, // AceSize
226 0x1F, 0x00, 0x03, 0x00, // ACCESS_MASK: "Read Control" (0x00020000) |
227 // "Delete" (0x00010000) |
228 // "Notify" (0x00000010) |
229 // "Enumerate Subkeys" (0x00000008) |
230 // "Create Subkey" (0x00000004) |
231 // "Set Value" (0x00000002) |
232 // "Query Value" (0x00000001)
233 // (SidStart: S-1-5-32-547 "Power Users")
234 0x01, 0x02, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x05,
236 0x20, 0x00, 0x00, 0x00,
237 0x23, 0x02, 0x00, 0x00,
238
239 // Owner SID (S-1-5-32-544 "Administrators")
240 0x01, 0x02, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x05,
242 0x20, 0x00, 0x00, 0x00,
243 0x20, 0x02, 0x00, 0x00,
244
245 // Group SID (S-1-5-21-domain-513 "Domain Users")
246 0x01, 0x05, 0x00, 0x00,
247 0x00, 0x00, 0x00, 0x05,
248 0x15, 0x00, 0x00, 0x00,
249 0xAC, 0xD0, 0x49, 0xCB,
250 0xE6, 0x52, 0x47, 0x9C,
251 0xE4, 0x31, 0xDB, 0x5C,
252 0x01, 0x02, 0x00, 0x00
253 };
254
255 // Same security for SYSTEM, SAM and .DEFAULT
256 static UCHAR SystemSecurity[] =
257 {
258 // SECURITY_DESCRIPTOR_RELATIVE
259 0x01, // Revision
260 0x00, // Sbz1
261 0x04, 0x94, // Control: SE_SELF_RELATIVE (0x8000) |
262 // SE_DACL_PROTECTED (0x1000) |
263 // SE_DACL_AUTO_INHERITED (0x0400) |
264 // SE_DACL_PRESENT (0x0004)
265 0x8C, 0x00, 0x00, 0x00, // Owner
266 0x9C, 0x00, 0x00, 0x00, // Group
267 0x00, 0x00, 0x00, 0x00, // Sacl (None)
268 0x14, 0x00, 0x00, 0x00, // Dacl
269
270 // DACL
271 0x02, // AclRevision
272 0x00, // Sbz1
273 0x78, 0x00, // AclSize
274 0x05, 0x00, // AceCount
275 0x00, 0x00, // Sbz2
276
277 // (1st ACE)
278 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
279 0x02, // AceFlags: CONTAINER_INHERIT_ACE
280 0x18, 0x00, // AceSize
281 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
282 // (SidStart: S-1-5-32-544 "Administrators")
283 0x01, 0x02, 0x00, 0x00,
284 0x00, 0x00, 0x00, 0x05,
285 0x20, 0x00, 0x00, 0x00,
286 0x20, 0x02, 0x00, 0x00,
287
288 // (2nd ACE)
289 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
290 0x0A, // AceFlags: INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE
291 0x14, 0x00, // AceSize
292 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
293 // (SidStart: S-1-3-0 "Creator Owner")
294 0x01, 0x01, 0x00, 0x00,
295 0x00, 0x00, 0x00, 0x03,
296 0x00, 0x00, 0x00, 0x00,
297
298 // (3rd ACE)
299 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
300 0x02, // AceFlags: CONTAINER_INHERIT_ACE
301 0x14, 0x00, // AceSize
302 0x3F, 0x00, 0x0F, 0x00, // ACCESS_MASK: "Full Control" (0x000F003F)
303 // (SidStart: S-1-5-18 "Local System")
304 0x01, 0x01, 0x00, 0x00,
305 0x00, 0x00, 0x00, 0x05,
306 0x12, 0x00, 0x00, 0x00,
307
308 // (4th ACE)
309 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
310 0x02, // AceFlags: CONTAINER_INHERIT_ACE
311 0x18, 0x00, // AceSize
312 0x19, 0x00, 0x02, 0x00, // ACCESS_MASK: "Read Control" (0x00020000) |
313 // "Notify" (0x00000010) |
314 // "Enumerate Subkeys" (0x00000008) |
315 // "Query Value" (0x00000001)
316 // (SidStart: S-1-5-32-545 "Users")
317 0x01, 0x02, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x05,
319 0x20, 0x00, 0x00, 0x00,
320 0x21, 0x02, 0x00, 0x00,
321
322 // (5th ACE)
323 0x00, // AceType : ACCESS_ALLOWED_ACE_TYPE
324 0x02, // AceFlags: CONTAINER_INHERIT_ACE
325 0x18, 0x00, // AceSize
326 0x19, 0x00, 0x02, 0x00, // ACCESS_MASK: "Read Control" (0x00020000) |
327 // "Notify" (0x00000010) |
328 // "Enumerate Subkeys" (0x00000008) |
329 // "Query Value" (0x00000001)
330 // (SidStart: S-1-5-32-547 "Power Users")
331 0x01, 0x02, 0x00, 0x00,
332 0x00, 0x00, 0x00, 0x05,
333 0x20, 0x00, 0x00, 0x00,
334 0x23, 0x02, 0x00, 0x00,
335
336 // Owner SID (S-1-5-32-544 "Administrators")
337 0x01, 0x02, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x05,
339 0x20, 0x00, 0x00, 0x00,
340 0x20, 0x02, 0x00, 0x00,
341
342 // Group SID (S-1-5-21-domain-513 "Domain Users")
343 0x01, 0x05, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x05,
345 0x15, 0x00, 0x00, 0x00,
346 0xAC, 0xD0, 0x49, 0xCB,
347 0xE6, 0x52, 0x47, 0x9C,
348 0xE4, 0x31, 0xDB, 0x5C,
349 0x01, 0x02, 0x00, 0x00
350 };
351
352 /* GLOBALS ******************************************************************/
353
354 HIVE_LIST_ENTRY RegistryHives[/*MAX_NUMBER_OF_REGISTRY_HIVES*/] =
355 {
356 /* Special Setup system registry hive */
357 // WARNING: Please *keep* it in first position!
358 { "SETUPREG", L"Registry\\Machine\\SYSTEM" , &SystemHive , SystemSecurity , sizeof(SystemSecurity) },
359
360 /* Regular registry hives */
361 { "SYSTEM" , L"Registry\\Machine\\SYSTEM" , &SystemHive , SystemSecurity , sizeof(SystemSecurity) },
362 { "SOFTWARE", L"Registry\\Machine\\SOFTWARE" , &SoftwareHive, SoftwareSecurity, sizeof(SoftwareSecurity) },
363 { "DEFAULT" , L"Registry\\User\\.DEFAULT" , &DefaultHive , SystemSecurity , sizeof(SystemSecurity) },
364 { "SAM" , L"Registry\\Machine\\SAM" , &SamHive , SystemSecurity , sizeof(SystemSecurity) },
365 { "SECURITY", L"Registry\\Machine\\SECURITY" , &SecurityHive, NULL , 0 },
366 { "BCD" , L"Registry\\Machine\\BCD00000000", &BcdHive , BcdSecurity , sizeof(BcdSecurity) },
367 };
368 C_ASSERT(_countof(RegistryHives) == MAX_NUMBER_OF_REGISTRY_HIVES);
369
370 /* FUNCTIONS ****************************************************************/
371
372 static PMEMKEY
373 CreateInMemoryStructure(
374 IN PCMHIVE RegistryHive,
375 IN HCELL_INDEX KeyCellOffset)
376 {
377 PMEMKEY Key;
378
379 Key = (PMEMKEY)malloc(sizeof(MEMKEY));
380 if (!Key)
381 return NULL;
382
383 Key->RegistryHive = RegistryHive;
384 Key->KeyCellOffset = KeyCellOffset;
385 return Key;
386 }
387
388 LIST_ENTRY CmiHiveListHead;
389 LIST_ENTRY CmiReparsePointsHead;
390
391 static LONG
392 RegpCreateOrOpenKey(
393 IN HKEY hParentKey,
394 IN PCWSTR KeyName,
395 IN BOOL AllowCreation,
396 IN BOOL Volatile,
397 OUT PHKEY Key)
398 {
399 PWSTR LocalKeyName;
400 PWSTR End;
401 UNICODE_STRING KeyString;
402 NTSTATUS Status;
403 PREPARSE_POINT CurrentReparsePoint;
404 PMEMKEY CurrentKey;
405 PCMHIVE ParentRegistryHive;
406 HCELL_INDEX ParentCellOffset;
407 PCM_KEY_NODE ParentKeyCell;
408 PLIST_ENTRY Ptr;
409 PCM_KEY_NODE SubKeyCell;
410 HCELL_INDEX BlockOffset;
411
412 DPRINT("RegpCreateOrOpenKey('%S')\n", KeyName);
413
414 if (*KeyName == OBJ_NAME_PATH_SEPARATOR)
415 {
416 KeyName++;
417 ParentRegistryHive = RootKey->RegistryHive;
418 ParentCellOffset = RootKey->KeyCellOffset;
419 }
420 else if (hParentKey == NULL)
421 {
422 ParentRegistryHive = RootKey->RegistryHive;
423 ParentCellOffset = RootKey->KeyCellOffset;
424 }
425 else
426 {
427 ParentRegistryHive = HKEY_TO_MEMKEY(hParentKey)->RegistryHive;
428 ParentCellOffset = HKEY_TO_MEMKEY(hParentKey)->KeyCellOffset;
429 }
430
431 LocalKeyName = (PWSTR)KeyName;
432 for (;;)
433 {
434 End = (PWSTR)strchrW(LocalKeyName, OBJ_NAME_PATH_SEPARATOR);
435 if (End)
436 {
437 KeyString.Buffer = LocalKeyName;
438 KeyString.Length = KeyString.MaximumLength =
439 (USHORT)((ULONG_PTR)End - (ULONG_PTR)LocalKeyName);
440 }
441 else
442 {
443 RtlInitUnicodeString(&KeyString, LocalKeyName);
444 if (KeyString.Length == 0)
445 {
446 /* Trailing path separator: we're done */
447 break;
448 }
449 }
450
451 ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&ParentRegistryHive->Hive, ParentCellOffset);
452 if (!ParentKeyCell)
453 return STATUS_UNSUCCESSFUL;
454
455 VERIFY_KEY_CELL(ParentKeyCell);
456
457 BlockOffset = CmpFindSubKeyByName(&ParentRegistryHive->Hive, ParentKeyCell, &KeyString);
458 if (BlockOffset != HCELL_NIL)
459 {
460 Status = STATUS_SUCCESS;
461
462 /* Search for a possible reparse point */
463 Ptr = CmiReparsePointsHead.Flink;
464 while (Ptr != &CmiReparsePointsHead)
465 {
466 CurrentReparsePoint = CONTAINING_RECORD(Ptr, REPARSE_POINT, ListEntry);
467 if (CurrentReparsePoint->SourceHive == ParentRegistryHive &&
468 CurrentReparsePoint->SourceKeyCellOffset == BlockOffset)
469 {
470 ParentRegistryHive = CurrentReparsePoint->DestinationHive;
471 BlockOffset = CurrentReparsePoint->DestinationKeyCellOffset;
472 break;
473 }
474 Ptr = Ptr->Flink;
475 }
476 }
477 else if (AllowCreation) // && (BlockOffset == HCELL_NIL)
478 {
479 Status = CmiAddSubKey(ParentRegistryHive,
480 ParentCellOffset,
481 &KeyString,
482 Volatile,
483 &BlockOffset);
484 }
485 else // if (BlockOffset == HCELL_NIL)
486 {
487 Status = STATUS_OBJECT_NAME_NOT_FOUND; // ERROR_PATH_NOT_FOUND;
488 }
489
490 HvReleaseCell(&ParentRegistryHive->Hive, ParentCellOffset);
491
492 if (!NT_SUCCESS(Status))
493 {
494 DPRINT("RegpCreateOrOpenKey('%S'): Could not create or open subkey '%wZ'\n", KeyName, &KeyString);
495 return ERROR_UNSUCCESSFUL;
496 }
497
498 ParentCellOffset = BlockOffset;
499 if (End)
500 LocalKeyName = End + 1;
501 else
502 break;
503 }
504
505 CurrentKey = CreateInMemoryStructure(ParentRegistryHive, ParentCellOffset);
506 if (!CurrentKey)
507 return ERROR_OUTOFMEMORY;
508
509 *Key = MEMKEY_TO_HKEY(CurrentKey);
510
511 return ERROR_SUCCESS;
512 }
513
514 LONG WINAPI
515 RegCloseKey(
516 IN HKEY hKey)
517 {
518 PMEMKEY Key = HKEY_TO_MEMKEY(hKey); // ParentKey
519
520 /* Free the object */
521 free(Key);
522
523 return ERROR_SUCCESS;
524 }
525
526 LONG WINAPI
527 RegCreateKeyW(
528 IN HKEY hKey,
529 IN LPCWSTR lpSubKey,
530 OUT PHKEY phkResult)
531 {
532 return RegpCreateOrOpenKey(hKey, lpSubKey, TRUE, FALSE, phkResult);
533 }
534
535 LONG WINAPI
536 RegCreateKeyExW(
537 IN HKEY hKey,
538 IN LPCWSTR lpSubKey,
539 IN DWORD Reserved,
540 IN LPWSTR lpClass OPTIONAL,
541 IN DWORD dwOptions,
542 IN REGSAM samDesired,
543 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes OPTIONAL,
544 OUT PHKEY phkResult,
545 OUT LPDWORD lpdwDisposition OPTIONAL)
546 {
547 return RegpCreateOrOpenKey(hKey,
548 lpSubKey,
549 TRUE,
550 (dwOptions & REG_OPTION_VOLATILE) != 0,
551 phkResult);
552 }
553
554 LONG WINAPI
555 RegDeleteKeyW(
556 IN HKEY hKey,
557 IN LPCWSTR lpSubKey)
558 {
559 LONG rc;
560 HKEY hTargetKey;
561 PMEMKEY Key; // ParentKey
562 PHHIVE Hive;
563 PCM_KEY_NODE KeyNode; // ParentNode
564 PCM_KEY_NODE Parent;
565 HCELL_INDEX ParentCell;
566
567 NTSTATUS Status;
568
569 if (lpSubKey)
570 {
571 rc = RegOpenKeyW(hKey, lpSubKey, &hTargetKey);
572 if (rc != ERROR_SUCCESS)
573 return rc;
574 }
575 else
576 {
577 hTargetKey = hKey;
578 }
579
580 /* Don't allow deleting the root */
581 if (hTargetKey == RootKey)
582 {
583 /* Fail */
584 Status = STATUS_CANNOT_DELETE;
585 goto Quit;
586 }
587
588 /* Get the hive and node */
589 Key = HKEY_TO_MEMKEY(hTargetKey);
590 Hive = &Key->RegistryHive->Hive;
591
592 /* Get the key node */
593 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Key->KeyCellOffset);
594 if (!KeyNode)
595 {
596 Status = ERROR_UNSUCCESSFUL;
597 goto Quit;
598 }
599
600 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
601
602 /* Check if we don't have any children */
603 if (!(KeyNode->SubKeyCounts[Stable] + KeyNode->SubKeyCounts[Volatile]) &&
604 !(KeyNode->Flags & KEY_NO_DELETE))
605 {
606 /* Get the parent and free the cell */
607 ParentCell = KeyNode->Parent;
608 Status = CmpFreeKeyByCell(Hive, Key->KeyCellOffset, TRUE);
609 if (NT_SUCCESS(Status))
610 {
611 /* Get the parent node */
612 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
613 if (Parent)
614 {
615 /* Make sure we're dirty */
616 ASSERT(HvIsCellDirty(Hive, ParentCell));
617
618 /* Update the write time */
619 KeQuerySystemTime(&Parent->LastWriteTime);
620
621 /* Release the cell */
622 HvReleaseCell(Hive, ParentCell);
623 }
624 }
625 }
626 else
627 {
628 /* Fail */
629 Status = STATUS_CANNOT_DELETE;
630 }
631
632 /* Release the cell */
633 HvReleaseCell(Hive, Key->KeyCellOffset);
634
635 Quit:
636 if (lpSubKey)
637 RegCloseKey(hTargetKey);
638
639 return Status;
640 }
641
642 LONG WINAPI
643 RegOpenKeyW(
644 IN HKEY hKey,
645 IN LPCWSTR lpSubKey,
646 OUT PHKEY phkResult)
647 {
648 return RegpCreateOrOpenKey(hKey, lpSubKey, FALSE, FALSE, phkResult);
649 }
650
651 LONG WINAPI
652 RegSetValueExW(
653 IN HKEY hKey,
654 IN LPCWSTR lpValueName OPTIONAL,
655 IN ULONG Reserved,
656 IN ULONG dwType,
657 IN const UCHAR* lpData,
658 IN ULONG cbData)
659 {
660 PMEMKEY Key = HKEY_TO_MEMKEY(hKey); // ParentKey
661 PHHIVE Hive;
662 PCM_KEY_NODE KeyNode; // ParentNode
663 PCM_KEY_VALUE ValueCell;
664 ULONG ChildIndex;
665 HCELL_INDEX CellIndex;
666 UNICODE_STRING ValueNameString;
667
668 PVOID DataCell;
669 ULONG DataCellSize;
670 NTSTATUS Status;
671
672 if (dwType == REG_LINK)
673 {
674 PMEMKEY DestKey;
675
676 /* Special handling of registry links */
677 if (cbData != sizeof(PVOID))
678 return STATUS_INVALID_PARAMETER;
679
680 DestKey = HKEY_TO_MEMKEY(*(PHKEY)lpData);
681
682 // FIXME: Add additional checks for the validity of DestKey
683
684 /* Create the link in registry hive (if applicable) */
685 if (Key->RegistryHive != DestKey->RegistryHive)
686 return STATUS_SUCCESS;
687
688 DPRINT1("Save link to registry\n");
689 return STATUS_NOT_IMPLEMENTED;
690 }
691
692 if ((cbData & ~CM_KEY_VALUE_SPECIAL_SIZE) != cbData)
693 return STATUS_UNSUCCESSFUL;
694
695 Hive = &Key->RegistryHive->Hive;
696
697 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Key->KeyCellOffset);
698 if (!KeyNode)
699 return ERROR_UNSUCCESSFUL;
700
701 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
702
703 /* Mark the parent as dirty since we are going to create a new value in it */
704 HvMarkCellDirty(Hive, Key->KeyCellOffset, FALSE);
705
706 /* Initialize value name string */
707 RtlInitUnicodeString(&ValueNameString, lpValueName);
708 if (!CmpFindNameInList(Hive,
709 &KeyNode->ValueList,
710 &ValueNameString,
711 &ChildIndex,
712 &CellIndex))
713 {
714 /* Sanity check */
715 ASSERT(CellIndex == HCELL_NIL);
716 /* Fail */
717 // Status = STATUS_INSUFFICIENT_RESOURCES;
718 return ERROR_UNSUCCESSFUL;
719 }
720 if (CellIndex == HCELL_NIL)
721 {
722 /* The value doesn't exist, create a new one */
723 Status = CmiAddValueKey(Key->RegistryHive,
724 KeyNode,
725 ChildIndex,
726 &ValueNameString,
727 &ValueCell,
728 &CellIndex);
729 }
730 else
731 {
732 /* The value already exists, use it. Get the value cell. */
733 ValueCell = HvGetCell(&Key->RegistryHive->Hive, CellIndex);
734 ASSERT(ValueCell != NULL);
735 Status = STATUS_SUCCESS;
736 }
737
738 // /**/HvReleaseCell(Hive, CellIndex);/**/
739
740 if (!NT_SUCCESS(Status))
741 return ERROR_UNSUCCESSFUL;
742
743 /* Get size of the allocated cell (if any) */
744 if (!(ValueCell->DataLength & CM_KEY_VALUE_SPECIAL_SIZE) &&
745 (ValueCell->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE) != 0)
746 {
747 DataCell = HvGetCell(Hive, ValueCell->Data);
748 if (!DataCell)
749 return ERROR_UNSUCCESSFUL;
750
751 DataCellSize = (ULONG)(-HvGetCellSize(Hive, DataCell));
752 }
753 else
754 {
755 DataCell = NULL;
756 DataCellSize = 0;
757 }
758
759 if (cbData <= sizeof(HCELL_INDEX))
760 {
761 /* If data size <= sizeof(HCELL_INDEX) then store data in the data offset */
762 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
763 if (DataCell)
764 HvFreeCell(Hive, ValueCell->Data);
765
766 RtlCopyMemory(&ValueCell->Data, lpData, cbData);
767 ValueCell->DataLength = (cbData | CM_KEY_VALUE_SPECIAL_SIZE);
768 ValueCell->Type = dwType;
769 }
770 else
771 {
772 if (cbData > DataCellSize)
773 {
774 /* New data size is larger than the current, destroy current
775 * data block and allocate a new one. */
776 HCELL_INDEX NewOffset;
777
778 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
779
780 NewOffset = HvAllocateCell(Hive, cbData, Stable, HCELL_NIL);
781 if (NewOffset == HCELL_NIL)
782 {
783 DPRINT("HvAllocateCell() has failed!\n");
784 return ERROR_UNSUCCESSFUL;
785 }
786
787 if (DataCell)
788 HvFreeCell(Hive, ValueCell->Data);
789
790 ValueCell->Data = NewOffset;
791 DataCell = (PVOID)HvGetCell(Hive, NewOffset);
792 }
793
794 /* Copy new contents to cell */
795 RtlCopyMemory(DataCell, lpData, cbData);
796 ValueCell->DataLength = (cbData & ~CM_KEY_VALUE_SPECIAL_SIZE);
797 ValueCell->Type = dwType;
798 HvMarkCellDirty(Hive, ValueCell->Data, FALSE);
799 }
800
801 HvMarkCellDirty(Hive, CellIndex, FALSE);
802
803 /* Check if the maximum value name length changed, update it if so */
804 if (KeyNode->MaxValueNameLen < ValueNameString.Length)
805 KeyNode->MaxValueNameLen = ValueNameString.Length;
806
807 /* Check if the maximum data length changed, update it if so */
808 if (KeyNode->MaxValueDataLen < cbData)
809 KeyNode->MaxValueDataLen = cbData;
810
811 /* Save the write time */
812 KeQuerySystemTime(&KeyNode->LastWriteTime);
813
814 return ERROR_SUCCESS;
815 }
816
817
818 // Synced with freeldr/ntldr/registry.c
819 static
820 VOID
821 RepGetValueData(
822 IN PHHIVE Hive,
823 IN PCM_KEY_VALUE ValueCell,
824 OUT PULONG Type OPTIONAL,
825 OUT PUCHAR Data OPTIONAL,
826 IN OUT PULONG DataSize OPTIONAL)
827 {
828 ULONG DataLength;
829 PVOID DataCell;
830
831 /* Does the caller want the type? */
832 if (Type != NULL)
833 *Type = ValueCell->Type;
834
835 /* Does the caller provide DataSize? */
836 if (DataSize != NULL)
837 {
838 // NOTE: CmpValueToData doesn't support big data (the function will
839 // bugcheck if so), FreeLdr is not supposed to read such data.
840 // If big data is needed, use instead CmpGetValueData.
841 // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...);
842 DataCell = CmpValueToData(Hive, ValueCell, &DataLength);
843
844 /* Does the caller want the data? */
845 if ((Data != NULL) && (*DataSize != 0))
846 {
847 RtlCopyMemory(Data,
848 DataCell,
849 min(*DataSize, DataLength));
850 }
851
852 /* Return the actual data length */
853 *DataSize = DataLength;
854 }
855 }
856
857 // Similar to RegQueryValue in freeldr/ntldr/registry.c
858 LONG WINAPI
859 RegQueryValueExW(
860 IN HKEY hKey,
861 IN LPCWSTR lpValueName,
862 IN PULONG lpReserved,
863 OUT PULONG lpType OPTIONAL,
864 OUT PUCHAR lpData OPTIONAL,
865 IN OUT PULONG lpcbData OPTIONAL)
866 {
867 PMEMKEY ParentKey = HKEY_TO_MEMKEY(hKey);
868 PHHIVE Hive = &ParentKey->RegistryHive->Hive;
869 PCM_KEY_NODE KeyNode;
870 PCM_KEY_VALUE ValueCell;
871 HCELL_INDEX CellIndex;
872 UNICODE_STRING ValueNameString;
873
874 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey->KeyCellOffset);
875 if (!KeyNode)
876 return ERROR_UNSUCCESSFUL;
877
878 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
879
880 /* Initialize value name string */
881 RtlInitUnicodeString(&ValueNameString, lpValueName);
882 CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString);
883 if (CellIndex == HCELL_NIL)
884 return ERROR_FILE_NOT_FOUND;
885
886 /* Get the value cell */
887 ValueCell = HvGetCell(Hive, CellIndex);
888 ASSERT(ValueCell != NULL);
889
890 RepGetValueData(Hive, ValueCell, lpType, lpData, lpcbData);
891
892 HvReleaseCell(Hive, CellIndex);
893
894 return ERROR_SUCCESS;
895 }
896
897 LONG WINAPI
898 RegDeleteValueW(
899 IN HKEY hKey,
900 IN LPCWSTR lpValueName OPTIONAL)
901 {
902 PMEMKEY Key = HKEY_TO_MEMKEY(hKey); // ParentKey
903 PHHIVE Hive = &Key->RegistryHive->Hive;
904 PCM_KEY_NODE KeyNode; // ParentNode
905 PCM_KEY_VALUE ValueCell;
906 HCELL_INDEX CellIndex;
907 ULONG ChildIndex;
908 UNICODE_STRING ValueNameString;
909
910 NTSTATUS Status;
911
912 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Key->KeyCellOffset);
913 if (!KeyNode)
914 return ERROR_UNSUCCESSFUL;
915
916 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
917
918 /* Initialize value name string */
919 RtlInitUnicodeString(&ValueNameString, lpValueName);
920 if (!CmpFindNameInList(Hive,
921 &KeyNode->ValueList,
922 &ValueNameString,
923 &ChildIndex,
924 &CellIndex))
925 {
926 /* Sanity check */
927 ASSERT(CellIndex == HCELL_NIL);
928 }
929 if (CellIndex == HCELL_NIL)
930 {
931 Status = ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
932 goto Quit;
933 }
934
935 /* We found the value, mark all relevant cells dirty */
936 HvMarkCellDirty(Hive, Key->KeyCellOffset, FALSE);
937 HvMarkCellDirty(Hive, KeyNode->ValueList.List, FALSE);
938 HvMarkCellDirty(Hive, CellIndex, FALSE);
939
940 /* Get the key value */
941 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
942 ASSERT(ValueCell);
943
944 /* Mark it and all related data as dirty */
945 if (!CmpMarkValueDataDirty(Hive, ValueCell))
946 {
947 /* Not enough log space, fail */
948 Status = STATUS_NO_LOG_SPACE;
949 goto Quit;
950 }
951
952 /* Sanity checks */
953 ASSERT(HvIsCellDirty(Hive, KeyNode->ValueList.List));
954 ASSERT(HvIsCellDirty(Hive, CellIndex));
955
956 /* Remove the value from the child list */
957 Status = CmpRemoveValueFromList(Hive, ChildIndex, &KeyNode->ValueList);
958 if (!NT_SUCCESS(Status))
959 {
960 /* Set known error */
961 Status = STATUS_INSUFFICIENT_RESOURCES;
962 goto Quit;
963 }
964
965 /* Remove the value and its data itself */
966 if (!CmpFreeValue(Hive, CellIndex))
967 {
968 /* Failed to free the value, fail */
969 Status = STATUS_INSUFFICIENT_RESOURCES;
970 goto Quit;
971 }
972
973 /* Set the last write time */
974 KeQuerySystemTime(&KeyNode->LastWriteTime);
975
976 /* Sanity check */
977 ASSERT(HvIsCellDirty(Hive, Key->KeyCellOffset));
978
979 /* Check if the value list is empty now */
980 if (!KeyNode->ValueList.Count)
981 {
982 /* Then clear key node data */
983 KeyNode->MaxValueNameLen = 0;
984 KeyNode->MaxValueDataLen = 0;
985 }
986
987 /* Change default Status to success */
988 Status = STATUS_SUCCESS;
989
990 Quit:
991 /* Check if we had a value */
992 if (ValueCell)
993 {
994 /* Release the child cell */
995 ASSERT(CellIndex != HCELL_NIL);
996 HvReleaseCell(Hive, CellIndex);
997 }
998
999 /* Release the parent cell, if any */
1000 if (KeyNode)
1001 HvReleaseCell(Hive, Key->KeyCellOffset);
1002
1003 return Status;
1004 }
1005
1006
1007 static BOOL
1008 ConnectRegistry(
1009 IN HKEY RootKey,
1010 IN PCMHIVE HiveToConnect,
1011 IN PUCHAR SecurityDescriptor,
1012 IN ULONG SecurityDescriptorLength,
1013 IN PCWSTR Path)
1014 {
1015 NTSTATUS Status;
1016 LONG rc;
1017 PREPARSE_POINT ReparsePoint;
1018 PMEMKEY NewKey;
1019
1020 ReparsePoint = (PREPARSE_POINT)malloc(sizeof(*ReparsePoint));
1021 if (!ReparsePoint)
1022 return FALSE;
1023
1024 /*
1025 * Use a dummy root key name:
1026 * - On 2k/XP/2k3, this is "$$$PROTO.HIV"
1027 * - On Vista+, this is "CMI-CreateHive{guid}"
1028 * See https://github.com/libyal/winreg-kb/blob/master/documentation/Registry%20files.asciidoc
1029 * for more information.
1030 */
1031 Status = CmiInitializeHive(HiveToConnect, L"$$$PROTO.HIV");
1032 if (!NT_SUCCESS(Status))
1033 {
1034 DPRINT1("CmiInitializeHive() failed with status 0x%08x\n", Status);
1035 free(ReparsePoint);
1036 return FALSE;
1037 }
1038
1039 /*
1040 * Add security to the root key.
1041 * NOTE: One can implement this using the lpSecurityAttributes
1042 * parameter of RegCreateKeyExW.
1043 */
1044 Status = CmiCreateSecurityKey(&HiveToConnect->Hive,
1045 HiveToConnect->Hive.BaseBlock->RootCell,
1046 SecurityDescriptor, SecurityDescriptorLength);
1047 if (!NT_SUCCESS(Status))
1048 DPRINT1("Failed to add security for root key '%S'\n", Path);
1049
1050 /* Create the key */
1051 rc = RegCreateKeyExW(RootKey,
1052 Path,
1053 0,
1054 NULL,
1055 REG_OPTION_VOLATILE,
1056 0,
1057 NULL,
1058 (PHKEY)&NewKey,
1059 NULL);
1060 if (rc != ERROR_SUCCESS)
1061 {
1062 free(ReparsePoint);
1063 return FALSE;
1064 }
1065
1066 ReparsePoint->SourceHive = NewKey->RegistryHive;
1067 ReparsePoint->SourceKeyCellOffset = NewKey->KeyCellOffset;
1068 NewKey->RegistryHive = HiveToConnect;
1069 NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell;
1070 ReparsePoint->DestinationHive = NewKey->RegistryHive;
1071 ReparsePoint->DestinationKeyCellOffset = NewKey->KeyCellOffset;
1072 InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
1073
1074 return TRUE;
1075 }
1076
1077 static BOOL
1078 CreateSymLink(
1079 IN PCWSTR LinkKeyPath OPTIONAL,
1080 IN OUT PHKEY LinkKeyHandle OPTIONAL,
1081 // IN PCWSTR TargetKeyPath OPTIONAL,
1082 IN HKEY TargetKeyHandle)
1083 {
1084 LONG rc;
1085 PMEMKEY LinkKey, TargetKey;
1086 PREPARSE_POINT ReparsePoint;
1087
1088 ReparsePoint = (PREPARSE_POINT)malloc(sizeof(*ReparsePoint));
1089 if (!ReparsePoint)
1090 return FALSE;
1091
1092 if (LinkKeyPath && !(LinkKeyHandle && *LinkKeyHandle))
1093 {
1094 /* Create the link key */
1095 rc = RegCreateKeyExW(NULL,
1096 LinkKeyPath,
1097 0,
1098 NULL,
1099 REG_OPTION_VOLATILE,
1100 0,
1101 NULL,
1102 (PHKEY)&LinkKey,
1103 NULL);
1104 if (rc != ERROR_SUCCESS)
1105 {
1106 free(ReparsePoint);
1107 return FALSE;
1108 }
1109 }
1110 else if (LinkKeyHandle)
1111 {
1112 /* Use the user-provided link key handle */
1113 LinkKey = HKEY_TO_MEMKEY(*LinkKeyHandle);
1114 }
1115
1116 if (LinkKeyHandle)
1117 *LinkKeyHandle = MEMKEY_TO_HKEY(LinkKey);
1118
1119 TargetKey = HKEY_TO_MEMKEY(TargetKeyHandle);
1120
1121 ReparsePoint->SourceHive = LinkKey->RegistryHive;
1122 ReparsePoint->SourceKeyCellOffset = LinkKey->KeyCellOffset;
1123 ReparsePoint->DestinationHive = TargetKey->RegistryHive;
1124 ReparsePoint->DestinationKeyCellOffset = TargetKey->KeyCellOffset;
1125 InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
1126
1127 return TRUE;
1128 }
1129
1130 VOID
1131 RegInitializeRegistry(
1132 IN PCSTR HiveList)
1133 {
1134 NTSTATUS Status;
1135 UINT i;
1136 HKEY ControlSetKey;
1137
1138 InitializeListHead(&CmiHiveListHead);
1139 InitializeListHead(&CmiReparsePointsHead);
1140
1141 Status = CmiInitializeHive(&RootHive, L"");
1142 if (!NT_SUCCESS(Status))
1143 {
1144 DPRINT1("CmiInitializeHive() failed with status 0x%08x\n", Status);
1145 return;
1146 }
1147
1148 RootKey = CreateInMemoryStructure(&RootHive,
1149 RootHive.Hive.BaseBlock->RootCell);
1150
1151 for (i = 0; i < _countof(RegistryHives); ++i)
1152 {
1153 /* Skip this registry hive if it's not in the list */
1154 if (!strstr(HiveList, RegistryHives[i].HiveName))
1155 continue;
1156
1157 /* Create the registry key */
1158 ConnectRegistry(NULL,
1159 RegistryHives[i].CmHive,
1160 RegistryHives[i].SecurityDescriptor,
1161 RegistryHives[i].SecurityDescriptorLength,
1162 RegistryHives[i].HiveRegistryPath);
1163
1164 /* If we happen to deal with the special setup registry hive, stop there */
1165 // if (strcmp(RegistryHives[i].HiveName, "SETUPREG") == 0)
1166 if (i == 0)
1167 break;
1168 }
1169
1170 /* Create the 'ControlSet001' key */
1171 RegCreateKeyW(NULL,
1172 L"Registry\\Machine\\SYSTEM\\ControlSet001",
1173 &ControlSetKey);
1174
1175 /* Create the 'CurrentControlSet' key as a symlink to 'ControlSet001' */
1176 CreateSymLink(L"Registry\\Machine\\SYSTEM\\CurrentControlSet",
1177 NULL, ControlSetKey);
1178
1179 RegCloseKey(ControlSetKey);
1180
1181 #if 0
1182 /* Link SECURITY to SAM */
1183 CmpLinkKeyToHive(L"\\Registry\\Machine\\Security\\SAM", L"\\Registry\\Machine\\SAM\\SAM");
1184 /* Link S-1-5-18 to .Default */
1185 CmpLinkKeyToHive(L"\\Registry\\User\\S-1-5-18", L"\\Registry\\User\\.Default");
1186 #endif
1187 }
1188
1189 VOID
1190 RegShutdownRegistry(VOID)
1191 {
1192 PLIST_ENTRY Entry;
1193 PREPARSE_POINT ReparsePoint;
1194
1195 /* Clean up the reparse points list */
1196 while (!IsListEmpty(&CmiReparsePointsHead))
1197 {
1198 Entry = RemoveHeadList(&CmiReparsePointsHead);
1199 ReparsePoint = CONTAINING_RECORD(Entry, REPARSE_POINT, ListEntry);
1200 free(ReparsePoint);
1201 }
1202
1203 /* FIXME: clean up the complete hive */
1204
1205 free(RootKey);
1206 }
1207
1208 /* EOF */