/* FUNCTIONS *****************************************************************/
+BOOLEAN
+NTAPI
+CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
+ IN POBJECT_ATTRIBUTES SourceFile,
+ OUT PCMHIVE *CmHive)
+{
+ NTSTATUS Status;
+ PCM_KEY_BODY KeyBody;
+ PCMHIVE Hive;
+ BOOLEAN Loaded = FALSE;
+ PAGED_CODE();
+
+ /* Sanity check */
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+
+ /* Reference the handle */
+ Status = ObReferenceObjectByHandle(KeyHandle,
+ 0,
+ CmpKeyObjectType,
+ KernelMode,
+ (PVOID)&KeyBody,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Loaded;
+
+ /* Don't touch deleted KCBs */
+ if (KeyBody->KeyControlBlock->Delete) return Loaded;
+
+ Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
+
+ /* Must be the root key */
+ if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
+ !(Hive->FileUserName.Buffer))
+ {
+ /* It isn't */
+ ObDereferenceObject(KeyBody);
+ return Loaded;
+ }
+
+ /* Now compare the name of the file */
+ if (!RtlCompareUnicodeString(&Hive->FileUserName,
+ SourceFile->ObjectName,
+ TRUE))
+ {
+ /* Same file found */
+ Loaded = TRUE;
+ *CmHive = Hive;
+
+ /* If the hive is frozen, not sure what to do */
+ if (Hive->Frozen)
+ {
+ /* FIXME: TODO */
+ DPRINT1("ERROR: Hive is frozen\n");
+ while (TRUE);
+ }
+ }
+
+ /* Dereference and return result */
+ ObDereferenceObject(KeyBody);
+ return Loaded;
+ }
+
BOOLEAN
NTAPI
CmpDoFlushAll(IN BOOLEAN ForceFlush)
if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
{
/* Acquire the flusher lock */
- ExAcquirePushLockExclusive((PVOID)&Hive->FlusherLock);
-
- /* Do the sync */
- Status = HvSyncHive(&Hive->Hive);
+ CmpLockHiveFlusherExclusive(Hive);
+
+ /* Check for illegal state */
+ if ((ForceFlush) && (Hive->UseCount))
+ {
+ /* Registry needs to be locked down */
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
+ DPRINT1("FIXME: Hive is damaged and needs fixup\n");
+ while (TRUE);
+ }
+
+ /* Only sync if we are forced to or if it won't cause a hive shrink */
+ if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
+ {
+ /* Do the sync */
+ Status = HvSyncHive(&Hive->Hive);
- /* If something failed - set the flag and continue looping*/
- if (!NT_SUCCESS(Status)) Result = FALSE;
+ /* If something failed - set the flag and continue looping */
+ if (!NT_SUCCESS(Status)) Result = FALSE;
+ }
+ else
+ {
+ /* We won't flush if the hive might shrink */
+ Result = FALSE;
+ CmpForceForceFlush = TRUE;
+ }
/* Release the flusher lock */
- ExReleasePushLock((PVOID)&Hive->FlusherLock);
+ CmpUnlockHiveFlusher(Hive);
}
/* Try the next entry */
{
/* Then make sure it's valid and dirty it */
ASSERT(Parent->ValueList.List != HCELL_NIL);
- HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
+ if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
+ {
+ /* Fail if we're out of space for log changes */
+ return STATUS_NO_LOG_SPACE;
+ }
}
- /* Allocate avalue cell */
+ /* Allocate a value cell */
ValueCell = HvAllocateCell(Hive,
FIELD_OFFSET(CM_KEY_VALUE, Name) +
CmpNameSize(Hive, ValueName),
/* Set it up and copy the name */
CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
- CellData->u.KeyValue.Flags = 0;
- CellData->u.KeyValue.Type = Type;
- CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
- CellData->u.KeyValue.Name,
- ValueName);
+ _SEH2_TRY
+ {
+ /* This can crash since the name is coming from user-mode */
+ CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
+ CellData->u.KeyValue.Name,
+ ValueName);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail */
+ DPRINT1("Invalid user data!\n");
+ HvFreeCell(Hive, ValueCell);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ /* Check for compressed name */
if (CellData->u.KeyValue.NameLength < ValueName->Length)
{
/* This is a compressed name */
CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
}
+ else
+ {
+ /* No flags to set */
+ CellData->u.KeyValue.Flags = 0;
+ }
/* Check if this is a normal key */
if (DataSize > CM_KEY_VALUE_SMALL)
CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
CellData->u.KeyValue.Data = SmallData;
}
+
+ /* Set the type now */
+ CellData->u.KeyValue.Type = Type;
/* Add this value cell to the child list */
Status = CmpAddValueToList(Hive,
&Parent->ValueList);
/* If we failed, free the entire cell, including the data */
- if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Overwrite the status with a known one */
+ CmpFreeValue(Hive, ValueCell);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
/* Return Status */
return Status;
PCELL_DATA CellData;
ULONG Length;
BOOLEAN WasSmall, IsSmall;
+
+ /* Registry writes must be blocked */
+ CMP_ASSERT_FLUSH_LOCK(Hive);
/* Mark the old child cell dirty */
- HvMarkCellDirty(Hive, OldChild, FALSE);
+ if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
/* See if this is a small or normal key */
WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
ASSERT_VALUE_BIG(Hive, DataSize);
/* Mark the old value dirty */
- CmpMarkValueDataDirty(Hive, Value);
+ if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
/* Check if we have a small key */
if (IsSmall)
Value->Type = Type;
return STATUS_SUCCESS;
}
- else
+
+ /* We have a normal key. Was the old cell also normal and had data? */
+ if (!(WasSmall) && (Length > 0))
{
- /* We have a normal key. Was the old cell also normal and had data? */
- if (!(WasSmall) && (Length > 0))
- {
- /* Get the current data cell and actual data inside it */
- DataCell = Value->Data;
- ASSERT(DataCell != HCELL_NIL);
- CellData = HvGetCell(Hive, DataCell);
- if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
+ /* Get the current data cell and actual data inside it */
+ DataCell = Value->Data;
+ ASSERT(DataCell != HCELL_NIL);
+ CellData = HvGetCell(Hive, DataCell);
+ if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
- /* Immediately release the cell */
- HvReleaseCell(Hive, DataCell);
+ /* Immediately release the cell */
+ HvReleaseCell(Hive, DataCell);
- /* Make sure that the data cell actually has a size */
- ASSERT(HvGetCellSize(Hive, CellData) > 0);
+ /* Make sure that the data cell actually has a size */
+ ASSERT(HvGetCellSize(Hive, CellData) > 0);
- /* Check if the previous data cell could fit our new data */
- if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
- {
- /* Re-use it then */
- NewCell = DataCell;
- }
- else
- {
- /* Otherwise, re-allocate the current data cell */
- NewCell = HvReallocateCell(Hive, DataCell, DataSize);
- if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
- }
+ /* Check if the previous data cell could fit our new data */
+ if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
+ {
+ /* Re-use it then */
+ NewCell = DataCell;
}
else
{
- /* This was a small key, or a key with no data, allocate a cell */
- NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
+ /* Otherwise, re-allocate the current data cell */
+ NewCell = HvReallocateCell(Hive, DataCell, DataSize);
if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
}
+ }
+ else
+ {
+ /* This was a small key, or a key with no data, allocate a cell */
+ NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
+ if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
+ }
- /* Now get the actual data for our data cell */
- CellData = HvGetCell(Hive, NewCell);
- if (!CellData) ASSERT(FALSE);
+ /* Now get the actual data for our data cell */
+ CellData = HvGetCell(Hive, NewCell);
+ if (!CellData) ASSERT(FALSE);
- /* Release it immediately */
- HvReleaseCell(Hive, NewCell);
+ /* Release it immediately */
+ HvReleaseCell(Hive, NewCell);
- /* Copy our data into the data cell's buffer, and set up the value */
- RtlCopyMemory(CellData, Data, DataSize);
- Value->Data = NewCell;
- Value->DataLength = DataSize;
- Value->Type = Type;
+ /* Copy our data into the data cell's buffer, and set up the value */
+ RtlCopyMemory(CellData, Data, DataSize);
+ Value->Data = NewCell;
+ Value->DataLength = DataSize;
+ Value->Type = Type;
- /* Return success */
- ASSERT(HvIsCellDirty(Hive, NewCell));
- return STATUS_SUCCESS;
- }
+ /* Return success */
+ ASSERT(HvIsCellDirty(Hive, NewCell));
+ return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
-CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
- IN PUNICODE_STRING ValueName,
- IN ULONG Type,
- IN PVOID Data,
- IN ULONG DataLength)
+CmpQueryKeyData(IN PHHIVE Hive,
+ IN PCM_KEY_NODE Node,
+ IN KEY_INFORMATION_CLASS KeyInformationClass,
+ IN OUT PVOID KeyInformation,
+ IN ULONG Length,
+ IN OUT PULONG ResultLength)
{
- PHHIVE Hive;
- PCM_KEY_NODE Parent;
- PCM_KEY_VALUE Value = NULL;
- HCELL_INDEX CurrentChild, Cell;
NTSTATUS Status;
- BOOLEAN Found, Result;
- ULONG Count, ChildIndex, SmallData, Storage;
- VALUE_SEARCH_RETURN_TYPE SearchResult;
+ ULONG Size, SizeLeft, MinimumSize;
+ PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
+ USHORT NameLength;
- /* Acquire hive lock */
- CmpLockRegistry();
- CmpAcquireKcbLockShared(Kcb);
-
- /* Sanity check */
- ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
-
- /* Don't touch deleted KCBs */
-DoAgain:
- if (Kcb->Delete)
- {
- /* Fail */
- Status = STATUS_KEY_DELETED;
- goto Quickie;
- }
-
- /* Don't let anyone mess with symlinks */
- if ((Kcb->Flags & KEY_SYM_LINK) &&
- ((Type != REG_LINK) ||
- !(ValueName) ||
- !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
- {
- /* Invalid modification of a symlink key */
- Status = STATUS_ACCESS_DENIED;
- goto Quickie;
- }
-
- /* Search for the value */
- SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
- ValueName,
- Type,
- Data,
- DataLength);
- if (SearchResult == SearchNeedExclusiveLock)
+ /* Check if the value is compressed */
+ if (Node->Flags & KEY_COMP_NAME)
{
- /* Try again with the exclusive lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
+ /* Get the compressed name size */
+ NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
}
- else if (SearchResult == SearchSuccess)
+ else
{
- /* We don't actually need to do anything! */
- Status = STATUS_SUCCESS;
- goto Quickie;
+ /* Get the real size */
+ NameLength = Node->NameLength;
}
- /* We need the exclusive KCB lock now */
- if (!(CmpIsKcbLockedExclusive(Kcb)) && !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
+ /* Check what kind of information is being requested */
+ switch (KeyInformationClass)
{
- /* Acquire exclusive lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- }
+ /* Basic information */
+ case KeyBasicInformation:
- /* Get pointer to key cell */
- Hive = Kcb->KeyHive;
- Cell = Kcb->KeyCell;
+ /* This is the size we need */
+ Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
- /* Prepare to scan the key node */
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
- Count = Parent->ValueList.Count;
- Found = FALSE;
- if (Count > 0)
- {
- /* Try to find the existing name */
- Result = CmpFindNameInList(Hive,
- &Parent->ValueList,
- ValueName,
- &ChildIndex,
- &CurrentChild);
- if (!Result)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ /* And this is the minimum we can work with */
+ MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
- /* Check if we found something */
- if (CurrentChild != HCELL_NIL)
- {
- /* Get its value */
- Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
- if (!Value)
+ /* Let the caller know and assume success */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the bufer we got is too small */
+ if (Length < MinimumSize)
{
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
+ /* Let the caller know and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
}
- /* Remember that we found it */
- Found = TRUE;
- }
- }
- else
- {
- /* No child list, we'll need to add it */
- ChildIndex = 0;
- }
-
- /* The KCB must be locked exclusive at this point */
- ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
- (CmpTestRegistryLockExclusive() == TRUE));
-
- /* Mark the cell dirty */
- HvMarkCellDirty(Hive, Cell, FALSE);
+ /* Copy the basic information */
+ Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyBasicInformation.TitleIndex = 0;
+ Info->KeyBasicInformation.NameLength = NameLength;
- /* Get the storage type */
- Storage = HvGetCellType(Cell);
+ /* Only the name is left */
+ SizeLeft = Length - MinimumSize;
+ Size = NameLength;
- /* Check if this is small data */
- SmallData = 0;
- if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
- {
- /* Copy it */
- RtlCopyMemory(&SmallData, Data, DataLength);
- }
+ /* Check if we don't have enough space for the name */
+ if (SizeLeft < Size)
+ {
+ /* Truncate the name we'll return, and tell the caller */
+ Size = SizeLeft;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
- /* Check if we didn't find a matching key */
- if (!Found)
- {
- /* Call the internal routine */
- Status = CmpSetValueKeyNew(Hive,
- Parent,
- ValueName,
- ChildIndex,
- Type,
- Data,
- DataLength,
- Storage,
- SmallData);
- }
- else
- {
- /* Call the internal routine */
- Status = CmpSetValueKeyExisting(Hive,
- CurrentChild,
- Value,
- Type,
- Data,
- DataLength,
- Storage,
- SmallData);
- }
+ /* Check if this is a compressed key */
+ if (Node->Flags & KEY_COMP_NAME)
+ {
+ /* Copy the compressed name */
+ CmpCopyCompressedName(Info->KeyBasicInformation.Name,
+ SizeLeft,
+ Node->Name,
+ Node->NameLength);
+ }
+ else
+ {
+ /* Otherwise, copy the raw name */
+ RtlCopyMemory(Info->KeyBasicInformation.Name,
+ Node->Name,
+ Size);
+ }
+ break;
- /* Check for success */
- if (NT_SUCCESS(Status))
- {
- /* Check if the maximum value name length changed */
- ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
- if (Parent->MaxValueNameLen < ValueName->Length)
- {
- /* Set the new values */
- Parent->MaxValueNameLen = ValueName->Length;
- Kcb->KcbMaxValueNameLen = ValueName->Length;
- }
-
- /* Check if the maximum data length changed */
- ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
- if (Parent->MaxValueDataLen < DataLength)
- {
- /* Update it */
- Parent->MaxValueDataLen = DataLength;
- Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
- }
+ /* Node information */
+ case KeyNodeInformation:
+
+ /* Calculate the size we need */
+ Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
+ NameLength +
+ Node->ClassLength;
+
+ /* And the minimum size we can support */
+ MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
+
+ /* Return the size to the caller and assume succes */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the caller's buffer is too small */
+ if (Length < MinimumSize)
+ {
+ /* Let them know, and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ /* Copy the basic information */
+ Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyNodeInformation.TitleIndex = 0;
+ Info->KeyNodeInformation.ClassLength = Node->ClassLength;
+ Info->KeyNodeInformation.NameLength = NameLength;
+
+ /* Now the name is left */
+ SizeLeft = Length - MinimumSize;
+ Size = NameLength;
+
+ /* Check if the name can fit entirely */
+ if (SizeLeft < Size)
+ {
+ /* It can't, we'll have to truncate. Tell the caller */
+ Size = SizeLeft;
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ /* Check if the key node name is compressed */
+ if (Node->Flags & KEY_COMP_NAME)
+ {
+ /* Copy the compressed name */
+ CmpCopyCompressedName(Info->KeyNodeInformation.Name,
+ SizeLeft,
+ Node->Name,
+ Node->NameLength);
+ }
+ else
+ {
+ /* It isn't, so copy the raw name */
+ RtlCopyMemory(Info->KeyNodeInformation.Name,
+ Node->Name,
+ Size);
+ }
+
+ /* Check if the node has a class */
+ if (Node->ClassLength > 0)
+ {
+ /* It does. We don't support these yet */
+ ASSERTMSG("Classes not supported\n", FALSE);
+ }
+ else
+ {
+ /* It doesn't, so set offset to -1, not 0! */
+ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+ }
+ break;
+
+ /* Full information requsted */
+ case KeyFullInformation:
+
+ /* This is the size we need */
+ Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
+ Node->ClassLength;
+
+ /* This is what we can work with */
+ MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+
+ /* Return it to caller and assume success */
+ *ResultLength = Size;
+ Status = STATUS_SUCCESS;
+
+ /* Check if the caller's buffer is to small */
+ if (Length < MinimumSize)
+ {
+ /* Let them know and fail */
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ /* Now copy all the basic information */
+ Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
+ Info->KeyFullInformation.TitleIndex = 0;
+ Info->KeyFullInformation.ClassLength = Node->ClassLength;
+ Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
+ Node->SubKeyCounts[Volatile];
+ Info->KeyFullInformation.Values = Node->ValueList.Count;
+ Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
+ Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
+ Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
+ Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
+
+ /* Check if we have a class */
+ if (Node->ClassLength > 0)
+ {
+ /* We do, but we currently don't support this */
+ ASSERTMSG("Classes not supported\n", FALSE);
+ }
+ else
+ {
+ /* We don't have a class, so set offset to -1, not 0! */
+ Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
+ }
+ break;
+
+ /* Any other class that got sent here is invalid! */
+ default:
+
+ /* Set failure code */
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Return status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
+ IN PUNICODE_STRING ValueName,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataLength)
+{
+ PHHIVE Hive = NULL;
+ PCM_KEY_NODE Parent;
+ PCM_KEY_VALUE Value = NULL;
+ HCELL_INDEX CurrentChild, Cell;
+ NTSTATUS Status;
+ BOOLEAN Found, Result;
+ ULONG Count, ChildIndex, SmallData, Storage;
+ VALUE_SEARCH_RETURN_TYPE SearchResult;
+ BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
+ HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
+
+ /* Acquire hive and KCB lock */
+ CmpLockRegistry();
+ CmpAcquireKcbLockShared(Kcb);
+
+ /* Sanity check */
+ ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
+
+ /* Don't touch deleted KCBs */
+DoAgain:
+ if (Kcb->Delete)
+ {
+ /* Fail */
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
+ }
+
+ /* Don't let anyone mess with symlinks */
+ if ((Kcb->Flags & KEY_SYM_LINK) &&
+ ((Type != REG_LINK) ||
+ !(ValueName) ||
+ !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
+ {
+ /* Invalid modification of a symlink key */
+ Status = STATUS_ACCESS_DENIED;
+ goto Quickie;
+ }
+
+ /* Check if this is the first attempt */
+ if (FirstTry)
+ {
+ /* Search for the value in the cache */
+ SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
+ ValueName,
+ Type,
+ Data,
+ DataLength);
+ if (SearchResult == SearchNeedExclusiveLock)
+ {
+ /* Try again with the exclusive lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (SearchResult == SearchSuccess)
+ {
+ /* We don't actually need to do anything! */
+ Status = STATUS_SUCCESS;
+ goto Quickie;
+ }
+
+ /* We need the exclusive KCB lock now */
+ if (!(CmpIsKcbLockedExclusive(Kcb)) &&
+ !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
+ {
+ /* Acquire exclusive lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ }
+
+ /* Cache lookup failed, so don't try it next time */
+ FirstTry = FALSE;
+
+ /* Now grab the flush lock since the key will be modified */
+ ASSERT(FlusherLocked == FALSE);
+ CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
+ FlusherLocked = TRUE;
+ goto DoAgain;
+ }
+ else
+ {
+ /* Get pointer to key cell */
+ Hive = Kcb->KeyHive;
+ Cell = Kcb->KeyCell;
+
+ /* Get the parent */
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
+ ASSERT(Parent);
+ ParentCell = Cell;
+
+ /* Prepare to scan the key node */
+ Count = Parent->ValueList.Count;
+ Found = FALSE;
+ if (Count > 0)
+ {
+ /* Try to find the existing name */
+ Result = CmpFindNameInList(Hive,
+ &Parent->ValueList,
+ ValueName,
+ &ChildIndex,
+ &CurrentChild);
+ if (!Result)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Check if we found something */
+ if (CurrentChild != HCELL_NIL)
+ {
+ /* Release existing child */
+ if (ChildCell != HCELL_NIL)
+ {
+ HvReleaseCell(Hive, ChildCell);
+ ChildCell = HCELL_NIL;
+ }
+
+ /* Get its value */
+ Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
+ if (!Value)
+ {
+ /* Fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* Remember that we found it */
+ ChildCell = CurrentChild;
+ Found = TRUE;
+ }
+ }
+ else
+ {
+ /* No child list, we'll need to add it */
+ ChildIndex = 0;
+ }
+ }
+
+ /* Should only get here on the second pass */
+ ASSERT(FirstTry == FALSE);
+
+ /* The KCB must be locked exclusive at this point */
+ CMP_ASSERT_KCB_LOCK(Kcb);
+
+ /* Mark the cell dirty */
+ if (!HvMarkCellDirty(Hive, Cell, FALSE))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
+
+ /* Get the storage type */
+ Storage = HvGetCellType(Cell);
+
+ /* Check if this is small data */
+ SmallData = 0;
+ if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
+ {
+ /* Need SEH because user data may be invalid */
+ _SEH2_TRY
+ {
+ /* Copy it */
+ RtlCopyMemory(&SmallData, Data, DataLength);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Return failure code */
+ Status = _SEH2_GetExceptionCode();
+ _SEH2_YIELD(goto Quickie);
+ }
+ _SEH2_END;
+ }
+
+ /* Check if we didn't find a matching key */
+ if (!Found)
+ {
+ /* Call the internal routine */
+ Status = CmpSetValueKeyNew(Hive,
+ Parent,
+ ValueName,
+ ChildIndex,
+ Type,
+ Data,
+ DataLength,
+ Storage,
+ SmallData);
+ }
+ else
+ {
+ /* Call the internal routine */
+ Status = CmpSetValueKeyExisting(Hive,
+ CurrentChild,
+ Value,
+ Type,
+ Data,
+ DataLength,
+ Storage,
+ SmallData);
+ }
+
+ /* Check for success */
+ if (NT_SUCCESS(Status))
+ {
+ /* Check if the maximum value name length changed */
+ ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
+ if (Parent->MaxValueNameLen < ValueName->Length)
+ {
+ /* Set the new values */
+ Parent->MaxValueNameLen = ValueName->Length;
+ Kcb->KcbMaxValueNameLen = ValueName->Length;
+ }
+
+ /* Check if the maximum data length changed */
+ ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
+ if (Parent->MaxValueDataLen < DataLength)
+ {
+ /* Update it */
+ Parent->MaxValueDataLen = DataLength;
+ Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
+ }
/* Save the write time */
KeQuerySystemTime(&Parent->LastWriteTime);
- KeQuerySystemTime(&Kcb->KcbLastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Check if the cell is cached */
if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
Kcb->ValueCache.Count = Parent->ValueList.Count;
Kcb->ValueCache.ValueList = Parent->ValueList.List;
}
+
+ /* Notify registered callbacks */
+ CmpReportNotify(Kcb,
+ Hive,
+ Kcb->KeyCell,
+ REG_NOTIFY_CHANGE_LAST_SET);
}
+ /* Release the cells */
Quickie:
+ if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
+ if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
+
/* Release the locks */
+ if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
return Status;
/* Get the hive and the cell index */
Hive = Kcb->KeyHive;
Cell = Kcb->KeyCell;
+
+ /* Lock flushes */
+ CmpLockHiveFlusherShared((PCMHIVE)Hive);
/* Get the parent key node */
Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Parent);
/* Get the value list and check if it has any entries */
ChildList = &Parent->ValueList;
if (ChildCell == HCELL_NIL) goto Quickie;
/* We found the value, mark all relevant cells dirty */
- HvMarkCellDirty(Hive, Cell, FALSE);
- HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
- HvMarkCellDirty(Hive, ChildCell, FALSE);
+ if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
+ (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
+ (HvMarkCellDirty(Hive, ChildCell, FALSE))))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
/* Get the key value */
Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
- if (!Value) ASSERT(FALSE);
+ ASSERT(Value);
/* Mark it and all related data as dirty */
- CmpMarkValueDataDirty(Hive, Value);
+ if (!CmpMarkValueDataDirty(Hive, Value))
+ {
+ /* Not enough log space, fail */
+ Status = STATUS_NO_LOG_SPACE;
+ goto Quickie;
+ }
/* Ssanity checks */
ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
/* Remove the value from the child list */
Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
- if(!NT_SUCCESS(Status)) goto Quickie;
+ if (!NT_SUCCESS(Status))
+ {
+ /* Set known error */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
/* Remove the value and its data itself */
if (!CmpFreeValue(Hive, ChildCell))
/* Set the last write time */
KeQuerySystemTime(&Parent->LastWriteTime);
- KeQuerySystemTime(&Kcb->KcbLastWriteTime);
+ Kcb->KcbLastWriteTime = Parent->LastWriteTime;
/* Sanity check */
ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
/* Set the value cache */
Kcb->ValueCache.Count = ChildList->Count;
Kcb->ValueCache.ValueList = ChildList->List;
+
+ /* Notify registered callbacks */
+ CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
/* Change default Status to success */
Status = STATUS_SUCCESS;
}
/* Release locks */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
return Status;
/* Sanity check */
ASSERT(ValueData != NULL);
- /* Query the information requested */
- Result = CmpQueryKeyValueData(Kcb,
- CachedValue,
- ValueData,
- ValueCached,
- KeyValueInformationClass,
- KeyValueInformation,
- Length,
- ResultLength,
- &Status);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
+ /* User data, protect against exceptions */
+ _SEH2_TRY
+ {
+ /* Query the information requested */
+ Result = CmpQueryKeyValueData(Kcb,
+ CachedValue,
+ ValueData,
+ ValueCached,
+ KeyValueInformationClass,
+ KeyValueInformation,
+ Length,
+ ResultLength,
+ &Status);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Release the value cell */
+ if (CellToRelease != HCELL_NIL)
+ {
+ HvReleaseCell(Hive, CellToRelease);
+ CellToRelease = HCELL_NIL;
+ }
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
}
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
}
else
{
PCM_KEY_VALUE ValueData = NULL;
PAGED_CODE();
- /* Acquire hive lock */
- CmpLockRegistry();
-
- /* Lock the KCB shared */
- CmpAcquireKcbLockShared(Kcb);
-
- /* Don't touch deleted keys */
-DoAgain:
- if (Kcb->Delete)
- {
- /* Undo everything */
- CmpReleaseKcbLock(Kcb);
- CmpUnlockRegistry();
- return STATUS_KEY_DELETED;
- }
-
- /* Get the hive and parent */
- Hive = Kcb->KeyHive;
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
- /* Make sure the index is valid */
- //if (Index >= Kcb->ValueCache.Count)
- if (Index >= Parent->ValueList.Count)
- {
- /* Release the cell and fail */
- HvReleaseCell(Hive, Kcb->KeyCell);
- Status = STATUS_NO_MORE_ENTRIES;
- goto Quickie;
- }
-
- /* We don't deal with this yet */
- if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
- {
- /* Shouldn't happen */
- ASSERT(FALSE);
- }
-
- /* Find the value list */
- Result = CmpGetValueListFromCache(Kcb,
- &CellData,
- &IndexIsCached,
- &CellToRelease);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Check if we need an exclusive lock */
- ASSERT(CellToRelease == HCELL_NIL);
- ASSERT(ValueData == NULL);
-
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
- }
- else if (Result != SearchSuccess)
- {
- /* Sanity check */
- ASSERT(CellData == NULL);
-
- /* Release the cell and fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
- /* Now get the key value */
- Result = CmpGetValueKeyFromCache(Kcb,
- CellData,
- Index,
- &CachedValue,
- &ValueData,
- IndexIsCached,
- &ValueIsCached,
- &CellToRelease2);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
- }
- else if (Result != SearchSuccess)
- {
- /* Sanity check */
- ASSERT(ValueData == NULL);
-
- /* Release the cells and fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
- /* Query the information requested */
- Result = CmpQueryKeyValueData(Kcb,
- CachedValue,
- ValueData,
- ValueIsCached,
- KeyValueInformationClass,
- KeyValueInformation,
- Length,
- ResultLength,
- &Status);
- if (Result == SearchNeedExclusiveLock)
- {
- /* Try with exclusive KCB lock */
- CmpConvertKcbSharedToExclusive(Kcb);
- goto DoAgain;
- }
-
-Quickie:
- /* If we have a cell to release, do so */
- if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
-
- /* Release the parent cell */
- HvReleaseCell(Hive, Kcb->KeyCell);
-
- /* If we have a cell to release, do so */
- if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
-
- /* Release locks */
- CmpReleaseKcbLock(Kcb);
- CmpUnlockRegistry();
- return Status;
-}
-
-NTSTATUS
-NTAPI
-CmpQueryKeyData(IN PHHIVE Hive,
- IN PCM_KEY_NODE Node,
- IN KEY_INFORMATION_CLASS KeyInformationClass,
- IN OUT PVOID KeyInformation,
- IN ULONG Length,
- IN OUT PULONG ResultLength)
-{
- NTSTATUS Status;
- ULONG Size, SizeLeft, MinimumSize;
- PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
- USHORT NameLength;
-
- /* Check if the value is compressed */
- if (Node->Flags & KEY_COMP_NAME)
- {
- /* Get the compressed name size */
- NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
- }
- else
- {
- /* Get the real size */
- NameLength = Node->NameLength;
- }
-
- /* Check what kind of information is being requested */
- switch (KeyInformationClass)
- {
- /* Basic information */
- case KeyBasicInformation:
-
- /* This is the size we need */
- Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
-
- /* And this is the minimum we can work with */
- MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
-
- /* Let the caller know and assume success */
- *ResultLength = Size;
- Status = STATUS_SUCCESS;
-
- /* Check if the bufer we got is too small */
- if (Length < MinimumSize)
- {
- /* Let the caller know and fail */
- Status = STATUS_BUFFER_TOO_SMALL;
- break;
- }
-
- /* Copy the basic information */
- Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
- Info->KeyBasicInformation.TitleIndex = 0;
- Info->KeyBasicInformation.NameLength = NameLength;
-
- /* Only the name is left */
- SizeLeft = Length - MinimumSize;
- Size = NameLength;
-
- /* Check if we don't have enough space for the name */
- if (SizeLeft < Size)
- {
- /* Truncate the name we'll return, and tell the caller */
- Size = SizeLeft;
- Status = STATUS_BUFFER_OVERFLOW;
- }
-
- /* Check if this is a compressed key */
- if (Node->Flags & KEY_COMP_NAME)
- {
- /* Copy the compressed name */
- CmpCopyCompressedName(Info->KeyBasicInformation.Name,
- SizeLeft,
- Node->Name,
- Node->NameLength);
- }
- else
- {
- /* Otherwise, copy the raw name */
- RtlCopyMemory(Info->KeyBasicInformation.Name,
- Node->Name,
- Size);
- }
- break;
-
- /* Node information */
- case KeyNodeInformation:
-
- /* Calculate the size we need */
- Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
- NameLength +
- Node->ClassLength;
-
- /* And the minimum size we can support */
- MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
-
- /* Return the size to the caller and assume succes */
- *ResultLength = Size;
- Status = STATUS_SUCCESS;
-
- /* Check if the caller's buffer is too small */
- if (Length < MinimumSize)
- {
- /* Let them know, and fail */
- Status = STATUS_BUFFER_TOO_SMALL;
- break;
- }
-
- /* Copy the basic information */
- Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
- Info->KeyNodeInformation.TitleIndex = 0;
- Info->KeyNodeInformation.ClassLength = Node->ClassLength;
- Info->KeyNodeInformation.NameLength = NameLength;
-
- /* Now the name is left */
- SizeLeft = Length - MinimumSize;
- Size = NameLength;
-
- /* Check if the name can fit entirely */
- if (SizeLeft < Size)
- {
- /* It can't, we'll have to truncate. Tell the caller */
- Size = SizeLeft;
- Status = STATUS_BUFFER_OVERFLOW;
- }
-
- /* Check if the key node name is compressed */
- if (Node->Flags & KEY_COMP_NAME)
- {
- /* Copy the compressed name */
- CmpCopyCompressedName(Info->KeyNodeInformation.Name,
- SizeLeft,
- Node->Name,
- Node->NameLength);
- }
- else
- {
- /* It isn't, so copy the raw name */
- RtlCopyMemory(Info->KeyNodeInformation.Name,
- Node->Name,
- Size);
- }
+ /* Acquire hive lock */
+ CmpLockRegistry();
+
+ /* Lock the KCB shared */
+ CmpAcquireKcbLockShared(Kcb);
- /* Check if the node has a class */
- if (Node->ClassLength > 0)
- {
- /* It does. We don't support these yet */
- ASSERTMSG("Classes not supported\n", FALSE);
- }
- else
- {
- /* It doesn't, so set offset to -1, not 0! */
- Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
- }
- break;
+ /* Don't touch deleted keys */
+DoAgain:
+ if (Kcb->Delete)
+ {
+ /* Undo everything */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
+ return STATUS_KEY_DELETED;
+ }
- /* Full information requsted */
- case KeyFullInformation:
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
- /* This is the size we need */
- Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
- Node->ClassLength;
+ /* FIXME: Lack of cache? */
+ if (Kcb->ValueCache.Count != Parent->ValueList.Count)
+ {
+ DPRINT1("HACK: Overriding value cache count\n");
+ Kcb->ValueCache.Count = Parent->ValueList.Count;
+ }
- /* This is what we can work with */
- MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
+ /* Make sure the index is valid */
+ if (Index >= Kcb->ValueCache.Count)
+ {
+ /* Release the cell and fail */
+ HvReleaseCell(Hive, Kcb->KeyCell);
+ Status = STATUS_NO_MORE_ENTRIES;
+ goto Quickie;
+ }
+
+ /* We don't deal with this yet */
+ if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
+ {
+ /* Shouldn't happen */
+ ASSERT(FALSE);
+ }
- /* Return it to caller and assume success */
- *ResultLength = Size;
- Status = STATUS_SUCCESS;
+ /* Find the value list */
+ Result = CmpGetValueListFromCache(Kcb,
+ &CellData,
+ &IndexIsCached,
+ &CellToRelease);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Check if we need an exclusive lock */
+ ASSERT(CellToRelease == HCELL_NIL);
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (Result != SearchSuccess)
+ {
+ /* Sanity check */
+ ASSERT(CellData == NULL);
- /* Check if the caller's buffer is to small */
- if (Length < MinimumSize)
- {
- /* Let them know and fail */
- Status = STATUS_BUFFER_TOO_SMALL;
- break;
- }
+ /* Release the cell and fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
- /* Now copy all the basic information */
- Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
- Info->KeyFullInformation.TitleIndex = 0;
- Info->KeyFullInformation.ClassLength = Node->ClassLength;
- Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
- Node->SubKeyCounts[Volatile];
- Info->KeyFullInformation.Values = Node->ValueList.Count;
- Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
- Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
- Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
- Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
+ /* Now get the key value */
+ Result = CmpGetValueKeyFromCache(Kcb,
+ CellData,
+ Index,
+ &CachedValue,
+ &ValueData,
+ IndexIsCached,
+ &ValueIsCached,
+ &CellToRelease2);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Cleanup state */
+ ASSERT(CellToRelease2 == HCELL_NIL);
+ if (CellToRelease)
+ {
+ HvReleaseCell(Hive, CellToRelease);
+ CellToRelease = HCELL_NIL;
+ }
+ HvReleaseCell(Hive, Kcb->KeyCell);
- /* Check if we have a class */
- if (Node->ClassLength > 0)
- {
- /* We do, but we currently don't support this */
- ASSERTMSG("Classes not supported\n", FALSE);
- }
- else
- {
- /* We don't have a class, so set offset to -1, not 0! */
- Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
- }
- break;
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ else if (Result != SearchSuccess)
+ {
+ /* Sanity check */
+ ASSERT(ValueData == NULL);
- /* Any other class that got sent here is invalid! */
- default:
+ /* Release the cells and fail */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Quickie;
+ }
+
+ /* User data, need SEH */
+ _SEH2_TRY
+ {
+ /* Query the information requested */
+ Result = CmpQueryKeyValueData(Kcb,
+ CachedValue,
+ ValueData,
+ ValueIsCached,
+ KeyValueInformationClass,
+ KeyValueInformation,
+ Length,
+ ResultLength,
+ &Status);
+ if (Result == SearchNeedExclusiveLock)
+ {
+ /* Cleanup state */
+ if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
+ HvReleaseCell(Hive, Kcb->KeyCell);
+ if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
- /* Set failure code */
- Status = STATUS_INVALID_PARAMETER;
- break;
+ /* Try with exclusive KCB lock */
+ CmpConvertKcbSharedToExclusive(Kcb);
+ goto DoAgain;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Get exception code */
+ Status = _SEH2_GetExceptionCode();
}
+ _SEH2_END;
- /* Return status */
+Quickie:
+ /* If we have a cell to release, do so */
+ if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
+
+ /* Release the parent cell */
+ HvReleaseCell(Hive, Kcb->KeyCell);
+
+ /* If we have a cell to release, do so */
+ if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
+
+ /* Release locks */
+ CmpReleaseKcbLock(Kcb);
+ CmpUnlockRegistry();
return Status;
}
NTSTATUS Status;
PHHIVE Hive;
PCM_KEY_NODE Parent;
+ HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */
CmpLockRegistry();
/* Lock KCB shared */
CmpAcquireKcbLockShared(Kcb);
- /* Get the hive and parent */
- Hive = Kcb->KeyHive;
- Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
-
/* Don't touch deleted keys */
if (Kcb->Delete)
{
case KeyBasicInformation:
case KeyNodeInformation:
- /* Call the internal API */
- Status = CmpQueryKeyData(Hive,
- Parent,
- KeyInformationClass,
- KeyInformation,
- Length,
- ResultLength);
+ /* Get the hive and parent */
+ Hive = Kcb->KeyHive;
+ Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
+ ASSERT(Parent);
+
+ /* Track cell references */
+ if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
+ {
+ /* Not enough memory to track references */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ /* Call the internal API */
+ Status = CmpQueryKeyData(Hive,
+ Parent,
+ KeyInformationClass,
+ KeyInformation,
+ Length,
+ ResultLength);
+ }
break;
/* Unsupported classes for now */
}
Quickie:
+ /* Release references */
+ HvReleaseFreeCellRefArray(&CellReferences);
+
/* Release locks */
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
PHHIVE Hive;
PCM_KEY_NODE Parent, Child;
HCELL_INDEX ChildCell;
+ HV_TRACK_CELL_REF CellReferences = {0};
/* Acquire hive lock */
CmpLockRegistry();
if (Kcb->Delete)
{
/* Undo everything */
- CmpReleaseKcbLock(Kcb);
- CmpUnlockRegistry();
- return STATUS_KEY_DELETED;
+ Status = STATUS_KEY_DELETED;
+ goto Quickie;
}
/* Get the hive and parent */
Hive = Kcb->KeyHive;
Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
- if (!Parent)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Parent);
/* Get the child cell */
ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
/* Now get the actual child node */
Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
- if (!Child)
+ ASSERT(Child);
+
+ /* Track references */
+ if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
{
- /* Fail */
+ /* Can't allocate memory for tracking */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Quickie;
}
- /* Query the data requested */
- Status = CmpQueryKeyData(Hive,
- Child,
- KeyInformationClass,
- KeyInformation,
- Length,
- ResultLength);
+ /* Data can be user-mode, use SEH */
+ _SEH2_TRY
+ {
+ /* Query the data requested */
+ Status = CmpQueryKeyData(Hive,
+ Child,
+ KeyInformationClass,
+ KeyInformation,
+ Length,
+ ResultLength);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail with exception code */
+ Status = _SEH2_GetExceptionCode();
+ _SEH2_YIELD(goto Quickie);
+ }
+ _SEH2_END;
Quickie:
+ /* Release references */
+ HvReleaseFreeCellRefArray(&CellReferences);
+
/* Release locks */
CmpReleaseKcbLock(Kcb);
CmpUnlockRegistry();
Hive = Kcb->KeyHive;
Cell = Kcb->KeyCell;
+ /* Lock flushes */
+ CmpLockHiveFlusherShared((PCMHIVE)Hive);
+
/* Get the key node */
Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
- if (!Node)
- {
- /* Fail */
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto Quickie;
- }
+ ASSERT(Node);
/* Sanity check */
ASSERT(Node->Flags == Kcb->Flags);
if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
!(Node->Flags & KEY_NO_DELETE))
{
+ /* Send notification to registered callbacks */
+ CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
+
/* Get the parent and free the cell */
ParentCell = Node->Parent;
Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
if (NT_SUCCESS(Status))
{
+ /* Flush any notifications */
+ CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
+
/* Clean up information we have on the subkey */
CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
Status = STATUS_CANNOT_DELETE;
}
-Quickie:
/* Release the cell */
HvReleaseCell(Hive, Cell);
-
+
+ /* Release flush lock */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
+
/* Release the KCB locks */
Quickie2:
CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
}
else
{
+ /* Don't touch the hive */
+ CmpLockHiveFlusherExclusive(CmHive);
+ ASSERT(CmHive->ViewLock);
+ KeAcquireGuardedMutex(CmHive->ViewLock);
+ CmHive->ViewLockOwner = KeGetCurrentThread();
+
+ /* Will the hive shrink? */
+ if (HvHiveWillShrink(Hive))
+ {
+ /* I don't believe the current Hv does shrinking */
+ ASSERT(FALSE);
+ }
+ else
+ {
+ /* Now we can release views */
+ ASSERT(CmHive->ViewLock);
+ CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
+ ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
+ KeReleaseGuardedMutex(CmHive->ViewLock);
+ }
+
/* Flush only this hive */
if (!HvSyncHive(Hive))
{
/* Fail */
Status = STATUS_REGISTRY_IO_FAILED;
}
+
+ /* Release the flush lock */
+ CmpUnlockHiveFlusher((PCMHIVE)Hive);
}
/* Return the status */
SECURITY_CLIENT_CONTEXT ClientSecurityContext;
HANDLE KeyHandle;
BOOLEAN Allocate = TRUE;
- PCMHIVE CmHive;
+ PCMHIVE CmHive, LoadedHive;
NTSTATUS Status;
+ CM_PARSE_CONTEXT ParseContext;
/* Check if we have a trust key */
if (KeyBody)
}
/* Open the target key */
+#if 0
Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
+#else
+ RtlZeroMemory(&ParseContext, sizeof(ParseContext));
+ ParseContext.CreateOperation = FALSE;
+ Status = ObOpenObjectByName(TargetKey,
+ CmpKeyObjectType,
+ KernelMode,
+ NULL,
+ KEY_READ,
+ &ParseContext,
+ &KeyHandle);
+#endif
if (!NT_SUCCESS(Status)) KeyHandle = NULL;
-
+
/* Open the hive */
Status = CmpCmdHiveOpen(SourceFile,
&ClientSecurityContext,
/* Lock the registry */
CmpLockRegistryExclusive();
- /* FIXME: Check if we are already loaded */
-
+ /* Check if we are already loaded */
+ if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
+ {
+ /* That's okay then */
+ ASSERT(LoadedHive);
+ Status = STATUS_SUCCESS;
+ }
+
/* Release the registry */
CmpUnlockRegistry();
}
/* Close the key handle if we had one */
if (KeyHandle) ZwClose(KeyHandle);
- DPRINT1("Failed: %lx\n", Status);
return Status;
}
/* Lock the registry shared */
CmpLockRegistry();
+ /* Lock loading */
+ ExAcquirePushLockExclusive(&CmpLoadHiveLock);
+
/* Lock the hive to this thread */
CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
CmHive->CreatorOwner = KeGetCurrentThread();
TargetKey->SecurityDescriptor);
if (NT_SUCCESS(Status))
{
- /* FIXME: Add to HiveList key */
+ /* Add to HiveList key */
+ CmpAddToHiveFileList(CmHive);
/* Sync the hive if necessary */
if (Allocate)
{
- /* Sync it */
+ /* Sync it under the flusher lock */
+ CmpLockHiveFlusherExclusive(CmHive);
HvSyncHive(&CmHive->Hive);
+ CmpUnlockHiveFlusher(CmHive);
}
/* Release the hive */
CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
CmHive->CreatorOwner = NULL;
+
+ /* Allow loads */
+ ExReleasePushLock(&CmpLoadHiveLock);
}
else
{
/* FIXME: TODO */
-
+ ASSERT(FALSE);
+ }
+
+ /* Is this first profile load? */
+ if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
+ {
+ /* User is now logged on, set quotas */
+ CmpProfileLoaded = TRUE;
+ CmpSetGlobalQuotaAllowed();
}
/* Unlock the registry */