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