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)
10 /* INCLUDES ******************************************************************/
16 /* FUNCTIONS *****************************************************************/
20 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle
,
21 IN POBJECT_ATTRIBUTES SourceFile
,
27 BOOLEAN Loaded
= FALSE
;
31 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
33 /* Reference the handle */
34 Status
= ObReferenceObjectByHandle(KeyHandle
,
40 if (!NT_SUCCESS(Status
)) return Loaded
;
42 /* Don't touch deleted KCBs */
43 if (KeyBody
->KeyControlBlock
->Delete
) return Loaded
;
45 Hive
= CONTAINING_RECORD(KeyBody
->KeyControlBlock
->KeyHive
, CMHIVE
, Hive
);
47 /* Must be the root key */
48 if (!(KeyBody
->KeyControlBlock
->Flags
& KEY_HIVE_ENTRY
) ||
49 !(Hive
->FileUserName
.Buffer
))
52 ObDereferenceObject(KeyBody
);
56 /* Now compare the name of the file */
57 if (!RtlCompareUnicodeString(&Hive
->FileUserName
,
58 SourceFile
->ObjectName
,
65 /* If the hive is frozen, not sure what to do */
69 DPRINT1("ERROR: Hive is frozen\n");
74 /* Dereference and return result */
75 ObDereferenceObject(KeyBody
);
81 CmpDoFlushAll(IN BOOLEAN ForceFlush
)
83 PLIST_ENTRY NextEntry
;
86 BOOLEAN Result
= TRUE
;
88 /* Make sure that the registry isn't read-only now */
89 if (CmpNoWrite
) return TRUE
;
91 /* Otherwise, acquire the hive list lock and disable force flush */
92 CmpForceForceFlush
= FALSE
;
93 ExAcquirePushLockShared(&CmpHiveListHeadLock
);
95 /* Loop the hive list */
96 NextEntry
= CmpHiveListHead
.Flink
;
97 while (NextEntry
!= &CmpHiveListHead
)
100 Hive
= CONTAINING_RECORD(NextEntry
, CMHIVE
, HiveList
);
101 if (!(Hive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
))
103 /* Acquire the flusher lock */
104 CmpLockHiveFlusherExclusive(Hive
);
106 /* Check for illegal state */
107 if ((ForceFlush
) && (Hive
->UseCount
))
109 /* Registry needs to be locked down */
110 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
111 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
115 /* Only sync if we are forced to or if it won't cause a hive shrink */
116 if ((ForceFlush
) || (!HvHiveWillShrink(&Hive
->Hive
)))
119 Status
= HvSyncHive(&Hive
->Hive
);
121 /* If something failed - set the flag and continue looping */
122 if (!NT_SUCCESS(Status
)) Result
= FALSE
;
126 /* We won't flush if the hive might shrink */
128 CmpForceForceFlush
= TRUE
;
131 /* Release the flusher lock */
132 CmpUnlockHiveFlusher(Hive
);
135 /* Try the next entry */
136 NextEntry
= NextEntry
->Flink
;
139 /* Release lock and return */
140 ExReleasePushLock(&CmpHiveListHeadLock
);
146 CmpSetValueKeyNew(IN PHHIVE Hive
,
147 IN PCM_KEY_NODE Parent
,
148 IN PUNICODE_STRING ValueName
,
153 IN ULONG StorageType
,
157 HCELL_INDEX ValueCell
;
160 /* Check if we already have a value list */
161 if (Parent
->ValueList
.Count
)
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
))
167 /* Fail if we're out of space for log changes */
168 return STATUS_NO_LOG_SPACE
;
172 /* Allocate a value cell */
173 ValueCell
= HvAllocateCell(Hive
,
174 FIELD_OFFSET(CM_KEY_VALUE
, Name
) +
175 CmpNameSize(Hive
, ValueName
),
178 if (ValueCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
180 /* Get the actual data for it */
181 CellData
= HvGetCell(Hive
, ValueCell
);
182 if (!CellData
) ASSERT(FALSE
);
184 /* Now we can release it, make sure it's also dirty */
185 HvReleaseCell(Hive
, ValueCell
);
186 ASSERT(HvIsCellDirty(Hive
, ValueCell
));
188 /* Set it up and copy the name */
189 CellData
->u
.KeyValue
.Signature
= CM_KEY_VALUE_SIGNATURE
;
192 /* This can crash since the name is coming from user-mode */
193 CellData
->u
.KeyValue
.NameLength
= CmpCopyName(Hive
,
194 CellData
->u
.KeyValue
.Name
,
197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
200 DPRINT1("Invalid user data!\n");
201 HvFreeCell(Hive
, ValueCell
);
202 _SEH2_YIELD(return _SEH2_GetExceptionCode());
206 /* Check for compressed name */
207 if (CellData
->u
.KeyValue
.NameLength
< ValueName
->Length
)
209 /* This is a compressed name */
210 CellData
->u
.KeyValue
.Flags
= VALUE_COMP_NAME
;
214 /* No flags to set */
215 CellData
->u
.KeyValue
.Flags
= 0;
218 /* Check if this is a normal key */
219 if (DataSize
> CM_KEY_VALUE_SMALL
)
221 /* Build a data cell for it */
222 Status
= CmpSetValueDataNew(Hive
,
227 &CellData
->u
.KeyValue
.Data
);
228 if (!NT_SUCCESS(Status
))
230 /* We failed, free the cell */
231 HvFreeCell(Hive
, ValueCell
);
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
));
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
;
246 /* Set the type now */
247 CellData
->u
.KeyValue
.Type
= Type
;
249 /* Add this value cell to the child list */
250 Status
= CmpAddValueToList(Hive
,
256 /* If we failed, free the entire cell, including the data */
257 if (!NT_SUCCESS(Status
))
259 /* Overwrite the status with a known one */
260 CmpFreeValue(Hive
, ValueCell
);
261 Status
= STATUS_INSUFFICIENT_RESOURCES
;
270 CmpSetValueKeyExisting(IN PHHIVE Hive
,
271 IN HCELL_INDEX OldChild
,
272 IN PCM_KEY_VALUE Value
,
276 IN ULONG StorageType
,
279 HCELL_INDEX DataCell
, NewCell
;
282 BOOLEAN WasSmall
, IsSmall
;
284 /* Registry writes must be blocked */
285 CMP_ASSERT_FLUSH_LOCK(Hive
);
287 /* Mark the old child cell dirty */
288 if (!HvMarkCellDirty(Hive
, OldChild
, FALSE
)) return STATUS_NO_LOG_SPACE
;
290 /* See if this is a small or normal key */
291 WasSmall
= CmpIsKeyValueSmall(&Length
, Value
->DataLength
);
293 /* See if our new data can fit in a small key */
294 IsSmall
= (DataSize
<= CM_KEY_VALUE_SMALL
) ? TRUE
: FALSE
;
296 /* Big keys are unsupported */
297 ASSERT_VALUE_BIG(Hive
, Length
);
298 ASSERT_VALUE_BIG(Hive
, DataSize
);
300 /* Mark the old value dirty */
301 if (!CmpMarkValueDataDirty(Hive
, Value
)) return STATUS_NO_LOG_SPACE
;
303 /* Check if we have a small key */
306 /* Check if we had a normal key with some data in it */
307 if (!(WasSmall
) && (Length
> 0))
309 /* Free the previous data */
310 CmpFreeValueData(Hive
, Value
->Data
, Length
);
313 /* Write our data directly */
314 Value
->DataLength
= DataSize
+ CM_KEY_VALUE_SPECIAL_SIZE
;
315 Value
->Data
= TempData
;
317 return STATUS_SUCCESS
;
320 /* We have a normal key. Was the old cell also normal and had data? */
321 if (!(WasSmall
) && (Length
> 0))
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
;
329 /* Immediately release the cell */
330 HvReleaseCell(Hive
, DataCell
);
332 /* Make sure that the data cell actually has a size */
333 ASSERT(HvGetCellSize(Hive
, CellData
) > 0);
335 /* Check if the previous data cell could fit our new data */
336 if (DataSize
<= (ULONG
)(HvGetCellSize(Hive
, CellData
)))
343 /* Otherwise, re-allocate the current data cell */
344 NewCell
= HvReallocateCell(Hive
, DataCell
, DataSize
);
345 if (NewCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
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
;
355 /* Now get the actual data for our data cell */
356 CellData
= HvGetCell(Hive
, NewCell
);
357 if (!CellData
) ASSERT(FALSE
);
359 /* Release it immediately */
360 HvReleaseCell(Hive
, NewCell
);
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
;
369 ASSERT(HvIsCellDirty(Hive
, NewCell
));
370 return STATUS_SUCCESS
;
375 CmpQueryKeyData(IN PHHIVE Hive
,
376 IN PCM_KEY_NODE Node
,
377 IN KEY_INFORMATION_CLASS KeyInformationClass
,
378 IN OUT PVOID KeyInformation
,
380 IN OUT PULONG ResultLength
)
383 ULONG Size
, SizeLeft
, MinimumSize
;
384 PKEY_INFORMATION Info
= (PKEY_INFORMATION
)KeyInformation
;
387 /* Check if the value is compressed */
388 if (Node
->Flags
& KEY_COMP_NAME
)
390 /* Get the compressed name size */
391 NameLength
= CmpCompressedNameSize(Node
->Name
, Node
->NameLength
);
395 /* Get the real size */
396 NameLength
= Node
->NameLength
;
399 /* Check what kind of information is being requested */
400 switch (KeyInformationClass
)
402 /* Basic information */
403 case KeyBasicInformation
:
405 /* This is the size we need */
406 Size
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
) + NameLength
;
408 /* And this is the minimum we can work with */
409 MinimumSize
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
);
411 /* Let the caller know and assume success */
412 *ResultLength
= Size
;
413 Status
= STATUS_SUCCESS
;
415 /* Check if the bufer we got is too small */
416 if (Length
< MinimumSize
)
418 /* Let the caller know and fail */
419 Status
= STATUS_BUFFER_TOO_SMALL
;
423 /* Copy the basic information */
424 Info
->KeyBasicInformation
.LastWriteTime
= Node
->LastWriteTime
;
425 Info
->KeyBasicInformation
.TitleIndex
= 0;
426 Info
->KeyBasicInformation
.NameLength
= NameLength
;
428 /* Only the name is left */
429 SizeLeft
= Length
- MinimumSize
;
432 /* Check if we don't have enough space for the name */
435 /* Truncate the name we'll return, and tell the caller */
437 Status
= STATUS_BUFFER_OVERFLOW
;
440 /* Check if this is a compressed key */
441 if (Node
->Flags
& KEY_COMP_NAME
)
443 /* Copy the compressed name */
444 CmpCopyCompressedName(Info
->KeyBasicInformation
.Name
,
451 /* Otherwise, copy the raw name */
452 RtlCopyMemory(Info
->KeyBasicInformation
.Name
,
458 /* Node information */
459 case KeyNodeInformation
:
461 /* Calculate the size we need */
462 Size
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) +
466 /* And the minimum size we can support */
467 MinimumSize
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
);
469 /* Return the size to the caller and assume succes */
470 *ResultLength
= Size
;
471 Status
= STATUS_SUCCESS
;
473 /* Check if the caller's buffer is too small */
474 if (Length
< MinimumSize
)
476 /* Let them know, and fail */
477 Status
= STATUS_BUFFER_TOO_SMALL
;
481 /* Copy the basic information */
482 Info
->KeyNodeInformation
.LastWriteTime
= Node
->LastWriteTime
;
483 Info
->KeyNodeInformation
.TitleIndex
= 0;
484 Info
->KeyNodeInformation
.ClassLength
= Node
->ClassLength
;
485 Info
->KeyNodeInformation
.NameLength
= NameLength
;
487 /* Now the name is left */
488 SizeLeft
= Length
- MinimumSize
;
491 /* Check if the name can fit entirely */
494 /* It can't, we'll have to truncate. Tell the caller */
496 Status
= STATUS_BUFFER_OVERFLOW
;
499 /* Check if the key node name is compressed */
500 if (Node
->Flags
& KEY_COMP_NAME
)
502 /* Copy the compressed name */
503 CmpCopyCompressedName(Info
->KeyNodeInformation
.Name
,
510 /* It isn't, so copy the raw name */
511 RtlCopyMemory(Info
->KeyNodeInformation
.Name
,
516 /* Check if the node has a class */
517 if (Node
->ClassLength
> 0)
519 /* It does. We don't support these yet */
520 ASSERTMSG("Classes not supported\n", FALSE
);
524 /* It doesn't, so set offset to -1, not 0! */
525 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
529 /* Full information requsted */
530 case KeyFullInformation
:
532 /* This is the size we need */
533 Size
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
) +
536 /* This is what we can work with */
537 MinimumSize
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
539 /* Return it to caller and assume success */
540 *ResultLength
= Size
;
541 Status
= STATUS_SUCCESS
;
543 /* Check if the caller's buffer is to small */
544 if (Length
< MinimumSize
)
546 /* Let them know and fail */
547 Status
= STATUS_BUFFER_TOO_SMALL
;
551 /* Now copy all the basic information */
552 Info
->KeyFullInformation
.LastWriteTime
= Node
->LastWriteTime
;
553 Info
->KeyFullInformation
.TitleIndex
= 0;
554 Info
->KeyFullInformation
.ClassLength
= Node
->ClassLength
;
555 Info
->KeyFullInformation
.SubKeys
= Node
->SubKeyCounts
[Stable
] +
556 Node
->SubKeyCounts
[Volatile
];
557 Info
->KeyFullInformation
.Values
= Node
->ValueList
.Count
;
558 Info
->KeyFullInformation
.MaxNameLen
= Node
->MaxNameLen
;
559 Info
->KeyFullInformation
.MaxClassLen
= Node
->MaxClassLen
;
560 Info
->KeyFullInformation
.MaxValueNameLen
= Node
->MaxValueNameLen
;
561 Info
->KeyFullInformation
.MaxValueDataLen
= Node
->MaxValueDataLen
;
563 /* Check if we have a class */
564 if (Node
->ClassLength
> 0)
566 /* We do, but we currently don't support this */
567 ASSERTMSG("Classes not supported\n", FALSE
);
571 /* We don't have a class, so set offset to -1, not 0! */
572 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
576 /* Any other class that got sent here is invalid! */
579 /* Set failure code */
580 Status
= STATUS_INVALID_PARAMETER
;
590 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
591 IN PUNICODE_STRING ValueName
,
598 PCM_KEY_VALUE Value
= NULL
;
599 HCELL_INDEX CurrentChild
, Cell
;
601 BOOLEAN Found
, Result
;
602 ULONG Count
, ChildIndex
, SmallData
, Storage
;
603 VALUE_SEARCH_RETURN_TYPE SearchResult
;
604 BOOLEAN FirstTry
= TRUE
, FlusherLocked
= FALSE
;
605 HCELL_INDEX ParentCell
= HCELL_NIL
, ChildCell
= HCELL_NIL
;
607 /* Acquire hive and KCB lock */
609 CmpAcquireKcbLockShared(Kcb
);
612 ASSERT(sizeof(ULONG
) == CM_KEY_VALUE_SMALL
);
614 /* Don't touch deleted KCBs */
619 Status
= STATUS_KEY_DELETED
;
623 /* Don't let anyone mess with symlinks */
624 if ((Kcb
->Flags
& KEY_SYM_LINK
) &&
625 ((Type
!= REG_LINK
) ||
627 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName
, ValueName
, TRUE
))))
629 /* Invalid modification of a symlink key */
630 Status
= STATUS_ACCESS_DENIED
;
634 /* Check if this is the first attempt */
637 /* Search for the value in the cache */
638 SearchResult
= CmpCompareNewValueDataAgainstKCBCache(Kcb
,
643 if (SearchResult
== SearchNeedExclusiveLock
)
645 /* Try again with the exclusive lock */
646 CmpConvertKcbSharedToExclusive(Kcb
);
649 else if (SearchResult
== SearchSuccess
)
651 /* We don't actually need to do anything! */
652 Status
= STATUS_SUCCESS
;
656 /* We need the exclusive KCB lock now */
657 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
658 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
660 /* Acquire exclusive lock */
661 CmpConvertKcbSharedToExclusive(Kcb
);
664 /* Cache lookup failed, so don't try it next time */
667 /* Now grab the flush lock since the key will be modified */
668 ASSERT(FlusherLocked
== FALSE
);
669 CmpLockHiveFlusherShared((PCMHIVE
)Kcb
->KeyHive
);
670 FlusherLocked
= TRUE
;
675 /* Get pointer to key cell */
680 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
684 /* Prepare to scan the key node */
685 Count
= Parent
->ValueList
.Count
;
689 /* Try to find the existing name */
690 Result
= CmpFindNameInList(Hive
,
698 Status
= STATUS_INSUFFICIENT_RESOURCES
;
702 /* Check if we found something */
703 if (CurrentChild
!= HCELL_NIL
)
705 /* Release existing child */
706 if (ChildCell
!= HCELL_NIL
)
708 HvReleaseCell(Hive
, ChildCell
);
709 ChildCell
= HCELL_NIL
;
713 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
, CurrentChild
);
717 Status
= STATUS_INSUFFICIENT_RESOURCES
;
721 /* Remember that we found it */
722 ChildCell
= CurrentChild
;
728 /* No child list, we'll need to add it */
733 /* Should only get here on the second pass */
734 ASSERT(FirstTry
== FALSE
);
736 /* The KCB must be locked exclusive at this point */
737 CMP_ASSERT_KCB_LOCK(Kcb
);
739 /* Mark the cell dirty */
740 if (!HvMarkCellDirty(Hive
, Cell
, FALSE
))
742 /* Not enough log space, fail */
743 Status
= STATUS_NO_LOG_SPACE
;
747 /* Get the storage type */
748 Storage
= HvGetCellType(Cell
);
750 /* Check if this is small data */
752 if ((DataLength
<= CM_KEY_VALUE_SMALL
) && (DataLength
> 0))
754 /* Need SEH because user data may be invalid */
758 RtlCopyMemory(&SmallData
, Data
, DataLength
);
760 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
762 /* Return failure code */
763 Status
= _SEH2_GetExceptionCode();
764 _SEH2_YIELD(goto Quickie
);
769 /* Check if we didn't find a matching key */
772 /* Call the internal routine */
773 Status
= CmpSetValueKeyNew(Hive
,
785 /* Call the internal routine */
786 Status
= CmpSetValueKeyExisting(Hive
,
796 /* Check for success */
797 if (NT_SUCCESS(Status
))
799 /* Check if the maximum value name length changed */
800 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
801 if (Parent
->MaxValueNameLen
< ValueName
->Length
)
803 /* Set the new values */
804 Parent
->MaxValueNameLen
= ValueName
->Length
;
805 Kcb
->KcbMaxValueNameLen
= ValueName
->Length
;
808 /* Check if the maximum data length changed */
809 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
810 if (Parent
->MaxValueDataLen
< DataLength
)
813 Parent
->MaxValueDataLen
= DataLength
;
814 Kcb
->KcbMaxValueDataLen
= Parent
->MaxValueDataLen
;
817 /* Save the write time */
818 KeQuerySystemTime(&Parent
->LastWriteTime
);
819 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
821 /* Check if the cell is cached */
822 if ((Found
) && (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)))
824 /* Shouldn't happen */
829 /* Cleanup the value cache */
830 CmpCleanUpKcbValueCache(Kcb
);
833 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
834 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
836 /* Set the value cache */
837 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
838 Kcb
->ValueCache
.ValueList
= Parent
->ValueList
.List
;
841 /* Notify registered callbacks */
845 REG_NOTIFY_CHANGE_LAST_SET
);
848 /* Release the cells */
850 if ((ParentCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ParentCell
);
851 if ((ChildCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ChildCell
);
853 /* Release the locks */
854 if (FlusherLocked
) CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
855 CmpReleaseKcbLock(Kcb
);
862 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
863 IN UNICODE_STRING ValueName
)
865 NTSTATUS Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
868 HCELL_INDEX ChildCell
, Cell
;
869 PCHILD_LIST ChildList
;
870 PCM_KEY_VALUE Value
= NULL
;
874 /* Acquire hive lock */
877 /* Lock KCB exclusively */
878 CmpAcquireKcbLockExclusive(Kcb
);
880 /* Don't touch deleted keys */
883 /* Undo everything */
884 CmpReleaseKcbLock(Kcb
);
886 return STATUS_KEY_DELETED
;
889 /* Get the hive and the cell index */
894 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
896 /* Get the parent key node */
897 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
900 /* Get the value list and check if it has any entries */
901 ChildList
= &Parent
->ValueList
;
902 if (ChildList
->Count
)
904 /* Try to find this value */
905 Result
= CmpFindNameInList(Hive
,
913 Status
= STATUS_INSUFFICIENT_RESOURCES
;
917 /* Value not found, return error */
918 if (ChildCell
== HCELL_NIL
) goto Quickie
;
920 /* We found the value, mark all relevant cells dirty */
921 if (!((HvMarkCellDirty(Hive
, Cell
, FALSE
)) &&
922 (HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
)) &&
923 (HvMarkCellDirty(Hive
, ChildCell
, FALSE
))))
925 /* Not enough log space, fail */
926 Status
= STATUS_NO_LOG_SPACE
;
930 /* Get the key value */
931 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
,ChildCell
);
934 /* Mark it and all related data as dirty */
935 if (!CmpMarkValueDataDirty(Hive
, Value
))
937 /* Not enough log space, fail */
938 Status
= STATUS_NO_LOG_SPACE
;
943 ASSERT(HvIsCellDirty(Hive
, Parent
->ValueList
.List
));
944 ASSERT(HvIsCellDirty(Hive
, ChildCell
));
946 /* Remove the value from the child list */
947 Status
= CmpRemoveValueFromList(Hive
, ChildIndex
, ChildList
);
948 if (!NT_SUCCESS(Status
))
950 /* Set known error */
951 Status
= STATUS_INSUFFICIENT_RESOURCES
;
955 /* Remove the value and its data itself */
956 if (!CmpFreeValue(Hive
, ChildCell
))
958 /* Failed to free the value, fail */
959 Status
= STATUS_INSUFFICIENT_RESOURCES
;
963 /* Set the last write time */
964 KeQuerySystemTime(&Parent
->LastWriteTime
);
965 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
968 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
969 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
970 ASSERT(HvIsCellDirty(Hive
, Cell
));
972 /* Check if the value list is empty now */
973 if (!Parent
->ValueList
.Count
)
975 /* Then clear key node data */
976 Parent
->MaxValueNameLen
= 0;
977 Parent
->MaxValueDataLen
= 0;
978 Kcb
->KcbMaxValueNameLen
= 0;
979 Kcb
->KcbMaxValueDataLen
= 0;
982 /* Cleanup the value cache */
983 CmpCleanUpKcbValueCache(Kcb
);
986 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
987 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
989 /* Set the value cache */
990 Kcb
->ValueCache
.Count
= ChildList
->Count
;
991 Kcb
->ValueCache
.ValueList
= ChildList
->List
;
993 /* Notify registered callbacks */
994 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_LAST_SET
);
996 /* Change default Status to success */
997 Status
= STATUS_SUCCESS
;
1001 /* Release the parent cell, if any */
1002 if (Parent
) HvReleaseCell(Hive
, Cell
);
1004 /* Check if we had a value */
1007 /* Release the child cell */
1008 ASSERT(ChildCell
!= HCELL_NIL
);
1009 HvReleaseCell(Hive
, ChildCell
);
1013 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1014 CmpReleaseKcbLock(Kcb
);
1015 CmpUnlockRegistry();
1021 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1022 IN UNICODE_STRING ValueName
,
1023 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1024 IN PVOID KeyValueInformation
,
1026 IN PULONG ResultLength
)
1029 PCM_KEY_VALUE ValueData
;
1031 BOOLEAN ValueCached
= FALSE
;
1032 PCM_CACHED_VALUE
*CachedValue
;
1033 HCELL_INDEX CellToRelease
;
1034 VALUE_SEARCH_RETURN_TYPE Result
;
1038 /* Acquire hive lock */
1041 /* Lock the KCB shared */
1042 CmpAcquireKcbLockShared(Kcb
);
1044 /* Don't touch deleted keys */
1048 /* Undo everything */
1049 CmpReleaseKcbLock(Kcb
);
1050 CmpUnlockRegistry();
1051 return STATUS_KEY_DELETED
;
1054 /* We don't deal with this yet */
1055 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1057 /* Shouldn't happen */
1062 Hive
= Kcb
->KeyHive
;
1064 /* Find the key value */
1065 Result
= CmpFindValueByNameFromCache(Kcb
,
1072 if (Result
== SearchNeedExclusiveLock
)
1074 /* Check if we need an exclusive lock */
1075 ASSERT(CellToRelease
== HCELL_NIL
);
1076 ASSERT(ValueData
== NULL
);
1078 /* Try with exclusive KCB lock */
1079 CmpConvertKcbSharedToExclusive(Kcb
);
1083 if (Result
== SearchSuccess
)
1086 ASSERT(ValueData
!= NULL
);
1088 /* User data, protect against exceptions */
1091 /* Query the information requested */
1092 Result
= CmpQueryKeyValueData(Kcb
,
1096 KeyValueInformationClass
,
1097 KeyValueInformation
,
1101 if (Result
== SearchNeedExclusiveLock
)
1103 /* Release the value cell */
1104 if (CellToRelease
!= HCELL_NIL
)
1106 HvReleaseCell(Hive
, CellToRelease
);
1107 CellToRelease
= HCELL_NIL
;
1110 /* Try with exclusive KCB lock */
1111 CmpConvertKcbSharedToExclusive(Kcb
);
1112 _SEH2_YIELD(goto DoAgain
);
1115 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1117 Status
= _SEH2_GetExceptionCode();
1123 /* Failed to find the value */
1124 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1127 /* If we have a cell to release, do so */
1128 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1131 CmpReleaseKcbLock(Kcb
);
1132 CmpUnlockRegistry();
1138 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1140 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1141 IN PVOID KeyValueInformation
,
1143 IN PULONG ResultLength
)
1147 PCM_KEY_NODE Parent
;
1148 HCELL_INDEX CellToRelease
= HCELL_NIL
, CellToRelease2
= HCELL_NIL
;
1149 VALUE_SEARCH_RETURN_TYPE Result
;
1150 BOOLEAN IndexIsCached
, ValueIsCached
= FALSE
;
1151 PCELL_DATA CellData
;
1152 PCM_CACHED_VALUE
*CachedValue
;
1153 PCM_KEY_VALUE ValueData
= NULL
;
1156 /* Acquire hive lock */
1159 /* Lock the KCB shared */
1160 CmpAcquireKcbLockShared(Kcb
);
1162 /* Don't touch deleted keys */
1166 /* Undo everything */
1167 CmpReleaseKcbLock(Kcb
);
1168 CmpUnlockRegistry();
1169 return STATUS_KEY_DELETED
;
1172 /* Get the hive and parent */
1173 Hive
= Kcb
->KeyHive
;
1174 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1177 /* FIXME: Lack of cache? */
1178 if (Kcb
->ValueCache
.Count
!= Parent
->ValueList
.Count
)
1180 DPRINT1("HACK: Overriding value cache count\n");
1181 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
1184 /* Make sure the index is valid */
1185 if (Index
>= Kcb
->ValueCache
.Count
)
1187 /* Release the cell and fail */
1188 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1189 Status
= STATUS_NO_MORE_ENTRIES
;
1193 /* We don't deal with this yet */
1194 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1196 /* Shouldn't happen */
1200 /* Find the value list */
1201 Result
= CmpGetValueListFromCache(Kcb
,
1205 if (Result
== SearchNeedExclusiveLock
)
1207 /* Check if we need an exclusive lock */
1208 ASSERT(CellToRelease
== HCELL_NIL
);
1209 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1211 /* Try with exclusive KCB lock */
1212 CmpConvertKcbSharedToExclusive(Kcb
);
1215 else if (Result
!= SearchSuccess
)
1218 ASSERT(CellData
== NULL
);
1220 /* Release the cell and fail */
1221 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1225 /* Now get the key value */
1226 Result
= CmpGetValueKeyFromCache(Kcb
,
1234 if (Result
== SearchNeedExclusiveLock
)
1237 ASSERT(CellToRelease2
== HCELL_NIL
);
1240 HvReleaseCell(Hive
, CellToRelease
);
1241 CellToRelease
= HCELL_NIL
;
1243 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1245 /* Try with exclusive KCB lock */
1246 CmpConvertKcbSharedToExclusive(Kcb
);
1249 else if (Result
!= SearchSuccess
)
1252 ASSERT(ValueData
== NULL
);
1254 /* Release the cells and fail */
1255 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1259 /* User data, need SEH */
1262 /* Query the information requested */
1263 Result
= CmpQueryKeyValueData(Kcb
,
1267 KeyValueInformationClass
,
1268 KeyValueInformation
,
1272 if (Result
== SearchNeedExclusiveLock
)
1275 if (CellToRelease2
) HvReleaseCell(Hive
, CellToRelease2
);
1276 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1277 if (CellToRelease
) HvReleaseCell(Hive
, CellToRelease
);
1279 /* Try with exclusive KCB lock */
1280 CmpConvertKcbSharedToExclusive(Kcb
);
1281 _SEH2_YIELD(goto DoAgain
);
1284 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1286 /* Get exception code */
1287 Status
= _SEH2_GetExceptionCode();
1292 /* If we have a cell to release, do so */
1293 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1295 /* Release the parent cell */
1296 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1298 /* If we have a cell to release, do so */
1299 if (CellToRelease2
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease2
);
1302 CmpReleaseKcbLock(Kcb
);
1303 CmpUnlockRegistry();
1309 CmpQueryKeyDataFromCache(
1310 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1311 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo
,
1313 _Out_ PULONG ResultLength
)
1317 HCELL_INDEX KeyCell
;
1321 /* Get the hive and cell index */
1322 KeyHive
= Kcb
->KeyHash
.KeyHive
;
1323 KeyCell
= Kcb
->KeyHash
.KeyCell
;
1326 /* Get the cell node */
1327 Node
= HvGetCell(KeyHive
, KeyCell
);
1331 ASSERT(Node
->ValueList
.Count
== Kcb
->ValueCache
.Count
);
1333 if (!(Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
))
1335 SubKeyCount
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1336 if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1338 ASSERT(SubKeyCount
== 0);
1340 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1342 ASSERT(SubKeyCount
== 1);
1344 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1346 ASSERT(SubKeyCount
== Kcb
->IndexHint
->Count
);
1350 ASSERT(SubKeyCount
== Kcb
->SubKeyCount
);
1354 ASSERT(Node
->LastWriteTime
.QuadPart
== Kcb
->KcbLastWriteTime
.QuadPart
);
1355 ASSERT(Node
->MaxNameLen
== Kcb
->KcbMaxNameLen
);
1356 ASSERT(Node
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
1357 ASSERT(Node
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
1359 /* Release the cell */
1360 HvReleaseCell(KeyHive
, KeyCell
);
1364 /* Make sure we have a name block */
1365 if (Kcb
->NameBlock
== NULL
)
1367 return STATUS_INSUFFICIENT_RESOURCES
;
1370 /* Check for compressed name */
1371 if (Kcb
->NameBlock
->Compressed
)
1373 /* Calculate the name size */
1374 NameLength
= CmpCompressedNameSize(Kcb
->NameBlock
->NameHash
.Name
,
1375 Kcb
->NameBlock
->NameHash
.NameLength
);
1379 /* Use the stored name size */
1380 NameLength
= Kcb
->NameBlock
->NameHash
.NameLength
;
1383 /* Validate buffer length (we do not copy the name!) */
1384 *ResultLength
= sizeof(KeyCachedInfo
);
1385 if (Length
< *ResultLength
)
1387 return STATUS_BUFFER_TOO_SMALL
;
1390 /* Fill the structure */
1391 KeyCachedInfo
->LastWriteTime
= Kcb
->KcbLastWriteTime
;
1392 KeyCachedInfo
->TitleIndex
= 0;
1393 KeyCachedInfo
->NameLength
= NameLength
;
1394 KeyCachedInfo
->Values
= Kcb
->ValueCache
.Count
;
1395 KeyCachedInfo
->MaxNameLen
= Kcb
->KcbMaxNameLen
;
1396 KeyCachedInfo
->MaxValueNameLen
= Kcb
->KcbMaxValueNameLen
;
1397 KeyCachedInfo
->MaxValueDataLen
= Kcb
->KcbMaxValueDataLen
;
1399 /* Check the ExtFlags for what we have */
1400 if (Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
)
1402 /* Cache is not valid, do a full lookup */
1403 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb
);
1405 /* Get the cell node */
1406 Node
= HvGetCell(KeyHive
, KeyCell
);
1409 return STATUS_INSUFFICIENT_RESOURCES
;
1412 /* Calculate number of subkeys */
1413 KeyCachedInfo
->SubKeys
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1415 /* Release the cell */
1416 HvReleaseCell(KeyHive
, KeyCell
);
1418 else if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1420 /* There are no subkeys */
1421 KeyCachedInfo
->SubKeys
= 0;
1423 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1425 /* There is exactly one subley */
1426 KeyCachedInfo
->SubKeys
= 1;
1428 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1430 /* Get the number of subkeys from the subkey hint */
1431 KeyCachedInfo
->SubKeys
= Kcb
->IndexHint
->Count
;
1435 /* No subkey hint, use the key count field */
1436 KeyCachedInfo
->SubKeys
= Kcb
->SubKeyCount
;
1439 return STATUS_SUCCESS
;
1444 CmpQueryFlagsInformation(
1445 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1446 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo
,
1448 _In_ PULONG ResultLength
)
1450 /* Validate the buffer size */
1451 *ResultLength
= sizeof(*KeyFlagsInfo
);
1452 if (Length
< *ResultLength
)
1454 return STATUS_BUFFER_TOO_SMALL
;
1457 /* Copy the user flags */
1458 KeyFlagsInfo
->UserFlags
= Kcb
->KcbUserFlags
;
1460 return STATUS_SUCCESS
;
1465 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1466 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1467 IN PVOID KeyInformation
,
1469 IN PULONG ResultLength
)
1473 PCM_KEY_NODE Parent
;
1474 HV_TRACK_CELL_REF CellReferences
= {0};
1476 /* Acquire hive lock */
1479 /* Lock KCB shared */
1480 CmpAcquireKcbLockShared(Kcb
);
1482 /* Don't touch deleted keys */
1486 Status
= STATUS_KEY_DELETED
;
1490 /* Check what class we got */
1491 switch (KeyInformationClass
)
1493 /* Typical information */
1494 case KeyFullInformation
:
1495 case KeyBasicInformation
:
1496 case KeyNodeInformation
:
1498 /* Get the hive and parent */
1499 Hive
= Kcb
->KeyHive
;
1500 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1503 /* Track cell references */
1504 if (!HvTrackCellRef(&CellReferences
, Hive
, Kcb
->KeyCell
))
1506 /* Not enough memory to track references */
1507 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1511 /* Call the internal API */
1512 Status
= CmpQueryKeyData(Hive
,
1514 KeyInformationClass
,
1521 case KeyCachedInformation
:
1522 /* Call the internal API */
1523 Status
= CmpQueryKeyDataFromCache(Kcb
,
1529 case KeyFlagsInformation
:
1530 /* Call the internal API */
1531 Status
= CmpQueryFlagsInformation(Kcb
,
1537 /* Unsupported class for now */
1538 case KeyNameInformation
:
1540 /* Print message and fail */
1541 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1542 Status
= STATUS_NOT_IMPLEMENTED
;
1545 /* Illegal classes */
1548 /* Print message and fail */
1549 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1550 Status
= STATUS_INVALID_INFO_CLASS
;
1555 /* Release references */
1556 HvReleaseFreeCellRefArray(&CellReferences
);
1559 CmpReleaseKcbLock(Kcb
);
1560 CmpUnlockRegistry();
1566 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1568 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1569 IN PVOID KeyInformation
,
1571 IN PULONG ResultLength
)
1575 PCM_KEY_NODE Parent
, Child
;
1576 HCELL_INDEX ChildCell
;
1577 HV_TRACK_CELL_REF CellReferences
= {0};
1579 /* Acquire hive lock */
1582 /* Lock the KCB shared */
1583 CmpAcquireKcbLockShared(Kcb
);
1585 /* Don't touch deleted keys */
1588 /* Undo everything */
1589 Status
= STATUS_KEY_DELETED
;
1593 /* Get the hive and parent */
1594 Hive
= Kcb
->KeyHive
;
1595 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1598 /* Get the child cell */
1599 ChildCell
= CmpFindSubKeyByNumber(Hive
, Parent
, Index
);
1601 /* Release the parent cell */
1602 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1604 /* Check if we found the child */
1605 if (ChildCell
== HCELL_NIL
)
1607 /* We didn't, fail */
1608 Status
= STATUS_NO_MORE_ENTRIES
;
1612 /* Now get the actual child node */
1613 Child
= (PCM_KEY_NODE
)HvGetCell(Hive
, ChildCell
);
1616 /* Track references */
1617 if (!HvTrackCellRef(&CellReferences
, Hive
, ChildCell
))
1619 /* Can't allocate memory for tracking */
1620 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1624 /* Data can be user-mode, use SEH */
1627 /* Query the data requested */
1628 Status
= CmpQueryKeyData(Hive
,
1630 KeyInformationClass
,
1635 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1637 /* Fail with exception code */
1638 Status
= _SEH2_GetExceptionCode();
1639 _SEH2_YIELD(goto Quickie
);
1644 /* Release references */
1645 HvReleaseFreeCellRefArray(&CellReferences
);
1648 CmpReleaseKcbLock(Kcb
);
1649 CmpUnlockRegistry();
1655 CmDeleteKey(IN PCM_KEY_BODY KeyBody
)
1659 PCM_KEY_NODE Node
, Parent
;
1660 HCELL_INDEX Cell
, ParentCell
;
1661 PCM_KEY_CONTROL_BLOCK Kcb
;
1663 /* Acquire hive lock */
1667 Kcb
= KeyBody
->KeyControlBlock
;
1669 /* Don't allow deleting the root */
1670 if (!Kcb
->ParentKcb
)
1673 CmpUnlockRegistry();
1674 return STATUS_CANNOT_DELETE
;
1677 /* Lock parent and child */
1678 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1680 /* Check if we're already being deleted */
1683 /* Don't do it twice */
1684 Status
= STATUS_SUCCESS
;
1688 /* Get the hive and node */
1689 Hive
= Kcb
->KeyHive
;
1690 Cell
= Kcb
->KeyCell
;
1693 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
1695 /* Get the key node */
1696 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
1700 ASSERT(Node
->Flags
== Kcb
->Flags
);
1702 /* Check if we don't have any children */
1703 if (!(Node
->SubKeyCounts
[Stable
] + Node
->SubKeyCounts
[Volatile
]) &&
1704 !(Node
->Flags
& KEY_NO_DELETE
))
1706 /* Send notification to registered callbacks */
1707 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_NAME
);
1709 /* Get the parent and free the cell */
1710 ParentCell
= Node
->Parent
;
1711 Status
= CmpFreeKeyByCell(Hive
, Cell
, TRUE
);
1712 if (NT_SUCCESS(Status
))
1714 /* Flush any notifications */
1715 CmpFlushNotifiesOnKeyBodyList(Kcb
, FALSE
);
1717 /* Clean up information we have on the subkey */
1718 CmpCleanUpSubKeyInfo(Kcb
->ParentKcb
);
1720 /* Get the parent node */
1721 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, ParentCell
);
1724 /* Update the maximum name length */
1725 Kcb
->ParentKcb
->KcbMaxNameLen
= (USHORT
)Parent
->MaxNameLen
;
1727 /* Make sure we're dirty */
1728 ASSERT(HvIsCellDirty(Hive
, ParentCell
));
1730 /* Update the write time */
1731 KeQuerySystemTime(&Parent
->LastWriteTime
);
1732 Kcb
->ParentKcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
1734 /* Release the cell */
1735 HvReleaseCell(Hive
, ParentCell
);
1738 /* Set the KCB in delete mode and remove it */
1740 CmpRemoveKeyControlBlock(Kcb
);
1742 /* Clear the cell */
1743 Kcb
->KeyCell
= HCELL_NIL
;
1749 Status
= STATUS_CANNOT_DELETE
;
1752 /* Release the cell */
1753 HvReleaseCell(Hive
, Cell
);
1755 /* Release flush lock */
1756 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1758 /* Release the KCB locks */
1760 CmpReleaseTwoKcbLockByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1762 /* Release hive lock */
1763 CmpUnlockRegistry();
1769 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1770 IN BOOLEAN ExclusiveLock
)
1773 NTSTATUS Status
= STATUS_SUCCESS
;
1776 /* Ignore flushes until we're ready */
1777 if (CmpNoWrite
) return STATUS_SUCCESS
;
1780 Hive
= Kcb
->KeyHive
;
1781 CmHive
= (PCMHIVE
)Hive
;
1783 /* Check if this is the master hive */
1784 if (CmHive
== CmiVolatileHive
)
1786 /* Flush all the hives instead */
1787 CmpDoFlushAll(FALSE
);
1791 /* Don't touch the hive */
1792 CmpLockHiveFlusherExclusive(CmHive
);
1793 ASSERT(CmHive
->ViewLock
);
1794 KeAcquireGuardedMutex(CmHive
->ViewLock
);
1795 CmHive
->ViewLockOwner
= KeGetCurrentThread();
1797 /* Will the hive shrink? */
1798 if (HvHiveWillShrink(Hive
))
1800 /* I don't believe the current Hv does shrinking */
1805 /* Now we can release views */
1806 ASSERT(CmHive
->ViewLock
);
1807 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive
);
1808 ASSERT(KeGetCurrentThread() == CmHive
->ViewLockOwner
);
1809 KeReleaseGuardedMutex(CmHive
->ViewLock
);
1812 /* Flush only this hive */
1813 if (!HvSyncHive(Hive
))
1816 Status
= STATUS_REGISTRY_IO_FAILED
;
1819 /* Release the flush lock */
1820 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1823 /* Return the status */
1829 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey
,
1830 IN POBJECT_ATTRIBUTES SourceFile
,
1832 IN PCM_KEY_BODY KeyBody
)
1834 SECURITY_QUALITY_OF_SERVICE ServiceQos
;
1835 SECURITY_CLIENT_CONTEXT ClientSecurityContext
;
1837 BOOLEAN Allocate
= TRUE
;
1838 PCMHIVE CmHive
, LoadedHive
;
1840 CM_PARSE_CONTEXT ParseContext
;
1842 /* Check if we have a trust key */
1846 DPRINT1("Trusted classes not yet supported\n");
1847 return STATUS_NOT_IMPLEMENTED
;
1850 /* Build a service QoS for a security context */
1851 ServiceQos
.Length
= sizeof(SECURITY_QUALITY_OF_SERVICE
);
1852 ServiceQos
.ImpersonationLevel
= SecurityImpersonation
;
1853 ServiceQos
.ContextTrackingMode
= SECURITY_DYNAMIC_TRACKING
;
1854 ServiceQos
.EffectiveOnly
= TRUE
;
1855 Status
= SeCreateClientSecurity(PsGetCurrentThread(),
1858 &ClientSecurityContext
);
1859 if (!NT_SUCCESS(Status
))
1862 DPRINT1("Security context failed\n");
1866 /* Open the target key */
1868 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, TargetKey
);
1870 RtlZeroMemory(&ParseContext
, sizeof(ParseContext
));
1871 ParseContext
.CreateOperation
= FALSE
;
1872 Status
= ObOpenObjectByName(TargetKey
,
1880 if (!NT_SUCCESS(Status
)) KeyHandle
= NULL
;
1883 Status
= CmpCmdHiveOpen(SourceFile
,
1884 &ClientSecurityContext
,
1889 /* Get rid of the security context */
1890 SeDeleteClientSecurity(&ClientSecurityContext
);
1892 /* See if we failed */
1893 if (!NT_SUCCESS(Status
))
1895 /* See if the target already existed */
1898 /* Lock the registry */
1899 CmpLockRegistryExclusive();
1901 /* Check if we are already loaded */
1902 if (CmpIsHiveAlreadyLoaded(KeyHandle
, SourceFile
, &LoadedHive
))
1904 /* That's okay then */
1906 Status
= STATUS_SUCCESS
;
1909 /* Release the registry */
1910 CmpUnlockRegistry();
1913 /* Close the key handle if we had one */
1914 if (KeyHandle
) ZwClose(KeyHandle
);
1918 /* Lock the registry shared */
1922 ExAcquirePushLockExclusive(&CmpLoadHiveLock
);
1924 /* Lock the hive to this thread */
1925 CmHive
->Hive
.HiveFlags
|= HIVE_IS_UNLOADING
;
1926 CmHive
->CreatorOwner
= KeGetCurrentThread();
1929 if (Flags
& REG_NO_LAZY_FLUSH
) CmHive
->Hive
.HiveFlags
|= HIVE_NOLAZYFLUSH
;
1932 Status
= CmpLinkHiveToMaster(TargetKey
->ObjectName
,
1933 TargetKey
->RootDirectory
,
1936 TargetKey
->SecurityDescriptor
);
1937 if (NT_SUCCESS(Status
))
1939 /* Add to HiveList key */
1940 CmpAddToHiveFileList(CmHive
);
1942 /* Sync the hive if necessary */
1945 /* Sync it under the flusher lock */
1946 CmpLockHiveFlusherExclusive(CmHive
);
1947 HvSyncHive(&CmHive
->Hive
);
1948 CmpUnlockHiveFlusher(CmHive
);
1951 /* Release the hive */
1952 CmHive
->Hive
.HiveFlags
&= ~HIVE_IS_UNLOADING
;
1953 CmHive
->CreatorOwner
= NULL
;
1956 ExReleasePushLock(&CmpLoadHiveLock
);
1960 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status
);
1965 /* Is this first profile load? */
1966 if (!(CmpProfileLoaded
) && !(CmpWasSetupBoot
))
1968 /* User is now logged on, set quotas */
1969 CmpProfileLoaded
= TRUE
;
1970 CmpSetGlobalQuotaAllowed();
1973 /* Unlock the registry */
1974 CmpUnlockRegistry();
1976 /* Close handle and return */
1977 if (KeyHandle
) ZwClose(KeyHandle
);
1983 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1987 return STATUS_NOT_IMPLEMENTED
;
1992 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb
,
1993 IN BOOLEAN RemoveEmptyCacheEntries
)
1996 PCM_KEY_CONTROL_BLOCK CachedKcb
;
1997 PCM_KEY_CONTROL_BLOCK ParentKcb
;
1998 ULONG ParentKeyCount
;
2002 DPRINT("CmCountOpenSubKeys() called\n");
2004 /* The root key is the only referenced key. There are no refereced sub keys. */
2005 if (RootKcb
->RefCount
== 1)
2007 DPRINT("open sub keys: 0\n");
2011 /* Enumerate all hash lists */
2012 for (i
= 0; i
< CmpHashTableSize
; i
++)
2014 /* Get the first cache entry */
2015 Entry
= CmpCacheTable
[i
].Entry
;
2017 /* Enumerate all cache entries */
2020 /* Get the KCB of the current cache entry */
2021 CachedKcb
= CONTAINING_RECORD(Entry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
2023 /* Check keys only that are subkeys to our root key */
2024 if (CachedKcb
->TotalLevels
> RootKcb
->TotalLevels
)
2026 /* Calculate the number of parent keys to the root key */
2027 ParentKeyCount
= CachedKcb
->TotalLevels
- RootKcb
->TotalLevels
;
2029 /* Find a parent key that could be the root key */
2030 ParentKcb
= CachedKcb
;
2031 for (j
= 0; j
< ParentKeyCount
; j
++)
2033 ParentKcb
= ParentKcb
->ParentKcb
;
2036 /* Check whether the parent is the root key */
2037 if (ParentKcb
== RootKcb
)
2039 DPRINT("Found a sub key \n");
2040 DPRINT("RefCount = %u\n", CachedKcb
->RefCount
);
2042 if (CachedKcb
->RefCount
> 0)
2044 /* Count the current hash entry if it is in use */
2047 else if ((CachedKcb
->RefCount
== 0) && (RemoveEmptyCacheEntries
== TRUE
))
2049 /* Remove the current key from the delayed close list */
2050 CmpRemoveFromDelayedClose(CachedKcb
);
2052 /* Remove the current cache entry */
2053 CmpCleanUpKcbCacheWithLock(CachedKcb
, TRUE
);
2055 /* Restart, because the hash list has changed */
2056 Entry
= CmpCacheTable
[i
].Entry
;
2062 /* Get the next cache entry */
2063 Entry
= Entry
->NextHash
;
2067 DPRINT("open sub keys: %u\n", SubKeys
);