Sync with trunk r64509.
[reactos.git] / ntoskrnl / config / cmapi.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmapi.c
5 * PURPOSE: Configuration Manager - Internal Registry APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Eric Kohl
8 * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include "ntoskrnl.h"
14 #define NDEBUG
15 #include "debug.h"
16
17 /* FUNCTIONS *****************************************************************/
18
19 BOOLEAN
20 NTAPI
21 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
22 IN POBJECT_ATTRIBUTES SourceFile,
23 OUT PCMHIVE *CmHive)
24 {
25 NTSTATUS Status;
26 PCM_KEY_BODY KeyBody;
27 PCMHIVE Hive;
28 BOOLEAN Loaded = FALSE;
29 PAGED_CODE();
30
31 /* Sanity check */
32 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
33
34 /* Reference the handle */
35 Status = ObReferenceObjectByHandle(KeyHandle,
36 0,
37 CmpKeyObjectType,
38 KernelMode,
39 (PVOID)&KeyBody,
40 NULL);
41 if (!NT_SUCCESS(Status)) return Loaded;
42
43 /* Don't touch deleted KCBs */
44 if (KeyBody->KeyControlBlock->Delete) return Loaded;
45
46 Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
47
48 /* Must be the root key */
49 if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
50 !(Hive->FileUserName.Buffer))
51 {
52 /* It isn't */
53 ObDereferenceObject(KeyBody);
54 return Loaded;
55 }
56
57 /* Now compare the name of the file */
58 if (!RtlCompareUnicodeString(&Hive->FileUserName,
59 SourceFile->ObjectName,
60 TRUE))
61 {
62 /* Same file found */
63 Loaded = TRUE;
64 *CmHive = Hive;
65
66 /* If the hive is frozen, not sure what to do */
67 if (Hive->Frozen)
68 {
69 /* FIXME: TODO */
70 DPRINT1("ERROR: Hive is frozen\n");
71 while (TRUE);
72 }
73 }
74
75 /* Dereference and return result */
76 ObDereferenceObject(KeyBody);
77 return Loaded;
78 }
79
80 BOOLEAN
81 NTAPI
82 CmpDoFlushAll(IN BOOLEAN ForceFlush)
83 {
84 PLIST_ENTRY NextEntry;
85 PCMHIVE Hive;
86 NTSTATUS Status;
87 BOOLEAN Result = TRUE;
88
89 /* Make sure that the registry isn't read-only now */
90 if (CmpNoWrite) return TRUE;
91
92 /* Otherwise, acquire the hive list lock and disable force flush */
93 CmpForceForceFlush = FALSE;
94 ExAcquirePushLockShared(&CmpHiveListHeadLock);
95
96 /* Loop the hive list */
97 NextEntry = CmpHiveListHead.Flink;
98 while (NextEntry != &CmpHiveListHead)
99 {
100 /* Get the hive */
101 Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
102 if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
103 {
104 /* Acquire the flusher lock */
105 CmpLockHiveFlusherExclusive(Hive);
106
107 /* Check for illegal state */
108 if ((ForceFlush) && (Hive->UseCount))
109 {
110 /* Registry needs to be locked down */
111 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
112 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
113 while (TRUE);
114 }
115
116 /* Only sync if we are forced to or if it won't cause a hive shrink */
117 if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
118 {
119 /* Do the sync */
120 Status = HvSyncHive(&Hive->Hive);
121
122 /* If something failed - set the flag and continue looping */
123 if (!NT_SUCCESS(Status)) Result = FALSE;
124 }
125 else
126 {
127 /* We won't flush if the hive might shrink */
128 Result = FALSE;
129 CmpForceForceFlush = TRUE;
130 }
131
132 /* Release the flusher lock */
133 CmpUnlockHiveFlusher(Hive);
134 }
135
136 /* Try the next entry */
137 NextEntry = NextEntry->Flink;
138 }
139
140 /* Release lock and return */
141 ExReleasePushLock(&CmpHiveListHeadLock);
142 return Result;
143 }
144
145 NTSTATUS
146 NTAPI
147 CmpSetValueKeyNew(IN PHHIVE Hive,
148 IN PCM_KEY_NODE Parent,
149 IN PUNICODE_STRING ValueName,
150 IN ULONG Index,
151 IN ULONG Type,
152 IN PVOID Data,
153 IN ULONG DataSize,
154 IN ULONG StorageType,
155 IN ULONG SmallData)
156 {
157 PCELL_DATA CellData;
158 HCELL_INDEX ValueCell;
159 NTSTATUS Status;
160
161 /* Check if we already have a value list */
162 if (Parent->ValueList.Count)
163 {
164 /* Then make sure it's valid and dirty it */
165 ASSERT(Parent->ValueList.List != HCELL_NIL);
166 if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
167 {
168 /* Fail if we're out of space for log changes */
169 return STATUS_NO_LOG_SPACE;
170 }
171 }
172
173 /* Allocate a value cell */
174 ValueCell = HvAllocateCell(Hive,
175 FIELD_OFFSET(CM_KEY_VALUE, Name) +
176 CmpNameSize(Hive, ValueName),
177 StorageType,
178 HCELL_NIL);
179 if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
180
181 /* Get the actual data for it */
182 CellData = HvGetCell(Hive, ValueCell);
183 if (!CellData) ASSERT(FALSE);
184
185 /* Now we can release it, make sure it's also dirty */
186 HvReleaseCell(Hive, ValueCell);
187 ASSERT(HvIsCellDirty(Hive, ValueCell));
188
189 /* Set it up and copy the name */
190 CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
191 _SEH2_TRY
192 {
193 /* This can crash since the name is coming from user-mode */
194 CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
195 CellData->u.KeyValue.Name,
196 ValueName);
197 }
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
199 {
200 /* Fail */
201 DPRINT1("Invalid user data!\n");
202 HvFreeCell(Hive, ValueCell);
203 _SEH2_YIELD(return _SEH2_GetExceptionCode());
204 }
205 _SEH2_END;
206
207 /* Check for compressed name */
208 if (CellData->u.KeyValue.NameLength < ValueName->Length)
209 {
210 /* This is a compressed name */
211 CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
212 }
213 else
214 {
215 /* No flags to set */
216 CellData->u.KeyValue.Flags = 0;
217 }
218
219 /* Check if this is a normal key */
220 if (DataSize > CM_KEY_VALUE_SMALL)
221 {
222 /* Build a data cell for it */
223 Status = CmpSetValueDataNew(Hive,
224 Data,
225 DataSize,
226 StorageType,
227 ValueCell,
228 &CellData->u.KeyValue.Data);
229 if (!NT_SUCCESS(Status))
230 {
231 /* We failed, free the cell */
232 HvFreeCell(Hive, ValueCell);
233 return Status;
234 }
235
236 /* Otherwise, set the data length, and make sure the data is dirty */
237 CellData->u.KeyValue.DataLength = DataSize;
238 ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
239 }
240 else
241 {
242 /* This is a small key, set the data directly inside */
243 CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
244 CellData->u.KeyValue.Data = SmallData;
245 }
246
247 /* Set the type now */
248 CellData->u.KeyValue.Type = Type;
249
250 /* Add this value cell to the child list */
251 Status = CmpAddValueToList(Hive,
252 ValueCell,
253 Index,
254 StorageType,
255 &Parent->ValueList);
256
257 /* If we failed, free the entire cell, including the data */
258 if (!NT_SUCCESS(Status))
259 {
260 /* Overwrite the status with a known one */
261 CmpFreeValue(Hive, ValueCell);
262 Status = STATUS_INSUFFICIENT_RESOURCES;
263 }
264
265 /* Return Status */
266 return Status;
267 }
268
269 NTSTATUS
270 NTAPI
271 CmpSetValueKeyExisting(IN PHHIVE Hive,
272 IN HCELL_INDEX OldChild,
273 IN PCM_KEY_VALUE Value,
274 IN ULONG Type,
275 IN PVOID Data,
276 IN ULONG DataSize,
277 IN ULONG StorageType,
278 IN ULONG TempData)
279 {
280 HCELL_INDEX DataCell, NewCell;
281 PCELL_DATA CellData;
282 ULONG Length;
283 BOOLEAN WasSmall, IsSmall;
284
285 /* Registry writes must be blocked */
286 CMP_ASSERT_FLUSH_LOCK(Hive);
287
288 /* Mark the old child cell dirty */
289 if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
290
291 /* See if this is a small or normal key */
292 WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
293
294 /* See if our new data can fit in a small key */
295 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
296
297 /* Big keys are unsupported */
298 ASSERT_VALUE_BIG(Hive, Length);
299 ASSERT_VALUE_BIG(Hive, DataSize);
300
301 /* Mark the old value dirty */
302 if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
303
304 /* Check if we have a small key */
305 if (IsSmall)
306 {
307 /* Check if we had a normal key with some data in it */
308 if (!(WasSmall) && (Length > 0))
309 {
310 /* Free the previous data */
311 CmpFreeValueData(Hive, Value->Data, Length);
312 }
313
314 /* Write our data directly */
315 Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
316 Value->Data = TempData;
317 Value->Type = Type;
318 return STATUS_SUCCESS;
319 }
320
321 /* We have a normal key. Was the old cell also normal and had data? */
322 if (!(WasSmall) && (Length > 0))
323 {
324 /* Get the current data cell and actual data inside it */
325 DataCell = Value->Data;
326 ASSERT(DataCell != HCELL_NIL);
327 CellData = HvGetCell(Hive, DataCell);
328 if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
329
330 /* Immediately release the cell */
331 HvReleaseCell(Hive, DataCell);
332
333 /* Make sure that the data cell actually has a size */
334 ASSERT(HvGetCellSize(Hive, CellData) > 0);
335
336 /* Check if the previous data cell could fit our new data */
337 if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
338 {
339 /* Re-use it then */
340 NewCell = DataCell;
341 }
342 else
343 {
344 /* Otherwise, re-allocate the current data cell */
345 NewCell = HvReallocateCell(Hive, DataCell, DataSize);
346 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
347 }
348 }
349 else
350 {
351 /* This was a small key, or a key with no data, allocate a cell */
352 NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
353 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
354 }
355
356 /* Now get the actual data for our data cell */
357 CellData = HvGetCell(Hive, NewCell);
358 if (!CellData) ASSERT(FALSE);
359
360 /* Release it immediately */
361 HvReleaseCell(Hive, NewCell);
362
363 /* Copy our data into the data cell's buffer, and set up the value */
364 RtlCopyMemory(CellData, Data, DataSize);
365 Value->Data = NewCell;
366 Value->DataLength = DataSize;
367 Value->Type = Type;
368
369 /* Return success */
370 ASSERT(HvIsCellDirty(Hive, NewCell));
371 return STATUS_SUCCESS;
372 }
373
374 NTSTATUS
375 NTAPI
376 CmpQueryKeyData(IN PHHIVE Hive,
377 IN PCM_KEY_NODE Node,
378 IN KEY_INFORMATION_CLASS KeyInformationClass,
379 IN OUT PVOID KeyInformation,
380 IN ULONG Length,
381 IN OUT PULONG ResultLength)
382 {
383 NTSTATUS Status;
384 ULONG Size, SizeLeft, MinimumSize, Offset;
385 PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
386 USHORT NameLength;
387 PVOID ClassData;
388
389 /* Check if the value is compressed */
390 if (Node->Flags & KEY_COMP_NAME)
391 {
392 /* Get the compressed name size */
393 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
394 }
395 else
396 {
397 /* Get the real size */
398 NameLength = Node->NameLength;
399 }
400
401 /* Check what kind of information is being requested */
402 switch (KeyInformationClass)
403 {
404 /* Basic information */
405 case KeyBasicInformation:
406
407 /* This is the size we need */
408 Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
409
410 /* And this is the minimum we can work with */
411 MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
412
413 /* Let the caller know and assume success */
414 *ResultLength = Size;
415 Status = STATUS_SUCCESS;
416
417 /* Check if the bufer we got is too small */
418 if (Length < MinimumSize)
419 {
420 /* Let the caller know and fail */
421 Status = STATUS_BUFFER_TOO_SMALL;
422 break;
423 }
424
425 /* Copy the basic information */
426 Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
427 Info->KeyBasicInformation.TitleIndex = 0;
428 Info->KeyBasicInformation.NameLength = NameLength;
429
430 /* Only the name is left */
431 SizeLeft = Length - MinimumSize;
432 Size = NameLength;
433
434 /* Check if we don't have enough space for the name */
435 if (SizeLeft < Size)
436 {
437 /* Truncate the name we'll return, and tell the caller */
438 Size = SizeLeft;
439 Status = STATUS_BUFFER_OVERFLOW;
440 }
441
442 /* Check if this is a compressed key */
443 if (Node->Flags & KEY_COMP_NAME)
444 {
445 /* Copy the compressed name */
446 CmpCopyCompressedName(Info->KeyBasicInformation.Name,
447 SizeLeft,
448 Node->Name,
449 Node->NameLength);
450 }
451 else
452 {
453 /* Otherwise, copy the raw name */
454 RtlCopyMemory(Info->KeyBasicInformation.Name,
455 Node->Name,
456 Size);
457 }
458 break;
459
460 /* Node information */
461 case KeyNodeInformation:
462
463 /* Calculate the size we need */
464 Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
465 NameLength +
466 Node->ClassLength;
467
468 /* And the minimum size we can support */
469 MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
470
471 /* Return the size to the caller and assume succes */
472 *ResultLength = Size;
473 Status = STATUS_SUCCESS;
474
475 /* Check if the caller's buffer is too small */
476 if (Length < MinimumSize)
477 {
478 /* Let them know, and fail */
479 Status = STATUS_BUFFER_TOO_SMALL;
480 break;
481 }
482
483 /* Copy the basic information */
484 Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
485 Info->KeyNodeInformation.TitleIndex = 0;
486 Info->KeyNodeInformation.ClassLength = Node->ClassLength;
487 Info->KeyNodeInformation.NameLength = NameLength;
488
489 /* Now the name is left */
490 SizeLeft = Length - MinimumSize;
491 Size = NameLength;
492
493 /* Check if the name can fit entirely */
494 if (SizeLeft < Size)
495 {
496 /* It can't, we'll have to truncate. Tell the caller */
497 Size = SizeLeft;
498 Status = STATUS_BUFFER_OVERFLOW;
499 }
500
501 /* Check if the key node name is compressed */
502 if (Node->Flags & KEY_COMP_NAME)
503 {
504 /* Copy the compressed name */
505 CmpCopyCompressedName(Info->KeyNodeInformation.Name,
506 SizeLeft,
507 Node->Name,
508 Node->NameLength);
509 }
510 else
511 {
512 /* It isn't, so copy the raw name */
513 RtlCopyMemory(Info->KeyNodeInformation.Name,
514 Node->Name,
515 Size);
516 }
517
518 /* Check if the node has a class */
519 if (Node->ClassLength > 0)
520 {
521 /* Set the class offset */
522 Offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength;
523 Offset = ALIGN_UP_BY(Offset, sizeof(ULONG));
524 Info->KeyNodeInformation.ClassOffset = Offset;
525
526 /* Get the class data */
527 ClassData = HvGetCell(Hive, Node->Class);
528 if (ClassData == NULL)
529 {
530 Status = STATUS_INSUFFICIENT_RESOURCES;
531 break;
532 }
533
534 /* Check if we can copy anything */
535 if (Length > Offset)
536 {
537 /* Copy the class data */
538 RtlCopyMemory((PUCHAR)Info + Offset,
539 ClassData,
540 min(Node->ClassLength, Length - Offset));
541 }
542
543 /* Check if the buffer was large enough */
544 if (Length < Offset + Node->ClassLength)
545 {
546 Status = STATUS_BUFFER_OVERFLOW;
547 }
548
549 /* Release the class cell */
550 HvReleaseCell(Hive, Node->Class);
551 }
552 else
553 {
554 /* It doesn't, so set offset to -1, not 0! */
555 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
556 }
557 break;
558
559 /* Full information requsted */
560 case KeyFullInformation:
561
562 /* This is the size we need */
563 Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
564 Node->ClassLength;
565
566 /* This is what we can work with */
567 MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
568
569 /* Return it to caller and assume success */
570 *ResultLength = Size;
571 Status = STATUS_SUCCESS;
572
573 /* Check if the caller's buffer is to small */
574 if (Length < MinimumSize)
575 {
576 /* Let them know and fail */
577 Status = STATUS_BUFFER_TOO_SMALL;
578 break;
579 }
580
581 /* Now copy all the basic information */
582 Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
583 Info->KeyFullInformation.TitleIndex = 0;
584 Info->KeyFullInformation.ClassLength = Node->ClassLength;
585 Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
586 Node->SubKeyCounts[Volatile];
587 Info->KeyFullInformation.Values = Node->ValueList.Count;
588 Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
589 Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
590 Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
591 Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
592
593 /* Check if we have a class */
594 if (Node->ClassLength > 0)
595 {
596 /* Set the class offset */
597 Offset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
598 Info->KeyFullInformation.ClassOffset = Offset;
599
600 /* Get the class data */
601 ClassData = HvGetCell(Hive, Node->Class);
602 if (ClassData == NULL)
603 {
604 Status = STATUS_INSUFFICIENT_RESOURCES;
605 break;
606 }
607
608 /* Copy the class data */
609 NT_ASSERT(Length > Offset);
610 RtlCopyMemory(Info->KeyFullInformation.Class,
611 ClassData,
612 min(Node->ClassLength, Length - Offset));
613
614 /* Check if the buffer was large enough */
615 if (Length < Offset + Node->ClassLength)
616 {
617 Status = STATUS_BUFFER_OVERFLOW;
618 }
619
620 /* Release the class cell */
621 HvReleaseCell(Hive, Node->Class);
622 }
623 else
624 {
625 /* We don't have a class, so set offset to -1, not 0! */
626 Info->KeyFullInformation.ClassOffset = 0xFFFFFFFF;
627 }
628 break;
629
630 /* Any other class that got sent here is invalid! */
631 default:
632
633 /* Set failure code */
634 Status = STATUS_INVALID_PARAMETER;
635 break;
636 }
637
638 /* Return status */
639 return Status;
640 }
641
642 NTSTATUS
643 NTAPI
644 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
645 IN PUNICODE_STRING ValueName,
646 IN ULONG Type,
647 IN PVOID Data,
648 IN ULONG DataLength)
649 {
650 PHHIVE Hive = NULL;
651 PCM_KEY_NODE Parent;
652 PCM_KEY_VALUE Value = NULL;
653 HCELL_INDEX CurrentChild, Cell;
654 NTSTATUS Status;
655 BOOLEAN Found, Result;
656 ULONG Count, ChildIndex, SmallData, Storage;
657 VALUE_SEARCH_RETURN_TYPE SearchResult;
658 BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
659 HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
660
661 /* Acquire hive and KCB lock */
662 CmpLockRegistry();
663 CmpAcquireKcbLockShared(Kcb);
664
665 /* Sanity check */
666 ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
667
668 /* Don't touch deleted KCBs */
669 DoAgain:
670 if (Kcb->Delete)
671 {
672 /* Fail */
673 Status = STATUS_KEY_DELETED;
674 goto Quickie;
675 }
676
677 /* Don't let anyone mess with symlinks */
678 if ((Kcb->Flags & KEY_SYM_LINK) &&
679 ((Type != REG_LINK) ||
680 !(ValueName) ||
681 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
682 {
683 /* Invalid modification of a symlink key */
684 Status = STATUS_ACCESS_DENIED;
685 goto Quickie;
686 }
687
688 /* Check if this is the first attempt */
689 if (FirstTry)
690 {
691 /* Search for the value in the cache */
692 SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
693 ValueName,
694 Type,
695 Data,
696 DataLength);
697 if (SearchResult == SearchNeedExclusiveLock)
698 {
699 /* Try again with the exclusive lock */
700 CmpConvertKcbSharedToExclusive(Kcb);
701 goto DoAgain;
702 }
703 else if (SearchResult == SearchSuccess)
704 {
705 /* We don't actually need to do anything! */
706 Status = STATUS_SUCCESS;
707 goto Quickie;
708 }
709
710 /* We need the exclusive KCB lock now */
711 if (!(CmpIsKcbLockedExclusive(Kcb)) &&
712 !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
713 {
714 /* Acquire exclusive lock */
715 CmpConvertKcbSharedToExclusive(Kcb);
716 }
717
718 /* Cache lookup failed, so don't try it next time */
719 FirstTry = FALSE;
720
721 /* Now grab the flush lock since the key will be modified */
722 ASSERT(FlusherLocked == FALSE);
723 CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
724 FlusherLocked = TRUE;
725 goto DoAgain;
726 }
727 else
728 {
729 /* Get pointer to key cell */
730 Hive = Kcb->KeyHive;
731 Cell = Kcb->KeyCell;
732
733 /* Get the parent */
734 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
735 ASSERT(Parent);
736 ParentCell = Cell;
737
738 /* Prepare to scan the key node */
739 Count = Parent->ValueList.Count;
740 Found = FALSE;
741 if (Count > 0)
742 {
743 /* Try to find the existing name */
744 Result = CmpFindNameInList(Hive,
745 &Parent->ValueList,
746 ValueName,
747 &ChildIndex,
748 &CurrentChild);
749 if (!Result)
750 {
751 /* Fail */
752 Status = STATUS_INSUFFICIENT_RESOURCES;
753 goto Quickie;
754 }
755
756 /* Check if we found something */
757 if (CurrentChild != HCELL_NIL)
758 {
759 /* Release existing child */
760 if (ChildCell != HCELL_NIL)
761 {
762 HvReleaseCell(Hive, ChildCell);
763 ChildCell = HCELL_NIL;
764 }
765
766 /* Get its value */
767 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
768 if (!Value)
769 {
770 /* Fail */
771 Status = STATUS_INSUFFICIENT_RESOURCES;
772 goto Quickie;
773 }
774
775 /* Remember that we found it */
776 ChildCell = CurrentChild;
777 Found = TRUE;
778 }
779 }
780 else
781 {
782 /* No child list, we'll need to add it */
783 ChildIndex = 0;
784 }
785 }
786
787 /* Should only get here on the second pass */
788 ASSERT(FirstTry == FALSE);
789
790 /* The KCB must be locked exclusive at this point */
791 CMP_ASSERT_KCB_LOCK(Kcb);
792
793 /* Mark the cell dirty */
794 if (!HvMarkCellDirty(Hive, Cell, FALSE))
795 {
796 /* Not enough log space, fail */
797 Status = STATUS_NO_LOG_SPACE;
798 goto Quickie;
799 }
800
801 /* Get the storage type */
802 Storage = HvGetCellType(Cell);
803
804 /* Check if this is small data */
805 SmallData = 0;
806 if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
807 {
808 /* Need SEH because user data may be invalid */
809 _SEH2_TRY
810 {
811 /* Copy it */
812 RtlCopyMemory(&SmallData, Data, DataLength);
813 }
814 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
815 {
816 /* Return failure code */
817 Status = _SEH2_GetExceptionCode();
818 _SEH2_YIELD(goto Quickie);
819 }
820 _SEH2_END;
821 }
822
823 /* Check if we didn't find a matching key */
824 if (!Found)
825 {
826 /* Call the internal routine */
827 Status = CmpSetValueKeyNew(Hive,
828 Parent,
829 ValueName,
830 ChildIndex,
831 Type,
832 Data,
833 DataLength,
834 Storage,
835 SmallData);
836 }
837 else
838 {
839 /* Call the internal routine */
840 Status = CmpSetValueKeyExisting(Hive,
841 CurrentChild,
842 Value,
843 Type,
844 Data,
845 DataLength,
846 Storage,
847 SmallData);
848 }
849
850 /* Check for success */
851 if (NT_SUCCESS(Status))
852 {
853 /* Check if the maximum value name length changed */
854 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
855 if (Parent->MaxValueNameLen < ValueName->Length)
856 {
857 /* Set the new values */
858 Parent->MaxValueNameLen = ValueName->Length;
859 Kcb->KcbMaxValueNameLen = ValueName->Length;
860 }
861
862 /* Check if the maximum data length changed */
863 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
864 if (Parent->MaxValueDataLen < DataLength)
865 {
866 /* Update it */
867 Parent->MaxValueDataLen = DataLength;
868 Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
869 }
870
871 /* Save the write time */
872 KeQuerySystemTime(&Parent->LastWriteTime);
873 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
874
875 /* Check if the cell is cached */
876 if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
877 {
878 /* Shouldn't happen */
879 ASSERT(FALSE);
880 }
881 else
882 {
883 /* Cleanup the value cache */
884 CmpCleanUpKcbValueCache(Kcb);
885
886 /* Sanity checks */
887 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
888 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
889
890 /* Set the value cache */
891 Kcb->ValueCache.Count = Parent->ValueList.Count;
892 Kcb->ValueCache.ValueList = Parent->ValueList.List;
893 }
894
895 /* Notify registered callbacks */
896 CmpReportNotify(Kcb,
897 Hive,
898 Kcb->KeyCell,
899 REG_NOTIFY_CHANGE_LAST_SET);
900 }
901
902 /* Release the cells */
903 Quickie:
904 if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
905 if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
906
907 /* Release the locks */
908 if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
909 CmpReleaseKcbLock(Kcb);
910 CmpUnlockRegistry();
911 return Status;
912 }
913
914 NTSTATUS
915 NTAPI
916 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
917 IN UNICODE_STRING ValueName)
918 {
919 NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
920 PHHIVE Hive;
921 PCM_KEY_NODE Parent;
922 HCELL_INDEX ChildCell, Cell;
923 PCHILD_LIST ChildList;
924 PCM_KEY_VALUE Value = NULL;
925 ULONG ChildIndex;
926 BOOLEAN Result;
927
928 /* Acquire hive lock */
929 CmpLockRegistry();
930
931 /* Lock KCB exclusively */
932 CmpAcquireKcbLockExclusive(Kcb);
933
934 /* Don't touch deleted keys */
935 if (Kcb->Delete)
936 {
937 /* Undo everything */
938 CmpReleaseKcbLock(Kcb);
939 CmpUnlockRegistry();
940 return STATUS_KEY_DELETED;
941 }
942
943 /* Get the hive and the cell index */
944 Hive = Kcb->KeyHive;
945 Cell = Kcb->KeyCell;
946
947 /* Lock flushes */
948 CmpLockHiveFlusherShared((PCMHIVE)Hive);
949
950 /* Get the parent key node */
951 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
952 ASSERT(Parent);
953
954 /* Get the value list and check if it has any entries */
955 ChildList = &Parent->ValueList;
956 if (ChildList->Count)
957 {
958 /* Try to find this value */
959 Result = CmpFindNameInList(Hive,
960 ChildList,
961 &ValueName,
962 &ChildIndex,
963 &ChildCell);
964 if (!Result)
965 {
966 /* Fail */
967 Status = STATUS_INSUFFICIENT_RESOURCES;
968 goto Quickie;
969 }
970
971 /* Value not found, return error */
972 if (ChildCell == HCELL_NIL) goto Quickie;
973
974 /* We found the value, mark all relevant cells dirty */
975 if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
976 (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
977 (HvMarkCellDirty(Hive, ChildCell, FALSE))))
978 {
979 /* Not enough log space, fail */
980 Status = STATUS_NO_LOG_SPACE;
981 goto Quickie;
982 }
983
984 /* Get the key value */
985 Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
986 ASSERT(Value);
987
988 /* Mark it and all related data as dirty */
989 if (!CmpMarkValueDataDirty(Hive, Value))
990 {
991 /* Not enough log space, fail */
992 Status = STATUS_NO_LOG_SPACE;
993 goto Quickie;
994 }
995
996 /* Ssanity checks */
997 ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
998 ASSERT(HvIsCellDirty(Hive, ChildCell));
999
1000 /* Remove the value from the child list */
1001 Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
1002 if (!NT_SUCCESS(Status))
1003 {
1004 /* Set known error */
1005 Status = STATUS_INSUFFICIENT_RESOURCES;
1006 goto Quickie;
1007 }
1008
1009 /* Remove the value and its data itself */
1010 if (!CmpFreeValue(Hive, ChildCell))
1011 {
1012 /* Failed to free the value, fail */
1013 Status = STATUS_INSUFFICIENT_RESOURCES;
1014 goto Quickie;
1015 }
1016
1017 /* Set the last write time */
1018 KeQuerySystemTime(&Parent->LastWriteTime);
1019 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
1020
1021 /* Sanity check */
1022 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1023 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1024 ASSERT(HvIsCellDirty(Hive, Cell));
1025
1026 /* Check if the value list is empty now */
1027 if (!Parent->ValueList.Count)
1028 {
1029 /* Then clear key node data */
1030 Parent->MaxValueNameLen = 0;
1031 Parent->MaxValueDataLen = 0;
1032 Kcb->KcbMaxValueNameLen = 0;
1033 Kcb->KcbMaxValueDataLen = 0;
1034 }
1035
1036 /* Cleanup the value cache */
1037 CmpCleanUpKcbValueCache(Kcb);
1038
1039 /* Sanity checks */
1040 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
1041 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
1042
1043 /* Set the value cache */
1044 Kcb->ValueCache.Count = ChildList->Count;
1045 Kcb->ValueCache.ValueList = ChildList->List;
1046
1047 /* Notify registered callbacks */
1048 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
1049
1050 /* Change default Status to success */
1051 Status = STATUS_SUCCESS;
1052 }
1053
1054 Quickie:
1055 /* Release the parent cell, if any */
1056 if (Parent) HvReleaseCell(Hive, Cell);
1057
1058 /* Check if we had a value */
1059 if (Value)
1060 {
1061 /* Release the child cell */
1062 ASSERT(ChildCell != HCELL_NIL);
1063 HvReleaseCell(Hive, ChildCell);
1064 }
1065
1066 /* Release locks */
1067 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1068 CmpReleaseKcbLock(Kcb);
1069 CmpUnlockRegistry();
1070 return Status;
1071 }
1072
1073 NTSTATUS
1074 NTAPI
1075 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1076 IN UNICODE_STRING ValueName,
1077 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1078 IN PVOID KeyValueInformation,
1079 IN ULONG Length,
1080 IN PULONG ResultLength)
1081 {
1082 NTSTATUS Status;
1083 PCM_KEY_VALUE ValueData;
1084 ULONG Index;
1085 BOOLEAN ValueCached = FALSE;
1086 PCM_CACHED_VALUE *CachedValue;
1087 HCELL_INDEX CellToRelease;
1088 VALUE_SEARCH_RETURN_TYPE Result;
1089 PHHIVE Hive;
1090 PAGED_CODE();
1091
1092 /* Acquire hive lock */
1093 CmpLockRegistry();
1094
1095 /* Lock the KCB shared */
1096 CmpAcquireKcbLockShared(Kcb);
1097
1098 /* Don't touch deleted keys */
1099 DoAgain:
1100 if (Kcb->Delete)
1101 {
1102 /* Undo everything */
1103 CmpReleaseKcbLock(Kcb);
1104 CmpUnlockRegistry();
1105 return STATUS_KEY_DELETED;
1106 }
1107
1108 /* We don't deal with this yet */
1109 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1110 {
1111 /* Shouldn't happen */
1112 ASSERT(FALSE);
1113 }
1114
1115 /* Get the hive */
1116 Hive = Kcb->KeyHive;
1117
1118 /* Find the key value */
1119 Result = CmpFindValueByNameFromCache(Kcb,
1120 &ValueName,
1121 &CachedValue,
1122 &Index,
1123 &ValueData,
1124 &ValueCached,
1125 &CellToRelease);
1126 if (Result == SearchNeedExclusiveLock)
1127 {
1128 /* Check if we need an exclusive lock */
1129 ASSERT(CellToRelease == HCELL_NIL);
1130 ASSERT(ValueData == NULL);
1131
1132 /* Try with exclusive KCB lock */
1133 CmpConvertKcbSharedToExclusive(Kcb);
1134 goto DoAgain;
1135 }
1136
1137 if (Result == SearchSuccess)
1138 {
1139 /* Sanity check */
1140 ASSERT(ValueData != NULL);
1141
1142 /* User data, protect against exceptions */
1143 _SEH2_TRY
1144 {
1145 /* Query the information requested */
1146 Result = CmpQueryKeyValueData(Kcb,
1147 CachedValue,
1148 ValueData,
1149 ValueCached,
1150 KeyValueInformationClass,
1151 KeyValueInformation,
1152 Length,
1153 ResultLength,
1154 &Status);
1155 if (Result == SearchNeedExclusiveLock)
1156 {
1157 /* Release the value cell */
1158 if (CellToRelease != HCELL_NIL)
1159 {
1160 HvReleaseCell(Hive, CellToRelease);
1161 CellToRelease = HCELL_NIL;
1162 }
1163
1164 /* Try with exclusive KCB lock */
1165 CmpConvertKcbSharedToExclusive(Kcb);
1166 _SEH2_YIELD(goto DoAgain);
1167 }
1168 }
1169 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1170 {
1171 Status = _SEH2_GetExceptionCode();
1172 }
1173 _SEH2_END;
1174 }
1175 else
1176 {
1177 /* Failed to find the value */
1178 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1179 }
1180
1181 /* If we have a cell to release, do so */
1182 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1183
1184 /* Release locks */
1185 CmpReleaseKcbLock(Kcb);
1186 CmpUnlockRegistry();
1187 return Status;
1188 }
1189
1190 NTSTATUS
1191 NTAPI
1192 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1193 IN ULONG Index,
1194 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1195 IN PVOID KeyValueInformation,
1196 IN ULONG Length,
1197 IN PULONG ResultLength)
1198 {
1199 NTSTATUS Status;
1200 PHHIVE Hive;
1201 PCM_KEY_NODE Parent;
1202 HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
1203 VALUE_SEARCH_RETURN_TYPE Result;
1204 BOOLEAN IndexIsCached, ValueIsCached = FALSE;
1205 PCELL_DATA CellData;
1206 PCM_CACHED_VALUE *CachedValue;
1207 PCM_KEY_VALUE ValueData = NULL;
1208 PAGED_CODE();
1209
1210 /* Acquire hive lock */
1211 CmpLockRegistry();
1212
1213 /* Lock the KCB shared */
1214 CmpAcquireKcbLockShared(Kcb);
1215
1216 /* Don't touch deleted keys */
1217 DoAgain:
1218 if (Kcb->Delete)
1219 {
1220 /* Undo everything */
1221 CmpReleaseKcbLock(Kcb);
1222 CmpUnlockRegistry();
1223 return STATUS_KEY_DELETED;
1224 }
1225
1226 /* Get the hive and parent */
1227 Hive = Kcb->KeyHive;
1228 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1229 ASSERT(Parent);
1230
1231 /* FIXME: Lack of cache? */
1232 if (Kcb->ValueCache.Count != Parent->ValueList.Count)
1233 {
1234 DPRINT1("HACK: Overriding value cache count\n");
1235 Kcb->ValueCache.Count = Parent->ValueList.Count;
1236 }
1237
1238 /* Make sure the index is valid */
1239 if (Index >= Kcb->ValueCache.Count)
1240 {
1241 /* Release the cell and fail */
1242 HvReleaseCell(Hive, Kcb->KeyCell);
1243 Status = STATUS_NO_MORE_ENTRIES;
1244 goto Quickie;
1245 }
1246
1247 /* We don't deal with this yet */
1248 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1249 {
1250 /* Shouldn't happen */
1251 ASSERT(FALSE);
1252 }
1253
1254 /* Find the value list */
1255 Result = CmpGetValueListFromCache(Kcb,
1256 &CellData,
1257 &IndexIsCached,
1258 &CellToRelease);
1259 if (Result == SearchNeedExclusiveLock)
1260 {
1261 /* Check if we need an exclusive lock */
1262 ASSERT(CellToRelease == HCELL_NIL);
1263 HvReleaseCell(Hive, Kcb->KeyCell);
1264
1265 /* Try with exclusive KCB lock */
1266 CmpConvertKcbSharedToExclusive(Kcb);
1267 goto DoAgain;
1268 }
1269 else if (Result != SearchSuccess)
1270 {
1271 /* Sanity check */
1272 ASSERT(CellData == NULL);
1273
1274 /* Release the cell and fail */
1275 Status = STATUS_INSUFFICIENT_RESOURCES;
1276 goto Quickie;
1277 }
1278
1279 /* Now get the key value */
1280 Result = CmpGetValueKeyFromCache(Kcb,
1281 CellData,
1282 Index,
1283 &CachedValue,
1284 &ValueData,
1285 IndexIsCached,
1286 &ValueIsCached,
1287 &CellToRelease2);
1288 if (Result == SearchNeedExclusiveLock)
1289 {
1290 /* Cleanup state */
1291 ASSERT(CellToRelease2 == HCELL_NIL);
1292 if (CellToRelease)
1293 {
1294 HvReleaseCell(Hive, CellToRelease);
1295 CellToRelease = HCELL_NIL;
1296 }
1297 HvReleaseCell(Hive, Kcb->KeyCell);
1298
1299 /* Try with exclusive KCB lock */
1300 CmpConvertKcbSharedToExclusive(Kcb);
1301 goto DoAgain;
1302 }
1303 else if (Result != SearchSuccess)
1304 {
1305 /* Sanity check */
1306 ASSERT(ValueData == NULL);
1307
1308 /* Release the cells and fail */
1309 Status = STATUS_INSUFFICIENT_RESOURCES;
1310 goto Quickie;
1311 }
1312
1313 /* User data, need SEH */
1314 _SEH2_TRY
1315 {
1316 /* Query the information requested */
1317 Result = CmpQueryKeyValueData(Kcb,
1318 CachedValue,
1319 ValueData,
1320 ValueIsCached,
1321 KeyValueInformationClass,
1322 KeyValueInformation,
1323 Length,
1324 ResultLength,
1325 &Status);
1326 if (Result == SearchNeedExclusiveLock)
1327 {
1328 /* Cleanup state */
1329 if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
1330 HvReleaseCell(Hive, Kcb->KeyCell);
1331 if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
1332
1333 /* Try with exclusive KCB lock */
1334 CmpConvertKcbSharedToExclusive(Kcb);
1335 _SEH2_YIELD(goto DoAgain);
1336 }
1337 }
1338 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1339 {
1340 /* Get exception code */
1341 Status = _SEH2_GetExceptionCode();
1342 }
1343 _SEH2_END;
1344
1345 Quickie:
1346 /* If we have a cell to release, do so */
1347 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1348
1349 /* Release the parent cell */
1350 HvReleaseCell(Hive, Kcb->KeyCell);
1351
1352 /* If we have a cell to release, do so */
1353 if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
1354
1355 /* Release locks */
1356 CmpReleaseKcbLock(Kcb);
1357 CmpUnlockRegistry();
1358 return Status;
1359 }
1360
1361 static
1362 NTSTATUS
1363 CmpQueryKeyDataFromCache(
1364 _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1365 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo,
1366 _In_ ULONG Length,
1367 _Out_ PULONG ResultLength)
1368 {
1369 PCM_KEY_NODE Node;
1370 PHHIVE KeyHive;
1371 HCELL_INDEX KeyCell;
1372 USHORT NameLength;
1373 PAGED_CODE();
1374
1375 /* Get the hive and cell index */
1376 KeyHive = Kcb->KeyHash.KeyHive;
1377 KeyCell = Kcb->KeyHash.KeyCell;
1378
1379 #if DBG
1380 /* Get the cell node */
1381 Node = HvGetCell(KeyHive, KeyCell);
1382 if (Node != NULL)
1383 {
1384 ULONG SubKeyCount;
1385 ASSERT(Node->ValueList.Count == Kcb->ValueCache.Count);
1386
1387 if (!(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO))
1388 {
1389 SubKeyCount = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1390 if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1391 {
1392 ASSERT(SubKeyCount == 0);
1393 }
1394 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1395 {
1396 ASSERT(SubKeyCount == 1);
1397 }
1398 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1399 {
1400 ASSERT(SubKeyCount == Kcb->IndexHint->Count);
1401 }
1402 else
1403 {
1404 ASSERT(SubKeyCount == Kcb->SubKeyCount);
1405 }
1406 }
1407
1408 ASSERT(Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart);
1409 ASSERT(Node->MaxNameLen == Kcb->KcbMaxNameLen);
1410 ASSERT(Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
1411 ASSERT(Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
1412
1413 /* Release the cell */
1414 HvReleaseCell(KeyHive, KeyCell);
1415 }
1416 #endif // DBG
1417
1418 /* Make sure we have a name block */
1419 if (Kcb->NameBlock == NULL)
1420 {
1421 return STATUS_INSUFFICIENT_RESOURCES;
1422 }
1423
1424 /* Check for compressed name */
1425 if (Kcb->NameBlock->Compressed)
1426 {
1427 /* Calculate the name size */
1428 NameLength = CmpCompressedNameSize(Kcb->NameBlock->NameHash.Name,
1429 Kcb->NameBlock->NameHash.NameLength);
1430 }
1431 else
1432 {
1433 /* Use the stored name size */
1434 NameLength = Kcb->NameBlock->NameHash.NameLength;
1435 }
1436
1437 /* Validate buffer length (we do not copy the name!) */
1438 *ResultLength = sizeof(*KeyCachedInfo);
1439 if (Length < *ResultLength)
1440 {
1441 return STATUS_BUFFER_TOO_SMALL;
1442 }
1443
1444 /* Fill the structure */
1445 KeyCachedInfo->LastWriteTime = Kcb->KcbLastWriteTime;
1446 KeyCachedInfo->TitleIndex = 0;
1447 KeyCachedInfo->NameLength = NameLength;
1448 KeyCachedInfo->Values = Kcb->ValueCache.Count;
1449 KeyCachedInfo->MaxNameLen = Kcb->KcbMaxNameLen;
1450 KeyCachedInfo->MaxValueNameLen = Kcb->KcbMaxValueNameLen;
1451 KeyCachedInfo->MaxValueDataLen = Kcb->KcbMaxValueDataLen;
1452
1453 /* Check the ExtFlags for what we have */
1454 if (Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO)
1455 {
1456 /* Cache is not valid, do a full lookup */
1457 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb);
1458
1459 /* Get the cell node */
1460 Node = HvGetCell(KeyHive, KeyCell);
1461 if (Node == NULL)
1462 {
1463 return STATUS_INSUFFICIENT_RESOURCES;
1464 }
1465
1466 /* Calculate number of subkeys */
1467 KeyCachedInfo->SubKeys = Node->SubKeyCounts[0] + Node->SubKeyCounts[1];
1468
1469 /* Release the cell */
1470 HvReleaseCell(KeyHive, KeyCell);
1471 }
1472 else if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY)
1473 {
1474 /* There are no subkeys */
1475 KeyCachedInfo->SubKeys = 0;
1476 }
1477 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE)
1478 {
1479 /* There is exactly one subley */
1480 KeyCachedInfo->SubKeys = 1;
1481 }
1482 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT)
1483 {
1484 /* Get the number of subkeys from the subkey hint */
1485 KeyCachedInfo->SubKeys = Kcb->IndexHint->Count;
1486 }
1487 else
1488 {
1489 /* No subkey hint, use the key count field */
1490 KeyCachedInfo->SubKeys = Kcb->SubKeyCount;
1491 }
1492
1493 return STATUS_SUCCESS;
1494 }
1495
1496 static
1497 NTSTATUS
1498 CmpQueryFlagsInformation(
1499 _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1500 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo,
1501 _In_ ULONG Length,
1502 _In_ PULONG ResultLength)
1503 {
1504 /* Validate the buffer size */
1505 *ResultLength = sizeof(*KeyFlagsInfo);
1506 if (Length < *ResultLength)
1507 {
1508 return STATUS_BUFFER_TOO_SMALL;
1509 }
1510
1511 /* Copy the user flags */
1512 KeyFlagsInfo->UserFlags = Kcb->KcbUserFlags;
1513
1514 return STATUS_SUCCESS;
1515 }
1516
1517 static
1518 NTSTATUS
1519 CmpQueryNameInformation(
1520 _In_ PCM_KEY_CONTROL_BLOCK Kcb,
1521 _Out_opt_ PKEY_NAME_INFORMATION KeyNameInfo,
1522 _In_ ULONG Length,
1523 _Out_ PULONG ResultLength)
1524 {
1525 ULONG NeededLength;
1526 PCM_KEY_CONTROL_BLOCK CurrentKcb;
1527
1528 NeededLength = 0;
1529 CurrentKcb = Kcb;
1530
1531 /* Count the needed buffer size */
1532 while (CurrentKcb)
1533 {
1534 if (CurrentKcb->NameBlock->Compressed)
1535 NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1536 else
1537 NeededLength += CurrentKcb->NameBlock->NameLength;
1538
1539 NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR);
1540
1541 CurrentKcb = CurrentKcb->ParentKcb;
1542 }
1543
1544 _SEH2_TRY
1545 {
1546 *ResultLength = NeededLength + FIELD_OFFSET(KEY_NAME_INFORMATION, Name[0]);
1547 if (Length < *ResultLength)
1548 return STATUS_BUFFER_TOO_SMALL;
1549 }
1550 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1551 {
1552 return _SEH2_GetExceptionCode();
1553 }
1554 _SEH2_END;
1555
1556 /* Do the real copy */
1557 KeyNameInfo->NameLength = 0;
1558 CurrentKcb = Kcb;
1559
1560 _SEH2_TRY
1561 {
1562 while (CurrentKcb)
1563 {
1564 ULONG NameLength;
1565
1566 if (CurrentKcb->NameBlock->Compressed)
1567 {
1568 NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1569 /* Copy the compressed name */
1570 CmpCopyCompressedName(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1571 NameLength,
1572 CurrentKcb->NameBlock->Name,
1573 CurrentKcb->NameBlock->NameLength);
1574 }
1575 else
1576 {
1577 NameLength = CurrentKcb->NameBlock->NameLength;
1578 /* Otherwise, copy the raw name */
1579 RtlCopyMemory(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1580 CurrentKcb->NameBlock->Name,
1581 NameLength);
1582 }
1583
1584 NeededLength -= NameLength;
1585 NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
1586 /* Add path separator */
1587 KeyNameInfo->Name[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
1588 KeyNameInfo->NameLength += NameLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
1589
1590 CurrentKcb = CurrentKcb->ParentKcb;
1591 }
1592 }
1593 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1594 {
1595 return _SEH2_GetExceptionCode();
1596 }
1597 _SEH2_END;
1598
1599 /* Make sure we copied everything */
1600 ASSERT(NeededLength == 0);
1601 ASSERT(KeyNameInfo->Name[0] == OBJ_NAME_PATH_SEPARATOR);
1602
1603 /* We're done */
1604 return STATUS_SUCCESS;
1605 }
1606
1607
1608 NTSTATUS
1609 NTAPI
1610 CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb,
1611 _In_ KEY_INFORMATION_CLASS KeyInformationClass,
1612 _Out_opt_ PVOID KeyInformation,
1613 _In_ ULONG Length,
1614 _Out_ PULONG ResultLength)
1615 {
1616 NTSTATUS Status;
1617 PHHIVE Hive;
1618 PCM_KEY_NODE Parent;
1619 HV_TRACK_CELL_REF CellReferences = {0};
1620
1621 /* Acquire hive lock */
1622 CmpLockRegistry();
1623
1624 /* Lock KCB shared */
1625 CmpAcquireKcbLockShared(Kcb);
1626
1627 /* Don't touch deleted keys */
1628 if (Kcb->Delete)
1629 {
1630 /* Fail */
1631 Status = STATUS_KEY_DELETED;
1632 goto Quickie;
1633 }
1634
1635 /* Check what class we got */
1636 switch (KeyInformationClass)
1637 {
1638 /* Typical information */
1639 case KeyFullInformation:
1640 case KeyBasicInformation:
1641 case KeyNodeInformation:
1642
1643 /* Get the hive and parent */
1644 Hive = Kcb->KeyHive;
1645 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1646 ASSERT(Parent);
1647
1648 /* Track cell references */
1649 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
1650 {
1651 /* Not enough memory to track references */
1652 Status = STATUS_INSUFFICIENT_RESOURCES;
1653 }
1654 else
1655 {
1656 /* Call the internal API */
1657 Status = CmpQueryKeyData(Hive,
1658 Parent,
1659 KeyInformationClass,
1660 KeyInformation,
1661 Length,
1662 ResultLength);
1663 }
1664 break;
1665
1666 case KeyCachedInformation:
1667 /* Call the internal API */
1668 Status = CmpQueryKeyDataFromCache(Kcb,
1669 KeyInformation,
1670 Length,
1671 ResultLength);
1672 break;
1673
1674 case KeyFlagsInformation:
1675 /* Call the internal API */
1676 Status = CmpQueryFlagsInformation(Kcb,
1677 KeyInformation,
1678 Length,
1679 ResultLength);
1680 break;
1681
1682 case KeyNameInformation:
1683 /* Call the internal API */
1684 Status = CmpQueryNameInformation(Kcb,
1685 KeyInformation,
1686 Length,
1687 ResultLength);
1688 break;
1689
1690 /* Illegal classes */
1691 default:
1692
1693 /* Print message and fail */
1694 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1695 Status = STATUS_INVALID_INFO_CLASS;
1696 break;
1697 }
1698
1699 Quickie:
1700 /* Release references */
1701 HvReleaseFreeCellRefArray(&CellReferences);
1702
1703 /* Release locks */
1704 CmpReleaseKcbLock(Kcb);
1705 CmpUnlockRegistry();
1706 return Status;
1707 }
1708
1709 NTSTATUS
1710 NTAPI
1711 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1712 IN ULONG Index,
1713 IN KEY_INFORMATION_CLASS KeyInformationClass,
1714 IN PVOID KeyInformation,
1715 IN ULONG Length,
1716 IN PULONG ResultLength)
1717 {
1718 NTSTATUS Status;
1719 PHHIVE Hive;
1720 PCM_KEY_NODE Parent, Child;
1721 HCELL_INDEX ChildCell;
1722 HV_TRACK_CELL_REF CellReferences = {0};
1723
1724 /* Acquire hive lock */
1725 CmpLockRegistry();
1726
1727 /* Lock the KCB shared */
1728 CmpAcquireKcbLockShared(Kcb);
1729
1730 /* Don't touch deleted keys */
1731 if (Kcb->Delete)
1732 {
1733 /* Undo everything */
1734 Status = STATUS_KEY_DELETED;
1735 goto Quickie;
1736 }
1737
1738 /* Get the hive and parent */
1739 Hive = Kcb->KeyHive;
1740 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1741 ASSERT(Parent);
1742
1743 /* Get the child cell */
1744 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1745
1746 /* Release the parent cell */
1747 HvReleaseCell(Hive, Kcb->KeyCell);
1748
1749 /* Check if we found the child */
1750 if (ChildCell == HCELL_NIL)
1751 {
1752 /* We didn't, fail */
1753 Status = STATUS_NO_MORE_ENTRIES;
1754 goto Quickie;
1755 }
1756
1757 /* Now get the actual child node */
1758 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1759 ASSERT(Child);
1760
1761 /* Track references */
1762 if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
1763 {
1764 /* Can't allocate memory for tracking */
1765 Status = STATUS_INSUFFICIENT_RESOURCES;
1766 goto Quickie;
1767 }
1768
1769 /* Data can be user-mode, use SEH */
1770 _SEH2_TRY
1771 {
1772 /* Query the data requested */
1773 Status = CmpQueryKeyData(Hive,
1774 Child,
1775 KeyInformationClass,
1776 KeyInformation,
1777 Length,
1778 ResultLength);
1779 }
1780 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1781 {
1782 /* Fail with exception code */
1783 Status = _SEH2_GetExceptionCode();
1784 _SEH2_YIELD(goto Quickie);
1785 }
1786 _SEH2_END;
1787
1788 Quickie:
1789 /* Release references */
1790 HvReleaseFreeCellRefArray(&CellReferences);
1791
1792 /* Release locks */
1793 CmpReleaseKcbLock(Kcb);
1794 CmpUnlockRegistry();
1795 return Status;
1796 }
1797
1798 NTSTATUS
1799 NTAPI
1800 CmDeleteKey(IN PCM_KEY_BODY KeyBody)
1801 {
1802 NTSTATUS Status;
1803 PHHIVE Hive;
1804 PCM_KEY_NODE Node, Parent;
1805 HCELL_INDEX Cell, ParentCell;
1806 PCM_KEY_CONTROL_BLOCK Kcb;
1807
1808 /* Acquire hive lock */
1809 CmpLockRegistry();
1810
1811 /* Get the kcb */
1812 Kcb = KeyBody->KeyControlBlock;
1813
1814 /* Don't allow deleting the root */
1815 if (!Kcb->ParentKcb)
1816 {
1817 /* Fail */
1818 CmpUnlockRegistry();
1819 return STATUS_CANNOT_DELETE;
1820 }
1821
1822 /* Lock parent and child */
1823 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1824
1825 /* Check if we're already being deleted */
1826 if (Kcb->Delete)
1827 {
1828 /* Don't do it twice */
1829 Status = STATUS_SUCCESS;
1830 goto Quickie2;
1831 }
1832
1833 /* Get the hive and node */
1834 Hive = Kcb->KeyHive;
1835 Cell = Kcb->KeyCell;
1836
1837 /* Lock flushes */
1838 CmpLockHiveFlusherShared((PCMHIVE)Hive);
1839
1840 /* Get the key node */
1841 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1842 ASSERT(Node);
1843
1844 /* Sanity check */
1845 ASSERT(Node->Flags == Kcb->Flags);
1846
1847 /* Check if we don't have any children */
1848 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
1849 !(Node->Flags & KEY_NO_DELETE))
1850 {
1851 /* Send notification to registered callbacks */
1852 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
1853
1854 /* Get the parent and free the cell */
1855 ParentCell = Node->Parent;
1856 Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1857 if (NT_SUCCESS(Status))
1858 {
1859 /* Flush any notifications */
1860 CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
1861
1862 /* Clean up information we have on the subkey */
1863 CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
1864
1865 /* Get the parent node */
1866 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1867 if (Parent)
1868 {
1869 /* Update the maximum name length */
1870 Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
1871
1872 /* Make sure we're dirty */
1873 ASSERT(HvIsCellDirty(Hive, ParentCell));
1874
1875 /* Update the write time */
1876 KeQuerySystemTime(&Parent->LastWriteTime);
1877 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
1878
1879 /* Release the cell */
1880 HvReleaseCell(Hive, ParentCell);
1881 }
1882
1883 /* Set the KCB in delete mode and remove it */
1884 Kcb->Delete = TRUE;
1885 CmpRemoveKeyControlBlock(Kcb);
1886
1887 /* Clear the cell */
1888 Kcb->KeyCell = HCELL_NIL;
1889 }
1890 }
1891 else
1892 {
1893 /* Fail */
1894 Status = STATUS_CANNOT_DELETE;
1895 }
1896
1897 /* Release the cell */
1898 HvReleaseCell(Hive, Cell);
1899
1900 /* Release flush lock */
1901 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1902
1903 /* Release the KCB locks */
1904 Quickie2:
1905 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1906
1907 /* Release hive lock */
1908 CmpUnlockRegistry();
1909 return Status;
1910 }
1911
1912 NTSTATUS
1913 NTAPI
1914 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1915 IN BOOLEAN ExclusiveLock)
1916 {
1917 PCMHIVE CmHive;
1918 NTSTATUS Status = STATUS_SUCCESS;
1919 PHHIVE Hive;
1920
1921 /* Ignore flushes until we're ready */
1922 if (CmpNoWrite) return STATUS_SUCCESS;
1923
1924 /* Get the hives */
1925 Hive = Kcb->KeyHive;
1926 CmHive = (PCMHIVE)Hive;
1927
1928 /* Check if this is the master hive */
1929 if (CmHive == CmiVolatileHive)
1930 {
1931 /* Flush all the hives instead */
1932 CmpDoFlushAll(FALSE);
1933 }
1934 else
1935 {
1936 /* Don't touch the hive */
1937 CmpLockHiveFlusherExclusive(CmHive);
1938 ASSERT(CmHive->ViewLock);
1939 KeAcquireGuardedMutex(CmHive->ViewLock);
1940 CmHive->ViewLockOwner = KeGetCurrentThread();
1941
1942 /* Will the hive shrink? */
1943 if (HvHiveWillShrink(Hive))
1944 {
1945 /* I don't believe the current Hv does shrinking */
1946 ASSERT(FALSE);
1947 }
1948 else
1949 {
1950 /* Now we can release views */
1951 ASSERT(CmHive->ViewLock);
1952 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1953 ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
1954 KeReleaseGuardedMutex(CmHive->ViewLock);
1955 }
1956
1957 /* Flush only this hive */
1958 if (!HvSyncHive(Hive))
1959 {
1960 /* Fail */
1961 Status = STATUS_REGISTRY_IO_FAILED;
1962 }
1963
1964 /* Release the flush lock */
1965 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1966 }
1967
1968 /* Return the status */
1969 return Status;
1970 }
1971
1972 NTSTATUS
1973 NTAPI
1974 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1975 IN POBJECT_ATTRIBUTES SourceFile,
1976 IN ULONG Flags,
1977 IN PCM_KEY_BODY KeyBody)
1978 {
1979 SECURITY_QUALITY_OF_SERVICE ServiceQos;
1980 SECURITY_CLIENT_CONTEXT ClientSecurityContext;
1981 HANDLE KeyHandle;
1982 BOOLEAN Allocate = TRUE;
1983 PCMHIVE CmHive, LoadedHive;
1984 NTSTATUS Status;
1985 CM_PARSE_CONTEXT ParseContext;
1986
1987 /* Check if we have a trust key */
1988 if (KeyBody)
1989 {
1990 /* Fail */
1991 DPRINT("Trusted classes not yet supported\n");
1992 }
1993
1994 /* Build a service QoS for a security context */
1995 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1996 ServiceQos.ImpersonationLevel = SecurityImpersonation;
1997 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1998 ServiceQos.EffectiveOnly = TRUE;
1999 Status = SeCreateClientSecurity(PsGetCurrentThread(),
2000 &ServiceQos,
2001 FALSE,
2002 &ClientSecurityContext);
2003 if (!NT_SUCCESS(Status))
2004 {
2005 /* Fail */
2006 DPRINT1("Security context failed\n");
2007 return Status;
2008 }
2009
2010 /* Open the target key */
2011 #if 0
2012 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
2013 #else
2014 RtlZeroMemory(&ParseContext, sizeof(ParseContext));
2015 ParseContext.CreateOperation = FALSE;
2016 Status = ObOpenObjectByName(TargetKey,
2017 CmpKeyObjectType,
2018 KernelMode,
2019 NULL,
2020 KEY_READ,
2021 &ParseContext,
2022 &KeyHandle);
2023 #endif
2024 if (!NT_SUCCESS(Status)) KeyHandle = NULL;
2025
2026 /* Open the hive */
2027 Status = CmpCmdHiveOpen(SourceFile,
2028 &ClientSecurityContext,
2029 &Allocate,
2030 &CmHive,
2031 0);
2032
2033 /* Get rid of the security context */
2034 SeDeleteClientSecurity(&ClientSecurityContext);
2035
2036 /* See if we failed */
2037 if (!NT_SUCCESS(Status))
2038 {
2039 /* See if the target already existed */
2040 if (KeyHandle)
2041 {
2042 /* Lock the registry */
2043 CmpLockRegistryExclusive();
2044
2045 /* Check if we are already loaded */
2046 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
2047 {
2048 /* That's okay then */
2049 ASSERT(LoadedHive);
2050 Status = STATUS_SUCCESS;
2051 }
2052
2053 /* Release the registry */
2054 CmpUnlockRegistry();
2055 }
2056
2057 /* Close the key handle if we had one */
2058 if (KeyHandle) ZwClose(KeyHandle);
2059 return Status;
2060 }
2061
2062 /* Lock the registry shared */
2063 CmpLockRegistry();
2064
2065 /* Lock loading */
2066 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
2067
2068 /* Lock the hive to this thread */
2069 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
2070 CmHive->CreatorOwner = KeGetCurrentThread();
2071
2072 /* Set flag */
2073 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
2074
2075 /* Link the hive */
2076 Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
2077 TargetKey->RootDirectory,
2078 CmHive,
2079 Allocate,
2080 TargetKey->SecurityDescriptor);
2081 if (NT_SUCCESS(Status))
2082 {
2083 /* Add to HiveList key */
2084 CmpAddToHiveFileList(CmHive);
2085
2086 /* Sync the hive if necessary */
2087 if (Allocate)
2088 {
2089 /* Sync it under the flusher lock */
2090 CmpLockHiveFlusherExclusive(CmHive);
2091 HvSyncHive(&CmHive->Hive);
2092 CmpUnlockHiveFlusher(CmHive);
2093 }
2094
2095 /* Release the hive */
2096 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
2097 CmHive->CreatorOwner = NULL;
2098
2099 /* Allow loads */
2100 ExReleasePushLock(&CmpLoadHiveLock);
2101 }
2102 else
2103 {
2104 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status);
2105 /* FIXME: TODO */
2106 ASSERT(FALSE);
2107 }
2108
2109 /* Is this first profile load? */
2110 if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
2111 {
2112 /* User is now logged on, set quotas */
2113 CmpProfileLoaded = TRUE;
2114 CmpSetGlobalQuotaAllowed();
2115 }
2116
2117 /* Unlock the registry */
2118 CmpUnlockRegistry();
2119
2120 /* Close handle and return */
2121 if (KeyHandle) ZwClose(KeyHandle);
2122 return Status;
2123 }
2124
2125 NTSTATUS
2126 NTAPI
2127 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2128 IN ULONG Flags)
2129 {
2130 UNIMPLEMENTED;
2131 return STATUS_NOT_IMPLEMENTED;
2132 }
2133
2134 ULONG
2135 NTAPI
2136 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
2137 IN BOOLEAN RemoveEmptyCacheEntries)
2138 {
2139 PCM_KEY_HASH Entry;
2140 PCM_KEY_CONTROL_BLOCK CachedKcb;
2141 PCM_KEY_CONTROL_BLOCK ParentKcb;
2142 ULONG ParentKeyCount;
2143 ULONG i, j;
2144 ULONG SubKeys = 0;
2145
2146 DPRINT("CmCountOpenSubKeys() called\n");
2147
2148 /* The root key is the only referenced key. There are no refereced sub keys. */
2149 if (RootKcb->RefCount == 1)
2150 {
2151 DPRINT("open sub keys: 0\n");
2152 return 0;
2153 }
2154
2155 /* Enumerate all hash lists */
2156 for (i = 0; i < CmpHashTableSize; i++)
2157 {
2158 /* Get the first cache entry */
2159 Entry = CmpCacheTable[i].Entry;
2160
2161 /* Enumerate all cache entries */
2162 while (Entry)
2163 {
2164 /* Get the KCB of the current cache entry */
2165 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
2166
2167 /* Check keys only that are subkeys to our root key */
2168 if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
2169 {
2170 /* Calculate the number of parent keys to the root key */
2171 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
2172
2173 /* Find a parent key that could be the root key */
2174 ParentKcb = CachedKcb;
2175 for (j = 0; j < ParentKeyCount; j++)
2176 {
2177 ParentKcb = ParentKcb->ParentKcb;
2178 }
2179
2180 /* Check whether the parent is the root key */
2181 if (ParentKcb == RootKcb)
2182 {
2183 DPRINT("Found a sub key \n");
2184 DPRINT("RefCount = %u\n", CachedKcb->RefCount);
2185
2186 if (CachedKcb->RefCount > 0)
2187 {
2188 /* Count the current hash entry if it is in use */
2189 SubKeys++;
2190 }
2191 else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
2192 {
2193 /* Remove the current key from the delayed close list */
2194 CmpRemoveFromDelayedClose(CachedKcb);
2195
2196 /* Remove the current cache entry */
2197 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
2198
2199 /* Restart, because the hash list has changed */
2200 Entry = CmpCacheTable[i].Entry;
2201 continue;
2202 }
2203 }
2204 }
2205
2206 /* Get the next cache entry */
2207 Entry = Entry->NextHash;
2208 }
2209 }
2210
2211 DPRINT("open sub keys: %u\n", SubKeys);
2212
2213 return SubKeys;
2214 }
2215
2216 HCELL_INDEX
2217 NTAPI
2218 CmpCopyCell(IN PHHIVE SourceHive,
2219 IN HCELL_INDEX SourceCell,
2220 IN PHHIVE DestinationHive,
2221 IN HSTORAGE_TYPE StorageType)
2222 {
2223 PCELL_DATA SourceData;
2224 PCELL_DATA DestinationData = NULL;
2225 HCELL_INDEX DestinationCell = HCELL_NIL;
2226 LONG DataSize;
2227 PAGED_CODE();
2228
2229 /* Get the data and the size of the source cell */
2230 SourceData = HvGetCell(SourceHive, SourceCell);
2231 DataSize = HvGetCellSize(SourceHive, SourceData);
2232
2233 /* Allocate a new cell in the destination hive */
2234 DestinationCell = HvAllocateCell(DestinationHive,
2235 DataSize,
2236 StorageType,
2237 HCELL_NIL);
2238 if (DestinationCell == HCELL_NIL) goto Cleanup;
2239
2240 /* Get the data of the destination cell */
2241 DestinationData = HvGetCell(DestinationHive, DestinationCell);
2242
2243 /* Copy the data from the source cell to the destination cell */
2244 RtlMoveMemory(DestinationData, SourceData, DataSize);
2245
2246 Cleanup:
2247
2248 /* Release the cells */
2249 if (SourceData) HvReleaseCell(SourceHive, SourceCell);
2250 if (DestinationData) HvReleaseCell(DestinationHive, DestinationCell);
2251
2252 /* Return the destination cell index */
2253 return DestinationCell;
2254 }
2255
2256 static
2257 NTSTATUS
2258 NTAPI
2259 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive,
2260 IN HCELL_INDEX SrcKeyCell,
2261 IN PHHIVE DestinationHive,
2262 IN HCELL_INDEX Parent,
2263 IN HSTORAGE_TYPE StorageType,
2264 OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2265 {
2266 NTSTATUS Status;
2267 PCM_KEY_NODE SrcNode;
2268 PCM_KEY_NODE DestNode = NULL;
2269 HCELL_INDEX NewKeyCell, SubKey, NewSubKey;
2270 ULONG Index, SubKeyCount;
2271 PAGED_CODE();
2272
2273 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
2274 SourceHive,
2275 SrcKeyCell,
2276 DestinationHive,
2277 Parent,
2278 StorageType,
2279 DestKeyCell);
2280
2281 /* Get the source cell node */
2282 SrcNode = HvGetCell(SourceHive, SrcKeyCell);
2283
2284 /* Sanity check */
2285 ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE);
2286
2287 /* Create a simple copy of the source key */
2288 NewKeyCell = CmpCopyCell(SourceHive,
2289 SrcKeyCell,
2290 DestinationHive,
2291 StorageType);
2292 if (NewKeyCell == HCELL_NIL)
2293 {
2294 /* Not enough storage space */
2295 Status = STATUS_INSUFFICIENT_RESOURCES;
2296 goto Cleanup;
2297 }
2298
2299 /* Get the destination cell node */
2300 DestNode = HvGetCell(DestinationHive, NewKeyCell);
2301
2302 /* Set the parent */
2303 DestNode->Parent = Parent;
2304
2305 // TODO: These should also be copied!
2306 DestNode->Security = DestNode->Class = HCELL_NIL;
2307
2308 /* Copy the value list */
2309 Status = CmpCopyKeyValueList(SourceHive,
2310 &SrcNode->ValueList,
2311 DestinationHive,
2312 &DestNode->ValueList,
2313 StorageType);
2314 if (!NT_SUCCESS(Status)) goto Cleanup;
2315
2316 /* Clear the invalid subkey index */
2317 DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0;
2318 DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL;
2319
2320 /* Calculate the total number of subkeys */
2321 SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile];
2322
2323 /* Loop through all the subkeys */
2324 for (Index = 0; Index < SubKeyCount; Index++)
2325 {
2326 /* Get the subkey */
2327 SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index);
2328 ASSERT(SubKey != HCELL_NIL);
2329
2330 /* Call the function recursively for the subkey */
2331 Status = CmpDeepCopyKeyInternal(SourceHive,
2332 SubKey,
2333 DestinationHive,
2334 NewKeyCell,
2335 StorageType,
2336 &NewSubKey);
2337 if (!NT_SUCCESS(Status)) goto Cleanup;
2338
2339 /* Add the copy of the subkey to the new key */
2340 if (!CmpAddSubKey(DestinationHive,
2341 NewKeyCell,
2342 NewSubKey))
2343 {
2344 Status = STATUS_INSUFFICIENT_RESOURCES;
2345 goto Cleanup;
2346 }
2347 }
2348
2349 /* Set the cell index if requested and return success */
2350 if (DestKeyCell) *DestKeyCell = NewKeyCell;
2351 Status = STATUS_SUCCESS;
2352
2353 Cleanup:
2354
2355 /* Release the cells */
2356 if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell);
2357 if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell);
2358
2359 return Status;
2360 }
2361
2362 NTSTATUS
2363 NTAPI
2364 CmpDeepCopyKey(IN PHHIVE SourceHive,
2365 IN HCELL_INDEX SrcKeyCell,
2366 IN PHHIVE DestinationHive,
2367 IN HSTORAGE_TYPE StorageType,
2368 OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2369 {
2370 /* Call the internal function */
2371 return CmpDeepCopyKeyInternal(SourceHive,
2372 SrcKeyCell,
2373 DestinationHive,
2374 HCELL_NIL,
2375 StorageType,
2376 DestKeyCell);
2377 }
2378
2379 NTSTATUS
2380 NTAPI
2381 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2382 IN HANDLE FileHandle,
2383 IN ULONG Flags)
2384 {
2385 NTSTATUS Status = STATUS_SUCCESS;
2386 PCMHIVE KeyHive = NULL;
2387 PAGED_CODE();
2388
2389 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags);
2390
2391 /* Lock the registry and KCB */
2392 CmpLockRegistry();
2393 CmpAcquireKcbLockShared(Kcb);
2394
2395 if (Kcb->Delete)
2396 {
2397 /* The source key has been deleted, do nothing */
2398 Status = STATUS_KEY_DELETED;
2399 goto Cleanup;
2400 }
2401
2402 if (Kcb->KeyHive == &CmiVolatileHive->Hive)
2403 {
2404 /* Keys that are directly in the master hive can't be saved */
2405 Status = STATUS_ACCESS_DENIED;
2406 goto Cleanup;
2407 }
2408
2409 /* Create a new hive that will hold the key */
2410 Status = CmpInitializeHive(&KeyHive,
2411 HINIT_CREATE,
2412 HIVE_VOLATILE,
2413 HFILE_TYPE_PRIMARY,
2414 NULL,
2415 NULL,
2416 NULL,
2417 NULL,
2418 NULL,
2419 0);
2420 if (!NT_SUCCESS(Status)) goto Cleanup;
2421
2422 /* Copy the key recursively into the new hive */
2423 Status = CmpDeepCopyKey(Kcb->KeyHive,
2424 Kcb->KeyCell,
2425 &KeyHive->Hive,
2426 Stable,
2427 &KeyHive->Hive.BaseBlock->RootCell);
2428 if (!NT_SUCCESS(Status)) goto Cleanup;
2429
2430 /* Set the primary handle of the hive */
2431 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2432
2433 /* Dump the hive into the file */
2434 HvWriteHive(&KeyHive->Hive);
2435
2436 Cleanup:
2437
2438 /* Free the hive */
2439 if (KeyHive) CmpDestroyHive(KeyHive);
2440
2441 /* Release the locks */
2442 CmpReleaseKcbLock(Kcb);
2443 CmpUnlockRegistry();
2444
2445 return Status;
2446 }