xref: /reactos/ntoskrnl/config/cmparse.c (revision 1734f297)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/config/cmparse.c
5  * PURPOSE:         Configuration Manager - Object Manager Parse Interface
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14 
15 /* GLOBALS *******************************************************************/
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 BOOLEAN
20 NTAPI
21 CmpGetNextName(IN OUT PUNICODE_STRING RemainingName,
22                OUT PUNICODE_STRING NextName,
23                OUT PBOOLEAN LastName)
24 {
25     BOOLEAN NameValid = TRUE;
26 
27     ASSERT(RemainingName->Length % sizeof(WCHAR) == 0);
28 
29     /* Check if there's nothing left in the name */
30     if (!(RemainingName->Buffer) ||
31         (!RemainingName->Length) ||
32         !(*RemainingName->Buffer))
33     {
34         /* Clear the next name and set this as last */
35         *LastName = TRUE;
36         NextName->Buffer = NULL;
37         NextName->Length = 0;
38         return TRUE;
39     }
40 
41     /* Check if we have a path separator */
42     while ((RemainingName->Length) &&
43            (*RemainingName->Buffer == OBJ_NAME_PATH_SEPARATOR))
44     {
45         /* Skip it */
46         RemainingName->Buffer++;
47         RemainingName->Length -= sizeof(WCHAR);
48         RemainingName->MaximumLength -= sizeof(WCHAR);
49     }
50 
51     /* Start loop at where the current buffer is */
52     NextName->Buffer = RemainingName->Buffer;
53     while ((RemainingName->Length) &&
54            (*RemainingName->Buffer != OBJ_NAME_PATH_SEPARATOR))
55     {
56         /* Move to the next character */
57         RemainingName->Buffer++;
58         RemainingName->Length -= sizeof(WCHAR);
59         RemainingName->MaximumLength -= sizeof(WCHAR);
60     }
61 
62     /* See how many chars we parsed and validate the length */
63     NextName->Length = (USHORT)((ULONG_PTR)RemainingName->Buffer -
64                                 (ULONG_PTR)NextName->Buffer);
65     if (NextName->Length > 512) NameValid = FALSE;
66     NextName->MaximumLength = NextName->Length;
67 
68     /* If there's nothing left, we're last */
69     *LastName = !RemainingName->Length;
70     return NameValid;
71 }
72 
73 BOOLEAN
74 NTAPI
75 CmpGetSymbolicLink(IN PHHIVE Hive,
76                    IN OUT PUNICODE_STRING ObjectName,
77                    IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb,
78                    IN PUNICODE_STRING RemainingName OPTIONAL)
79 {
80     HCELL_INDEX LinkCell = HCELL_NIL;
81     PCM_KEY_VALUE LinkValue = NULL;
82     PWSTR LinkName = NULL;
83     BOOLEAN LinkNameAllocated = FALSE;
84     PWSTR NewBuffer;
85     ULONG Length = 0;
86     ULONG ValueLength = 0;
87     BOOLEAN Result = FALSE;
88     HCELL_INDEX CellToRelease = HCELL_NIL;
89     PCM_KEY_NODE Node;
90     UNICODE_STRING NewObjectName;
91 
92     /* Make sure we're not being deleted */
93     if (SymbolicKcb->Delete) return FALSE;
94 
95     /* Get the key node */
96     Node = (PCM_KEY_NODE)HvGetCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell);
97     if (!Node) goto Exit;
98 
99     /* Find the symbolic link key */
100     LinkCell = CmpFindValueByName(Hive, Node, &CmSymbolicLinkValueName);
101     HvReleaseCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell);
102     if (LinkCell == HCELL_NIL) goto Exit;
103 
104     /* Get the value cell */
105     LinkValue = (PCM_KEY_VALUE)HvGetCell(Hive, LinkCell);
106     if (!LinkValue) goto Exit;
107 
108     /* Make sure it's a registry link */
109     if (LinkValue->Type != REG_LINK) goto Exit;
110 
111     /* Now read the value data */
112     if (!CmpGetValueData(Hive,
113                          LinkValue,
114                          &ValueLength,
115                          (PVOID*)&LinkName,
116                          &LinkNameAllocated,
117                          &CellToRelease))
118     {
119         /* Fail */
120         goto Exit;
121     }
122 
123     /* Get the length */
124     Length = ValueLength + sizeof(WCHAR);
125 
126     /* Make sure we start with a slash */
127     if (*LinkName != OBJ_NAME_PATH_SEPARATOR) goto Exit;
128 
129     /* Add the remaining name if needed */
130     if (RemainingName) Length += RemainingName->Length + sizeof(WCHAR);
131 
132     /* Check for overflow */
133     if (Length > 0xFFFF) goto Exit;
134 
135     /* Check if we need a new buffer */
136     if (Length > ObjectName->MaximumLength)
137     {
138         /* We do -- allocate one */
139         NewBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
140         if (!NewBuffer) goto Exit;
141 
142         /* Setup the new string and copy the symbolic target */
143         NewObjectName.Buffer = NewBuffer;
144         NewObjectName.MaximumLength = (USHORT)Length;
145         NewObjectName.Length = (USHORT)ValueLength;
146         RtlCopyMemory(NewBuffer, LinkName, ValueLength);
147 
148         /* Check if we need to add anything else */
149         if (RemainingName)
150         {
151             /* Add the remaining name */
152             NewBuffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
153             NewObjectName.Length += sizeof(WCHAR);
154             RtlAppendUnicodeStringToString(&NewObjectName, RemainingName);
155         }
156 
157         /* Free the old buffer */
158         ExFreePool(ObjectName->Buffer);
159         *ObjectName = NewObjectName;
160     }
161     else
162     {
163         /* The old name is large enough -- update the length */
164         ObjectName->Length = (USHORT)ValueLength;
165         if (RemainingName)
166         {
167             /* Copy the remaining name inside */
168             RtlMoveMemory(&ObjectName->Buffer[(ValueLength / sizeof(WCHAR)) + 1],
169                           RemainingName->Buffer,
170                           RemainingName->Length);
171 
172             /* Add the slash and update the length */
173             ObjectName->Buffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
174             ObjectName->Length += RemainingName->Length + sizeof(WCHAR);
175         }
176 
177         /* Copy the symbolic link target name */
178         RtlCopyMemory(ObjectName->Buffer, LinkName, ValueLength);
179     }
180 
181     /* Null-terminate the whole thing */
182     ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] = UNICODE_NULL;
183     Result = TRUE;
184 
185 Exit:
186     /* Free the link name */
187     if (LinkNameAllocated) ExFreePool(LinkName);
188 
189     /* Check if we had a value cell */
190     if (LinkValue)
191     {
192         /* Release it */
193         ASSERT(LinkCell != HCELL_NIL);
194         HvReleaseCell(Hive, LinkCell);
195     }
196 
197     /* Check if we had an active cell and release it, then return the result */
198     if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
199     return Result;
200 }
201 
202 NTSTATUS
203 NTAPI
204 CmpDoCreateChild(IN PHHIVE Hive,
205                  IN HCELL_INDEX ParentCell,
206                  IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
207                  IN PACCESS_STATE AccessState,
208                  IN PUNICODE_STRING Name,
209                  IN KPROCESSOR_MODE AccessMode,
210                  IN PCM_PARSE_CONTEXT ParseContext,
211                  IN PCM_KEY_CONTROL_BLOCK ParentKcb,
212                  IN ULONG Flags,
213                  OUT PHCELL_INDEX KeyCell,
214                  OUT PVOID *Object)
215 {
216     NTSTATUS Status = STATUS_SUCCESS;
217     PCM_KEY_BODY KeyBody;
218     HCELL_INDEX ClassCell = HCELL_NIL;
219     PCM_KEY_NODE KeyNode;
220     PCELL_DATA CellData;
221     ULONG StorageType;
222     PCM_KEY_CONTROL_BLOCK Kcb;
223     PSECURITY_DESCRIPTOR NewDescriptor;
224 
225     /* Get the storage type */
226     StorageType = Stable;
227     if (ParseContext->CreateOptions & REG_OPTION_VOLATILE) StorageType = Volatile;
228 
229     /* Allocate the child */
230     *KeyCell = HvAllocateCell(Hive,
231                               FIELD_OFFSET(CM_KEY_NODE, Name) +
232                               CmpNameSize(Hive, Name),
233                               StorageType,
234                               HCELL_NIL);
235     if (*KeyCell == HCELL_NIL)
236     {
237         /* Fail */
238         Status = STATUS_INSUFFICIENT_RESOURCES;
239         goto Quickie;
240     }
241 
242     /* Get the key node */
243     KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, *KeyCell);
244     if (!KeyNode)
245     {
246         /* Fail, this should never happen */
247         ASSERT(FALSE);
248         Status = STATUS_INSUFFICIENT_RESOURCES;
249         goto Quickie;
250     }
251 
252     /* Release the cell */
253     HvReleaseCell(Hive, *KeyCell);
254 
255     /* Check if we have a class name */
256     if (ParseContext->Class.Length > 0)
257     {
258         /* Allocate a class cell */
259         ClassCell = HvAllocateCell(Hive,
260                                    ParseContext->Class.Length,
261                                    StorageType,
262                                    HCELL_NIL);
263         if (ClassCell == HCELL_NIL)
264         {
265             /* Fail */
266             Status = STATUS_INSUFFICIENT_RESOURCES;
267             goto Quickie;
268         }
269     }
270 
271     /* Allocate the Cm Object */
272     Status = ObCreateObject(AccessMode,
273                             CmpKeyObjectType,
274                             NULL,
275                             AccessMode,
276                             NULL,
277                             sizeof(CM_KEY_BODY),
278                             0,
279                             0,
280                             Object);
281     if (!NT_SUCCESS(Status)) goto Quickie;
282 
283     /* Setup the key body */
284     KeyBody = (PCM_KEY_BODY)(*Object);
285     KeyBody->Type = CM_KEY_BODY_TYPE;
286     KeyBody->KeyControlBlock = NULL;
287 
288     /* Check if we had a class */
289     if (ParseContext->Class.Length > 0)
290     {
291         /* Get the class cell */
292         CellData = HvGetCell(Hive, ClassCell);
293         if (!CellData)
294         {
295             /* Fail, this should never happen */
296             ASSERT(FALSE);
297             Status = STATUS_INSUFFICIENT_RESOURCES;
298             ObDereferenceObject(*Object);
299             goto Quickie;
300         }
301 
302         /* Release the cell */
303         HvReleaseCell(Hive, ClassCell);
304 
305         /* Copy the class data */
306         RtlCopyMemory(&CellData->u.KeyString[0],
307                       ParseContext->Class.Buffer,
308                       ParseContext->Class.Length);
309     }
310 
311     /* Fill out the key node */
312     KeyNode->Signature = CM_KEY_NODE_SIGNATURE;
313     KeyNode->Flags = Flags;
314     KeQuerySystemTime(&KeyNode->LastWriteTime);
315     KeyNode->Spare = 0;
316     KeyNode->Parent = ParentCell;
317     KeyNode->SubKeyCounts[Stable] = 0;
318     KeyNode->SubKeyCounts[Volatile] = 0;
319     KeyNode->SubKeyLists[Stable] = HCELL_NIL;
320     KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
321     KeyNode->ValueList.Count = 0;
322     KeyNode->ValueList.List = HCELL_NIL;
323     KeyNode->Security = HCELL_NIL;
324     KeyNode->Class = ClassCell;
325     KeyNode->ClassLength = ParseContext->Class.Length;
326     KeyNode->MaxValueDataLen = 0;
327     KeyNode->MaxNameLen = 0;
328     KeyNode->MaxValueNameLen = 0;
329     KeyNode->MaxClassLen = 0;
330     KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name);
331     if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME;
332 
333     /* Create the KCB */
334     Kcb = CmpCreateKeyControlBlock(Hive,
335                                    *KeyCell,
336                                    KeyNode,
337                                    ParentKcb,
338                                    0, // CMP_LOCK_HASHES_FOR_KCB,
339                                    Name);
340     if (!Kcb)
341     {
342         /* Fail */
343         ObDereferenceObjectDeferDelete(*Object);
344         Status = STATUS_INSUFFICIENT_RESOURCES;
345         goto Quickie;
346     }
347 
348     /* Sanity check */
349     ASSERT(Kcb->RefCount == 1);
350 
351     /* Now fill out the Cm object */
352     KeyBody->NotifyBlock = NULL;
353     KeyBody->ProcessID = PsGetCurrentProcessId();
354     KeyBody->KeyControlBlock = Kcb;
355 
356     /* Link it with the KCB */
357     EnlistKeyBodyWithKCB(KeyBody, 0);
358 
359     /* Assign security */
360     Status = SeAssignSecurity(ParentDescriptor,
361                               AccessState->SecurityDescriptor,
362                               &NewDescriptor,
363                               TRUE,
364                               &AccessState->SubjectSecurityContext,
365                               &CmpKeyObjectType->TypeInfo.GenericMapping,
366                               CmpKeyObjectType->TypeInfo.PoolType);
367     if (NT_SUCCESS(Status))
368     {
369         Status = CmpSecurityMethod(*Object,
370                                    AssignSecurityDescriptor,
371                                    NULL,
372                                    NewDescriptor,
373                                    NULL,
374                                    NULL,
375                                    CmpKeyObjectType->TypeInfo.PoolType,
376                                    &CmpKeyObjectType->TypeInfo.GenericMapping);
377     }
378 
379     /* Now that the security descriptor is copied in the hive, we can free the original */
380     SeDeassignSecurity(&NewDescriptor);
381 
382     if (NT_SUCCESS(Status))
383     {
384         /* Send notification to registered callbacks */
385         CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME);
386     }
387 
388 Quickie:
389     /* Check if we got here because of failure */
390     if (!NT_SUCCESS(Status))
391     {
392         /* Free any cells we might've allocated */
393         if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell);
394         HvFreeCell(Hive, *KeyCell);
395     }
396 
397     /* Return status */
398     return Status;
399 }
400 
401 NTSTATUS
402 NTAPI
403 CmpDoCreate(IN PHHIVE Hive,
404             IN HCELL_INDEX Cell,
405             IN PACCESS_STATE AccessState,
406             IN PUNICODE_STRING Name,
407             IN KPROCESSOR_MODE AccessMode,
408             IN PCM_PARSE_CONTEXT ParseContext,
409             IN PCM_KEY_CONTROL_BLOCK ParentKcb,
410             OUT PVOID *Object)
411 {
412     NTSTATUS Status;
413     PCELL_DATA CellData;
414     HCELL_INDEX KeyCell;
415     ULONG ParentType;
416     PCM_KEY_BODY KeyBody;
417     PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
418     LARGE_INTEGER TimeStamp;
419     PCM_KEY_NODE KeyNode;
420 
421     /* Check if the parent is being deleted */
422     if (ParentKcb->Delete)
423     {
424         /* It has, quit */
425         ASSERT(FALSE);
426         Status = STATUS_OBJECT_NAME_NOT_FOUND;
427         goto Exit;
428     }
429 
430     /* Get the parent node */
431     KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
432     if (!KeyNode)
433     {
434         /* Fail */
435         ASSERT(FALSE);
436         Status = STATUS_INSUFFICIENT_RESOURCES;
437         goto Exit;
438     }
439 
440     /* Make sure nobody added us yet */
441     if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL)
442     {
443         /* Fail */
444         ASSERT(FALSE);
445         Status = STATUS_REPARSE;
446         goto Exit;
447     }
448 
449     /* Sanity check */
450     ASSERT(Cell == ParentKcb->KeyCell);
451 
452     /* Get the parent type */
453     ParentType = HvGetCellType(Cell);
454     if ((ParentType == Volatile) &&
455         !(ParseContext->CreateOptions & REG_OPTION_VOLATILE))
456     {
457         /* Children of volatile parents must also be volatile */
458         //ASSERT(FALSE);
459         Status = STATUS_CHILD_MUST_BE_VOLATILE;
460         goto Exit;
461     }
462 
463     /* Don't allow children under symlinks */
464     if (ParentKcb->Flags & KEY_SYM_LINK)
465     {
466         /* Fail */
467         ASSERT(FALSE);
468         Status = STATUS_ACCESS_DENIED;
469         goto Exit;
470     }
471 
472     /* Make the cell dirty for now */
473     HvMarkCellDirty(Hive, Cell, FALSE);
474 
475     /* Do the actual create operation */
476     Status = CmpDoCreateChild(Hive,
477                               Cell,
478                               SecurityDescriptor,
479                               AccessState,
480                               Name,
481                               AccessMode,
482                               ParseContext,
483                               ParentKcb,
484                               0,
485                               &KeyCell,
486                               Object);
487     if (NT_SUCCESS(Status))
488     {
489         /* Get the key body */
490         KeyBody = (PCM_KEY_BODY)(*Object);
491 
492         /* Now add the subkey */
493         if (!CmpAddSubKey(Hive, Cell, KeyCell))
494         {
495             /* Failure! We don't handle this yet! */
496             ASSERT(FALSE);
497         }
498 
499         /* Get the key node */
500         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
501         if (!KeyNode)
502         {
503             /* Fail, this shouldn't happen */
504             ASSERT(FALSE);
505         }
506 
507         /* Sanity checks */
508         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
509         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
510         ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb);
511         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
512 
513         /* Update the timestamp */
514         KeQuerySystemTime(&TimeStamp);
515         KeyNode->LastWriteTime = TimeStamp;
516         KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
517 
518         /* Check if we need to update name maximum */
519         if (KeyNode->MaxNameLen < Name->Length)
520         {
521             /* Do it */
522             KeyNode->MaxNameLen = Name->Length;
523             KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length;
524         }
525 
526         /* Check if we need to update class length maximum */
527         if (KeyNode->MaxClassLen < ParseContext->Class.Length)
528         {
529             /* Update it */
530             KeyNode->MaxClassLen = ParseContext->Class.Length;
531         }
532 
533         /* Check if we're creating a symbolic link */
534         if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK)
535         {
536             /* Get the cell data */
537             CellData = HvGetCell(Hive, KeyCell);
538             if (!CellData)
539             {
540                 /* This shouldn't happen */
541                 ASSERT(FALSE);
542             }
543 
544             /* Update the flags */
545             CellData->u.KeyNode.Flags |= KEY_SYM_LINK;
546             KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags;
547             HvReleaseCell(Hive, KeyCell);
548         }
549     }
550 
551 Exit:
552     /* Release the flusher lock and return status */
553     return Status;
554 }
555 
556 NTSTATUS
557 NTAPI
558 CmpDoOpen(IN PHHIVE Hive,
559           IN HCELL_INDEX Cell,
560           IN PCM_KEY_NODE Node,
561           IN PACCESS_STATE AccessState,
562           IN KPROCESSOR_MODE AccessMode,
563           IN ULONG Attributes,
564           IN PCM_PARSE_CONTEXT Context OPTIONAL,
565           IN ULONG ControlFlags,
566           IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb,
567           IN PUNICODE_STRING KeyName,
568           OUT PVOID *Object)
569 {
570     NTSTATUS Status;
571     PCM_KEY_BODY KeyBody = NULL;
572     PCM_KEY_CONTROL_BLOCK Kcb = NULL;
573 
574     /* Make sure the hive isn't locked */
575     if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
576         (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
577     {
578         /* It is, don't touch it */
579         return STATUS_OBJECT_NAME_NOT_FOUND;
580     }
581 
582     /* Check if we have a context */
583     if (Context)
584     {
585         /* Check if this is a link create (which shouldn't be an open) */
586         if (Context->CreateLink)
587         {
588             return STATUS_ACCESS_DENIED;
589         }
590 
591         /* Check if this is symlink create attempt */
592         if (Context->CreateOptions & REG_OPTION_CREATE_LINK)
593         {
594             /* Key already exists */
595             return STATUS_OBJECT_NAME_COLLISION;
596         }
597 
598         /* Set the disposition */
599         Context->Disposition = REG_OPENED_EXISTING_KEY;
600     }
601 
602     /* Do this in the registry lock */
603     CmpLockRegistry();
604 
605     /* If we have a KCB, make sure it's locked */
606     //ASSERT(CmpIsKcbLockedExclusive(*CachedKcb));
607 
608     /* Check if caller doesn't want to create a KCB */
609     if (ControlFlags & CMP_OPEN_KCB_NO_CREATE)
610     {
611         /* Check if this is a symlink */
612         if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
613         {
614             /* This case for a cached KCB is not implemented yet */
615             ASSERT(FALSE);
616         }
617 
618         /* The caller wants to open a cached KCB */
619         if (!CmpReferenceKeyControlBlock(*CachedKcb))
620         {
621             /* Release the registry lock */
622             CmpUnlockRegistry();
623 
624             /* Return failure code */
625             return STATUS_INSUFFICIENT_RESOURCES;
626         }
627 
628         /* Our kcb is that one */
629         Kcb = *CachedKcb;
630     }
631     else
632     {
633         /* Check if this is a symlink */
634         if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
635         {
636             /* Create the KCB for the symlink */
637             Kcb = CmpCreateKeyControlBlock(Hive,
638                                            Cell,
639                                            Node,
640                                            *CachedKcb,
641                                            0,
642                                            KeyName);
643             if (!Kcb)
644             {
645                 /* Release registry lock and return failure */
646                 CmpUnlockRegistry();
647                 return STATUS_INSUFFICIENT_RESOURCES;
648             }
649 
650             /* Make sure it's also locked, and set the pointer */
651             //ASSERT(CmpIsKcbLockedExclusive(Kcb));
652             *CachedKcb = Kcb;
653 
654             /* Release the registry lock */
655             CmpUnlockRegistry();
656 
657             /* Return reparse required */
658             return STATUS_REPARSE;
659         }
660 
661         /* Create the KCB. FIXME: Use lock flag */
662         Kcb = CmpCreateKeyControlBlock(Hive,
663                                        Cell,
664                                        Node,
665                                        *CachedKcb,
666                                        0,
667                                        KeyName);
668         if (!Kcb)
669         {
670             /* Release registry lock and return failure */
671             CmpUnlockRegistry();
672             return STATUS_INSUFFICIENT_RESOURCES;
673         }
674     }
675 
676     /* Make sure it's also locked, and set the pointer */
677     //ASSERT(CmpIsKcbLockedExclusive(Kcb));
678     *CachedKcb = Kcb;
679 
680     /* Release the registry lock */
681     CmpUnlockRegistry();
682 
683     /* Allocate the key object */
684     Status = ObCreateObject(AccessMode,
685                             CmpKeyObjectType,
686                             NULL,
687                             AccessMode,
688                             NULL,
689                             sizeof(CM_KEY_BODY),
690                             0,
691                             0,
692                             Object);
693     if (NT_SUCCESS(Status))
694     {
695         /* Get the key body and fill it out */
696         KeyBody = (PCM_KEY_BODY)(*Object);
697         KeyBody->KeyControlBlock = Kcb;
698         KeyBody->Type = CM_KEY_BODY_TYPE;
699         KeyBody->ProcessID = PsGetCurrentProcessId();
700         KeyBody->NotifyBlock = NULL;
701 
702         /* Link to the KCB */
703         EnlistKeyBodyWithKCB(KeyBody, 0);
704 
705         if (!ObCheckObjectAccess(*Object,
706                                  AccessState,
707                                  FALSE,
708                                  AccessMode,
709                                  &Status))
710         {
711             /* Access check failed */
712             ObDereferenceObject(*Object);
713         }
714     }
715     else
716     {
717         /* Failed, dereference the KCB */
718         CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
719     }
720 
721     /* Return status */
722     return Status;
723 }
724 
725 NTSTATUS
726 NTAPI
727 CmpCreateLinkNode(IN PHHIVE Hive,
728                   IN HCELL_INDEX Cell,
729                   IN PACCESS_STATE AccessState,
730                   IN UNICODE_STRING Name,
731                   IN KPROCESSOR_MODE AccessMode,
732                   IN ULONG CreateOptions,
733                   IN PCM_PARSE_CONTEXT Context,
734                   IN PCM_KEY_CONTROL_BLOCK ParentKcb,
735                   OUT PVOID *Object)
736 {
737     NTSTATUS Status;
738     HCELL_INDEX KeyCell, LinkCell, ChildCell;
739     PCM_KEY_BODY KeyBody;
740     LARGE_INTEGER TimeStamp;
741     PCM_KEY_NODE KeyNode;
742     PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb;
743 
744     /* Link nodes only allowed on the master */
745     if (Hive != &CmiVolatileHive->Hive)
746     {
747         /* Fail */
748         DPRINT1("Invalid link node attempt\n");
749         return STATUS_ACCESS_DENIED;
750     }
751 
752     /* Check if the parent is being deleted */
753     if (ParentKcb->Delete)
754     {
755         /* It is, quit */
756         ASSERT(FALSE);
757         Status = STATUS_OBJECT_NAME_NOT_FOUND;
758         goto Exit;
759     }
760 
761     /* Allocate a link node */
762     LinkCell = HvAllocateCell(Hive,
763                               FIELD_OFFSET(CM_KEY_NODE, Name) +
764                               CmpNameSize(Hive, &Name),
765                               Stable,
766                               HCELL_NIL);
767     if (LinkCell == HCELL_NIL)
768     {
769         /* Fail */
770         Status = STATUS_INSUFFICIENT_RESOURCES;
771         goto Exit;
772     }
773 
774     /* Get the key cell */
775     KeyCell = Context->ChildHive.KeyCell;
776     if (KeyCell != HCELL_NIL)
777     {
778         /* Hive exists! */
779         ChildCell = KeyCell;
780 
781         /* Get the node data */
782         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
783         if (!KeyNode)
784         {
785             /* Fail */
786             ASSERT(FALSE);
787             Status = STATUS_INSUFFICIENT_RESOURCES;
788             goto Exit;
789         }
790 
791         /* Fill out the data */
792         KeyNode->Parent = LinkCell;
793         KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
794         HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
795 
796         /* Now open the key cell */
797         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell);
798         if (!KeyNode)
799         {
800             /* Fail */
801             ASSERT(FALSE);
802             Status = STATUS_INSUFFICIENT_RESOURCES;
803             goto Exit;
804         }
805 
806         /* Open the parent */
807         Status = CmpDoOpen(Context->ChildHive.KeyHive,
808                            KeyCell,
809                            KeyNode,
810                            AccessState,
811                            AccessMode,
812                            CreateOptions,
813                            NULL,
814                            0,
815                            &Kcb,
816                            &Name,
817                            Object);
818         HvReleaseCell(Context->ChildHive.KeyHive, KeyCell);
819     }
820     else
821     {
822         /* Do the actual create operation */
823         Status = CmpDoCreateChild(Context->ChildHive.KeyHive,
824                                   Cell,
825                                   NULL,
826                                   AccessState,
827                                   &Name,
828                                   AccessMode,
829                                   Context,
830                                   ParentKcb,
831                                   KEY_HIVE_ENTRY | KEY_NO_DELETE,
832                                   &ChildCell,
833                                   Object);
834         if (NT_SUCCESS(Status))
835         {
836             /* Setup root pointer */
837             Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell;
838         }
839     }
840 
841     /* Check if open or create suceeded */
842     if (NT_SUCCESS(Status))
843     {
844         /* Mark the cell dirty */
845         HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE);
846 
847         /* Get the key node */
848         KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
849         if (!KeyNode)
850         {
851             /* Fail */
852             ASSERT(FALSE);
853             Status = STATUS_INSUFFICIENT_RESOURCES;
854             goto Exit;
855         }
856 
857         /* Release it */
858         HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
859 
860         /* Set the parent and flags */
861         KeyNode->Parent = LinkCell;
862         KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
863 
864         /* Get the link node */
865         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, LinkCell);
866         if (!KeyNode)
867         {
868             /* Fail */
869             ASSERT(FALSE);
870             Status = STATUS_INSUFFICIENT_RESOURCES;
871             goto Exit;
872         }
873 
874         /* Set it up */
875         KeyNode->Signature = CM_LINK_NODE_SIGNATURE;
876         KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE;
877         KeyNode->Parent = Cell;
878         KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name);
879         if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME;
880         KeQuerySystemTime(&TimeStamp);
881         KeyNode->LastWriteTime = TimeStamp;
882 
883         /* Clear out the rest */
884         KeyNode->SubKeyCounts[Stable] = 0;
885         KeyNode->SubKeyCounts[Volatile] = 0;
886         KeyNode->SubKeyLists[Stable] = HCELL_NIL;
887         KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
888         KeyNode->ValueList.Count = 0;
889         KeyNode->ValueList.List = HCELL_NIL;
890         KeyNode->ClassLength = 0;
891 
892         /* Reference the root node */
893         KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive;
894         KeyNode->ChildHiveReference.KeyCell = ChildCell;
895         HvReleaseCell(Hive, LinkCell);
896 
897         /* Get the parent node */
898         KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
899         if (!KeyNode)
900         {
901             /* Fail */
902             ASSERT(FALSE);
903             Status = STATUS_INSUFFICIENT_RESOURCES;
904             goto Exit;
905         }
906 
907         /* Now add the subkey */
908         if (!CmpAddSubKey(Hive, Cell, LinkCell))
909         {
910             /* Failure! We don't handle this yet! */
911             ASSERT(FALSE);
912         }
913 
914         /* Get the key body */
915         KeyBody = (PCM_KEY_BODY)*Object;
916 
917         /* Sanity checks */
918         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
919         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
920         ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
921 
922         /* Update the timestamp */
923         KeQuerySystemTime(&TimeStamp);
924         KeyNode->LastWriteTime = TimeStamp;
925         KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
926 
927         /* Check if we need to update name maximum */
928         if (KeyNode->MaxNameLen < Name.Length)
929         {
930             /* Do it */
931             KeyNode->MaxNameLen = Name.Length;
932             KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length;
933         }
934 
935         /* Check if we need to update class length maximum */
936         if (KeyNode->MaxClassLen < Context->Class.Length)
937         {
938             /* Update it */
939             KeyNode->MaxClassLen = Context->Class.Length;
940         }
941 
942         /* Release the cell */
943         HvReleaseCell(Hive, Cell);
944     }
945     else
946     {
947         /* Release the link cell */
948         HvReleaseCell(Hive, LinkCell);
949     }
950 
951 Exit:
952     /* Release the flusher locks and return status */
953     return Status;
954 }
955 
956 VOID
957 NTAPI
958 CmpHandleExitNode(IN OUT PHHIVE *Hive,
959                   IN OUT HCELL_INDEX *Cell,
960                   IN OUT PCM_KEY_NODE *KeyNode,
961                   IN OUT PHHIVE *ReleaseHive,
962                   IN OUT HCELL_INDEX *ReleaseCell)
963 {
964     /* Check if we have anything to release */
965     if (*ReleaseCell != HCELL_NIL)
966     {
967         /* Release it */
968         ASSERT(*ReleaseHive != NULL);
969         HvReleaseCell(*ReleaseHive, *ReleaseCell);
970     }
971 
972     /* Get the link references */
973     *Hive = (*KeyNode)->ChildHiveReference.KeyHive;
974     *Cell = (*KeyNode)->ChildHiveReference.KeyCell;
975 
976     /* Get the new node */
977     *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell);
978     if (*KeyNode)
979     {
980         /* Set the new release values */
981         *ReleaseCell = *Cell;
982         *ReleaseHive = *Hive;
983     }
984     else
985     {
986         /* Nothing to release */
987         *ReleaseCell = HCELL_NIL;
988         *ReleaseHive = NULL;
989     }
990 }
991 
992 NTSTATUS
993 NTAPI
994 CmpBuildHashStackAndLookupCache(IN PCM_KEY_BODY ParseObject,
995                                 IN OUT PCM_KEY_CONTROL_BLOCK *Kcb,
996                                 IN PUNICODE_STRING Current,
997                                 OUT PHHIVE *Hive,
998                                 OUT HCELL_INDEX *Cell,
999                                 OUT PULONG TotalRemainingSubkeys,
1000                                 OUT PULONG MatchRemainSubkeyLevel,
1001                                 OUT PULONG TotalSubkeys,
1002                                 OUT PULONG OuterStackArray,
1003                                 OUT PULONG *LockedKcbs)
1004 {
1005     /* We don't lock anything for now */
1006     *LockedKcbs = NULL;
1007 
1008     /* Calculate hash values */
1009     *TotalRemainingSubkeys = 0xBAADF00D;
1010 
1011     /* Lock the registry */
1012     CmpLockRegistry();
1013 
1014     /* Return hive and cell data */
1015     *Hive = (*Kcb)->KeyHive;
1016     *Cell = (*Kcb)->KeyCell;
1017 
1018     /* Make sure it's not a dead KCB */
1019     ASSERT((*Kcb)->RefCount > 0);
1020 
1021     /* Reference it */
1022     (VOID)CmpReferenceKeyControlBlock(*Kcb);
1023 
1024     /* Return success for now */
1025     return STATUS_SUCCESS;
1026 }
1027 
1028 NTSTATUS
1029 NTAPI
1030 CmpParseKey(IN PVOID ParseObject,
1031             IN PVOID ObjectType,
1032             IN OUT PACCESS_STATE AccessState,
1033             IN KPROCESSOR_MODE AccessMode,
1034             IN ULONG Attributes,
1035             IN OUT PUNICODE_STRING CompleteName,
1036             IN OUT PUNICODE_STRING RemainingName,
1037             IN OUT PVOID Context OPTIONAL,
1038             IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
1039             OUT PVOID *Object)
1040 {
1041     NTSTATUS Status;
1042     PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb;
1043     PHHIVE Hive = NULL;
1044     PCM_KEY_NODE Node = NULL;
1045     HCELL_INDEX Cell = HCELL_NIL, NextCell;
1046     PHHIVE HiveToRelease = NULL;
1047     HCELL_INDEX CellToRelease = HCELL_NIL;
1048     UNICODE_STRING Current, NextName;
1049     PCM_PARSE_CONTEXT ParseContext = Context;
1050     ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0;
1051     PULONG LockedKcbs = NULL;
1052     BOOLEAN Result, Last;
1053     PAGED_CODE();
1054 
1055     /* Loop path separators at the end */
1056     while ((RemainingName->Length) &&
1057            (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] ==
1058             OBJ_NAME_PATH_SEPARATOR))
1059     {
1060         /* Remove path separator */
1061         RemainingName->Length -= sizeof(WCHAR);
1062     }
1063 
1064     /* Fail if this isn't a key object */
1065     if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH;
1066 
1067     /* Copy the remaining name */
1068     Current = *RemainingName;
1069 
1070     /* Check if this is a create */
1071     if (!(ParseContext) || !(ParseContext->CreateOperation))
1072     {
1073         /* It isn't, so no context */
1074         ParseContext = NULL;
1075     }
1076 
1077     /* Grab the KCB */
1078     Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock;
1079 
1080     /* Sanity check */
1081     ASSERT(Kcb != NULL);
1082 
1083     /* Fail if the key was marked as deleted */
1084     if (Kcb->Delete)
1085         return STATUS_KEY_DELETED;
1086 
1087     /* Lookup in the cache */
1088     Status = CmpBuildHashStackAndLookupCache(ParseObject,
1089                                              &Kcb,
1090                                              &Current,
1091                                              &Hive,
1092                                              &Cell,
1093                                              &TotalRemainingSubkeys,
1094                                              &MatchRemainSubkeyLevel,
1095                                              &TotalSubkeys,
1096                                              NULL,
1097                                              &LockedKcbs);
1098 
1099     /* This is now the parent */
1100     ParentKcb = Kcb;
1101 
1102     /* Sanity check */
1103     ASSERT(ParentKcb != NULL);
1104 
1105     /* Check if everything was found cached */
1106     if (!TotalRemainingSubkeys)
1107         ASSERTMSG("Caching not implemented\n", FALSE);
1108 
1109     /* Don't do anything if we're being deleted */
1110     if (Kcb->Delete)
1111     {
1112         Status = STATUS_OBJECT_NAME_NOT_FOUND;
1113         goto Quickie;
1114     }
1115 
1116     /* Check if this is a symlink */
1117     if (Kcb->Flags & KEY_SYM_LINK)
1118     {
1119         /* Get the next name */
1120         Result = CmpGetNextName(&Current, &NextName, &Last);
1121         Current.Buffer = NextName.Buffer;
1122 
1123         /* Validate the current name string length */
1124         if (Current.Length + NextName.Length > MAXUSHORT)
1125         {
1126             /* too long */
1127             Status = STATUS_NAME_TOO_LONG;
1128             goto Quickie;
1129         }
1130         Current.Length += NextName.Length;
1131 
1132         /* Validate the current name string maximum length */
1133         if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
1134         {
1135             /* too long */
1136             Status = STATUS_NAME_TOO_LONG;
1137             goto Quickie;
1138         }
1139         Current.MaximumLength += NextName.MaximumLength;
1140 
1141         /* Parse the symlink */
1142         if (CmpGetSymbolicLink(Hive,
1143                                CompleteName,
1144                                Kcb,
1145                                &Current))
1146         {
1147             /* Symlink parse succeeded */
1148             Status = STATUS_REPARSE;
1149         }
1150         else
1151         {
1152             /* Couldn't find symlink */
1153             Status = STATUS_OBJECT_NAME_NOT_FOUND;
1154         }
1155 
1156         /* We're done */
1157         goto Quickie;
1158     }
1159 
1160     /* Get the key node */
1161     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1162     if (!Node)
1163     {
1164         Status = STATUS_INSUFFICIENT_RESOURCES;
1165         goto Quickie;
1166     }
1167 
1168     /* Start parsing */
1169     Status = STATUS_NOT_IMPLEMENTED;
1170     while (TRUE)
1171     {
1172         /* Get the next component */
1173         Result = CmpGetNextName(&Current, &NextName, &Last);
1174         if ((Result) && (NextName.Length))
1175         {
1176             /* See if this is a sym link */
1177             if (!(Kcb->Flags & KEY_SYM_LINK))
1178             {
1179                 /* Find the subkey */
1180                 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName);
1181                 if (NextCell != HCELL_NIL)
1182                 {
1183                     /* Get the new node */
1184                     Cell = NextCell;
1185                     Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1186                     ASSERT(Node);
1187 
1188                     /* Check if this was the last key */
1189                     if (Last)
1190                     {
1191                         /* Is this an exit node */
1192                         if (Node->Flags & KEY_HIVE_EXIT)
1193                         {
1194                             /* Handle it */
1195                             CmpHandleExitNode(&Hive,
1196                                               &Cell,
1197                                               &Node,
1198                                               &HiveToRelease,
1199                                               &CellToRelease);
1200                             if (!Node)
1201                             {
1202                                 /* Fail */
1203                                 Status = STATUS_INSUFFICIENT_RESOURCES;
1204                                 break;
1205                             }
1206                         }
1207 
1208                         /* Do the open */
1209                         Status = CmpDoOpen(Hive,
1210                                            Cell,
1211                                            Node,
1212                                            AccessState,
1213                                            AccessMode,
1214                                            Attributes,
1215                                            ParseContext,
1216                                            0,
1217                                            &Kcb,
1218                                            &NextName,
1219                                            Object);
1220                         if (Status == STATUS_REPARSE)
1221                         {
1222                             /* Parse the symlink */
1223                             if (!CmpGetSymbolicLink(Hive,
1224                                                     CompleteName,
1225                                                     Kcb,
1226                                                     NULL))
1227                             {
1228                                 /* Symlink parse failed */
1229                                 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1230                             }
1231                         }
1232 
1233                         /* We are done */
1234                         break;
1235                     }
1236 
1237                     /* Is this an exit node */
1238                     if (Node->Flags & KEY_HIVE_EXIT)
1239                     {
1240                         /* Handle it */
1241                         CmpHandleExitNode(&Hive,
1242                                           &Cell,
1243                                           &Node,
1244                                           &HiveToRelease,
1245                                           &CellToRelease);
1246                         if (!Node)
1247                         {
1248                             /* Fail */
1249                             Status = STATUS_INSUFFICIENT_RESOURCES;
1250                             break;
1251                         }
1252                     }
1253 
1254                     /* Create a KCB for this key */
1255                     Kcb = CmpCreateKeyControlBlock(Hive,
1256                                                    Cell,
1257                                                    Node,
1258                                                    ParentKcb,
1259                                                    0,
1260                                                    &NextName);
1261                     if (!Kcb)
1262                     {
1263                         /* Fail */
1264                         Status = STATUS_INSUFFICIENT_RESOURCES;
1265                         break;
1266                     }
1267 
1268                     /* Dereference the parent and set the new one */
1269                     CmpDereferenceKeyControlBlock(ParentKcb);
1270                     ParentKcb = Kcb;
1271                 }
1272                 else
1273                 {
1274                     /* Check if this was the last key for a create */
1275                     if ((Last) && (ParseContext))
1276                     {
1277                         /* Check if we're doing a link node */
1278                         if (ParseContext->CreateLink)
1279                         {
1280                             /* The only thing we should see */
1281                             Status = CmpCreateLinkNode(Hive,
1282                                                        Cell,
1283                                                        AccessState,
1284                                                        NextName,
1285                                                        AccessMode,
1286                                                        Attributes,
1287                                                        ParseContext,
1288                                                        ParentKcb,
1289                                                        Object);
1290                         }
1291                         else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates)
1292                         {
1293                             /* Creating keys in the master hive is not allowed */
1294                             Status = STATUS_INVALID_PARAMETER;
1295                         }
1296                         else
1297                         {
1298                             /* Do the create */
1299                             Status = CmpDoCreate(Hive,
1300                                                  Cell,
1301                                                  AccessState,
1302                                                  &NextName,
1303                                                  AccessMode,
1304                                                  ParseContext,
1305                                                  ParentKcb,
1306                                                  Object);
1307                         }
1308 
1309                         /* Check for reparse (in this case, someone beat us) */
1310                         if (Status == STATUS_REPARSE) break;
1311 
1312                         /* Update disposition */
1313                         ParseContext->Disposition = REG_CREATED_NEW_KEY;
1314                         break;
1315                     }
1316                     else
1317                     {
1318                         /* Key not found */
1319                         Status = STATUS_OBJECT_NAME_NOT_FOUND;
1320                         break;
1321                     }
1322                 }
1323             }
1324             else
1325             {
1326                 /* Save the next name */
1327                 Current.Buffer = NextName.Buffer;
1328 
1329                 /* Validate the current name string length */
1330                 if (Current.Length + NextName.Length > MAXUSHORT)
1331                 {
1332                     /* too long */
1333                     Status = STATUS_NAME_TOO_LONG;
1334                     break;
1335                 }
1336                 Current.Length += NextName.Length;
1337 
1338                 /* Validate the current name string maximum length */
1339                 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
1340                 {
1341                     /* too long */
1342                     Status = STATUS_NAME_TOO_LONG;
1343                     break;
1344                 }
1345                 Current.MaximumLength += NextName.MaximumLength;
1346 
1347                 /* Parse the symlink */
1348                 if (CmpGetSymbolicLink(Hive,
1349                                        CompleteName,
1350                                        Kcb,
1351                                        &Current))
1352                 {
1353                     /* Symlink parse succeeded */
1354                     Status = STATUS_REPARSE;
1355                 }
1356                 else
1357                 {
1358                     /* Couldn't find symlink */
1359                     Status = STATUS_OBJECT_NAME_NOT_FOUND;
1360                 }
1361 
1362                 /* We're done */
1363                 break;
1364             }
1365         }
1366         else if ((Result) && (Last))
1367         {
1368             /* Opening the root. Is this an exit node? */
1369             if (Node->Flags & KEY_HIVE_EXIT)
1370             {
1371                 /* Handle it */
1372                 CmpHandleExitNode(&Hive,
1373                                   &Cell,
1374                                   &Node,
1375                                   &HiveToRelease,
1376                                   &CellToRelease);
1377                 if (!Node)
1378                 {
1379                     /* Fail */
1380                     Status = STATUS_INSUFFICIENT_RESOURCES;
1381                     break;
1382                 }
1383             }
1384 
1385             /* Do the open */
1386             Status = CmpDoOpen(Hive,
1387                                Cell,
1388                                Node,
1389                                AccessState,
1390                                AccessMode,
1391                                Attributes,
1392                                ParseContext,
1393                                CMP_OPEN_KCB_NO_CREATE /* | CMP_CREATE_KCB_KCB_LOCKED */,
1394                                &Kcb,
1395                                &NextName,
1396                                Object);
1397             if (Status == STATUS_REPARSE)
1398             {
1399                 /* Nothing to do */
1400             }
1401 
1402             /* We're done */
1403             break;
1404         }
1405         else
1406         {
1407             /* Bogus */
1408             Status = STATUS_INVALID_PARAMETER;
1409             break;
1410         }
1411     }
1412 
1413     /* Dereference the parent if it exists */
1414 Quickie:
1415     if (ParentKcb)
1416         CmpDereferenceKeyControlBlock(ParentKcb);
1417 
1418     /* Unlock the registry */
1419     CmpUnlockRegistry();
1420     return Status;
1421 }
1422