Sync with trunk (r48144)
[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 = '20yk';
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 = '20yk';
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 ULONG HashKeyCopy;
1003
1004 /* We don't lock anything for now */
1005 *LockedKcbs = NULL;
1006
1007 /* Make a copy of the hash key */
1008 HashKeyCopy = (*Kcb)->ConvKey;
1009
1010 /* Calculate hash values */
1011 *TotalRemainingSubkeys = 0xBAADF00D;
1012
1013 /* Lock the registry */
1014 CmpLockRegistry();
1015
1016 /* Return hive and cell data */
1017 *Hive = (*Kcb)->KeyHive;
1018 *Cell = (*Kcb)->KeyCell;
1019
1020 /* Make sure it's not a dead KCB */
1021 ASSERT((*Kcb)->RefCount > 0);
1022
1023 /* Reference it */
1024 (VOID)CmpReferenceKeyControlBlock(*Kcb);
1025
1026 /* Return success for now */
1027 return STATUS_SUCCESS;
1028 }
1029
1030 NTSTATUS
1031 NTAPI
1032 CmpParseKey(IN PVOID ParseObject,
1033 IN PVOID ObjectType,
1034 IN OUT PACCESS_STATE AccessState,
1035 IN KPROCESSOR_MODE AccessMode,
1036 IN ULONG Attributes,
1037 IN OUT PUNICODE_STRING CompleteName,
1038 IN OUT PUNICODE_STRING RemainingName,
1039 IN OUT PVOID Context OPTIONAL,
1040 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
1041 OUT PVOID *Object)
1042 {
1043 NTSTATUS Status;
1044 PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb;
1045 PHHIVE Hive = NULL;
1046 PCM_KEY_NODE Node = NULL;
1047 HCELL_INDEX Cell = HCELL_NIL, NextCell;
1048 PHHIVE HiveToRelease = NULL;
1049 HCELL_INDEX CellToRelease = HCELL_NIL;
1050 UNICODE_STRING Current, NextName;
1051 PCM_PARSE_CONTEXT ParseContext = Context;
1052 ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0;
1053 PULONG LockedKcbs = NULL;
1054 BOOLEAN Result, Last;
1055 PAGED_CODE();
1056
1057 /* Loop path separators at the end */
1058 while ((RemainingName->Length) &&
1059 (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] ==
1060 OBJ_NAME_PATH_SEPARATOR))
1061 {
1062 /* Remove path separator */
1063 RemainingName->Length -= sizeof(WCHAR);
1064 }
1065
1066 /* Fail if this isn't a key object */
1067 if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH;
1068
1069 /* Copy the remaining name */
1070 Current = *RemainingName;
1071
1072 /* Check if this is a create */
1073 if (!(ParseContext) || !(ParseContext->CreateOperation))
1074 {
1075 /* It isn't, so no context */
1076 ParseContext = NULL;
1077 }
1078
1079 /* Grab the KCB */
1080 Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock;
1081
1082 /* Lookup in the cache */
1083 Status = CmpBuildHashStackAndLookupCache(ParseObject,
1084 &Kcb,
1085 &Current,
1086 &Hive,
1087 &Cell,
1088 &TotalRemainingSubkeys,
1089 &MatchRemainSubkeyLevel,
1090 &TotalSubkeys,
1091 NULL,
1092 &LockedKcbs);
1093
1094 /* This is now the parent */
1095 ParentKcb = Kcb;
1096
1097 /* Check if everything was found cached */
1098 if (!TotalRemainingSubkeys) ASSERTMSG("Caching not implemented", FALSE);
1099
1100 /* Don't do anything if we're being deleted */
1101 if (Kcb->Delete)
1102 {
1103 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1104 goto Quickie;
1105 }
1106
1107 /* Check if this is a symlink */
1108 if (Kcb->Flags & KEY_SYM_LINK)
1109 {
1110 /* Get the next name */
1111 Result = CmpGetNextName(&Current, &NextName, &Last);
1112 Current.Buffer = NextName.Buffer;
1113
1114 /* Validate the current name string length */
1115 if (Current.Length + NextName.Length > MAXUSHORT)
1116 {
1117 /* too long */
1118 Status = STATUS_NAME_TOO_LONG;
1119 goto Quickie;
1120 }
1121 Current.Length += NextName.Length;
1122
1123 /* Validate the current name string maximum length */
1124 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
1125 {
1126 /* too long */
1127 Status = STATUS_NAME_TOO_LONG;
1128 goto Quickie;
1129 }
1130 Current.MaximumLength += NextName.MaximumLength;
1131
1132 /* Parse the symlink */
1133 if (CmpGetSymbolicLink(Hive,
1134 CompleteName,
1135 Kcb,
1136 &Current))
1137 {
1138 /* Symlink parse succeeded */
1139 Status = STATUS_REPARSE;
1140 }
1141 else
1142 {
1143 /* Couldn't find symlink */
1144 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1145 }
1146
1147 /* We're done */
1148 goto Quickie;
1149 }
1150
1151 /* Get the key node */
1152 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1153 if (!Node)
1154 {
1155 Status = STATUS_INSUFFICIENT_RESOURCES;
1156 goto Quickie;
1157 }
1158
1159 /* Start parsing */
1160 Status = STATUS_NOT_IMPLEMENTED;
1161 while (TRUE)
1162 {
1163 /* Get the next component */
1164 Result = CmpGetNextName(&Current, &NextName, &Last);
1165 if ((Result) && (NextName.Length))
1166 {
1167 /* See if this is a sym link */
1168 if (!(Kcb->Flags & KEY_SYM_LINK))
1169 {
1170 /* Find the subkey */
1171 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName);
1172 if (NextCell != HCELL_NIL)
1173 {
1174 /* Get the new node */
1175 Cell = NextCell;
1176 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1177 if (!Node) ASSERT(FALSE);
1178
1179 /* Check if this was the last key */
1180 if (Last)
1181 {
1182 /* Is this an exit node */
1183 if (Node->Flags & KEY_HIVE_EXIT)
1184 {
1185 /* Handle it */
1186 CmpHandleExitNode(&Hive,
1187 &Cell,
1188 &Node,
1189 &HiveToRelease,
1190 &CellToRelease);
1191 if (!Node) ASSERT(FALSE);
1192 }
1193
1194 /* Do the open */
1195 Status = CmpDoOpen(Hive,
1196 Cell,
1197 Node,
1198 AccessState,
1199 AccessMode,
1200 Attributes,
1201 ParseContext,
1202 0,
1203 &Kcb,
1204 &NextName,
1205 Object);
1206 if (Status == STATUS_REPARSE)
1207 {
1208 /* Parse the symlink */
1209 if (!CmpGetSymbolicLink(Hive,
1210 CompleteName,
1211 Kcb,
1212 NULL))
1213 {
1214 /* Symlink parse failed */
1215 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1216 }
1217 }
1218
1219 /* We are done */
1220 break;
1221 }
1222
1223 /* Is this an exit node */
1224 if (Node->Flags & KEY_HIVE_EXIT)
1225 {
1226 /* Handle it */
1227 CmpHandleExitNode(&Hive,
1228 &Cell,
1229 &Node,
1230 &HiveToRelease,
1231 &CellToRelease);
1232 if (!Node) ASSERT(FALSE);
1233 }
1234
1235 /* Create a KCB for this key */
1236 Kcb = CmpCreateKeyControlBlock(Hive,
1237 Cell,
1238 Node,
1239 ParentKcb,
1240 0,
1241 &NextName);
1242 if (!Kcb) ASSERT(FALSE);
1243
1244 /* Dereference the parent and set the new one */
1245 CmpDereferenceKeyControlBlock(ParentKcb);
1246 ParentKcb = Kcb;
1247 }
1248 else
1249 {
1250 /* Check if this was the last key for a create */
1251 if ((Last) && (ParseContext))
1252 {
1253 /* Check if we're doing a link node */
1254 if (ParseContext->CreateLink)
1255 {
1256 /* The only thing we should see */
1257 Status = CmpCreateLinkNode(Hive,
1258 Cell,
1259 AccessState,
1260 NextName,
1261 AccessMode,
1262 Attributes,
1263 ParseContext,
1264 ParentKcb,
1265 Object);
1266 }
1267 else
1268 {
1269 /* Do the create */
1270 Status = CmpDoCreate(Hive,
1271 Cell,
1272 AccessState,
1273 &NextName,
1274 AccessMode,
1275 ParseContext,
1276 ParentKcb,
1277 Object);
1278 }
1279
1280 /* Check for reparse (in this case, someone beat us) */
1281 if (Status == STATUS_REPARSE) break;
1282
1283 /* Update disposition */
1284 ParseContext->Disposition = REG_CREATED_NEW_KEY;
1285 break;
1286 }
1287 else
1288 {
1289 /* Key not found */
1290 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1291 break;
1292 }
1293 }
1294 }
1295 else
1296 {
1297 /* Save the next name */
1298 Current.Buffer = NextName.Buffer;
1299
1300 /* Validate the current name string length */
1301 if (Current.Length + NextName.Length > MAXUSHORT)
1302 {
1303 /* too long */
1304 Status = STATUS_NAME_TOO_LONG;
1305 break;
1306 }
1307 Current.Length += NextName.Length;
1308
1309 /* Validate the current name string maximum length */
1310 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT)
1311 {
1312 /* too long */
1313 Status = STATUS_NAME_TOO_LONG;
1314 break;
1315 }
1316 Current.MaximumLength += NextName.MaximumLength;
1317
1318 /* Parse the symlink */
1319 if (CmpGetSymbolicLink(Hive,
1320 CompleteName,
1321 Kcb,
1322 &Current))
1323 {
1324 /* Symlink parse succeeded */
1325 Status = STATUS_REPARSE;
1326 }
1327 else
1328 {
1329 /* Couldn't find symlink */
1330 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1331 }
1332
1333 /* We're done */
1334 break;
1335 }
1336 }
1337 else if ((Result) && (Last))
1338 {
1339 /* Opening the root. Is this an exit node? */
1340 if (Node->Flags & KEY_HIVE_EXIT)
1341 {
1342 /* Handle it */
1343 CmpHandleExitNode(&Hive,
1344 &Cell,
1345 &Node,
1346 &HiveToRelease,
1347 &CellToRelease);
1348 if (!Node) ASSERT(FALSE);
1349 }
1350
1351 /* Do the open */
1352 Status = CmpDoOpen(Hive,
1353 Cell,
1354 Node,
1355 AccessState,
1356 AccessMode,
1357 Attributes,
1358 ParseContext,
1359 CMP_OPEN_KCB_NO_CREATE /* | CMP_CREATE_KCB_KCB_LOCKED */,
1360 &Kcb,
1361 &NextName,
1362 Object);
1363 if (Status == STATUS_REPARSE)
1364 {
1365 /* Nothing to do */
1366 }
1367
1368 /* We're done */
1369 break;
1370 }
1371 else
1372 {
1373 /* Bogus */
1374 Status = STATUS_INVALID_PARAMETER;
1375 break;
1376 }
1377 }
1378
1379 /* Dereference the parent if it exists */
1380 Quickie:
1381 if (ParentKcb) CmpDereferenceKeyControlBlock(ParentKcb);
1382
1383 /* Unlock the registry */
1384 CmpUnlockRegistry();
1385 return Status;
1386 }