/* * PROJECT: ReactOS Kernel * LICENSE: GPL - See COPYING in the top level directory * FILE: ntoskrnl/config/cmparse.c * PURPOSE: Configuration Manager - Object Manager Parse Interface * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include "ntoskrnl.h" #define NDEBUG #include "debug.h" /* GLOBALS *******************************************************************/ /* FUNCTIONS *****************************************************************/ BOOLEAN NTAPI CmpGetNextName(IN OUT PUNICODE_STRING RemainingName, OUT PUNICODE_STRING NextName, OUT PBOOLEAN LastName) { BOOLEAN NameValid = TRUE; ASSERT(RemainingName->Length % sizeof(WCHAR) == 0); /* Check if there's nothing left in the name */ if (!(RemainingName->Buffer) || (!RemainingName->Length) || !(*RemainingName->Buffer)) { /* Clear the next name and set this as last */ *LastName = TRUE; NextName->Buffer = NULL; NextName->Length = 0; return TRUE; } /* Check if we have a path separator */ while ((RemainingName->Length) && (*RemainingName->Buffer == OBJ_NAME_PATH_SEPARATOR)) { /* Skip it */ RemainingName->Buffer++; RemainingName->Length -= sizeof(WCHAR); RemainingName->MaximumLength -= sizeof(WCHAR); } /* Start loop at where the current buffer is */ NextName->Buffer = RemainingName->Buffer; while ((RemainingName->Length) && (*RemainingName->Buffer != OBJ_NAME_PATH_SEPARATOR)) { /* Move to the next character */ RemainingName->Buffer++; RemainingName->Length -= sizeof(WCHAR); RemainingName->MaximumLength -= sizeof(WCHAR); } /* See how many chars we parsed and validate the length */ NextName->Length = (USHORT)((ULONG_PTR)RemainingName->Buffer - (ULONG_PTR)NextName->Buffer); if (NextName->Length > 512) NameValid = FALSE; NextName->MaximumLength = NextName->Length; /* If there's nothing left, we're last */ *LastName = !RemainingName->Length; return NameValid; } BOOLEAN NTAPI CmpGetSymbolicLink(IN PHHIVE Hive, IN OUT PUNICODE_STRING ObjectName, IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb, IN PUNICODE_STRING RemainingName OPTIONAL) { HCELL_INDEX LinkCell = HCELL_NIL; PCM_KEY_VALUE LinkValue = NULL; PWSTR LinkName = NULL; BOOLEAN LinkNameAllocated = FALSE; PWSTR NewBuffer; ULONG Length = 0; ULONG ValueLength = 0; BOOLEAN Result = FALSE; HCELL_INDEX CellToRelease = HCELL_NIL; PCM_KEY_NODE Node; UNICODE_STRING NewObjectName; /* Make sure we're not being deleted */ if (SymbolicKcb->Delete) return FALSE; /* Get the key node */ Node = (PCM_KEY_NODE)HvGetCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell); if (!Node) goto Exit; /* Find the symbolic link key */ LinkCell = CmpFindValueByName(Hive, Node, &CmSymbolicLinkValueName); HvReleaseCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell); if (LinkCell == HCELL_NIL) goto Exit; /* Get the value cell */ LinkValue = (PCM_KEY_VALUE)HvGetCell(Hive, LinkCell); if (!LinkValue) goto Exit; /* Make sure it's a registry link */ if (LinkValue->Type != REG_LINK) goto Exit; /* Now read the value data */ if (!CmpGetValueData(Hive, LinkValue, &ValueLength, (PVOID*)&LinkName, &LinkNameAllocated, &CellToRelease)) { /* Fail */ goto Exit; } /* Get the length */ Length = ValueLength + sizeof(WCHAR); /* Make sure we start with a slash */ if (*LinkName != OBJ_NAME_PATH_SEPARATOR) goto Exit; /* Add the remaining name if needed */ if (RemainingName) Length += RemainingName->Length + sizeof(WCHAR); /* Check for overflow */ if (Length > 0xFFFF) goto Exit; /* Check if we need a new buffer */ if (Length > ObjectName->MaximumLength) { /* We do -- allocate one */ NewBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); if (!NewBuffer) goto Exit; /* Setup the new string and copy the symbolic target */ NewObjectName.Buffer = NewBuffer; NewObjectName.MaximumLength = (USHORT)Length; NewObjectName.Length = (USHORT)ValueLength; RtlCopyMemory(NewBuffer, LinkName, ValueLength); /* Check if we need to add anything else */ if (RemainingName) { /* Add the remaining name */ NewBuffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; NewObjectName.Length += sizeof(WCHAR); RtlAppendUnicodeStringToString(&NewObjectName, RemainingName); } /* Free the old buffer */ ExFreePool(ObjectName->Buffer); *ObjectName = NewObjectName; } else { /* The old name is large enough -- update the length */ ObjectName->Length = (USHORT)ValueLength; if (RemainingName) { /* Copy the remaining name inside */ RtlMoveMemory(&ObjectName->Buffer[(ValueLength / sizeof(WCHAR)) + 1], RemainingName->Buffer, RemainingName->Length); /* Add the slash and update the length */ ObjectName->Buffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; ObjectName->Length += RemainingName->Length + sizeof(WCHAR); } /* Copy the symbolic link target name */ RtlCopyMemory(ObjectName->Buffer, LinkName, ValueLength); } /* Null-terminate the whole thing */ ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] = UNICODE_NULL; Result = TRUE; Exit: /* Free the link name */ if (LinkNameAllocated) ExFreePool(LinkName); /* Check if we had a value cell */ if (LinkValue) { /* Release it */ ASSERT(LinkCell != HCELL_NIL); HvReleaseCell(Hive, LinkCell); } /* Check if we had an active cell and release it, then return the result */ if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); return Result; } NTSTATUS NTAPI CmpDoCreateChild(IN PHHIVE Hive, IN HCELL_INDEX ParentCell, IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, IN PACCESS_STATE AccessState, IN PUNICODE_STRING Name, IN KPROCESSOR_MODE AccessMode, IN PCM_PARSE_CONTEXT ParseContext, IN PCM_KEY_CONTROL_BLOCK ParentKcb, IN ULONG Flags, OUT PHCELL_INDEX KeyCell, OUT PVOID *Object) { NTSTATUS Status = STATUS_SUCCESS; PCM_KEY_BODY KeyBody; HCELL_INDEX ClassCell = HCELL_NIL; PCM_KEY_NODE KeyNode; PCELL_DATA CellData; ULONG StorageType; PCM_KEY_CONTROL_BLOCK Kcb; PSECURITY_DESCRIPTOR NewDescriptor; /* Get the storage type */ StorageType = Stable; if (ParseContext->CreateOptions & REG_OPTION_VOLATILE) StorageType = Volatile; /* Allocate the child */ *KeyCell = HvAllocateCell(Hive, FIELD_OFFSET(CM_KEY_NODE, Name) + CmpNameSize(Hive, Name), StorageType, HCELL_NIL); if (*KeyCell == HCELL_NIL) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Get the key node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, *KeyCell); if (!KeyNode) { /* Fail, this should never happen */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Release the cell */ HvReleaseCell(Hive, *KeyCell); /* Check if we have a class name */ if (ParseContext->Class.Length > 0) { /* Allocate a class cell */ ClassCell = HvAllocateCell(Hive, ParseContext->Class.Length, StorageType, HCELL_NIL); if (ClassCell == HCELL_NIL) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } } /* Allocate the Cm Object */ Status = ObCreateObject(AccessMode, CmpKeyObjectType, NULL, AccessMode, NULL, sizeof(CM_KEY_BODY), 0, 0, Object); if (!NT_SUCCESS(Status)) goto Quickie; /* Setup the key body */ KeyBody = (PCM_KEY_BODY)(*Object); KeyBody->Type = CM_KEY_BODY_TYPE; KeyBody->KeyControlBlock = NULL; /* Check if we had a class */ if (ParseContext->Class.Length > 0) { /* Get the class cell */ CellData = HvGetCell(Hive, ClassCell); if (!CellData) { /* Fail, this should never happen */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; ObDereferenceObject(*Object); goto Quickie; } /* Release the cell */ HvReleaseCell(Hive, ClassCell); /* Copy the class data */ RtlCopyMemory(&CellData->u.KeyString[0], ParseContext->Class.Buffer, ParseContext->Class.Length); } /* Fill out the key node */ KeyNode->Signature = CM_KEY_NODE_SIGNATURE; KeyNode->Flags = Flags; KeQuerySystemTime(&KeyNode->LastWriteTime); KeyNode->Spare = 0; KeyNode->Parent = ParentCell; KeyNode->SubKeyCounts[Stable] = 0; KeyNode->SubKeyCounts[Volatile] = 0; KeyNode->SubKeyLists[Stable] = HCELL_NIL; KeyNode->SubKeyLists[Volatile] = HCELL_NIL; KeyNode->ValueList.Count = 0; KeyNode->ValueList.List = HCELL_NIL; KeyNode->Security = HCELL_NIL; KeyNode->Class = ClassCell; KeyNode->ClassLength = ParseContext->Class.Length; KeyNode->MaxValueDataLen = 0; KeyNode->MaxNameLen = 0; KeyNode->MaxValueNameLen = 0; KeyNode->MaxClassLen = 0; KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name); if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME; /* Create the KCB */ Kcb = CmpCreateKeyControlBlock(Hive, *KeyCell, KeyNode, ParentKcb, 0, // CMP_LOCK_HASHES_FOR_KCB, Name); if (!Kcb) { /* Fail */ ObDereferenceObjectDeferDelete(*Object); Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Sanity check */ ASSERT(Kcb->RefCount == 1); /* Now fill out the Cm object */ KeyBody->NotifyBlock = NULL; KeyBody->ProcessID = PsGetCurrentProcessId(); KeyBody->KeyControlBlock = Kcb; /* Link it with the KCB */ EnlistKeyBodyWithKCB(KeyBody, 0); /* Assign security */ Status = SeAssignSecurity(ParentDescriptor, AccessState->SecurityDescriptor, &NewDescriptor, TRUE, &AccessState->SubjectSecurityContext, &CmpKeyObjectType->TypeInfo.GenericMapping, CmpKeyObjectType->TypeInfo.PoolType); if (NT_SUCCESS(Status)) { Status = CmpSecurityMethod(*Object, AssignSecurityDescriptor, NULL, NewDescriptor, NULL, NULL, CmpKeyObjectType->TypeInfo.PoolType, &CmpKeyObjectType->TypeInfo.GenericMapping); } /* Now that the security descriptor is copied in the hive, we can free the original */ SeDeassignSecurity(&NewDescriptor); if (NT_SUCCESS(Status)) { /* Send notification to registered callbacks */ CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME); } Quickie: /* Check if we got here because of failure */ if (!NT_SUCCESS(Status)) { /* Free any cells we might've allocated */ if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell); HvFreeCell(Hive, *KeyCell); } /* Return status */ return Status; } NTSTATUS NTAPI CmpDoCreate(IN PHHIVE Hive, IN HCELL_INDEX Cell, IN PACCESS_STATE AccessState, IN PUNICODE_STRING Name, IN KPROCESSOR_MODE AccessMode, IN PCM_PARSE_CONTEXT ParseContext, IN PCM_KEY_CONTROL_BLOCK ParentKcb, OUT PVOID *Object) { NTSTATUS Status; PCELL_DATA CellData; HCELL_INDEX KeyCell; ULONG ParentType; PCM_KEY_BODY KeyBody; PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; LARGE_INTEGER TimeStamp; PCM_KEY_NODE KeyNode; /* Check if the parent is being deleted */ if (ParentKcb->Delete) { /* It has, quit */ ASSERT(FALSE); Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Exit; } /* Get the parent node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Make sure nobody added us yet */ if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL) { /* Fail */ ASSERT(FALSE); Status = STATUS_REPARSE; goto Exit; } /* Sanity check */ ASSERT(Cell == ParentKcb->KeyCell); /* Get the parent type */ ParentType = HvGetCellType(Cell); if ((ParentType == Volatile) && !(ParseContext->CreateOptions & REG_OPTION_VOLATILE)) { /* Children of volatile parents must also be volatile */ //ASSERT(FALSE); Status = STATUS_CHILD_MUST_BE_VOLATILE; goto Exit; } /* Don't allow children under symlinks */ if (ParentKcb->Flags & KEY_SYM_LINK) { /* Fail */ ASSERT(FALSE); Status = STATUS_ACCESS_DENIED; goto Exit; } /* Make the cell dirty for now */ HvMarkCellDirty(Hive, Cell, FALSE); /* Do the actual create operation */ Status = CmpDoCreateChild(Hive, Cell, SecurityDescriptor, AccessState, Name, AccessMode, ParseContext, ParentKcb, 0, &KeyCell, Object); if (NT_SUCCESS(Status)) { /* Get the key body */ KeyBody = (PCM_KEY_BODY)(*Object); /* Now add the subkey */ if (!CmpAddSubKey(Hive, Cell, KeyCell)) { /* Failure! We don't handle this yet! */ ASSERT(FALSE); } /* Get the key node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!KeyNode) { /* Fail, this shouldn't happen */ ASSERT(FALSE); } /* Sanity checks */ ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb); ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); /* Update the timestamp */ KeQuerySystemTime(&TimeStamp); KeyNode->LastWriteTime = TimeStamp; KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; /* Check if we need to update name maximum */ if (KeyNode->MaxNameLen < Name->Length) { /* Do it */ KeyNode->MaxNameLen = Name->Length; KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length; } /* Check if we need to update class length maximum */ if (KeyNode->MaxClassLen < ParseContext->Class.Length) { /* Update it */ KeyNode->MaxClassLen = ParseContext->Class.Length; } /* Check if we're creating a symbolic link */ if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK) { /* Get the cell data */ CellData = HvGetCell(Hive, KeyCell); if (!CellData) { /* This shouldn't happen */ ASSERT(FALSE); } /* Update the flags */ CellData->u.KeyNode.Flags |= KEY_SYM_LINK; KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags; HvReleaseCell(Hive, KeyCell); } } Exit: /* Release the flusher lock and return status */ return Status; } NTSTATUS NTAPI CmpDoOpen(IN PHHIVE Hive, IN HCELL_INDEX Cell, IN PCM_KEY_NODE Node, IN PACCESS_STATE AccessState, IN KPROCESSOR_MODE AccessMode, IN ULONG Attributes, IN PCM_PARSE_CONTEXT Context OPTIONAL, IN ULONG ControlFlags, IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb, IN PUNICODE_STRING KeyName, OUT PVOID *Object) { NTSTATUS Status; PCM_KEY_BODY KeyBody = NULL; PCM_KEY_CONTROL_BLOCK Kcb = NULL; /* Make sure the hive isn't locked */ if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) { /* It is, don't touch it */ return STATUS_OBJECT_NAME_NOT_FOUND; } /* Check if we have a context */ if (Context) { /* Check if this is a link create (which shouldn't be an open) */ if (Context->CreateLink) { return STATUS_ACCESS_DENIED; } /* Check if this is symlink create attempt */ if (Context->CreateOptions & REG_OPTION_CREATE_LINK) { /* Key already exists */ return STATUS_OBJECT_NAME_COLLISION; } /* Set the disposition */ Context->Disposition = REG_OPENED_EXISTING_KEY; } /* Do this in the registry lock */ CmpLockRegistry(); /* If we have a KCB, make sure it's locked */ //ASSERT(CmpIsKcbLockedExclusive(*CachedKcb)); /* Check if caller doesn't want to create a KCB */ if (ControlFlags & CMP_OPEN_KCB_NO_CREATE) { /* Check if this is a symlink */ if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) { /* This case for a cached KCB is not implemented yet */ ASSERT(FALSE); } /* The caller wants to open a cached KCB */ if (!CmpReferenceKeyControlBlock(*CachedKcb)) { /* Release the registry lock */ CmpUnlockRegistry(); /* Return failure code */ return STATUS_INSUFFICIENT_RESOURCES; } /* Our kcb is that one */ Kcb = *CachedKcb; } else { /* Check if this is a symlink */ if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) { /* Create the KCB for the symlink */ Kcb = CmpCreateKeyControlBlock(Hive, Cell, Node, *CachedKcb, 0, KeyName); if (!Kcb) { /* Release registry lock and return failure */ CmpUnlockRegistry(); return STATUS_INSUFFICIENT_RESOURCES; } /* Make sure it's also locked, and set the pointer */ //ASSERT(CmpIsKcbLockedExclusive(Kcb)); *CachedKcb = Kcb; /* Release the registry lock */ CmpUnlockRegistry(); /* Return reparse required */ return STATUS_REPARSE; } /* Create the KCB. FIXME: Use lock flag */ Kcb = CmpCreateKeyControlBlock(Hive, Cell, Node, *CachedKcb, 0, KeyName); if (!Kcb) { /* Release registry lock and return failure */ CmpUnlockRegistry(); return STATUS_INSUFFICIENT_RESOURCES; } } /* Make sure it's also locked, and set the pointer */ //ASSERT(CmpIsKcbLockedExclusive(Kcb)); *CachedKcb = Kcb; /* Release the registry lock */ CmpUnlockRegistry(); /* Allocate the key object */ Status = ObCreateObject(AccessMode, CmpKeyObjectType, NULL, AccessMode, NULL, sizeof(CM_KEY_BODY), 0, 0, Object); if (NT_SUCCESS(Status)) { /* Get the key body and fill it out */ KeyBody = (PCM_KEY_BODY)(*Object); KeyBody->KeyControlBlock = Kcb; KeyBody->Type = CM_KEY_BODY_TYPE; KeyBody->ProcessID = PsGetCurrentProcessId(); KeyBody->NotifyBlock = NULL; /* Link to the KCB */ EnlistKeyBodyWithKCB(KeyBody, 0); if (!ObCheckObjectAccess(*Object, AccessState, FALSE, AccessMode, &Status)) { /* Access check failed */ ObDereferenceObject(*Object); } } else { /* Failed, dereference the KCB */ CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); } /* Return status */ return Status; } NTSTATUS NTAPI CmpCreateLinkNode(IN PHHIVE Hive, IN HCELL_INDEX Cell, IN PACCESS_STATE AccessState, IN UNICODE_STRING Name, IN KPROCESSOR_MODE AccessMode, IN ULONG CreateOptions, IN PCM_PARSE_CONTEXT Context, IN PCM_KEY_CONTROL_BLOCK ParentKcb, OUT PVOID *Object) { NTSTATUS Status; HCELL_INDEX KeyCell, LinkCell, ChildCell; PCM_KEY_BODY KeyBody; LARGE_INTEGER TimeStamp; PCM_KEY_NODE KeyNode; PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb; /* Link nodes only allowed on the master */ if (Hive != &CmiVolatileHive->Hive) { /* Fail */ DPRINT1("Invalid link node attempt\n"); return STATUS_ACCESS_DENIED; } /* Check if the parent is being deleted */ if (ParentKcb->Delete) { /* It is, quit */ ASSERT(FALSE); Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Exit; } /* Allocate a link node */ LinkCell = HvAllocateCell(Hive, FIELD_OFFSET(CM_KEY_NODE, Name) + CmpNameSize(Hive, &Name), Stable, HCELL_NIL); if (LinkCell == HCELL_NIL) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Get the key cell */ KeyCell = Context->ChildHive.KeyCell; if (KeyCell != HCELL_NIL) { /* Hive exists! */ ChildCell = KeyCell; /* Get the node data */ KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Fill out the data */ KeyNode->Parent = LinkCell; KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); /* Now open the key cell */ KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Open the parent */ Status = CmpDoOpen(Context->ChildHive.KeyHive, KeyCell, KeyNode, AccessState, AccessMode, CreateOptions, NULL, 0, &Kcb, &Name, Object); HvReleaseCell(Context->ChildHive.KeyHive, KeyCell); } else { /* Do the actual create operation */ Status = CmpDoCreateChild(Context->ChildHive.KeyHive, Cell, NULL, AccessState, &Name, AccessMode, Context, ParentKcb, KEY_HIVE_ENTRY | KEY_NO_DELETE, &ChildCell, Object); if (NT_SUCCESS(Status)) { /* Setup root pointer */ Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell; } } /* Check if open or create suceeded */ if (NT_SUCCESS(Status)) { /* Mark the cell dirty */ HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE); /* Get the key node */ KeyNode = HvGetCell(Context->ChildHive.KeyHive, ChildCell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Release it */ HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); /* Set the parent and flags */ KeyNode->Parent = LinkCell; KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; /* Get the link node */ KeyNode = HvGetCell(Hive, LinkCell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Set it up */ KeyNode->Signature = CM_LINK_NODE_SIGNATURE; KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE; KeyNode->Parent = Cell; KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name); if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME; KeQuerySystemTime(&TimeStamp); KeyNode->LastWriteTime = TimeStamp; /* Clear out the rest */ KeyNode->SubKeyCounts[Stable] = 0; KeyNode->SubKeyCounts[Volatile] = 0; KeyNode->SubKeyLists[Stable] = HCELL_NIL; KeyNode->SubKeyLists[Volatile] = HCELL_NIL; KeyNode->ValueList.Count = 0; KeyNode->ValueList.List = HCELL_NIL; KeyNode->ClassLength = 0; /* Reference the root node */ KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive; KeyNode->ChildHiveReference.KeyCell = ChildCell; HvReleaseCell(Hive, LinkCell); /* Get the parent node */ KeyNode = HvGetCell(Hive, Cell); if (!KeyNode) { /* Fail */ ASSERT(FALSE); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } /* Now add the subkey */ if (!CmpAddSubKey(Hive, Cell, LinkCell)) { /* Failure! We don't handle this yet! */ ASSERT(FALSE); } /* Get the key body */ KeyBody = (PCM_KEY_BODY)*Object; /* Sanity checks */ ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); /* Update the timestamp */ KeQuerySystemTime(&TimeStamp); KeyNode->LastWriteTime = TimeStamp; KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; /* Check if we need to update name maximum */ if (KeyNode->MaxNameLen < Name.Length) { /* Do it */ KeyNode->MaxNameLen = Name.Length; KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length; } /* Check if we need to update class length maximum */ if (KeyNode->MaxClassLen < Context->Class.Length) { /* Update it */ KeyNode->MaxClassLen = Context->Class.Length; } /* Release the cell */ HvReleaseCell(Hive, Cell); } else { /* Release the link cell */ HvReleaseCell(Hive, LinkCell); } Exit: /* Release the flusher locks and return status */ return Status; } VOID NTAPI CmpHandleExitNode(IN OUT PHHIVE *Hive, IN OUT HCELL_INDEX *Cell, IN OUT PCM_KEY_NODE *KeyNode, IN OUT PHHIVE *ReleaseHive, IN OUT HCELL_INDEX *ReleaseCell) { /* Check if we have anything to release */ if (*ReleaseCell != HCELL_NIL) { /* Release it */ ASSERT(*ReleaseHive != NULL); HvReleaseCell(*ReleaseHive, *ReleaseCell); } /* Get the link references */ *Hive = (*KeyNode)->ChildHiveReference.KeyHive; *Cell = (*KeyNode)->ChildHiveReference.KeyCell; /* Get the new node */ *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell); if (*KeyNode) { /* Set the new release values */ *ReleaseCell = *Cell; *ReleaseHive = *Hive; } else { /* Nothing to release */ *ReleaseCell = HCELL_NIL; *ReleaseHive = NULL; } } NTSTATUS NTAPI CmpBuildHashStackAndLookupCache(IN PCM_KEY_BODY ParseObject, IN OUT PCM_KEY_CONTROL_BLOCK *Kcb, IN PUNICODE_STRING Current, OUT PHHIVE *Hive, OUT HCELL_INDEX *Cell, OUT PULONG TotalRemainingSubkeys, OUT PULONG MatchRemainSubkeyLevel, OUT PULONG TotalSubkeys, OUT PULONG OuterStackArray, OUT PULONG *LockedKcbs) { /* We don't lock anything for now */ *LockedKcbs = NULL; /* Calculate hash values */ *TotalRemainingSubkeys = 0xBAADF00D; /* Lock the registry */ CmpLockRegistry(); /* Return hive and cell data */ *Hive = (*Kcb)->KeyHive; *Cell = (*Kcb)->KeyCell; /* Make sure it's not a dead KCB */ ASSERT((*Kcb)->RefCount > 0); /* Reference it */ (VOID)CmpReferenceKeyControlBlock(*Kcb); /* Return success for now */ return STATUS_SUCCESS; } NTSTATUS NTAPI CmpParseKey(IN PVOID ParseObject, IN PVOID ObjectType, IN OUT PACCESS_STATE AccessState, IN KPROCESSOR_MODE AccessMode, IN ULONG Attributes, IN OUT PUNICODE_STRING CompleteName, IN OUT PUNICODE_STRING RemainingName, IN OUT PVOID Context OPTIONAL, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, OUT PVOID *Object) { NTSTATUS Status; PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb; PHHIVE Hive = NULL; PCM_KEY_NODE Node = NULL; HCELL_INDEX Cell = HCELL_NIL, NextCell; PHHIVE HiveToRelease = NULL; HCELL_INDEX CellToRelease = HCELL_NIL; UNICODE_STRING Current, NextName; PCM_PARSE_CONTEXT ParseContext = Context; ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0; PULONG LockedKcbs = NULL; BOOLEAN Result, Last; PAGED_CODE(); /* Loop path separators at the end */ while ((RemainingName->Length) && (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] == OBJ_NAME_PATH_SEPARATOR)) { /* Remove path separator */ RemainingName->Length -= sizeof(WCHAR); } /* Fail if this isn't a key object */ if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH; /* Copy the remaining name */ Current = *RemainingName; /* Check if this is a create */ if (!(ParseContext) || !(ParseContext->CreateOperation)) { /* It isn't, so no context */ ParseContext = NULL; } /* Grab the KCB */ Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock; /* Sanity check */ ASSERT(Kcb != NULL); /* Fail if the key was marked as deleted */ if (Kcb->Delete) return STATUS_KEY_DELETED; /* Lookup in the cache */ Status = CmpBuildHashStackAndLookupCache(ParseObject, &Kcb, &Current, &Hive, &Cell, &TotalRemainingSubkeys, &MatchRemainSubkeyLevel, &TotalSubkeys, NULL, &LockedKcbs); /* This is now the parent */ ParentKcb = Kcb; /* Sanity check */ ASSERT(ParentKcb != NULL); /* Check if everything was found cached */ if (!TotalRemainingSubkeys) ASSERTMSG("Caching not implemented\n", FALSE); /* Don't do anything if we're being deleted */ if (Kcb->Delete) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Quickie; } /* Check if this is a symlink */ if (Kcb->Flags & KEY_SYM_LINK) { /* Get the next name */ Result = CmpGetNextName(&Current, &NextName, &Last); Current.Buffer = NextName.Buffer; /* Validate the current name string length */ if (Current.Length + NextName.Length > MAXUSHORT) { /* too long */ Status = STATUS_NAME_TOO_LONG; goto Quickie; } Current.Length += NextName.Length; /* Validate the current name string maximum length */ if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) { /* too long */ Status = STATUS_NAME_TOO_LONG; goto Quickie; } Current.MaximumLength += NextName.MaximumLength; /* Parse the symlink */ if (CmpGetSymbolicLink(Hive, CompleteName, Kcb, &Current)) { /* Symlink parse succeeded */ Status = STATUS_REPARSE; } else { /* Couldn't find symlink */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* We're done */ goto Quickie; } /* Get the key node */ Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!Node) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Quickie; } /* Start parsing */ Status = STATUS_NOT_IMPLEMENTED; while (TRUE) { /* Get the next component */ Result = CmpGetNextName(&Current, &NextName, &Last); if ((Result) && (NextName.Length)) { /* See if this is a sym link */ if (!(Kcb->Flags & KEY_SYM_LINK)) { /* Find the subkey */ NextCell = CmpFindSubKeyByName(Hive, Node, &NextName); if (NextCell != HCELL_NIL) { /* Get the new node */ Cell = NextCell; Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!Node) ASSERT(FALSE); /* Check if this was the last key */ if (Last) { /* Is this an exit node */ if (Node->Flags & KEY_HIVE_EXIT) { /* Handle it */ CmpHandleExitNode(&Hive, &Cell, &Node, &HiveToRelease, &CellToRelease); if (!Node) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } } /* Do the open */ Status = CmpDoOpen(Hive, Cell, Node, AccessState, AccessMode, Attributes, ParseContext, 0, &Kcb, &NextName, Object); if (Status == STATUS_REPARSE) { /* Parse the symlink */ if (!CmpGetSymbolicLink(Hive, CompleteName, Kcb, NULL)) { /* Symlink parse failed */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } } /* We are done */ break; } /* Is this an exit node */ if (Node->Flags & KEY_HIVE_EXIT) { /* Handle it */ CmpHandleExitNode(&Hive, &Cell, &Node, &HiveToRelease, &CellToRelease); if (!Node) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } } /* Create a KCB for this key */ Kcb = CmpCreateKeyControlBlock(Hive, Cell, Node, ParentKcb, 0, &NextName); if (!Kcb) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Dereference the parent and set the new one */ CmpDereferenceKeyControlBlock(ParentKcb); ParentKcb = Kcb; } else { /* Check if this was the last key for a create */ if ((Last) && (ParseContext)) { /* Check if we're doing a link node */ if (ParseContext->CreateLink) { /* The only thing we should see */ Status = CmpCreateLinkNode(Hive, Cell, AccessState, NextName, AccessMode, Attributes, ParseContext, ParentKcb, Object); } else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates) { /* Creating keys in the master hive is not allowed */ Status = STATUS_INVALID_PARAMETER; } else { /* Do the create */ Status = CmpDoCreate(Hive, Cell, AccessState, &NextName, AccessMode, ParseContext, ParentKcb, Object); } /* Check for reparse (in this case, someone beat us) */ if (Status == STATUS_REPARSE) break; /* Update disposition */ ParseContext->Disposition = REG_CREATED_NEW_KEY; break; } else { /* Key not found */ Status = STATUS_OBJECT_NAME_NOT_FOUND; break; } } } else { /* Save the next name */ Current.Buffer = NextName.Buffer; /* Validate the current name string length */ if (Current.Length + NextName.Length > MAXUSHORT) { /* too long */ Status = STATUS_NAME_TOO_LONG; break; } Current.Length += NextName.Length; /* Validate the current name string maximum length */ if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) { /* too long */ Status = STATUS_NAME_TOO_LONG; break; } Current.MaximumLength += NextName.MaximumLength; /* Parse the symlink */ if (CmpGetSymbolicLink(Hive, CompleteName, Kcb, &Current)) { /* Symlink parse succeeded */ Status = STATUS_REPARSE; } else { /* Couldn't find symlink */ Status = STATUS_OBJECT_NAME_NOT_FOUND; } /* We're done */ break; } } else if ((Result) && (Last)) { /* Opening the root. Is this an exit node? */ if (Node->Flags & KEY_HIVE_EXIT) { /* Handle it */ CmpHandleExitNode(&Hive, &Cell, &Node, &HiveToRelease, &CellToRelease); if (!Node) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; } } /* Do the open */ Status = CmpDoOpen(Hive, Cell, Node, AccessState, AccessMode, Attributes, ParseContext, CMP_OPEN_KCB_NO_CREATE /* | CMP_CREATE_KCB_KCB_LOCKED */, &Kcb, &NextName, Object); if (Status == STATUS_REPARSE) { /* Nothing to do */ } /* We're done */ break; } else { /* Bogus */ Status = STATUS_INVALID_PARAMETER; break; } } /* Dereference the parent if it exists */ Quickie: if (ParentKcb) CmpDereferenceKeyControlBlock(ParentKcb); /* Unlock the registry */ CmpUnlockRegistry(); return Status; }