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