[HIVEBCD]:
[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 PMEMKEY 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 CMHIVE BcdHive; /* \Registry\Machine\BCD00000000 */
51
52 static PMEMKEY
53 CreateInMemoryStructure(
54 IN PCMHIVE RegistryHive,
55 IN HCELL_INDEX KeyCellOffset)
56 {
57 PMEMKEY Key;
58
59 Key = (PMEMKEY) malloc (sizeof(MEMKEY));
60 if (!Key)
61 return NULL;
62
63 Key->RegistryHive = RegistryHive;
64 Key->KeyCellOffset = KeyCellOffset;
65 return Key;
66 }
67
68 LIST_ENTRY CmiReparsePointsHead;
69
70 static LONG
71 RegpOpenOrCreateKey(
72 IN HKEY hParentKey,
73 IN PCWSTR KeyName,
74 IN BOOL AllowCreation,
75 IN BOOL Volatile,
76 OUT PHKEY Key)
77 {
78 PWSTR LocalKeyName;
79 PWSTR End;
80 UNICODE_STRING KeyString;
81 NTSTATUS Status;
82 PREPARSE_POINT CurrentReparsePoint;
83 PMEMKEY CurrentKey;
84 PCMHIVE ParentRegistryHive;
85 HCELL_INDEX ParentCellOffset;
86 PLIST_ENTRY Ptr;
87 PCM_KEY_NODE SubKeyCell;
88 HCELL_INDEX BlockOffset;
89
90 DPRINT("RegpCreateOpenKey('%S')\n", KeyName);
91
92 if (*KeyName == L'\\')
93 {
94 KeyName++;
95 ParentRegistryHive = RootKey->RegistryHive;
96 ParentCellOffset = RootKey->KeyCellOffset;
97 }
98 else if (hParentKey == NULL)
99 {
100 ParentRegistryHive = RootKey->RegistryHive;
101 ParentCellOffset = RootKey->KeyCellOffset;
102 }
103 else
104 {
105 ParentRegistryHive = HKEY_TO_MEMKEY(RootKey)->RegistryHive;
106 ParentCellOffset = HKEY_TO_MEMKEY(RootKey)->KeyCellOffset;
107 }
108
109 LocalKeyName = (PWSTR)KeyName;
110 for (;;)
111 {
112 End = (PWSTR)strchrW(LocalKeyName, '\\');
113 if (End)
114 {
115 KeyString.Buffer = LocalKeyName;
116 KeyString.Length = KeyString.MaximumLength =
117 (USHORT)((ULONG_PTR)End - (ULONG_PTR)LocalKeyName);
118 }
119 else
120 {
121 RtlInitUnicodeString(&KeyString, LocalKeyName);
122 if (KeyString.Length == 0)
123 {
124 /* Trailing backslash char; we're done */
125 break;
126 }
127 }
128
129 Status = CmiScanForSubKey(ParentRegistryHive,
130 ParentCellOffset,
131 &KeyString,
132 OBJ_CASE_INSENSITIVE,
133 &SubKeyCell,
134 &BlockOffset);
135 if (NT_SUCCESS(Status))
136 {
137 /* Search for a possible reparse point */
138 Ptr = CmiReparsePointsHead.Flink;
139 while (Ptr != &CmiReparsePointsHead)
140 {
141 CurrentReparsePoint = CONTAINING_RECORD(Ptr, REPARSE_POINT, ListEntry);
142 if (CurrentReparsePoint->SourceHive == ParentRegistryHive &&
143 CurrentReparsePoint->SourceKeyCellOffset == BlockOffset)
144 {
145 ParentRegistryHive = CurrentReparsePoint->DestinationHive;
146 BlockOffset = CurrentReparsePoint->DestinationKeyCellOffset;
147 break;
148 }
149 Ptr = Ptr->Flink;
150 }
151 }
152 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND && AllowCreation)
153 {
154 Status = CmiAddSubKey(ParentRegistryHive,
155 ParentCellOffset,
156 &KeyString,
157 Volatile ? REG_OPTION_VOLATILE : 0,
158 &SubKeyCell,
159 &BlockOffset);
160 }
161 if (!NT_SUCCESS(Status))
162 return ERROR_UNSUCCESSFUL;
163
164 nextsubkey:
165 ParentCellOffset = BlockOffset;
166 if (End)
167 LocalKeyName = End + 1;
168 else
169 break;
170 }
171
172 CurrentKey = CreateInMemoryStructure(ParentRegistryHive, ParentCellOffset);
173 if (!CurrentKey)
174 return ERROR_OUTOFMEMORY;
175 *Key = MEMKEY_TO_HKEY(CurrentKey);
176
177 return ERROR_SUCCESS;
178 }
179
180 LONG WINAPI
181 RegCreateKeyW(
182 IN HKEY hKey,
183 IN LPCWSTR lpSubKey,
184 OUT PHKEY phkResult)
185 {
186 return RegpOpenOrCreateKey(hKey, lpSubKey, TRUE, FALSE, phkResult);
187 }
188
189 static PWSTR
190 MultiByteToWideChar(
191 IN PCSTR MultiByteString)
192 {
193 ANSI_STRING Source;
194 UNICODE_STRING Destination;
195 NTSTATUS Status;
196
197 RtlInitAnsiString(&Source, MultiByteString);
198 Status = RtlAnsiStringToUnicodeString(&Destination, &Source, TRUE);
199 if (!NT_SUCCESS(Status))
200 return NULL;
201 return Destination.Buffer;
202 }
203
204 LONG WINAPI
205 RegDeleteKeyW(
206 IN HKEY hKey,
207 IN LPCWSTR lpSubKey)
208 {
209 DPRINT1("FIXME: implement RegDeleteKeyW!\n");
210 return ERROR_SUCCESS;
211 }
212
213 LONG WINAPI
214 RegDeleteKeyA(
215 IN HKEY hKey,
216 IN LPCSTR lpSubKey)
217 {
218 PWSTR lpSubKeyW = NULL;
219 LONG rc;
220
221 if (lpSubKey != NULL && strchr(lpSubKey, '\\') != NULL)
222 return ERROR_INVALID_PARAMETER;
223
224 if (lpSubKey)
225 {
226 lpSubKeyW = MultiByteToWideChar(lpSubKey);
227 if (!lpSubKeyW)
228 return ERROR_OUTOFMEMORY;
229 }
230
231 rc = RegDeleteKeyW(hKey, lpSubKeyW);
232
233 if (lpSubKey)
234 free(lpSubKeyW);
235
236 return rc;
237 }
238
239 LONG WINAPI
240 RegOpenKeyW(
241 IN HKEY hKey,
242 IN LPCWSTR lpSubKey,
243 OUT PHKEY phkResult)
244 {
245 return RegpOpenOrCreateKey(hKey, lpSubKey, FALSE, FALSE, phkResult);
246 }
247
248 LONG WINAPI
249 RegCreateKeyExW(
250 IN HKEY hKey,
251 IN LPCWSTR lpSubKey,
252 IN DWORD Reserved,
253 IN LPWSTR lpClass OPTIONAL,
254 IN DWORD dwOptions,
255 IN REGSAM samDesired,
256 IN LPSECURITY_ATTRIBUTES lpSecurityAttributes OPTIONAL,
257 OUT PHKEY phkResult,
258 OUT LPDWORD lpdwDisposition OPTIONAL)
259 {
260 return RegpOpenOrCreateKey(hKey,
261 lpSubKey,
262 TRUE,
263 (dwOptions & REG_OPTION_VOLATILE) != 0,
264 phkResult);
265 }
266
267 LONG WINAPI
268 RegOpenKeyA(
269 IN HKEY hKey,
270 IN LPCSTR lpSubKey,
271 OUT PHKEY phkResult)
272 {
273 PWSTR lpSubKeyW;
274 LONG rc;
275
276 lpSubKeyW = MultiByteToWideChar(lpSubKey);
277 if (!lpSubKeyW)
278 return ERROR_OUTOFMEMORY;
279
280 rc = RegOpenKeyW(hKey, lpSubKeyW, phkResult);
281 free(lpSubKeyW);
282 return rc;
283 }
284
285 static LONG
286 RegpOpenOrCreateValue(
287 IN HKEY hKey,
288 IN LPCWSTR ValueName,
289 IN BOOL AllowCreation,
290 OUT PCM_KEY_VALUE *ValueCell,
291 OUT PHCELL_INDEX ValueCellOffset)
292 {
293 PMEMKEY ParentKey;
294 UNICODE_STRING ValueString;
295 NTSTATUS Status;
296
297 ParentKey = HKEY_TO_MEMKEY(hKey);
298 RtlInitUnicodeString(&ValueString, ValueName);
299
300 Status = CmiScanForValueKey(ParentKey->RegistryHive,
301 ParentKey->KeyCellOffset,
302 &ValueString,
303 ValueCell,
304 ValueCellOffset);
305 if (AllowCreation && Status == STATUS_OBJECT_NAME_NOT_FOUND)
306 {
307 Status = CmiAddValueKey(ParentKey->RegistryHive,
308 ParentKey->KeyCellOffset,
309 &ValueString,
310 ValueCell,
311 ValueCellOffset);
312 }
313 if (!NT_SUCCESS(Status))
314 return ERROR_UNSUCCESSFUL;
315 return ERROR_SUCCESS;
316 }
317
318 LONG WINAPI
319 RegSetValueExW(
320 IN HKEY hKey,
321 IN LPCWSTR lpValueName OPTIONAL,
322 IN ULONG Reserved,
323 IN ULONG dwType,
324 IN const UCHAR* lpData,
325 IN USHORT cbData)
326 {
327 PMEMKEY Key, DestKey;
328 PHKEY phKey;
329 PCM_KEY_VALUE ValueCell;
330 HCELL_INDEX ValueCellOffset;
331 PVOID DataCell;
332 LONG DataCellSize;
333 NTSTATUS Status;
334
335 if (dwType == REG_LINK)
336 {
337 /* Special handling of registry links */
338 if (cbData != sizeof(PVOID))
339 return STATUS_INVALID_PARAMETER;
340
341 phKey = (PHKEY)lpData;
342 Key = HKEY_TO_MEMKEY(hKey);
343 DestKey = HKEY_TO_MEMKEY(*phKey);
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->DataLength & REG_DATA_IN_OFFSET) &&
363 (ValueCell->DataLength & REG_DATA_SIZE_MASK) != 0)
364 {
365 DataCell = HvGetCell(&Key->RegistryHive->Hive, ValueCell->Data);
366 if (!DataCell)
367 return ERROR_UNSUCCESSFUL;
368
369 DataCellSize = -HvGetCellSize(&Key->RegistryHive->Hive, DataCell);
370 }
371 else
372 {
373 DataCell = NULL;
374 DataCellSize = 0;
375 }
376
377 if (cbData <= sizeof(HCELL_INDEX))
378 {
379 /* If data size <= sizeof(HCELL_INDEX) then store data in the data offset */
380 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
381 if (DataCell)
382 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
383
384 RtlCopyMemory(&ValueCell->Data, lpData, cbData);
385 ValueCell->DataLength = (ULONG)(cbData | REG_DATA_IN_OFFSET);
386 ValueCell->Type = dwType;
387 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
388 }
389 else
390 {
391 if (cbData > (SIZE_T)DataCellSize)
392 {
393 /* New data size is larger than the current, destroy current
394 * data block and allocate a new one. */
395 HCELL_INDEX NewOffset;
396
397 DPRINT("ValueCell->DataLength %u\n", ValueCell->DataLength);
398
399 NewOffset = HvAllocateCell(&Key->RegistryHive->Hive, cbData, Stable, HCELL_NIL);
400 if (NewOffset == HCELL_NIL)
401 {
402 DPRINT("HvAllocateCell() failed with status 0x%08x\n", Status);
403 return ERROR_UNSUCCESSFUL;
404 }
405
406 if (DataCell)
407 HvFreeCell(&Key->RegistryHive->Hive, ValueCell->Data);
408
409 ValueCell->Data = NewOffset;
410 DataCell = (PVOID)HvGetCell(&Key->RegistryHive->Hive, NewOffset);
411 }
412
413 /* Copy new contents to cellule */
414 RtlCopyMemory(DataCell, lpData, cbData);
415 ValueCell->DataLength = (ULONG)(cbData & REG_DATA_SIZE_MASK);
416 ValueCell->Type = dwType;
417 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCell->Data, FALSE);
418 HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
419 }
420
421 HvMarkCellDirty(&Key->RegistryHive->Hive, Key->KeyCellOffset, FALSE);
422
423 DPRINT("Return status 0x%08x\n", Status);
424 return Status;
425 }
426
427 LONG WINAPI
428 RegQueryValueExW(
429 IN HKEY hKey,
430 IN LPCWSTR lpValueName,
431 IN PULONG lpReserved,
432 OUT PULONG lpType,
433 OUT PUCHAR lpData,
434 OUT PSIZE_T lpcbData)
435 {
436 //ParentKey = HKEY_TO_MEMKEY(RootKey);
437 PCM_KEY_VALUE ValueCell;
438 HCELL_INDEX ValueCellOffset;
439 LONG rc;
440
441 rc = RegpOpenOrCreateValue(hKey,
442 lpValueName,
443 FALSE,
444 &ValueCell,
445 &ValueCellOffset);
446 if (rc != ERROR_SUCCESS)
447 return rc;
448
449 DPRINT1("RegQueryValueExW(%S) not implemented\n", lpValueName);
450 /* ValueCell and ValueCellOffset are valid */
451
452 return ERROR_UNSUCCESSFUL;
453 }
454
455 LONG WINAPI
456 RegDeleteValueW(
457 IN HKEY hKey,
458 IN LPCWSTR lpValueName OPTIONAL)
459 {
460 DPRINT1("RegDeleteValueW() unimplemented\n");
461 return ERROR_UNSUCCESSFUL;
462 }
463
464 static BOOL
465 ConnectRegistry(
466 IN HKEY RootKey,
467 IN PCMHIVE HiveToConnect,
468 IN LPCWSTR Path)
469 {
470 NTSTATUS Status;
471 PREPARSE_POINT ReparsePoint;
472 PMEMKEY NewKey;
473 LONG rc;
474
475 ReparsePoint = (PREPARSE_POINT)malloc(sizeof(REPARSE_POINT));
476 if (!ReparsePoint)
477 return FALSE;
478
479 Status = CmiInitializeTempHive(HiveToConnect);
480 if (!NT_SUCCESS(Status))
481 {
482 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
483 free(ReparsePoint);
484 return FALSE;
485 }
486
487 /* Create key */
488 rc = RegCreateKeyExW(RootKey,
489 Path,
490 0,
491 NULL,
492 REG_OPTION_VOLATILE,
493 0,
494 NULL,
495 (PHKEY)&NewKey,
496 NULL);
497 if (rc != ERROR_SUCCESS)
498 {
499 free(ReparsePoint);
500 return FALSE;
501 }
502
503 ReparsePoint->SourceHive = NewKey->RegistryHive;
504 ReparsePoint->SourceKeyCellOffset = NewKey->KeyCellOffset;
505 NewKey->RegistryHive = HiveToConnect;
506 NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell;
507 ReparsePoint->DestinationHive = NewKey->RegistryHive;
508 ReparsePoint->DestinationKeyCellOffset = NewKey->KeyCellOffset;
509 InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
510 return TRUE;
511 }
512
513 LIST_ENTRY CmiHiveListHead;
514
515 VOID
516 RegInitializeRegistry(VOID)
517 {
518 UNICODE_STRING RootKeyName = RTL_CONSTANT_STRING(L"\\");
519 NTSTATUS Status;
520 PMEMKEY ControlSetKey, CurrentControlSetKey;
521 PREPARSE_POINT ReparsePoint;
522
523 InitializeListHead(&CmiHiveListHead);
524 InitializeListHead(&CmiReparsePointsHead);
525
526 Status = CmiInitializeTempHive(&RootHive);
527 if (!NT_SUCCESS(Status))
528 {
529 DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
530 return;
531 }
532
533 RootKey = CreateInMemoryStructure(&RootHive,
534 RootHive.Hive.BaseBlock->RootCell);
535
536 /* Create DEFAULT key */
537 ConnectRegistry(NULL,
538 &DefaultHive,
539 L"Registry\\User\\.DEFAULT");
540
541 /* Create SAM key */
542 ConnectRegistry(NULL,
543 &SamHive,
544 L"Registry\\Machine\\SAM");
545
546 /* Create SECURITY key */
547 ConnectRegistry(NULL,
548 &SecurityHive,
549 L"Registry\\Machine\\SECURITY");
550
551 /* Create SOFTWARE key */
552 ConnectRegistry(NULL,
553 &SoftwareHive,
554 L"Registry\\Machine\\SOFTWARE");
555
556 /* Create BCD key */
557 ConnectRegistry(NULL,
558 &BcdHive,
559 L"Registry\\Machine\\BCD00000000");
560
561 /* Create SYSTEM key */
562 ConnectRegistry(NULL,
563 &SystemHive,
564 L"Registry\\Machine\\SYSTEM");
565
566 /* Create 'ControlSet001' key */
567 RegCreateKeyW(NULL,
568 L"Registry\\Machine\\SYSTEM\\ControlSet001",
569 (HKEY*)&ControlSetKey);
570
571 /* Create 'CurrentControlSet' key */
572 RegCreateKeyExW(NULL,
573 L"Registry\\Machine\\SYSTEM\\CurrentControlSet",
574 0,
575 NULL,
576 REG_OPTION_VOLATILE,
577 0,
578 NULL,
579 (HKEY*)&CurrentControlSetKey,
580 NULL);
581
582 /* Connect 'CurrentControlSet' to 'ControlSet001' */
583 ReparsePoint = (PREPARSE_POINT)malloc(sizeof(REPARSE_POINT));
584 ReparsePoint->SourceHive = CurrentControlSetKey->RegistryHive;
585 ReparsePoint->SourceKeyCellOffset = CurrentControlSetKey->KeyCellOffset;
586 ReparsePoint->DestinationHive = ControlSetKey->RegistryHive;
587 ReparsePoint->DestinationKeyCellOffset = ControlSetKey->KeyCellOffset;
588 InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
589 }
590
591 VOID
592 RegShutdownRegistry(VOID)
593 {
594 /* FIXME: clean up the complete hive */
595
596 free(RootKey);
597 }
598
599 /* EOF */