update for HEAD-2003050101
[reactos.git] / ntoskrnl / cm / registry.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/cm/registry.c
6  * PURPOSE:         Registry functions
7  * PROGRAMMERS:     Rex Jolliff
8  *                  Matt Pyne
9  *                  Jean Michault
10  * UPDATE HISTORY:
11  *                  Created 22/05/98
12  */
13
14 #include <ddk/ntddk.h>
15 #include <roscfg.h>
16 #include <limits.h>
17 #include <string.h>
18 #include <internal/pool.h>
19 #include <internal/registry.h>
20 #include <reactos/bugcodes.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 #include "cm.h"
26
27 /* GLOBALS ******************************************************************/
28
29 POBJECT_TYPE  CmiKeyType = NULL;
30 PREGISTRY_HIVE  CmiVolatileHive = NULL;
31 KSPIN_LOCK  CmiKeyListLock;
32
33 LIST_ENTRY CmiHiveListHead;
34 ERESOURCE CmiHiveListLock;
35
36 volatile BOOLEAN CmiHiveSyncEnabled = FALSE;
37 volatile BOOLEAN CmiHiveSyncPending = FALSE;
38 KDPC CmiHiveSyncDpc;
39 KTIMER CmiHiveSyncTimer;
40
41 static GENERIC_MAPPING CmiKeyMapping =
42         {KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS};
43
44
45
46 VOID
47 CmiCheckKey(BOOLEAN Verbose,
48   HANDLE Key);
49
50 static NTSTATUS
51 CmiCreateCurrentControlSetLink(VOID);
52
53 static VOID STDCALL
54 CmiHiveSyncDpcRoutine(PKDPC Dpc,
55                       PVOID DeferredContext,
56                       PVOID SystemArgument1,
57                       PVOID SystemArgument2);
58
59 /* FUNCTIONS ****************************************************************/
60
61 VOID
62 CmiCheckSubKeys(BOOLEAN Verbose,
63   HANDLE Key)
64 {
65   OBJECT_ATTRIBUTES ObjectAttributes;
66   PKEY_NODE_INFORMATION KeyInfo;
67   WCHAR KeyBuffer[MAX_PATH];
68   UNICODE_STRING KeyPath;
69   WCHAR Name[MAX_PATH];
70   ULONG BufferSize;
71   ULONG ResultSize;
72   NTSTATUS Status;
73   HANDLE SubKey;
74   ULONG Index;
75
76   Index = 0;
77   while (TRUE)
78     {
79       BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
80       KeyInfo = ExAllocatePool(PagedPool, BufferSize);
81
82       Status = NtEnumerateKey(Key,
83                               Index,
84                               KeyNodeInformation,
85                               KeyInfo,
86                               BufferSize,
87                               &ResultSize);
88       if (!NT_SUCCESS(Status))
89         {
90           ExFreePool(KeyInfo);
91           if (Status == STATUS_NO_MORE_ENTRIES)
92             Status = STATUS_SUCCESS;
93           break;
94         }
95
96       wcsncpy(Name,
97               KeyInfo->Name,
98               KeyInfo->NameLength / sizeof(WCHAR));
99
100       if (Verbose)
101         {
102           DbgPrint("Key: %S\n", Name);
103         }
104
105       /* FIXME: Check info. */
106
107       ExFreePool(KeyInfo);
108
109       wcscpy(KeyBuffer, L"\\Registry\\");
110       wcscat(KeyBuffer, Name);
111
112       RtlInitUnicodeString(&KeyPath, KeyBuffer);
113
114       InitializeObjectAttributes(&ObjectAttributes,
115                                  &KeyPath,
116                                  OBJ_CASE_INSENSITIVE,
117                                  NULL,
118                                  NULL);
119
120       Status = NtOpenKey(&SubKey,
121                          KEY_ALL_ACCESS,
122                          &ObjectAttributes);
123
124       assert(NT_SUCCESS(Status));
125
126       CmiCheckKey(Verbose, SubKey);
127
128       NtClose(SubKey);
129
130       Index++;
131     }
132
133   assert(NT_SUCCESS(Status));
134 }
135
136
137 VOID
138 CmiCheckValues(BOOLEAN Verbose,
139   HANDLE Key)
140 {
141   PKEY_NODE_INFORMATION ValueInfo;
142   WCHAR Name[MAX_PATH];
143   ULONG BufferSize;
144   ULONG ResultSize;
145   NTSTATUS Status;
146   ULONG Index;
147
148   Index = 0;
149   while (TRUE)
150     {
151       BufferSize = sizeof(KEY_NODE_INFORMATION) + 4096;
152       ValueInfo = ExAllocatePool(PagedPool, BufferSize);
153
154       Status = NtEnumerateValueKey(Key,
155                                    Index,
156                                    KeyNodeInformation,
157                                    ValueInfo,
158                                    BufferSize,
159                                    &ResultSize);
160       if (!NT_SUCCESS(Status))
161         {
162           ExFreePool(ValueInfo);
163           if (Status == STATUS_NO_MORE_ENTRIES)
164             Status = STATUS_SUCCESS;
165           break;
166         }
167
168       wcsncpy(Name,
169               ValueInfo->Name,
170               ValueInfo->NameLength / sizeof(WCHAR));
171
172       if (Verbose)
173         {
174           DbgPrint("Value: %S\n", Name);
175         }
176
177       /* FIXME: Check info. */
178
179       ExFreePool(ValueInfo);
180
181       Index++;
182     }
183
184   assert(NT_SUCCESS(Status));
185 }
186
187
188 VOID
189 CmiCheckKey(BOOLEAN Verbose,
190   HANDLE Key)
191 {
192   CmiCheckValues(Verbose, Key);
193   CmiCheckSubKeys(Verbose, Key);
194 }
195
196
197 VOID
198 CmiCheckByName(BOOLEAN Verbose,
199   PWSTR KeyName)
200 {
201   OBJECT_ATTRIBUTES ObjectAttributes;
202   WCHAR KeyPathBuffer[MAX_PATH];
203   UNICODE_STRING KeyPath;
204   NTSTATUS Status;
205   HANDLE Key;
206
207   wcscpy(KeyPathBuffer, L"\\Registry\\");
208   wcscat(KeyPathBuffer, KeyName);
209
210   RtlInitUnicodeString(&KeyPath, KeyPathBuffer);
211
212   InitializeObjectAttributes(&ObjectAttributes,
213                 &KeyPath,
214                 OBJ_CASE_INSENSITIVE,
215                 NULL,
216                 NULL);
217
218   Status = NtOpenKey(&Key,
219                 KEY_ALL_ACCESS,
220                 &ObjectAttributes);
221
222   if (CHECKED)
223     {
224       if (!NT_SUCCESS(Status))
225         {
226           DbgPrint("KeyPath %wZ  Status: %.08x", KeyPath, Status);
227           DbgPrint("KeyPath %S  Status: %.08x", KeyPath.Buffer, Status);
228           assert(NT_SUCCESS(Status));
229         }
230     }
231
232   CmiCheckKey(Verbose, Key);
233
234   NtClose(Key);
235 }
236
237
238 VOID
239 CmiCheckRegistry(BOOLEAN Verbose)
240 {
241   if (Verbose)
242     DbgPrint("Checking registry internals\n");
243
244   CmiCheckByName(Verbose, L"Machine");
245   CmiCheckByName(Verbose, L"User");
246 }
247
248
249 VOID
250 CmInitializeRegistry(VOID)
251 {
252   OBJECT_ATTRIBUTES ObjectAttributes;
253   UNICODE_STRING RootKeyName;
254   PKEY_OBJECT RootKey;
255   PKEY_OBJECT MachineKey;
256   PKEY_OBJECT UserKey;
257   HANDLE RootKeyHandle;
258   HANDLE KeyHandle;
259   NTSTATUS Status;
260
261   /*  Initialize the Key object type  */
262   CmiKeyType = ExAllocatePool(NonPagedPool, sizeof(OBJECT_TYPE));
263   assert(CmiKeyType);
264   CmiKeyType->TotalObjects = 0;
265   CmiKeyType->TotalHandles = 0;
266   CmiKeyType->MaxObjects = LONG_MAX;
267   CmiKeyType->MaxHandles = LONG_MAX;
268   CmiKeyType->PagedPoolCharge = 0;
269   CmiKeyType->NonpagedPoolCharge = sizeof(KEY_OBJECT);
270   CmiKeyType->Mapping = &CmiKeyMapping;
271   CmiKeyType->Dump = NULL;
272   CmiKeyType->Open = NULL;
273   CmiKeyType->Close = NULL;
274   CmiKeyType->Delete = CmiObjectDelete;
275   CmiKeyType->Parse = CmiObjectParse;
276   CmiKeyType->Security = CmiObjectSecurity;
277   CmiKeyType->QueryName = NULL;
278   CmiKeyType->OkayToClose = NULL;
279   CmiKeyType->Create = CmiObjectCreate;
280   CmiKeyType->DuplicationNotify = NULL;
281   RtlInitUnicodeString(&CmiKeyType->TypeName, L"Key");
282
283   /* Initialize the hive list */
284   InitializeListHead(&CmiHiveListHead);
285   ExInitializeResourceLite(&CmiHiveListLock);
286
287   /*  Build volatile registry store  */
288   Status = CmiCreateRegistryHive(NULL, &CmiVolatileHive, FALSE);
289   assert(NT_SUCCESS(Status));
290
291   /* Create '\Registry' key. */
292   RtlInitUnicodeString(&RootKeyName, REG_ROOT_KEY_NAME);
293   InitializeObjectAttributes(&ObjectAttributes, &RootKeyName, 0, NULL, NULL);
294   Status = ObCreateObject(&RootKeyHandle,
295                 STANDARD_RIGHTS_REQUIRED,
296                 &ObjectAttributes,
297                 CmiKeyType,
298                 (PVOID *) &RootKey);
299   assert(NT_SUCCESS(Status));
300   Status = ObReferenceObjectByHandle(RootKeyHandle,
301                                      STANDARD_RIGHTS_REQUIRED,
302                                      CmiKeyType,
303                                      KernelMode,
304                                      (PVOID *)&RootKey,
305                                      NULL);
306   assert(NT_SUCCESS(Status));
307   RootKey->RegistryHive = CmiVolatileHive;
308   RootKey->BlockOffset = CmiVolatileHive->HiveHeader->RootKeyCell;
309   RootKey->KeyCell = CmiGetBlock(CmiVolatileHive, RootKey->BlockOffset, NULL);
310   RootKey->Flags = 0;
311   RootKey->NumberOfSubKeys = 0;
312   RootKey->SubKeys = NULL;
313   RootKey->SizeOfSubKeys = 0;
314   RootKey->NameSize = strlen("Registry");
315   RootKey->Name = ExAllocatePool(PagedPool, RootKey->NameSize);
316   RtlCopyMemory(RootKey->Name, "Registry", RootKey->NameSize);
317
318   KeInitializeSpinLock(&CmiKeyListLock);
319
320   /* Create '\Registry\Machine' key. */
321   Status = ObCreateObject(&KeyHandle,
322                           STANDARD_RIGHTS_REQUIRED,
323                           NULL,
324                           CmiKeyType,
325                           (PVOID*)&MachineKey);
326   assert(NT_SUCCESS(Status));
327   Status = CmiAddSubKey(CmiVolatileHive,
328                         RootKey,
329                         MachineKey,
330                         L"Machine",
331                         wcslen(L"Machine") * sizeof(WCHAR),
332                         0,
333                         NULL,
334                         0);
335   assert(NT_SUCCESS(Status));
336   MachineKey->RegistryHive = CmiVolatileHive;
337   MachineKey->Flags = 0;
338   MachineKey->NumberOfSubKeys = 0;
339   MachineKey->SubKeys = NULL;
340   MachineKey->SizeOfSubKeys = MachineKey->KeyCell->NumberOfSubKeys;
341   MachineKey->NameSize = strlen("Machine");
342   MachineKey->Name = ExAllocatePool(PagedPool, MachineKey->NameSize);
343   RtlCopyMemory(MachineKey->Name, "Machine", MachineKey->NameSize);
344   CmiAddKeyToList(RootKey, MachineKey);
345
346   /* Create '\Registry\User' key. */
347   Status = ObCreateObject(&KeyHandle,
348                           STANDARD_RIGHTS_REQUIRED,
349                           NULL,
350                           CmiKeyType,
351                           (PVOID*)&UserKey);
352   assert(NT_SUCCESS(Status));
353   Status = CmiAddSubKey(CmiVolatileHive,
354                         RootKey,
355                         UserKey,
356                         L"User",
357                         wcslen(L"User") * sizeof(WCHAR),
358                         0,
359                         NULL,
360                         0);
361   assert(NT_SUCCESS(Status));
362   UserKey->RegistryHive = CmiVolatileHive;
363   UserKey->Flags = 0;
364   UserKey->NumberOfSubKeys = 0;
365   UserKey->SubKeys = NULL;
366   UserKey->SizeOfSubKeys = UserKey->KeyCell->NumberOfSubKeys;
367   UserKey->NameSize = strlen("User");
368   UserKey->Name = ExAllocatePool(PagedPool, UserKey->NameSize);
369   RtlCopyMemory(UserKey->Name, "User", UserKey->NameSize);
370   CmiAddKeyToList(RootKey, UserKey);
371 }
372
373
374 VOID
375 CmInit2(PCHAR CommandLine)
376 {
377   PCHAR p1, p2;
378   ULONG PiceStart;
379   NTSTATUS Status;
380
381   /* FIXME: Store system start options */
382
383
384
385   /* Create the 'CurrentControlSet' link. */
386   Status = CmiCreateCurrentControlSetLink();
387   if (!NT_SUCCESS(Status))
388     {
389       KeBugCheck(CONFIG_INITIALIZATION_FAILED);
390     }
391
392   /* Set PICE 'Start' value to 1, if PICE debugging is enabled */
393   PiceStart = 4;
394   p1 = (PCHAR)CommandLine;
395   while (p1 && (p2 = strchr(p1, '/')))
396     {
397       p2++;
398       if (_strnicmp(p2, "DEBUGPORT", 9) == 0)
399         {
400           p2 += 9;
401           if (*p2 == '=')
402             {
403               p2++;
404               if (_strnicmp(p2, "PICE", 4) == 0)
405                 {
406                   p2 += 4;
407                   PiceStart = 1;
408                 }
409             }
410         }
411       p1 = p2;
412     }
413
414   Status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
415                                  L"\\Pice",
416                                  L"Start",
417                                  REG_DWORD,
418                                  &PiceStart,
419                                  sizeof(ULONG));
420   if (!NT_SUCCESS(Status))
421     {
422       KeBugCheck(CONFIG_INITIALIZATION_FAILED);
423     }
424 }
425
426
427 static NTSTATUS
428 CmiCreateCurrentControlSetLink(VOID)
429 {
430   RTL_QUERY_REGISTRY_TABLE QueryTable[5];
431   WCHAR TargetNameBuffer[80];
432   ULONG TargetNameLength;
433   UNICODE_STRING LinkName;
434   UNICODE_STRING LinkValue;
435   ULONG CurrentSet;
436   ULONG DefaultSet;
437   ULONG Failed;
438   ULONG LastKnownGood;
439   NTSTATUS Status;
440   OBJECT_ATTRIBUTES ObjectAttributes;
441   HANDLE KeyHandle;
442
443   DPRINT("CmiCreateCurrentControlSetLink() called\n");
444
445   RtlZeroMemory(&QueryTable, sizeof(QueryTable));
446
447   QueryTable[0].Name = L"Current";
448   QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
449   QueryTable[0].EntryContext = &CurrentSet;
450
451   QueryTable[1].Name = L"Default";
452   QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
453   QueryTable[1].EntryContext = &DefaultSet;
454
455   QueryTable[2].Name = L"Failed";
456   QueryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
457   QueryTable[2].EntryContext = &Failed;
458
459   QueryTable[3].Name = L"LastKnownGood";
460   QueryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
461   QueryTable[3].EntryContext = &LastKnownGood;
462
463   Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
464                                   L"\\Registry\\Machine\\SYSTEM\\Select",
465                                   QueryTable,
466                                   NULL,
467                                   NULL);
468   if (!NT_SUCCESS(Status))
469     {
470       return(Status);
471     }
472
473   DPRINT("Current %ld  Default %ld\n", CurrentSet, DefaultSet);
474
475   swprintf(TargetNameBuffer,
476            L"\\Registry\\Machine\\SYSTEM\\ControlSet%03lu",
477            CurrentSet);
478   TargetNameLength = wcslen(TargetNameBuffer) * sizeof(WCHAR);
479
480   DPRINT("Link target '%S'\n", TargetNameBuffer);
481
482   RtlInitUnicodeStringFromLiteral(&LinkName,
483                                   L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet");
484   InitializeObjectAttributes(&ObjectAttributes,
485                              &LinkName,
486                              OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
487                              NULL,
488                              NULL);
489   Status = NtCreateKey(&KeyHandle,
490                        KEY_ALL_ACCESS | KEY_CREATE_LINK,
491                        &ObjectAttributes,
492                        0,
493                        NULL,
494                        REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
495                        NULL);
496   if (!NT_SUCCESS(Status))
497     {
498       DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
499       return(Status);
500     }
501
502   RtlInitUnicodeStringFromLiteral(&LinkValue,
503                                   L"SymbolicLinkValue");
504   Status = NtSetValueKey(KeyHandle,
505                          &LinkValue,
506                          0,
507                          REG_LINK,
508                          (PVOID)TargetNameBuffer,
509                          TargetNameLength);
510   if (!NT_SUCCESS(Status))
511     {
512       DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
513     }
514
515   NtClose(KeyHandle);
516
517   return(Status);
518 }
519
520
521 NTSTATUS
522 CmiConnectHive(PREGISTRY_HIVE RegistryHive,
523                PUNICODE_STRING KeyName)
524 {
525   OBJECT_ATTRIBUTES ObjectAttributes;
526   UNICODE_STRING ParentKeyName;
527   PKEY_OBJECT ParentKey;
528   PKEY_OBJECT NewKey;
529   HANDLE KeyHandle;
530   NTSTATUS Status;
531   PWSTR SubName;
532
533   DPRINT("CmiConnectHive(%p, %wZ) called.\n",
534          RegistryHive, KeyName);
535
536   SubName = wcsrchr (KeyName->Buffer, L'\\');
537   if (SubName == NULL)
538     {
539       return STATUS_UNSUCCESSFUL;
540     }
541
542   ParentKeyName.Length = (USHORT)(SubName - KeyName->Buffer) * sizeof(WCHAR);
543   ParentKeyName.MaximumLength = ParentKeyName.Length + sizeof(WCHAR);
544   ParentKeyName.Buffer = ExAllocatePool (NonPagedPool,
545                                          ParentKeyName.MaximumLength);
546   RtlCopyMemory (ParentKeyName.Buffer,
547                  KeyName->Buffer,
548                  ParentKeyName.Length);
549   ParentKeyName.Buffer[ParentKeyName.Length / sizeof(WCHAR)] = 0;
550   SubName++;
551
552   Status = ObReferenceObjectByName (&ParentKeyName,
553                                     OBJ_CASE_INSENSITIVE,
554                                     NULL,
555                                     STANDARD_RIGHTS_REQUIRED,
556                                     CmiKeyType,
557                                     KernelMode,
558                                     NULL,
559                                     (PVOID*)&ParentKey);
560   RtlFreeUnicodeString (&ParentKeyName);
561   if (!NT_SUCCESS(Status))
562     {
563       DPRINT1 ("ObReferenceObjectByName() failed (Status %lx)\n", Status);
564       return Status;
565     }
566
567   InitializeObjectAttributes(&ObjectAttributes,
568                              KeyName,
569                              0,
570                              NULL,
571                              NULL);
572
573   Status = ObCreateObject(&KeyHandle,
574                           STANDARD_RIGHTS_REQUIRED,
575                           &ObjectAttributes,
576                           CmiKeyType,
577                           (PVOID*)&NewKey);
578   if (!NT_SUCCESS(Status))
579     {
580       DPRINT1 ("ObCreateObject() failed (Status %lx)\n", Status);
581       ObDereferenceObject (ParentKey);
582       return Status;
583     }
584
585   NewKey->RegistryHive = RegistryHive;
586   NewKey->BlockOffset = RegistryHive->HiveHeader->RootKeyCell;
587   NewKey->KeyCell = CmiGetBlock(RegistryHive, NewKey->BlockOffset, NULL);
588   NewKey->Flags = 0;
589   NewKey->NumberOfSubKeys = 0;
590   NewKey->SubKeys = ExAllocatePool(PagedPool,
591   NewKey->KeyCell->NumberOfSubKeys * sizeof(ULONG));
592
593   if ((NewKey->SubKeys == NULL) && (NewKey->KeyCell->NumberOfSubKeys != 0))
594     {
595       DPRINT("NumberOfSubKeys %d\n", NewKey->KeyCell->NumberOfSubKeys);
596       NtClose(NewKey);
597       ObDereferenceObject (ParentKey);
598       return(STATUS_INSUFFICIENT_RESOURCES);
599     }
600
601   NewKey->SizeOfSubKeys = NewKey->KeyCell->NumberOfSubKeys;
602   NewKey->NameSize = wcslen (SubName);
603   NewKey->Name = ExAllocatePool(PagedPool, NewKey->NameSize);
604
605   if ((NewKey->Name == NULL) && (NewKey->NameSize != 0))
606     {
607       DPRINT("NewKey->NameSize %d\n", NewKey->NameSize);
608       if (NewKey->SubKeys != NULL)
609         ExFreePool(NewKey->SubKeys);
610       NtClose(KeyHandle);
611       ObDereferenceObject (ParentKey);
612       return(STATUS_INSUFFICIENT_RESOURCES);
613     }
614
615   wcstombs (NewKey->Name,
616             SubName,
617             NewKey->NameSize);
618
619   CmiAddKeyToList (ParentKey, NewKey);
620   ObDereferenceObject (ParentKey);
621
622   VERIFY_KEY_OBJECT(NewKey);
623
624   return(STATUS_SUCCESS);
625 }
626
627
628 static NTSTATUS
629 CmiInitializeSystemHive (PWSTR FileName,
630                          PUNICODE_STRING KeyName)
631 {
632   OBJECT_ATTRIBUTES ObjectAttributes;
633   UNICODE_STRING ControlSetKeyName;
634   UNICODE_STRING ControlSetLinkName;
635   UNICODE_STRING ControlSetValueName;
636   HANDLE KeyHandle;
637   NTSTATUS Status;
638   PREGISTRY_HIVE RegistryHive;
639
640   Status = CmiCreateRegistryHive (FileName,
641                                   &RegistryHive,
642                                   TRUE);
643   if (!NT_SUCCESS(Status))
644     {
645       DPRINT1 ("CmiCreateRegistryHive() failed (Status %lx)\n", Status);
646       return Status;
647     }
648
649   Status = CmiConnectHive (RegistryHive,
650                            KeyName);
651   if (!NT_SUCCESS(Status))
652     {
653       DPRINT1 ("CmiConnectHive() failed (Status %lx)\n", Status);
654       CmiRemoveRegistryHive (RegistryHive);
655       return Status;
656     }
657
658   /* Create 'ControlSet001' key */
659   RtlInitUnicodeStringFromLiteral (&ControlSetKeyName,
660                                    L"\\Registry\\Machine\\SYSTEM\\ControlSet001");
661   InitializeObjectAttributes (&ObjectAttributes,
662                               &ControlSetKeyName,
663                               OBJ_CASE_INSENSITIVE,
664                               NULL,
665                               NULL);
666   Status = NtCreateKey (&KeyHandle,
667                         KEY_ALL_ACCESS,
668                         &ObjectAttributes,
669                         0,
670                         NULL,
671                         REG_OPTION_NON_VOLATILE,
672                         NULL);
673   if (!NT_SUCCESS(Status))
674     {
675       DPRINT1 ("NtCreateKey() failed (Status %lx)\n", Status);
676       return Status;
677     }
678   NtClose (KeyHandle);
679
680   /* Link 'CurrentControlSet' to 'ControlSet001' key */
681   RtlInitUnicodeStringFromLiteral (&ControlSetLinkName,
682                                    L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet");
683   InitializeObjectAttributes (&ObjectAttributes,
684                               &ControlSetLinkName,
685                               OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
686                               NULL,
687                               NULL);
688   Status = NtCreateKey (&KeyHandle,
689                         KEY_ALL_ACCESS | KEY_CREATE_LINK,
690                         &ObjectAttributes,
691                         0,
692                         NULL,
693                         REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
694                         NULL);
695   if (!NT_SUCCESS(Status))
696     {
697       DPRINT1 ("NtCreateKey() failed (Status %lx)\n", Status);
698       return Status;
699     }
700
701   RtlInitUnicodeStringFromLiteral (&ControlSetValueName,
702                                    L"SymbolicLinkValue");
703   Status = NtSetValueKey (KeyHandle,
704                           &ControlSetValueName,
705                           0,
706                           REG_LINK,
707                           (PVOID)ControlSetKeyName.Buffer,
708                           ControlSetKeyName.Length);
709   if (!NT_SUCCESS(Status))
710     {
711       DPRINT1 ("NtSetValueKey() failed (Status %lx)\n", Status);
712     }
713   NtClose (KeyHandle);
714
715   return STATUS_SUCCESS;
716 }
717
718
719 NTSTATUS
720 CmiInitializeHive(PWSTR FileName,
721                   PUNICODE_STRING KeyName,
722                   BOOLEAN CreateNew)
723 {
724   PREGISTRY_HIVE RegistryHive;
725   NTSTATUS Status;
726
727   DPRINT("CmiInitializeHive(%s) called\n", KeyName);
728
729   Status = CmiCreateRegistryHive(FileName,
730                                  &RegistryHive,
731                                  CreateNew);
732   if (!NT_SUCCESS(Status))
733     {
734       DPRINT1("CmiCreateRegistryHive() failed (Status %lx)\n", Status);
735
736       /* FIXME: Try to load the backup hive */
737
738       KeBugCheck(0);
739       return(Status);
740     }
741
742   /* Connect the hive */
743   Status = CmiConnectHive(RegistryHive,
744                           KeyName);
745   if (!NT_SUCCESS(Status))
746     {
747       DPRINT1("CmiConnectHive() failed (Status %lx)\n", Status);
748       CmiRemoveRegistryHive(RegistryHive);
749       return(Status);
750     }
751
752   DPRINT("CmiInitializeHive() done\n");
753
754   return(STATUS_SUCCESS);
755 }
756
757
758 NTSTATUS
759 CmiInitHives(BOOLEAN SetupBoot)
760 {
761   PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
762   OBJECT_ATTRIBUTES ObjectAttributes;
763   UNICODE_STRING KeyName;
764   UNICODE_STRING ValueName;
765   HANDLE KeyHandle;
766
767   NTSTATUS Status;
768
769   WCHAR ConfigPath[MAX_PATH];
770
771   ULONG BufferSize;
772   ULONG ResultSize;
773   PWSTR EndPtr;
774
775
776   DPRINT("CmiInitHives() called\n");
777
778   if (SetupBoot == TRUE)
779     {
780       RtlInitUnicodeStringFromLiteral(&KeyName,
781                                       L"\\Registry\\Machine\\HARDWARE");
782       InitializeObjectAttributes(&ObjectAttributes,
783                                  &KeyName,
784                                  OBJ_CASE_INSENSITIVE,
785                                  NULL,
786                                  NULL);
787       Status =  NtOpenKey(&KeyHandle,
788                           KEY_ALL_ACCESS,
789                           &ObjectAttributes);
790       if (!NT_SUCCESS(Status))
791         {
792           DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
793           return(Status);
794         }
795
796       RtlInitUnicodeStringFromLiteral(&ValueName,
797                                       L"InstallPath");
798
799       BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
800       ValueInfo = ExAllocatePool(PagedPool,
801                                  BufferSize);
802       if (ValueInfo == NULL)
803         {
804           NtClose(KeyHandle);
805           return(STATUS_INSUFFICIENT_RESOURCES);
806         }
807
808       Status = NtQueryValueKey(KeyHandle,
809                                &ValueName,
810                                KeyValuePartialInformation,
811                                ValueInfo,
812                                BufferSize,
813                                &ResultSize);
814       NtClose(KeyHandle);
815       if (ValueInfo == NULL)
816         {
817           ExFreePool(ValueInfo);
818           return(Status);
819         }
820
821       RtlCopyMemory(ConfigPath,
822                     ValueInfo->Data,
823                     ValueInfo->DataLength);
824       ConfigPath[ValueInfo->DataLength / sizeof(WCHAR)] = (WCHAR)0;
825       ExFreePool(ValueInfo);
826     }
827   else
828     {
829       wcscpy(ConfigPath, L"\\SystemRoot");
830     }
831   wcscat(ConfigPath, L"\\system32\\config");
832
833   DPRINT("ConfigPath: %S\n", ConfigPath);
834
835   EndPtr = ConfigPath + wcslen(ConfigPath);
836
837   CmiDoVerify = TRUE;
838
839   /* FIXME: Save boot log */
840
841   /* Connect the SYSTEM hive only if it has been created */
842   if (SetupBoot == TRUE)
843     {
844       wcscpy(EndPtr, REG_SYSTEM_FILE_NAME);
845       DPRINT ("ConfigPath: %S\n", ConfigPath);
846
847       RtlInitUnicodeString (&KeyName,
848                             REG_SYSTEM_KEY_NAME);
849       Status = CmiInitializeSystemHive (ConfigPath,
850                                         &KeyName);
851       if (!NT_SUCCESS(Status))
852         {
853           DPRINT1("CmiInitializeSystemHive() failed (Status %lx)\n", Status);
854           return(Status);
855         }
856     }
857
858   /* Connect the SOFTWARE hive */
859   wcscpy(EndPtr, REG_SOFTWARE_FILE_NAME);
860   DPRINT ("ConfigPath: %S\n", ConfigPath);
861
862   RtlInitUnicodeString (&KeyName,
863                         REG_SOFTWARE_KEY_NAME);
864   Status = CmiInitializeHive(ConfigPath,
865                              &KeyName,
866                              SetupBoot);
867   if (!NT_SUCCESS(Status))
868     {
869       DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
870       return(Status);
871     }
872
873   /* Connect the SAM hive */
874   wcscpy(EndPtr, REG_SAM_FILE_NAME);
875   DPRINT ("ConfigPath: %S\n", ConfigPath);
876
877   RtlInitUnicodeString (&KeyName,
878                         REG_SAM_KEY_NAME);
879   Status = CmiInitializeHive(ConfigPath,
880                              &KeyName,
881                              SetupBoot);
882   if (!NT_SUCCESS(Status))
883     {
884       DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
885       return(Status);
886     }
887
888   /* Connect the SECURITY hive */
889   wcscpy(EndPtr, REG_SEC_FILE_NAME);
890   DPRINT ("ConfigPath: %S\n", ConfigPath);
891
892   RtlInitUnicodeString (&KeyName,
893                         REG_SEC_KEY_NAME);
894   Status = CmiInitializeHive(ConfigPath,
895                              &KeyName,
896                              SetupBoot);
897   if (!NT_SUCCESS(Status))
898     {
899       DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
900       return(Status);
901     }
902
903   /* Connect the DEFAULT hive */
904   wcscpy(EndPtr, REG_USER_FILE_NAME);
905   DPRINT ("ConfigPath: %S\n", ConfigPath);
906
907   RtlInitUnicodeString (&KeyName,
908                         REG_USER_KEY_NAME);
909   Status = CmiInitializeHive(ConfigPath,
910                              &KeyName,
911                              SetupBoot);
912   if (!NT_SUCCESS(Status))
913     {
914       DPRINT1("CmiInitializeHive() failed (Status %lx)\n", Status);
915       return(Status);
916     }
917
918 //  CmiCheckRegistry(TRUE);
919
920   /* Start automatic hive synchronization */
921   KeInitializeDpc(&CmiHiveSyncDpc,
922                   CmiHiveSyncDpcRoutine,
923                   NULL);
924   KeInitializeTimer(&CmiHiveSyncTimer);
925   CmiHiveSyncEnabled = TRUE;
926
927   DPRINT("CmiInitHives() done\n");
928
929   return(STATUS_SUCCESS);
930 }
931
932
933 VOID
934 CmShutdownRegistry(VOID)
935 {
936   PREGISTRY_HIVE Hive;
937   PLIST_ENTRY Entry;
938
939   DPRINT1("CmShutdownRegistry() called\n");
940
941   /* Stop automatic hive synchronization */
942   CmiHiveSyncEnabled = FALSE;
943
944   /* Acquire hive list lock exclusively */
945   ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
946
947   Entry = CmiHiveListHead.Flink;
948   while (Entry != &CmiHiveListHead)
949     {
950       Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
951
952       if (!IsVolatileHive(Hive))
953         {
954           /* Acquire hive resource exclusively */
955           ExAcquireResourceExclusiveLite(&Hive->HiveResource,
956                                          TRUE);
957
958           /* Flush non-volatile hive */
959           CmiFlushRegistryHive(Hive);
960
961           /* Release hive resource */
962           ExReleaseResourceLite(&Hive->HiveResource);
963         }
964
965       Entry = Entry->Flink;
966     }
967
968   /* Release hive list lock */
969   ExReleaseResourceLite(&CmiHiveListLock);
970
971   DPRINT1("CmShutdownRegistry() done\n");
972 }
973
974
975 VOID STDCALL
976 CmiHiveSyncRoutine(PVOID DeferredContext)
977 {
978   PREGISTRY_HIVE Hive;
979   PLIST_ENTRY Entry;
980
981   DPRINT("CmiHiveSyncRoutine() called\n");
982
983   CmiHiveSyncPending = FALSE;
984
985   /* Acquire hive list lock exclusively */
986   ExAcquireResourceExclusiveLite(&CmiHiveListLock, TRUE);
987
988   Entry = CmiHiveListHead.Flink;
989   while (Entry != &CmiHiveListHead)
990     {
991       Hive = CONTAINING_RECORD(Entry, REGISTRY_HIVE, HiveList);
992
993       if (!IsVolatileHive(Hive))
994         {
995           /* Acquire hive resource exclusively */
996           ExAcquireResourceExclusiveLite(&Hive->HiveResource,
997                                          TRUE);
998
999           /* Flush non-volatile hive */
1000           CmiFlushRegistryHive(Hive);
1001
1002           /* Release hive resource */
1003           ExReleaseResourceLite(&Hive->HiveResource);
1004         }
1005
1006       Entry = Entry->Flink;
1007     }
1008
1009   /* Release hive list lock */
1010   ExReleaseResourceLite(&CmiHiveListLock);
1011
1012   DPRINT("DeferredContext %x\n", DeferredContext);
1013   ExFreePool(DeferredContext);
1014
1015   DPRINT("CmiHiveSyncRoutine() done\n");
1016 }
1017
1018
1019 static VOID STDCALL
1020 CmiHiveSyncDpcRoutine(PKDPC Dpc,
1021                       PVOID DeferredContext,
1022                       PVOID SystemArgument1,
1023                       PVOID SystemArgument2)
1024 {
1025   PWORK_QUEUE_ITEM WorkQueueItem;
1026
1027   WorkQueueItem = ExAllocatePool(NonPagedPool,
1028                                  sizeof(WORK_QUEUE_ITEM));
1029   if (WorkQueueItem == NULL)
1030     {
1031       DbgPrint("Failed to allocate work item\n");
1032       return;
1033     }
1034
1035   ExInitializeWorkItem(WorkQueueItem,
1036                        CmiHiveSyncRoutine,
1037                        WorkQueueItem);
1038
1039   DPRINT("DeferredContext %x\n", WorkQueueItem);
1040   ExQueueWorkItem(WorkQueueItem,
1041                   CriticalWorkQueue);
1042 }
1043
1044
1045 VOID
1046 CmiSyncHives(VOID)
1047 {
1048   LARGE_INTEGER Timeout;
1049
1050   DPRINT("CmiSyncHives() called\n");
1051
1052   if (CmiHiveSyncEnabled == FALSE ||
1053       CmiHiveSyncPending == TRUE)
1054     return;
1055
1056   CmiHiveSyncPending = TRUE;
1057
1058
1059   Timeout.QuadPart = -50000000LL;
1060   KeSetTimer(&CmiHiveSyncTimer,
1061              Timeout,
1062              &CmiHiveSyncDpc);
1063 }
1064
1065 /* EOF */