xref: /reactos/ntoskrnl/config/cminit.c (revision 76f1da56)
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