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
, Offset
;
384 PKEY_INFORMATION Info
= (PKEY_INFORMATION
)KeyInformation
;
388 /* Check if the value is compressed */
389 if (Node
->Flags
& KEY_COMP_NAME
)
391 /* Get the compressed name size */
392 NameLength
= CmpCompressedNameSize(Node
->Name
, Node
->NameLength
);
396 /* Get the real size */
397 NameLength
= Node
->NameLength
;
400 /* Check what kind of information is being requested */
401 switch (KeyInformationClass
)
403 /* Basic information */
404 case KeyBasicInformation
:
406 /* This is the size we need */
407 Size
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
) + NameLength
;
409 /* And this is the minimum we can work with */
410 MinimumSize
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
);
412 /* Let the caller know and assume success */
413 *ResultLength
= Size
;
414 Status
= STATUS_SUCCESS
;
416 /* Check if the bufer we got is too small */
417 if (Length
< MinimumSize
)
419 /* Let the caller know and fail */
420 Status
= STATUS_BUFFER_TOO_SMALL
;
424 /* Copy the basic information */
425 Info
->KeyBasicInformation
.LastWriteTime
= Node
->LastWriteTime
;
426 Info
->KeyBasicInformation
.TitleIndex
= 0;
427 Info
->KeyBasicInformation
.NameLength
= NameLength
;
429 /* Only the name is left */
430 SizeLeft
= Length
- MinimumSize
;
433 /* Check if we don't have enough space for the name */
436 /* Truncate the name we'll return, and tell the caller */
438 Status
= STATUS_BUFFER_OVERFLOW
;
441 /* Check if this is a compressed key */
442 if (Node
->Flags
& KEY_COMP_NAME
)
444 /* Copy the compressed name */
445 CmpCopyCompressedName(Info
->KeyBasicInformation
.Name
,
452 /* Otherwise, copy the raw name */
453 RtlCopyMemory(Info
->KeyBasicInformation
.Name
,
459 /* Node information */
460 case KeyNodeInformation
:
462 /* Calculate the size we need */
463 Size
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) +
467 /* And the minimum size we can support */
468 MinimumSize
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
);
470 /* Return the size to the caller and assume succes */
471 *ResultLength
= Size
;
472 Status
= STATUS_SUCCESS
;
474 /* Check if the caller's buffer is too small */
475 if (Length
< MinimumSize
)
477 /* Let them know, and fail */
478 Status
= STATUS_BUFFER_TOO_SMALL
;
482 /* Copy the basic information */
483 Info
->KeyNodeInformation
.LastWriteTime
= Node
->LastWriteTime
;
484 Info
->KeyNodeInformation
.TitleIndex
= 0;
485 Info
->KeyNodeInformation
.ClassLength
= Node
->ClassLength
;
486 Info
->KeyNodeInformation
.NameLength
= NameLength
;
488 /* Now the name is left */
489 SizeLeft
= Length
- MinimumSize
;
492 /* Check if the name can fit entirely */
495 /* It can't, we'll have to truncate. Tell the caller */
497 Status
= STATUS_BUFFER_OVERFLOW
;
500 /* Check if the key node name is compressed */
501 if (Node
->Flags
& KEY_COMP_NAME
)
503 /* Copy the compressed name */
504 CmpCopyCompressedName(Info
->KeyNodeInformation
.Name
,
511 /* It isn't, so copy the raw name */
512 RtlCopyMemory(Info
->KeyNodeInformation
.Name
,
517 /* Check if the node has a class */
518 if (Node
->ClassLength
> 0)
520 /* Set the class offset */
521 Offset
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) + NameLength
;
522 Offset
= ALIGN_UP_BY(Offset
, sizeof(ULONG
));
523 Info
->KeyNodeInformation
.ClassOffset
= Offset
;
525 /* Get the class data */
526 ClassData
= HvGetCell(Hive
, Node
->Class
);
527 if (ClassData
== NULL
)
529 Status
= STATUS_INSUFFICIENT_RESOURCES
;
533 /* Check if we can copy anything */
536 /* Copy the class data */
537 RtlCopyMemory((PUCHAR
)Info
+ Offset
,
539 min(Node
->ClassLength
, Length
- Offset
));
542 /* Check if the buffer was large enough */
543 if (Length
< Offset
+ Node
->ClassLength
)
545 Status
= STATUS_BUFFER_OVERFLOW
;
548 /* Release the class cell */
549 HvReleaseCell(Hive
, Node
->Class
);
553 /* It doesn't, so set offset to -1, not 0! */
554 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
558 /* Full information requsted */
559 case KeyFullInformation
:
561 /* This is the size we need */
562 Size
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
) +
565 /* This is what we can work with */
566 MinimumSize
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
568 /* Return it to caller and assume success */
569 *ResultLength
= Size
;
570 Status
= STATUS_SUCCESS
;
572 /* Check if the caller's buffer is to small */
573 if (Length
< MinimumSize
)
575 /* Let them know and fail */
576 Status
= STATUS_BUFFER_TOO_SMALL
;
580 /* Now copy all the basic information */
581 Info
->KeyFullInformation
.LastWriteTime
= Node
->LastWriteTime
;
582 Info
->KeyFullInformation
.TitleIndex
= 0;
583 Info
->KeyFullInformation
.ClassLength
= Node
->ClassLength
;
584 Info
->KeyFullInformation
.SubKeys
= Node
->SubKeyCounts
[Stable
] +
585 Node
->SubKeyCounts
[Volatile
];
586 Info
->KeyFullInformation
.Values
= Node
->ValueList
.Count
;
587 Info
->KeyFullInformation
.MaxNameLen
= Node
->MaxNameLen
;
588 Info
->KeyFullInformation
.MaxClassLen
= Node
->MaxClassLen
;
589 Info
->KeyFullInformation
.MaxValueNameLen
= Node
->MaxValueNameLen
;
590 Info
->KeyFullInformation
.MaxValueDataLen
= Node
->MaxValueDataLen
;
592 /* Check if we have a class */
593 if (Node
->ClassLength
> 0)
595 /* Set the class offset */
596 Offset
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
597 Info
->KeyFullInformation
.ClassOffset
= Offset
;
599 /* Get the class data */
600 ClassData
= HvGetCell(Hive
, Node
->Class
);
601 if (ClassData
== NULL
)
603 Status
= STATUS_INSUFFICIENT_RESOURCES
;
607 /* Copy the class data */
608 NT_ASSERT(Length
> Offset
);
609 RtlCopyMemory(Info
->KeyFullInformation
.Class
,
611 min(Node
->ClassLength
, Length
- Offset
));
613 /* Check if the buffer was large enough */
614 if (Length
< Offset
+ Node
->ClassLength
)
616 Status
= STATUS_BUFFER_OVERFLOW
;
619 /* Release the class cell */
620 HvReleaseCell(Hive
, Node
->Class
);
624 /* We don't have a class, so set offset to -1, not 0! */
625 Info
->KeyFullInformation
.ClassOffset
= 0xFFFFFFFF;
629 /* Any other class that got sent here is invalid! */
632 /* Set failure code */
633 Status
= STATUS_INVALID_PARAMETER
;
643 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
644 IN PUNICODE_STRING ValueName
,
651 PCM_KEY_VALUE Value
= NULL
;
652 HCELL_INDEX CurrentChild
, Cell
;
654 BOOLEAN Found
, Result
;
655 ULONG Count
, ChildIndex
, SmallData
, Storage
;
656 VALUE_SEARCH_RETURN_TYPE SearchResult
;
657 BOOLEAN FirstTry
= TRUE
, FlusherLocked
= FALSE
;
658 HCELL_INDEX ParentCell
= HCELL_NIL
, ChildCell
= HCELL_NIL
;
660 /* Acquire hive and KCB lock */
662 CmpAcquireKcbLockShared(Kcb
);
665 ASSERT(sizeof(ULONG
) == CM_KEY_VALUE_SMALL
);
667 /* Don't touch deleted KCBs */
672 Status
= STATUS_KEY_DELETED
;
676 /* Don't let anyone mess with symlinks */
677 if ((Kcb
->Flags
& KEY_SYM_LINK
) &&
678 ((Type
!= REG_LINK
) ||
680 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName
, ValueName
, TRUE
))))
682 /* Invalid modification of a symlink key */
683 Status
= STATUS_ACCESS_DENIED
;
687 /* Check if this is the first attempt */
690 /* Search for the value in the cache */
691 SearchResult
= CmpCompareNewValueDataAgainstKCBCache(Kcb
,
696 if (SearchResult
== SearchNeedExclusiveLock
)
698 /* Try again with the exclusive lock */
699 CmpConvertKcbSharedToExclusive(Kcb
);
702 else if (SearchResult
== SearchSuccess
)
704 /* We don't actually need to do anything! */
705 Status
= STATUS_SUCCESS
;
709 /* We need the exclusive KCB lock now */
710 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
711 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
713 /* Acquire exclusive lock */
714 CmpConvertKcbSharedToExclusive(Kcb
);
717 /* Cache lookup failed, so don't try it next time */
720 /* Now grab the flush lock since the key will be modified */
721 ASSERT(FlusherLocked
== FALSE
);
722 CmpLockHiveFlusherShared((PCMHIVE
)Kcb
->KeyHive
);
723 FlusherLocked
= TRUE
;
728 /* Get pointer to key cell */
733 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
737 /* Prepare to scan the key node */
738 Count
= Parent
->ValueList
.Count
;
742 /* Try to find the existing name */
743 Result
= CmpFindNameInList(Hive
,
751 Status
= STATUS_INSUFFICIENT_RESOURCES
;
755 /* Check if we found something */
756 if (CurrentChild
!= HCELL_NIL
)
758 /* Release existing child */
759 if (ChildCell
!= HCELL_NIL
)
761 HvReleaseCell(Hive
, ChildCell
);
762 ChildCell
= HCELL_NIL
;
766 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
, CurrentChild
);
770 Status
= STATUS_INSUFFICIENT_RESOURCES
;
774 /* Remember that we found it */
775 ChildCell
= CurrentChild
;
781 /* No child list, we'll need to add it */
786 /* Should only get here on the second pass */
787 ASSERT(FirstTry
== FALSE
);
789 /* The KCB must be locked exclusive at this point */
790 CMP_ASSERT_KCB_LOCK(Kcb
);
792 /* Mark the cell dirty */
793 if (!HvMarkCellDirty(Hive
, Cell
, FALSE
))
795 /* Not enough log space, fail */
796 Status
= STATUS_NO_LOG_SPACE
;
800 /* Get the storage type */
801 Storage
= HvGetCellType(Cell
);
803 /* Check if this is small data */
805 if ((DataLength
<= CM_KEY_VALUE_SMALL
) && (DataLength
> 0))
807 /* Need SEH because user data may be invalid */
811 RtlCopyMemory(&SmallData
, Data
, DataLength
);
813 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
815 /* Return failure code */
816 Status
= _SEH2_GetExceptionCode();
817 _SEH2_YIELD(goto Quickie
);
822 /* Check if we didn't find a matching key */
825 /* Call the internal routine */
826 Status
= CmpSetValueKeyNew(Hive
,
838 /* Call the internal routine */
839 Status
= CmpSetValueKeyExisting(Hive
,
849 /* Check for success */
850 if (NT_SUCCESS(Status
))
852 /* Check if the maximum value name length changed */
853 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
854 if (Parent
->MaxValueNameLen
< ValueName
->Length
)
856 /* Set the new values */
857 Parent
->MaxValueNameLen
= ValueName
->Length
;
858 Kcb
->KcbMaxValueNameLen
= ValueName
->Length
;
861 /* Check if the maximum data length changed */
862 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
863 if (Parent
->MaxValueDataLen
< DataLength
)
866 Parent
->MaxValueDataLen
= DataLength
;
867 Kcb
->KcbMaxValueDataLen
= Parent
->MaxValueDataLen
;
870 /* Save the write time */
871 KeQuerySystemTime(&Parent
->LastWriteTime
);
872 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
874 /* Check if the cell is cached */
875 if ((Found
) && (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)))
877 /* Shouldn't happen */
882 /* Cleanup the value cache */
883 CmpCleanUpKcbValueCache(Kcb
);
886 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
887 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
889 /* Set the value cache */
890 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
891 Kcb
->ValueCache
.ValueList
= Parent
->ValueList
.List
;
894 /* Notify registered callbacks */
898 REG_NOTIFY_CHANGE_LAST_SET
);
901 /* Release the cells */
903 if ((ParentCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ParentCell
);
904 if ((ChildCell
!= HCELL_NIL
) && (Hive
)) HvReleaseCell(Hive
, ChildCell
);
906 /* Release the locks */
907 if (FlusherLocked
) CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
908 CmpReleaseKcbLock(Kcb
);
915 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
916 IN UNICODE_STRING ValueName
)
918 NTSTATUS Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
921 HCELL_INDEX ChildCell
, Cell
;
922 PCHILD_LIST ChildList
;
923 PCM_KEY_VALUE Value
= NULL
;
927 /* Acquire hive lock */
930 /* Lock KCB exclusively */
931 CmpAcquireKcbLockExclusive(Kcb
);
933 /* Don't touch deleted keys */
936 /* Undo everything */
937 CmpReleaseKcbLock(Kcb
);
939 return STATUS_KEY_DELETED
;
942 /* Get the hive and the cell index */
947 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
949 /* Get the parent key node */
950 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
953 /* Get the value list and check if it has any entries */
954 ChildList
= &Parent
->ValueList
;
955 if (ChildList
->Count
)
957 /* Try to find this value */
958 Result
= CmpFindNameInList(Hive
,
966 Status
= STATUS_INSUFFICIENT_RESOURCES
;
970 /* Value not found, return error */
971 if (ChildCell
== HCELL_NIL
) goto Quickie
;
973 /* We found the value, mark all relevant cells dirty */
974 if (!((HvMarkCellDirty(Hive
, Cell
, FALSE
)) &&
975 (HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
)) &&
976 (HvMarkCellDirty(Hive
, ChildCell
, FALSE
))))
978 /* Not enough log space, fail */
979 Status
= STATUS_NO_LOG_SPACE
;
983 /* Get the key value */
984 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
,ChildCell
);
987 /* Mark it and all related data as dirty */
988 if (!CmpMarkValueDataDirty(Hive
, Value
))
990 /* Not enough log space, fail */
991 Status
= STATUS_NO_LOG_SPACE
;
996 ASSERT(HvIsCellDirty(Hive
, Parent
->ValueList
.List
));
997 ASSERT(HvIsCellDirty(Hive
, ChildCell
));
999 /* Remove the value from the child list */
1000 Status
= CmpRemoveValueFromList(Hive
, ChildIndex
, ChildList
);
1001 if (!NT_SUCCESS(Status
))
1003 /* Set known error */
1004 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1008 /* Remove the value and its data itself */
1009 if (!CmpFreeValue(Hive
, ChildCell
))
1011 /* Failed to free the value, fail */
1012 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1016 /* Set the last write time */
1017 KeQuerySystemTime(&Parent
->LastWriteTime
);
1018 Kcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
1021 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
1022 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
1023 ASSERT(HvIsCellDirty(Hive
, Cell
));
1025 /* Check if the value list is empty now */
1026 if (!Parent
->ValueList
.Count
)
1028 /* Then clear key node data */
1029 Parent
->MaxValueNameLen
= 0;
1030 Parent
->MaxValueDataLen
= 0;
1031 Kcb
->KcbMaxValueNameLen
= 0;
1032 Kcb
->KcbMaxValueDataLen
= 0;
1035 /* Cleanup the value cache */
1036 CmpCleanUpKcbValueCache(Kcb
);
1039 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
1040 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
1042 /* Set the value cache */
1043 Kcb
->ValueCache
.Count
= ChildList
->Count
;
1044 Kcb
->ValueCache
.ValueList
= ChildList
->List
;
1046 /* Notify registered callbacks */
1047 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_LAST_SET
);
1049 /* Change default Status to success */
1050 Status
= STATUS_SUCCESS
;
1054 /* Release the parent cell, if any */
1055 if (Parent
) HvReleaseCell(Hive
, Cell
);
1057 /* Check if we had a value */
1060 /* Release the child cell */
1061 ASSERT(ChildCell
!= HCELL_NIL
);
1062 HvReleaseCell(Hive
, ChildCell
);
1066 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1067 CmpReleaseKcbLock(Kcb
);
1068 CmpUnlockRegistry();
1074 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1075 IN UNICODE_STRING ValueName
,
1076 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1077 IN PVOID KeyValueInformation
,
1079 IN PULONG ResultLength
)
1082 PCM_KEY_VALUE ValueData
;
1084 BOOLEAN ValueCached
= FALSE
;
1085 PCM_CACHED_VALUE
*CachedValue
;
1086 HCELL_INDEX CellToRelease
;
1087 VALUE_SEARCH_RETURN_TYPE Result
;
1091 /* Acquire hive lock */
1094 /* Lock the KCB shared */
1095 CmpAcquireKcbLockShared(Kcb
);
1097 /* Don't touch deleted keys */
1101 /* Undo everything */
1102 CmpReleaseKcbLock(Kcb
);
1103 CmpUnlockRegistry();
1104 return STATUS_KEY_DELETED
;
1107 /* We don't deal with this yet */
1108 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1110 /* Shouldn't happen */
1115 Hive
= Kcb
->KeyHive
;
1117 /* Find the key value */
1118 Result
= CmpFindValueByNameFromCache(Kcb
,
1125 if (Result
== SearchNeedExclusiveLock
)
1127 /* Check if we need an exclusive lock */
1128 ASSERT(CellToRelease
== HCELL_NIL
);
1129 ASSERT(ValueData
== NULL
);
1131 /* Try with exclusive KCB lock */
1132 CmpConvertKcbSharedToExclusive(Kcb
);
1136 if (Result
== SearchSuccess
)
1139 ASSERT(ValueData
!= NULL
);
1141 /* User data, protect against exceptions */
1144 /* Query the information requested */
1145 Result
= CmpQueryKeyValueData(Kcb
,
1149 KeyValueInformationClass
,
1150 KeyValueInformation
,
1154 if (Result
== SearchNeedExclusiveLock
)
1156 /* Release the value cell */
1157 if (CellToRelease
!= HCELL_NIL
)
1159 HvReleaseCell(Hive
, CellToRelease
);
1160 CellToRelease
= HCELL_NIL
;
1163 /* Try with exclusive KCB lock */
1164 CmpConvertKcbSharedToExclusive(Kcb
);
1165 _SEH2_YIELD(goto DoAgain
);
1168 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1170 Status
= _SEH2_GetExceptionCode();
1176 /* Failed to find the value */
1177 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
1180 /* If we have a cell to release, do so */
1181 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1184 CmpReleaseKcbLock(Kcb
);
1185 CmpUnlockRegistry();
1191 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1193 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
1194 IN PVOID KeyValueInformation
,
1196 IN PULONG ResultLength
)
1200 PCM_KEY_NODE Parent
;
1201 HCELL_INDEX CellToRelease
= HCELL_NIL
, CellToRelease2
= HCELL_NIL
;
1202 VALUE_SEARCH_RETURN_TYPE Result
;
1203 BOOLEAN IndexIsCached
, ValueIsCached
= FALSE
;
1204 PCELL_DATA CellData
;
1205 PCM_CACHED_VALUE
*CachedValue
;
1206 PCM_KEY_VALUE ValueData
= NULL
;
1209 /* Acquire hive lock */
1212 /* Lock the KCB shared */
1213 CmpAcquireKcbLockShared(Kcb
);
1215 /* Don't touch deleted keys */
1219 /* Undo everything */
1220 CmpReleaseKcbLock(Kcb
);
1221 CmpUnlockRegistry();
1222 return STATUS_KEY_DELETED
;
1225 /* Get the hive and parent */
1226 Hive
= Kcb
->KeyHive
;
1227 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1230 /* FIXME: Lack of cache? */
1231 if (Kcb
->ValueCache
.Count
!= Parent
->ValueList
.Count
)
1233 DPRINT1("HACK: Overriding value cache count\n");
1234 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
1237 /* Make sure the index is valid */
1238 if (Index
>= Kcb
->ValueCache
.Count
)
1240 /* Release the cell and fail */
1241 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1242 Status
= STATUS_NO_MORE_ENTRIES
;
1246 /* We don't deal with this yet */
1247 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
1249 /* Shouldn't happen */
1253 /* Find the value list */
1254 Result
= CmpGetValueListFromCache(Kcb
,
1258 if (Result
== SearchNeedExclusiveLock
)
1260 /* Check if we need an exclusive lock */
1261 ASSERT(CellToRelease
== HCELL_NIL
);
1262 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1264 /* Try with exclusive KCB lock */
1265 CmpConvertKcbSharedToExclusive(Kcb
);
1268 else if (Result
!= SearchSuccess
)
1271 ASSERT(CellData
== NULL
);
1273 /* Release the cell and fail */
1274 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1278 /* Now get the key value */
1279 Result
= CmpGetValueKeyFromCache(Kcb
,
1287 if (Result
== SearchNeedExclusiveLock
)
1290 ASSERT(CellToRelease2
== HCELL_NIL
);
1293 HvReleaseCell(Hive
, CellToRelease
);
1294 CellToRelease
= HCELL_NIL
;
1296 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1298 /* Try with exclusive KCB lock */
1299 CmpConvertKcbSharedToExclusive(Kcb
);
1302 else if (Result
!= SearchSuccess
)
1305 ASSERT(ValueData
== NULL
);
1307 /* Release the cells and fail */
1308 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1312 /* User data, need SEH */
1315 /* Query the information requested */
1316 Result
= CmpQueryKeyValueData(Kcb
,
1320 KeyValueInformationClass
,
1321 KeyValueInformation
,
1325 if (Result
== SearchNeedExclusiveLock
)
1328 if (CellToRelease2
) HvReleaseCell(Hive
, CellToRelease2
);
1329 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1330 if (CellToRelease
) HvReleaseCell(Hive
, CellToRelease
);
1332 /* Try with exclusive KCB lock */
1333 CmpConvertKcbSharedToExclusive(Kcb
);
1334 _SEH2_YIELD(goto DoAgain
);
1337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1339 /* Get exception code */
1340 Status
= _SEH2_GetExceptionCode();
1345 /* If we have a cell to release, do so */
1346 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
1348 /* Release the parent cell */
1349 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1351 /* If we have a cell to release, do so */
1352 if (CellToRelease2
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease2
);
1355 CmpReleaseKcbLock(Kcb
);
1356 CmpUnlockRegistry();
1362 CmpQueryKeyDataFromCache(
1363 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1364 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo
,
1366 _Out_ PULONG ResultLength
)
1370 HCELL_INDEX KeyCell
;
1374 /* Get the hive and cell index */
1375 KeyHive
= Kcb
->KeyHash
.KeyHive
;
1376 KeyCell
= Kcb
->KeyHash
.KeyCell
;
1379 /* Get the cell node */
1380 Node
= HvGetCell(KeyHive
, KeyCell
);
1384 ASSERT(Node
->ValueList
.Count
== Kcb
->ValueCache
.Count
);
1386 if (!(Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
))
1388 SubKeyCount
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1389 if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1391 ASSERT(SubKeyCount
== 0);
1393 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1395 ASSERT(SubKeyCount
== 1);
1397 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1399 ASSERT(SubKeyCount
== Kcb
->IndexHint
->Count
);
1403 ASSERT(SubKeyCount
== Kcb
->SubKeyCount
);
1407 ASSERT(Node
->LastWriteTime
.QuadPart
== Kcb
->KcbLastWriteTime
.QuadPart
);
1408 ASSERT(Node
->MaxNameLen
== Kcb
->KcbMaxNameLen
);
1409 ASSERT(Node
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
1410 ASSERT(Node
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
1412 /* Release the cell */
1413 HvReleaseCell(KeyHive
, KeyCell
);
1417 /* Make sure we have a name block */
1418 if (Kcb
->NameBlock
== NULL
)
1420 return STATUS_INSUFFICIENT_RESOURCES
;
1423 /* Check for compressed name */
1424 if (Kcb
->NameBlock
->Compressed
)
1426 /* Calculate the name size */
1427 NameLength
= CmpCompressedNameSize(Kcb
->NameBlock
->NameHash
.Name
,
1428 Kcb
->NameBlock
->NameHash
.NameLength
);
1432 /* Use the stored name size */
1433 NameLength
= Kcb
->NameBlock
->NameHash
.NameLength
;
1436 /* Validate buffer length (we do not copy the name!) */
1437 *ResultLength
= sizeof(*KeyCachedInfo
);
1438 if (Length
< *ResultLength
)
1440 return STATUS_BUFFER_TOO_SMALL
;
1443 /* Fill the structure */
1444 KeyCachedInfo
->LastWriteTime
= Kcb
->KcbLastWriteTime
;
1445 KeyCachedInfo
->TitleIndex
= 0;
1446 KeyCachedInfo
->NameLength
= NameLength
;
1447 KeyCachedInfo
->Values
= Kcb
->ValueCache
.Count
;
1448 KeyCachedInfo
->MaxNameLen
= Kcb
->KcbMaxNameLen
;
1449 KeyCachedInfo
->MaxValueNameLen
= Kcb
->KcbMaxValueNameLen
;
1450 KeyCachedInfo
->MaxValueDataLen
= Kcb
->KcbMaxValueDataLen
;
1452 /* Check the ExtFlags for what we have */
1453 if (Kcb
->ExtFlags
& CM_KCB_INVALID_CACHED_INFO
)
1455 /* Cache is not valid, do a full lookup */
1456 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb
);
1458 /* Get the cell node */
1459 Node
= HvGetCell(KeyHive
, KeyCell
);
1462 return STATUS_INSUFFICIENT_RESOURCES
;
1465 /* Calculate number of subkeys */
1466 KeyCachedInfo
->SubKeys
= Node
->SubKeyCounts
[0] + Node
->SubKeyCounts
[1];
1468 /* Release the cell */
1469 HvReleaseCell(KeyHive
, KeyCell
);
1471 else if (Kcb
->ExtFlags
& CM_KCB_NO_SUBKEY
)
1473 /* There are no subkeys */
1474 KeyCachedInfo
->SubKeys
= 0;
1476 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_ONE
)
1478 /* There is exactly one subley */
1479 KeyCachedInfo
->SubKeys
= 1;
1481 else if (Kcb
->ExtFlags
& CM_KCB_SUBKEY_HINT
)
1483 /* Get the number of subkeys from the subkey hint */
1484 KeyCachedInfo
->SubKeys
= Kcb
->IndexHint
->Count
;
1488 /* No subkey hint, use the key count field */
1489 KeyCachedInfo
->SubKeys
= Kcb
->SubKeyCount
;
1492 return STATUS_SUCCESS
;
1497 CmpQueryFlagsInformation(
1498 _In_ PCM_KEY_CONTROL_BLOCK Kcb
,
1499 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo
,
1501 _In_ PULONG ResultLength
)
1503 /* Validate the buffer size */
1504 *ResultLength
= sizeof(*KeyFlagsInfo
);
1505 if (Length
< *ResultLength
)
1507 return STATUS_BUFFER_TOO_SMALL
;
1510 /* Copy the user flags */
1511 KeyFlagsInfo
->UserFlags
= Kcb
->KcbUserFlags
;
1513 return STATUS_SUCCESS
;
1518 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1519 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1520 IN PVOID KeyInformation
,
1522 IN PULONG ResultLength
)
1526 PCM_KEY_NODE Parent
;
1527 HV_TRACK_CELL_REF CellReferences
= {0};
1529 /* Acquire hive lock */
1532 /* Lock KCB shared */
1533 CmpAcquireKcbLockShared(Kcb
);
1535 /* Don't touch deleted keys */
1539 Status
= STATUS_KEY_DELETED
;
1543 /* Check what class we got */
1544 switch (KeyInformationClass
)
1546 /* Typical information */
1547 case KeyFullInformation
:
1548 case KeyBasicInformation
:
1549 case KeyNodeInformation
:
1551 /* Get the hive and parent */
1552 Hive
= Kcb
->KeyHive
;
1553 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1556 /* Track cell references */
1557 if (!HvTrackCellRef(&CellReferences
, Hive
, Kcb
->KeyCell
))
1559 /* Not enough memory to track references */
1560 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1564 /* Call the internal API */
1565 Status
= CmpQueryKeyData(Hive
,
1567 KeyInformationClass
,
1574 case KeyCachedInformation
:
1575 /* Call the internal API */
1576 Status
= CmpQueryKeyDataFromCache(Kcb
,
1582 case KeyFlagsInformation
:
1583 /* Call the internal API */
1584 Status
= CmpQueryFlagsInformation(Kcb
,
1590 /* Unsupported class for now */
1591 case KeyNameInformation
:
1593 /* Print message and fail */
1594 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1595 Status
= STATUS_NOT_IMPLEMENTED
;
1598 /* Illegal classes */
1601 /* Print message and fail */
1602 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1603 Status
= STATUS_INVALID_INFO_CLASS
;
1608 /* Release references */
1609 HvReleaseFreeCellRefArray(&CellReferences
);
1612 CmpReleaseKcbLock(Kcb
);
1613 CmpUnlockRegistry();
1619 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1621 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1622 IN PVOID KeyInformation
,
1624 IN PULONG ResultLength
)
1628 PCM_KEY_NODE Parent
, Child
;
1629 HCELL_INDEX ChildCell
;
1630 HV_TRACK_CELL_REF CellReferences
= {0};
1632 /* Acquire hive lock */
1635 /* Lock the KCB shared */
1636 CmpAcquireKcbLockShared(Kcb
);
1638 /* Don't touch deleted keys */
1641 /* Undo everything */
1642 Status
= STATUS_KEY_DELETED
;
1646 /* Get the hive and parent */
1647 Hive
= Kcb
->KeyHive
;
1648 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1651 /* Get the child cell */
1652 ChildCell
= CmpFindSubKeyByNumber(Hive
, Parent
, Index
);
1654 /* Release the parent cell */
1655 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1657 /* Check if we found the child */
1658 if (ChildCell
== HCELL_NIL
)
1660 /* We didn't, fail */
1661 Status
= STATUS_NO_MORE_ENTRIES
;
1665 /* Now get the actual child node */
1666 Child
= (PCM_KEY_NODE
)HvGetCell(Hive
, ChildCell
);
1669 /* Track references */
1670 if (!HvTrackCellRef(&CellReferences
, Hive
, ChildCell
))
1672 /* Can't allocate memory for tracking */
1673 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1677 /* Data can be user-mode, use SEH */
1680 /* Query the data requested */
1681 Status
= CmpQueryKeyData(Hive
,
1683 KeyInformationClass
,
1688 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1690 /* Fail with exception code */
1691 Status
= _SEH2_GetExceptionCode();
1692 _SEH2_YIELD(goto Quickie
);
1697 /* Release references */
1698 HvReleaseFreeCellRefArray(&CellReferences
);
1701 CmpReleaseKcbLock(Kcb
);
1702 CmpUnlockRegistry();
1708 CmDeleteKey(IN PCM_KEY_BODY KeyBody
)
1712 PCM_KEY_NODE Node
, Parent
;
1713 HCELL_INDEX Cell
, ParentCell
;
1714 PCM_KEY_CONTROL_BLOCK Kcb
;
1716 /* Acquire hive lock */
1720 Kcb
= KeyBody
->KeyControlBlock
;
1722 /* Don't allow deleting the root */
1723 if (!Kcb
->ParentKcb
)
1726 CmpUnlockRegistry();
1727 return STATUS_CANNOT_DELETE
;
1730 /* Lock parent and child */
1731 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1733 /* Check if we're already being deleted */
1736 /* Don't do it twice */
1737 Status
= STATUS_SUCCESS
;
1741 /* Get the hive and node */
1742 Hive
= Kcb
->KeyHive
;
1743 Cell
= Kcb
->KeyCell
;
1746 CmpLockHiveFlusherShared((PCMHIVE
)Hive
);
1748 /* Get the key node */
1749 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
1753 ASSERT(Node
->Flags
== Kcb
->Flags
);
1755 /* Check if we don't have any children */
1756 if (!(Node
->SubKeyCounts
[Stable
] + Node
->SubKeyCounts
[Volatile
]) &&
1757 !(Node
->Flags
& KEY_NO_DELETE
))
1759 /* Send notification to registered callbacks */
1760 CmpReportNotify(Kcb
, Hive
, Cell
, REG_NOTIFY_CHANGE_NAME
);
1762 /* Get the parent and free the cell */
1763 ParentCell
= Node
->Parent
;
1764 Status
= CmpFreeKeyByCell(Hive
, Cell
, TRUE
);
1765 if (NT_SUCCESS(Status
))
1767 /* Flush any notifications */
1768 CmpFlushNotifiesOnKeyBodyList(Kcb
, FALSE
);
1770 /* Clean up information we have on the subkey */
1771 CmpCleanUpSubKeyInfo(Kcb
->ParentKcb
);
1773 /* Get the parent node */
1774 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, ParentCell
);
1777 /* Update the maximum name length */
1778 Kcb
->ParentKcb
->KcbMaxNameLen
= (USHORT
)Parent
->MaxNameLen
;
1780 /* Make sure we're dirty */
1781 ASSERT(HvIsCellDirty(Hive
, ParentCell
));
1783 /* Update the write time */
1784 KeQuerySystemTime(&Parent
->LastWriteTime
);
1785 Kcb
->ParentKcb
->KcbLastWriteTime
= Parent
->LastWriteTime
;
1787 /* Release the cell */
1788 HvReleaseCell(Hive
, ParentCell
);
1791 /* Set the KCB in delete mode and remove it */
1793 CmpRemoveKeyControlBlock(Kcb
);
1795 /* Clear the cell */
1796 Kcb
->KeyCell
= HCELL_NIL
;
1802 Status
= STATUS_CANNOT_DELETE
;
1805 /* Release the cell */
1806 HvReleaseCell(Hive
, Cell
);
1808 /* Release flush lock */
1809 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1811 /* Release the KCB locks */
1813 CmpReleaseTwoKcbLockByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1815 /* Release hive lock */
1816 CmpUnlockRegistry();
1822 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1823 IN BOOLEAN ExclusiveLock
)
1826 NTSTATUS Status
= STATUS_SUCCESS
;
1829 /* Ignore flushes until we're ready */
1830 if (CmpNoWrite
) return STATUS_SUCCESS
;
1833 Hive
= Kcb
->KeyHive
;
1834 CmHive
= (PCMHIVE
)Hive
;
1836 /* Check if this is the master hive */
1837 if (CmHive
== CmiVolatileHive
)
1839 /* Flush all the hives instead */
1840 CmpDoFlushAll(FALSE
);
1844 /* Don't touch the hive */
1845 CmpLockHiveFlusherExclusive(CmHive
);
1846 ASSERT(CmHive
->ViewLock
);
1847 KeAcquireGuardedMutex(CmHive
->ViewLock
);
1848 CmHive
->ViewLockOwner
= KeGetCurrentThread();
1850 /* Will the hive shrink? */
1851 if (HvHiveWillShrink(Hive
))
1853 /* I don't believe the current Hv does shrinking */
1858 /* Now we can release views */
1859 ASSERT(CmHive
->ViewLock
);
1860 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive
);
1861 ASSERT(KeGetCurrentThread() == CmHive
->ViewLockOwner
);
1862 KeReleaseGuardedMutex(CmHive
->ViewLock
);
1865 /* Flush only this hive */
1866 if (!HvSyncHive(Hive
))
1869 Status
= STATUS_REGISTRY_IO_FAILED
;
1872 /* Release the flush lock */
1873 CmpUnlockHiveFlusher((PCMHIVE
)Hive
);
1876 /* Return the status */
1882 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey
,
1883 IN POBJECT_ATTRIBUTES SourceFile
,
1885 IN PCM_KEY_BODY KeyBody
)
1887 SECURITY_QUALITY_OF_SERVICE ServiceQos
;
1888 SECURITY_CLIENT_CONTEXT ClientSecurityContext
;
1890 BOOLEAN Allocate
= TRUE
;
1891 PCMHIVE CmHive
, LoadedHive
;
1893 CM_PARSE_CONTEXT ParseContext
;
1895 /* Check if we have a trust key */
1899 DPRINT("Trusted classes not yet supported\n");
1902 /* Build a service QoS for a security context */
1903 ServiceQos
.Length
= sizeof(SECURITY_QUALITY_OF_SERVICE
);
1904 ServiceQos
.ImpersonationLevel
= SecurityImpersonation
;
1905 ServiceQos
.ContextTrackingMode
= SECURITY_DYNAMIC_TRACKING
;
1906 ServiceQos
.EffectiveOnly
= TRUE
;
1907 Status
= SeCreateClientSecurity(PsGetCurrentThread(),
1910 &ClientSecurityContext
);
1911 if (!NT_SUCCESS(Status
))
1914 DPRINT1("Security context failed\n");
1918 /* Open the target key */
1920 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, TargetKey
);
1922 RtlZeroMemory(&ParseContext
, sizeof(ParseContext
));
1923 ParseContext
.CreateOperation
= FALSE
;
1924 Status
= ObOpenObjectByName(TargetKey
,
1932 if (!NT_SUCCESS(Status
)) KeyHandle
= NULL
;
1935 Status
= CmpCmdHiveOpen(SourceFile
,
1936 &ClientSecurityContext
,
1941 /* Get rid of the security context */
1942 SeDeleteClientSecurity(&ClientSecurityContext
);
1944 /* See if we failed */
1945 if (!NT_SUCCESS(Status
))
1947 /* See if the target already existed */
1950 /* Lock the registry */
1951 CmpLockRegistryExclusive();
1953 /* Check if we are already loaded */
1954 if (CmpIsHiveAlreadyLoaded(KeyHandle
, SourceFile
, &LoadedHive
))
1956 /* That's okay then */
1958 Status
= STATUS_SUCCESS
;
1961 /* Release the registry */
1962 CmpUnlockRegistry();
1965 /* Close the key handle if we had one */
1966 if (KeyHandle
) ZwClose(KeyHandle
);
1970 /* Lock the registry shared */
1974 ExAcquirePushLockExclusive(&CmpLoadHiveLock
);
1976 /* Lock the hive to this thread */
1977 CmHive
->Hive
.HiveFlags
|= HIVE_IS_UNLOADING
;
1978 CmHive
->CreatorOwner
= KeGetCurrentThread();
1981 if (Flags
& REG_NO_LAZY_FLUSH
) CmHive
->Hive
.HiveFlags
|= HIVE_NOLAZYFLUSH
;
1984 Status
= CmpLinkHiveToMaster(TargetKey
->ObjectName
,
1985 TargetKey
->RootDirectory
,
1988 TargetKey
->SecurityDescriptor
);
1989 if (NT_SUCCESS(Status
))
1991 /* Add to HiveList key */
1992 CmpAddToHiveFileList(CmHive
);
1994 /* Sync the hive if necessary */
1997 /* Sync it under the flusher lock */
1998 CmpLockHiveFlusherExclusive(CmHive
);
1999 HvSyncHive(&CmHive
->Hive
);
2000 CmpUnlockHiveFlusher(CmHive
);
2003 /* Release the hive */
2004 CmHive
->Hive
.HiveFlags
&= ~HIVE_IS_UNLOADING
;
2005 CmHive
->CreatorOwner
= NULL
;
2008 ExReleasePushLock(&CmpLoadHiveLock
);
2012 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status
);
2017 /* Is this first profile load? */
2018 if (!(CmpProfileLoaded
) && !(CmpWasSetupBoot
))
2020 /* User is now logged on, set quotas */
2021 CmpProfileLoaded
= TRUE
;
2022 CmpSetGlobalQuotaAllowed();
2025 /* Unlock the registry */
2026 CmpUnlockRegistry();
2028 /* Close handle and return */
2029 if (KeyHandle
) ZwClose(KeyHandle
);
2035 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
2039 return STATUS_NOT_IMPLEMENTED
;
2044 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb
,
2045 IN BOOLEAN RemoveEmptyCacheEntries
)
2048 PCM_KEY_CONTROL_BLOCK CachedKcb
;
2049 PCM_KEY_CONTROL_BLOCK ParentKcb
;
2050 ULONG ParentKeyCount
;
2054 DPRINT("CmCountOpenSubKeys() called\n");
2056 /* The root key is the only referenced key. There are no refereced sub keys. */
2057 if (RootKcb
->RefCount
== 1)
2059 DPRINT("open sub keys: 0\n");
2063 /* Enumerate all hash lists */
2064 for (i
= 0; i
< CmpHashTableSize
; i
++)
2066 /* Get the first cache entry */
2067 Entry
= CmpCacheTable
[i
].Entry
;
2069 /* Enumerate all cache entries */
2072 /* Get the KCB of the current cache entry */
2073 CachedKcb
= CONTAINING_RECORD(Entry
, CM_KEY_CONTROL_BLOCK
, KeyHash
);
2075 /* Check keys only that are subkeys to our root key */
2076 if (CachedKcb
->TotalLevels
> RootKcb
->TotalLevels
)
2078 /* Calculate the number of parent keys to the root key */
2079 ParentKeyCount
= CachedKcb
->TotalLevels
- RootKcb
->TotalLevels
;
2081 /* Find a parent key that could be the root key */
2082 ParentKcb
= CachedKcb
;
2083 for (j
= 0; j
< ParentKeyCount
; j
++)
2085 ParentKcb
= ParentKcb
->ParentKcb
;
2088 /* Check whether the parent is the root key */
2089 if (ParentKcb
== RootKcb
)
2091 DPRINT("Found a sub key \n");
2092 DPRINT("RefCount = %u\n", CachedKcb
->RefCount
);
2094 if (CachedKcb
->RefCount
> 0)
2096 /* Count the current hash entry if it is in use */
2099 else if ((CachedKcb
->RefCount
== 0) && (RemoveEmptyCacheEntries
== TRUE
))
2101 /* Remove the current key from the delayed close list */
2102 CmpRemoveFromDelayedClose(CachedKcb
);
2104 /* Remove the current cache entry */
2105 CmpCleanUpKcbCacheWithLock(CachedKcb
, TRUE
);
2107 /* Restart, because the hash list has changed */
2108 Entry
= CmpCacheTable
[i
].Entry
;
2114 /* Get the next cache entry */
2115 Entry
= Entry
->NextHash
;
2119 DPRINT("open sub keys: %u\n", SubKeys
);