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