Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312/* * 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 *****************************************************************/
BOOLEANNTAPICmpGetNextName(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;}
BOOLEANNTAPICmpGetSymbolicLink(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;}
NTSTATUSNTAPICmpDoCreateChild(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; KeyBody->KcbLocked = FALSE;
/* 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, 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, CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
/* Assign security */ Status = SeAssignSecurity(ParentDescriptor, AccessState->SecurityDescriptor, &NewDescriptor, TRUE, &AccessState->SubjectSecurityContext, &CmpKeyObjectType->TypeInfo.GenericMapping, CmpKeyObjectType->TypeInfo.PoolType); if (NT_SUCCESS(Status)) { /* * FIXME: We must acquire a security lock when assigning * a security descriptor to this hive but since the * CmpAssignSecurityDescriptor function does nothing * (we lack the necessary security management implementations * anyway), do not do anything for now. */ Status = CmpAssignSecurityDescriptor(Kcb, NewDescriptor); }
/* 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;}
NTSTATUSNTAPICmpDoCreate(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;
/* Make sure the KCB is locked and lock the flusher */ CMP_ASSERT_KCB_LOCK(ParentKcb); CmpLockHiveFlusherShared((PCMHIVE)Hive);
/* Bail out on read-only KCBs */ if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY) { Status = STATUS_ACCESS_DENIED; goto Exit; }
/* 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)) { /* Free the created child */ CmpFreeKeyByCell(Hive, KeyCell, FALSE);
/* Purge out this KCB */ KeyBody->KeyControlBlock->Delete = TRUE; CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
/* And cleanup the key body object */ ObDereferenceObjectDeferDelete(*Object); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; }
/* Get the key node */ KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); if (!KeyNode) { /* Fail, this shouldn't happen */ CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
/* Purge out this KCB */ KeyBody->KeyControlBlock->Delete = TRUE; CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
/* And cleanup the key body object */ ObDereferenceObjectDeferDelete(*Object); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; }
/* Clean up information on this subkey */ CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
/* 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 */ CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
/* Purge out this KCB */ KeyBody->KeyControlBlock->Delete = TRUE; CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
/* And cleanup the key body object */ ObDereferenceObjectDeferDelete(*Object); Status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; }
/* 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 */ CmpUnlockHiveFlusher((PCMHIVE)Hive); return Status;}
NTSTATUSNTAPICmpDoOpen(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 PULONG KcbsLocked, IN PUNICODE_STRING KeyName, OUT PVOID *Object){ NTSTATUS Status; BOOLEAN LockKcb = FALSE; BOOLEAN IsLockShared = FALSE; 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; }
/* Lock the KCB on creation if asked */ if (ControlFlags & CMP_CREATE_KCB_KCB_LOCKED) { LockKcb = TRUE; }
/* Check if caller doesn't want to create a KCB */ if (ControlFlags & CMP_OPEN_KCB_NO_CREATE) { /* * The caller doesn't want to create a KCB. This means the KCB * is already in cache and other threads may take use of it * so it has to be locked in share mode. */ IsLockShared = TRUE;
/* Check if this is a symlink */ if (((*CachedKcb)->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) { /* Is this symlink found? */ if ((*CachedKcb)->ExtFlags & CM_KCB_SYM_LINK_FOUND) { /* Get the real KCB, is this deleted? */ Kcb = (*CachedKcb)->ValueCache.RealKcb; if (Kcb->Delete) { /* * The real KCB is gone, do a reparse. We used to lock the KCB in * shared mode as others may have taken use of it but since we * must do a reparse of the key the only thing that matter is us. * Lock the KCB exclusively so nobody is going to mess with the KCB. */ DPRINT1("The real KCB is deleted, attempt a reparse\n"); CmpUnLockKcbArray(KcbsLocked); CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey)); CmpCleanUpKcbValueCache(*CachedKcb); KcbsLocked[0] = 1; KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey); return STATUS_REPARSE; }
/* * The symlink has been found. As in the similar case above, * the KCB of the symlink exclusively, we don't want anybody * to mess it up. */ CmpUnLockKcbArray(KcbsLocked); CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey)); KcbsLocked[0] = 1; KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey); } else { /* We must do a reparse */ DPRINT("The symlink is not found, attempt a reparse\n"); return STATUS_REPARSE; } } else { /* This is not a symlink, just give the cached KCB already */ Kcb = *CachedKcb; }
/* The caller wants to open a cached KCB */ if (!CmpReferenceKeyControlBlock(Kcb)) { /* Return failure code */ return STATUS_INSUFFICIENT_RESOURCES; } } else { /* * The caller wants to create a new KCB. Unlike the code path above, here * we must check if the lock is exclusively held because in the scenario * where the caller doesn't want to create a KCB is because it is already * in the cache and it must have a shared lock instead. */ ASSERT(CmpIsKcbLockedExclusive(*CachedKcb));
/* 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, LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0, KeyName); if (!Kcb) { /* Return failure */ return STATUS_INSUFFICIENT_RESOURCES; }
/* Make sure it's also locked, and set the pointer */ ASSERT(CmpIsKcbLockedExclusive(Kcb)); *CachedKcb = Kcb;
/* Return reparse required */ return STATUS_REPARSE; }
/* Create the KCB */ Kcb = CmpCreateKeyControlBlock(Hive, Cell, Node, *CachedKcb, LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0, KeyName); if (!Kcb) { /* Return failure */ return STATUS_INSUFFICIENT_RESOURCES; }
/* Make sure it's also locked, and set the pointer */ ASSERT(CmpIsKcbLockedExclusive(Kcb)); *CachedKcb = Kcb; }
/* 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, IsLockShared ? CMP_ENLIST_KCB_LOCKED_SHARED : CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
/* * We are already holding a lock against the KCB that is assigned * to this key body. This is to prevent a potential deadlock on * CmpSecurityMethod as ObCheckObjectAccess will invoke the Object * Manager to call that method, of which CmpSecurityMethod would * attempt to acquire a lock again. */ KeyBody->KcbLocked = TRUE;
if (!ObCheckObjectAccess(*Object, AccessState, FALSE, AccessMode, &Status)) { /* Access check failed */ ObDereferenceObject(*Object); }
/* * We are done, the lock we are holding will be released * once the registry parsing is done. */ KeyBody->KcbLocked = FALSE; } else { /* Failed, dereference the KCB */ CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); }
/* Return status */ return Status;}
NTSTATUSNTAPICmpCreateLinkNode(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, IN PULONG KcbsLocked, 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; }
/* Make sure the KCB is locked and lock the flusher */ CMP_ASSERT_KCB_LOCK(ParentKcb); CmpLockHiveFlusherShared((PCMHIVE)Hive); CmpLockHiveFlusherShared((PCMHIVE)Context->ChildHive.KeyHive);
/* Bail out on read-only KCBs */ if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY) { Status = STATUS_ACCESS_DENIED; goto Exit; }
/* 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, CMP_CREATE_KCB_KCB_LOCKED, &Kcb, KcbsLocked, &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 = (PCM_KEY_NODE)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 = (PCM_KEY_NODE)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 = (PCM_KEY_NODE)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;
/* Clean up information on this subkey */ CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
/* 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 */ CmpUnlockHiveFlusher((PCMHIVE)Context->ChildHive.KeyHive); CmpUnlockHiveFlusher((PCMHIVE)Hive); return Status;}
VOIDNTAPICmpHandleExitNode(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; }}
/** * @brief * Computes the hashes of each subkey in key path name * and stores them in a hash stack for cache lookup. * * @param[in] RemainingName * A Unicode string structure consisting of the remaining * registry key path name. * * @param[in] ConvKey * The hash convkey of the current KCB to be supplied. * * @param[in,out] HashCacheStack * An array stack. This function uses this array to store * all the computed hashes of a key pathname. * * @param[out] TotalSubKeys * The number of total subkeys that have been found, returned * by this function to the caller. If no subkey levels are found * the function returns 0. * * @return * Returns the number of remaining subkey levels to caller. * If no subkey levels are found then this function returns 0. */staticULONGCmpComputeHashValue( _In_ PUNICODE_STRING RemainingName, _In_ ULONG ConvKey, _Inout_ PCM_HASH_CACHE_STACK HashCacheStack, _Out_ PULONG TotalSubKeys){ ULONG CopyConvKey; ULONG SubkeysInTotal; ULONG RemainingSubkeysInTotal; PWCHAR RemainingNameBuffer; USHORT RemainingNameLength; USHORT KeyNameLength;
/* Don't compute the hashes on a NULL remaining name */ RemainingNameBuffer = RemainingName->Buffer; RemainingNameLength = RemainingName->Length; if (RemainingNameLength == 0) { *TotalSubKeys = 0; return 0; }
/* Skip any leading separator */ while (RemainingNameLength >= sizeof(WCHAR) && *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR) { RemainingNameBuffer++; RemainingNameLength -= sizeof(WCHAR); }
/* Now set up the hash stack entries and compute the hashes */ SubkeysInTotal = 0; RemainingSubkeysInTotal = 0; KeyNameLength = 0; CopyConvKey = ConvKey; HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer; while (RemainingNameLength > 0) { /* Is this character a separator? */ if (*RemainingNameBuffer != OBJ_NAME_PATH_SEPARATOR) { /* It's not, add it to the hash */ CopyConvKey = COMPUTE_HASH_CHAR(CopyConvKey, *RemainingNameBuffer);
/* Go to the next character (add up the length of the character as well) */ RemainingNameBuffer++; KeyNameLength += sizeof(WCHAR); RemainingNameLength -= sizeof(WCHAR);
/* * We are at the end of the key name path. Take into account * the last character and if we still have space in the hash * stack, add it up in the remaining list. */ if (RemainingNameLength == 0) { if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) { HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength; HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength; HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey; RemainingSubkeysInTotal++; }
SubkeysInTotal++; } } else { /* Skip any leading separator */ while (RemainingNameLength >= sizeof(WCHAR) && *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR) { RemainingNameBuffer++; RemainingNameLength -= sizeof(WCHAR); }
/* * It would be possible that a malformed key pathname may be passed * to the registry parser such as a path with only separators like * "\\\\" for example. This would trick the function into believing * the key path has subkeys albeit that is not the case. */ ASSERT(RemainingNameLength != 0);
/* Take into account this subkey */ SubkeysInTotal++;
/* And add it up to the hash stack */ if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) { HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength; HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength; HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey;
RemainingSubkeysInTotal++; KeyNameLength = 0;
/* * Precaution check -- we have added up a remaining * subkey above but we must ensure we still have space * to hold up the new subkey for which we will compute * the hashes, so that we don't blow up the hash stack. */ if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) { HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer; } } } }
*TotalSubKeys = SubkeysInTotal; return RemainingSubkeysInTotal;}
/** * @brief * Compares each subkey's hash and name with those * captured in the hash cache stack. * * @param[in] HashCacheStack * A pointer to a hash cache stack array filled with * subkey hashes and names for comparison. * * @param[in] CurrentKcb * A pointer to the currently given KCB. * * @param[in] RemainingSubkeys * The remaining subkey levels to be supplied. * * @param[out] ParentKcb * A pointer to the parent KCB returned to the caller. * This parameter points to the parent of the current * KCB if all the subkeys match, otherwise it points * to the actual current KCB. * * @return * Returns TRUE if all the subkey levels match, otherwise * FALSE is returned. */staticBOOLEANCmpCompareSubkeys( _In_ PCM_HASH_CACHE_STACK HashCacheStack, _In_ PCM_KEY_CONTROL_BLOCK CurrentKcb, _In_ ULONG RemainingSubkeys, _Out_ PCM_KEY_CONTROL_BLOCK *ParentKcb){ LONG HashStackIndex; LONG Result; PCM_NAME_CONTROL_BLOCK NameBlock; UNICODE_STRING CurrentNameBlock;
ASSERT(CurrentKcb != NULL);
/* Loop each hash and check that they match */ HashStackIndex = RemainingSubkeys; while (HashStackIndex >= 0) { /* Does the subkey hash match? */ if (CurrentKcb->ConvKey != HashCacheStack[HashStackIndex].ConvKey) { *ParentKcb = CurrentKcb; return FALSE; }
/* Compare the subkey string, is the name compressed? */ NameBlock = CurrentKcb->NameBlock; if (NameBlock->Compressed) { Result = CmpCompareCompressedName(&HashCacheStack[HashStackIndex].NameOfKey, NameBlock->Name, NameBlock->NameLength); } else { CurrentNameBlock.Buffer = NameBlock->Name; CurrentNameBlock.Length = NameBlock->NameLength; CurrentNameBlock.MaximumLength = NameBlock->NameLength;
Result = RtlCompareUnicodeString(&HashCacheStack[HashStackIndex].NameOfKey, &CurrentNameBlock, TRUE); }
/* Do the subkey names match? */ if (Result) { *ParentKcb = CurrentKcb; return FALSE; }
/* Go to the next subkey hash */ HashStackIndex--; }
/* All the subkeys match */ *ParentKcb = CurrentKcb->ParentKcb; return TRUE;}
/** * @brief * Removes the subkeys on a remaining key pathname. * * @param[in] HashCacheStack * A pointer to a hash cache stack array filled with * subkey hashes and names. * * @param[in] RemainingSubkeys * The remaining subkey levels to be supplied. * * @param[in,out] RemainingName * A Unicode string structure consisting of the remaining * registry key path name, where the subkeys of such path * are to be removed. */staticVOIDCmpRemoveSubkeysInRemainingName( _In_ PCM_HASH_CACHE_STACK HashCacheStack, _In_ ULONG RemainingSubkeys, _Inout_ PUNICODE_STRING RemainingName){ ULONG HashStackIndex = 0;
/* Skip any leading separator on matching name */ while (RemainingName->Length >= sizeof(WCHAR) && RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { RemainingName->Buffer++; RemainingName->Length -= sizeof(WCHAR); }
/* Skip the subkeys as well */ while (HashStackIndex <= RemainingSubkeys) { RemainingName->Buffer += HashCacheStack[HashStackIndex].NameOfKey.Length / sizeof(WCHAR); RemainingName->Length -= HashCacheStack[HashStackIndex].NameOfKey.Length;
/* Skip any leading separator */ while (RemainingName->Length >= sizeof(WCHAR) && RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { RemainingName->Buffer++; RemainingName->Length -= sizeof(WCHAR); }
/* Go to the next hash */ HashStackIndex++; }}
/** * @brief * Looks up in the pool cache for key pathname that matches * with one in the said cache and returns a KCB pointing * to that name. This function performs locking of KCBs * during cache lookup. * * @param[in] HashCacheStack * A pointer to a hash cache stack array filled with * subkey hashes and names. * * @param[in] LockKcbsExclusive * If set to TRUE, the KCBs are locked exclusively by the * calling thread, otherwise they are locked in shared mode. * See Remarks for further information. * * @param[in] TotalRemainingSubkeys * The total remaining subkey levels to be supplied. * * @param[in,out] RemainingName * A Unicode string structure consisting of the remaining * registry key path name. The remaining name is updated * by the function if a key pathname is found in cache. * * @param[in,out] OuterStackArray * A pointer to an array that lives on the caller's stack. * The expected size of the array is up to 32 elements, * which is the imposed limit by CMP_HASH_STACK_LIMIT. * This limit also corresponds to the maximum depth of * subkey levels. * * @param[in,out] Kcb * A pointer to a KCB, this KCB is changed if the key pathname * is found in cache. * * @param[out] Hive * A pointer to a hive, this hive is changed if the key pathname * is found in cache. * * @param[out] Cell * A pointer to a cell, this cell is changed if the key pathname * is found in cache. * * @param[out] MatchRemainSubkeyLevel * A pointer to match subkey levels returned by the function. * If no match levels are found, this is 0. * * @return * Returns STATUS_SUCCESS if cache lookup has completed successfully. * STATUS_OBJECT_NAME_NOT_FOUND is returned if the current KCB of * the key pathname has been deleted. STATUS_RETRY is returned if * at least the current KCB or its parent have been deleted * and a cache lookup must be retried again. STATUS_UNSUCCESSFUL is * returned if a KCB is referenced too many times. * * @remarks * The function attempts to do a cache lookup with a shared lock * on KCBs so that other threads can simultaneously get access * to these KCBs. When the captured KCB is being deleted on us * we have to retry a lookup with exclusive look so that no other * threads will mess with the KCBs and perform appropriate actions * if a KCB is deleted. */staticNTSTATUSCmpLookInCache( _In_ PCM_HASH_CACHE_STACK HashCacheStack, _In_ BOOLEAN LockKcbsExclusive, _In_ ULONG TotalRemainingSubkeys, _Inout_ PUNICODE_STRING RemainingName, _Inout_ PULONG OuterStackArray, _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb, _Out_ PHHIVE *Hive, _Out_ PHCELL_INDEX Cell, _Out_ PULONG MatchRemainSubkeyLevel){ LONG RemainingSubkeys; ULONG TotalLevels; BOOLEAN SubkeysMatch; PCM_KEY_CONTROL_BLOCK CurrentKcb, ParentKcb; PCM_KEY_HASH HashEntry = NULL; BOOLEAN KeyFoundInCache = FALSE; PULONG LockedKcbs = NULL;
/* Reference the KCB */ if (!CmpReferenceKeyControlBlock(*Kcb)) { /* This key is opened too many times, bail out */ DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); return STATUS_UNSUCCESSFUL; }
/* Prepare to lock the KCBs */ LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack, LockKcbsExclusive ? CMP_LOCK_KCB_ARRAY_EXCLUSIVE : CMP_LOCK_KCB_ARRAY_SHARED, *Kcb, OuterStackArray, TotalRemainingSubkeys, 0); NT_ASSERT(LockedKcbs);
/* Lookup in the cache */ RemainingSubkeys = TotalRemainingSubkeys - 1; TotalLevels = TotalRemainingSubkeys + (*Kcb)->TotalLevels + 1; while (RemainingSubkeys >= 0) { /* Get the hash entry from the cache */ HashEntry = GET_HASH_ENTRY(CmpCacheTable, HashCacheStack[RemainingSubkeys].ConvKey)->Entry;
/* Take one level down as we are processing this hash entry */ TotalLevels--;
while (HashEntry != NULL) { /* Validate this hash and obtain the current KCB */ ASSERT_VALID_HASH(HashEntry); CurrentKcb = CONTAINING_RECORD(HashEntry, CM_KEY_CONTROL_BLOCK, KeyHash);
/* Does this KCB have matching levels? */ if (TotalLevels == CurrentKcb->TotalLevels) { /* * We have matching subkey levels but don't directly assume we have * a matching key path in cache. Start comparing each subkey. */ SubkeysMatch = CmpCompareSubkeys(HashCacheStack, CurrentKcb, RemainingSubkeys, &ParentKcb); if (SubkeysMatch) { /* All subkeys match, now check if the base KCB matches with parent */ if (*Kcb == ParentKcb) { /* Is the KCB marked as deleted? */ if (CurrentKcb->Delete || CurrentKcb->ParentKcb->Delete) { /* * Either the current or its parent KCB is marked * but we had a shared lock so probably a naughty * thread was deleting it. Retry doing a cache * lookup again with exclusive lock. */ if (!LockKcbsExclusive) { CmpUnLockKcbArray(LockedKcbs); CmpDereferenceKeyControlBlock(*Kcb); DPRINT1("The current KCB or its parent is deleted, retrying looking in the cache\n"); return STATUS_RETRY; }
/* We're under an exclusive lock, is the KCB deleted yet? */ if (CurrentKcb->Delete) { /* The KCB is gone, the key should no longer belong in the cache */ CmpRemoveKeyControlBlock(CurrentKcb); CmpUnLockKcbArray(LockedKcbs); CmpDereferenceKeyControlBlock(*Kcb); DPRINT1("The current KCB is deleted (KCB 0x%p)\n", CurrentKcb); return STATUS_OBJECT_NAME_NOT_FOUND; }
/* * The parent is deleted so it must be that somebody created * a fake key. Assert ourselves that is the case. */ ASSERT(CurrentKcb->ExtFlags & CM_KCB_KEY_NON_EXIST);
/* Remove this KCB out of cache if someone still uses it */ if (CurrentKcb->RefCount != 0) { CurrentKcb->Delete = TRUE; CmpRemoveKeyControlBlock(CurrentKcb); } else { /* Otherwise expunge it */ CmpRemoveFromDelayedClose(CurrentKcb); CmpCleanUpKcbCacheWithLock(CurrentKcb, FALSE); }
/* Stop looking for next hashes as the KCB is kaput */ break; }
/* We finally found the key in cache, acknowledge it */ KeyFoundInCache = TRUE;
/* Remove the subkeys in the remaining name and stop looking in the cache */ CmpRemoveSubkeysInRemainingName(HashCacheStack, RemainingSubkeys, RemainingName); break; } } }
/* Go to the next hash */ HashEntry = HashEntry->NextHash; }
/* Stop looking in cache if we found the matching key */ if (KeyFoundInCache) { DPRINT("Key found in cache, stop looking\n"); break; }
/* Keep looking in the cache until we run out of remaining subkeys */ RemainingSubkeys--; }
/* Return the matching subkey levels */ *MatchRemainSubkeyLevel = RemainingSubkeys + 1;
/* We have to update the KCB if the key was found in cache */ if (KeyFoundInCache) { /* * Before we change the KCB we must dereference the prior * KCB that we no longer need it. */ CmpDereferenceKeyControlBlock(*Kcb); *Kcb = CurrentKcb;
/* Reference the new KCB now */ if (!CmpReferenceKeyControlBlock(*Kcb)) { /* This key is opened too many times, bail out */ DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); return STATUS_UNSUCCESSFUL; }
/* Update hive and cell data from current KCB */ *Hive = CurrentKcb->KeyHive; *Cell = CurrentKcb->KeyCell; }
/* Unlock the KCBs */ CmpUnLockKcbArray(LockedKcbs); return STATUS_SUCCESS;}
/** * @brief * Builds a hash stack cache and looks up in the * pool cache for a matching key pathname. * * @param[in] ParseObject * A pointer to a parse object, acting as a key * body. This parameter is unused. * * @param[in,out] Kcb * A pointer to a KCB. This KCB is used by the * registry parser after hash stack and cache * lookup are done. This KCB might change if the * key is found to be cached in the cache pool. * * @param[in] Current * The current remaining key pathname. * * @param[out] Hive * A pointer to a registry hive, returned by the caller. * * @param[out] Cell * A pointer to a hive cell, returned by the caller. * * @param[out] TotalRemainingSubkeys * A pointer to a number of total remaining subkey levels, * returned by the caller. This can be 0 if no subkey levels * have been found. * * @param[out] MatchRemainSubkeyLevel * A pointer to a number of remaining subkey levels that match, * returned by the caller. This can be 0 if no matching levels * are found. * * @param[out] TotalSubkeys * A pointer to a number of total subkeys. This can be 0 if no * subkey levels are found. By definition, both MatchRemainSubkeyLevel * and TotalRemainingSubkeys are 0 as well. * * @param[in,out] OuterStackArray * A pointer to an array that lives on the caller's stack. * The expected size of the array is up to 32 elements, * which is the imposed limit by CMP_HASH_STACK_LIMIT. * This limit also corresponds to the maximum depth of * subkey levels. * * @param[out] LockedKcbs * A pointer to an array of locked KCBs, returned by the caller. * * @return * Returns STATUS_SUCCESS if all the operations have succeeded without * problems. STATUS_NAME_TOO_LONG is returned if the key pathname has * too many subkey levels (more than 32 levels deep). A failure NTSTATUS * code is returned otherwise. Refer to CmpLookInCache documentation * for more information about other returned status codes. * STATUS_UNSUCCESSFUL is returned if a KCB is referenced too many times. */NTSTATUSNTAPICmpBuildHashStackAndLookupCache( _In_ PCM_KEY_BODY ParseObject, _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb, _In_ PUNICODE_STRING Current, _Out_ PHHIVE *Hive, _Out_ PHCELL_INDEX Cell, _Out_ PULONG TotalRemainingSubkeys, _Out_ PULONG MatchRemainSubkeyLevel, _Out_ PULONG TotalSubkeys, _Inout_ PULONG OuterStackArray, _Out_ PULONG *LockedKcbs){ NTSTATUS Status; ULONG ConvKey; ULONG SubkeysInTotal, RemainingSubkeysInTotal, MatchRemainingSubkeys; CM_HASH_CACHE_STACK HashCacheStack[CMP_SUBKEY_LEVELS_DEPTH_LIMIT];
/* Make sure it's not a dead KCB */ ASSERT((*Kcb)->RefCount > 0);
/* Lock the registry */ CmpLockRegistry();
/* Calculate hash values for every subkey this key path has */ ConvKey = (*Kcb)->ConvKey; RemainingSubkeysInTotal = CmpComputeHashValue(Current, ConvKey, HashCacheStack, &SubkeysInTotal);
/* This key path has too many subkeys */ if (SubkeysInTotal > CMP_SUBKEY_LEVELS_DEPTH_LIMIT) { DPRINT1("The key path has too many subkeys - %lu\n", SubkeysInTotal); *LockedKcbs = NULL; return STATUS_NAME_TOO_LONG; }
/* Return hive and cell data */ *Hive = (*Kcb)->KeyHive; *Cell = (*Kcb)->KeyCell;
/* Do we have any subkeys? */ if (!RemainingSubkeysInTotal && !SubkeysInTotal) { /* * We don't have any subkeys nor remaining levels, the * KCB points to the actual key. Lock it. */ if (!CmpReferenceKeyControlBlock(*Kcb)) { /* This key is opened too many times, bail out */ DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); return STATUS_UNSUCCESSFUL; }
CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX(ConvKey));
/* Add this KCB in the array of locked KCBs */ OuterStackArray[0] = 1; OuterStackArray[1] = GET_HASH_INDEX(ConvKey); *LockedKcbs = OuterStackArray;
/* And return all the subkey level counters */ *TotalRemainingSubkeys = RemainingSubkeysInTotal; *MatchRemainSubkeyLevel = 0; *TotalSubkeys = SubkeysInTotal; return STATUS_SUCCESS; }
/* Lookup in the cache */ Status = CmpLookInCache(HashCacheStack, FALSE, RemainingSubkeysInTotal, Current, OuterStackArray, Kcb, Hive, Cell, &MatchRemainingSubkeys); if (!NT_SUCCESS(Status)) { /* Bail out if cache lookup failed for other reasons */ if (Status != STATUS_RETRY) { DPRINT1("CmpLookInCache() failed (Status 0x%lx)\n", Status); *LockedKcbs = NULL; return Status; }
/* Retry looking in the cache but with KCBs locked exclusively */ Status = CmpLookInCache(HashCacheStack, TRUE, RemainingSubkeysInTotal, Current, OuterStackArray, Kcb, Hive, Cell, &MatchRemainingSubkeys); if (!NT_SUCCESS(Status)) { DPRINT1("CmpLookInCache() failed after retry (Status 0x%lx)\n", Status); *LockedKcbs = NULL; return Status; } }
/* * Check if we have a full match of remaining levels. * * FIXME: It is possible we can catch a fake key from the cache * when we did the lookup, in such case we should not do any * locking as such KCB does not point to any real information. * Currently ReactOS doesn't create fake KCBs so we are good * for now. */ if (RemainingSubkeysInTotal == MatchRemainingSubkeys) { /* * Just simply lock this KCB as it points to the full * subkey levels in cache. */ CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX((*Kcb)->ConvKey)); OuterStackArray[0] = 1; OuterStackArray[1] = GET_HASH_INDEX((*Kcb)->ConvKey); *LockedKcbs = OuterStackArray; } else { /* * We only have a partial match so other subkey levels * have each KCB. Simply just lock them. */ *LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack, CMP_LOCK_KCB_ARRAY_EXCLUSIVE, *Kcb, OuterStackArray, RemainingSubkeysInTotal, MatchRemainingSubkeys); NT_ASSERT(*LockedKcbs); }
/* Return all the subkey level counters */ *TotalRemainingSubkeys = RemainingSubkeysInTotal; *MatchRemainSubkeyLevel = MatchRemainingSubkeys; *TotalSubkeys = SubkeysInTotal; return Status;}
NTSTATUSNTAPICmpParseKey(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; ULONG LockedKcbArray[CMP_KCBS_IN_ARRAY_LIMIT]; PULONG LockedKcbs; BOOLEAN IsKeyCached = FALSE; 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, LockedKcbArray, &LockedKcbs); CMP_ASSERT_REGISTRY_LOCK(); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to look in cache, stop parsing (Status 0x%lx)\n", Status); ParentKcb = NULL; goto Quickie; }
/* This is now the parent */ ParentKcb = Kcb;
/* Sanity check */ ASSERT(ParentKcb != NULL);
/* Don't do anything if we're being deleted */ if (Kcb->Delete) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Quickie; }
/* Check if everything was found cached */ if (!TotalRemainingSubkeys) { /* * We don't have any remaining subkey levels so we're good * that we have an already perfect candidate for a KCB, just * do the open directly. */ DPRINT("No remaining subkeys, the KCB points to the actual key\n"); IsKeyCached = TRUE; goto KeyCachedOpenNow; }
/* Check if we have a matching level */ if (MatchRemainSubkeyLevel) { /* * We have a matching level, check if that matches * with the total levels of subkeys. Do the open directly * if that is the case, because the whole subkeys levels * is cached. */ if (MatchRemainSubkeyLevel == TotalSubkeys) { DPRINT("We have a full matching level, open the key now\n"); IsKeyCached = TRUE; goto KeyCachedOpenNow; }
/* * We only have a partial match, make sure we did not * get mismatched hive data. */ ASSERT(Hive == Kcb->KeyHive); ASSERT(Cell == Kcb->KeyCell); }
/* * FIXME: Currently the registry parser doesn't check for fake * KCBs. CmpCreateKeyControlBlock does have the necessary implementation * to create such fake keys but we don't create these fake keys anywhere. * When we will do, we must improve the registry parser routine to handle * fake keys a bit differently here. */
/* 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;
/* CmpGetSymbolicLink doesn't want a lock */ CmpUnLockKcbArray(LockedKcbs); LockedKcbs = NULL;
/* 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); ASSERT(Node);
/* 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; } }
KeyCachedOpenNow: /* Do the open */ Status = CmpDoOpen(Hive, Cell, Node, AccessState, AccessMode, Attributes, ParseContext, IsKeyCached ? CMP_OPEN_KCB_NO_CREATE : CMP_CREATE_KCB_KCB_LOCKED, &Kcb, LockedKcbs, &NextName, Object); if (Status == STATUS_REPARSE) { /* CmpGetSymbolicLink doesn't want a lock */ CmpUnLockKcbArray(LockedKcbs); LockedKcbs = NULL;
/* 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, CMP_LOCK_HASHES_FOR_KCB, &NextName); if (!Kcb) { /* Fail */ Status = STATUS_INSUFFICIENT_RESOURCES; break; }
/* Dereference the parent and set the new one */ CmpDereferenceKeyControlBlockWithLock(ParentKcb, FALSE); 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, LockedKcbs, 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;
/* CmpGetSymbolicLink doesn't want a lock */ CmpUnLockKcbArray(LockedKcbs); LockedKcbs = NULL;
/* 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, &Kcb, LockedKcbs, &NextName, Object); if (Status == STATUS_REPARSE) { /* Nothing to do */ }
/* We're done */ break; } else { /* Bogus */ Status = STATUS_INVALID_PARAMETER; break; } }
Quickie: /* Unlock all the KCBs */ if (LockedKcbs != NULL) { CmpUnLockKcbArray(LockedKcbs); }
/* Dereference the parent if it exists */ if (ParentKcb) CmpDereferenceKeyControlBlock(ParentKcb);
/* Unlock the registry */ CmpUnlockRegistry(); return Status;}