Sync with trunk r63283
[reactos.git] / ntoskrnl / config / cmapi.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmapi.c
5 * PURPOSE: Configuration Manager - Internal Registry APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Eric Kohl
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include "ntoskrnl.h"
13 #define NDEBUG
14 #include "debug.h"
15
16 /* FUNCTIONS *****************************************************************/
17
18 BOOLEAN
19 NTAPI
20 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
21 IN POBJECT_ATTRIBUTES SourceFile,
22 OUT PCMHIVE *CmHive)
23 {
24 NTSTATUS Status;
25 PCM_KEY_BODY KeyBody;
26 PCMHIVE Hive;
27 BOOLEAN Loaded = FALSE;
28 PAGED_CODE();
29
30 /* Sanity check */
31 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
32
33 /* Reference the handle */
34 Status = ObReferenceObjectByHandle(KeyHandle,
35 0,
36 CmpKeyObjectType,
37 KernelMode,
38 (PVOID)&KeyBody,
39 NULL);
40 if (!NT_SUCCESS(Status)) return Loaded;
41
42 /* Don't touch deleted KCBs */
43 if (KeyBody->KeyControlBlock->Delete) return Loaded;
44
45 Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
46
47 /* Must be the root key */
48 if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
49 !(Hive->FileUserName.Buffer))
50 {
51 /* It isn't */
52 ObDereferenceObject(KeyBody);
53 return Loaded;
54 }
55
56 /* Now compare the name of the file */
57 if (!RtlCompareUnicodeString(&Hive->FileUserName,
58 SourceFile->ObjectName,
59 TRUE))
60 {
61 /* Same file found */
62 Loaded = TRUE;
63 *CmHive = Hive;
64
65 /* If the hive is frozen, not sure what to do */
66 if (Hive->Frozen)
67 {
68 /* FIXME: TODO */
69 DPRINT1("ERROR: Hive is frozen\n");
70 while (TRUE);
71 }
72 }
73
74 /* Dereference and return result */
75 ObDereferenceObject(KeyBody);
76 return Loaded;
77 }
78
79 BOOLEAN
80 NTAPI
81 CmpDoFlushAll(IN BOOLEAN ForceFlush)
82 {
83 PLIST_ENTRY NextEntry;
84 PCMHIVE Hive;
85 NTSTATUS Status;
86 BOOLEAN Result = TRUE;
87
88 /* Make sure that the registry isn't read-only now */
89 if (CmpNoWrite) return TRUE;
90
91 /* Otherwise, acquire the hive list lock and disable force flush */
92 CmpForceForceFlush = FALSE;
93 ExAcquirePushLockShared(&CmpHiveListHeadLock);
94
95 /* Loop the hive list */
96 NextEntry = CmpHiveListHead.Flink;
97 while (NextEntry != &CmpHiveListHead)
98 {
99 /* Get the hive */
100 Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
101 if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
102 {
103 /* Acquire the flusher lock */
104 CmpLockHiveFlusherExclusive(Hive);
105
106 /* Check for illegal state */
107 if ((ForceFlush) && (Hive->UseCount))
108 {
109 /* Registry needs to be locked down */
110 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
111 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
112 while (TRUE);
113 }
114
115 /* Only sync if we are forced to or if it won't cause a hive shrink */
116 if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
117 {
118 /* Do the sync */
119 Status = HvSyncHive(&Hive->Hive);
120
121 /* If something failed - set the flag and continue looping */
122 if (!NT_SUCCESS(Status)) Result = FALSE;
123 }
124 else
125 {
126 /* We won't flush if the hive might shrink */
127 Result = FALSE;
128 CmpForceForceFlush = TRUE;
129 }
130
131 /* Release the flusher lock */
132 CmpUnlockHiveFlusher(Hive);
133 }
134
135 /* Try the next entry */
136 NextEntry = NextEntry->Flink;
137 }
138
139 /* Release lock and return */
140 ExReleasePushLock(&CmpHiveListHeadLock);
141 return Result;
142 }
143
144 NTSTATUS
145 NTAPI
146 CmpSetValueKeyNew(IN PHHIVE Hive,
147 IN PCM_KEY_NODE Parent,
148 IN PUNICODE_STRING ValueName,
149 IN ULONG Index,
150 IN ULONG Type,
151 IN PVOID Data,
152 IN ULONG DataSize,
153 IN ULONG StorageType,
154 IN ULONG SmallData)
155 {
156 PCELL_DATA CellData;
157 HCELL_INDEX ValueCell;
158 NTSTATUS Status;
159
160 /* Check if we already have a value list */
161 if (Parent->ValueList.Count)
162 {
163 /* Then make sure it's valid and dirty it */
164 ASSERT(Parent->ValueList.List != HCELL_NIL);
165 if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
166 {
167 /* Fail if we're out of space for log changes */
168 return STATUS_NO_LOG_SPACE;
169 }
170 }
171
172 /* Allocate a value cell */
173 ValueCell = HvAllocateCell(Hive,
174 FIELD_OFFSET(CM_KEY_VALUE, Name) +
175 CmpNameSize(Hive, ValueName),
176 StorageType,
177 HCELL_NIL);
178 if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
179
180 /* Get the actual data for it */
181 CellData = HvGetCell(Hive, ValueCell);
182 if (!CellData) ASSERT(FALSE);
183
184 /* Now we can release it, make sure it's also dirty */
185 HvReleaseCell(Hive, ValueCell);
186 ASSERT(HvIsCellDirty(Hive, ValueCell));
187
188 /* Set it up and copy the name */
189 CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
190 _SEH2_TRY
191 {
192 /* This can crash since the name is coming from user-mode */
193 CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
194 CellData->u.KeyValue.Name,
195 ValueName);
196 }
197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
198 {
199 /* Fail */
200 DPRINT1("Invalid user data!\n");
201 HvFreeCell(Hive, ValueCell);
202 _SEH2_YIELD(return _SEH2_GetExceptionCode());
203 }
204 _SEH2_END;
205
206 /* Check for compressed name */
207 if (CellData->u.KeyValue.NameLength < ValueName->Length)
208 {
209 /* This is a compressed name */
210 CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
211 }
212 else
213 {
214 /* No flags to set */
215 CellData->u.KeyValue.Flags = 0;
216 }
217
218 /* Check if this is a normal key */
219 if (DataSize > CM_KEY_VALUE_SMALL)
220 {
221 /* Build a data cell for it */
222 Status = CmpSetValueDataNew(Hive,
223 Data,
224 DataSize,
225 StorageType,
226 ValueCell,
227 &CellData->u.KeyValue.Data);
228 if (!NT_SUCCESS(Status))
229 {
230 /* We failed, free the cell */
231 HvFreeCell(Hive, ValueCell);
232 return Status;
233 }
234
235 /* Otherwise, set the data length, and make sure the data is dirty */
236 CellData->u.KeyValue.DataLength = DataSize;
237 ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
238 }
239 else
240 {
241 /* This is a small key, set the data directly inside */
242 CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
243 CellData->u.KeyValue.Data = SmallData;
244 }
245
246 /* Set the type now */
247 CellData->u.KeyValue.Type = Type;
248
249 /* Add this value cell to the child list */
250 Status = CmpAddValueToList(Hive,
251 ValueCell,
252 Index,
253 StorageType,
254 &Parent->ValueList);
255
256 /* If we failed, free the entire cell, including the data */
257 if (!NT_SUCCESS(Status))
258 {
259 /* Overwrite the status with a known one */
260 CmpFreeValue(Hive, ValueCell);
261 Status = STATUS_INSUFFICIENT_RESOURCES;
262 }
263
264 /* Return Status */
265 return Status;
266 }
267
268 NTSTATUS
269 NTAPI
270 CmpSetValueKeyExisting(IN PHHIVE Hive,
271 IN HCELL_INDEX OldChild,
272 IN PCM_KEY_VALUE Value,
273 IN ULONG Type,
274 IN PVOID Data,
275 IN ULONG DataSize,
276 IN ULONG StorageType,
277 IN ULONG TempData)
278 {
279 HCELL_INDEX DataCell, NewCell;
280 PCELL_DATA CellData;
281 ULONG Length;
282 BOOLEAN WasSmall, IsSmall;
283
284 /* Registry writes must be blocked */
285 CMP_ASSERT_FLUSH_LOCK(Hive);
286
287 /* Mark the old child cell dirty */
288 if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
289
290 /* See if this is a small or normal key */
291 WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
292
293 /* See if our new data can fit in a small key */
294 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
295
296 /* Big keys are unsupported */
297 ASSERT_VALUE_BIG(Hive, Length);
298 ASSERT_VALUE_BIG(Hive, DataSize);
299
300 /* Mark the old value dirty */
301 if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
302
303 /* Check if we have a small key */
304 if (IsSmall)
305 {
306 /* Check if we had a normal key with some data in it */
307 if (!(WasSmall) && (Length > 0))
308 {
309 /* Free the previous data */
310 CmpFreeValueData(Hive, Value->Data, Length);
311 }
312
313 /* Write our data directly */
314 Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
315 Value->Data = TempData;
316 Value->Type = Type;
317 return STATUS_SUCCESS;
318 }
319
320 /* We have a normal key. Was the old cell also normal and had data? */
321 if (!(WasSmall) && (Length > 0))
322 {
323 /* Get the current data cell and actual data inside it */
324 DataCell = Value->Data;
325 ASSERT(DataCell != HCELL_NIL);
326 CellData = HvGetCell(Hive, DataCell);
327 if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
328
329 /* Immediately release the cell */
330 HvReleaseCell(Hive, DataCell);
331
332 /* Make sure that the data cell actually has a size */
333 ASSERT(HvGetCellSize(Hive, CellData) > 0);
334
335 /* Check if the previous data cell could fit our new data */
336 if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
337 {
338 /* Re-use it then */
339 NewCell = DataCell;
340 }
341 else
342 {
343 /* Otherwise, re-allocate the current data cell */
344 NewCell = HvReallocateCell(Hive, DataCell, DataSize);
345 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
346 }
347 }
348 else
349 {
350 /* This was a small key, or a key with no data, allocate a cell */
351 NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
352 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
353 }
354
355 /* Now get the actual data for our data cell */
356 CellData = HvGetCell(Hive, NewCell);
357 if (!CellData) ASSERT(FALSE);
358
359 /* Release it immediately */
360 HvReleaseCell(Hive, NewCell);
361
362 /* Copy our data into the data cell's buffer, and set up the value */
363 RtlCopyMemory(CellData, Data, DataSize);
364 Value->Data = NewCell;
365 Value->DataLength = DataSize;
366 Value->Type = Type;
367
368 /* Return success */
369 ASSERT(HvIsCellDirty(Hive, NewCell));
370 return STATUS_SUCCESS;
371 }
372
373 NTSTATUS
374 NTAPI
375 CmpQueryKeyData(IN PHHIVE Hive,
376 IN PCM_KEY_NODE Node,
377 IN KEY_INFORMATION_CLASS KeyInformationClass,
378 IN OUT PVOID KeyInformation,
379 IN ULONG Length,
380 IN OUT PULONG ResultLength)
381 {
382 NTSTATUS Status;
383 ULONG Size, SizeLeft, MinimumSize, Offset;
384 PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
385 USHORT NameLength;
386 PVOID ClassData;
387
388 /* Check if the value is compressed */
389 if (Node->Flags & KEY_COMP_NAME)
390 {
391 /* Get the compressed name size */
392 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
393 }
394 else
395 {
396 /* Get the real size */
397 NameLength = Node->NameLength;
398 }
399
400 /* Check what kind of information is being requested */
401 switch (KeyInformationClass)
402 {
403 /* Basic information */
404 case KeyBasicInformation:
405
406 /* This is the size we need */
407 Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
408
409 /* And this is the minimum we can work with */
410 MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
411
412 /* Let the caller know and assume success */
413 *ResultLength = Size;
414 Status = STATUS_SUCCESS;
415
416 /* Check if the bufer we got is too small */
417 if (Length < MinimumSize)
418 {
419 /* Let the caller know and fail */
420 Status = STATUS_BUFFER_TOO_SMALL;
421 break;
422 }
423
424 /* Copy the basic information */
425 Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
426 Info->KeyBasicInformation.TitleIndex = 0;
427 Info->KeyBasicInformation.NameLength = NameLength;
428
429 /* Only the name is left */
430 SizeLeft = Length - MinimumSize;
431 Size = NameLength;
432
433 /* Check if we don't have enough space for the name */
434 if (SizeLeft < Size)
435 {
436 /* Truncate the name we'll return, and tell the caller */
437 Size = SizeLeft;
438 Status = STATUS_BUFFER_OVERFLOW;
439 }
440
441 /* Check if this is a compressed key */
442 if (Node->Flags & KEY_COMP_NAME)
443 {
444 /* Copy the compressed name */
445 CmpCopyCompressedName(Info->KeyBasicInformation.Name,
446 SizeLeft,
447 Node->Name,
448 Node->NameLength);
449 }
450 else
451 {
452 /* Otherwise, copy the raw name */
453 RtlCopyMemory(Info->KeyBasicInformation.Name,
454 Node->Name,
455 Size);
456 }
457 break;
458
459 /* Node information */
460 case KeyNodeInformation:
461
462 /* Calculate the size we need */
463 Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
464 NameLength +
465 Node->ClassLength;
466
467 /* And the minimum size we can support */
468 MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
469
470 /* Return the size to the caller and assume succes */
471 *ResultLength = Size;
472 Status = STATUS_SUCCESS;
473
474 /* Check if the caller's buffer is too small */
475 if (Length < MinimumSize)
476 {
477 /* Let them know, and fail */
478 Status = STATUS_BUFFER_TOO_SMALL;
479 break;
480 }
481
482 /* Copy the basic information */
483 Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
484 Info->KeyNodeInformation.TitleIndex = 0;
485 Info->KeyNodeInformation.ClassLength = Node->ClassLength;
486 Info->KeyNodeInformation.NameLength = NameLength;
487
488 /* Now the name is left */
489 SizeLeft = Length - MinimumSize;
490 Size = NameLength;
491
492 /* Check if the name can fit entirely */
493 if (SizeLeft < Size)
494 {
495 /* It can't, we'll have to truncate. Tell the caller */
496 Size = SizeLeft;
497 Status = STATUS_BUFFER_OVERFLOW;
498 }
499
500 /* Check if the key node name is compressed */
501 if (Node->Flags & KEY_COMP_NAME)
502 {
503 /* Copy the compressed name */
504 CmpCopyCompressedName(Info->KeyNodeInformation.Name,
505 SizeLeft,
506 Node->Name,
507 Node->NameLength);
508 }
509 else
510 {
511 /* It isn't, so copy the raw name */
512 RtlCopyMemory(Info->KeyNodeInformation.Name,
513 Node->Name,
514 Size);
515 }
516
517 /* Check if the node has a class */
518 if (Node->ClassLength > 0)
519 {
520 /* Set the class offset */
521 Offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength;
522 Offset = ALIGN_UP_BY(Offset, sizeof(ULONG));
523 Info->KeyNodeInformation.ClassOffset = Offset;
524
525 /* Get the class data */
526 ClassData = HvGetCell(Hive, Node->Class);
527 if (ClassData == NULL)
528 {
529 Status = STATUS_INSUFFICIENT_RESOURCES;
530 break;
531 }
532
533 /* Check if we can copy anything */
534 if (Length > Offset)
535 {
536 /* Copy the class data */
537 RtlCopyMemory((PUCHAR)Info + Offset,
538 ClassData,
539 min(Node->ClassLength, Length - Offset));
540 }
541
542 /* Check if the buffer was large enough */
543 if (Length < Offset + Node->ClassLength)
544 {
545 Status = STATUS_BUFFER_OVERFLOW;
546 }
547
548 /* Release the class cell */
549 HvReleaseCell(Hive, Node->Class);
550 }
551 else
552 {
553 /* It doesn't, so set offset to -1, not 0! */
554 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
555 }
556 break;
557
558 /* Full information requsted */
559 case KeyFullInformation:
560
561 /* This is the size we need */
562 Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
563 Node->ClassLength;
564
565 /* This is what we can work with */
566 MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
567
568 /* Return it to caller and assume success */
569 *ResultLength = Size;
570 Status = STATUS_SUCCESS;
571
572 /* Check if the caller's buffer is to small */
573 if (Length < MinimumSize)
574 {
575 /* Let them know and fail */
576 Status = STATUS_BUFFER_TOO_SMALL;
577 break;
578 }
579
580 /* Now copy all the basic information */
581 Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
582 Info->KeyFullInformation.TitleIndex = 0;
583 Info->KeyFullInformation.ClassLength = Node->ClassLength;
584 Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
585 Node->SubKeyCounts[Volatile];
586 Info->KeyFullInformation.Values = Node->ValueList.Count;
587 Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
588 Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
589 Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
590 Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
591
592 /* Check if we have a class */
593 if (Node->ClassLength > 0)
594 {
595 /* Set the class offset */
596 Offset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
597 Info->KeyFullInformation.ClassOffset = Offset;
598
599 /* Get the class data */
600 ClassData = HvGetCell(Hive, Node->Class);
601 if (ClassData == NULL)
602 {
603 Status = STATUS_INSUFFICIENT_RESOURCES;
604 break;
605 }
606
607 /* Copy the class data */
608 NT_ASSERT(Length > Offset);
609 RtlCopyMemory(Info->KeyFullInformation.Class,
610 ClassData,
611 min(Node->ClassLength, Length - Offset));
612
613 /* Check if the buffer was large enough */
614 if (Length < Offset + Node->ClassLength)
615 {
616 Status = STATUS_BUFFER_OVERFLOW;
617 }
618
619 /* Release the class cell */
620 HvReleaseCell(Hive, Node->Class);
621 }
622 else
623 {
624 /* We don't have a class, so set offset to -1, not 0! */
625 Info->KeyFullInformation.ClassOffset = 0xFFFFFFFF;
626 }
627 break;
628
629 /* Any other class that got sent here is invalid! */
630 default:
631
632 /* Set failure code */
633 Status = STATUS_INVALID_PARAMETER;
634 break;
635 }
636
637 /* Return status */
638 return Status;
639 }
640
641 NTSTATUS
642 NTAPI
643 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
644 IN PUNICODE_STRING ValueName,
645 IN ULONG Type,
646 IN PVOID Data,
647 IN ULONG DataLength)
648 {
649 PHHIVE Hive = NULL;
650 PCM_KEY_NODE Parent;
651 PCM_KEY_VALUE Value = NULL;
652 HCELL_INDEX CurrentChild, Cell;
653 NTSTATUS Status;
654 BOOLEAN Found, Result;
655 ULONG Count, ChildIndex, SmallData, Storage;
656 VALUE_SEARCH_RETURN_TYPE SearchResult;
657 BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
658 HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
659
660 /* Acquire hive and KCB lock */
661 CmpLockRegistry();
662 CmpAcquireKcbLockShared(Kcb);
663
664 /* Sanity check */
665 ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
666
667 /* Don't touch deleted KCBs */
668 DoAgain:
669 if (Kcb->Delete)
670 {
671 /* Fail */
672 Status = STATUS_KEY_DELETED;
673 goto Quickie;
674 }
675
676 /* Don't let anyone mess with symlinks */
677 if ((Kcb->Flags & KEY_SYM_LINK) &&
678 ((Type != REG_LINK) ||
679 !(ValueName) ||
680 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
681 {
682 /* Invalid modification of a symlink key */
683 Status = STATUS_ACCESS_DENIED;
684 goto Quickie;
685 }
686
687 /* Check if this is the first attempt */
688 if (FirstTry)
689 {
690 /* Search for the value in the cache */
691 SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
692 ValueName,
693 Type,
694 Data,
695 DataLength);
696 if (SearchResult == SearchNeedExclusiveLock)
697 {
698 /* Try again with the exclusive lock */
699 CmpConvertKcbSharedToExclusive(Kcb);
700 goto DoAgain;
701 }
702 else if (SearchResult == SearchSuccess)
703 {
704 /* We don't actually need to do anything! */
705 Status = STATUS_SUCCESS;
706 goto Quickie;
707 }
708
709 /* We need the exclusive KCB lock now */
710 if (!(CmpIsKcbLockedExclusive(Kcb)) &&
711 !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
712 {
713 /* Acquire exclusive lock */
714 CmpConvertKcbSharedToExclusive(Kcb);
715 }
716
717 /* Cache lookup failed, so don't try it next time */
718 FirstTry = FALSE;
719
720 /* Now grab the flush lock since the key will be modified */
721 ASSERT(FlusherLocked == FALSE);
722 CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
723 FlusherLocked = TRUE;
724 goto DoAgain;
725 }
726 else
727 {
728 /* Get pointer to key cell */
729 Hive = Kcb->KeyHive;
730 Cell = Kcb->KeyCell;
731
732 /* Get the parent */
733 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
734 ASSERT(Parent);
735 ParentCell = Cell;
736
737 /* Prepare to scan the key node */
738 Count = Parent->ValueList.Count;
739 Found = FALSE;
740 if (Count > 0)
741 {
742 /* Try to find the existing name */
743 Result = CmpFindNameInList(Hive,
744 &Parent->ValueList,
745 ValueName,
746 &ChildIndex,
747 &CurrentChild);
748 if (!Result)
749 {
750 /* Fail */
751 Status = STATUS_INSUFFICIENT_RESOURCES;
752 goto Quickie;
753 }
754
755 /* Check if we found something */
756 if (CurrentChild != HCELL_NIL)
757 {
758 /* Release existing child */
759 if (ChildCell != HCELL_NIL)
760 {
761 HvReleaseCell(Hive, ChildCell);
762 ChildCell = HCELL_NIL;
763 }
764
765 /* Get its value */
766 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
767 if (!Value)
768 {
769 /* Fail */
770 Status = STATUS_INSUFFICIENT_RESOURCES;
771 goto Quickie;
772 }
773
774 /* Remember that we found it */
775 ChildCell = CurrentChild;
776 Found = TRUE;
777 }
778 }
779 else
780 {
781 /* No child list, we'll need to add it */
782 ChildIndex = 0;
783 }
784 }
785
786 /* Should only get here on the second pass */
787 ASSERT(FirstTry == FALSE);
788
789 /* The KCB must be locked exclusive at this point */
790 CMP_ASSERT_KCB_LOCK(Kcb);
791
792 /* Mark the cell dirty */
793 if (!HvMarkCellDirty(Hive, Cell, FALSE))
794 {
795 /* Not enough log space, fail */
796 Status = STATUS_NO_LOG_SPACE;
797 goto Quickie;
798 }
799
800 /* Get the storage type */
801 Storage = HvGetCellType(Cell);
802
803 /* Check if this is small data */
804 SmallData = 0;
805 if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
806 {
807 /* Need SEH because user data may be invalid */
808 _SEH2_TRY
809 {
810 /* Copy it */
811 RtlCopyMemory(&SmallData, Data, DataLength);
812 }
813 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
814 {
815 /* Return failure code */
816 Status = _SEH2_GetExceptionCode();
817 _SEH2_YIELD(goto Quickie);
818 }
819 _SEH2_END;
820 }
821
822 /* Check if we didn't find a matching key */
823 if (!Found)
824 {
825 /* Call the internal routine */
826 Status = CmpSetValueKeyNew(Hive,
827 Parent,
828 ValueName,
829 ChildIndex,
830 Type,
831 Data,
832 DataLength,
833 Storage,
834 SmallData);
835 }
836 else
837 {
838 /* Call the internal routine */
839 Status = CmpSetValueKeyExisting(Hive,
840 CurrentChild,
841 Value,
842 Type,
843 Data,
844 DataLength,
845 Storage,
846 SmallData);
847 }
848
849 /* Check for success */
850 if (NT_SUCCESS(Status))
851 {
852 /* Check if the maximum value name length changed */
853 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
854 if (Parent->MaxValueNameLen < ValueName->Length)
855 {
856 /* Set the new values */
857 Parent->MaxValueNameLen = ValueName->Length;
858 Kcb->KcbMaxValueNameLen = ValueName->Length;
859 }
860
861 /* Check if the maximum data length changed */
862 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
863 if (Parent->MaxValueDataLen < DataLength)
864 {
865 /* Update it */
866 Parent->MaxValueDataLen = DataLength;
867 Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
868 }
869
870 /* Save the write time */
871 KeQuerySystemTime(&Parent->LastWriteTime);
872 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
873
874 /* Check if the cell is cached */
875 if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
876 {
877 /* Shouldn't happen */
878 ASSERT(FALSE);
879 }
880 else
881 {
882 /* Cleanup the value cache */
883 CmpCleanUpKcbValueCache(Kcb);
884
885 /* Sanity checks */
886 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
887 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
888
889 /* Set the value cache */
890 Kcb->ValueCache.Count = Parent->ValueList.Count;
891 Kcb->ValueCache.ValueList = Parent->ValueList.List;
892 }
893
894 /* Notify registered callbacks */
895 CmpReportNotify(Kcb,
896 Hive,
897 Kcb->KeyCell,
898 REG_NOTIFY_CHANGE_LAST_SET);
899 }
900
901 /* Release the cells */
902 Quickie:
903 if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
904 if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
905
906 /* Release the locks */
907 if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
908 CmpReleaseKcbLock(Kcb);
909 CmpUnlockRegistry();
910 return Status;
911 }
912
913 NTSTATUS
914 NTAPI
915 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
916 IN UNICODE_STRING ValueName)
917 {
918 NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
919 PHHIVE Hive;
920 PCM_KEY_NODE Parent;
921 HCELL_INDEX ChildCell, Cell;
922 PCHILD_LIST ChildList;
923 PCM_KEY_VALUE Value = NULL;
924 ULONG ChildIndex;
925 BOOLEAN Result;
926
927 /* Acquire hive lock */
928 CmpLockRegistry();
929
930 /* Lock KCB exclusively */
931 CmpAcquireKcbLockExclusive(Kcb);
932
933 /* Don't touch deleted keys */
934 if (Kcb->Delete)
935 {
936 /* Undo everything */
937 CmpReleaseKcbLock(Kcb);
938 CmpUnlockRegistry();
939 return STATUS_KEY_DELETED;
940 }
941
942 /* Get the hive and the cell index */
943 Hive = Kcb->KeyHive;
944 Cell = Kcb->KeyCell;
945
946 /* Lock flushes */
947 CmpLockHiveFlusherShared((PCMHIVE)Hive);
948
949 /* Get the parent key node */
950 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
951 ASSERT(Parent);
952
953 /* Get the value list and check if it has any entries */
954 ChildList = &Parent->ValueList;
955 if (ChildList->Count)
956 {
957 /* Try to find this value */
958 Result = CmpFindNameInList(Hive,
959 ChildList,
960 &ValueName,
961 &ChildIndex,
962 &ChildCell);
963 if (!Result)
964 {
965 /* Fail */
966 Status = STATUS_INSUFFICIENT_RESOURCES;
967 goto Quickie;
968 }
969
970 /* Value not found, return error */
971 if (ChildCell == HCELL_NIL) goto Quickie;
972
973 /* We found the value, mark all relevant cells dirty */
974 if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
975 (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
976 (HvMarkCellDirty(Hive, ChildCell, FALSE))))
977 {
978 /* Not enough log space, fail */
979 Status = STATUS_NO_LOG_SPACE;
980 goto Quickie;
981 }
982
983 /* Get the key value */
984 Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
985 ASSERT(Value);
986
987 /* Mark it and all related data as dirty */
988 if (!CmpMarkValueDataDirty(Hive, Value))
989 {
990 /* Not enough log space, fail */
991 Status = STATUS_NO_LOG_SPACE;
992 goto Quickie;
993 }
994
995 /* Ssanity checks */
996 ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
997 ASSERT(HvIsCellDirty(Hive, ChildCell));
998
999 /* Remove the value from the child list */
1000 Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
1001 if (!NT_SUCCESS(Status))
1002 {
1003 /* Set known error */
1004 Status = STATUS_INSUFFICIENT_RESOURCES;
1005 goto Quickie;
1006 }
1007
1008 /* Remove the value and its data itself */
1009 if (!CmpFreeValue(Hive, ChildCell))
1010 {
1011 /* Failed to free the value, fail */
1012 Status = STATUS_INSUFFICIENT_RESOURCES;
1013 goto Quickie;
1014 }
1015
1016 /* Set the last write time */
1017 KeQuerySystemTime(&Parent->LastWriteTime);
1018 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
1019
1020 /* Sanity check */
1021 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1022 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1023 ASSERT(HvIsCellDirty(Hive, Cell));
1024
1025 /* Check if the value list is empty now */
1026 if (!Parent->ValueList.Count)
1027 {
1028 /* Then clear key node data */
1029 Parent->MaxValueNameLen = 0;
1030 Parent->MaxValueDataLen = 0;
1031 Kcb->KcbMaxValueNameLen = 0;
1032 Kcb->KcbMaxValueDataLen = 0;
1033 }
1034
1035 /* Cleanup the value cache */
1036 CmpCleanUpKcbValueCache(Kcb);
1037
1038 /* Sanity checks */
1039 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
1040 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
1041
1042 /* Set the value cache */
1043 Kcb->ValueCache.Count = ChildList->Count;
1044 Kcb->ValueCache.ValueList = ChildList->List;
1045
1046 /* Notify registered callbacks */
1047 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
1048
1049 /* Change default Status to success */
1050 Status = STATUS_SUCCESS;
1051 }
1052
1053 Quickie:
1054 /* Release the parent cell, if any */
1055 if (Parent) HvReleaseCell(Hive, Cell);
1056
1057 /* Check if we had a value */
1058 if (Value)
1059 {
1060 /* Release the child cell */
1061 ASSERT(ChildCell != HCELL_NIL);
1062 HvReleaseCell(Hive, ChildCell);
1063 }
1064
1065 /* Release locks */
1066 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1067 CmpReleaseKcbLock(Kcb);
1068 CmpUnlockRegistry();
1069 return Status;
1070 }
1071
1072 NTSTATUS
1073 NTAPI
1074 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1075 IN UNICODE_STRING ValueName,
1076 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1077 IN PVOID KeyValueInformation,
1078 IN ULONG Length,
1079 IN PULONG ResultLength)
1080 {
1081 NTSTATUS Status;
1082 PCM_KEY_VALUE ValueData;
1083 ULONG Index;
1084 BOOLEAN ValueCached = FALSE;
1085 PCM_CACHED_VALUE *CachedValue;
1086 HCELL_INDEX CellToRelease;
1087 VALUE_SEARCH_RETURN_TYPE Result;
1088 PHHIVE Hive;
1089 PAGED_CODE();
1090
1091 /* Acquire hive lock */
1092 CmpLockRegistry();
1093
1094 /* Lock the KCB shared */
1095 CmpAcquireKcbLockShared(Kcb);
1096
1097 /* Don't touch deleted keys */
1098 DoAgain:
1099 if (Kcb->Delete)
1100 {
1101 /* Undo everything */
1102 CmpReleaseKcbLock(Kcb);
1103 CmpUnlockRegistry();
1104 return STATUS_KEY_DELETED;
1105 }
1106
1107 /* We don't deal with this yet */
1108 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1109 {
1110 /* Shouldn't happen */
1111 ASSERT(FALSE);
1112 }
1113
1114 /* Get the hive */
1115 Hive = Kcb->KeyHive;
1116
1117 /* Find the key value */
1118 Result = CmpFindValueByNameFromCache(Kcb,
1119 &ValueName,
1120 &CachedValue,
1121 &Index,
1122 &ValueData,
1123 &ValueCached,
1124 &CellToRelease);
1125 if (Result == SearchNeedExclusiveLock)
1126 {
1127 /* Check if we need an exclusive lock */
1128 ASSERT(CellToRelease == HCELL_NIL);
1129 ASSERT(ValueData == NULL);
1130
1131 /* Try with exclusive KCB lock */
1132 CmpConvertKcbSharedToExclusive(Kcb);
1133 goto DoAgain;
1134 }
1135
1136 if (Result == SearchSuccess)
1137 {
1138 /* Sanity check */
1139 ASSERT(ValueData != NULL);
1140
1141 /* User data, protect against exceptions */
1142 _SEH2_TRY
1143 {
1144 /* Query the information requested */
1145 Result = CmpQueryKeyValueData(Kcb,
1146 CachedValue,
1147 ValueData,
1148 ValueCached,
1149 KeyValueInformationClass,
1150 KeyValueInformation,
1151 Length,
1152 ResultLength,
1153 &Status);
1154 if (Result == SearchNeedExclusiveLock)
1155 {
1156 /* Release the value cell */
1157 if (CellToRelease != HCELL_NIL)
1158 {
1159 HvReleaseCell(Hive, CellToRelease);
1160 CellToRelease = HCELL_NIL;
1161 }
1162
1163 /* Try with exclusive KCB lock */
1164 CmpConvertKcbSharedToExclusive(Kcb);
1165 _SEH2_YIELD(goto DoAgain);
1166 }
1167 }
1168 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1169 {
1170 Status = _SEH2_GetExceptionCode();
1171 }
1172 _SEH2_END;
1173 }
1174 else
1175 {
1176 /* Failed to find the value */
1177 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1178 }
1179
1180 /* If we have a cell to release, do so */
1181 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1182
1183 /* Release locks */
1184 CmpReleaseKcbLock(Kcb);
1185 CmpUnlockRegistry();
1186 return Status;
1187 }
1188
1189 NTSTATUS
1190 NTAPI
1191 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1192 IN ULONG Index,
1193 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1194 IN PVOID KeyValueInformation,
1195 IN ULONG Length,
1196 IN PULONG ResultLength)
1197 {
1198 NTSTATUS Status;
1199 PHHIVE Hive;
1200 PCM_KEY_NODE Parent;
1201 HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
1202 VALUE_SEARCH_RETURN_TYPE Result;
1203 BOOLEAN IndexIsCached, ValueIsCached = FALSE;
1204 PCELL_DATA CellData;
1205 PCM_CACHED_VALUE *CachedValue;
1206 PCM_KEY_VALUE ValueData = NULL;
1207 PAGED_CODE();
1208
1209 /* Acquire hive lock */
1210 CmpLockRegistry();
1211
1212 /* Lock the KCB shared */
1213 CmpAcquireKcbLockShared(Kcb);
1214
1215 /* Don't touch deleted keys */
1216 DoAgain:
1217 if (Kcb->Delete)
1218 {
1219 /* Undo everything */
1220 CmpReleaseKcbLock(Kcb);
1221 CmpUnlockRegistry();
1222 return STATUS_KEY_DELETED;
1223 }
1224
1225 /* Get the hive and parent */
1226 Hive = Kcb->KeyHive;
1227 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1228 ASSERT(Parent);
1229
1230 /* FIXME: Lack of cache? */
1231 if (Kcb->ValueCache.Count != Parent->ValueList.Count)
1232 {
1233 DPRINT1("HACK: Overriding value cache count\n");
1234 Kcb->ValueCache.Count = Parent->ValueList.Count;
1235 }
1236
1237 /* Make sure the index is valid */
1238 if (Index >= Kcb->ValueCache.Count)
1239 {
1240 /* Release the cell and fail */
1241 HvReleaseCell(Hive, Kcb->KeyCell);
1242 Status = STATUS_NO_MORE_ENTRIES;
1243 goto Quickie;
1244 }
1245
1246 /* We don't deal with this yet */
1247 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1248 {
1249 /* Shouldn't happen */
1250 ASSERT(FALSE);
1251 }
1252
1253 /* Find the value list */
1254 Result = CmpGetValueListFromCache(Kcb,
1255 &CellData,
1256 &IndexIsCached,
1257 &CellToRelease);
1258 if (Result == SearchNeedExclusiveLock)
1259 {
1260 /* Check if we need an exclusive lock */
1261 ASSERT(CellToRelease == HCELL_NIL);
1262 HvReleaseCell(Hive, Kcb->KeyCell);
1263
1264 /* Try with exclusive KCB lock */
1265 CmpConvertKcbSharedToExclusive(Kcb);
1266 goto DoAgain;
1267 }
1268 else if (Result != SearchSuccess)
1269 {
1270 /* Sanity check */
1271 ASSERT(CellData == NULL);
1272
1273 /* Release the cell and fail */
1274 Status = STATUS_INSUFFICIENT_RESOURCES;
1275 goto Quickie;
1276 }
1277
1278 /* Now get the key value */
1279 Result = CmpGetValueKeyFromCache(Kcb,
1280 CellData,
1281 Index,
1282 &CachedValue,
1283 &ValueData,
1284 IndexIsCached,
1285 &ValueIsCached,
1286 &CellToRelease2);
1287 if (Result == SearchNeedExclusiveLock)
1288 {
1289 /* Cleanup state */
1290 ASSERT(CellToRelease2 == HCELL_NIL);
1291 if (CellToRelease)
1292 {
1293 HvReleaseCell(Hive, CellToRelease);
1294 CellToRelease = HCELL_NIL;
1295 }
1296 HvReleaseCell(Hive, Kcb->KeyCell);
1297
1298 /* Try with exclusive KCB lock */
1299 CmpConvertKcbSharedToExclusive(Kcb);
1300 goto DoAgain;
1301 }
1302 else if (Result != SearchSuccess)
1303 {
1304 /* Sanity check */
1305 ASSERT(ValueData == NULL);
1306
1307 /* Release the cells and fail */
1308 Status = STATUS_INSUFFICIENT_RESOURCES;
1309 goto Quickie;
1310 }
1311
1312 /* User data, need SEH */
1313 _SEH2_TRY
1314 {
1315 /* Query the information requested */
1316 Result = CmpQueryKeyValueData(Kcb,
1317 CachedValue,
1318 ValueData,
1319 ValueIsCached,
1320 KeyValueInformationClass,
1321 KeyValueInformation,
1322 Length,
1323 ResultLength,
1324 &Status);
1325 if (Result == SearchNeedExclusiveLock)
1326 {
1327 /* Cleanup state */
1328 if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
1329 HvReleaseCell(Hive, Kcb->KeyCell);
1330 if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
1331
1332 /* Try with exclusive KCB lock */
1333 CmpConvertKcbSharedToExclusive(Kcb);
1334 _SEH2_YIELD(goto DoAgain);
1335 }
1336 }
1337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1338 {
1339 /* Get exception code */
1340 Status = _SEH2_GetExceptionCode();
1341 }
1342 _SEH2_END;
1343
1344 Quickie:
1345 /* If we have a cell to release, do so */
1346 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1347
1348 /* Release the parent cell */
1349 HvReleaseCell(Hive, Kcb->KeyCell);
1350
1351 /* If we have a cell to release, do so */
1352 if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
1353
1354 /* Release locks */
1355 CmpReleaseKcbLock(Kcb);
1356 CmpUnlockRegistry();
1357 return Status;
1358 }
1359
1360 static
1361 NTSTATUS
1362 CmpQueryKeyDataFromCache(
1363 _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1364 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo,
1365 _In_ ULONG Length,
1366 _Out_ PULONG ResultLength)
1367 {
1368 PCM_KEY_NODE Node;
1369 PHHIVE KeyHive;
1370 HCELL_INDEX KeyCell;
1371 USHORT NameLength;
1372 PAGED_CODE();
1373
1374 /* Get the hive and cell index */
1375 KeyHive = Kcb->KeyHash.KeyHive;
1376 KeyCell = Kcb->KeyHash.KeyCell;
1377
1378 #if DBG
1379 /* Get the cell node */
1380 Node = HvGetCell(KeyHive, KeyCell);
1381 if (Node != NULL)
1382 {
1383 ULONG SubKeyCount;
1384 ASSERT(Node->ValueList.Count == Kcb->ValueCache.Count);
1385
1386 if (!(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO))
1387 {
1388 SubKeyCount = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1389 if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1390 {
1391 ASSERT(SubKeyCount == 0);
1392 }
1393 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1394 {
1395 ASSERT(SubKeyCount == 1);
1396 }
1397 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1398 {
1399 ASSERT(SubKeyCount == Kcb->IndexHint->Count);
1400 }
1401 else
1402 {
1403 ASSERT(SubKeyCount == Kcb->SubKeyCount);
1404 }
1405 }
1406
1407 ASSERT(Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart);
1408 ASSERT(Node->MaxNameLen == Kcb->KcbMaxNameLen);
1409 ASSERT(Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1410 ASSERT(Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1411
1412 /* Release the cell */
1413 HvReleaseCell(KeyHive, KeyCell);
1414 }
1415 #endif // DBG
1416
1417 /* Make sure we have a name block */
1418 if (Kcb->NameBlock == NULL)
1419 {
1420 return STATUS_INSUFFICIENT_RESOURCES;
1421 }
1422
1423 /* Check for compressed name */
1424 if (Kcb->NameBlock->Compressed)
1425 {
1426 /* Calculate the name size */
1427 NameLength = CmpCompressedNameSize(Kcb->NameBlock->NameHash.Name,
1428 Kcb->NameBlock->NameHash.NameLength);
1429 }
1430 else
1431 {
1432 /* Use the stored name size */
1433 NameLength = Kcb->NameBlock->NameHash.NameLength;
1434 }
1435
1436 /* Validate buffer length (we do not copy the name!) */
1437 *ResultLength = sizeof(*KeyCachedInfo);
1438 if (Length < *ResultLength)
1439 {
1440 return STATUS_BUFFER_TOO_SMALL;
1441 }
1442
1443 /* Fill the structure */
1444 KeyCachedInfo->LastWriteTime = Kcb->KcbLastWriteTime;
1445 KeyCachedInfo->TitleIndex = 0;
1446 KeyCachedInfo->NameLength = NameLength;
1447 KeyCachedInfo->Values = Kcb->ValueCache.Count;
1448 KeyCachedInfo->MaxNameLen = Kcb->KcbMaxNameLen;
1449 KeyCachedInfo->MaxValueNameLen = Kcb->KcbMaxValueNameLen;
1450 KeyCachedInfo->MaxValueDataLen = Kcb->KcbMaxValueDataLen;
1451
1452 /* Check the ExtFlags for what we have */
1453 if (Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO)
1454 {
1455 /* Cache is not valid, do a full lookup */
1456 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb);
1457
1458 /* Get the cell node */
1459 Node = HvGetCell(KeyHive, KeyCell);
1460 if (Node == NULL)
1461 {
1462 return STATUS_INSUFFICIENT_RESOURCES;
1463 }
1464
1465 /* Calculate number of subkeys */
1466 KeyCachedInfo->SubKeys = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1467
1468 /* Release the cell */
1469 HvReleaseCell(KeyHive, KeyCell);
1470 }
1471 else if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1472 {
1473 /* There are no subkeys */
1474 KeyCachedInfo->SubKeys = 0;
1475 }
1476 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1477 {
1478 /* There is exactly one subley */
1479 KeyCachedInfo->SubKeys = 1;
1480 }
1481 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1482 {
1483 /* Get the number of subkeys from the subkey hint */
1484 KeyCachedInfo->SubKeys = Kcb->IndexHint->Count;
1485 }
1486 else
1487 {
1488 /* No subkey hint, use the key count field */
1489 KeyCachedInfo->SubKeys = Kcb->SubKeyCount;
1490 }
1491
1492 return STATUS_SUCCESS;
1493 }
1494
1495 static
1496 NTSTATUS
1497 CmpQueryFlagsInformation(
1498 _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1499 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo,
1500 _In_ ULONG Length,
1501 _In_ PULONG ResultLength)
1502 {
1503 /* Validate the buffer size */
1504 *ResultLength = sizeof(*KeyFlagsInfo);
1505 if (Length < *ResultLength)
1506 {
1507 return STATUS_BUFFER_TOO_SMALL;
1508 }
1509
1510 /* Copy the user flags */
1511 KeyFlagsInfo->UserFlags = Kcb->KcbUserFlags;
1512
1513 return STATUS_SUCCESS;
1514 }
1515
1516 NTSTATUS
1517 NTAPI
1518 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1519 IN KEY_INFORMATION_CLASS KeyInformationClass,
1520 IN PVOID KeyInformation,
1521 IN ULONG Length,
1522 IN PULONG ResultLength)
1523 {
1524 NTSTATUS Status;
1525 PHHIVE Hive;
1526 PCM_KEY_NODE Parent;
1527 HV_TRACK_CELL_REF CellReferences = {0};
1528
1529 /* Acquire hive lock */
1530 CmpLockRegistry();
1531
1532 /* Lock KCB shared */
1533 CmpAcquireKcbLockShared(Kcb);
1534
1535 /* Don't touch deleted keys */
1536 if (Kcb->Delete)
1537 {
1538 /* Fail */
1539 Status = STATUS_KEY_DELETED;
1540 goto Quickie;
1541 }
1542
1543 /* Check what class we got */
1544 switch (KeyInformationClass)
1545 {
1546 /* Typical information */
1547 case KeyFullInformation:
1548 case KeyBasicInformation:
1549 case KeyNodeInformation:
1550
1551 /* Get the hive and parent */
1552 Hive = Kcb->KeyHive;
1553 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1554 ASSERT(Parent);
1555
1556 /* Track cell references */
1557 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
1558 {
1559 /* Not enough memory to track references */
1560 Status = STATUS_INSUFFICIENT_RESOURCES;
1561 }
1562 else
1563 {
1564 /* Call the internal API */
1565 Status = CmpQueryKeyData(Hive,
1566 Parent,
1567 KeyInformationClass,
1568 KeyInformation,
1569 Length,
1570 ResultLength);
1571 }
1572 break;
1573
1574 case KeyCachedInformation:
1575 /* Call the internal API */
1576 Status = CmpQueryKeyDataFromCache(Kcb,
1577 KeyInformation,
1578 Length,
1579 ResultLength);
1580 break;
1581
1582 case KeyFlagsInformation:
1583 /* Call the internal API */
1584 Status = CmpQueryFlagsInformation(Kcb,
1585 KeyInformation,
1586 Length,
1587 ResultLength);
1588 break;
1589
1590 /* Unsupported class for now */
1591 case KeyNameInformation:
1592
1593 /* Print message and fail */
1594 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1595 Status = STATUS_NOT_IMPLEMENTED;
1596 break;
1597
1598 /* Illegal classes */
1599 default:
1600
1601 /* Print message and fail */
1602 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1603 Status = STATUS_INVALID_INFO_CLASS;
1604 break;
1605 }
1606
1607 Quickie:
1608 /* Release references */
1609 HvReleaseFreeCellRefArray(&CellReferences);
1610
1611 /* Release locks */
1612 CmpReleaseKcbLock(Kcb);
1613 CmpUnlockRegistry();
1614 return Status;
1615 }
1616
1617 NTSTATUS
1618 NTAPI
1619 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1620 IN ULONG Index,
1621 IN KEY_INFORMATION_CLASS KeyInformationClass,
1622 IN PVOID KeyInformation,
1623 IN ULONG Length,
1624 IN PULONG ResultLength)
1625 {
1626 NTSTATUS Status;
1627 PHHIVE Hive;
1628 PCM_KEY_NODE Parent, Child;
1629 HCELL_INDEX ChildCell;
1630 HV_TRACK_CELL_REF CellReferences = {0};
1631
1632 /* Acquire hive lock */
1633 CmpLockRegistry();
1634
1635 /* Lock the KCB shared */
1636 CmpAcquireKcbLockShared(Kcb);
1637
1638 /* Don't touch deleted keys */
1639 if (Kcb->Delete)
1640 {
1641 /* Undo everything */
1642 Status = STATUS_KEY_DELETED;
1643 goto Quickie;
1644 }
1645
1646 /* Get the hive and parent */
1647 Hive = Kcb->KeyHive;
1648 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1649 ASSERT(Parent);
1650
1651 /* Get the child cell */
1652 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1653
1654 /* Release the parent cell */
1655 HvReleaseCell(Hive, Kcb->KeyCell);
1656
1657 /* Check if we found the child */
1658 if (ChildCell == HCELL_NIL)
1659 {
1660 /* We didn't, fail */
1661 Status = STATUS_NO_MORE_ENTRIES;
1662 goto Quickie;
1663 }
1664
1665 /* Now get the actual child node */
1666 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1667 ASSERT(Child);
1668
1669 /* Track references */
1670 if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
1671 {
1672 /* Can't allocate memory for tracking */
1673 Status = STATUS_INSUFFICIENT_RESOURCES;
1674 goto Quickie;
1675 }
1676
1677 /* Data can be user-mode, use SEH */
1678 _SEH2_TRY
1679 {
1680 /* Query the data requested */
1681 Status = CmpQueryKeyData(Hive,
1682 Child,
1683 KeyInformationClass,
1684 KeyInformation,
1685 Length,
1686 ResultLength);
1687 }
1688 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1689 {
1690 /* Fail with exception code */
1691 Status = _SEH2_GetExceptionCode();
1692 _SEH2_YIELD(goto Quickie);
1693 }
1694 _SEH2_END;
1695
1696 Quickie:
1697 /* Release references */
1698 HvReleaseFreeCellRefArray(&CellReferences);
1699
1700 /* Release locks */
1701 CmpReleaseKcbLock(Kcb);
1702 CmpUnlockRegistry();
1703 return Status;
1704 }
1705
1706 NTSTATUS
1707 NTAPI
1708 CmDeleteKey(IN PCM_KEY_BODY KeyBody)
1709 {
1710 NTSTATUS Status;
1711 PHHIVE Hive;
1712 PCM_KEY_NODE Node, Parent;
1713 HCELL_INDEX Cell, ParentCell;
1714 PCM_KEY_CONTROL_BLOCK Kcb;
1715
1716 /* Acquire hive lock */
1717 CmpLockRegistry();
1718
1719 /* Get the kcb */
1720 Kcb = KeyBody->KeyControlBlock;
1721
1722 /* Don't allow deleting the root */
1723 if (!Kcb->ParentKcb)
1724 {
1725 /* Fail */
1726 CmpUnlockRegistry();
1727 return STATUS_CANNOT_DELETE;
1728 }
1729
1730 /* Lock parent and child */
1731 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1732
1733 /* Check if we're already being deleted */
1734 if (Kcb->Delete)
1735 {
1736 /* Don't do it twice */
1737 Status = STATUS_SUCCESS;
1738 goto Quickie2;
1739 }
1740
1741 /* Get the hive and node */
1742 Hive = Kcb->KeyHive;
1743 Cell = Kcb->KeyCell;
1744
1745 /* Lock flushes */
1746 CmpLockHiveFlusherShared((PCMHIVE)Hive);
1747
1748 /* Get the key node */
1749 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1750 ASSERT(Node);
1751
1752 /* Sanity check */
1753 ASSERT(Node->Flags == Kcb->Flags);
1754
1755 /* Check if we don't have any children */
1756 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
1757 !(Node->Flags & KEY_NO_DELETE))
1758 {
1759 /* Send notification to registered callbacks */
1760 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
1761
1762 /* Get the parent and free the cell */
1763 ParentCell = Node->Parent;
1764 Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1765 if (NT_SUCCESS(Status))
1766 {
1767 /* Flush any notifications */
1768 CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
1769
1770 /* Clean up information we have on the subkey */
1771 CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
1772
1773 /* Get the parent node */
1774 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1775 if (Parent)
1776 {
1777 /* Update the maximum name length */
1778 Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
1779
1780 /* Make sure we're dirty */
1781 ASSERT(HvIsCellDirty(Hive, ParentCell));
1782
1783 /* Update the write time */
1784 KeQuerySystemTime(&Parent->LastWriteTime);
1785 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
1786
1787 /* Release the cell */
1788 HvReleaseCell(Hive, ParentCell);
1789 }
1790
1791 /* Set the KCB in delete mode and remove it */
1792 Kcb->Delete = TRUE;
1793 CmpRemoveKeyControlBlock(Kcb);
1794
1795 /* Clear the cell */
1796 Kcb->KeyCell = HCELL_NIL;
1797 }
1798 }
1799 else
1800 {
1801 /* Fail */
1802 Status = STATUS_CANNOT_DELETE;
1803 }
1804
1805 /* Release the cell */
1806 HvReleaseCell(Hive, Cell);
1807
1808 /* Release flush lock */
1809 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1810
1811 /* Release the KCB locks */
1812 Quickie2:
1813 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1814
1815 /* Release hive lock */
1816 CmpUnlockRegistry();
1817 return Status;
1818 }
1819
1820 NTSTATUS
1821 NTAPI
1822 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1823 IN BOOLEAN ExclusiveLock)
1824 {
1825 PCMHIVE CmHive;
1826 NTSTATUS Status = STATUS_SUCCESS;
1827 PHHIVE Hive;
1828
1829 /* Ignore flushes until we're ready */
1830 if (CmpNoWrite) return STATUS_SUCCESS;
1831
1832 /* Get the hives */
1833 Hive = Kcb->KeyHive;
1834 CmHive = (PCMHIVE)Hive;
1835
1836 /* Check if this is the master hive */
1837 if (CmHive == CmiVolatileHive)
1838 {
1839 /* Flush all the hives instead */
1840 CmpDoFlushAll(FALSE);
1841 }
1842 else
1843 {
1844 /* Don't touch the hive */
1845 CmpLockHiveFlusherExclusive(CmHive);
1846 ASSERT(CmHive->ViewLock);
1847 KeAcquireGuardedMutex(CmHive->ViewLock);
1848 CmHive->ViewLockOwner = KeGetCurrentThread();
1849
1850 /* Will the hive shrink? */
1851 if (HvHiveWillShrink(Hive))
1852 {
1853 /* I don't believe the current Hv does shrinking */
1854 ASSERT(FALSE);
1855 }
1856 else
1857 {
1858 /* Now we can release views */
1859 ASSERT(CmHive->ViewLock);
1860 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1861 ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
1862 KeReleaseGuardedMutex(CmHive->ViewLock);
1863 }
1864
1865 /* Flush only this hive */
1866 if (!HvSyncHive(Hive))
1867 {
1868 /* Fail */
1869 Status = STATUS_REGISTRY_IO_FAILED;
1870 }
1871
1872 /* Release the flush lock */
1873 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1874 }
1875
1876 /* Return the status */
1877 return Status;
1878 }
1879
1880 NTSTATUS
1881 NTAPI
1882 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1883 IN POBJECT_ATTRIBUTES SourceFile,
1884 IN ULONG Flags,
1885 IN PCM_KEY_BODY KeyBody)
1886 {
1887 SECURITY_QUALITY_OF_SERVICE ServiceQos;
1888 SECURITY_CLIENT_CONTEXT ClientSecurityContext;
1889 HANDLE KeyHandle;
1890 BOOLEAN Allocate = TRUE;
1891 PCMHIVE CmHive, LoadedHive;
1892 NTSTATUS Status;
1893 CM_PARSE_CONTEXT ParseContext;
1894
1895 /* Check if we have a trust key */
1896 if (KeyBody)
1897 {
1898 /* Fail */
1899 DPRINT("Trusted classes not yet supported\n");
1900 }
1901
1902 /* Build a service QoS for a security context */
1903 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1904 ServiceQos.ImpersonationLevel = SecurityImpersonation;
1905 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1906 ServiceQos.EffectiveOnly = TRUE;
1907 Status = SeCreateClientSecurity(PsGetCurrentThread(),
1908 &ServiceQos,
1909 FALSE,
1910 &ClientSecurityContext);
1911 if (!NT_SUCCESS(Status))
1912 {
1913 /* Fail */
1914 DPRINT1("Security context failed\n");
1915 return Status;
1916 }
1917
1918 /* Open the target key */
1919 #if 0
1920 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
1921 #else
1922 RtlZeroMemory(&ParseContext, sizeof(ParseContext));
1923 ParseContext.CreateOperation = FALSE;
1924 Status = ObOpenObjectByName(TargetKey,
1925 CmpKeyObjectType,
1926 KernelMode,
1927 NULL,
1928 KEY_READ,
1929 &ParseContext,
1930 &KeyHandle);
1931 #endif
1932 if (!NT_SUCCESS(Status)) KeyHandle = NULL;
1933
1934 /* Open the hive */
1935 Status = CmpCmdHiveOpen(SourceFile,
1936 &ClientSecurityContext,
1937 &Allocate,
1938 &CmHive,
1939 0);
1940
1941 /* Get rid of the security context */
1942 SeDeleteClientSecurity(&ClientSecurityContext);
1943
1944 /* See if we failed */
1945 if (!NT_SUCCESS(Status))
1946 {
1947 /* See if the target already existed */
1948 if (KeyHandle)
1949 {
1950 /* Lock the registry */
1951 CmpLockRegistryExclusive();
1952
1953 /* Check if we are already loaded */
1954 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
1955 {
1956 /* That's okay then */
1957 ASSERT(LoadedHive);
1958 Status = STATUS_SUCCESS;
1959 }
1960
1961 /* Release the registry */
1962 CmpUnlockRegistry();
1963 }
1964
1965 /* Close the key handle if we had one */
1966 if (KeyHandle) ZwClose(KeyHandle);
1967 return Status;
1968 }
1969
1970 /* Lock the registry shared */
1971 CmpLockRegistry();
1972
1973 /* Lock loading */
1974 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1975
1976 /* Lock the hive to this thread */
1977 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
1978 CmHive->CreatorOwner = KeGetCurrentThread();
1979
1980 /* Set flag */
1981 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
1982
1983 /* Link the hive */
1984 Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
1985 TargetKey->RootDirectory,
1986 CmHive,
1987 Allocate,
1988 TargetKey->SecurityDescriptor);
1989 if (NT_SUCCESS(Status))
1990 {
1991 /* Add to HiveList key */
1992 CmpAddToHiveFileList(CmHive);
1993
1994 /* Sync the hive if necessary */
1995 if (Allocate)
1996 {
1997 /* Sync it under the flusher lock */
1998 CmpLockHiveFlusherExclusive(CmHive);
1999 HvSyncHive(&CmHive->Hive);
2000 CmpUnlockHiveFlusher(CmHive);
2001 }
2002
2003 /* Release the hive */
2004 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
2005 CmHive->CreatorOwner = NULL;
2006
2007 /* Allow loads */
2008 ExReleasePushLock(&CmpLoadHiveLock);
2009 }
2010 else
2011 {
2012 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status);
2013 /* FIXME: TODO */
2014 ASSERT(FALSE);
2015 }
2016
2017 /* Is this first profile load? */
2018 if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
2019 {
2020 /* User is now logged on, set quotas */
2021 CmpProfileLoaded = TRUE;
2022 CmpSetGlobalQuotaAllowed();
2023 }
2024
2025 /* Unlock the registry */
2026 CmpUnlockRegistry();
2027
2028 /* Close handle and return */
2029 if (KeyHandle) ZwClose(KeyHandle);
2030 return Status;
2031 }
2032
2033 NTSTATUS
2034 NTAPI
2035 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2036 IN ULONG Flags)
2037 {
2038 UNIMPLEMENTED;
2039 return STATUS_NOT_IMPLEMENTED;
2040 }
2041
2042 ULONG
2043 NTAPI
2044 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
2045 IN BOOLEAN RemoveEmptyCacheEntries)
2046 {
2047 PCM_KEY_HASH Entry;
2048 PCM_KEY_CONTROL_BLOCK CachedKcb;
2049 PCM_KEY_CONTROL_BLOCK ParentKcb;
2050 ULONG ParentKeyCount;
2051 ULONG i, j;
2052 ULONG SubKeys = 0;
2053
2054 DPRINT("CmCountOpenSubKeys() called\n");
2055
2056 /* The root key is the only referenced key. There are no refereced sub keys. */
2057 if (RootKcb->RefCount == 1)
2058 {
2059 DPRINT("open sub keys: 0\n");
2060 return 0;
2061 }
2062
2063 /* Enumerate all hash lists */
2064 for (i = 0; i < CmpHashTableSize; i++)
2065 {
2066 /* Get the first cache entry */
2067 Entry = CmpCacheTable[i].Entry;
2068
2069 /* Enumerate all cache entries */
2070 while (Entry)
2071 {
2072 /* Get the KCB of the current cache entry */
2073 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
2074
2075 /* Check keys only that are subkeys to our root key */
2076 if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
2077 {
2078 /* Calculate the number of parent keys to the root key */
2079 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
2080
2081 /* Find a parent key that could be the root key */
2082 ParentKcb = CachedKcb;
2083 for (j = 0; j < ParentKeyCount; j++)
2084 {
2085 ParentKcb = ParentKcb->ParentKcb;
2086 }
2087
2088 /* Check whether the parent is the root key */
2089 if (ParentKcb == RootKcb)
2090 {
2091 DPRINT("Found a sub key \n");
2092 DPRINT("RefCount = %u\n", CachedKcb->RefCount);
2093
2094 if (CachedKcb->RefCount > 0)
2095 {
2096 /* Count the current hash entry if it is in use */
2097 SubKeys++;
2098 }
2099 else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
2100 {
2101 /* Remove the current key from the delayed close list */
2102 CmpRemoveFromDelayedClose(CachedKcb);
2103
2104 /* Remove the current cache entry */
2105 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
2106
2107 /* Restart, because the hash list has changed */
2108 Entry = CmpCacheTable[i].Entry;
2109 continue;
2110 }
2111 }
2112 }
2113
2114 /* Get the next cache entry */
2115 Entry = Entry->NextHash;
2116 }
2117 }
2118
2119 DPRINT("open sub keys: %u\n", SubKeys);
2120
2121 return SubKeys;
2122 }