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