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