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)
8 * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
11 /* INCLUDES ******************************************************************/
17 /* FUNCTIONS *****************************************************************/
21 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle
,
22 IN POBJECT_ATTRIBUTES SourceFile
,
28 BOOLEAN Loaded
= FALSE
;
32 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
34 /* Reference the handle */
35 Status
= ObReferenceObjectByHandle(KeyHandle
,
41 if (!NT_SUCCESS(Status
)) return Loaded
;
43 /* Don't touch deleted KCBs */
44 if (KeyBody
->KeyControlBlock
->Delete
) return Loaded
;
46 Hive
= CONTAINING_RECORD(KeyBody
->KeyControlBlock
->KeyHive
, CMHIVE
, Hive
);
48 /* Must be the root key */
49 if (!(KeyBody
->KeyControlBlock
->Flags
& KEY_HIVE_ENTRY
) ||
50 !(Hive
->FileUserName
.Buffer
))
53 ObDereferenceObject(KeyBody
);
57 /* Now compare the name of the file */
58 if (!RtlCompareUnicodeString(&Hive
->FileUserName
,
59 SourceFile
->ObjectName
,
66 /* If the hive is frozen, not sure what to do */
70 DPRINT1("ERROR: Hive is frozen\n");
75 /* Dereference and return result */
76 ObDereferenceObject(KeyBody
);
82 CmpDoFlushAll(IN BOOLEAN ForceFlush
)
84 PLIST_ENTRY NextEntry
;
87 BOOLEAN Result
= TRUE
;
89 /* Make sure that the registry isn't read-only now */
90 if (CmpNoWrite
) return TRUE
;
92 /* Otherwise, acquire the hive list lock and disable force flush */
93 CmpForceForceFlush
= FALSE
;
94 ExAcquirePushLockShared(&CmpHiveListHeadLock
);
96 /* Loop the hive list */
97 NextEntry
= CmpHiveListHead
.Flink
;
98 while (NextEntry
!= &CmpHiveListHead
)
101 Hive
= CONTAINING_RECORD(NextEntry
, CMHIVE
, HiveList
);
102 if (!(Hive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
))
104 /* Acquire the flusher lock */
105 CmpLockHiveFlusherExclusive(Hive
);
107 /* Check for illegal state */
108 if ((ForceFlush
) && (Hive
->UseCount
))
110 /* Registry needs to be locked down */
111 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
112 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
116 /* Only sync if we are forced to or if it won't cause a hive shrink */
117 if ((ForceFlush
) || (!HvHiveWillShrink(&Hive
->Hive
)))
120 Status
= HvSyncHive(&Hive
->Hive
);
122 /* If something failed - set the flag and continue looping */
123 if (!NT_SUCCESS(Status
)) Result
= FALSE
;
127 /* We won't flush if the hive might shrink */
129 CmpForceForceFlush
= TRUE
;
132 /* Release the flusher lock */
133 CmpUnlockHiveFlusher(Hive
);
136 /* Try the next entry */
137 NextEntry
= NextEntry
->Flink
;
140 /* Release lock and return */
141 ExReleasePushLock(&CmpHiveListHeadLock
);
147 CmpSetValueKeyNew(IN PHHIVE Hive
,
148 IN PCM_KEY_NODE Parent
,
149 IN PUNICODE_STRING ValueName
,
154 IN ULONG StorageType
,
158 HCELL_INDEX ValueCell
;
161 /* Check if we already have a value list */
162 if (Parent
->ValueList
.Count
)
164 /* Then make sure it's valid and dirty it */
165 ASSERT(Parent
->ValueList
.List
!= HCELL_NIL
);
166 if (!HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
))
168 /* Fail if we're out of space for log changes */
169 return STATUS_NO_LOG_SPACE
;
173 /* Allocate a value cell */
174 ValueCell
= HvAllocateCell(Hive
,
175 FIELD_OFFSET(CM_KEY_VALUE
, Name
) +
176 CmpNameSize(Hive
, ValueName
),
179 if (ValueCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
181 /* Get the actual data for it */
182 CellData
= HvGetCell(Hive
, ValueCell
);
183 if (!CellData
) ASSERT(FALSE
);
185 /* Now we can release it, make sure it's also dirty */
186 HvReleaseCell(Hive
, ValueCell
);
187 ASSERT(HvIsCellDirty(Hive
, ValueCell
));
189 /* Set it up and copy the name */
190 CellData
->u
.KeyValue
.Signature
= CM_KEY_VALUE_SIGNATURE
;
193 /* This can crash since the name is coming from user-mode */
194 CellData
->u
.KeyValue
.NameLength
= CmpCopyName(Hive
,
195 CellData
->u
.KeyValue
.Name
,
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
201 DPRINT1("Invalid user data!\n");
202 HvFreeCell(Hive
, ValueCell
);
203 _SEH2_YIELD(return _SEH2_GetExceptionCode());
207 /* Check for compressed name */
208 if (CellData
->u
.KeyValue
.NameLength
< ValueName
->Length
)
210 /* This is a compressed name */
211 CellData
->u
.KeyValue
.Flags
= VALUE_COMP_NAME
;
215 /* No flags to set */
216 CellData
->u
.KeyValue
.Flags
= 0;
219 /* Check if this is a normal key */
220 if (DataSize
> CM_KEY_VALUE_SMALL
)
222 /* Build a data cell for it */
223 Status
= CmpSetValueDataNew(Hive
,
228 &CellData
->u
.KeyValue
.Data
);
229 if (!NT_SUCCESS(Status
))
231 /* We failed, free the cell */
232 HvFreeCell(Hive
, ValueCell
);
236 /* Otherwise, set the data length, and make sure the data is dirty */
237 CellData
->u
.KeyValue
.DataLength
= DataSize
;
238 ASSERT(HvIsCellDirty(Hive
, CellData
->u
.KeyValue
.Data
));
242 /* This is a small key, set the data directly inside */
243 CellData
->u
.KeyValue
.DataLength
= DataSize
+ CM_KEY_VALUE_SPECIAL_SIZE
;
244 CellData
->u
.KeyValue
.Data
= SmallData
;
247 /* Set the type now */
248 CellData
->u
.KeyValue
.Type
= Type
;
250 /* Add this value cell to the child list */
251 Status
= CmpAddValueToList(Hive
,
257 /* If we failed, free the entire cell, including the data */
258 if (!NT_SUCCESS(Status
))
260 /* Overwrite the status with a known one */
261 CmpFreeValue(Hive
, ValueCell
);
262 Status
= STATUS_INSUFFICIENT_RESOURCES
;
271 CmpSetValueKeyExisting(IN PHHIVE Hive
,
272 IN HCELL_INDEX OldChild
,
273 IN PCM_KEY_VALUE Value
,
277 IN ULONG StorageType
,
280 HCELL_INDEX DataCell
, NewCell
;
283 BOOLEAN WasSmall
, IsSmall
;
285 /* Registry writes must be blocked */
286 CMP_ASSERT_FLUSH_LOCK(Hive
);
288 /* Mark the old child cell dirty */
289 if (!HvMarkCellDirty(Hive
, OldChild
, FALSE
)) return STATUS_NO_LOG_SPACE
;
291 /* See if this is a small or normal key */
292 WasSmall
= CmpIsKeyValueSmall(&Length
, Value
->DataLength
);
294 /* See if our new data can fit in a small key */
295 IsSmall
= (DataSize
<= CM_KEY_VALUE_SMALL
) ? TRUE
: FALSE
;
297 /* Big keys are unsupported */
298 ASSERT_VALUE_BIG(Hive
, Length
);
299 ASSERT_VALUE_BIG(Hive
, DataSize
);
301 /* Mark the old value dirty */
302 if (!CmpMarkValueDataDirty(Hive
, Value
)) return STATUS_NO_LOG_SPACE
;
304 /* Check if we have a small key */
307 /* Check if we had a normal key with some data in it */
308 if (!(WasSmall
) && (Length
> 0))
310 /* Free the previous data */
311 CmpFreeValueData(Hive
, Value
->Data
, Length
);
314 /* Write our data directly */
315 Value
->DataLength
= DataSize
+ CM_KEY_VALUE_SPECIAL_SIZE
;
316 Value
->Data
= TempData
;
318 return STATUS_SUCCESS
;
321 /* We have a normal key. Was the old cell also normal and had data? */
322 if (!(WasSmall
) && (Length
> 0))
324 /* Get the current data cell and actual data inside it */
325 DataCell
= Value
->Data
;
326 ASSERT(DataCell
!= HCELL_NIL
);
327 CellData
= HvGetCell(Hive
, DataCell
);
328 if (!CellData
) return STATUS_INSUFFICIENT_RESOURCES
;
330 /* Immediately release the cell */
331 HvReleaseCell(Hive
, DataCell
);
333 /* Make sure that the data cell actually has a size */
334 ASSERT(HvGetCellSize(Hive
, CellData
) > 0);
336 /* Check if the previous data cell could fit our new data */
337 if (DataSize
<= (ULONG
)(HvGetCellSize(Hive
, CellData
)))
344 /* Otherwise, re-allocate the current data cell */
345 NewCell
= HvReallocateCell(Hive
, DataCell
, DataSize
);
346 if (NewCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
351 /* This was a small key, or a key with no data, allocate a cell */
352 NewCell
= HvAllocateCell(Hive
, DataSize
, StorageType
, HCELL_NIL
);
353 if (NewCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
356 /* Now get the actual data for our data cell */
357 CellData
= HvGetCell(Hive
, NewCell
);
358 if (!CellData
) ASSERT(FALSE
);
360 /* Release it immediately */
361 HvReleaseCell(Hive
, NewCell
);
363 /* Copy our data into the data cell's buffer, and set up the value */
364 RtlCopyMemory(CellData
, Data
, DataSize
);
365 Value
->Data
= NewCell
;
366 Value
->DataLength
= DataSize
;
370 ASSERT(HvIsCellDirty(Hive
, NewCell
));
371 return STATUS_SUCCESS
;
376 CmpQueryKeyData(IN PHHIVE Hive
,
377 IN PCM_KEY_NODE Node
,
378 IN KEY_INFORMATION_CLASS KeyInformationClass
,
379 IN OUT PVOID KeyInformation
,
381 IN OUT PULONG ResultLength
)
384 ULONG Size
, SizeLeft
, MinimumSize
, Offset
;
385 PKEY_INFORMATION Info
= (PKEY_INFORMATION
)KeyInformation
;
389 /* Check if the value is compressed */
390 if (Node
->Flags
& KEY_COMP_NAME
)
392 /* Get the compressed name size */
393 NameLength
= CmpCompressedNameSize(Node
->Name
, Node
->NameLength
);
397 /* Get the real size */
398 NameLength
= Node
->NameLength
;
401 /* Check what kind of information is being requested */
402 switch (KeyInformationClass
)
404 /* Basic information */
405 case KeyBasicInformation
:
407 /* This is the size we need */
408 Size
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
) + NameLength
;
410 /* And this is the minimum we can work with */
411 MinimumSize
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
);
413 /* Let the caller know and assume success */
414 *ResultLength
= Size
;
415 Status
= STATUS_SUCCESS
;
417 /* Check if the bufer we got is too small */
418 if (Length
< MinimumSize
)
420 /* Let the caller know and fail */
421 Status
= STATUS_BUFFER_TOO_SMALL
;
425 /* Copy the basic information */
426 Info
->KeyBasicInformation
.LastWriteTime
= Node
->LastWriteTime
;
427 Info
->KeyBasicInformation
.TitleIndex
= 0;
428 Info
->KeyBasicInformation
.NameLength
= NameLength
;
430 /* Only the name is left */
431 SizeLeft
= Length
- MinimumSize
;
434 /* Check if we don't have enough space for the name */
437 /* Truncate the name we'll return, and tell the caller */
439 Status
= STATUS_BUFFER_OVERFLOW
;
442 /* Check if this is a compressed key */
443 if (Node
->Flags
& KEY_COMP_NAME
)
445 /* Copy the compressed name */
446 CmpCopyCompressedName(Info
->KeyBasicInformation
.Name
,
453 /* Otherwise, copy the raw name */
454 RtlCopyMemory(Info
->KeyBasicInformation
.Name
,
460 /* Node information */
461 case KeyNodeInformation
:
463 /* Calculate the size we need */
464 Size
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) +
468 /* And the minimum size we can support */
469 MinimumSize
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
);
471 /* Return the size to the caller and assume succes */
472 *ResultLength
= Size
;
473 Status
= STATUS_SUCCESS
;
475 /* Check if the caller's buffer is too small */
476 if (Length
< MinimumSize
)
478 /* Let them know, and fail */
479 Status
= STATUS_BUFFER_TOO_SMALL
;
483 /* Copy the basic information */
484 Info
->KeyNodeInformation
.LastWriteTime
= Node
->LastWriteTime
;
485 Info
->KeyNodeInformation
.TitleIndex
= 0;
486 Info
->KeyNodeInformation
.ClassLength
= Node
->ClassLength
;
487 Info
->KeyNodeInformation
.NameLength
= NameLength
;
489 /* Now the name is left */
490 SizeLeft
= Length
- MinimumSize
;
493 /* Check if the name can fit entirely */
496 /* It can't, we'll have to truncate. Tell the caller */
498 Status
= STATUS_BUFFER_OVERFLOW
;
501 /* Check if the key node name is compressed */
502 if (Node
->Flags
& KEY_COMP_NAME
)
504 /* Copy the compressed name */
505 CmpCopyCompressedName(Info
->KeyNodeInformation
.Name
,
512 /* It isn't, so copy the raw name */
513 RtlCopyMemory(Info
->KeyNodeInformation
.Name
,
518 /* Check if the node has a class */
519 if (Node
->ClassLength
> 0)
521 /* Set the class offset */
522 Offset
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) + NameLength
;
523 Offset
= ALIGN_UP_BY(Offset
, sizeof(ULONG
));
524 Info
->KeyNodeInformation
.ClassOffset
= Offset
;
526 /* Get the class data */
527 ClassData
= HvGetCell(Hive
, Node
->Class
);
528 if (ClassData
== NULL
)
530 Status
= STATUS_INSUFFICIENT_RESOURCES
;
534 /* Check if we can copy anything */
537 /* Copy the class data */
538 RtlCopyMemory((PUCHAR
)Info
+ Offset
,
540 min(Node
->ClassLength
, Length
- Offset
));
543 /* Check if the buffer was large enough */
544 if (Length
< Offset
+ Node
->ClassLength
)
546 Status
= STATUS_BUFFER_OVERFLOW
;
549 /* Release the class cell */
550 HvReleaseCell(Hive
, Node
->Class
);
554 /* It doesn't, so set offset to -1, not 0! */
555 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
559 /* Full information requsted */
560 case KeyFullInformation
:
562 /* This is the size we need */
563 Size
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
) +
566 /* This is what we can work with */
567 MinimumSize
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
569 /* Return it to caller and assume success */
570 *ResultLength
= Size
;
571 Status
= STATUS_SUCCESS
;
573 /* Check if the caller's buffer is to small */
574 if (Length
< MinimumSize
)
576 /* Let them know and fail */
577 Status
= STATUS_BUFFER_TOO_SMALL
;
581 /* Now copy all the basic information */
582 Info
->KeyFullInformation
.LastWriteTime
= Node
->LastWriteTime
;
583 Info
->KeyFullInformation
.TitleIndex
= 0;
584 Info
->KeyFullInformation
.ClassLength
= Node
->ClassLength
;
585 Info
->KeyFullInformation
.SubKeys
= Node
->SubKeyCounts
[Stable
] +
586 Node
->SubKeyCounts
[Volatile
];
587 Info
->KeyFullInformation
.Values
= Node
->ValueList
.Count
;
588 Info
->KeyFullInformation
.MaxNameLen
= Node
->MaxNameLen
;
589 Info
->KeyFullInformation
.MaxClassLen
= Node
->MaxClassLen
;
590 Info
->KeyFullInformation
.MaxValueNameLen
= Node
->MaxValueNameLen
;
591 Info
->KeyFullInformation
.MaxValueDataLen
= Node
->MaxValueDataLen
;
593 /* Check if we have a class */
594 if (Node
->ClassLength
> 0)
596 /* Set the class offset */
597 Offset
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
598 Info
->KeyFullInformation
.ClassOffset
= Offset
;
600 /* Get the class data */
601 ClassData
= HvGetCell(Hive
, Node
->Class
);
602 if (ClassData
== NULL
)
604 Status
= STATUS_INSUFFICIENT_RESOURCES
;
608 /* Copy the class data */
609 NT_ASSERT(Length
> Offset
);
610 RtlCopyMemory(Info
->KeyFullInformation
.Class
,
612 min(Node
->ClassLength
, Length
- Offset
));
614 /* Check if the buffer was large enough */
615 if (Length
< Offset
+ Node
->ClassLength
)
617 Status
= STATUS_BUFFER_OVERFLOW
;
620 /* Release the class cell */
621 HvReleaseCell(Hive
, Node
->Class
);
625 /* We don't have a class, so set offset to -1, not 0! */
626 Info
->KeyFullInformation
.ClassOffset
= 0xFFFFFFFF;
630 /* Any other class that got sent here is invalid! */
633 /* Set failure code */
634 Status
= STATUS_INVALID_PARAMETER
;
644 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
645 IN PUNICODE_STRING ValueName
,
652 PCM_KEY_VALUE Value
= NULL
;
653 HCELL_INDEX CurrentChild
, Cell
;
655 BOOLEAN Found
, Result
;
656 ULONG Count
, ChildIndex
, SmallData
, Storage
;
657 VALUE_SEARCH_RETURN_TYPE SearchResult
;
658 BOOLEAN FirstTry
= TRUE
, FlusherLocked
= FALSE
;
659 HCELL_INDEX ParentCell
= HCELL_NIL
, ChildCell
= HCELL_NIL
;
661 /* Acquire hive and KCB lock */
663 CmpAcquireKcbLockShared(Kcb
);
666 ASSERT(sizeof(ULONG
) == CM_KEY_VALUE_SMALL
);
668 /* Don't touch deleted KCBs */
673 Status
= STATUS_KEY_DELETED
;
677 /* Don't let anyone mess with symlinks */
678 if ((Kcb
->Flags
& KEY_SYM_LINK
) &&
679 ((Type
!= REG_LINK
) ||
681 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName
, ValueName
, TRUE
))))
683 /* Invalid modification of a symlink key */
684 Status
= STATUS_ACCESS_DENIED
;
688 /* Check if this is the first attempt */
691 /* Search for the value in the cache */
692 SearchResult
= CmpCompareNewValueDataAgainstKCBCache(Kcb
,
697 if (SearchResult
== SearchNeedExclusiveLock
)
699 /* Try again with the exclusive lock */
700 CmpConvertKcbSharedToExclusive(Kcb
);
703 else if (SearchResult
== SearchSuccess
)
705 /* We don't actually need to do anything! */
706 Status
= STATUS_SUCCESS
;
710 /* We need the exclusive KCB lock now */
711 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
712 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
714 /* Acquire exclusive lock */
715 CmpConvertKcbSharedToExclusive(Kcb
);
718 /* Cache lookup failed, so don't try it next time */
721 /* Now grab the flush lock since the key will be modified */
722 ASSERT(FlusherLocked
== FALSE
);
723 CmpLockHiveFlusherShared((PCMHIVE
)Kcb
->KeyHive
);
724 FlusherLocked
= TRUE
;
729 /* Get pointer to key cell */
734 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
738 /* Prepare to scan the key node */
739 Count
= Parent
->ValueList
.Count
;
743 /* Try to find the existing name */
744 Result
= CmpFindNameInList(Hive
,
752 Status
= STATUS_INSUFFICIENT_RESOURCES
;
756 /* Check if we found something */
757 if (CurrentChild
!= HCELL_NIL
)
759 /* Release existing child */
760 if (ChildCell
!= HCELL_NIL
)
762 HvReleaseCell(Hive
, ChildCell
);
763 ChildCell
= HCELL_NIL
;
767 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
, CurrentChild
);
771 Status
= STATUS_INSUFFICIENT_RESOURCES
;
775 /* Remember that we found it */
776 ChildCell
= CurrentChild
;
782 /* No child list, we'll need to add it */
787 /* Should only get here on the second pass */
788 ASSERT(FirstTry
== FALSE
);
790 /* The KCB must be locked exclusive at this point */
791 CMP_ASSERT_KCB_LOCK(Kcb
);
793 /* Mark the cell dirty */
794 if (!HvMarkCellDirty(Hive
, Cell
, FALSE
))
796 /* Not enough log space, fail */
797 Status
= STATUS_NO_LOG_SPACE
;
801 /* Get the storage type */
802 Storage
= HvGetCellType(Cell
);
804 /* Check if this is small data */
806 if ((DataLength
<= CM_KEY_VALUE_SMALL
) && (DataLength
> 0))
808 /* Need SEH because user data may be invalid */
812 RtlCopyMemory(&SmallData
, Data
, DataLength
);
814 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
816 /* Return failure code */
817 Status
= _SEH2_GetExceptionCode();
818 _SEH2_YIELD(goto Quickie
);
823 /* Check if we didn't find a matching key */
826 /* Call the internal routine */
827 Status
= CmpSetValueKeyNew(Hive
,
839 /* Call the internal routine */
840 Status
= CmpSetValueKeyExisting(Hive
,
850 /* Check for success */
851 if (NT_SUCCESS(Status
))
853 /* Check if the maximum value name length changed */
854 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
855 if (Parent
->MaxValueNameLen
< ValueName
->Length
)
857 /* Set the new values */
858 Parent
->MaxValueNameLen
= ValueName
->Length
;
859 Kcb
->KcbMaxValueNameLen
= ValueName
->Length
;
862 /* Check if the maximum data length changed */
863 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
864 if (Parent
->MaxValueDataLen
< DataLength
)
867 Parent
->MaxValueDataLen
= DataLength
;
868 Kcb
->KcbMaxValueDataLen
= Parent
->MaxValueDataLen
;
871 /* Save the write time */
872 KeQuerySystemTime(&Parent
->LastWriteTime
);
873 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
875 /* Check if the cell is cached */
876 if ((Found
) && (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)))
878 /* Shouldn't happen */
883 /* Cleanup the value cache */
884 CmpCleanUpKcbValueCache(Kcb
);
887 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
888 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
890 /* Set the value cache */
891 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
892 Kcb
->ValueCache
.ValueList
= Parent
->ValueList
.List
;
895 /* Notify registered callbacks */
899 REG_NOTIFY_CHANGE_LAST_SET
);
902 /* Release the cells */
904 if ((ParentCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ParentCell
);
905 if ((ChildCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ChildCell
);
907 /* Release the locks */
908 if (FlusherLocked
) CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
909 CmpReleaseKcbLock(Kcb
);
916 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
917 IN UNICODE_STRING ValueName
)
919 NTSTATUS Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
922 HCELL_INDEX ChildCell
, Cell
;
923 PCHILD_LIST ChildList
;
924 PCM_KEY_VALUE Value
= NULL
;
928 /* Acquire hive lock */
931 /* Lock KCB exclusively */
932 CmpAcquireKcbLockExclusive(Kcb
);
934 /* Don't touch deleted keys */
937 /* Undo everything */
938 CmpReleaseKcbLock(Kcb
);
940 return STATUS_KEY_DELETED
;
943 /* Get the hive and the cell index */
948 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
950 /* Get the parent key node */
951 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
954 /* Get the value list and check if it has any entries */
955 ChildList
= &Parent
->ValueList
;
956 if (ChildList
->Count
)
958 /* Try to find this value */
959 Result
= CmpFindNameInList(Hive
,
967 Status
= STATUS_INSUFFICIENT_RESOURCES
;
971 /* Value not found, return error */
972 if (ChildCell
== HCELL_NIL
) goto Quickie
;
974 /* We found the value, mark all relevant cells dirty */
975 if (!((HvMarkCellDirty(Hive
, Cell
, FALSE
)) &&
976 (HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
)) &&
977 (HvMarkCellDirty(Hive
, ChildCell
, FALSE
))))
979 /* Not enough log space, fail */
980 Status
= STATUS_NO_LOG_SPACE
;
984 /* Get the key value */
985 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
,ChildCell
);
988 /* Mark it and all related data as dirty */
989 if (!CmpMarkValueDataDirty(Hive
, Value
))
991 /* Not enough log space, fail */
992 Status
= STATUS_NO_LOG_SPACE
;
997 ASSERT(HvIsCellDirty(Hive
, Parent
->ValueList
.List
));
998 ASSERT(HvIsCellDirty(Hive
, ChildCell
));
1000 /* Remove the value from the child list */
1001 Status
= CmpRemoveValueFromList(Hive
, ChildIndex
, ChildList
);
1002 if (!NT_SUCCESS(Status
))
1004 /* Set known error */
1005 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1009 /* Remove the value and its data itself */
1010 if (!CmpFreeValue(Hive
, ChildCell
))
1012 /* Failed to free the value, fail */
1013 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1017 /* Set the last write time */
1018 KeQuerySystemTime(&Parent
->LastWriteTime
);
1019 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
1022 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
1023 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
1024 ASSERT(HvIsCellDirty(Hive
, Cell
));
1026 /* Check if the value list is empty now */
1027 if (!Parent
->ValueList
.Count
)
1029 /* Then clear key node data */
1030 Parent
->MaxValueNameLen
= 0;
1031 Parent
->MaxValueDataLen
= 0;
1032 Kcb
->KcbMaxValueNameLen
= 0;
1033 Kcb
->KcbMaxValueDataLen
= 0;
1036 /* Cleanup the value cache */
1037 CmpCleanUpKcbValueCache(Kcb
);
1040 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
1041 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
1043 /* Set the value cache */
1044 Kcb
->ValueCache
.Count
= ChildList
->Count
;
1045 Kcb
->ValueCache
.ValueList
= ChildList
->List
;
1047 /* Notify registered callbacks */
1048 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_LAST_SET
);
1050 /* Change default Status to success */
1051 Status
= STATUS_SUCCESS
;
1055 /* Release the parent cell, if any */
1056 if (Parent
) HvReleaseCell(Hive
, Cell
);
1058 /* Check if we had a value */
1061 /* Release the child cell */
1062 ASSERT(ChildCell
!= HCELL_NIL
);
1063 HvReleaseCell(Hive
, ChildCell
);
1067 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1068 CmpReleaseKcbLock(Kcb
);
1069 CmpUnlockRegistry();
1075 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1076 IN UNICODE_STRING ValueName
,
1077 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1078 IN PVOID KeyValueInformation
,
1080 IN PULONG ResultLength
)
1083 PCM_KEY_VALUE ValueData
;
1085 BOOLEAN ValueCached
= FALSE
;
1086 PCM_CACHED_VALUE
*CachedValue
;
1087 HCELL_INDEX CellToRelease
;
1088 VALUE_SEARCH_RETURN_TYPE Result
;
1092 /* Acquire hive lock */
1095 /* Lock the KCB shared */
1096 CmpAcquireKcbLockShared(Kcb
);
1098 /* Don't touch deleted keys */
1102 /* Undo everything */
1103 CmpReleaseKcbLock(Kcb
);
1104 CmpUnlockRegistry();
1105 return STATUS_KEY_DELETED
;
1108 /* We don't deal with this yet */
1109 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1111 /* Shouldn't happen */
1116 Hive
= Kcb
->KeyHive
;
1118 /* Find the key value */
1119 Result
= CmpFindValueByNameFromCache(Kcb
,
1126 if (Result
== SearchNeedExclusiveLock
)
1128 /* Check if we need an exclusive lock */
1129 ASSERT(CellToRelease
== HCELL_NIL
);
1130 ASSERT(ValueData
== NULL
);
1132 /* Try with exclusive KCB lock */
1133 CmpConvertKcbSharedToExclusive(Kcb
);
1137 if (Result
== SearchSuccess
)
1140 ASSERT(ValueData
!= NULL
);
1142 /* User data, protect against exceptions */
1145 /* Query the information requested */
1146 Result
= CmpQueryKeyValueData(Kcb
,
1150 KeyValueInformationClass
,
1151 KeyValueInformation
,
1155 if (Result
== SearchNeedExclusiveLock
)
1157 /* Release the value cell */
1158 if (CellToRelease
!= HCELL_NIL
)
1160 HvReleaseCell(Hive
, CellToRelease
);
1161 CellToRelease
= HCELL_NIL
;
1164 /* Try with exclusive KCB lock */
1165 CmpConvertKcbSharedToExclusive(Kcb
);
1166 _SEH2_YIELD(goto DoAgain
);
1169 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1171 Status
= _SEH2_GetExceptionCode();
1177 /* Failed to find the value */
1178 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1181 /* If we have a cell to release, do so */
1182 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1185 CmpReleaseKcbLock(Kcb
);
1186 CmpUnlockRegistry();
1192 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1194 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1195 IN PVOID KeyValueInformation
,
1197 IN PULONG ResultLength
)
1201 PCM_KEY_NODE Parent
;
1202 HCELL_INDEX CellToRelease
= HCELL_NIL
, CellToRelease2
= HCELL_NIL
;
1203 VALUE_SEARCH_RETURN_TYPE Result
;
1204 BOOLEAN IndexIsCached
, ValueIsCached
= FALSE
;
1205 PCELL_DATA CellData
;
1206 PCM_CACHED_VALUE
*CachedValue
;
1207 PCM_KEY_VALUE ValueData
= NULL
;
1210 /* Acquire hive lock */
1213 /* Lock the KCB shared */
1214 CmpAcquireKcbLockShared(Kcb
);
1216 /* Don't touch deleted keys */
1220 /* Undo everything */
1221 CmpReleaseKcbLock(Kcb
);
1222 CmpUnlockRegistry();
1223 return STATUS_KEY_DELETED
;
1226 /* Get the hive and parent */
1227 Hive
= Kcb
->KeyHive
;
1228 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1231 /* FIXME: Lack of cache? */
1232 if (Kcb
->ValueCache
.Count
!= Parent
->ValueList
.Count
)
1234 DPRINT1("HACK: Overriding value cache count\n");
1235 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
1238 /* Make sure the index is valid */
1239 if (Index
>= Kcb
->ValueCache
.Count
)
1241 /* Release the cell and fail */
1242 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1243 Status
= STATUS_NO_MORE_ENTRIES
;
1247 /* We don't deal with this yet */
1248 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1250 /* Shouldn't happen */
1254 /* Find the value list */
1255 Result
= CmpGetValueListFromCache(Kcb
,
1259 if (Result
== SearchNeedExclusiveLock
)
1261 /* Check if we need an exclusive lock */
1262 ASSERT(CellToRelease
== HCELL_NIL
);
1263 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1265 /* Try with exclusive KCB lock */
1266 CmpConvertKcbSharedToExclusive(Kcb
);
1269 else if (Result
!= SearchSuccess
)
1272 ASSERT(CellData
== NULL
);
1274 /* Release the cell and fail */
1275 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1279 /* Now get the key value */
1280 Result
= CmpGetValueKeyFromCache(Kcb
,
1288 if (Result
== SearchNeedExclusiveLock
)
1291 ASSERT(CellToRelease2
== HCELL_NIL
);
1294 HvReleaseCell(Hive
, CellToRelease
);
1295 CellToRelease
= HCELL_NIL
;
1297 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1299 /* Try with exclusive KCB lock */
1300 CmpConvertKcbSharedToExclusive(Kcb
);
1303 else if (Result
!= SearchSuccess
)
1306 ASSERT(ValueData
== NULL
);
1308 /* Release the cells and fail */
1309 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1313 /* User data, need SEH */
1316 /* Query the information requested */
1317 Result
= CmpQueryKeyValueData(Kcb
,
1321 KeyValueInformationClass
,
1322 KeyValueInformation
,
1326 if (Result
== SearchNeedExclusiveLock
)
1329 if (CellToRelease2
) HvReleaseCell(Hive
, CellToRelease2
);
1330 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1331 if (CellToRelease
) HvReleaseCell(Hive
, CellToRelease
);
1333 /* Try with exclusive KCB lock */
1334 CmpConvertKcbSharedToExclusive(Kcb
);
1335 _SEH2_YIELD(goto DoAgain
);
1338 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1340 /* Get exception code */
1341 Status
= _SEH2_GetExceptionCode();
1346 /* If we have a cell to release, do so */
1347 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1349 /* Release the parent cell */
1350 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1352 /* If we have a cell to release, do so */
1353 if (CellToRelease2
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease2
);
1356 CmpReleaseKcbLock(Kcb
);
1357 CmpUnlockRegistry();
1363 CmpQueryKeyDataFromCache(
1364 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1365 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo
,
1367 _Out_ PULONG ResultLength
)
1371 HCELL_INDEX KeyCell
;
1375 /* Get the hive and cell index */
1376 KeyHive
= Kcb
->KeyHash
.KeyHive
;
1377 KeyCell
= Kcb
->KeyHash
.KeyCell
;
1380 /* Get the cell node */
1381 Node
= HvGetCell(KeyHive
, KeyCell
);
1385 ASSERT(Node
->ValueList
.Count
== Kcb
->ValueCache
.Count
);
1387 if (!(Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
))
1389 SubKeyCount
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1390 if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1392 ASSERT(SubKeyCount
== 0);
1394 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1396 ASSERT(SubKeyCount
== 1);
1398 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1400 ASSERT(SubKeyCount
== Kcb
->IndexHint
->Count
);
1404 ASSERT(SubKeyCount
== Kcb
->SubKeyCount
);
1408 ASSERT(Node
->LastWriteTime
.QuadPart
== Kcb
->KcbLastWriteTime
.QuadPart
);
1409 ASSERT(Node
->MaxNameLen
== Kcb
->KcbMaxNameLen
);
1410 ASSERT(Node
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
1411 ASSERT(Node
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
1413 /* Release the cell */
1414 HvReleaseCell(KeyHive
, KeyCell
);
1418 /* Make sure we have a name block */
1419 if (Kcb
->NameBlock
== NULL
)
1421 return STATUS_INSUFFICIENT_RESOURCES
;
1424 /* Check for compressed name */
1425 if (Kcb
->NameBlock
->Compressed
)
1427 /* Calculate the name size */
1428 NameLength
= CmpCompressedNameSize(Kcb
->NameBlock
->NameHash
.Name
,
1429 Kcb
->NameBlock
->NameHash
.NameLength
);
1433 /* Use the stored name size */
1434 NameLength
= Kcb
->NameBlock
->NameHash
.NameLength
;
1437 /* Validate buffer length (we do not copy the name!) */
1438 *ResultLength
= sizeof(*KeyCachedInfo
);
1439 if (Length
< *ResultLength
)
1441 return STATUS_BUFFER_TOO_SMALL
;
1444 /* Fill the structure */
1445 KeyCachedInfo
->LastWriteTime
= Kcb
->KcbLastWriteTime
;
1446 KeyCachedInfo
->TitleIndex
= 0;
1447 KeyCachedInfo
->NameLength
= NameLength
;
1448 KeyCachedInfo
->Values
= Kcb
->ValueCache
.Count
;
1449 KeyCachedInfo
->MaxNameLen
= Kcb
->KcbMaxNameLen
;
1450 KeyCachedInfo
->MaxValueNameLen
= Kcb
->KcbMaxValueNameLen
;
1451 KeyCachedInfo
->MaxValueDataLen
= Kcb
->KcbMaxValueDataLen
;
1453 /* Check the ExtFlags for what we have */
1454 if (Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
)
1456 /* Cache is not valid, do a full lookup */
1457 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb
);
1459 /* Get the cell node */
1460 Node
= HvGetCell(KeyHive
, KeyCell
);
1463 return STATUS_INSUFFICIENT_RESOURCES
;
1466 /* Calculate number of subkeys */
1467 KeyCachedInfo
->SubKeys
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1469 /* Release the cell */
1470 HvReleaseCell(KeyHive
, KeyCell
);
1472 else if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1474 /* There are no subkeys */
1475 KeyCachedInfo
->SubKeys
= 0;
1477 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1479 /* There is exactly one subley */
1480 KeyCachedInfo
->SubKeys
= 1;
1482 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1484 /* Get the number of subkeys from the subkey hint */
1485 KeyCachedInfo
->SubKeys
= Kcb
->IndexHint
->Count
;
1489 /* No subkey hint, use the key count field */
1490 KeyCachedInfo
->SubKeys
= Kcb
->SubKeyCount
;
1493 return STATUS_SUCCESS
;
1498 CmpQueryFlagsInformation(
1499 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1500 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo
,
1502 _In_ PULONG ResultLength
)
1504 /* Validate the buffer size */
1505 *ResultLength
= sizeof(*KeyFlagsInfo
);
1506 if (Length
< *ResultLength
)
1508 return STATUS_BUFFER_TOO_SMALL
;
1511 /* Copy the user flags */
1512 KeyFlagsInfo
->UserFlags
= Kcb
->KcbUserFlags
;
1514 return STATUS_SUCCESS
;
1519 CmpQueryNameInformation(
1520 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1521 _Out_opt_ PKEY_NAME_INFORMATION KeyNameInfo
,
1523 _Out_ PULONG ResultLength
)
1526 PCM_KEY_CONTROL_BLOCK CurrentKcb
;
1531 /* Count the needed buffer size */
1534 if (CurrentKcb
->NameBlock
->Compressed
)
1535 NeededLength
+= CmpCompressedNameSize(CurrentKcb
->NameBlock
->Name
, CurrentKcb
->NameBlock
->NameLength
);
1537 NeededLength
+= CurrentKcb
->NameBlock
->NameLength
;
1539 NeededLength
+= sizeof(OBJ_NAME_PATH_SEPARATOR
);
1541 CurrentKcb
= CurrentKcb
->ParentKcb
;
1546 *ResultLength
= FIELD_OFFSET(KEY_NAME_INFORMATION
, Name
) + NeededLength
;
1547 if (Length
< RTL_SIZEOF_THROUGH_FIELD(KEY_NAME_INFORMATION
, NameLength
))
1548 return STATUS_BUFFER_TOO_SMALL
;
1549 if (Length
< *ResultLength
)
1551 KeyNameInfo
->NameLength
= NeededLength
;
1552 return STATUS_BUFFER_OVERFLOW
;
1555 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1557 return _SEH2_GetExceptionCode();
1561 /* Do the real copy */
1562 KeyNameInfo
->NameLength
= 0;
1571 if (CurrentKcb
->NameBlock
->Compressed
)
1573 NameLength
= CmpCompressedNameSize(CurrentKcb
->NameBlock
->Name
, CurrentKcb
->NameBlock
->NameLength
);
1574 /* Copy the compressed name */
1575 CmpCopyCompressedName(&KeyNameInfo
->Name
[(NeededLength
- NameLength
)/sizeof(WCHAR
)],
1577 CurrentKcb
->NameBlock
->Name
,
1578 CurrentKcb
->NameBlock
->NameLength
);
1582 NameLength
= CurrentKcb
->NameBlock
->NameLength
;
1583 /* Otherwise, copy the raw name */
1584 RtlCopyMemory(&KeyNameInfo
->Name
[(NeededLength
- NameLength
)/sizeof(WCHAR
)],
1585 CurrentKcb
->NameBlock
->Name
,
1589 NeededLength
-= NameLength
;
1590 NeededLength
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
1591 /* Add path separator */
1592 KeyNameInfo
->Name
[NeededLength
/sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
1593 KeyNameInfo
->NameLength
+= NameLength
+ sizeof(OBJ_NAME_PATH_SEPARATOR
);
1595 CurrentKcb
= CurrentKcb
->ParentKcb
;
1598 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1600 return _SEH2_GetExceptionCode();
1604 /* Make sure we copied everything */
1605 ASSERT(NeededLength
== 0);
1606 ASSERT(KeyNameInfo
->Name
[0] == OBJ_NAME_PATH_SEPARATOR
);
1609 return STATUS_SUCCESS
;
1615 CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1616 _In_ KEY_INFORMATION_CLASS KeyInformationClass
,
1617 _Out_opt_ PVOID KeyInformation
,
1619 _Out_ PULONG ResultLength
)
1623 PCM_KEY_NODE Parent
;
1624 HV_TRACK_CELL_REF CellReferences
= {0};
1626 /* Acquire hive lock */
1629 /* Lock KCB shared */
1630 CmpAcquireKcbLockShared(Kcb
);
1632 /* Don't touch deleted keys */
1636 Status
= STATUS_KEY_DELETED
;
1640 /* Check what class we got */
1641 switch (KeyInformationClass
)
1643 /* Typical information */
1644 case KeyFullInformation
:
1645 case KeyBasicInformation
:
1646 case KeyNodeInformation
:
1648 /* Get the hive and parent */
1649 Hive
= Kcb
->KeyHive
;
1650 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1653 /* Track cell references */
1654 if (!HvTrackCellRef(&CellReferences
, Hive
, Kcb
->KeyCell
))
1656 /* Not enough memory to track references */
1657 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1661 /* Call the internal API */
1662 Status
= CmpQueryKeyData(Hive
,
1664 KeyInformationClass
,
1671 case KeyCachedInformation
:
1672 /* Call the internal API */
1673 Status
= CmpQueryKeyDataFromCache(Kcb
,
1679 case KeyFlagsInformation
:
1680 /* Call the internal API */
1681 Status
= CmpQueryFlagsInformation(Kcb
,
1687 case KeyNameInformation
:
1688 /* Call the internal API */
1689 Status
= CmpQueryNameInformation(Kcb
,
1695 /* Illegal classes */
1698 /* Print message and fail */
1699 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1700 Status
= STATUS_INVALID_INFO_CLASS
;
1705 /* Release references */
1706 HvReleaseFreeCellRefArray(&CellReferences
);
1709 CmpReleaseKcbLock(Kcb
);
1710 CmpUnlockRegistry();
1716 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1718 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1719 IN PVOID KeyInformation
,
1721 IN PULONG ResultLength
)
1725 PCM_KEY_NODE Parent
, Child
;
1726 HCELL_INDEX ChildCell
;
1727 HV_TRACK_CELL_REF CellReferences
= {0};
1729 /* Acquire hive lock */
1732 /* Lock the KCB shared */
1733 CmpAcquireKcbLockShared(Kcb
);
1735 /* Don't touch deleted keys */
1738 /* Undo everything */
1739 Status
= STATUS_KEY_DELETED
;
1743 /* Get the hive and parent */
1744 Hive
= Kcb
->KeyHive
;
1745 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1748 /* Get the child cell */
1749 ChildCell
= CmpFindSubKeyByNumber(Hive
, Parent
, Index
);
1751 /* Release the parent cell */
1752 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1754 /* Check if we found the child */
1755 if (ChildCell
== HCELL_NIL
)
1757 /* We didn't, fail */
1758 Status
= STATUS_NO_MORE_ENTRIES
;
1762 /* Now get the actual child node */
1763 Child
= (PCM_KEY_NODE
)HvGetCell(Hive
, ChildCell
);
1766 /* Track references */
1767 if (!HvTrackCellRef(&CellReferences
, Hive
, ChildCell
))
1769 /* Can't allocate memory for tracking */
1770 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1774 /* Data can be user-mode, use SEH */
1777 /* Query the data requested */
1778 Status
= CmpQueryKeyData(Hive
,
1780 KeyInformationClass
,
1785 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1787 /* Fail with exception code */
1788 Status
= _SEH2_GetExceptionCode();
1789 _SEH2_YIELD(goto Quickie
);
1794 /* Release references */
1795 HvReleaseFreeCellRefArray(&CellReferences
);
1798 CmpReleaseKcbLock(Kcb
);
1799 CmpUnlockRegistry();
1805 CmDeleteKey(IN PCM_KEY_BODY KeyBody
)
1809 PCM_KEY_NODE Node
, Parent
;
1810 HCELL_INDEX Cell
, ParentCell
;
1811 PCM_KEY_CONTROL_BLOCK Kcb
;
1813 /* Acquire hive lock */
1817 Kcb
= KeyBody
->KeyControlBlock
;
1819 /* Don't allow deleting the root */
1820 if (!Kcb
->ParentKcb
)
1823 CmpUnlockRegistry();
1824 return STATUS_CANNOT_DELETE
;
1827 /* Lock parent and child */
1828 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1830 /* Check if we're already being deleted */
1833 /* Don't do it twice */
1834 Status
= STATUS_SUCCESS
;
1838 /* Get the hive and node */
1839 Hive
= Kcb
->KeyHive
;
1840 Cell
= Kcb
->KeyCell
;
1843 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
1845 /* Get the key node */
1846 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
1850 ASSERT(Node
->Flags
== Kcb
->Flags
);
1852 /* Check if we don't have any children */
1853 if (!(Node
->SubKeyCounts
[Stable
] + Node
->SubKeyCounts
[Volatile
]) &&
1854 !(Node
->Flags
& KEY_NO_DELETE
))
1856 /* Send notification to registered callbacks */
1857 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_NAME
);
1859 /* Get the parent and free the cell */
1860 ParentCell
= Node
->Parent
;
1861 Status
= CmpFreeKeyByCell(Hive
, Cell
, TRUE
);
1862 if (NT_SUCCESS(Status
))
1864 /* Flush any notifications */
1865 CmpFlushNotifiesOnKeyBodyList(Kcb
, FALSE
);
1867 /* Clean up information we have on the subkey */
1868 CmpCleanUpSubKeyInfo(Kcb
->ParentKcb
);
1870 /* Get the parent node */
1871 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, ParentCell
);
1874 /* Update the maximum name length */
1875 Kcb
->ParentKcb
->KcbMaxNameLen
= (USHORT
)Parent
->MaxNameLen
;
1877 /* Make sure we're dirty */
1878 ASSERT(HvIsCellDirty(Hive
, ParentCell
));
1880 /* Update the write time */
1881 KeQuerySystemTime(&Parent
->LastWriteTime
);
1882 Kcb
->ParentKcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
1884 /* Release the cell */
1885 HvReleaseCell(Hive
, ParentCell
);
1888 /* Set the KCB in delete mode and remove it */
1890 CmpRemoveKeyControlBlock(Kcb
);
1892 /* Clear the cell */
1893 Kcb
->KeyCell
= HCELL_NIL
;
1899 Status
= STATUS_CANNOT_DELETE
;
1902 /* Release the cell */
1903 HvReleaseCell(Hive
, Cell
);
1905 /* Release flush lock */
1906 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1908 /* Release the KCB locks */
1910 CmpReleaseTwoKcbLockByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1912 /* Release hive lock */
1913 CmpUnlockRegistry();
1919 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1920 IN BOOLEAN ExclusiveLock
)
1923 NTSTATUS Status
= STATUS_SUCCESS
;
1926 /* Ignore flushes until we're ready */
1927 if (CmpNoWrite
) return STATUS_SUCCESS
;
1930 Hive
= Kcb
->KeyHive
;
1931 CmHive
= (PCMHIVE
)Hive
;
1933 /* Check if this is the master hive */
1934 if (CmHive
== CmiVolatileHive
)
1936 /* Flush all the hives instead */
1937 CmpDoFlushAll(FALSE
);
1941 /* Don't touch the hive */
1942 CmpLockHiveFlusherExclusive(CmHive
);
1943 ASSERT(CmHive
->ViewLock
);
1944 KeAcquireGuardedMutex(CmHive
->ViewLock
);
1945 CmHive
->ViewLockOwner
= KeGetCurrentThread();
1947 /* Will the hive shrink? */
1948 if (HvHiveWillShrink(Hive
))
1950 /* I don't believe the current Hv does shrinking */
1955 /* Now we can release views */
1956 ASSERT(CmHive
->ViewLock
);
1957 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive
);
1958 ASSERT(KeGetCurrentThread() == CmHive
->ViewLockOwner
);
1959 KeReleaseGuardedMutex(CmHive
->ViewLock
);
1962 /* Flush only this hive */
1963 if (!HvSyncHive(Hive
))
1966 Status
= STATUS_REGISTRY_IO_FAILED
;
1969 /* Release the flush lock */
1970 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1973 /* Return the status */
1979 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey
,
1980 IN POBJECT_ATTRIBUTES SourceFile
,
1982 IN PCM_KEY_BODY KeyBody
)
1984 SECURITY_QUALITY_OF_SERVICE ServiceQos
;
1985 SECURITY_CLIENT_CONTEXT ClientSecurityContext
;
1987 BOOLEAN Allocate
= TRUE
;
1988 PCMHIVE CmHive
, LoadedHive
;
1990 CM_PARSE_CONTEXT ParseContext
;
1992 /* Check if we have a trust key */
1996 DPRINT("Trusted classes not yet supported\n");
1999 /* Build a service QoS for a security context */
2000 ServiceQos
.Length
= sizeof(SECURITY_QUALITY_OF_SERVICE
);
2001 ServiceQos
.ImpersonationLevel
= SecurityImpersonation
;
2002 ServiceQos
.ContextTrackingMode
= SECURITY_DYNAMIC_TRACKING
;
2003 ServiceQos
.EffectiveOnly
= TRUE
;
2004 Status
= SeCreateClientSecurity(PsGetCurrentThread(),
2007 &ClientSecurityContext
);
2008 if (!NT_SUCCESS(Status
))
2011 DPRINT1("Security context failed\n");
2015 /* Open the target key */
2017 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, TargetKey
);
2019 RtlZeroMemory(&ParseContext
, sizeof(ParseContext
));
2020 ParseContext
.CreateOperation
= FALSE
;
2021 Status
= ObOpenObjectByName(TargetKey
,
2029 if (!NT_SUCCESS(Status
)) KeyHandle
= NULL
;
2032 Status
= CmpCmdHiveOpen(SourceFile
,
2033 &ClientSecurityContext
,
2038 /* Get rid of the security context */
2039 SeDeleteClientSecurity(&ClientSecurityContext
);
2041 /* See if we failed */
2042 if (!NT_SUCCESS(Status
))
2044 /* See if the target already existed */
2047 /* Lock the registry */
2048 CmpLockRegistryExclusive();
2050 /* Check if we are already loaded */
2051 if (CmpIsHiveAlreadyLoaded(KeyHandle
, SourceFile
, &LoadedHive
))
2053 /* That's okay then */
2055 Status
= STATUS_SUCCESS
;
2058 /* Release the registry */
2059 CmpUnlockRegistry();
2062 /* Close the key handle if we had one */
2063 if (KeyHandle
) ZwClose(KeyHandle
);
2067 /* Lock the registry shared */
2071 ExAcquirePushLockExclusive(&CmpLoadHiveLock
);
2073 /* Lock the hive to this thread */
2074 CmHive
->Hive
.HiveFlags
|= HIVE_IS_UNLOADING
;
2075 CmHive
->CreatorOwner
= KeGetCurrentThread();
2078 if (Flags
& REG_NO_LAZY_FLUSH
) CmHive
->Hive
.HiveFlags
|= HIVE_NOLAZYFLUSH
;
2081 Status
= CmpLinkHiveToMaster(TargetKey
->ObjectName
,
2082 TargetKey
->RootDirectory
,
2085 TargetKey
->SecurityDescriptor
);
2086 if (NT_SUCCESS(Status
))
2088 /* Add to HiveList key */
2089 CmpAddToHiveFileList(CmHive
);
2091 /* Sync the hive if necessary */
2094 /* Sync it under the flusher lock */
2095 CmpLockHiveFlusherExclusive(CmHive
);
2096 HvSyncHive(&CmHive
->Hive
);
2097 CmpUnlockHiveFlusher(CmHive
);
2100 /* Release the hive */
2101 CmHive
->Hive
.HiveFlags
&= ~HIVE_IS_UNLOADING
;
2102 CmHive
->CreatorOwner
= NULL
;
2105 ExReleasePushLock(&CmpLoadHiveLock
);
2109 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status
);
2114 /* Is this first profile load? */
2115 if (!(CmpProfileLoaded
) && !(CmpWasSetupBoot
))
2117 /* User is now logged on, set quotas */
2118 CmpProfileLoaded
= TRUE
;
2119 CmpSetGlobalQuotaAllowed();
2122 /* Unlock the registry */
2123 CmpUnlockRegistry();
2125 /* Close handle and return */
2126 if (KeyHandle
) ZwClose(KeyHandle
);
2132 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
2136 return STATUS_NOT_IMPLEMENTED
;
2141 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb
,
2142 IN BOOLEAN RemoveEmptyCacheEntries
)
2145 PCM_KEY_CONTROL_BLOCK CachedKcb
;
2146 PCM_KEY_CONTROL_BLOCK ParentKcb
;
2147 ULONG ParentKeyCount
;
2151 DPRINT("CmCountOpenSubKeys() called\n");
2153 /* The root key is the only referenced key. There are no refereced sub keys. */
2154 if (RootKcb
->RefCount
== 1)
2156 DPRINT("open sub keys: 0\n");
2160 /* Enumerate all hash lists */
2161 for (i
= 0; i
< CmpHashTableSize
; i
++)
2163 /* Get the first cache entry */
2164 Entry
= CmpCacheTable
[i
].Entry
;
2166 /* Enumerate all cache entries */
2169 /* Get the KCB of the current cache entry */
2170 CachedKcb
= CONTAINING_RECORD(Entry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
2172 /* Check keys only that are subkeys to our root key */
2173 if (CachedKcb
->TotalLevels
> RootKcb
->TotalLevels
)
2175 /* Calculate the number of parent keys to the root key */
2176 ParentKeyCount
= CachedKcb
->TotalLevels
- RootKcb
->TotalLevels
;
2178 /* Find a parent key that could be the root key */
2179 ParentKcb
= CachedKcb
;
2180 for (j
= 0; j
< ParentKeyCount
; j
++)
2182 ParentKcb
= ParentKcb
->ParentKcb
;
2185 /* Check whether the parent is the root key */
2186 if (ParentKcb
== RootKcb
)
2188 DPRINT("Found a sub key \n");
2189 DPRINT("RefCount = %u\n", CachedKcb
->RefCount
);
2191 if (CachedKcb
->RefCount
> 0)
2193 /* Count the current hash entry if it is in use */
2196 else if ((CachedKcb
->RefCount
== 0) && (RemoveEmptyCacheEntries
!= FALSE
))
2198 /* Remove the current key from the delayed close list */
2199 CmpRemoveFromDelayedClose(CachedKcb
);
2201 /* Remove the current cache entry */
2202 CmpCleanUpKcbCacheWithLock(CachedKcb
, TRUE
);
2204 /* Restart, because the hash list has changed */
2205 Entry
= CmpCacheTable
[i
].Entry
;
2211 /* Get the next cache entry */
2212 Entry
= Entry
->NextHash
;
2216 DPRINT("open sub keys: %u\n", SubKeys
);
2223 CmpCopyCell(IN PHHIVE SourceHive
,
2224 IN HCELL_INDEX SourceCell
,
2225 IN PHHIVE DestinationHive
,
2226 IN HSTORAGE_TYPE StorageType
)
2228 PCELL_DATA SourceData
;
2229 PCELL_DATA DestinationData
= NULL
;
2230 HCELL_INDEX DestinationCell
= HCELL_NIL
;
2234 /* Get the data and the size of the source cell */
2235 SourceData
= HvGetCell(SourceHive
, SourceCell
);
2236 DataSize
= HvGetCellSize(SourceHive
, SourceData
);
2238 /* Allocate a new cell in the destination hive */
2239 DestinationCell
= HvAllocateCell(DestinationHive
,
2243 if (DestinationCell
== HCELL_NIL
) goto Cleanup
;
2245 /* Get the data of the destination cell */
2246 DestinationData
= HvGetCell(DestinationHive
, DestinationCell
);
2248 /* Copy the data from the source cell to the destination cell */
2249 RtlMoveMemory(DestinationData
, SourceData
, DataSize
);
2253 /* Release the cells */
2254 if (SourceData
) HvReleaseCell(SourceHive
, SourceCell
);
2255 if (DestinationData
) HvReleaseCell(DestinationHive
, DestinationCell
);
2257 /* Return the destination cell index */
2258 return DestinationCell
;
2264 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive
,
2265 IN HCELL_INDEX SrcKeyCell
,
2266 IN PHHIVE DestinationHive
,
2267 IN HCELL_INDEX Parent
,
2268 IN HSTORAGE_TYPE StorageType
,
2269 OUT PHCELL_INDEX DestKeyCell OPTIONAL
)
2272 PCM_KEY_NODE SrcNode
;
2273 PCM_KEY_NODE DestNode
= NULL
;
2274 HCELL_INDEX NewKeyCell
, SubKey
, NewSubKey
;
2275 ULONG Index
, SubKeyCount
;
2278 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
2286 /* Get the source cell node */
2287 SrcNode
= HvGetCell(SourceHive
, SrcKeyCell
);
2290 ASSERT(SrcNode
->Signature
== CM_KEY_NODE_SIGNATURE
);
2292 /* Create a simple copy of the source key */
2293 NewKeyCell
= CmpCopyCell(SourceHive
,
2297 if (NewKeyCell
== HCELL_NIL
)
2299 /* Not enough storage space */
2300 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2304 /* Get the destination cell node */
2305 DestNode
= HvGetCell(DestinationHive
, NewKeyCell
);
2307 /* Set the parent */
2308 DestNode
->Parent
= Parent
;
2310 // TODO: These should also be copied!
2311 DestNode
->Security
= DestNode
->Class
= HCELL_NIL
;
2313 /* Copy the value list */
2314 Status
= CmpCopyKeyValueList(SourceHive
,
2315 &SrcNode
->ValueList
,
2317 &DestNode
->ValueList
,
2319 if (!NT_SUCCESS(Status
)) goto Cleanup
;
2321 /* Clear the invalid subkey index */
2322 DestNode
->SubKeyCounts
[Stable
] = DestNode
->SubKeyCounts
[Volatile
] = 0;
2323 DestNode
->SubKeyLists
[Stable
] = DestNode
->SubKeyLists
[Volatile
] = HCELL_NIL
;
2325 /* Calculate the total number of subkeys */
2326 SubKeyCount
= SrcNode
->SubKeyCounts
[Stable
] + SrcNode
->SubKeyCounts
[Volatile
];
2328 /* Loop through all the subkeys */
2329 for (Index
= 0; Index
< SubKeyCount
; Index
++)
2331 /* Get the subkey */
2332 SubKey
= CmpFindSubKeyByNumber(SourceHive
, SrcNode
, Index
);
2333 ASSERT(SubKey
!= HCELL_NIL
);
2335 /* Call the function recursively for the subkey */
2336 Status
= CmpDeepCopyKeyInternal(SourceHive
,
2342 if (!NT_SUCCESS(Status
)) goto Cleanup
;
2344 /* Add the copy of the subkey to the new key */
2345 if (!CmpAddSubKey(DestinationHive
,
2349 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2354 /* Set the cell index if requested and return success */
2355 if (DestKeyCell
) *DestKeyCell
= NewKeyCell
;
2356 Status
= STATUS_SUCCESS
;
2360 /* Release the cells */
2361 if (SrcNode
) HvReleaseCell(SourceHive
, SrcKeyCell
);
2362 if (DestNode
) HvReleaseCell(DestinationHive
, NewKeyCell
);
2369 CmpDeepCopyKey(IN PHHIVE SourceHive
,
2370 IN HCELL_INDEX SrcKeyCell
,
2371 IN PHHIVE DestinationHive
,
2372 IN HSTORAGE_TYPE StorageType
,
2373 OUT PHCELL_INDEX DestKeyCell OPTIONAL
)
2375 /* Call the internal function */
2376 return CmpDeepCopyKeyInternal(SourceHive
,
2386 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
2387 IN HANDLE FileHandle
,
2390 NTSTATUS Status
= STATUS_SUCCESS
;
2391 PCMHIVE KeyHive
= NULL
;
2394 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb
, FileHandle
, Flags
);
2396 /* Lock the registry and KCB */
2398 CmpAcquireKcbLockShared(Kcb
);
2402 /* The source key has been deleted, do nothing */
2403 Status
= STATUS_KEY_DELETED
;
2407 if (Kcb
->KeyHive
== &CmiVolatileHive
->Hive
)
2409 /* Keys that are directly in the master hive can't be saved */
2410 Status
= STATUS_ACCESS_DENIED
;
2414 /* Create a new hive that will hold the key */
2415 Status
= CmpInitializeHive(&KeyHive
,
2425 if (!NT_SUCCESS(Status
)) goto Cleanup
;
2427 /* Copy the key recursively into the new hive */
2428 Status
= CmpDeepCopyKey(Kcb
->KeyHive
,
2432 &KeyHive
->Hive
.BaseBlock
->RootCell
);
2433 if (!NT_SUCCESS(Status
)) goto Cleanup
;
2435 /* Set the primary handle of the hive */
2436 KeyHive
->FileHandles
[HFILE_TYPE_PRIMARY
] = FileHandle
;
2438 /* Dump the hive into the file */
2439 HvWriteHive(&KeyHive
->Hive
);
2444 if (KeyHive
) CmpDestroyHive(KeyHive
);
2446 /* Release the locks */
2447 CmpReleaseKcbLock(Kcb
);
2448 CmpUnlockRegistry();
2455 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb
,
2456 IN PCM_KEY_CONTROL_BLOCK LowKcb
,
2457 IN HANDLE FileHandle
)
2459 PCMHIVE KeyHive
= NULL
;
2460 NTSTATUS Status
= STATUS_SUCCESS
;
2464 DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb
, LowKcb
, FileHandle
);
2466 /* Lock the registry and the KCBs */
2468 CmpAcquireKcbLockShared(HighKcb
);
2469 CmpAcquireKcbLockShared(LowKcb
);
2471 if (LowKcb
->Delete
|| HighKcb
->Delete
)
2473 /* The source key has been deleted, do nothing */
2474 Status
= STATUS_KEY_DELETED
;
2478 /* Create a new hive that will hold the key */
2479 Status
= CmpInitializeHive(&KeyHive
,
2489 if (!NT_SUCCESS(Status
))
2492 /* Copy the low precedence key recursively into the new hive */
2493 Status
= CmpDeepCopyKey(LowKcb
->KeyHive
,
2497 &KeyHive
->Hive
.BaseBlock
->RootCell
);
2498 if (!NT_SUCCESS(Status
))
2501 /* Copy the high precedence key recursively into the new hive */
2502 Status
= CmpDeepCopyKey(HighKcb
->KeyHive
,
2506 &KeyHive
->Hive
.BaseBlock
->RootCell
);
2507 if (!NT_SUCCESS(Status
))
2510 /* Set the primary handle of the hive */
2511 KeyHive
->FileHandles
[HFILE_TYPE_PRIMARY
] = FileHandle
;
2513 /* Dump the hive into the file */
2514 HvWriteHive(&KeyHive
->Hive
);
2519 CmpDestroyHive(KeyHive
);
2521 /* Release the locks */
2522 CmpReleaseKcbLock(LowKcb
);
2523 CmpReleaseKcbLock(HighKcb
);
2524 CmpUnlockRegistry();