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
CmpGetNextName(IN OUT PUNICODE_STRING RemainingName,OUT PUNICODE_STRING NextName,OUT PBOOLEAN LastName)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
CmpGetSymbolicLink(IN PHHIVE Hive,IN OUT PUNICODE_STRING ObjectName,IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb,IN PUNICODE_STRING RemainingName OPTIONAL)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
CmpDoCreateChild(IN PHHIVE Hive,IN HCELL_INDEX ParentCell,IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,IN PACCESS_STATE AccessState,IN PUNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN PCM_PARSE_CONTEXT ParseContext,IN PCM_KEY_CONTROL_BLOCK ParentKcb,IN ULONG Flags,OUT PHCELL_INDEX KeyCell,OUT PVOID * Object)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 KeyBody->KcbLocked = FALSE;
288
289 /* Check if we had a class */
290 if (ParseContext->Class.Length > 0)
291 {
292 /* Get the class cell */
293 CellData = HvGetCell(Hive, ClassCell);
294 if (!CellData)
295 {
296 /* Fail, this should never happen */
297 ASSERT(FALSE);
298 Status = STATUS_INSUFFICIENT_RESOURCES;
299 ObDereferenceObject(*Object);
300 goto Quickie;
301 }
302
303 /* Release the cell */
304 HvReleaseCell(Hive, ClassCell);
305
306 /* Copy the class data */
307 RtlCopyMemory(&CellData->u.KeyString[0],
308 ParseContext->Class.Buffer,
309 ParseContext->Class.Length);
310 }
311
312 /* Fill out the key node */
313 KeyNode->Signature = CM_KEY_NODE_SIGNATURE;
314 KeyNode->Flags = Flags;
315 KeQuerySystemTime(&KeyNode->LastWriteTime);
316 KeyNode->Spare = 0;
317 KeyNode->Parent = ParentCell;
318 KeyNode->SubKeyCounts[Stable] = 0;
319 KeyNode->SubKeyCounts[Volatile] = 0;
320 KeyNode->SubKeyLists[Stable] = HCELL_NIL;
321 KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
322 KeyNode->ValueList.Count = 0;
323 KeyNode->ValueList.List = HCELL_NIL;
324 KeyNode->Security = HCELL_NIL;
325 KeyNode->Class = ClassCell;
326 KeyNode->ClassLength = ParseContext->Class.Length;
327 KeyNode->MaxValueDataLen = 0;
328 KeyNode->MaxNameLen = 0;
329 KeyNode->MaxValueNameLen = 0;
330 KeyNode->MaxClassLen = 0;
331 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name);
332 if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME;
333
334 /* Create the KCB */
335 Kcb = CmpCreateKeyControlBlock(Hive,
336 *KeyCell,
337 KeyNode,
338 ParentKcb,
339 CMP_LOCK_HASHES_FOR_KCB,
340 Name);
341 if (!Kcb)
342 {
343 /* Fail */
344 ObDereferenceObjectDeferDelete(*Object);
345 Status = STATUS_INSUFFICIENT_RESOURCES;
346 goto Quickie;
347 }
348
349 /* Sanity check */
350 ASSERT(Kcb->RefCount == 1);
351
352 /* Now fill out the Cm object */
353 KeyBody->NotifyBlock = NULL;
354 KeyBody->ProcessID = PsGetCurrentProcessId();
355 KeyBody->KeyControlBlock = Kcb;
356
357 /* Link it with the KCB */
358 EnlistKeyBodyWithKCB(KeyBody, CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
359
360 /* Assign security */
361 Status = SeAssignSecurity(ParentDescriptor,
362 AccessState->SecurityDescriptor,
363 &NewDescriptor,
364 TRUE,
365 &AccessState->SubjectSecurityContext,
366 &CmpKeyObjectType->TypeInfo.GenericMapping,
367 CmpKeyObjectType->TypeInfo.PoolType);
368 if (NT_SUCCESS(Status))
369 {
370 /*
371 * FIXME: We must acquire a security lock when assigning
372 * a security descriptor to this hive but since the
373 * CmpAssignSecurityDescriptor function does nothing
374 * (we lack the necessary security management implementations
375 * anyway), do not do anything for now.
376 */
377 Status = CmpAssignSecurityDescriptor(Kcb, NewDescriptor);
378 }
379
380 /* Now that the security descriptor is copied in the hive, we can free the original */
381 SeDeassignSecurity(&NewDescriptor);
382
383 if (NT_SUCCESS(Status))
384 {
385 /* Send notification to registered callbacks */
386 CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME);
387 }
388
389 Quickie:
390 /* Check if we got here because of failure */
391 if (!NT_SUCCESS(Status))
392 {
393 /* Free any cells we might've allocated */
394 if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell);
395 HvFreeCell(Hive, *KeyCell);
396 }
397
398 /* Return status */
399 return Status;
400 }
401
402 NTSTATUS
403 NTAPI
CmpDoCreate(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PACCESS_STATE AccessState,IN PUNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN PCM_PARSE_CONTEXT ParseContext,IN PCM_KEY_CONTROL_BLOCK ParentKcb,OUT PVOID * Object)404 CmpDoCreate(IN PHHIVE Hive,
405 IN HCELL_INDEX Cell,
406 IN PACCESS_STATE AccessState,
407 IN PUNICODE_STRING Name,
408 IN KPROCESSOR_MODE AccessMode,
409 IN PCM_PARSE_CONTEXT ParseContext,
410 IN PCM_KEY_CONTROL_BLOCK ParentKcb,
411 OUT PVOID *Object)
412 {
413 NTSTATUS Status;
414 PCELL_DATA CellData;
415 HCELL_INDEX KeyCell;
416 ULONG ParentType;
417 PCM_KEY_BODY KeyBody;
418 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
419 LARGE_INTEGER TimeStamp;
420 PCM_KEY_NODE KeyNode;
421
422 /* Make sure the KCB is locked and lock the flusher */
423 CMP_ASSERT_KCB_LOCK(ParentKcb);
424 CmpLockHiveFlusherShared((PCMHIVE)Hive);
425
426 /* Bail out on read-only KCBs */
427 if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)
428 {
429 Status = STATUS_ACCESS_DENIED;
430 goto Exit;
431 }
432
433 /* Check if the parent is being deleted */
434 if (ParentKcb->Delete)
435 {
436 /* It has, quit */
437 ASSERT(FALSE);
438 Status = STATUS_OBJECT_NAME_NOT_FOUND;
439 goto Exit;
440 }
441
442 /* Get the parent node */
443 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
444 if (!KeyNode)
445 {
446 /* Fail */
447 ASSERT(FALSE);
448 Status = STATUS_INSUFFICIENT_RESOURCES;
449 goto Exit;
450 }
451
452 /* Make sure nobody added us yet */
453 if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL)
454 {
455 /* Fail */
456 ASSERT(FALSE);
457 Status = STATUS_REPARSE;
458 goto Exit;
459 }
460
461 /* Sanity check */
462 ASSERT(Cell == ParentKcb->KeyCell);
463
464 /* Get the parent type */
465 ParentType = HvGetCellType(Cell);
466 if ((ParentType == Volatile) &&
467 !(ParseContext->CreateOptions & REG_OPTION_VOLATILE))
468 {
469 /* Children of volatile parents must also be volatile */
470 //ASSERT(FALSE);
471 Status = STATUS_CHILD_MUST_BE_VOLATILE;
472 goto Exit;
473 }
474
475 /* Don't allow children under symlinks */
476 if (ParentKcb->Flags & KEY_SYM_LINK)
477 {
478 /* Fail */
479 ASSERT(FALSE);
480 Status = STATUS_ACCESS_DENIED;
481 goto Exit;
482 }
483
484 /* Make the cell dirty for now */
485 HvMarkCellDirty(Hive, Cell, FALSE);
486
487 /* Do the actual create operation */
488 Status = CmpDoCreateChild(Hive,
489 Cell,
490 SecurityDescriptor,
491 AccessState,
492 Name,
493 AccessMode,
494 ParseContext,
495 ParentKcb,
496 0,
497 &KeyCell,
498 Object);
499 if (NT_SUCCESS(Status))
500 {
501 /* Get the key body */
502 KeyBody = (PCM_KEY_BODY)(*Object);
503
504 /* Now add the subkey */
505 if (!CmpAddSubKey(Hive, Cell, KeyCell))
506 {
507 /* Free the created child */
508 CmpFreeKeyByCell(Hive, KeyCell, FALSE);
509
510 /* Purge out this KCB */
511 KeyBody->KeyControlBlock->Delete = TRUE;
512 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
513
514 /* And cleanup the key body object */
515 ObDereferenceObjectDeferDelete(*Object);
516 Status = STATUS_INSUFFICIENT_RESOURCES;
517 goto Exit;
518 }
519
520 /* Get the key node */
521 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
522 if (!KeyNode)
523 {
524 /* Fail, this shouldn't happen */
525 CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
526
527 /* Purge out this KCB */
528 KeyBody->KeyControlBlock->Delete = TRUE;
529 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
530
531 /* And cleanup the key body object */
532 ObDereferenceObjectDeferDelete(*Object);
533 Status = STATUS_INSUFFICIENT_RESOURCES;
534 goto Exit;
535 }
536
537 /* Clean up information on this subkey */
538 CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
539
540 /* Sanity checks */
541 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
542 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
543 ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb);
544 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
545
546 /* Update the timestamp */
547 KeQuerySystemTime(&TimeStamp);
548 KeyNode->LastWriteTime = TimeStamp;
549 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
550
551 /* Check if we need to update name maximum */
552 if (KeyNode->MaxNameLen < Name->Length)
553 {
554 /* Do it */
555 KeyNode->MaxNameLen = Name->Length;
556 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length;
557 }
558
559 /* Check if we need to update class length maximum */
560 if (KeyNode->MaxClassLen < ParseContext->Class.Length)
561 {
562 /* Update it */
563 KeyNode->MaxClassLen = ParseContext->Class.Length;
564 }
565
566 /* Check if we're creating a symbolic link */
567 if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK)
568 {
569 /* Get the cell data */
570 CellData = HvGetCell(Hive, KeyCell);
571 if (!CellData)
572 {
573 /* This shouldn't happen */
574 CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above
575
576 /* Purge out this KCB */
577 KeyBody->KeyControlBlock->Delete = TRUE;
578 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock);
579
580 /* And cleanup the key body object */
581 ObDereferenceObjectDeferDelete(*Object);
582 Status = STATUS_INSUFFICIENT_RESOURCES;
583 goto Exit;
584 }
585
586 /* Update the flags */
587 CellData->u.KeyNode.Flags |= KEY_SYM_LINK;
588 KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags;
589 HvReleaseCell(Hive, KeyCell);
590 }
591 }
592
593 Exit:
594 /* Release the flusher lock and return status */
595 CmpUnlockHiveFlusher((PCMHIVE)Hive);
596 return Status;
597 }
598
599 NTSTATUS
600 NTAPI
CmpDoOpen(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PCM_KEY_NODE Node,IN PACCESS_STATE AccessState,IN KPROCESSOR_MODE AccessMode,IN ULONG Attributes,IN PCM_PARSE_CONTEXT Context OPTIONAL,IN ULONG ControlFlags,IN OUT PCM_KEY_CONTROL_BLOCK * CachedKcb,IN PULONG KcbsLocked,IN PUNICODE_STRING KeyName,OUT PVOID * Object)601 CmpDoOpen(IN PHHIVE Hive,
602 IN HCELL_INDEX Cell,
603 IN PCM_KEY_NODE Node,
604 IN PACCESS_STATE AccessState,
605 IN KPROCESSOR_MODE AccessMode,
606 IN ULONG Attributes,
607 IN PCM_PARSE_CONTEXT Context OPTIONAL,
608 IN ULONG ControlFlags,
609 IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb,
610 IN PULONG KcbsLocked,
611 IN PUNICODE_STRING KeyName,
612 OUT PVOID *Object)
613 {
614 NTSTATUS Status;
615 BOOLEAN LockKcb = FALSE;
616 BOOLEAN IsLockShared = FALSE;
617 PCM_KEY_BODY KeyBody = NULL;
618 PCM_KEY_CONTROL_BLOCK Kcb = NULL;
619
620 /* Make sure the hive isn't locked */
621 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) &&
622 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread()))
623 {
624 /* It is, don't touch it */
625 return STATUS_OBJECT_NAME_NOT_FOUND;
626 }
627
628 /* Check if we have a context */
629 if (Context)
630 {
631 /* Check if this is a link create (which shouldn't be an open) */
632 if (Context->CreateLink)
633 {
634 return STATUS_ACCESS_DENIED;
635 }
636
637 /* Check if this is symlink create attempt */
638 if (Context->CreateOptions & REG_OPTION_CREATE_LINK)
639 {
640 /* Key already exists */
641 return STATUS_OBJECT_NAME_COLLISION;
642 }
643
644 /* Set the disposition */
645 Context->Disposition = REG_OPENED_EXISTING_KEY;
646 }
647
648 /* Lock the KCB on creation if asked */
649 if (ControlFlags & CMP_CREATE_KCB_KCB_LOCKED)
650 {
651 LockKcb = TRUE;
652 }
653
654 /* Check if caller doesn't want to create a KCB */
655 if (ControlFlags & CMP_OPEN_KCB_NO_CREATE)
656 {
657 /*
658 * The caller doesn't want to create a KCB. This means the KCB
659 * is already in cache and other threads may take use of it
660 * so it has to be locked in share mode.
661 */
662 IsLockShared = TRUE;
663
664 /* Check if this is a symlink */
665 if (((*CachedKcb)->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
666 {
667 /* Is this symlink found? */
668 if ((*CachedKcb)->ExtFlags & CM_KCB_SYM_LINK_FOUND)
669 {
670 /* Get the real KCB, is this deleted? */
671 Kcb = (*CachedKcb)->ValueCache.RealKcb;
672 if (Kcb->Delete)
673 {
674 /*
675 * The real KCB is gone, do a reparse. We used to lock the KCB in
676 * shared mode as others may have taken use of it but since we
677 * must do a reparse of the key the only thing that matter is us.
678 * Lock the KCB exclusively so nobody is going to mess with the KCB.
679 */
680 DPRINT1("The real KCB is deleted, attempt a reparse\n");
681 CmpUnLockKcbArray(KcbsLocked);
682 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey));
683 CmpCleanUpKcbValueCache(*CachedKcb);
684 KcbsLocked[0] = 1;
685 KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey);
686 return STATUS_REPARSE;
687 }
688
689 /*
690 * The symlink has been found. As in the similar case above,
691 * the KCB of the symlink exclusively, we don't want anybody
692 * to mess it up.
693 */
694 CmpUnLockKcbArray(KcbsLocked);
695 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey));
696 KcbsLocked[0] = 1;
697 KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey);
698 }
699 else
700 {
701 /* We must do a reparse */
702 DPRINT("The symlink is not found, attempt a reparse\n");
703 return STATUS_REPARSE;
704 }
705 }
706 else
707 {
708 /* This is not a symlink, just give the cached KCB already */
709 Kcb = *CachedKcb;
710 }
711
712 /* The caller wants to open a cached KCB */
713 if (!CmpReferenceKeyControlBlock(Kcb))
714 {
715 /* Return failure code */
716 return STATUS_INSUFFICIENT_RESOURCES;
717 }
718 }
719 else
720 {
721 /*
722 * The caller wants to create a new KCB. Unlike the code path above, here
723 * we must check if the lock is exclusively held because in the scenario
724 * where the caller doesn't want to create a KCB is because it is already
725 * in the cache and it must have a shared lock instead.
726 */
727 ASSERT(CmpIsKcbLockedExclusive(*CachedKcb));
728
729 /* Check if this is a symlink */
730 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK))
731 {
732 /* Create the KCB for the symlink */
733 Kcb = CmpCreateKeyControlBlock(Hive,
734 Cell,
735 Node,
736 *CachedKcb,
737 LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0,
738 KeyName);
739 if (!Kcb)
740 {
741 /* Return failure */
742 return STATUS_INSUFFICIENT_RESOURCES;
743 }
744
745 /* Make sure it's also locked, and set the pointer */
746 ASSERT(CmpIsKcbLockedExclusive(Kcb));
747 *CachedKcb = Kcb;
748
749 /* Return reparse required */
750 return STATUS_REPARSE;
751 }
752
753 /* Create the KCB */
754 Kcb = CmpCreateKeyControlBlock(Hive,
755 Cell,
756 Node,
757 *CachedKcb,
758 LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0,
759 KeyName);
760 if (!Kcb)
761 {
762 /* Return failure */
763 return STATUS_INSUFFICIENT_RESOURCES;
764 }
765
766 /* Make sure it's also locked, and set the pointer */
767 ASSERT(CmpIsKcbLockedExclusive(Kcb));
768 *CachedKcb = Kcb;
769 }
770
771 /* Allocate the key object */
772 Status = ObCreateObject(AccessMode,
773 CmpKeyObjectType,
774 NULL,
775 AccessMode,
776 NULL,
777 sizeof(CM_KEY_BODY),
778 0,
779 0,
780 Object);
781 if (NT_SUCCESS(Status))
782 {
783 /* Get the key body and fill it out */
784 KeyBody = (PCM_KEY_BODY)(*Object);
785 KeyBody->KeyControlBlock = Kcb;
786 KeyBody->Type = CM_KEY_BODY_TYPE;
787 KeyBody->ProcessID = PsGetCurrentProcessId();
788 KeyBody->NotifyBlock = NULL;
789
790 /* Link to the KCB */
791 EnlistKeyBodyWithKCB(KeyBody, IsLockShared ? CMP_ENLIST_KCB_LOCKED_SHARED : CMP_ENLIST_KCB_LOCKED_EXCLUSIVE);
792
793 /*
794 * We are already holding a lock against the KCB that is assigned
795 * to this key body. This is to prevent a potential deadlock on
796 * CmpSecurityMethod as ObCheckObjectAccess will invoke the Object
797 * Manager to call that method, of which CmpSecurityMethod would
798 * attempt to acquire a lock again.
799 */
800 KeyBody->KcbLocked = TRUE;
801
802 if (!ObCheckObjectAccess(*Object,
803 AccessState,
804 FALSE,
805 AccessMode,
806 &Status))
807 {
808 /* Access check failed */
809 ObDereferenceObject(*Object);
810 }
811
812 /*
813 * We are done, the lock we are holding will be released
814 * once the registry parsing is done.
815 */
816 KeyBody->KcbLocked = FALSE;
817 }
818 else
819 {
820 /* Failed, dereference the KCB */
821 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE);
822 }
823
824 /* Return status */
825 return Status;
826 }
827
828 NTSTATUS
829 NTAPI
CmpCreateLinkNode(IN PHHIVE Hive,IN HCELL_INDEX Cell,IN PACCESS_STATE AccessState,IN UNICODE_STRING Name,IN KPROCESSOR_MODE AccessMode,IN ULONG CreateOptions,IN PCM_PARSE_CONTEXT Context,IN PCM_KEY_CONTROL_BLOCK ParentKcb,IN PULONG KcbsLocked,OUT PVOID * Object)830 CmpCreateLinkNode(IN PHHIVE Hive,
831 IN HCELL_INDEX Cell,
832 IN PACCESS_STATE AccessState,
833 IN UNICODE_STRING Name,
834 IN KPROCESSOR_MODE AccessMode,
835 IN ULONG CreateOptions,
836 IN PCM_PARSE_CONTEXT Context,
837 IN PCM_KEY_CONTROL_BLOCK ParentKcb,
838 IN PULONG KcbsLocked,
839 OUT PVOID *Object)
840 {
841 NTSTATUS Status;
842 HCELL_INDEX KeyCell, LinkCell, ChildCell;
843 PCM_KEY_BODY KeyBody;
844 LARGE_INTEGER TimeStamp;
845 PCM_KEY_NODE KeyNode;
846 PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb;
847
848 /* Link nodes only allowed on the master */
849 if (Hive != &CmiVolatileHive->Hive)
850 {
851 /* Fail */
852 DPRINT1("Invalid link node attempt\n");
853 return STATUS_ACCESS_DENIED;
854 }
855
856 /* Make sure the KCB is locked and lock the flusher */
857 CMP_ASSERT_KCB_LOCK(ParentKcb);
858 CmpLockHiveFlusherShared((PCMHIVE)Hive);
859 CmpLockHiveFlusherShared((PCMHIVE)Context->ChildHive.KeyHive);
860
861 /* Bail out on read-only KCBs */
862 if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)
863 {
864 Status = STATUS_ACCESS_DENIED;
865 goto Exit;
866 }
867
868 /* Check if the parent is being deleted */
869 if (ParentKcb->Delete)
870 {
871 /* It is, quit */
872 ASSERT(FALSE);
873 Status = STATUS_OBJECT_NAME_NOT_FOUND;
874 goto Exit;
875 }
876
877 /* Allocate a link node */
878 LinkCell = HvAllocateCell(Hive,
879 FIELD_OFFSET(CM_KEY_NODE, Name) +
880 CmpNameSize(Hive, &Name),
881 Stable,
882 HCELL_NIL);
883 if (LinkCell == HCELL_NIL)
884 {
885 /* Fail */
886 Status = STATUS_INSUFFICIENT_RESOURCES;
887 goto Exit;
888 }
889
890 /* Get the key cell */
891 KeyCell = Context->ChildHive.KeyCell;
892 if (KeyCell != HCELL_NIL)
893 {
894 /* Hive exists! */
895 ChildCell = KeyCell;
896
897 /* Get the node data */
898 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
899 if (!KeyNode)
900 {
901 /* Fail */
902 ASSERT(FALSE);
903 Status = STATUS_INSUFFICIENT_RESOURCES;
904 goto Exit;
905 }
906
907 /* Fill out the data */
908 KeyNode->Parent = LinkCell;
909 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
910 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
911
912 /* Now open the key cell */
913 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell);
914 if (!KeyNode)
915 {
916 /* Fail */
917 ASSERT(FALSE);
918 Status = STATUS_INSUFFICIENT_RESOURCES;
919 goto Exit;
920 }
921
922 /* Open the parent */
923 Status = CmpDoOpen(Context->ChildHive.KeyHive,
924 KeyCell,
925 KeyNode,
926 AccessState,
927 AccessMode,
928 CreateOptions,
929 NULL,
930 CMP_CREATE_KCB_KCB_LOCKED,
931 &Kcb,
932 KcbsLocked,
933 &Name,
934 Object);
935 HvReleaseCell(Context->ChildHive.KeyHive, KeyCell);
936 }
937 else
938 {
939 /* Do the actual create operation */
940 Status = CmpDoCreateChild(Context->ChildHive.KeyHive,
941 Cell,
942 NULL,
943 AccessState,
944 &Name,
945 AccessMode,
946 Context,
947 ParentKcb,
948 KEY_HIVE_ENTRY | KEY_NO_DELETE,
949 &ChildCell,
950 Object);
951 if (NT_SUCCESS(Status))
952 {
953 /* Setup root pointer */
954 Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell;
955 }
956 }
957
958 /* Check if open or create suceeded */
959 if (NT_SUCCESS(Status))
960 {
961 /* Mark the cell dirty */
962 HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE);
963
964 /* Get the key node */
965 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell);
966 if (!KeyNode)
967 {
968 /* Fail */
969 ASSERT(FALSE);
970 Status = STATUS_INSUFFICIENT_RESOURCES;
971 goto Exit;
972 }
973
974 /* Release it */
975 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell);
976
977 /* Set the parent and flags */
978 KeyNode->Parent = LinkCell;
979 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE;
980
981 /* Get the link node */
982 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, LinkCell);
983 if (!KeyNode)
984 {
985 /* Fail */
986 ASSERT(FALSE);
987 Status = STATUS_INSUFFICIENT_RESOURCES;
988 goto Exit;
989 }
990
991 /* Set it up */
992 KeyNode->Signature = CM_LINK_NODE_SIGNATURE;
993 KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE;
994 KeyNode->Parent = Cell;
995 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name);
996 if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME;
997 KeQuerySystemTime(&TimeStamp);
998 KeyNode->LastWriteTime = TimeStamp;
999
1000 /* Clear out the rest */
1001 KeyNode->SubKeyCounts[Stable] = 0;
1002 KeyNode->SubKeyCounts[Volatile] = 0;
1003 KeyNode->SubKeyLists[Stable] = HCELL_NIL;
1004 KeyNode->SubKeyLists[Volatile] = HCELL_NIL;
1005 KeyNode->ValueList.Count = 0;
1006 KeyNode->ValueList.List = HCELL_NIL;
1007 KeyNode->ClassLength = 0;
1008
1009 /* Reference the root node */
1010 KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive;
1011 KeyNode->ChildHiveReference.KeyCell = ChildCell;
1012 HvReleaseCell(Hive, LinkCell);
1013
1014 /* Get the parent node */
1015 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1016 if (!KeyNode)
1017 {
1018 /* Fail */
1019 ASSERT(FALSE);
1020 Status = STATUS_INSUFFICIENT_RESOURCES;
1021 goto Exit;
1022 }
1023
1024 /* Now add the subkey */
1025 if (!CmpAddSubKey(Hive, Cell, LinkCell))
1026 {
1027 /* Failure! We don't handle this yet! */
1028 ASSERT(FALSE);
1029 }
1030
1031 /* Get the key body */
1032 KeyBody = (PCM_KEY_BODY)*Object;
1033
1034 /* Clean up information on this subkey */
1035 CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb);
1036
1037 /* Sanity checks */
1038 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell);
1039 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive);
1040 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen);
1041
1042 /* Update the timestamp */
1043 KeQuerySystemTime(&TimeStamp);
1044 KeyNode->LastWriteTime = TimeStamp;
1045 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp;
1046
1047 /* Check if we need to update name maximum */
1048 if (KeyNode->MaxNameLen < Name.Length)
1049 {
1050 /* Do it */
1051 KeyNode->MaxNameLen = Name.Length;
1052 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length;
1053 }
1054
1055 /* Check if we need to update class length maximum */
1056 if (KeyNode->MaxClassLen < Context->Class.Length)
1057 {
1058 /* Update it */
1059 KeyNode->MaxClassLen = Context->Class.Length;
1060 }
1061
1062 /* Release the cell */
1063 HvReleaseCell(Hive, Cell);
1064 }
1065 else
1066 {
1067 /* Release the link cell */
1068 HvReleaseCell(Hive, LinkCell);
1069 }
1070
1071 Exit:
1072 /* Release the flusher locks and return status */
1073 CmpUnlockHiveFlusher((PCMHIVE)Context->ChildHive.KeyHive);
1074 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1075 return Status;
1076 }
1077
1078 VOID
1079 NTAPI
CmpHandleExitNode(IN OUT PHHIVE * Hive,IN OUT HCELL_INDEX * Cell,IN OUT PCM_KEY_NODE * KeyNode,IN OUT PHHIVE * ReleaseHive,IN OUT HCELL_INDEX * ReleaseCell)1080 CmpHandleExitNode(IN OUT PHHIVE *Hive,
1081 IN OUT HCELL_INDEX *Cell,
1082 IN OUT PCM_KEY_NODE *KeyNode,
1083 IN OUT PHHIVE *ReleaseHive,
1084 IN OUT HCELL_INDEX *ReleaseCell)
1085 {
1086 /* Check if we have anything to release */
1087 if (*ReleaseCell != HCELL_NIL)
1088 {
1089 /* Release it */
1090 ASSERT(*ReleaseHive != NULL);
1091 HvReleaseCell(*ReleaseHive, *ReleaseCell);
1092 }
1093
1094 /* Get the link references */
1095 *Hive = (*KeyNode)->ChildHiveReference.KeyHive;
1096 *Cell = (*KeyNode)->ChildHiveReference.KeyCell;
1097
1098 /* Get the new node */
1099 *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell);
1100 if (*KeyNode)
1101 {
1102 /* Set the new release values */
1103 *ReleaseCell = *Cell;
1104 *ReleaseHive = *Hive;
1105 }
1106 else
1107 {
1108 /* Nothing to release */
1109 *ReleaseCell = HCELL_NIL;
1110 *ReleaseHive = NULL;
1111 }
1112 }
1113
1114 /**
1115 * @brief
1116 * Computes the hashes of each subkey in key path name
1117 * and stores them in a hash stack for cache lookup.
1118 *
1119 * @param[in] RemainingName
1120 * A Unicode string structure consisting of the remaining
1121 * registry key path name.
1122 *
1123 * @param[in] ConvKey
1124 * The hash convkey of the current KCB to be supplied.
1125 *
1126 * @param[in,out] HashCacheStack
1127 * An array stack. This function uses this array to store
1128 * all the computed hashes of a key pathname.
1129 *
1130 * @param[out] TotalSubKeys
1131 * The number of total subkeys that have been found, returned
1132 * by this function to the caller. If no subkey levels are found
1133 * the function returns 0.
1134 *
1135 * @return
1136 * Returns the number of remaining subkey levels to caller.
1137 * If no subkey levels are found then this function returns 0.
1138 */
1139 static
1140 ULONG
CmpComputeHashValue(_In_ PUNICODE_STRING RemainingName,_In_ ULONG ConvKey,_Inout_ PCM_HASH_CACHE_STACK HashCacheStack,_Out_ PULONG TotalSubKeys)1141 CmpComputeHashValue(
1142 _In_ PUNICODE_STRING RemainingName,
1143 _In_ ULONG ConvKey,
1144 _Inout_ PCM_HASH_CACHE_STACK HashCacheStack,
1145 _Out_ PULONG TotalSubKeys)
1146 {
1147 ULONG CopyConvKey;
1148 ULONG SubkeysInTotal;
1149 ULONG RemainingSubkeysInTotal;
1150 PWCHAR RemainingNameBuffer;
1151 USHORT RemainingNameLength;
1152 USHORT KeyNameLength;
1153
1154 /* Don't compute the hashes on a NULL remaining name */
1155 RemainingNameBuffer = RemainingName->Buffer;
1156 RemainingNameLength = RemainingName->Length;
1157 if (RemainingNameLength == 0)
1158 {
1159 *TotalSubKeys = 0;
1160 return 0;
1161 }
1162
1163 /* Skip any leading separator */
1164 while (RemainingNameLength >= sizeof(WCHAR) &&
1165 *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR)
1166 {
1167 RemainingNameBuffer++;
1168 RemainingNameLength -= sizeof(WCHAR);
1169 }
1170
1171 /* Now set up the hash stack entries and compute the hashes */
1172 SubkeysInTotal = 0;
1173 RemainingSubkeysInTotal = 0;
1174 KeyNameLength = 0;
1175 CopyConvKey = ConvKey;
1176 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer;
1177 while (RemainingNameLength > 0)
1178 {
1179 /* Is this character a separator? */
1180 if (*RemainingNameBuffer != OBJ_NAME_PATH_SEPARATOR)
1181 {
1182 /* It's not, add it to the hash */
1183 CopyConvKey = COMPUTE_HASH_CHAR(CopyConvKey, *RemainingNameBuffer);
1184
1185 /* Go to the next character (add up the length of the character as well) */
1186 RemainingNameBuffer++;
1187 KeyNameLength += sizeof(WCHAR);
1188 RemainingNameLength -= sizeof(WCHAR);
1189
1190 /*
1191 * We are at the end of the key name path. Take into account
1192 * the last character and if we still have space in the hash
1193 * stack, add it up in the remaining list.
1194 */
1195 if (RemainingNameLength == 0)
1196 {
1197 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1198 {
1199 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength;
1200 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength;
1201 HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey;
1202 RemainingSubkeysInTotal++;
1203 }
1204
1205 SubkeysInTotal++;
1206 }
1207 }
1208 else
1209 {
1210 /* Skip any leading separator */
1211 while (RemainingNameLength >= sizeof(WCHAR) &&
1212 *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR)
1213 {
1214 RemainingNameBuffer++;
1215 RemainingNameLength -= sizeof(WCHAR);
1216 }
1217
1218 /*
1219 * It would be possible that a malformed key pathname may be passed
1220 * to the registry parser such as a path with only separators like
1221 * "\\\\" for example. This would trick the function into believing
1222 * the key path has subkeys albeit that is not the case.
1223 */
1224 ASSERT(RemainingNameLength != 0);
1225
1226 /* Take into account this subkey */
1227 SubkeysInTotal++;
1228
1229 /* And add it up to the hash stack */
1230 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1231 {
1232 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength;
1233 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength;
1234 HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey;
1235
1236 RemainingSubkeysInTotal++;
1237 KeyNameLength = 0;
1238
1239 /*
1240 * Precaution check -- we have added up a remaining
1241 * subkey above but we must ensure we still have space
1242 * to hold up the new subkey for which we will compute
1243 * the hashes, so that we don't blow up the hash stack.
1244 */
1245 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1246 {
1247 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer;
1248 }
1249 }
1250 }
1251 }
1252
1253 *TotalSubKeys = SubkeysInTotal;
1254 return RemainingSubkeysInTotal;
1255 }
1256
1257 /**
1258 * @brief
1259 * Compares each subkey's hash and name with those
1260 * captured in the hash cache stack.
1261 *
1262 * @param[in] HashCacheStack
1263 * A pointer to a hash cache stack array filled with
1264 * subkey hashes and names for comparison.
1265 *
1266 * @param[in] CurrentKcb
1267 * A pointer to the currently given KCB.
1268 *
1269 * @param[in] RemainingSubkeys
1270 * The remaining subkey levels to be supplied.
1271 *
1272 * @param[out] ParentKcb
1273 * A pointer to the parent KCB returned to the caller.
1274 * This parameter points to the parent of the current
1275 * KCB if all the subkeys match, otherwise it points
1276 * to the actual current KCB.
1277 *
1278 * @return
1279 * Returns TRUE if all the subkey levels match, otherwise
1280 * FALSE is returned.
1281 */
1282 static
1283 BOOLEAN
CmpCompareSubkeys(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ PCM_KEY_CONTROL_BLOCK CurrentKcb,_In_ ULONG RemainingSubkeys,_Out_ PCM_KEY_CONTROL_BLOCK * ParentKcb)1284 CmpCompareSubkeys(
1285 _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1286 _In_ PCM_KEY_CONTROL_BLOCK CurrentKcb,
1287 _In_ ULONG RemainingSubkeys,
1288 _Out_ PCM_KEY_CONTROL_BLOCK *ParentKcb)
1289 {
1290 LONG HashStackIndex;
1291 LONG Result;
1292 PCM_NAME_CONTROL_BLOCK NameBlock;
1293 UNICODE_STRING CurrentNameBlock;
1294
1295 ASSERT(CurrentKcb != NULL);
1296
1297 /* Loop each hash and check that they match */
1298 HashStackIndex = RemainingSubkeys;
1299 while (HashStackIndex >= 0)
1300 {
1301 /* Does the subkey hash match? */
1302 if (CurrentKcb->ConvKey != HashCacheStack[HashStackIndex].ConvKey)
1303 {
1304 *ParentKcb = CurrentKcb;
1305 return FALSE;
1306 }
1307
1308 /* Compare the subkey string, is the name compressed? */
1309 NameBlock = CurrentKcb->NameBlock;
1310 if (NameBlock->Compressed)
1311 {
1312 Result = CmpCompareCompressedName(&HashCacheStack[HashStackIndex].NameOfKey,
1313 NameBlock->Name,
1314 NameBlock->NameLength);
1315 }
1316 else
1317 {
1318 CurrentNameBlock.Buffer = NameBlock->Name;
1319 CurrentNameBlock.Length = NameBlock->NameLength;
1320 CurrentNameBlock.MaximumLength = NameBlock->NameLength;
1321
1322 Result = RtlCompareUnicodeString(&HashCacheStack[HashStackIndex].NameOfKey,
1323 &CurrentNameBlock,
1324 TRUE);
1325 }
1326
1327 /* Do the subkey names match? */
1328 if (Result)
1329 {
1330 *ParentKcb = CurrentKcb;
1331 return FALSE;
1332 }
1333
1334 /* Go to the next subkey hash */
1335 HashStackIndex--;
1336 }
1337
1338 /* All the subkeys match */
1339 *ParentKcb = CurrentKcb->ParentKcb;
1340 return TRUE;
1341 }
1342
1343 /**
1344 * @brief
1345 * Removes the subkeys on a remaining key pathname.
1346 *
1347 * @param[in] HashCacheStack
1348 * A pointer to a hash cache stack array filled with
1349 * subkey hashes and names.
1350 *
1351 * @param[in] RemainingSubkeys
1352 * The remaining subkey levels to be supplied.
1353 *
1354 * @param[in,out] RemainingName
1355 * A Unicode string structure consisting of the remaining
1356 * registry key path name, where the subkeys of such path
1357 * are to be removed.
1358 */
1359 static
1360 VOID
CmpRemoveSubkeysInRemainingName(_In_ PCM_HASH_CACHE_STACK HashCacheStack,_In_ ULONG RemainingSubkeys,_Inout_ PUNICODE_STRING RemainingName)1361 CmpRemoveSubkeysInRemainingName(
1362 _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1363 _In_ ULONG RemainingSubkeys,
1364 _Inout_ PUNICODE_STRING RemainingName)
1365 {
1366 ULONG HashStackIndex = 0;
1367
1368 /* Skip any leading separator on matching name */
1369 while (RemainingName->Length >= sizeof(WCHAR) &&
1370 RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1371 {
1372 RemainingName->Buffer++;
1373 RemainingName->Length -= sizeof(WCHAR);
1374 }
1375
1376 /* Skip the subkeys as well */
1377 while (HashStackIndex <= RemainingSubkeys)
1378 {
1379 RemainingName->Buffer += HashCacheStack[HashStackIndex].NameOfKey.Length / sizeof(WCHAR);
1380 RemainingName->Length -= HashCacheStack[HashStackIndex].NameOfKey.Length;
1381
1382 /* Skip any leading separator */
1383 while (RemainingName->Length >= sizeof(WCHAR) &&
1384 RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1385 {
1386 RemainingName->Buffer++;
1387 RemainingName->Length -= sizeof(WCHAR);
1388 }
1389
1390 /* Go to the next hash */
1391 HashStackIndex++;
1392 }
1393 }
1394
1395 /**
1396 * @brief
1397 * Looks up in the pool cache for key pathname that matches
1398 * with one in the said cache and returns a KCB pointing
1399 * to that name. This function performs locking of KCBs
1400 * during cache lookup.
1401 *
1402 * @param[in] HashCacheStack
1403 * A pointer to a hash cache stack array filled with
1404 * subkey hashes and names.
1405 *
1406 * @param[in] LockKcbsExclusive
1407 * If set to TRUE, the KCBs are locked exclusively by the
1408 * calling thread, otherwise they are locked in shared mode.
1409 * See Remarks for further information.
1410 *
1411 * @param[in] TotalRemainingSubkeys
1412 * The total remaining subkey levels to be supplied.
1413 *
1414 * @param[in,out] RemainingName
1415 * A Unicode string structure consisting of the remaining
1416 * registry key path name. The remaining name is updated
1417 * by the function if a key pathname is found in cache.
1418 *
1419 * @param[in,out] OuterStackArray
1420 * A pointer to an array that lives on the caller's stack.
1421 * The expected size of the array is up to 32 elements,
1422 * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1423 * This limit also corresponds to the maximum depth of
1424 * subkey levels.
1425 *
1426 * @param[in,out] Kcb
1427 * A pointer to a KCB, this KCB is changed if the key pathname
1428 * is found in cache.
1429 *
1430 * @param[out] Hive
1431 * A pointer to a hive, this hive is changed if the key pathname
1432 * is found in cache.
1433 *
1434 * @param[out] Cell
1435 * A pointer to a cell, this cell is changed if the key pathname
1436 * is found in cache.
1437 *
1438 * @param[out] MatchRemainSubkeyLevel
1439 * A pointer to match subkey levels returned by the function.
1440 * If no match levels are found, this is 0.
1441 *
1442 * @return
1443 * Returns STATUS_SUCCESS if cache lookup has completed successfully.
1444 * STATUS_OBJECT_NAME_NOT_FOUND is returned if the current KCB of
1445 * the key pathname has been deleted. STATUS_RETRY is returned if
1446 * at least the current KCB or its parent have been deleted
1447 * and a cache lookup must be retried again. STATUS_UNSUCCESSFUL is
1448 * returned if a KCB is referenced too many times.
1449 *
1450 * @remarks
1451 * The function attempts to do a cache lookup with a shared lock
1452 * on KCBs so that other threads can simultaneously get access
1453 * to these KCBs. When the captured KCB is being deleted on us
1454 * we have to retry a lookup with exclusive look so that no other
1455 * threads will mess with the KCBs and perform appropriate actions
1456 * if a KCB is deleted.
1457 */
1458 static
1459 NTSTATUS
CmpLookInCache(_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)1460 CmpLookInCache(
1461 _In_ PCM_HASH_CACHE_STACK HashCacheStack,
1462 _In_ BOOLEAN LockKcbsExclusive,
1463 _In_ ULONG TotalRemainingSubkeys,
1464 _Inout_ PUNICODE_STRING RemainingName,
1465 _Inout_ PULONG OuterStackArray,
1466 _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb,
1467 _Out_ PHHIVE *Hive,
1468 _Out_ PHCELL_INDEX Cell,
1469 _Out_ PULONG MatchRemainSubkeyLevel)
1470 {
1471 LONG RemainingSubkeys;
1472 ULONG TotalLevels;
1473 BOOLEAN SubkeysMatch;
1474 PCM_KEY_CONTROL_BLOCK CurrentKcb, ParentKcb;
1475 PCM_KEY_HASH HashEntry = NULL;
1476 BOOLEAN KeyFoundInCache = FALSE;
1477 PULONG LockedKcbs = NULL;
1478
1479 /* Reference the KCB */
1480 if (!CmpReferenceKeyControlBlock(*Kcb))
1481 {
1482 /* This key is opened too many times, bail out */
1483 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1484 return STATUS_UNSUCCESSFUL;
1485 }
1486
1487 /* Prepare to lock the KCBs */
1488 LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack,
1489 LockKcbsExclusive ? CMP_LOCK_KCB_ARRAY_EXCLUSIVE : CMP_LOCK_KCB_ARRAY_SHARED,
1490 *Kcb,
1491 OuterStackArray,
1492 TotalRemainingSubkeys,
1493 0);
1494 NT_ASSERT(LockedKcbs);
1495
1496 /* Lookup in the cache */
1497 RemainingSubkeys = TotalRemainingSubkeys - 1;
1498 TotalLevels = TotalRemainingSubkeys + (*Kcb)->TotalLevels + 1;
1499 while (RemainingSubkeys >= 0)
1500 {
1501 /* Get the hash entry from the cache */
1502 HashEntry = GET_HASH_ENTRY(CmpCacheTable, HashCacheStack[RemainingSubkeys].ConvKey)->Entry;
1503
1504 /* Take one level down as we are processing this hash entry */
1505 TotalLevels--;
1506
1507 while (HashEntry != NULL)
1508 {
1509 /* Validate this hash and obtain the current KCB */
1510 ASSERT_VALID_HASH(HashEntry);
1511 CurrentKcb = CONTAINING_RECORD(HashEntry, CM_KEY_CONTROL_BLOCK, KeyHash);
1512
1513 /* Does this KCB have matching levels? */
1514 if (TotalLevels == CurrentKcb->TotalLevels)
1515 {
1516 /*
1517 * We have matching subkey levels but don't directly assume we have
1518 * a matching key path in cache. Start comparing each subkey.
1519 */
1520 SubkeysMatch = CmpCompareSubkeys(HashCacheStack,
1521 CurrentKcb,
1522 RemainingSubkeys,
1523 &ParentKcb);
1524 if (SubkeysMatch)
1525 {
1526 /* All subkeys match, now check if the base KCB matches with parent */
1527 if (*Kcb == ParentKcb)
1528 {
1529 /* Is the KCB marked as deleted? */
1530 if (CurrentKcb->Delete ||
1531 CurrentKcb->ParentKcb->Delete)
1532 {
1533 /*
1534 * Either the current or its parent KCB is marked
1535 * but we had a shared lock so probably a naughty
1536 * thread was deleting it. Retry doing a cache
1537 * lookup again with exclusive lock.
1538 */
1539 if (!LockKcbsExclusive)
1540 {
1541 CmpUnLockKcbArray(LockedKcbs);
1542 CmpDereferenceKeyControlBlock(*Kcb);
1543 DPRINT1("The current KCB or its parent is deleted, retrying looking in the cache\n");
1544 return STATUS_RETRY;
1545 }
1546
1547 /* We're under an exclusive lock, is the KCB deleted yet? */
1548 if (CurrentKcb->Delete)
1549 {
1550 /* The KCB is gone, the key should no longer belong in the cache */
1551 CmpRemoveKeyControlBlock(CurrentKcb);
1552 CmpUnLockKcbArray(LockedKcbs);
1553 CmpDereferenceKeyControlBlock(*Kcb);
1554 DPRINT1("The current KCB is deleted (KCB 0x%p)\n", CurrentKcb);
1555 return STATUS_OBJECT_NAME_NOT_FOUND;
1556 }
1557
1558 /*
1559 * The parent is deleted so it must be that somebody created
1560 * a fake key. Assert ourselves that is the case.
1561 */
1562 ASSERT(CurrentKcb->ExtFlags & CM_KCB_KEY_NON_EXIST);
1563
1564 /* Remove this KCB out of cache if someone still uses it */
1565 if (CurrentKcb->RefCount != 0)
1566 {
1567 CurrentKcb->Delete = TRUE;
1568 CmpRemoveKeyControlBlock(CurrentKcb);
1569 }
1570 else
1571 {
1572 /* Otherwise expunge it */
1573 CmpRemoveFromDelayedClose(CurrentKcb);
1574 CmpCleanUpKcbCacheWithLock(CurrentKcb, FALSE);
1575 }
1576
1577 /* Stop looking for next hashes as the KCB is kaput */
1578 break;
1579 }
1580
1581 /* We finally found the key in cache, acknowledge it */
1582 KeyFoundInCache = TRUE;
1583
1584 /* Remove the subkeys in the remaining name and stop looking in the cache */
1585 CmpRemoveSubkeysInRemainingName(HashCacheStack, RemainingSubkeys, RemainingName);
1586 break;
1587 }
1588 }
1589 }
1590
1591 /* Go to the next hash */
1592 HashEntry = HashEntry->NextHash;
1593 }
1594
1595 /* Stop looking in cache if we found the matching key */
1596 if (KeyFoundInCache)
1597 {
1598 DPRINT("Key found in cache, stop looking\n");
1599 break;
1600 }
1601
1602 /* Keep looking in the cache until we run out of remaining subkeys */
1603 RemainingSubkeys--;
1604 }
1605
1606 /* Return the matching subkey levels */
1607 *MatchRemainSubkeyLevel = RemainingSubkeys + 1;
1608
1609 /* We have to update the KCB if the key was found in cache */
1610 if (KeyFoundInCache)
1611 {
1612 /*
1613 * Before we change the KCB we must dereference the prior
1614 * KCB that we no longer need it.
1615 */
1616 CmpDereferenceKeyControlBlock(*Kcb);
1617 *Kcb = CurrentKcb;
1618
1619 /* Reference the new KCB now */
1620 if (!CmpReferenceKeyControlBlock(*Kcb))
1621 {
1622 /* This key is opened too many times, bail out */
1623 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1624 return STATUS_UNSUCCESSFUL;
1625 }
1626
1627 /* Update hive and cell data from current KCB */
1628 *Hive = CurrentKcb->KeyHive;
1629 *Cell = CurrentKcb->KeyCell;
1630 }
1631
1632 /* Unlock the KCBs */
1633 CmpUnLockKcbArray(LockedKcbs);
1634 return STATUS_SUCCESS;
1635 }
1636
1637 /**
1638 * @brief
1639 * Builds a hash stack cache and looks up in the
1640 * pool cache for a matching key pathname.
1641 *
1642 * @param[in] ParseObject
1643 * A pointer to a parse object, acting as a key
1644 * body. This parameter is unused.
1645 *
1646 * @param[in,out] Kcb
1647 * A pointer to a KCB. This KCB is used by the
1648 * registry parser after hash stack and cache
1649 * lookup are done. This KCB might change if the
1650 * key is found to be cached in the cache pool.
1651 *
1652 * @param[in] Current
1653 * The current remaining key pathname.
1654 *
1655 * @param[out] Hive
1656 * A pointer to a registry hive, returned by the caller.
1657 *
1658 * @param[out] Cell
1659 * A pointer to a hive cell, returned by the caller.
1660 *
1661 * @param[out] TotalRemainingSubkeys
1662 * A pointer to a number of total remaining subkey levels,
1663 * returned by the caller. This can be 0 if no subkey levels
1664 * have been found.
1665 *
1666 * @param[out] MatchRemainSubkeyLevel
1667 * A pointer to a number of remaining subkey levels that match,
1668 * returned by the caller. This can be 0 if no matching levels
1669 * are found.
1670 *
1671 * @param[out] TotalSubkeys
1672 * A pointer to a number of total subkeys. This can be 0 if no
1673 * subkey levels are found. By definition, both MatchRemainSubkeyLevel
1674 * and TotalRemainingSubkeys are 0 as well.
1675 *
1676 * @param[in,out] OuterStackArray
1677 * A pointer to an array that lives on the caller's stack.
1678 * The expected size of the array is up to 32 elements,
1679 * which is the imposed limit by CMP_HASH_STACK_LIMIT.
1680 * This limit also corresponds to the maximum depth of
1681 * subkey levels.
1682 *
1683 * @param[out] LockedKcbs
1684 * A pointer to an array of locked KCBs, returned by the caller.
1685 *
1686 * @return
1687 * Returns STATUS_SUCCESS if all the operations have succeeded without
1688 * problems. STATUS_NAME_TOO_LONG is returned if the key pathname has
1689 * too many subkey levels (more than 32 levels deep). A failure NTSTATUS
1690 * code is returned otherwise. Refer to CmpLookInCache documentation
1691 * for more information about other returned status codes.
1692 * STATUS_UNSUCCESSFUL is returned if a KCB is referenced too many times.
1693 */
1694 NTSTATUS
1695 NTAPI
CmpBuildHashStackAndLookupCache(_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)1696 CmpBuildHashStackAndLookupCache(
1697 _In_ PCM_KEY_BODY ParseObject,
1698 _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb,
1699 _In_ PUNICODE_STRING Current,
1700 _Out_ PHHIVE *Hive,
1701 _Out_ PHCELL_INDEX Cell,
1702 _Out_ PULONG TotalRemainingSubkeys,
1703 _Out_ PULONG MatchRemainSubkeyLevel,
1704 _Out_ PULONG TotalSubkeys,
1705 _Inout_ PULONG OuterStackArray,
1706 _Out_ PULONG *LockedKcbs)
1707 {
1708 NTSTATUS Status;
1709 ULONG ConvKey;
1710 ULONG SubkeysInTotal, RemainingSubkeysInTotal, MatchRemainingSubkeys;
1711 CM_HASH_CACHE_STACK HashCacheStack[CMP_SUBKEY_LEVELS_DEPTH_LIMIT];
1712
1713 /* Make sure it's not a dead KCB */
1714 ASSERT((*Kcb)->RefCount > 0);
1715
1716 /* Lock the registry */
1717 CmpLockRegistry();
1718
1719 /* Calculate hash values for every subkey this key path has */
1720 ConvKey = (*Kcb)->ConvKey;
1721 RemainingSubkeysInTotal = CmpComputeHashValue(Current,
1722 ConvKey,
1723 HashCacheStack,
1724 &SubkeysInTotal);
1725
1726 /* This key path has too many subkeys */
1727 if (SubkeysInTotal > CMP_SUBKEY_LEVELS_DEPTH_LIMIT)
1728 {
1729 DPRINT1("The key path has too many subkeys - %lu\n", SubkeysInTotal);
1730 *LockedKcbs = NULL;
1731 return STATUS_NAME_TOO_LONG;
1732 }
1733
1734 /* Return hive and cell data */
1735 *Hive = (*Kcb)->KeyHive;
1736 *Cell = (*Kcb)->KeyCell;
1737
1738 /* Do we have any subkeys? */
1739 if (!RemainingSubkeysInTotal && !SubkeysInTotal)
1740 {
1741 /*
1742 * We don't have any subkeys nor remaining levels, the
1743 * KCB points to the actual key. Lock it.
1744 */
1745 if (!CmpReferenceKeyControlBlock(*Kcb))
1746 {
1747 /* This key is opened too many times, bail out */
1748 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb);
1749 return STATUS_UNSUCCESSFUL;
1750 }
1751
1752 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX(ConvKey));
1753
1754 /* Add this KCB in the array of locked KCBs */
1755 OuterStackArray[0] = 1;
1756 OuterStackArray[1] = GET_HASH_INDEX(ConvKey);
1757 *LockedKcbs = OuterStackArray;
1758
1759 /* And return all the subkey level counters */
1760 *TotalRemainingSubkeys = RemainingSubkeysInTotal;
1761 *MatchRemainSubkeyLevel = 0;
1762 *TotalSubkeys = SubkeysInTotal;
1763 return STATUS_SUCCESS;
1764 }
1765
1766 /* Lookup in the cache */
1767 Status = CmpLookInCache(HashCacheStack,
1768 FALSE,
1769 RemainingSubkeysInTotal,
1770 Current,
1771 OuterStackArray,
1772 Kcb,
1773 Hive,
1774 Cell,
1775 &MatchRemainingSubkeys);
1776 if (!NT_SUCCESS(Status))
1777 {
1778 /* Bail out if cache lookup failed for other reasons */
1779 if (Status != STATUS_RETRY)
1780 {
1781 DPRINT1("CmpLookInCache() failed (Status 0x%lx)\n", Status);
1782 *LockedKcbs = NULL;
1783 return Status;
1784 }
1785
1786 /* Retry looking in the cache but with KCBs locked exclusively */
1787 Status = CmpLookInCache(HashCacheStack,
1788 TRUE,
1789 RemainingSubkeysInTotal,
1790 Current,
1791 OuterStackArray,
1792 Kcb,
1793 Hive,
1794 Cell,
1795 &MatchRemainingSubkeys);
1796 if (!NT_SUCCESS(Status))
1797 {
1798 DPRINT1("CmpLookInCache() failed after retry (Status 0x%lx)\n", Status);
1799 *LockedKcbs = NULL;
1800 return Status;
1801 }
1802 }
1803
1804 /*
1805 * Check if we have a full match of remaining levels.
1806 *
1807 * FIXME: It is possible we can catch a fake key from the cache
1808 * when we did the lookup, in such case we should not do any
1809 * locking as such KCB does not point to any real information.
1810 * Currently ReactOS doesn't create fake KCBs so we are good
1811 * for now.
1812 */
1813 if (RemainingSubkeysInTotal == MatchRemainingSubkeys)
1814 {
1815 /*
1816 * Just simply lock this KCB as it points to the full
1817 * subkey levels in cache.
1818 */
1819 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX((*Kcb)->ConvKey));
1820 OuterStackArray[0] = 1;
1821 OuterStackArray[1] = GET_HASH_INDEX((*Kcb)->ConvKey);
1822 *LockedKcbs = OuterStackArray;
1823 }
1824 else
1825 {
1826 /*
1827 * We only have a partial match so other subkey levels
1828 * have each KCB. Simply just lock them.
1829 */
1830 *LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack,
1831 CMP_LOCK_KCB_ARRAY_EXCLUSIVE,
1832 *Kcb,
1833 OuterStackArray,
1834 RemainingSubkeysInTotal,
1835 MatchRemainingSubkeys);
1836 NT_ASSERT(*LockedKcbs);
1837 }
1838
1839 /* Return all the subkey level counters */
1840 *TotalRemainingSubkeys = RemainingSubkeysInTotal;
1841 *MatchRemainSubkeyLevel = MatchRemainingSubkeys;
1842 *TotalSubkeys = SubkeysInTotal;
1843 return Status;
1844 }
1845
1846 NTSTATUS
1847 NTAPI
CmpParseKey(IN PVOID ParseObject,IN PVOID ObjectType,IN OUT PACCESS_STATE AccessState,IN KPROCESSOR_MODE AccessMode,IN ULONG Attributes,IN OUT PUNICODE_STRING CompleteName,IN OUT PUNICODE_STRING RemainingName,IN OUT PVOID Context OPTIONAL,IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,OUT PVOID * Object)1848 CmpParseKey(IN PVOID ParseObject,
1849 IN PVOID ObjectType,
1850 IN OUT PACCESS_STATE AccessState,
1851 IN KPROCESSOR_MODE AccessMode,
1852 IN ULONG Attributes,
1853 IN OUT PUNICODE_STRING CompleteName,
1854 IN OUT PUNICODE_STRING RemainingName,
1855 IN OUT PVOID Context OPTIONAL,
1856 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
1857 OUT PVOID *Object)
1858 {
1859 NTSTATUS Status;
1860 PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb;
1861 PHHIVE Hive = NULL;
1862 PCM_KEY_NODE Node = NULL;
1863 HCELL_INDEX Cell = HCELL_NIL, NextCell;
1864 PHHIVE HiveToRelease = NULL;
1865 HCELL_INDEX CellToRelease = HCELL_NIL;
1866 UNICODE_STRING Current, NextName;
1867 PCM_PARSE_CONTEXT ParseContext = Context;
1868 ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0;
1869 ULONG LockedKcbArray[CMP_KCBS_IN_ARRAY_LIMIT];
1870 PULONG LockedKcbs;
1871 BOOLEAN IsKeyCached = FALSE;
1872 BOOLEAN Result, Last;
1873 PAGED_CODE();
1874
1875 /* Loop path separators at the end */
1876 while (RemainingName->Length &&
1877 (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] ==
1878 OBJ_NAME_PATH_SEPARATOR))
1879 {
1880 /* Remove path separator */
1881 RemainingName->Length -= sizeof(WCHAR);
1882 }
1883
1884 /* Fail if this isn't a key object */
1885 if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH;
1886
1887 /* Copy the remaining name */
1888 Current = *RemainingName;
1889
1890 /* Check if this is a create */
1891 if (!ParseContext || !ParseContext->CreateOperation)
1892 {
1893 /* It isn't, so no context */
1894 ParseContext = NULL;
1895 }
1896
1897 /* Grab the KCB */
1898 Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock;
1899
1900 /* Sanity check */
1901 ASSERT(Kcb != NULL);
1902
1903 /* Fail if the key was marked as deleted */
1904 if (Kcb->Delete)
1905 return STATUS_KEY_DELETED;
1906
1907 /* Lookup in the cache */
1908 Status = CmpBuildHashStackAndLookupCache(ParseObject,
1909 &Kcb,
1910 &Current,
1911 &Hive,
1912 &Cell,
1913 &TotalRemainingSubkeys,
1914 &MatchRemainSubkeyLevel,
1915 &TotalSubkeys,
1916 LockedKcbArray,
1917 &LockedKcbs);
1918 CMP_ASSERT_REGISTRY_LOCK();
1919 if (!NT_SUCCESS(Status))
1920 {
1921 DPRINT1("Failed to look in cache, stop parsing (Status 0x%lx)\n", Status);
1922 ParentKcb = NULL;
1923 goto Quickie;
1924 }
1925
1926 /* This is now the parent */
1927 ParentKcb = Kcb;
1928
1929 /* Sanity check */
1930 ASSERT(ParentKcb != NULL);
1931
1932 /* Don't do anything if we're being deleted */
1933 if (Kcb->Delete)
1934 {
1935 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1936 goto Quickie;
1937 }
1938
1939 /* Check if everything was found cached */
1940 if (!TotalRemainingSubkeys)
1941 {
1942 /*
1943 * We don't have any remaining subkey levels so we're good
1944 * that we have an already perfect candidate for a KCB, just
1945 * do the open directly.
1946 */
1947 DPRINT("No remaining subkeys, the KCB points to the actual key\n");
1948 IsKeyCached = TRUE;
1949 goto KeyCachedOpenNow;
1950 }
1951
1952 /* Check if we have a matching level */
1953 if (MatchRemainSubkeyLevel)
1954 {
1955 /*
1956 * We have a matching level, check if that matches
1957 * with the total levels of subkeys. Do the open directly
1958 * if that is the case, because the whole subkeys levels
1959 * is cached.
1960 */
1961 if (MatchRemainSubkeyLevel == TotalSubkeys)
1962 {
1963 DPRINT("We have a full matching level, open the key now\n");
1964 IsKeyCached = TRUE;
1965 goto KeyCachedOpenNow;
1966 }
1967
1968 /*
1969 * We only have a partial match, make sure we did not
1970 * get mismatched hive data.
1971 */
1972 ASSERT(Hive == Kcb->KeyHive);
1973 ASSERT(Cell == Kcb->KeyCell);
1974 }
1975
1976 /*
1977 * FIXME: Currently the registry parser doesn't check for fake
1978 * KCBs. CmpCreateKeyControlBlock does have the necessary implementation
1979 * to create such fake keys but we don't create these fake keys anywhere.
1980 * When we will do, we must improve the registry parser routine to handle
1981 * fake keys a bit differently here.
1982 */
1983
1984 /* Check if this is a symlink */
1985 if (Kcb->Flags & KEY_SYM_LINK)
1986 {
1987 /* Get the next name */
1988 Result = CmpGetNextName(&Current, &NextName, &Last);
1989 Current.Buffer = NextName.Buffer;
1990
1991 /* Validate the current name string length */
1992 if (Current.Length + NextName.Length > MAXUSHORT)
1993 {
1994 /* too long */
1995 Status = STATUS_NAME_TOO_LONG;
1996 goto Quickie;
1997 }
1998 Current.Length += NextName.Length;
1999
2000 /* Validate the current name string maximum length */
2001 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
2002 {
2003 /* too long */
2004 Status = STATUS_NAME_TOO_LONG;
2005 goto Quickie;
2006 }
2007 Current.MaximumLength += NextName.MaximumLength;
2008
2009 /* CmpGetSymbolicLink doesn't want a lock */
2010 CmpUnLockKcbArray(LockedKcbs);
2011 LockedKcbs = NULL;
2012
2013 /* Parse the symlink */
2014 if (CmpGetSymbolicLink(Hive,
2015 CompleteName,
2016 Kcb,
2017 &Current))
2018 {
2019 /* Symlink parse succeeded */
2020 Status = STATUS_REPARSE;
2021 }
2022 else
2023 {
2024 /* Couldn't find symlink */
2025 Status = STATUS_OBJECT_NAME_NOT_FOUND;
2026 }
2027
2028 /* We're done */
2029 goto Quickie;
2030 }
2031
2032 /* Get the key node */
2033 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
2034 if (!Node)
2035 {
2036 Status = STATUS_INSUFFICIENT_RESOURCES;
2037 goto Quickie;
2038 }
2039
2040 /* Start parsing */
2041 Status = STATUS_NOT_IMPLEMENTED;
2042 while (TRUE)
2043 {
2044 /* Get the next component */
2045 Result = CmpGetNextName(&Current, &NextName, &Last);
2046 if (Result && NextName.Length)
2047 {
2048 /* See if this is a sym link */
2049 if (!(Kcb->Flags & KEY_SYM_LINK))
2050 {
2051 /* Find the subkey */
2052 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName);
2053 if (NextCell != HCELL_NIL)
2054 {
2055 /* Get the new node */
2056 Cell = NextCell;
2057 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
2058 ASSERT(Node);
2059
2060 /* Check if this was the last key */
2061 if (Last)
2062 {
2063 /* Is this an exit node */
2064 if (Node->Flags & KEY_HIVE_EXIT)
2065 {
2066 /* Handle it */
2067 CmpHandleExitNode(&Hive,
2068 &Cell,
2069 &Node,
2070 &HiveToRelease,
2071 &CellToRelease);
2072 if (!Node)
2073 {
2074 /* Fail */
2075 Status = STATUS_INSUFFICIENT_RESOURCES;
2076 break;
2077 }
2078 }
2079
2080 KeyCachedOpenNow:
2081 /* Do the open */
2082 Status = CmpDoOpen(Hive,
2083 Cell,
2084 Node,
2085 AccessState,
2086 AccessMode,
2087 Attributes,
2088 ParseContext,
2089 IsKeyCached ? CMP_OPEN_KCB_NO_CREATE : CMP_CREATE_KCB_KCB_LOCKED,
2090 &Kcb,
2091 LockedKcbs,
2092 &NextName,
2093 Object);
2094 if (Status == STATUS_REPARSE)
2095 {
2096 /* CmpGetSymbolicLink doesn't want a lock */
2097 CmpUnLockKcbArray(LockedKcbs);
2098 LockedKcbs = NULL;
2099
2100 /* Parse the symlink */
2101 if (!CmpGetSymbolicLink(Hive,
2102 CompleteName,
2103 Kcb,
2104 NULL))
2105 {
2106 /* Symlink parse failed */
2107 Status = STATUS_OBJECT_NAME_NOT_FOUND;
2108 }
2109 }
2110
2111 /* We are done */
2112 break;
2113 }
2114
2115 /* Is this an exit node */
2116 if (Node->Flags & KEY_HIVE_EXIT)
2117 {
2118 /* Handle it */
2119 CmpHandleExitNode(&Hive,
2120 &Cell,
2121 &Node,
2122 &HiveToRelease,
2123 &CellToRelease);
2124 if (!Node)
2125 {
2126 /* Fail */
2127 Status = STATUS_INSUFFICIENT_RESOURCES;
2128 break;
2129 }
2130 }
2131
2132 /* Create a KCB for this key */
2133 Kcb = CmpCreateKeyControlBlock(Hive,
2134 Cell,
2135 Node,
2136 ParentKcb,
2137 CMP_LOCK_HASHES_FOR_KCB,
2138 &NextName);
2139 if (!Kcb)
2140 {
2141 /* Fail */
2142 Status = STATUS_INSUFFICIENT_RESOURCES;
2143 break;
2144 }
2145
2146 /* Dereference the parent and set the new one */
2147 CmpDereferenceKeyControlBlockWithLock(ParentKcb, FALSE);
2148 ParentKcb = Kcb;
2149 }
2150 else
2151 {
2152 /* Check if this was the last key for a create */
2153 if (Last && ParseContext)
2154 {
2155 /* Check if we're doing a link node */
2156 if (ParseContext->CreateLink)
2157 {
2158 /* The only thing we should see */
2159 Status = CmpCreateLinkNode(Hive,
2160 Cell,
2161 AccessState,
2162 NextName,
2163 AccessMode,
2164 Attributes,
2165 ParseContext,
2166 ParentKcb,
2167 LockedKcbs,
2168 Object);
2169 }
2170 else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates)
2171 {
2172 /* Creating keys in the master hive is not allowed */
2173 Status = STATUS_INVALID_PARAMETER;
2174 }
2175 else
2176 {
2177 /* Do the create */
2178 Status = CmpDoCreate(Hive,
2179 Cell,
2180 AccessState,
2181 &NextName,
2182 AccessMode,
2183 ParseContext,
2184 ParentKcb,
2185 Object);
2186 }
2187
2188 /* Check for reparse (in this case, someone beat us) */
2189 if (Status == STATUS_REPARSE) break;
2190
2191 /* Update disposition */
2192 ParseContext->Disposition = REG_CREATED_NEW_KEY;
2193 break;
2194 }
2195 else
2196 {
2197 /* Key not found */
2198 Status = STATUS_OBJECT_NAME_NOT_FOUND;
2199 break;
2200 }
2201 }
2202 }
2203 else
2204 {
2205 /* Save the next name */
2206 Current.Buffer = NextName.Buffer;
2207
2208 /* Validate the current name string length */
2209 if (Current.Length + NextName.Length > MAXUSHORT)
2210 {
2211 /* too long */
2212 Status = STATUS_NAME_TOO_LONG;
2213 break;
2214 }
2215 Current.Length += NextName.Length;
2216
2217 /* Validate the current name string maximum length */
2218 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
2219 {
2220 /* too long */
2221 Status = STATUS_NAME_TOO_LONG;
2222 break;
2223 }
2224 Current.MaximumLength += NextName.MaximumLength;
2225
2226 /* CmpGetSymbolicLink doesn't want a lock */
2227 CmpUnLockKcbArray(LockedKcbs);
2228 LockedKcbs = NULL;
2229
2230 /* Parse the symlink */
2231 if (CmpGetSymbolicLink(Hive,
2232 CompleteName,
2233 Kcb,
2234 &Current))
2235 {
2236 /* Symlink parse succeeded */
2237 Status = STATUS_REPARSE;
2238 }
2239 else
2240 {
2241 /* Couldn't find symlink */
2242 Status = STATUS_OBJECT_NAME_NOT_FOUND;
2243 }
2244
2245 /* We're done */
2246 break;
2247 }
2248 }
2249 else if (Result && Last)
2250 {
2251 /* Opening the root. Is this an exit node? */
2252 if (Node->Flags & KEY_HIVE_EXIT)
2253 {
2254 /* Handle it */
2255 CmpHandleExitNode(&Hive,
2256 &Cell,
2257 &Node,
2258 &HiveToRelease,
2259 &CellToRelease);
2260 if (!Node)
2261 {
2262 /* Fail */
2263 Status = STATUS_INSUFFICIENT_RESOURCES;
2264 break;
2265 }
2266 }
2267
2268 /* Do the open */
2269 Status = CmpDoOpen(Hive,
2270 Cell,
2271 Node,
2272 AccessState,
2273 AccessMode,
2274 Attributes,
2275 ParseContext,
2276 CMP_OPEN_KCB_NO_CREATE,
2277 &Kcb,
2278 LockedKcbs,
2279 &NextName,
2280 Object);
2281 if (Status == STATUS_REPARSE)
2282 {
2283 /* Nothing to do */
2284 }
2285
2286 /* We're done */
2287 break;
2288 }
2289 else
2290 {
2291 /* Bogus */
2292 Status = STATUS_INVALID_PARAMETER;
2293 break;
2294 }
2295 }
2296
2297 Quickie:
2298 /* Unlock all the KCBs */
2299 if (LockedKcbs != NULL)
2300 {
2301 CmpUnLockKcbArray(LockedKcbs);
2302 }
2303
2304 /* Dereference the parent if it exists */
2305 if (ParentKcb)
2306 CmpDereferenceKeyControlBlock(ParentKcb);
2307
2308 /* Unlock the registry */
2309 CmpUnlockRegistry();
2310 return Status;
2311 }
2312