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