[MKHIVE] Misc fixes:
[reactos.git] / reactos / 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 /* COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS hive maker
21 * FILE: tools/mkhive/registry.c
22 * PURPOSE: Registry code
23 * PROGRAMMER: Hervé Poussineau
24 */
25
26 /*
27 * TODO:
28 * - Implement RegDeleteKeyW()
29 * - Implement RegEnumValue()
30 * - Implement RegQueryValueExW()
31 */
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdio.h>
36
37 #define NDEBUG
38 #include "mkhive.h"
39
40 #define REG_DATA_SIZE_MASK 0x7FFFFFFF
41 #define REG_DATA_IN_OFFSET 0x80000000
42
43 static CMHIVE RootHive;
44 static MEMKEY RootKey;
45 CMHIVE DefaultHive; /* \Registry\User\.DEFAULT */
46 CMHIVE SamHive; /* \Registry\Machine\SAM */
47 CMHIVE SecurityHive; /* \Registry\Machine\SECURITY */
48 CMHIVE SoftwareHive; /* \Registry\Machine\SOFTWARE */
49 CMHIVE SystemHive; /* \Registry\Machine\SYSTEM */
50
51 static MEMKEY
52 CreateInMemoryStructure(
53 IN PCMHIVE RegistryHive,
54 IN HCELL_INDEX KeyCellOffset,
55 IN PCUNICODE_STRING KeyName)
56 {
57 MEMKEY Key;
58
59 Key = (MEMKEY) malloc (sizeof(KEY));
60 if (!Key)
61 return NULL;
62
63 InitializeListHead (&Key->SubKeyList);
64 InitializeListHead (&Key->ValueList);
65 InitializeListHead (&Key->KeyList);
66
67 Key->SubKeyCount = 0;
68 Key->ValueCount = 0;
69
70 Key->NameSize = KeyName->Length;
71 /* FIXME: It's not enough to allocate this way, because later
72 this memory gets overwritten with bigger names */
73 Key->Name = malloc (Key->NameSize);
74 if (!Key->Name)
75 return NULL;
76 memcpy(Key->Name, KeyName->Buffer, KeyName->Length);
77
78 Key->DataType = 0;
79 Key->DataSize = 0;
80 Key->Data = NULL;
81
82 Key->RegistryHive = RegistryHive;
83 Key->KeyCellOffset = KeyCellOffset;
84 Key->KeyCell = (PCM_KEY_NODE)HvGetCell (&RegistryHive->Hive, Key->KeyCellOffset);
85 if (!Key->KeyCell)
86 {
87 free(Key->Name);
88 free(Key);
89 return NULL;
90 }
91 Key->KeyCell->SubKeyLists[Stable] = HCELL_NIL;
92 Key->KeyCell->SubKeyLists[Volatile] = HCELL_NIL;
93 Key->LinkedKey = NULL;
94 return Key;
95 }
96
97 static LONG
98 RegpOpenOrCreateKey(
99 IN HKEY hParentKey,
100 IN PCWSTR KeyName,
101 IN BOOL AllowCreation,
102 OUT PHKEY Key)
103 {
104 PWSTR LocalKeyName;
105 PWSTR End;
106 UNICODE_STRING KeyString;
107 NTSTATUS Status;
108 MEMKEY ParentKey;
109 MEMKEY CurrentKey;
110 PLIST_ENTRY Ptr;
111 PCM_KEY_NODE SubKeyCell;
112 HCELL_INDEX BlockOffset;
113
114 DPRINT("RegpCreateOpenKey('%S')\n", KeyName);
115
116 if (*KeyName == L'\\')
117 {
118 KeyName++;
119 ParentKey = RootKey;
120 }
121 else if (hParentKey == NULL)
122 {
123 ParentKey = RootKey;
124 }
125 else
126 {
127 ParentKey = HKEY_TO_MEMKEY(RootKey);
128 }
129
130 LocalKeyName = (PWSTR)KeyName;
131 for (;;)
132 {
133 End = (PWSTR)strchrW(LocalKeyName, '\\');
134 if (End)
135 {
136 KeyString.Buffer = LocalKeyName;
137 KeyString.Length = KeyString.MaximumLength =
138 (USHORT)((ULONG_PTR)End - (ULONG_PTR)LocalKeyName);
139 }
140 else
141 {
142 RtlInitUnicodeString(&KeyString, LocalKeyName);
143 if (KeyString.Length == 0)
144 {
145 /* Trailing backslash char; we're done */
146 break;
147 }
148 }
149
150 /* Redirect from 'CurrentControlSet' to 'ControlSet001' */
151 if (!strncmpW(LocalKeyName, L"CurrentControlSet", 17) &&
152 ParentKey->NameSize == 12 &&
153 !memcmp(ParentKey->Name, L"SYSTEM", 12))
154 RtlInitUnicodeString(&KeyString, L"ControlSet001");
155
156 /* Check subkey in memory structure */
157 Ptr = ParentKey->SubKeyList.Flink;
158 while (Ptr != &ParentKey->SubKeyList)
159 {
160 CurrentKey = CONTAINING_RECORD(Ptr, KEY, KeyList);
161 if (CurrentKey->NameSize == KeyString.Length
162 && memcmp(CurrentKey->Name, KeyString.Buffer, KeyString.Length) == 0)
163 {
164 goto nextsubkey;
165 }
166
167 Ptr = Ptr->Flink;
168 }
169
170 Status = CmiScanForSubKey(
171 ParentKey->RegistryHive,
172 ParentKey->KeyCell,
173 &KeyString,
174 OBJ_CASE_INSENSITIVE,
175 &SubKeyCell,
176 &BlockOffset);
177 if (AllowCreation && Status == STATUS_OBJECT_NAME_NOT_FOUND)
178 {
179 Status = CmiAddSubKey(
180 ParentKey->RegistryHive,
181 ParentKey->KeyCell,
182 ParentKey->KeyCellOffset,
183 &KeyString,
184 0,
185 &SubKeyCell,
186 &BlockOffset);
187 }
188 if (!NT_SUCCESS(Status))
189 return ERROR_UNSUCCESSFUL;
190
191 /* Now, SubKeyCell/BlockOffset are valid */
192 CurrentKey = CreateInMemoryStructure(
193 ParentKey->RegistryHive,
194 BlockOffset,
195 &KeyString);
196 if (!CurrentKey)
197 return ERROR_OUTOFMEMORY;
198
199 /* Add CurrentKey in ParentKey */
200 InsertTailList(&ParentKey->SubKeyList, &CurrentKey->KeyList);
201 ParentKey->SubKeyCount++;
202
203 nextsubkey:
204 ParentKey = CurrentKey;
205 if (End)
206 LocalKeyName = End + 1;
207 else
208 break;
209 }
210
211 *Key = MEMKEY_TO_HKEY(ParentKey);
212
213 return ERROR_SUCCESS;
214 }
215
216 LONG WINAPI
217 RegCreateKeyW(
218 IN HKEY hKey,
219 IN LPCWSTR lpSubKey,
220 OUT PHKEY phkResult)
221 {
222 return RegpOpenOrCreateKey(hKey, lpSubKey, TRUE, phkResult);
223 }
224
225 static PWSTR
226 MultiByteToWideChar(
227 IN PCSTR MultiByteString)
228 {
229 ANSI_STRING Source;
230 UNICODE_STRING Destination;
231 NTSTATUS Status;
232
233 RtlInitAnsiString(&Source, MultiByteString);
234 Status = RtlAnsiStringToUnicodeString(&Destination, &Source, TRUE);
235 if (!NT_SUCCESS(Status))
236 return NULL;
237 return Destination.Buffer;
238 }
239
240 LONG WINAPI
241 RegCreateKeyA(
242 IN HKEY hKey,
243 IN LPCSTR lpSubKey,
244 OUT PHKEY phkResult)
245 {
246 PWSTR lpSubKeyW;
247 LONG rc;
248
249 lpSubKeyW = MultiByteToWideChar(lpSubKey);
250 if (!lpSubKeyW)
251 return ERROR_OUTOFMEMORY;
252
253 rc = RegCreateKeyW(hKey, lpSubKeyW, phkResult);
254 free(lpSubKeyW);
255 return rc;
256 }
257
258 LONG WINAPI
259 RegDeleteKeyW(
260 IN HKEY hKey,
261 IN LPCWSTR lpSubKey)
262 {
263 DPRINT1("FIXME: implement RegDeleteKeyW!\n");
264 return ERROR_SUCCESS;
265 }
266
267 LONG WINAPI
268 RegDeleteKeyA(
269 IN HKEY hKey,
270 IN LPCSTR lpSubKey)
271 {
272 PWSTR lpSubKeyW = NULL;
273 LONG rc;
274
275 if (lpSubKey != NULL && strchr(lpSubKey, '\\') != NULL)
276 return ERROR_INVALID_PARAMETER;
277
278 if (lpSubKey)
279 {
280 lpSubKeyW = MultiByteToWideChar(lpSubKey);
281 if (!lpSubKeyW)
282 return ERROR_OUTOFMEMORY;
283 }
284
285 rc = RegDeleteKeyW(hKey, lpSubKeyW);
286
287 if (lpSubKey)
288 free(lpSubKeyW);
289
290 return rc;
291 }
292
293 LONG WINAPI
294 RegOpenKeyW(
295 IN HKEY hKey,
296 IN LPCWSTR lpSubKey,
297 OUT PHKEY phkResult)
298 {
299 return RegpOpenOrCreateKey(hKey, lpSubKey, FALSE, phkResult);
300 }
301
302 LONG WINAPI
303 RegOpenKeyA(
304 IN HKEY hKey,
305 IN LPCSTR lpSubKey,
306 OUT PHKEY phkResult)
307 {
308 PWSTR lpSubKeyW;
309 LONG rc;
310
311 lpSubKeyW = MultiByteToWideChar(lpSubKey);
312 if (!lpSubKeyW)
313 return ERROR_OUTOFMEMORY;
314
315 rc = RegOpenKeyW(hKey, lpSubKeyW, phkResult);
316 free(lpSubKeyW);
317 return rc;
318 }
319
320 static LONG
321 RegpOpenOrCreateValue(
322 IN HKEY hKey,
323 IN LPCWSTR ValueName,
324 IN BOOL AllowCreation,
325 OUT PCM_KEY_VALUE *ValueCell,
326 OUT PHCELL_INDEX ValueCellOffset)
327 {
328 MEMKEY ParentKey;
329 UNICODE_STRING ValueString;
330 NTSTATUS Status;
331
332 ParentKey = HKEY_TO_MEMKEY(hKey);
333 RtlInitUnicodeString(&ValueString, ValueName);
334
335 Status = CmiScanForValueKey(
336 ParentKey->RegistryHive,
337 ParentKey->KeyCell,
338 &ValueString,
339 ValueCell,
340 ValueCellOffset);
341 if (AllowCreation && Status == STATUS_OBJECT_NAME_NOT_FOUND)
342 {
343 Status = CmiAddValueKey(
344 ParentKey->RegistryHive,
345 ParentKey->KeyCell,
346 ParentKey->KeyCellOffset,
347 &ValueString,
348 ValueCell,
349 ValueCellOffset);
350 }
351 if (!NT_SUCCESS(Status))
352 return ERROR_UNSUCCESSFUL;
353 return ERROR_SUCCESS;
354 }
355
356 LONG WINAPI
357 RegSetValueExW(
358 IN HKEY hKey,
359 IN LPCWSTR lpValueName OPTIONAL,
360 IN ULONG Reserved,
361 IN ULONG dwType,
362 IN const UCHAR* lpData,
363 IN USHORT cbData)
364 {
365 MEMKEY Key, DestKey;
366 PHKEY phKey;
367 PCM_KEY_VALUE ValueCell;
368 HCELL_INDEX ValueCellOffset;
369 PVOID DataCell;
370 LONG DataCellSize;
371 NTSTATUS Status;
372
373 if (dwType == REG_LINK)
374 {
375 /* Special handling of registry links */
376 if (cbData != sizeof(PVOID))
377 return STATUS_INVALID_PARAMETER;
378 phKey = (PHKEY)lpData;
379 Key = HKEY_TO_MEMKEY(hKey);
380 DestKey = HKEY_TO_MEMKEY(*phKey);
381
382 /* Create the link in memory */
383 Key->DataType = REG_LINK;
384 Key->LinkedKey = DestKey;
385
386 /* Create the link in registry hive (if applicable) */
387 if (Key->RegistryHive != DestKey->RegistryHive)
388 return STATUS_SUCCESS;
389 DPRINT1("Save link to registry\n");
390 return STATUS_NOT_IMPLEMENTED;
391 }
392
393 if ((cbData & REG_DATA_SIZE_MASK) != cbData)
394 return STATUS_UNSUCCESSFUL;
395
396 Key = HKEY_TO_MEMKEY(hKey);
397
398 Status = RegpOpenOrCreateValue(hKey, lpValueName, TRUE, &ValueCell, &ValueCellOffset);
399 if (!NT_SUCCESS(Status))
400 return ERROR_UNSUCCESSFUL;
401
402 /* Get size of the allocated cellule (if any) */
403 if (!(ValueCell->DataLength & REG_DATA_IN_OFFSET) &&
404 (ValueCell->DataLength & REG_DATA_SIZE_MASK) != 0)
405 {
406 DataCell = HvGetCell(&Key->RegistryHive->Hive, ValueCell->Data);
407 if (!DataCell)
408 return ERROR_UNSUCCESSFUL;
409 DataCellSize = -HvGetCellSize(&Key->RegistryHive->Hive, DataCell);
410 }
411 else
412 {
413 DataCell = NULL;
414 DataCellSize = 0;
415 }
416
417 if (cbData <= sizeof(HCELL_INDEX))
418 {
419 /* If data size <= sizeof(HCELL_INDEX) then store data in the data offset */
420 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
421 if (DataCell)
422 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
423
424 RtlCopyMemory(&ValueCell->Data, lpData, cbData);
425 ValueCell->DataLength = (ULONG)(cbData | REG_DATA_IN_OFFSET);
426 ValueCell->Type = dwType;
427 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
428 }
429 else
430 {
431 if (cbData > (SIZE_T)DataCellSize)
432 {
433 /* New data size is larger than the current, destroy current
434 * data block and allocate a new one. */
435 HCELL_INDEX NewOffset;
436
437 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
438
439 NewOffset = HvAllocateCell(&Key->RegistryHive->Hive, cbData, Stable, HCELL_NIL);
440 if (NewOffset == HCELL_NIL)
441 {
442 DPRINT("HvAllocateCell() failed with status 0x%08x\n", Status);
443 return ERROR_UNSUCCESSFUL;
444 }
445
446 if (DataCell)
447 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
448
449 ValueCell->Data = NewOffset;
450 DataCell = (PVOID)HvGetCell(&Key->RegistryHive->Hive, NewOffset);
451 }
452
453 /* Copy new contents to cellule */
454 RtlCopyMemory(DataCell, lpData, cbData);
455 ValueCell->DataLength = (ULONG)(cbData & REG_DATA_SIZE_MASK);
456 ValueCell->Type = dwType;
457 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCell->Data, FALSE);
458 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
459 }
460
461 if (cbData > Key->KeyCell->MaxValueDataLen)
462 Key->KeyCell->MaxValueDataLen = cbData;
463
464 HvMarkCellDirty(&Key->RegistryHive->Hive, Key->KeyCellOffset, FALSE);
465
466 DPRINT("Return status 0x%08x\n", Status);
467 return Status;
468 }
469
470 LONG WINAPI
471 RegSetValueExA(
472 IN HKEY hKey,
473 IN LPCSTR lpValueName OPTIONAL,
474 IN ULONG Reserved,
475 IN ULONG dwType,
476 IN const UCHAR* lpData,
477 IN ULONG cbData)
478 {
479 LPWSTR lpValueNameW = NULL;
480 const UCHAR* lpDataW;
481 USHORT cbDataW;
482 LONG rc = ERROR_SUCCESS;
483
484 DPRINT("RegSetValueA(%s)\n", lpValueName);
485 if (lpValueName)
486 {
487 lpValueNameW = MultiByteToWideChar(lpValueName);
488 if (!lpValueNameW)
489 return ERROR_OUTOFMEMORY;
490 }
491
492 if ((dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ)
493 && cbData != 0)
494 {
495 ANSI_STRING AnsiString;
496 UNICODE_STRING Data;
497
498 if (lpData[cbData - 1] != '\0')
499 cbData++;
500 RtlInitAnsiString(&AnsiString, NULL);
501 AnsiString.Buffer = (PSTR)lpData;
502 AnsiString.Length = (USHORT)cbData - 1;
503 AnsiString.MaximumLength = (USHORT)cbData;
504 RtlAnsiStringToUnicodeString (&Data, &AnsiString, TRUE);
505 lpDataW = (const UCHAR*)Data.Buffer;
506 cbDataW = Data.MaximumLength;
507 }
508 else
509 {
510 lpDataW = lpData;
511 cbDataW = (USHORT)cbData;
512 }
513
514 if (rc == ERROR_SUCCESS)
515 rc = RegSetValueExW(hKey, lpValueNameW, 0, dwType, lpDataW, cbDataW);
516 if (lpValueNameW)
517 free(lpValueNameW);
518 if (lpData != lpDataW)
519 free((PVOID)lpDataW);
520 return rc;
521 }
522
523 LONG WINAPI
524 RegQueryValueExW(
525 IN HKEY hKey,
526 IN LPCWSTR lpValueName,
527 IN PULONG lpReserved,
528 OUT PULONG lpType,
529 OUT PUCHAR lpData,
530 OUT PSIZE_T lpcbData)
531 {
532 //ParentKey = HKEY_TO_MEMKEY(RootKey);
533 PCM_KEY_VALUE ValueCell;
534 HCELL_INDEX ValueCellOffset;
535 LONG rc;
536
537 rc = RegpOpenOrCreateValue(
538 hKey,
539 lpValueName,
540 FALSE,
541 &ValueCell,
542 &ValueCellOffset);
543 if (rc != ERROR_SUCCESS)
544 return rc;
545
546 DPRINT1("RegQueryValueExW(%S) not implemented\n", lpValueName);
547 /* ValueCell and ValueCellOffset are valid */
548
549 return ERROR_UNSUCCESSFUL;
550 }
551
552 LONG WINAPI
553 RegQueryValueExA(
554 IN HKEY hKey,
555 IN LPCSTR lpValueName,
556 IN PULONG lpReserved,
557 OUT PULONG lpType,
558 OUT PUCHAR lpData,
559 OUT PSIZE_T lpcbData)
560 {
561 LPWSTR lpValueNameW = NULL;
562 LONG rc;
563
564 if (lpValueName)
565 {
566 lpValueNameW = MultiByteToWideChar(lpValueName);
567 if (!lpValueNameW)
568 return ERROR_OUTOFMEMORY;
569 }
570
571 rc = RegQueryValueExW(hKey, lpValueNameW, lpReserved, lpType, lpData, lpcbData);
572 if (lpValueNameW)
573 free(lpValueNameW);
574 return rc;
575 }
576
577 LONG WINAPI
578 RegDeleteValueW(
579 IN HKEY hKey,
580 IN LPCWSTR lpValueName OPTIONAL)
581 {
582 DPRINT1("RegDeleteValueW() unimplemented\n");
583 return ERROR_UNSUCCESSFUL;
584 }
585
586 LONG WINAPI
587 RegDeleteValueA(
588 IN HKEY hKey,
589 IN LPCSTR lpValueName OPTIONAL)
590 {
591 LPWSTR lpValueNameW;
592 LONG rc;
593
594 if (lpValueName)
595 {
596 lpValueNameW = MultiByteToWideChar(lpValueName);
597 if (!lpValueNameW)
598 return ERROR_OUTOFMEMORY;
599 rc = RegDeleteValueW(hKey, lpValueNameW);
600 free(lpValueNameW);
601 }
602 else
603 rc = RegDeleteValueW(hKey, NULL);
604 return rc;
605 }
606
607 static BOOL
608 ConnectRegistry(
609 IN HKEY RootKey,
610 IN PCMHIVE HiveToConnect,
611 IN LPCWSTR Path)
612 {
613 NTSTATUS Status;
614 MEMKEY NewKey;
615 LONG rc;
616
617 Status = CmiInitializeTempHive(HiveToConnect);
618 if (!NT_SUCCESS(Status))
619 {
620 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
621 return FALSE;
622 }
623
624 /* Create key */
625 rc = RegCreateKeyW(
626 RootKey,
627 Path,
628 (PHKEY)&NewKey);
629 if (rc != ERROR_SUCCESS)
630 return FALSE;
631
632 NewKey->RegistryHive = HiveToConnect;
633 NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell;
634 NewKey->KeyCell = (PCM_KEY_NODE)HvGetCell (&HiveToConnect->Hive, NewKey->KeyCellOffset);
635 return TRUE;
636 }
637
638 LIST_ENTRY CmiHiveListHead;
639
640 VOID
641 RegInitializeRegistry(VOID)
642 {
643 UNICODE_STRING RootKeyName = RTL_CONSTANT_STRING(L"\\");
644 NTSTATUS Status;
645 HKEY ControlSetKey;
646
647 InitializeListHead(&CmiHiveListHead);
648
649 Status = CmiInitializeTempHive(&RootHive);
650 if (!NT_SUCCESS(Status))
651 {
652 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
653 return;
654 }
655
656 RootKey = CreateInMemoryStructure(
657 &RootHive,
658 RootHive.Hive.BaseBlock->RootCell,
659 &RootKeyName);
660
661 /* Create DEFAULT key */
662 ConnectRegistry(
663 NULL,
664 &DefaultHive,
665 L"Registry\\User\\.DEFAULT");
666
667 /* Create SAM key */
668 ConnectRegistry(
669 NULL,
670 &SamHive,
671 L"Registry\\Machine\\SAM");
672
673 /* Create SECURITY key */
674 ConnectRegistry(
675 NULL,
676 &SecurityHive,
677 L"Registry\\Machine\\SECURITY");
678
679 /* Create SOFTWARE key */
680 ConnectRegistry(
681 NULL,
682 &SoftwareHive,
683 L"Registry\\Machine\\SOFTWARE");
684
685 /* Create SYSTEM key */
686 ConnectRegistry(
687 NULL,
688 &SystemHive,
689 L"Registry\\Machine\\SYSTEM");
690
691 /* Create 'ControlSet001' key */
692 RegCreateKeyW(
693 NULL,
694 L"Registry\\Machine\\SYSTEM\\ControlSet001",
695 &ControlSetKey);
696 }
697
698 VOID
699 RegShutdownRegistry(VOID)
700 {
701 /* FIXME: clean up the complete hive */
702
703 free(RootKey->Name);
704 free(RootKey);
705 }
706
707 /* EOF */