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)
9 /* INCLUDES ******************************************************************/
15 /* FUNCTIONS *****************************************************************/
19 CmpDoFlushAll(IN BOOLEAN ForceFlush
)
21 PLIST_ENTRY NextEntry
;
24 BOOLEAN Result
= TRUE
;
26 /* Make sure that the registry isn't read-only now */
27 if (CmpNoWrite
) return TRUE
;
29 /* Otherwise, acquire the hive list lock and disable force flush */
30 CmpForceForceFlush
= FALSE
;
31 ExAcquirePushLockShared(&CmpHiveListHeadLock
);
33 /* Loop the hive list */
34 NextEntry
= CmpHiveListHead
.Flink
;
35 while (NextEntry
!= &CmpHiveListHead
)
38 Hive
= CONTAINING_RECORD(NextEntry
, CMHIVE
, HiveList
);
39 if (!(Hive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
))
41 /* Acquire the flusher lock */
42 ExAcquirePushLockExclusive((PVOID
)&Hive
->FlusherLock
);
45 Status
= HvSyncHive(&Hive
->Hive
);
47 /* If something failed - set the flag and continue looping*/
48 if (!NT_SUCCESS(Status
)) Result
= FALSE
;
50 /* Release the flusher lock */
51 ExReleasePushLock((PVOID
)&Hive
->FlusherLock
);
54 /* Try the next entry */
55 NextEntry
= NextEntry
->Flink
;
58 /* Release lock and return */
59 ExReleasePushLock(&CmpHiveListHeadLock
);
65 CmpSetValueKeyNew(IN PHHIVE Hive
,
66 IN PCM_KEY_NODE Parent
,
67 IN PUNICODE_STRING ValueName
,
76 HCELL_INDEX ValueCell
;
79 /* Check if we already have a value list */
80 if (Parent
->ValueList
.Count
)
82 /* Then make sure it's valid and dirty it */
83 ASSERT(Parent
->ValueList
.List
!= HCELL_NIL
);
84 HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
);
87 /* Allocate avalue cell */
88 ValueCell
= HvAllocateCell(Hive
,
89 FIELD_OFFSET(CM_KEY_VALUE
, Name
) +
90 CmpNameSize(Hive
, ValueName
),
93 if (ValueCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
95 /* Get the actual data for it */
96 CellData
= HvGetCell(Hive
, ValueCell
);
97 if (!CellData
) ASSERT(FALSE
);
99 /* Now we can release it, make sure it's also dirty */
100 HvReleaseCell(Hive
, ValueCell
);
101 ASSERT(HvIsCellDirty(Hive
, ValueCell
));
103 /* Set it up and copy the name */
104 CellData
->u
.KeyValue
.Signature
= CM_KEY_VALUE_SIGNATURE
;
105 CellData
->u
.KeyValue
.Flags
= 0;
106 CellData
->u
.KeyValue
.Type
= Type
;
107 CellData
->u
.KeyValue
.NameLength
= CmpCopyName(Hive
,
108 CellData
->u
.KeyValue
.Name
,
110 if (CellData
->u
.KeyValue
.NameLength
< ValueName
->Length
)
112 /* This is a compressed name */
113 CellData
->u
.KeyValue
.Flags
= VALUE_COMP_NAME
;
116 /* Check if this is a normal key */
117 if (DataSize
> CM_KEY_VALUE_SMALL
)
119 /* Build a data cell for it */
120 Status
= CmpSetValueDataNew(Hive
,
125 &CellData
->u
.KeyValue
.Data
);
126 if (!NT_SUCCESS(Status
))
128 /* We failed, free the cell */
129 HvFreeCell(Hive
, ValueCell
);
133 /* Otherwise, set the data length, and make sure the data is dirty */
134 CellData
->u
.KeyValue
.DataLength
= DataSize
;
135 ASSERT(HvIsCellDirty(Hive
, CellData
->u
.KeyValue
.Data
));
139 /* This is a small key, set the data directly inside */
140 CellData
->u
.KeyValue
.DataLength
= DataSize
+ CM_KEY_VALUE_SPECIAL_SIZE
;
141 CellData
->u
.KeyValue
.Data
= SmallData
;
144 /* Add this value cell to the child list */
145 Status
= CmpAddValueToList(Hive
,
151 /* If we failed, free the entire cell, including the data */
152 if (!NT_SUCCESS(Status
)) CmpFreeValue(Hive
, ValueCell
);
160 CmpSetValueKeyExisting(IN PHHIVE Hive
,
161 IN HCELL_INDEX OldChild
,
162 IN PCM_KEY_VALUE Value
,
166 IN ULONG StorageType
,
169 HCELL_INDEX DataCell
, NewCell
;
172 BOOLEAN WasSmall
, IsSmall
;
174 /* Mark the old child cell dirty */
175 HvMarkCellDirty(Hive
, OldChild
, FALSE
);
177 /* See if this is a small or normal key */
178 WasSmall
= CmpIsKeyValueSmall(&Length
, Value
->DataLength
);
180 /* See if our new data can fit in a small key */
181 IsSmall
= (DataSize
<= CM_KEY_VALUE_SMALL
) ? TRUE
: FALSE
;
183 /* Big keys are unsupported */
184 ASSERT_VALUE_BIG(Hive
, Length
);
185 ASSERT_VALUE_BIG(Hive
, DataSize
);
187 /* Mark the old value dirty */
188 CmpMarkValueDataDirty(Hive
, Value
);
190 /* Check if we have a small key */
193 /* Check if we had a normal key with some data in it */
194 if (!(WasSmall
) && (Length
> 0))
196 /* Free the previous data */
197 CmpFreeValueData(Hive
, Value
->Data
, Length
);
200 /* Write our data directly */
201 Value
->DataLength
= DataSize
+ CM_KEY_VALUE_SPECIAL_SIZE
;
202 Value
->Data
= TempData
;
204 return STATUS_SUCCESS
;
208 /* We have a normal key. Was the old cell also normal and had data? */
209 if (!(WasSmall
) && (Length
> 0))
211 /* Get the current data cell and actual data inside it */
212 DataCell
= Value
->Data
;
213 ASSERT(DataCell
!= HCELL_NIL
);
214 CellData
= HvGetCell(Hive
, DataCell
);
215 if (!CellData
) return STATUS_INSUFFICIENT_RESOURCES
;
217 /* Immediately release the cell */
218 HvReleaseCell(Hive
, DataCell
);
220 /* Make sure that the data cell actually has a size */
221 ASSERT(HvGetCellSize(Hive
, CellData
) > 0);
223 /* Check if the previous data cell could fit our new data */
224 if (DataSize
<= (ULONG
)(HvGetCellSize(Hive
, CellData
)))
231 /* Otherwise, re-allocate the current data cell */
232 NewCell
= HvReallocateCell(Hive
, DataCell
, DataSize
);
233 if (NewCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
238 /* This was a small key, or a key with no data, allocate a cell */
239 NewCell
= HvAllocateCell(Hive
, DataSize
, StorageType
, HCELL_NIL
);
240 if (NewCell
== HCELL_NIL
) return STATUS_INSUFFICIENT_RESOURCES
;
243 /* Now get the actual data for our data cell */
244 CellData
= HvGetCell(Hive
, NewCell
);
245 if (!CellData
) ASSERT(FALSE
);
247 /* Release it immediately */
248 HvReleaseCell(Hive
, NewCell
);
250 /* Copy our data into the data cell's buffer, and set up the value */
251 RtlCopyMemory(CellData
, Data
, DataSize
);
252 Value
->Data
= NewCell
;
253 Value
->DataLength
= DataSize
;
257 ASSERT(HvIsCellDirty(Hive
, NewCell
));
258 return STATUS_SUCCESS
;
264 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
265 IN PUNICODE_STRING ValueName
,
272 PCM_KEY_VALUE Value
= NULL
;
273 HCELL_INDEX CurrentChild
, Cell
;
275 BOOLEAN Found
, Result
;
276 ULONG Count
, ChildIndex
, SmallData
, Storage
;
277 VALUE_SEARCH_RETURN_TYPE SearchResult
;
279 /* Acquire hive lock */
281 CmpAcquireKcbLockShared(Kcb
);
284 ASSERT(sizeof(ULONG
) == CM_KEY_VALUE_SMALL
);
286 /* Don't touch deleted KCBs */
291 Status
= STATUS_KEY_DELETED
;
295 /* Don't let anyone mess with symlinks */
296 if ((Kcb
->Flags
& KEY_SYM_LINK
) &&
297 ((Type
!= REG_LINK
) ||
299 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName
, ValueName
, TRUE
))))
301 /* Invalid modification of a symlink key */
302 Status
= STATUS_ACCESS_DENIED
;
306 /* Search for the value */
307 SearchResult
= CmpCompareNewValueDataAgainstKCBCache(Kcb
,
312 if (SearchResult
== SearchNeedExclusiveLock
)
314 /* Try again with the exclusive lock */
315 CmpConvertKcbSharedToExclusive(Kcb
);
318 else if (SearchResult
== SearchSuccess
)
320 /* We don't actually need to do anything! */
321 Status
= STATUS_SUCCESS
;
325 /* We need the exclusive KCB lock now */
326 if (!(CmpIsKcbLockedExclusive(Kcb
)) && !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
328 /* Acquire exclusive lock */
329 CmpConvertKcbSharedToExclusive(Kcb
);
332 /* Get pointer to key cell */
336 /* Prepare to scan the key node */
337 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
338 Count
= Parent
->ValueList
.Count
;
342 /* Try to find the existing name */
343 Result
= CmpFindNameInList(Hive
,
351 Status
= STATUS_INSUFFICIENT_RESOURCES
;
355 /* Check if we found something */
356 if (CurrentChild
!= HCELL_NIL
)
359 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
, CurrentChild
);
363 Status
= STATUS_INSUFFICIENT_RESOURCES
;
367 /* Remember that we found it */
373 /* No child list, we'll need to add it */
377 /* The KCB must be locked exclusive at this point */
378 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
379 (CmpTestRegistryLockExclusive() == TRUE
));
381 /* Mark the cell dirty */
382 HvMarkCellDirty(Hive
, Cell
, FALSE
);
384 /* Get the storage type */
385 Storage
= HvGetCellType(Cell
);
387 /* Check if this is small data */
389 if ((DataLength
<= CM_KEY_VALUE_SMALL
) && (DataLength
> 0))
392 RtlCopyMemory(&SmallData
, Data
, DataLength
);
395 /* Check if we didn't find a matching key */
398 /* Call the internal routine */
399 Status
= CmpSetValueKeyNew(Hive
,
411 /* Call the internal routine */
412 Status
= CmpSetValueKeyExisting(Hive
,
422 /* Check for success */
423 if (NT_SUCCESS(Status
))
425 /* Check if the maximum value name length changed */
426 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
427 if (Parent
->MaxValueNameLen
< ValueName
->Length
)
429 /* Set the new values */
430 Parent
->MaxValueNameLen
= ValueName
->Length
;
431 Kcb
->KcbMaxValueNameLen
= ValueName
->Length
;
434 /* Check if the maximum data length changed */
435 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
436 if (Parent
->MaxValueDataLen
< DataLength
)
439 Parent
->MaxValueDataLen
= DataLength
;
440 Kcb
->KcbMaxValueDataLen
= Parent
->MaxValueDataLen
;
443 /* Save the write time */
444 KeQuerySystemTime(&Parent
->LastWriteTime
);
445 KeQuerySystemTime(&Kcb
->KcbLastWriteTime
);
447 /* Check if the cell is cached */
448 if ((Found
) && (CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)))
450 /* Shouldn't happen */
455 /* Cleanup the value cache */
456 CmpCleanUpKcbValueCache(Kcb
);
459 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
460 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
462 /* Set the value cache */
463 Kcb
->ValueCache
.Count
= Parent
->ValueList
.Count
;
464 Kcb
->ValueCache
.ValueList
= Parent
->ValueList
.List
;
469 /* Release the locks */
470 CmpReleaseKcbLock(Kcb
);
477 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
478 IN UNICODE_STRING ValueName
)
480 NTSTATUS Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
483 HCELL_INDEX ChildCell
, Cell
;
484 PCHILD_LIST ChildList
;
485 PCM_KEY_VALUE Value
= NULL
;
489 /* Acquire hive lock */
492 /* Lock KCB exclusively */
493 CmpAcquireKcbLockExclusive(Kcb
);
495 /* Don't touch deleted keys */
498 /* Undo everything */
499 CmpReleaseKcbLock(Kcb
);
501 return STATUS_KEY_DELETED
;
504 /* Get the hive and the cell index */
508 /* Get the parent key node */
509 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
513 Status
= STATUS_INSUFFICIENT_RESOURCES
;
517 /* Get the value list and check if it has any entries */
518 ChildList
= &Parent
->ValueList
;
519 if (ChildList
->Count
)
521 /* Try to find this value */
522 Result
= CmpFindNameInList(Hive
,
530 Status
= STATUS_INSUFFICIENT_RESOURCES
;
534 /* Value not found, return error */
535 if (ChildCell
== HCELL_NIL
) goto Quickie
;
537 /* We found the value, mark all relevant cells dirty */
538 HvMarkCellDirty(Hive
, Cell
, FALSE
);
539 HvMarkCellDirty(Hive
, Parent
->ValueList
.List
, FALSE
);
540 HvMarkCellDirty(Hive
, ChildCell
, FALSE
);
542 /* Get the key value */
543 Value
= (PCM_KEY_VALUE
)HvGetCell(Hive
,ChildCell
);
544 if (!Value
) ASSERT(FALSE
);
546 /* Mark it and all related data as dirty */
547 CmpMarkValueDataDirty(Hive
, Value
);
550 ASSERT(HvIsCellDirty(Hive
, Parent
->ValueList
.List
));
551 ASSERT(HvIsCellDirty(Hive
, ChildCell
));
553 /* Remove the value from the child list */
554 Status
= CmpRemoveValueFromList(Hive
, ChildIndex
, ChildList
);
555 if(!NT_SUCCESS(Status
)) goto Quickie
;
557 /* Remove the value and its data itself */
558 if (!CmpFreeValue(Hive
, ChildCell
))
560 /* Failed to free the value, fail */
561 Status
= STATUS_INSUFFICIENT_RESOURCES
;
565 /* Set the last write time */
566 KeQuerySystemTime(&Parent
->LastWriteTime
);
567 KeQuerySystemTime(&Kcb
->KcbLastWriteTime
);
570 ASSERT(Parent
->MaxValueNameLen
== Kcb
->KcbMaxValueNameLen
);
571 ASSERT(Parent
->MaxValueDataLen
== Kcb
->KcbMaxValueDataLen
);
572 ASSERT(HvIsCellDirty(Hive
, Cell
));
574 /* Check if the value list is empty now */
575 if (!Parent
->ValueList
.Count
)
577 /* Then clear key node data */
578 Parent
->MaxValueNameLen
= 0;
579 Parent
->MaxValueDataLen
= 0;
580 Kcb
->KcbMaxValueNameLen
= 0;
581 Kcb
->KcbMaxValueDataLen
= 0;
584 /* Cleanup the value cache */
585 CmpCleanUpKcbValueCache(Kcb
);
588 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
589 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
591 /* Set the value cache */
592 Kcb
->ValueCache
.Count
= ChildList
->Count
;
593 Kcb
->ValueCache
.ValueList
= ChildList
->List
;
595 /* Change default Status to success */
596 Status
= STATUS_SUCCESS
;
600 /* Release the parent cell, if any */
601 if (Parent
) HvReleaseCell(Hive
, Cell
);
603 /* Check if we had a value */
606 /* Release the child cell */
607 ASSERT(ChildCell
!= HCELL_NIL
);
608 HvReleaseCell(Hive
, ChildCell
);
612 CmpReleaseKcbLock(Kcb
);
619 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
620 IN UNICODE_STRING ValueName
,
621 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
622 IN PVOID KeyValueInformation
,
624 IN PULONG ResultLength
)
627 PCM_KEY_VALUE ValueData
;
629 BOOLEAN ValueCached
= FALSE
;
630 PCM_CACHED_VALUE
*CachedValue
;
631 HCELL_INDEX CellToRelease
;
632 VALUE_SEARCH_RETURN_TYPE Result
;
636 /* Acquire hive lock */
639 /* Lock the KCB shared */
640 CmpAcquireKcbLockShared(Kcb
);
642 /* Don't touch deleted keys */
646 /* Undo everything */
647 CmpReleaseKcbLock(Kcb
);
649 return STATUS_KEY_DELETED
;
652 /* We don't deal with this yet */
653 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
655 /* Shouldn't happen */
662 /* Find the key value */
663 Result
= CmpFindValueByNameFromCache(Kcb
,
670 if (Result
== SearchNeedExclusiveLock
)
672 /* Check if we need an exclusive lock */
673 ASSERT(CellToRelease
== HCELL_NIL
);
674 ASSERT(ValueData
== NULL
);
676 /* Try with exclusive KCB lock */
677 CmpConvertKcbSharedToExclusive(Kcb
);
681 if (Result
== SearchSuccess
)
684 ASSERT(ValueData
!= NULL
);
686 /* Query the information requested */
687 Result
= CmpQueryKeyValueData(Kcb
,
691 KeyValueInformationClass
,
696 if (Result
== SearchNeedExclusiveLock
)
698 /* Try with exclusive KCB lock */
699 CmpConvertKcbSharedToExclusive(Kcb
);
705 /* Failed to find the value */
706 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
709 /* If we have a cell to release, do so */
710 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
713 CmpReleaseKcbLock(Kcb
);
720 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
722 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
723 IN PVOID KeyValueInformation
,
725 IN PULONG ResultLength
)
730 HCELL_INDEX CellToRelease
= HCELL_NIL
, CellToRelease2
= HCELL_NIL
;
731 VALUE_SEARCH_RETURN_TYPE Result
;
732 BOOLEAN IndexIsCached
, ValueIsCached
= FALSE
;
734 PCM_CACHED_VALUE
*CachedValue
;
735 PCM_KEY_VALUE ValueData
= NULL
;
738 /* Acquire hive lock */
741 /* Lock the KCB shared */
742 CmpAcquireKcbLockShared(Kcb
);
744 /* Don't touch deleted keys */
748 /* Undo everything */
749 CmpReleaseKcbLock(Kcb
);
751 return STATUS_KEY_DELETED
;
754 /* Get the hive and parent */
756 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
760 Status
= STATUS_INSUFFICIENT_RESOURCES
;
764 /* Make sure the index is valid */
765 //if (Index >= Kcb->ValueCache.Count)
766 if (Index
>= Parent
->ValueList
.Count
)
768 /* Release the cell and fail */
769 HvReleaseCell(Hive
, Kcb
->KeyCell
);
770 Status
= STATUS_NO_MORE_ENTRIES
;
774 /* We don't deal with this yet */
775 if (Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
)
777 /* Shouldn't happen */
781 /* Find the value list */
782 Result
= CmpGetValueListFromCache(Kcb
,
786 if (Result
== SearchNeedExclusiveLock
)
788 /* Check if we need an exclusive lock */
789 ASSERT(CellToRelease
== HCELL_NIL
);
790 ASSERT(ValueData
== NULL
);
792 /* Try with exclusive KCB lock */
793 CmpConvertKcbSharedToExclusive(Kcb
);
796 else if (Result
!= SearchSuccess
)
799 ASSERT(CellData
== NULL
);
801 /* Release the cell and fail */
802 Status
= STATUS_INSUFFICIENT_RESOURCES
;
806 /* Now get the key value */
807 Result
= CmpGetValueKeyFromCache(Kcb
,
815 if (Result
== SearchNeedExclusiveLock
)
817 /* Try with exclusive KCB lock */
818 CmpConvertKcbSharedToExclusive(Kcb
);
821 else if (Result
!= SearchSuccess
)
824 ASSERT(ValueData
== NULL
);
826 /* Release the cells and fail */
827 Status
= STATUS_INSUFFICIENT_RESOURCES
;
831 /* Query the information requested */
832 Result
= CmpQueryKeyValueData(Kcb
,
836 KeyValueInformationClass
,
841 if (Result
== SearchNeedExclusiveLock
)
843 /* Try with exclusive KCB lock */
844 CmpConvertKcbSharedToExclusive(Kcb
);
849 /* If we have a cell to release, do so */
850 if (CellToRelease
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease
);
852 /* Release the parent cell */
853 HvReleaseCell(Hive
, Kcb
->KeyCell
);
855 /* If we have a cell to release, do so */
856 if (CellToRelease2
!= HCELL_NIL
) HvReleaseCell(Hive
, CellToRelease2
);
859 CmpReleaseKcbLock(Kcb
);
866 CmpQueryKeyData(IN PHHIVE Hive
,
867 IN PCM_KEY_NODE Node
,
868 IN KEY_INFORMATION_CLASS KeyInformationClass
,
869 IN OUT PVOID KeyInformation
,
871 IN OUT PULONG ResultLength
)
874 ULONG Size
, SizeLeft
, MinimumSize
;
875 PKEY_INFORMATION Info
= (PKEY_INFORMATION
)KeyInformation
;
878 /* Check if the value is compressed */
879 if (Node
->Flags
& KEY_COMP_NAME
)
881 /* Get the compressed name size */
882 NameLength
= CmpCompressedNameSize(Node
->Name
, Node
->NameLength
);
886 /* Get the real size */
887 NameLength
= Node
->NameLength
;
890 /* Check what kind of information is being requested */
891 switch (KeyInformationClass
)
893 /* Basic information */
894 case KeyBasicInformation
:
896 /* This is the size we need */
897 Size
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
) + NameLength
;
899 /* And this is the minimum we can work with */
900 MinimumSize
= FIELD_OFFSET(KEY_BASIC_INFORMATION
, Name
);
902 /* Let the caller know and assume success */
903 *ResultLength
= Size
;
904 Status
= STATUS_SUCCESS
;
906 /* Check if the bufer we got is too small */
907 if (Length
< MinimumSize
)
909 /* Let the caller know and fail */
910 Status
= STATUS_BUFFER_TOO_SMALL
;
914 /* Copy the basic information */
915 Info
->KeyBasicInformation
.LastWriteTime
= Node
->LastWriteTime
;
916 Info
->KeyBasicInformation
.TitleIndex
= 0;
917 Info
->KeyBasicInformation
.NameLength
= NameLength
;
919 /* Only the name is left */
920 SizeLeft
= Length
- MinimumSize
;
923 /* Check if we don't have enough space for the name */
926 /* Truncate the name we'll return, and tell the caller */
928 Status
= STATUS_BUFFER_OVERFLOW
;
931 /* Check if this is a compressed key */
932 if (Node
->Flags
& KEY_COMP_NAME
)
934 /* Copy the compressed name */
935 CmpCopyCompressedName(Info
->KeyBasicInformation
.Name
,
942 /* Otherwise, copy the raw name */
943 RtlCopyMemory(Info
->KeyBasicInformation
.Name
,
949 /* Node information */
950 case KeyNodeInformation
:
952 /* Calculate the size we need */
953 Size
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
) +
957 /* And the minimum size we can support */
958 MinimumSize
= FIELD_OFFSET(KEY_NODE_INFORMATION
, Name
);
960 /* Return the size to the caller and assume succes */
961 *ResultLength
= Size
;
962 Status
= STATUS_SUCCESS
;
964 /* Check if the caller's buffer is too small */
965 if (Length
< MinimumSize
)
967 /* Let them know, and fail */
968 Status
= STATUS_BUFFER_TOO_SMALL
;
972 /* Copy the basic information */
973 Info
->KeyNodeInformation
.LastWriteTime
= Node
->LastWriteTime
;
974 Info
->KeyNodeInformation
.TitleIndex
= 0;
975 Info
->KeyNodeInformation
.ClassLength
= Node
->ClassLength
;
976 Info
->KeyNodeInformation
.NameLength
= NameLength
;
978 /* Now the name is left */
979 SizeLeft
= Length
- MinimumSize
;
982 /* Check if the name can fit entirely */
985 /* It can't, we'll have to truncate. Tell the caller */
987 Status
= STATUS_BUFFER_OVERFLOW
;
990 /* Check if the key node name is compressed */
991 if (Node
->Flags
& KEY_COMP_NAME
)
993 /* Copy the compressed name */
994 CmpCopyCompressedName(Info
->KeyNodeInformation
.Name
,
1001 /* It isn't, so copy the raw name */
1002 RtlCopyMemory(Info
->KeyNodeInformation
.Name
,
1007 /* Check if the node has a class */
1008 if (Node
->ClassLength
> 0)
1010 /* It does. We don't support these yet */
1011 ASSERTMSG("Classes not supported\n", FALSE
);
1015 /* It doesn't, so set offset to -1, not 0! */
1016 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
1020 /* Full information requsted */
1021 case KeyFullInformation
:
1023 /* This is the size we need */
1024 Size
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
) +
1027 /* This is what we can work with */
1028 MinimumSize
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
1030 /* Return it to caller and assume success */
1031 *ResultLength
= Size
;
1032 Status
= STATUS_SUCCESS
;
1034 /* Check if the caller's buffer is to small */
1035 if (Length
< MinimumSize
)
1037 /* Let them know and fail */
1038 Status
= STATUS_BUFFER_TOO_SMALL
;
1042 /* Now copy all the basic information */
1043 Info
->KeyFullInformation
.LastWriteTime
= Node
->LastWriteTime
;
1044 Info
->KeyFullInformation
.TitleIndex
= 0;
1045 Info
->KeyFullInformation
.ClassLength
= Node
->ClassLength
;
1046 Info
->KeyFullInformation
.SubKeys
= Node
->SubKeyCounts
[Stable
] +
1047 Node
->SubKeyCounts
[Volatile
];
1048 Info
->KeyFullInformation
.Values
= Node
->ValueList
.Count
;
1049 Info
->KeyFullInformation
.MaxNameLen
= Node
->MaxNameLen
;
1050 Info
->KeyFullInformation
.MaxClassLen
= Node
->MaxClassLen
;
1051 Info
->KeyFullInformation
.MaxValueNameLen
= Node
->MaxValueNameLen
;
1052 Info
->KeyFullInformation
.MaxValueDataLen
= Node
->MaxValueDataLen
;
1054 /* Check if we have a class */
1055 if (Node
->ClassLength
> 0)
1057 /* We do, but we currently don't support this */
1058 ASSERTMSG("Classes not supported\n", FALSE
);
1062 /* We don't have a class, so set offset to -1, not 0! */
1063 Info
->KeyNodeInformation
.ClassOffset
= 0xFFFFFFFF;
1067 /* Any other class that got sent here is invalid! */
1070 /* Set failure code */
1071 Status
= STATUS_INVALID_PARAMETER
;
1081 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1082 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1083 IN PVOID KeyInformation
,
1085 IN PULONG ResultLength
)
1089 PCM_KEY_NODE Parent
;
1091 /* Acquire hive lock */
1094 /* Lock KCB shared */
1095 CmpAcquireKcbLockShared(Kcb
);
1097 /* Get the hive and parent */
1098 Hive
= Kcb
->KeyHive
;
1099 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1103 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1107 /* Don't touch deleted keys */
1111 Status
= STATUS_KEY_DELETED
;
1115 /* Check what class we got */
1116 switch (KeyInformationClass
)
1118 /* Typical information */
1119 case KeyFullInformation
:
1120 case KeyBasicInformation
:
1121 case KeyNodeInformation
:
1123 /* Call the internal API */
1124 Status
= CmpQueryKeyData(Hive
,
1126 KeyInformationClass
,
1132 /* Unsupported classes for now */
1133 case KeyNameInformation
:
1134 case KeyCachedInformation
:
1135 case KeyFlagsInformation
:
1137 /* Print message and fail */
1138 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1139 Status
= STATUS_NOT_IMPLEMENTED
;
1142 /* Illegal classes */
1145 /* Print message and fail */
1146 DPRINT1("Unsupported class: %d!\n", KeyInformationClass
);
1147 Status
= STATUS_INVALID_INFO_CLASS
;
1153 CmpReleaseKcbLock(Kcb
);
1154 CmpUnlockRegistry();
1160 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1162 IN KEY_INFORMATION_CLASS KeyInformationClass
,
1163 IN PVOID KeyInformation
,
1165 IN PULONG ResultLength
)
1169 PCM_KEY_NODE Parent
, Child
;
1170 HCELL_INDEX ChildCell
;
1172 /* Acquire hive lock */
1175 /* Lock the KCB shared */
1176 CmpAcquireKcbLockShared(Kcb
);
1178 /* Don't touch deleted keys */
1181 /* Undo everything */
1182 CmpReleaseKcbLock(Kcb
);
1183 CmpUnlockRegistry();
1184 return STATUS_KEY_DELETED
;
1187 /* Get the hive and parent */
1188 Hive
= Kcb
->KeyHive
;
1189 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
1193 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1197 /* Get the child cell */
1198 ChildCell
= CmpFindSubKeyByNumber(Hive
, Parent
, Index
);
1200 /* Release the parent cell */
1201 HvReleaseCell(Hive
, Kcb
->KeyCell
);
1203 /* Check if we found the child */
1204 if (ChildCell
== HCELL_NIL
)
1206 /* We didn't, fail */
1207 Status
= STATUS_NO_MORE_ENTRIES
;
1211 /* Now get the actual child node */
1212 Child
= (PCM_KEY_NODE
)HvGetCell(Hive
, ChildCell
);
1216 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1220 /* Query the data requested */
1221 Status
= CmpQueryKeyData(Hive
,
1223 KeyInformationClass
,
1230 CmpReleaseKcbLock(Kcb
);
1231 CmpUnlockRegistry();
1237 CmDeleteKey(IN PCM_KEY_BODY KeyBody
)
1241 PCM_KEY_NODE Node
, Parent
;
1242 HCELL_INDEX Cell
, ParentCell
;
1243 PCM_KEY_CONTROL_BLOCK Kcb
;
1245 /* Acquire hive lock */
1249 Kcb
= KeyBody
->KeyControlBlock
;
1251 /* Don't allow deleting the root */
1252 if (!Kcb
->ParentKcb
)
1255 CmpUnlockRegistry();
1256 return STATUS_CANNOT_DELETE
;
1259 /* Lock parent and child */
1260 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1262 /* Check if we're already being deleted */
1265 /* Don't do it twice */
1266 Status
= STATUS_SUCCESS
;
1270 /* Get the hive and node */
1271 Hive
= Kcb
->KeyHive
;
1272 Cell
= Kcb
->KeyCell
;
1274 /* Get the key node */
1275 Node
= (PCM_KEY_NODE
)HvGetCell(Hive
, Cell
);
1279 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1284 ASSERT(Node
->Flags
== Kcb
->Flags
);
1286 /* Check if we don't have any children */
1287 if (!(Node
->SubKeyCounts
[Stable
] + Node
->SubKeyCounts
[Volatile
]) &&
1288 !(Node
->Flags
& KEY_NO_DELETE
))
1290 /* Get the parent and free the cell */
1291 ParentCell
= Node
->Parent
;
1292 Status
= CmpFreeKeyByCell(Hive
, Cell
, TRUE
);
1293 if (NT_SUCCESS(Status
))
1295 /* Clean up information we have on the subkey */
1296 CmpCleanUpSubKeyInfo(Kcb
->ParentKcb
);
1298 /* Get the parent node */
1299 Parent
= (PCM_KEY_NODE
)HvGetCell(Hive
, ParentCell
);
1302 /* Update the maximum name length */
1303 Kcb
->ParentKcb
->KcbMaxNameLen
= Parent
->MaxNameLen
;
1305 /* Make sure we're dirty */
1306 ASSERT(HvIsCellDirty(Hive
, ParentCell
));
1308 /* Update the write time */
1309 KeQuerySystemTime(&Parent
->LastWriteTime
);
1310 KeQuerySystemTime(&Kcb
->ParentKcb
->KcbLastWriteTime
);
1312 /* Release the cell */
1313 HvReleaseCell(Hive
, ParentCell
);
1316 /* Set the KCB in delete mode and remove it */
1318 CmpRemoveKeyControlBlock(Kcb
);
1320 /* Clear the cell */
1321 Kcb
->KeyCell
= HCELL_NIL
;
1327 Status
= STATUS_CANNOT_DELETE
;
1331 /* Release the cell */
1332 HvReleaseCell(Hive
, Cell
);
1334 /* Release the KCB locks */
1336 CmpReleaseTwoKcbLockByKey(Kcb
->ConvKey
, Kcb
->ParentKcb
->ConvKey
);
1338 /* Release hive lock */
1339 CmpUnlockRegistry();
1345 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb
,
1346 IN BOOLEAN ExclusiveLock
)
1349 NTSTATUS Status
= STATUS_SUCCESS
;
1352 /* Ignore flushes until we're ready */
1353 if (CmpNoWrite
) return STATUS_SUCCESS
;
1356 Hive
= Kcb
->KeyHive
;
1357 CmHive
= (PCMHIVE
)Hive
;
1359 /* Check if this is the master hive */
1360 if (CmHive
== CmiVolatileHive
)
1362 /* Flush all the hives instead */
1363 CmpDoFlushAll(FALSE
);
1367 /* Flush only this hive */
1368 if (!HvSyncHive(Hive
))
1371 Status
= STATUS_REGISTRY_IO_FAILED
;
1375 /* Return the status */
1381 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey
,
1382 IN POBJECT_ATTRIBUTES SourceFile
,
1384 IN PCM_KEY_BODY KeyBody
)
1386 SECURITY_QUALITY_OF_SERVICE ServiceQos
;
1387 SECURITY_CLIENT_CONTEXT ClientSecurityContext
;
1389 BOOLEAN Allocate
= TRUE
;
1393 /* Check if we have a trust key */
1397 DPRINT1("Trusted classes not yet supported\n");
1398 return STATUS_NOT_IMPLEMENTED
;
1401 /* Build a service QoS for a security context */
1402 ServiceQos
.Length
= sizeof(SECURITY_QUALITY_OF_SERVICE
);
1403 ServiceQos
.ImpersonationLevel
= SecurityImpersonation
;
1404 ServiceQos
.ContextTrackingMode
= SECURITY_DYNAMIC_TRACKING
;
1405 ServiceQos
.EffectiveOnly
= TRUE
;
1406 Status
= SeCreateClientSecurity(PsGetCurrentThread(),
1409 &ClientSecurityContext
);
1410 if (!NT_SUCCESS(Status
))
1413 DPRINT1("Security context failed\n");
1417 /* Open the target key */
1418 Status
= ZwOpenKey(&KeyHandle
, KEY_READ
, TargetKey
);
1419 if (!NT_SUCCESS(Status
)) KeyHandle
= NULL
;
1422 Status
= CmpCmdHiveOpen(SourceFile
,
1423 &ClientSecurityContext
,
1428 /* Get rid of the security context */
1429 SeDeleteClientSecurity(&ClientSecurityContext
);
1431 /* See if we failed */
1432 if (!NT_SUCCESS(Status
))
1434 /* See if the target already existed */
1437 /* Lock the registry */
1438 CmpLockRegistryExclusive();
1440 /* FIXME: Check if we are already loaded */
1442 /* Release the registry */
1443 CmpUnlockRegistry();
1446 /* Close the key handle if we had one */
1447 if (KeyHandle
) ZwClose(KeyHandle
);
1448 DPRINT1("Failed: %lx\n", Status
);
1452 /* Lock the registry shared */
1455 /* Lock the hive to this thread */
1456 CmHive
->Hive
.HiveFlags
|= HIVE_IS_UNLOADING
;
1457 CmHive
->CreatorOwner
= KeGetCurrentThread();
1460 if (Flags
& REG_NO_LAZY_FLUSH
) CmHive
->Hive
.HiveFlags
|= HIVE_NOLAZYFLUSH
;
1463 Status
= CmpLinkHiveToMaster(TargetKey
->ObjectName
,
1464 TargetKey
->RootDirectory
,
1467 TargetKey
->SecurityDescriptor
);
1468 if (NT_SUCCESS(Status
))
1470 /* FIXME: Add to HiveList key */
1472 /* Sync the hive if necessary */
1476 HvSyncHive(&CmHive
->Hive
);
1479 /* Release the hive */
1480 CmHive
->Hive
.HiveFlags
&= ~HIVE_IS_UNLOADING
;
1481 CmHive
->CreatorOwner
= NULL
;
1489 /* Unlock the registry */
1490 CmpUnlockRegistry();
1492 /* Close handle and return */
1493 if (KeyHandle
) ZwClose(KeyHandle
);