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