xref: /reactos/ntoskrnl/config/cminit.c (revision 09dde2cf)
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      * Note: We use FILE_SYNCHRONOUS_IO_NONALERT here to simplify CmpFileRead/CmpFileWrite.
371      *       Windows does async I/O and therefore does not use this flag (or SYNCHRONIZE).
372      */
373     Status = ZwCreateFile(Primary,
374                           DesiredAccess | SYNCHRONIZE,
375                           &ObjectAttributes,
376                           &IoStatusBlock,
377                           NULL,
378                           AttributeFlags,
379                           ShareMode,
380                           CreateDisposition,
381                           FILE_SYNCHRONOUS_IO_NONALERT | IoFlags,
382                           NULL,
383                           0);
384     /* Check if anything failed until now */
385     if (!NT_SUCCESS(Status))
386     {
387         DPRINT1("ZwCreateFile(%wZ) failed, Status 0x%08lx.\n", ObjectAttributes.ObjectName, Status);
388 
389         /* Close handles and free buffers */
390         if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
391         ObDereferenceObject(Event);
392         ZwClose(EventHandle);
393         *Primary = NULL;
394         return Status;
395     }
396 
397     if (MarkAsSystemHive)
398     {
399         /* We opened it, mark it as a system hive */
400         Status = ZwFsControlFile(*Primary,
401                                  EventHandle,
402                                  NULL,
403                                  NULL,
404                                  &IoStatusBlock,
405                                  FSCTL_MARK_AS_SYSTEM_HIVE,
406                                  NULL,
407                                  0,
408                                  NULL,
409                                  0);
410         if (Status == STATUS_PENDING)
411         {
412             /* Wait for completion */
413             KeWaitForSingleObject(Event,
414                                   Executive,
415                                   KernelMode,
416                                   FALSE,
417                                   NULL);
418             Status = IoStatusBlock.Status;
419         }
420 
421         /* If we don't support it, ignore the failure */
422         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
423 
424         if (!NT_SUCCESS(Status))
425         {
426             /* Close handles and free buffers */
427             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
428             ObDereferenceObject(Event);
429             ZwClose(EventHandle);
430             ZwClose(*Primary);
431             *Primary = NULL;
432             return Status;
433         }
434     }
435 
436     /* Disable compression */
437     CompressionState = 0;
438     Status = ZwFsControlFile(*Primary,
439                              EventHandle,
440                              NULL,
441                              NULL,
442                              &IoStatusBlock,
443                              FSCTL_SET_COMPRESSION,
444                              &CompressionState,
445                              sizeof(CompressionState),
446                              NULL,
447                              0);
448     if (Status == STATUS_PENDING)
449     {
450         /* Wait for completion */
451         KeWaitForSingleObject(Event,
452                               Executive,
453                               KernelMode,
454                               FALSE,
455                               NULL);
456     }
457 
458     /* Get the disposition */
459     *PrimaryDisposition = (ULONG)IoStatusBlock.Information;
460     if (IoStatusBlock.Information != FILE_CREATED)
461     {
462         /* Check how large the file is */
463         Status = ZwQueryInformationFile(*Primary,
464                                         &IoStatusBlock,
465                                         &FileInformation,
466                                         sizeof(FileInformation),
467                                         FileStandardInformation);
468         if (NT_SUCCESS(Status))
469         {
470             /* Check if it's 0 bytes */
471             if (!FileInformation.EndOfFile.QuadPart)
472             {
473                 /* Assume it's a new file */
474                 *PrimaryDisposition = FILE_CREATED;
475             }
476         }
477     }
478 
479     /* Check if the caller wants cluster size returned */
480     if (ClusterSize)
481     {
482         /* Query it */
483         Status = ZwQueryVolumeInformationFile(*Primary,
484                                               &IoStatusBlock,
485                                               &FsSizeInformation,
486                                               sizeof(FsSizeInformation),
487                                               FileFsSizeInformation);
488         if (!NT_SUCCESS(Status))
489         {
490             /* Close handles and free buffers */
491             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
492             ObDereferenceObject(Event);
493             ZwClose(EventHandle);
494             return Status;
495         }
496 
497         /* Check if the sector size is invalid */
498         if (FsSizeInformation.BytesPerSector > HBLOCK_SIZE)
499         {
500             /* Close handles and free buffers */
501             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
502             ObDereferenceObject(Event);
503             ZwClose(EventHandle);
504             return STATUS_CANNOT_LOAD_REGISTRY_FILE;
505         }
506 
507         /* Return cluster size */
508         *ClusterSize = max(1, FsSizeInformation.BytesPerSector / HSECTOR_SIZE);
509     }
510 
511     /* Check if we don't need to create a log file */
512     if (!Extension)
513     {
514         /* We're done, close handles */
515         ObDereferenceObject(Event);
516         ZwClose(EventHandle);
517         return STATUS_SUCCESS;
518     }
519 
520     /* Check if we can create the hive */
521     CreateDisposition = CmpShareSystemHives ? FILE_OPEN : FILE_OPEN_IF;
522     if (*PrimaryDisposition == FILE_CREATED)
523     {
524         /* Over-write the existing log file, since this is a new hive */
525         CreateDisposition = FILE_SUPERSEDE;
526     }
527 
528     /* Setup the name */
529     RtlInitUnicodeString(&ExtensionName, Extension);
530     RtlAppendUnicodeStringToString(&FullName, &ExtensionName);
531 
532     /* Initialize the attributes */
533     InitializeObjectAttributes(&ObjectAttributes,
534                                &FullName,
535                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
536                                NULL,
537                                NULL);
538 
539     /* Setup the flags */
540     IoFlags = FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING;
541 
542     /* Check if this is a log file */
543     if (!_wcsnicmp(Extension, L".log", 4))
544     {
545         /* Hide log files */
546         AttributeFlags |= FILE_ATTRIBUTE_HIDDEN;
547     }
548 
549     /* Now create the file.
550      * Note: We use FILE_SYNCHRONOUS_IO_NONALERT here to simplify CmpFileRead/CmpFileWrite.
551      *       Windows does async I/O and therefore does not use this flag (or SYNCHRONIZE).
552      */
553     Status = ZwCreateFile(Log,
554                           DesiredAccess | SYNCHRONIZE,
555                           &ObjectAttributes,
556                           &IoStatusBlock,
557                           NULL,
558                           AttributeFlags,
559                           ShareMode,
560                           CreateDisposition,
561                           FILE_SYNCHRONOUS_IO_NONALERT | IoFlags,
562                           NULL,
563                           0);
564     if ((NT_SUCCESS(Status)) && (MarkAsSystemHive))
565     {
566         /* We opened it, mark it as a system hive */
567         Status = ZwFsControlFile(*Log,
568                                  EventHandle,
569                                  NULL,
570                                  NULL,
571                                  &IoStatusBlock,
572                                  FSCTL_MARK_AS_SYSTEM_HIVE,
573                                  NULL,
574                                  0,
575                                  NULL,
576                                  0);
577         if (Status == STATUS_PENDING)
578         {
579             /* Wait for completion */
580             KeWaitForSingleObject(Event,
581                                   Executive,
582                                   KernelMode,
583                                   FALSE,
584                                   NULL);
585             Status = IoStatusBlock.Status;
586         }
587 
588         /* If we don't support it, ignore the failure */
589         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
590 
591         /* If we failed, close the handle */
592         if (!NT_SUCCESS(Status)) ZwClose(*Log);
593     }
594 
595     /* Check if anything failed until now */
596     if (!NT_SUCCESS(Status))
597     {
598         /* Clear the handle */
599         *Log = NULL;
600     }
601     else
602     {
603         /* Disable compression */
604         Status = ZwFsControlFile(*Log,
605                                  EventHandle,
606                                  NULL,
607                                  NULL,
608                                  &IoStatusBlock,
609                                  FSCTL_SET_COMPRESSION,
610                                  &CompressionState,
611                                  sizeof(CompressionState),
612                                  NULL,
613                                  0);
614         if (Status == STATUS_PENDING)
615         {
616             /* Wait for completion */
617             KeWaitForSingleObject(Event,
618                                   Executive,
619                                   KernelMode,
620                                   FALSE,
621                                   NULL);
622         }
623 
624         /* Return the disposition */
625         *LogDisposition = (ULONG)IoStatusBlock.Information;
626     }
627 
628     /* We're done, close handles and free buffers */
629     if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
630     ObDereferenceObject(Event);
631     ZwClose(EventHandle);
632     return STATUS_SUCCESS;
633 }
634 
635 VOID
636 NTAPI
637 CmpCloseHiveFiles(IN PCMHIVE Hive)
638 {
639     ULONG i;
640 
641     for (i = 0; i < HFILE_TYPE_MAX; i++)
642     {
643         if (Hive->FileHandles[i] != NULL)
644         {
645             ZwClose(Hive->FileHandles[i]);
646             Hive->FileHandles[i] = NULL;
647         }
648     }
649 }
650