xref: /reactos/ntoskrnl/config/cminit.c (revision 50cf16b3)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/config/cminit.c
5  * PURPOSE:         Configuration Manager - Hive Initialization
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14 
15 /* FUNCTIONS *****************************************************************/
16 
17 NTSTATUS
18 NTAPI
19 CmpInitializeHive(OUT PCMHIVE *CmHive,
20                   IN ULONG OperationType,
21                   IN ULONG HiveFlags,
22                   IN ULONG FileType,
23                   IN PVOID HiveData OPTIONAL,
24                   IN HANDLE Primary,
25                   IN HANDLE Log,
26                   IN HANDLE External,
27                   IN PCUNICODE_STRING FileName OPTIONAL,
28                   IN ULONG CheckFlags)
29 {
30     PCMHIVE Hive;
31     IO_STATUS_BLOCK IoStatusBlock;
32     FILE_FS_SIZE_INFORMATION FileSizeInformation;
33     NTSTATUS Status;
34     ULONG Cluster;
35 
36     /* Assume failure */
37     *CmHive = NULL;
38 
39     /*
40      * The following are invalid:
41      * - An external hive that is also internal.
42      * - A log hive that is not a primary hive too.
43      * - A volatile hive that is linked to permanent storage,
44      *   unless this hive is a shared system hive.
45      * - An in-memory initialization without hive data.
46      * - A log hive that is not linked to a correct file type.
47      */
48     if (((External) && ((Primary) || (Log))) ||
49         ((Log) && !(Primary)) ||
50         (!(CmpShareSystemHives) && (HiveFlags & HIVE_VOLATILE) &&
51             ((Primary) || (External) || (Log))) ||
52         ((OperationType == HINIT_MEMORY) && (!HiveData)) ||
53         ((Log) && (FileType != HFILE_TYPE_LOG)))
54     {
55         /* Fail the request */
56         return STATUS_INVALID_PARAMETER;
57     }
58 
59     /* Check if this is a primary hive */
60     if (Primary)
61     {
62         /* Get the cluster size */
63         Status = ZwQueryVolumeInformationFile(Primary,
64                                               &IoStatusBlock,
65                                               &FileSizeInformation,
66                                               sizeof(FILE_FS_SIZE_INFORMATION),
67                                               FileFsSizeInformation);
68         if (!NT_SUCCESS(Status)) return Status;
69 
70         /* Make sure it's not larger then the block size */
71         if (FileSizeInformation.BytesPerSector > HBLOCK_SIZE)
72         {
73             /* Fail */
74             return STATUS_REGISTRY_IO_FAILED;
75         }
76 
77         /* Otherwise, calculate the cluster */
78         Cluster = FileSizeInformation.BytesPerSector / HSECTOR_SIZE;
79         Cluster = max(1, Cluster);
80     }
81     else
82     {
83         /* Otherwise use cluster 1 */
84         Cluster = 1;
85     }
86 
87     /* Allocate the hive */
88     Hive = ExAllocatePoolWithTag(NonPagedPool, sizeof(CMHIVE), TAG_CMHIVE);
89     if (!Hive) return STATUS_INSUFFICIENT_RESOURCES;
90 
91     /* Setup null fields */
92     Hive->UnloadEvent = NULL;
93     Hive->RootKcb = NULL;
94     Hive->Frozen = FALSE;
95     Hive->UnloadWorkItem = NULL;
96     Hive->GrowOnlyMode = FALSE;
97     Hive->GrowOffset = 0;
98     Hive->CellRemapArray = NULL;
99     Hive->UseCountLog.Next = 0;
100     Hive->LockHiveLog.Next = 0;
101     Hive->FileObject = NULL;
102     Hive->NotifyList.Flink = NULL;
103     Hive->NotifyList.Blink = NULL;
104 
105     /* Set the loading flag */
106     Hive->HiveIsLoading = TRUE;
107 
108     /* Set the current thread as creator */
109     Hive->CreatorOwner = KeGetCurrentThread();
110 
111     /* Initialize lists */
112     InitializeListHead(&Hive->KcbConvertListHead);
113     InitializeListHead(&Hive->KnodeConvertListHead);
114     InitializeListHead(&Hive->TrustClassEntry);
115 
116     /* Allocate the view log */
117     Hive->ViewLock = ExAllocatePoolWithTag(NonPagedPool,
118                                            sizeof(KGUARDED_MUTEX),
119                                            TAG_CMHIVE);
120     if (!Hive->ViewLock)
121     {
122         /* Cleanup allocation and fail */
123         ExFreePoolWithTag(Hive, TAG_CMHIVE);
124         return STATUS_INSUFFICIENT_RESOURCES;
125     }
126 
127     /* Allocate the flush lock */
128     Hive->FlusherLock = ExAllocatePoolWithTag(NonPagedPool,
129                                               sizeof(ERESOURCE),
130                                               TAG_CMHIVE);
131     if (!Hive->FlusherLock)
132     {
133         /* Cleanup allocations and fail */
134         ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE);
135         ExFreePoolWithTag(Hive, TAG_CMHIVE);
136         return STATUS_INSUFFICIENT_RESOURCES;
137     }
138 
139     /* Setup the handles */
140     Hive->FileHandles[HFILE_TYPE_PRIMARY] = Primary;
141     Hive->FileHandles[HFILE_TYPE_LOG] = Log;
142     Hive->FileHandles[HFILE_TYPE_EXTERNAL] = External;
143 
144     /* Initailize the guarded mutex */
145     KeInitializeGuardedMutex(Hive->ViewLock);
146     Hive->ViewLockOwner = NULL;
147 
148     /* Initialize the flush lock */
149     ExInitializeResourceLite(Hive->FlusherLock);
150 
151     /* Setup hive locks */
152     ExInitializePushLock(&Hive->HiveLock);
153     Hive->HiveLockOwner = NULL;
154     ExInitializePushLock(&Hive->WriterLock);
155     Hive->WriterLockOwner = NULL;
156     ExInitializePushLock(&Hive->SecurityLock);
157     Hive->HiveSecurityLockOwner = NULL;
158 
159     /* Clear file names */
160     RtlInitEmptyUnicodeString(&Hive->FileUserName, NULL, 0);
161     RtlInitEmptyUnicodeString(&Hive->FileFullPath, NULL, 0);
162 
163     /* Initialize the view list */
164     CmpInitHiveViewList(Hive);
165 
166     /* Initailize the security cache */
167     CmpInitSecurityCache(Hive);
168 
169     /* Setup flags */
170     Hive->Flags = 0;
171     Hive->FlushCount = 0;
172 
173     /* Initialize it */
174     Status = HvInitialize(&Hive->Hive,
175                           OperationType,
176                           HiveFlags,
177                           FileType,
178                           HiveData,
179                           CmpAllocate,
180                           CmpFree,
181                           CmpFileSetSize,
182                           CmpFileWrite,
183                           CmpFileRead,
184                           CmpFileFlush,
185                           Cluster,
186                           FileName);
187     if (!NT_SUCCESS(Status))
188     {
189         /* Cleanup allocations and fail */
190         ExDeleteResourceLite(Hive->FlusherLock);
191         ExFreePoolWithTag(Hive->FlusherLock, TAG_CMHIVE);
192         ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE);
193         ExFreePoolWithTag(Hive, TAG_CMHIVE);
194         return Status;
195     }
196 
197     /* Check if we should verify the registry */
198     if ((OperationType == HINIT_FILE) ||
199         (OperationType == HINIT_MEMORY) ||
200         (OperationType == HINIT_MEMORY_INPLACE) ||
201         (OperationType == HINIT_MAPFILE))
202     {
203         /* Verify integrity */
204         ULONG CheckStatus = CmCheckRegistry(Hive, CheckFlags);
205         if (CheckStatus != 0)
206         {
207             /* Cleanup allocations and fail */
208             ExDeleteResourceLite(Hive->FlusherLock);
209             ExFreePoolWithTag(Hive->FlusherLock, TAG_CMHIVE);
210             ExFreePoolWithTag(Hive->ViewLock, TAG_CMHIVE);
211             ExFreePoolWithTag(Hive, TAG_CMHIVE);
212             return STATUS_REGISTRY_CORRUPT;
213         }
214     }
215 
216     /* Reset the loading flag */
217     Hive->HiveIsLoading = FALSE;
218 
219     /* Lock the hive list */
220     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
221 
222     /* Insert this hive */
223     InsertHeadList(&CmpHiveListHead, &Hive->HiveList);
224 
225     /* Release the lock */
226     ExReleasePushLock(&CmpHiveListHeadLock);
227 
228     /* Return the hive and success */
229     *CmHive = Hive;
230     return STATUS_SUCCESS;
231 }
232 
233 NTSTATUS
234 NTAPI
235 CmpDestroyHive(IN PCMHIVE CmHive)
236 {
237     /* Remove the hive from the list */
238     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
239     RemoveEntryList(&CmHive->HiveList);
240     ExReleasePushLock(&CmpHiveListHeadLock);
241 
242     /* Destroy the security descriptor cache */
243     CmpDestroySecurityCache(CmHive);
244 
245     /* Destroy the view list */
246     CmpDestroyHiveViewList(CmHive);
247 
248     /* Delete the flusher lock */
249     ExDeleteResourceLite(CmHive->FlusherLock);
250     ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE);
251 
252     /* Delete the view lock */
253     ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE);
254 
255     /* Free the hive storage */
256     HvFree(&CmHive->Hive);
257 
258     /* Free the hive */
259     CmpFree(CmHive, TAG_CM);
260 
261     return STATUS_SUCCESS;
262 }
263 
264 NTSTATUS
265 NTAPI
266 CmpOpenHiveFiles(IN PCUNICODE_STRING BaseName,
267                  IN PCWSTR Extension OPTIONAL,
268                  OUT PHANDLE Primary,
269                  OUT PHANDLE Log,
270                  OUT PULONG PrimaryDisposition,
271                  OUT PULONG LogDisposition,
272                  IN BOOLEAN CreateAllowed,
273                  IN BOOLEAN MarkAsSystemHive,
274                  IN BOOLEAN NoBuffering,
275                  OUT PULONG ClusterSize OPTIONAL)
276 {
277     HANDLE EventHandle;
278     PKEVENT Event;
279     NTSTATUS Status;
280     UNICODE_STRING FullName, ExtensionName;
281     PWCHAR NameBuffer;
282     USHORT Length;
283     OBJECT_ATTRIBUTES ObjectAttributes;
284     IO_STATUS_BLOCK IoStatusBlock;
285     ULONG AttributeFlags, ShareMode, DesiredAccess, CreateDisposition, IoFlags;
286     USHORT CompressionState;
287     FILE_STANDARD_INFORMATION FileInformation;
288     FILE_FS_SIZE_INFORMATION FsSizeInformation;
289 
290     /* Create event */
291     Status = CmpCreateEvent(NotificationEvent, &EventHandle, &Event);
292     if (!NT_SUCCESS(Status)) return Status;
293 
294     /* Initialize the full name */
295     RtlInitEmptyUnicodeString(&FullName, NULL, 0);
296     Length = BaseName->Length;
297 
298     /* Check if we have an extension */
299     if (Extension)
300     {
301         /* Update the name length */
302         Length += (USHORT)wcslen(Extension) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
303 
304         /* Allocate the buffer for the full name */
305         NameBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
306         if (!NameBuffer)
307         {
308             /* Fail */
309             ObDereferenceObject(Event);
310             ZwClose(EventHandle);
311             return STATUS_NO_MEMORY;
312         }
313 
314         /* Build the full name */
315         FullName.Buffer = NameBuffer;
316         FullName.MaximumLength = Length;
317         RtlCopyUnicodeString(&FullName, BaseName);
318     }
319     else
320     {
321         /* The base name is the full name */
322         FullName = *BaseName;
323         NameBuffer = NULL;
324     }
325 
326     /* Initialize the attributes */
327     InitializeObjectAttributes(&ObjectAttributes,
328                                &FullName,
329                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
330                                NULL,
331                                NULL);
332 
333     /* Check if we can create the hive */
334     if ((CreateAllowed) && !(CmpShareSystemHives))
335     {
336         /* Open only or create */
337         CreateDisposition = FILE_OPEN_IF;
338     }
339     else
340     {
341         /* Open only */
342         CreateDisposition = FILE_OPEN;
343     }
344 
345     /* Setup the flags */
346     // FIXME : FILE_OPEN_FOR_BACKUP_INTENT is unimplemented and breaks 3rd stage boot
347     IoFlags = //FILE_OPEN_FOR_BACKUP_INTENT |
348               FILE_NO_COMPRESSION |
349               FILE_RANDOM_ACCESS |
350               (NoBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0);
351 
352     /* Set share and access modes */
353     if ((CmpMiniNTBoot) && (CmpShareSystemHives))
354     {
355         /* We're on Live CD or otherwise sharing */
356         DesiredAccess = FILE_READ_DATA;
357         ShareMode = FILE_SHARE_READ;
358     }
359     else
360     {
361         /* We want to write exclusively */
362         ShareMode = 0;
363         DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA;
364     }
365 
366     /* Default attributes */
367     AttributeFlags = FILE_ATTRIBUTE_NORMAL;
368 
369     /* Now create the file */
370     Status = ZwCreateFile(Primary,
371                           DesiredAccess | SYNCHRONIZE,
372                           &ObjectAttributes,
373                           &IoStatusBlock,
374                           NULL,
375                           AttributeFlags,
376                           ShareMode,
377                           CreateDisposition,
378                           FILE_SYNCHRONOUS_IO_NONALERT | IoFlags,
379                           NULL,
380                           0);
381     /* Check if anything failed until now */
382     if (!NT_SUCCESS(Status))
383     {
384         DPRINT1("ZwCreateFile(%wZ) failed, Status 0x%08lx.\n", ObjectAttributes.ObjectName, Status);
385 
386         /* Close handles and free buffers */
387         if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
388         ObDereferenceObject(Event);
389         ZwClose(EventHandle);
390         *Primary = NULL;
391         return Status;
392     }
393 
394     if (MarkAsSystemHive)
395     {
396         /* We opened it, mark it as a system hive */
397         Status = ZwFsControlFile(*Primary,
398                                  EventHandle,
399                                  NULL,
400                                  NULL,
401                                  &IoStatusBlock,
402                                  FSCTL_MARK_AS_SYSTEM_HIVE,
403                                  NULL,
404                                  0,
405                                  NULL,
406                                  0);
407         if (Status == STATUS_PENDING)
408         {
409             /* Wait for completion */
410             KeWaitForSingleObject(Event,
411                                   Executive,
412                                   KernelMode,
413                                   FALSE,
414                                   NULL);
415             Status = IoStatusBlock.Status;
416         }
417 
418         /* If we don't support it, ignore the failure */
419         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
420 
421         if (!NT_SUCCESS(Status))
422         {
423             /* Close handles and free buffers */
424             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
425             ObDereferenceObject(Event);
426             ZwClose(EventHandle);
427             ZwClose(*Primary);
428             *Primary = NULL;
429             return Status;
430         }
431     }
432 
433     /* Disable compression */
434     CompressionState = 0;
435     Status = ZwFsControlFile(*Primary,
436                              EventHandle,
437                              NULL,
438                              NULL,
439                              &IoStatusBlock,
440                              FSCTL_SET_COMPRESSION,
441                              &CompressionState,
442                              sizeof(CompressionState),
443                              NULL,
444                              0);
445     if (Status == STATUS_PENDING)
446     {
447         /* Wait for completion */
448         KeWaitForSingleObject(Event,
449                               Executive,
450                               KernelMode,
451                               FALSE,
452                               NULL);
453     }
454 
455     /* Get the disposition */
456     *PrimaryDisposition = (ULONG)IoStatusBlock.Information;
457     if (IoStatusBlock.Information != FILE_CREATED)
458     {
459         /* Check how large the file is */
460         Status = ZwQueryInformationFile(*Primary,
461                                         &IoStatusBlock,
462                                         &FileInformation,
463                                         sizeof(FileInformation),
464                                         FileStandardInformation);
465         if (NT_SUCCESS(Status))
466         {
467             /* Check if it's 0 bytes */
468             if (!FileInformation.EndOfFile.QuadPart)
469             {
470                 /* Assume it's a new file */
471                 *PrimaryDisposition = FILE_CREATED;
472             }
473         }
474     }
475 
476     /* Check if the caller wants cluster size returned */
477     if (ClusterSize)
478     {
479         /* Query it */
480         Status = ZwQueryVolumeInformationFile(*Primary,
481                                               &IoStatusBlock,
482                                               &FsSizeInformation,
483                                               sizeof(FsSizeInformation),
484                                               FileFsSizeInformation);
485         if (!NT_SUCCESS(Status))
486         {
487             /* Close handles and free buffers */
488             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
489             ObDereferenceObject(Event);
490             ZwClose(EventHandle);
491             return Status;
492         }
493 
494         /* Check if the sector size is invalid */
495         if (FsSizeInformation.BytesPerSector > HBLOCK_SIZE)
496         {
497             /* Close handles and free buffers */
498             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
499             ObDereferenceObject(Event);
500             ZwClose(EventHandle);
501             return STATUS_CANNOT_LOAD_REGISTRY_FILE;
502         }
503 
504         /* Return cluster size */
505         *ClusterSize = max(1, FsSizeInformation.BytesPerSector / HSECTOR_SIZE);
506     }
507 
508     /* Check if we don't need to create a log file */
509     if (!Extension)
510     {
511         /* We're done, close handles */
512         ObDereferenceObject(Event);
513         ZwClose(EventHandle);
514         return STATUS_SUCCESS;
515     }
516 
517     /* Check if we can create the hive */
518     CreateDisposition = CmpShareSystemHives ? FILE_OPEN : FILE_OPEN_IF;
519     if (*PrimaryDisposition == FILE_CREATED)
520     {
521         /* Over-write the existing log file, since this is a new hive */
522         CreateDisposition = FILE_SUPERSEDE;
523     }
524 
525     /* Setup the name */
526     RtlInitUnicodeString(&ExtensionName, Extension);
527     RtlAppendUnicodeStringToString(&FullName, &ExtensionName);
528 
529     /* Initialize the attributes */
530     InitializeObjectAttributes(&ObjectAttributes,
531                                &FullName,
532                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
533                                NULL,
534                                NULL);
535 
536     /* Setup the flags */
537     IoFlags = FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING;
538 
539     /* Check if this is a log file */
540     if (!_wcsnicmp(Extension, L".log", 4))
541     {
542         /* Hide log files */
543         AttributeFlags |= FILE_ATTRIBUTE_HIDDEN;
544     }
545 
546     /* Now create the file */
547     Status = ZwCreateFile(Log,
548                           DesiredAccess,
549                           &ObjectAttributes,
550                           &IoStatusBlock,
551                           NULL,
552                           AttributeFlags,
553                           ShareMode,
554                           CreateDisposition,
555                           IoFlags,
556                           NULL,
557                           0);
558     if ((NT_SUCCESS(Status)) && (MarkAsSystemHive))
559     {
560         /* We opened it, mark it as a system hive */
561         Status = ZwFsControlFile(*Log,
562                                  EventHandle,
563                                  NULL,
564                                  NULL,
565                                  &IoStatusBlock,
566                                  FSCTL_MARK_AS_SYSTEM_HIVE,
567                                  NULL,
568                                  0,
569                                  NULL,
570                                  0);
571         if (Status == STATUS_PENDING)
572         {
573             /* Wait for completion */
574             KeWaitForSingleObject(Event,
575                                   Executive,
576                                   KernelMode,
577                                   FALSE,
578                                   NULL);
579             Status = IoStatusBlock.Status;
580         }
581 
582         /* If we don't support it, ignore the failure */
583         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
584 
585         /* If we failed, close the handle */
586         if (!NT_SUCCESS(Status)) ZwClose(*Log);
587     }
588 
589     /* Check if anything failed until now */
590     if (!NT_SUCCESS(Status))
591     {
592         /* Clear the handle */
593         *Log = NULL;
594     }
595     else
596     {
597         /* Disable compression */
598         Status = ZwFsControlFile(*Log,
599                                  EventHandle,
600                                  NULL,
601                                  NULL,
602                                  &IoStatusBlock,
603                                  FSCTL_SET_COMPRESSION,
604                                  &CompressionState,
605                                  sizeof(CompressionState),
606                                  NULL,
607                                  0);
608         if (Status == STATUS_PENDING)
609         {
610             /* Wait for completion */
611             KeWaitForSingleObject(Event,
612                                   Executive,
613                                   KernelMode,
614                                   FALSE,
615                                   NULL);
616         }
617 
618         /* Return the disposition */
619         *LogDisposition = (ULONG)IoStatusBlock.Information;
620     }
621 
622     /* We're done, close handles and free buffers */
623     if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
624     ObDereferenceObject(Event);
625     ZwClose(EventHandle);
626     return STATUS_SUCCESS;
627 }
628 
629 VOID
630 NTAPI
631 CmpCloseHiveFiles(IN PCMHIVE Hive)
632 {
633     ULONG i;
634 
635     for (i = 0; i < HFILE_TYPE_MAX; i++)
636     {
637         if (Hive->FileHandles[i] != NULL)
638         {
639             ZwClose(Hive->FileHandles[i]);
640             Hive->FileHandles[i] = NULL;
641         }
642     }
643 }
644