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