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