[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(IN HKEY hKey,
250 IN LPCWSTR lpSubKey)
251 {
252 if (lpSubKey != NULL && wcschr(lpSubKey, L'\\') != NULL)
253 return(ERROR_INVALID_PARAMETER);
254
255 DPRINT1("RegDeleteKeyW: FIXME!\n");
256
257 return(ERROR_SUCCESS);
258 }
259
260 LONG WINAPI
261 RegDeleteKeyA(IN HKEY hKey,
262 IN LPCSTR lpSubKey)
263 {
264 PWSTR lpSubKeyW;
265 LONG rc;
266
267 if (lpSubKey != NULL && strchr(lpSubKey, '\\') != NULL)
268 return(ERROR_INVALID_PARAMETER);
269
270 lpSubKeyW = MultiByteToWideChar(lpSubKey);
271 if (!lpSubKeyW)
272 return ERROR_OUTOFMEMORY;
273
274 rc = RegDeleteKeyW(hKey, lpSubKeyW);
275 free(lpSubKeyW);
276 return rc;
277 }
278
279 LONG WINAPI
280 RegOpenKeyW(
281 IN HKEY hKey,
282 IN LPCWSTR lpSubKey,
283 OUT PHKEY phkResult)
284 {
285 return RegpOpenOrCreateKey(hKey, lpSubKey, FALSE, phkResult);
286 }
287
288 LONG WINAPI
289 RegOpenKeyA(
290 IN HKEY hKey,
291 IN LPCSTR lpSubKey,
292 OUT PHKEY phkResult)
293 {
294 PWSTR lpSubKeyW;
295 LONG rc;
296
297 lpSubKeyW = MultiByteToWideChar(lpSubKey);
298 if (!lpSubKeyW)
299 return ERROR_OUTOFMEMORY;
300
301 rc = RegOpenKeyW(hKey, lpSubKeyW, phkResult);
302 free(lpSubKeyW);
303 return rc;
304 }
305
306 static LONG
307 RegpOpenOrCreateValue(
308 IN HKEY hKey,
309 IN LPCWSTR ValueName,
310 IN BOOL AllowCreation,
311 OUT PCM_KEY_VALUE *ValueCell,
312 OUT PHCELL_INDEX ValueCellOffset)
313 {
314 MEMKEY ParentKey;
315 UNICODE_STRING ValueString;
316 NTSTATUS Status;
317
318 ParentKey = HKEY_TO_MEMKEY(hKey);
319 RtlInitUnicodeString(&ValueString, ValueName);
320
321 Status = CmiScanForValueKey(
322 ParentKey->RegistryHive,
323 ParentKey->KeyCell,
324 &ValueString,
325 ValueCell,
326 ValueCellOffset);
327 if (AllowCreation && Status == STATUS_OBJECT_NAME_NOT_FOUND)
328 {
329 Status = CmiAddValueKey(
330 ParentKey->RegistryHive,
331 ParentKey->KeyCell,
332 ParentKey->KeyCellOffset,
333 &ValueString,
334 ValueCell,
335 ValueCellOffset);
336 }
337 if (!NT_SUCCESS(Status))
338 return ERROR_UNSUCCESSFUL;
339 return ERROR_SUCCESS;
340 }
341
342 LONG WINAPI
343 RegSetValueExW(
344 IN HKEY hKey,
345 IN LPCWSTR lpValueName OPTIONAL,
346 IN ULONG Reserved,
347 IN ULONG dwType,
348 IN const UCHAR* lpData,
349 IN USHORT cbData)
350 {
351 MEMKEY Key, DestKey;
352 PHKEY phKey;
353 PCM_KEY_VALUE ValueCell;
354 HCELL_INDEX ValueCellOffset;
355 PVOID DataCell;
356 LONG DataCellSize;
357 NTSTATUS Status;
358
359 if (dwType == REG_LINK)
360 {
361 /* Special handling of registry links */
362 if (cbData != sizeof(PVOID))
363 return STATUS_INVALID_PARAMETER;
364 phKey = (PHKEY)lpData;
365 Key = HKEY_TO_MEMKEY(hKey);
366 DestKey = HKEY_TO_MEMKEY(*phKey);
367
368 /* Create the link in memory */
369 Key->DataType = REG_LINK;
370 Key->LinkedKey = DestKey;
371
372 /* Create the link in registry hive (if applicable) */
373 if (Key->RegistryHive != DestKey->RegistryHive)
374 return STATUS_SUCCESS;
375 DPRINT1("Save link to registry\n");
376 return STATUS_NOT_IMPLEMENTED;
377 }
378
379 if ((cbData & REG_DATA_SIZE_MASK) != cbData)
380 return STATUS_UNSUCCESSFUL;
381
382 Key = HKEY_TO_MEMKEY(hKey);
383
384 Status = RegpOpenOrCreateValue(hKey, lpValueName, TRUE, &ValueCell, &ValueCellOffset);
385 if (!NT_SUCCESS(Status))
386 return ERROR_UNSUCCESSFUL;
387
388 /* Get size of the allocated cellule (if any) */
389 if (!(ValueCell->DataLength & REG_DATA_IN_OFFSET) &&
390 (ValueCell->DataLength & REG_DATA_SIZE_MASK) != 0)
391 {
392 DataCell = HvGetCell(&Key->RegistryHive->Hive, ValueCell->Data);
393 if (!DataCell)
394 return ERROR_UNSUCCESSFUL;
395 DataCellSize = -HvGetCellSize(&Key->RegistryHive->Hive, DataCell);
396 }
397 else
398 {
399 DataCell = NULL;
400 DataCellSize = 0;
401 }
402
403 if (cbData <= sizeof(HCELL_INDEX))
404 {
405 /* If data size <= sizeof(HCELL_INDEX) then store data in the data offset */
406 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
407 if (DataCell)
408 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
409
410 RtlCopyMemory(&ValueCell->Data, lpData, cbData);
411 ValueCell->DataLength = (ULONG)(cbData | REG_DATA_IN_OFFSET);
412 ValueCell->Type = dwType;
413 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
414 }
415 else
416 {
417 if (cbData > (SIZE_T)DataCellSize)
418 {
419 /* New data size is larger than the current, destroy current
420 * data block and allocate a new one. */
421 HCELL_INDEX NewOffset;
422
423 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
424
425 NewOffset = HvAllocateCell(&Key->RegistryHive->Hive, cbData, Stable, HCELL_NIL);
426 if (NewOffset == HCELL_NIL)
427 {
428 DPRINT("HvAllocateCell() failed with status 0x%08x\n", Status);
429 return ERROR_UNSUCCESSFUL;
430 }
431
432 if (DataCell)
433 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
434
435 ValueCell->Data = NewOffset;
436 DataCell = (PVOID)HvGetCell(&Key->RegistryHive->Hive, NewOffset);
437 }
438
439 /* Copy new contents to cellule */
440 RtlCopyMemory(DataCell, lpData, cbData);
441 ValueCell->DataLength = (ULONG)(cbData & REG_DATA_SIZE_MASK);
442 ValueCell->Type = dwType;
443 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCell->Data, FALSE);
444 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
445 }
446
447 HvMarkCellDirty(&Key->RegistryHive->Hive, Key->KeyCellOffset, FALSE);
448
449 DPRINT("Return status 0x%08x\n", Status);
450 return Status;
451 }
452
453 LONG WINAPI
454 RegSetValueExA(
455 IN HKEY hKey,
456 IN LPCSTR lpValueName OPTIONAL,
457 IN ULONG Reserved,
458 IN ULONG dwType,
459 IN const UCHAR* lpData,
460 IN ULONG cbData)
461 {
462 LPWSTR lpValueNameW = NULL;
463 const UCHAR* lpDataW;
464 USHORT cbDataW;
465 LONG rc = ERROR_SUCCESS;
466
467 DPRINT("RegSetValueA(%s)\n", lpValueName);
468 if (lpValueName)
469 {
470 lpValueNameW = MultiByteToWideChar(lpValueName);
471 if (!lpValueNameW)
472 return ERROR_OUTOFMEMORY;
473 }
474
475 if ((dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ)
476 && cbData != 0)
477 {
478 ANSI_STRING AnsiString;
479 UNICODE_STRING Data;
480
481 if (lpData[cbData - 1] != '\0')
482 cbData++;
483 RtlInitAnsiString(&AnsiString, NULL);
484 AnsiString.Buffer = (PSTR)lpData;
485 AnsiString.Length = (USHORT)cbData - 1;
486 AnsiString.MaximumLength = (USHORT)cbData;
487 RtlAnsiStringToUnicodeString (&Data, &AnsiString, TRUE);
488 lpDataW = (const UCHAR*)Data.Buffer;
489 cbDataW = Data.MaximumLength;
490 }
491 else
492 {
493 lpDataW = lpData;
494 cbDataW = (USHORT)cbData;
495 }
496
497 if (rc == ERROR_SUCCESS)
498 rc = RegSetValueExW(hKey, lpValueNameW, 0, dwType, lpDataW, cbDataW);
499 if (lpValueNameW)
500 free(lpValueNameW);
501 if (lpData != lpDataW)
502 free((PVOID)lpDataW);
503 return rc;
504 }
505
506 LONG WINAPI
507 RegQueryValueExW(
508 IN HKEY hKey,
509 IN LPCWSTR lpValueName,
510 IN PULONG lpReserved,
511 OUT PULONG lpType,
512 OUT PUCHAR lpData,
513 OUT PSIZE_T lpcbData)
514 {
515 //ParentKey = HKEY_TO_MEMKEY(RootKey);
516 PCM_KEY_VALUE ValueCell;
517 HCELL_INDEX ValueCellOffset;
518 LONG rc;
519
520 rc = RegpOpenOrCreateValue(
521 hKey,
522 lpValueName,
523 FALSE,
524 &ValueCell,
525 &ValueCellOffset);
526 if (rc != ERROR_SUCCESS)
527 return rc;
528
529 DPRINT1("RegQueryValueExW(%S) not implemented\n", lpValueName);
530 /* ValueCell and ValueCellOffset are valid */
531
532 return ERROR_UNSUCCESSFUL;
533 }
534
535 LONG WINAPI
536 RegQueryValueExA(
537 IN HKEY hKey,
538 IN LPCSTR lpValueName,
539 IN PULONG lpReserved,
540 OUT PULONG lpType,
541 OUT PUCHAR lpData,
542 OUT PSIZE_T lpcbData)
543 {
544 LPWSTR lpValueNameW = NULL;
545 LONG rc;
546
547 if (lpValueName)
548 {
549 lpValueNameW = MultiByteToWideChar(lpValueName);
550 if (!lpValueNameW)
551 return ERROR_OUTOFMEMORY;
552 }
553
554 rc = RegQueryValueExW(hKey, lpValueNameW, lpReserved, lpType, lpData, lpcbData);
555 if (lpValueNameW)
556 free(lpValueNameW);
557 return ERROR_UNSUCCESSFUL;
558 }
559
560 LONG WINAPI
561 RegDeleteValueW(
562 IN HKEY hKey,
563 IN LPCWSTR lpValueName OPTIONAL)
564 {
565 DPRINT1("RegDeleteValueW() unimplemented\n");
566 return ERROR_UNSUCCESSFUL;
567 }
568
569 LONG WINAPI
570 RegDeleteValueA(
571 IN HKEY hKey,
572 IN LPCSTR lpValueName OPTIONAL)
573 {
574 LPWSTR lpValueNameW;
575 LONG rc;
576
577 if (lpValueName)
578 {
579 lpValueNameW = MultiByteToWideChar(lpValueName);
580 if (!lpValueNameW)
581 return ERROR_OUTOFMEMORY;
582 rc = RegDeleteValueW(hKey, lpValueNameW);
583 free(lpValueNameW);
584 }
585 else
586 rc = RegDeleteValueW(hKey, NULL);
587 return rc;
588 }
589
590 static BOOL
591 ConnectRegistry(
592 IN HKEY RootKey,
593 IN PCMHIVE HiveToConnect,
594 IN LPCWSTR Path)
595 {
596 NTSTATUS Status;
597 MEMKEY NewKey;
598 LONG rc;
599
600 Status = CmiInitializeTempHive(HiveToConnect);
601 if (!NT_SUCCESS(Status))
602 {
603 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
604 return FALSE;
605 }
606
607 /* Create key */
608 rc = RegCreateKeyW(
609 RootKey,
610 Path,
611 (PHKEY)&NewKey);
612 if (rc != ERROR_SUCCESS)
613 return FALSE;
614
615 NewKey->RegistryHive = HiveToConnect;
616 NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell;
617 NewKey->KeyCell = (PCM_KEY_NODE)HvGetCell (&HiveToConnect->Hive, NewKey->KeyCellOffset);
618 return TRUE;
619 }
620
621 LIST_ENTRY CmiHiveListHead;
622
623 VOID
624 RegInitializeRegistry(VOID)
625 {
626 UNICODE_STRING RootKeyName = RTL_CONSTANT_STRING(L"\\");
627 NTSTATUS Status;
628 HKEY ControlSetKey;
629
630 InitializeListHead(&CmiHiveListHead);
631
632 Status = CmiInitializeTempHive(&RootHive);
633 if (!NT_SUCCESS(Status))
634 {
635 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
636 return;
637 }
638
639 RootKey = CreateInMemoryStructure(
640 &RootHive,
641 RootHive.Hive.BaseBlock->RootCell,
642 &RootKeyName);
643
644 /* Create DEFAULT key */
645 ConnectRegistry(
646 NULL,
647 &DefaultHive,
648 L"Registry\\User\\.DEFAULT");
649
650 /* Create SAM key */
651 ConnectRegistry(
652 NULL,
653 &SamHive,
654 L"Registry\\Machine\\SAM");
655
656 /* Create SECURITY key */
657 ConnectRegistry(
658 NULL,
659 &SecurityHive,
660 L"Registry\\Machine\\SECURITY");
661
662 /* Create SOFTWARE key */
663 ConnectRegistry(
664 NULL,
665 &SoftwareHive,
666 L"Registry\\Machine\\SOFTWARE");
667
668 /* Create SYSTEM key */
669 ConnectRegistry(
670 NULL,
671 &SystemHive,
672 L"Registry\\Machine\\SYSTEM");
673
674 /* Create 'ControlSet001' key */
675 RegCreateKeyW(
676 NULL,
677 L"Registry\\Machine\\SYSTEM\\ControlSet001",
678 &ControlSetKey);
679 }
680
681 /* EOF */