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