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