xref: /reactos/ntoskrnl/config/cminit.c (revision d09998df)
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 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     /* Lock the hive list */
217     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
218 
219     /* Insert this hive */
220     InsertHeadList(&CmpHiveListHead, &Hive->HiveList);
221 
222     /* Release the lock */
223     ExReleasePushLock(&CmpHiveListHeadLock);
224 
225     /* Return the hive and success */
226     *CmHive = Hive;
227     return STATUS_SUCCESS;
228 }
229 
230 NTSTATUS
231 NTAPI
232 CmpDestroyHive(IN PCMHIVE CmHive)
233 {
234     /* Remove the hive from the list */
235     ExAcquirePushLockExclusive(&CmpHiveListHeadLock);
236     RemoveEntryList(&CmHive->HiveList);
237     ExReleasePushLock(&CmpHiveListHeadLock);
238 
239     /* Destroy the security descriptor cache */
240     CmpDestroySecurityCache(CmHive);
241 
242     /* Destroy the view list */
243     CmpDestroyHiveViewList(CmHive);
244 
245     /* Delete the flusher lock */
246     ExDeleteResourceLite(CmHive->FlusherLock);
247     ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE);
248 
249     /* Delete the view lock */
250     ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE);
251 
252     /* Free the hive storage */
253     HvFree(&CmHive->Hive);
254 
255     /* Free the hive */
256     CmpFree(CmHive, TAG_CM);
257 
258     return STATUS_SUCCESS;
259 }
260 
261 NTSTATUS
262 NTAPI
263 CmpOpenHiveFiles(IN PCUNICODE_STRING BaseName,
264                  IN PCWSTR Extension OPTIONAL,
265                  OUT PHANDLE Primary,
266                  OUT PHANDLE Log,
267                  OUT PULONG PrimaryDisposition,
268                  OUT PULONG LogDisposition,
269                  IN BOOLEAN CreateAllowed,
270                  IN BOOLEAN MarkAsSystemHive,
271                  IN BOOLEAN NoBuffering,
272                  OUT PULONG ClusterSize OPTIONAL)
273 {
274     HANDLE EventHandle;
275     PKEVENT Event;
276     NTSTATUS Status;
277     UNICODE_STRING FullName, ExtensionName;
278     PWCHAR NameBuffer;
279     USHORT Length;
280     OBJECT_ATTRIBUTES ObjectAttributes;
281     IO_STATUS_BLOCK IoStatusBlock;
282     ULONG AttributeFlags, ShareMode, DesiredAccess, CreateDisposition, IoFlags;
283     USHORT CompressionState;
284     FILE_STANDARD_INFORMATION FileInformation;
285     FILE_FS_SIZE_INFORMATION FsSizeInformation;
286 
287     /* Create event */
288     Status = CmpCreateEvent(NotificationEvent, &EventHandle, &Event);
289     if (!NT_SUCCESS(Status)) return Status;
290 
291     /* Initialize the full name */
292     RtlInitEmptyUnicodeString(&FullName, NULL, 0);
293     Length = BaseName->Length;
294 
295     /* Check if we have an extension */
296     if (Extension)
297     {
298         /* Update the name length */
299         Length += (USHORT)wcslen(Extension) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
300 
301         /* Allocate the buffer for the full name */
302         NameBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
303         if (!NameBuffer)
304         {
305             /* Fail */
306             ObDereferenceObject(Event);
307             ZwClose(EventHandle);
308             return STATUS_NO_MEMORY;
309         }
310 
311         /* Build the full name */
312         FullName.Buffer = NameBuffer;
313         FullName.MaximumLength = Length;
314         RtlCopyUnicodeString(&FullName, BaseName);
315     }
316     else
317     {
318         /* The base name is the full name */
319         FullName = *BaseName;
320         NameBuffer = NULL;
321     }
322 
323     /* Initialize the attributes */
324     InitializeObjectAttributes(&ObjectAttributes,
325                                &FullName,
326                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
327                                NULL,
328                                NULL);
329 
330     /* Check if we can create the hive */
331     if ((CreateAllowed) && !(CmpShareSystemHives))
332     {
333         /* Open only or create */
334         CreateDisposition = FILE_OPEN_IF;
335     }
336     else
337     {
338         /* Open only */
339         CreateDisposition = FILE_OPEN;
340     }
341 
342     /* Setup the flags */
343     // FIXME : FILE_OPEN_FOR_BACKUP_INTENT is unimplemented and breaks 3rd stage boot
344     IoFlags = //FILE_OPEN_FOR_BACKUP_INTENT |
345               FILE_NO_COMPRESSION |
346               FILE_RANDOM_ACCESS |
347               (NoBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0);
348 
349     /* Set share and access modes */
350     if ((CmpMiniNTBoot) && (CmpShareSystemHives))
351     {
352         /* We're on Live CD or otherwise sharing */
353         DesiredAccess = FILE_READ_DATA;
354         ShareMode = FILE_SHARE_READ;
355     }
356     else
357     {
358         /* We want to write exclusively */
359         ShareMode = 0;
360         DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA;
361     }
362 
363     /* Default attributes */
364     AttributeFlags = FILE_ATTRIBUTE_NORMAL;
365 
366     /* Now create the file */
367     Status = ZwCreateFile(Primary,
368                           DesiredAccess | SYNCHRONIZE,
369                           &ObjectAttributes,
370                           &IoStatusBlock,
371                           NULL,
372                           AttributeFlags,
373                           ShareMode,
374                           CreateDisposition,
375                           FILE_SYNCHRONOUS_IO_NONALERT | IoFlags,
376                           NULL,
377                           0);
378     /* Check if anything failed until now */
379     if (!NT_SUCCESS(Status))
380     {
381         DPRINT1("ZwCreateFile(%wZ) failed, Status 0x%08lx.\n", ObjectAttributes.ObjectName, Status);
382 
383         /* Close handles and free buffers */
384         if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
385         ObDereferenceObject(Event);
386         ZwClose(EventHandle);
387         *Primary = NULL;
388         return Status;
389     }
390 
391     if (MarkAsSystemHive)
392     {
393         /* We opened it, mark it as a system hive */
394         Status = ZwFsControlFile(*Primary,
395                                  EventHandle,
396                                  NULL,
397                                  NULL,
398                                  &IoStatusBlock,
399                                  FSCTL_MARK_AS_SYSTEM_HIVE,
400                                  NULL,
401                                  0,
402                                  NULL,
403                                  0);
404         if (Status == STATUS_PENDING)
405         {
406             /* Wait for completion */
407             KeWaitForSingleObject(Event,
408                                   Executive,
409                                   KernelMode,
410                                   FALSE,
411                                   NULL);
412             Status = IoStatusBlock.Status;
413         }
414 
415         /* If we don't support it, ignore the failure */
416         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
417 
418         if (!NT_SUCCESS(Status))
419         {
420             /* Close handles and free buffers */
421             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
422             ObDereferenceObject(Event);
423             ZwClose(EventHandle);
424             ZwClose(*Primary);
425             *Primary = NULL;
426             return Status;
427         }
428     }
429 
430     /* Disable compression */
431     CompressionState = 0;
432     Status = ZwFsControlFile(*Primary,
433                              EventHandle,
434                              NULL,
435                              NULL,
436                              &IoStatusBlock,
437                              FSCTL_SET_COMPRESSION,
438                              &CompressionState,
439                              sizeof(CompressionState),
440                              NULL,
441                              0);
442     if (Status == STATUS_PENDING)
443     {
444         /* Wait for completion */
445         KeWaitForSingleObject(Event,
446                               Executive,
447                               KernelMode,
448                               FALSE,
449                               NULL);
450     }
451 
452     /* Get the disposition */
453     *PrimaryDisposition = (ULONG)IoStatusBlock.Information;
454     if (IoStatusBlock.Information != FILE_CREATED)
455     {
456         /* Check how large the file is */
457         Status = ZwQueryInformationFile(*Primary,
458                                         &IoStatusBlock,
459                                         &FileInformation,
460                                         sizeof(FileInformation),
461                                         FileStandardInformation);
462         if (NT_SUCCESS(Status))
463         {
464             /* Check if it's 0 bytes */
465             if (!FileInformation.EndOfFile.QuadPart)
466             {
467                 /* Assume it's a new file */
468                 *PrimaryDisposition = FILE_CREATED;
469             }
470         }
471     }
472 
473     /* Check if the caller wants cluster size returned */
474     if (ClusterSize)
475     {
476         /* Query it */
477         Status = ZwQueryVolumeInformationFile(*Primary,
478                                               &IoStatusBlock,
479                                               &FsSizeInformation,
480                                               sizeof(FsSizeInformation),
481                                               FileFsSizeInformation);
482         if (!NT_SUCCESS(Status))
483         {
484             /* Close handles and free buffers */
485             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
486             ObDereferenceObject(Event);
487             ZwClose(EventHandle);
488             return Status;
489         }
490 
491         /* Check if the sector size is invalid */
492         if (FsSizeInformation.BytesPerSector > HBLOCK_SIZE)
493         {
494             /* Close handles and free buffers */
495             if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
496             ObDereferenceObject(Event);
497             ZwClose(EventHandle);
498             return STATUS_CANNOT_LOAD_REGISTRY_FILE;
499         }
500 
501         /* Return cluster size */
502         *ClusterSize = max(1, FsSizeInformation.BytesPerSector / HSECTOR_SIZE);
503     }
504 
505     /* Check if we don't need to create a log file */
506     if (!Extension)
507     {
508         /* We're done, close handles */
509         ObDereferenceObject(Event);
510         ZwClose(EventHandle);
511         return STATUS_SUCCESS;
512     }
513 
514     /* Check if we can create the hive */
515     CreateDisposition = CmpShareSystemHives ? FILE_OPEN : FILE_OPEN_IF;
516     if (*PrimaryDisposition == FILE_CREATED)
517     {
518         /* Over-write the existing log file, since this is a new hive */
519         CreateDisposition = FILE_SUPERSEDE;
520     }
521 
522     /* Setup the name */
523     RtlInitUnicodeString(&ExtensionName, Extension);
524     RtlAppendUnicodeStringToString(&FullName, &ExtensionName);
525 
526     /* Initialize the attributes */
527     InitializeObjectAttributes(&ObjectAttributes,
528                                &FullName,
529                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
530                                NULL,
531                                NULL);
532 
533     /* Setup the flags */
534     IoFlags = FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING;
535 
536     /* Check if this is a log file */
537     if (!_wcsnicmp(Extension, L".log", 4))
538     {
539         /* Hide log files */
540         AttributeFlags |= FILE_ATTRIBUTE_HIDDEN;
541     }
542 
543     /* Now create the file */
544     Status = ZwCreateFile(Log,
545                           DesiredAccess,
546                           &ObjectAttributes,
547                           &IoStatusBlock,
548                           NULL,
549                           AttributeFlags,
550                           ShareMode,
551                           CreateDisposition,
552                           IoFlags,
553                           NULL,
554                           0);
555     if ((NT_SUCCESS(Status)) && (MarkAsSystemHive))
556     {
557         /* We opened it, mark it as a system hive */
558         Status = ZwFsControlFile(*Log,
559                                  EventHandle,
560                                  NULL,
561                                  NULL,
562                                  &IoStatusBlock,
563                                  FSCTL_MARK_AS_SYSTEM_HIVE,
564                                  NULL,
565                                  0,
566                                  NULL,
567                                  0);
568         if (Status == STATUS_PENDING)
569         {
570             /* Wait for completion */
571             KeWaitForSingleObject(Event,
572                                   Executive,
573                                   KernelMode,
574                                   FALSE,
575                                   NULL);
576             Status = IoStatusBlock.Status;
577         }
578 
579         /* If we don't support it, ignore the failure */
580         if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS;
581 
582         /* If we failed, close the handle */
583         if (!NT_SUCCESS(Status)) ZwClose(*Log);
584     }
585 
586     /* Check if anything failed until now */
587     if (!NT_SUCCESS(Status))
588     {
589         /* Clear the handle */
590         *Log = NULL;
591     }
592     else
593     {
594         /* Disable compression */
595         Status = ZwFsControlFile(*Log,
596                                  EventHandle,
597                                  NULL,
598                                  NULL,
599                                  &IoStatusBlock,
600                                  FSCTL_SET_COMPRESSION,
601                                  &CompressionState,
602                                  sizeof(CompressionState),
603                                  NULL,
604                                  0);
605         if (Status == STATUS_PENDING)
606         {
607             /* Wait for completion */
608             KeWaitForSingleObject(Event,
609                                   Executive,
610                                   KernelMode,
611                                   FALSE,
612                                   NULL);
613         }
614 
615         /* Return the disposition */
616         *LogDisposition = (ULONG)IoStatusBlock.Information;
617     }
618 
619     /* We're done, close handles and free buffers */
620     if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM);
621     ObDereferenceObject(Event);
622     ZwClose(EventHandle);
623     return STATUS_SUCCESS;
624 }
625 
626 VOID
627 NTAPI
628 CmpCloseHiveFiles(IN PCMHIVE Hive)
629 {
630     ULONG i;
631 
632     for (i = 0; i < HFILE_TYPE_MAX; i++)
633     {
634         if (Hive->FileHandles[i] != NULL)
635         {
636             ZwClose(Hive->FileHandles[i]);
637             Hive->FileHandles[i] = NULL;
638         }
639     }
640 }
641