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