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