e3b18049ff5cd36d2955506fa180d9e744151565
[reactos.git] / reactos / 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 while (CurrentKcb)
1560 {
1561 ULONG NameLength;
1562
1563 _SEH2_TRY
1564 {
1565 if (CurrentKcb->NameBlock->Compressed)
1566 {
1567 NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength);
1568 /* Copy the compressed name */
1569 CmpCopyCompressedName(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1570 NameLength,
1571 CurrentKcb->NameBlock->Name,
1572 CurrentKcb->NameBlock->NameLength);
1573 }
1574 else
1575 {
1576 NameLength = CurrentKcb->NameBlock->NameLength;
1577 /* Otherwise, copy the raw name */
1578 RtlCopyMemory(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)],
1579 CurrentKcb->NameBlock->Name,
1580 NameLength);
1581 }
1582
1583 NeededLength -= NameLength;
1584 NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
1585 /* Add path separator */
1586 KeyNameInfo->Name[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
1587 KeyNameInfo->NameLength += NameLength + sizeof(OBJ_NAME_PATH_SEPARATOR);
1588 }
1589 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1590 {
1591 return _SEH2_GetExceptionCode();
1592 }
1593 _SEH2_END;
1594
1595 CurrentKcb = CurrentKcb->ParentKcb;
1596 }
1597
1598 /* Make sure we copied everything */
1599 ASSERT(NeededLength == 0);
1600 ASSERT(KeyNameInfo->Name[0] == OBJ_NAME_PATH_SEPARATOR);
1601
1602 /* We're done */
1603 return STATUS_SUCCESS;
1604 }
1605
1606
1607 NTSTATUS
1608 NTAPI
1609 CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb,
1610 _In_ KEY_INFORMATION_CLASS KeyInformationClass,
1611 _Out_opt_ PVOID KeyInformation,
1612 _In_ ULONG Length,
1613 _Out_ PULONG ResultLength)
1614 {
1615 NTSTATUS Status;
1616 PHHIVE Hive;
1617 PCM_KEY_NODE Parent;
1618 HV_TRACK_CELL_REF CellReferences = {0};
1619
1620 /* Acquire hive lock */
1621 CmpLockRegistry();
1622
1623 /* Lock KCB shared */
1624 CmpAcquireKcbLockShared(Kcb);
1625
1626 /* Don't touch deleted keys */
1627 if (Kcb->Delete)
1628 {
1629 /* Fail */
1630 Status = STATUS_KEY_DELETED;
1631 goto Quickie;
1632 }
1633
1634 /* Check what class we got */
1635 switch (KeyInformationClass)
1636 {
1637 /* Typical information */
1638 case KeyFullInformation:
1639 case KeyBasicInformation:
1640 case KeyNodeInformation:
1641
1642 /* Get the hive and parent */
1643 Hive = Kcb->KeyHive;
1644 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1645 ASSERT(Parent);
1646
1647 /* Track cell references */
1648 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
1649 {
1650 /* Not enough memory to track references */
1651 Status = STATUS_INSUFFICIENT_RESOURCES;
1652 }
1653 else
1654 {
1655 /* Call the internal API */
1656 Status = CmpQueryKeyData(Hive,
1657 Parent,
1658 KeyInformationClass,
1659 KeyInformation,
1660 Length,
1661 ResultLength);
1662 }
1663 break;
1664
1665 case KeyCachedInformation:
1666 /* Call the internal API */
1667 Status = CmpQueryKeyDataFromCache(Kcb,
1668 KeyInformation,
1669 Length,
1670 ResultLength);
1671 break;
1672
1673 case KeyFlagsInformation:
1674 /* Call the internal API */
1675 Status = CmpQueryFlagsInformation(Kcb,
1676 KeyInformation,
1677 Length,
1678 ResultLength);
1679 break;
1680
1681 case KeyNameInformation:
1682 /* Call the internal API */
1683 Status = CmpQueryNameInformation(Kcb,
1684 KeyInformation,
1685 Length,
1686 ResultLength);
1687 break;
1688
1689 /* Illegal classes */
1690 default:
1691
1692 /* Print message and fail */
1693 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1694 Status = STATUS_INVALID_INFO_CLASS;
1695 break;
1696 }
1697
1698 Quickie:
1699 /* Release references */
1700 HvReleaseFreeCellRefArray(&CellReferences);
1701
1702 /* Release locks */
1703 CmpReleaseKcbLock(Kcb);
1704 CmpUnlockRegistry();
1705 return Status;
1706 }
1707
1708 NTSTATUS
1709 NTAPI
1710 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1711 IN ULONG Index,
1712 IN KEY_INFORMATION_CLASS KeyInformationClass,
1713 IN PVOID KeyInformation,
1714 IN ULONG Length,
1715 IN PULONG ResultLength)
1716 {
1717 NTSTATUS Status;
1718 PHHIVE Hive;
1719 PCM_KEY_NODE Parent, Child;
1720 HCELL_INDEX ChildCell;
1721 HV_TRACK_CELL_REF CellReferences = {0};
1722
1723 /* Acquire hive lock */
1724 CmpLockRegistry();
1725
1726 /* Lock the KCB shared */
1727 CmpAcquireKcbLockShared(Kcb);
1728
1729 /* Don't touch deleted keys */
1730 if (Kcb->Delete)
1731 {
1732 /* Undo everything */
1733 Status = STATUS_KEY_DELETED;
1734 goto Quickie;
1735 }
1736
1737 /* Get the hive and parent */
1738 Hive = Kcb->KeyHive;
1739 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1740 ASSERT(Parent);
1741
1742 /* Get the child cell */
1743 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1744
1745 /* Release the parent cell */
1746 HvReleaseCell(Hive, Kcb->KeyCell);
1747
1748 /* Check if we found the child */
1749 if (ChildCell == HCELL_NIL)
1750 {
1751 /* We didn't, fail */
1752 Status = STATUS_NO_MORE_ENTRIES;
1753 goto Quickie;
1754 }
1755
1756 /* Now get the actual child node */
1757 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1758 ASSERT(Child);
1759
1760 /* Track references */
1761 if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
1762 {
1763 /* Can't allocate memory for tracking */
1764 Status = STATUS_INSUFFICIENT_RESOURCES;
1765 goto Quickie;
1766 }
1767
1768 /* Data can be user-mode, use SEH */
1769 _SEH2_TRY
1770 {
1771 /* Query the data requested */
1772 Status = CmpQueryKeyData(Hive,
1773 Child,
1774 KeyInformationClass,
1775 KeyInformation,
1776 Length,
1777 ResultLength);
1778 }
1779 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1780 {
1781 /* Fail with exception code */
1782 Status = _SEH2_GetExceptionCode();
1783 _SEH2_YIELD(goto Quickie);
1784 }
1785 _SEH2_END;
1786
1787 Quickie:
1788 /* Release references */
1789 HvReleaseFreeCellRefArray(&CellReferences);
1790
1791 /* Release locks */
1792 CmpReleaseKcbLock(Kcb);
1793 CmpUnlockRegistry();
1794 return Status;
1795 }
1796
1797 NTSTATUS
1798 NTAPI
1799 CmDeleteKey(IN PCM_KEY_BODY KeyBody)
1800 {
1801 NTSTATUS Status;
1802 PHHIVE Hive;
1803 PCM_KEY_NODE Node, Parent;
1804 HCELL_INDEX Cell, ParentCell;
1805 PCM_KEY_CONTROL_BLOCK Kcb;
1806
1807 /* Acquire hive lock */
1808 CmpLockRegistry();
1809
1810 /* Get the kcb */
1811 Kcb = KeyBody->KeyControlBlock;
1812
1813 /* Don't allow deleting the root */
1814 if (!Kcb->ParentKcb)
1815 {
1816 /* Fail */
1817 CmpUnlockRegistry();
1818 return STATUS_CANNOT_DELETE;
1819 }
1820
1821 /* Lock parent and child */
1822 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1823
1824 /* Check if we're already being deleted */
1825 if (Kcb->Delete)
1826 {
1827 /* Don't do it twice */
1828 Status = STATUS_SUCCESS;
1829 goto Quickie2;
1830 }
1831
1832 /* Get the hive and node */
1833 Hive = Kcb->KeyHive;
1834 Cell = Kcb->KeyCell;
1835
1836 /* Lock flushes */
1837 CmpLockHiveFlusherShared((PCMHIVE)Hive);
1838
1839 /* Get the key node */
1840 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1841 ASSERT(Node);
1842
1843 /* Sanity check */
1844 ASSERT(Node->Flags == Kcb->Flags);
1845
1846 /* Check if we don't have any children */
1847 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
1848 !(Node->Flags & KEY_NO_DELETE))
1849 {
1850 /* Send notification to registered callbacks */
1851 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
1852
1853 /* Get the parent and free the cell */
1854 ParentCell = Node->Parent;
1855 Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1856 if (NT_SUCCESS(Status))
1857 {
1858 /* Flush any notifications */
1859 CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
1860
1861 /* Clean up information we have on the subkey */
1862 CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
1863
1864 /* Get the parent node */
1865 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1866 if (Parent)
1867 {
1868 /* Update the maximum name length */
1869 Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen;
1870
1871 /* Make sure we're dirty */
1872 ASSERT(HvIsCellDirty(Hive, ParentCell));
1873
1874 /* Update the write time */
1875 KeQuerySystemTime(&Parent->LastWriteTime);
1876 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
1877
1878 /* Release the cell */
1879 HvReleaseCell(Hive, ParentCell);
1880 }
1881
1882 /* Set the KCB in delete mode and remove it */
1883 Kcb->Delete = TRUE;
1884 CmpRemoveKeyControlBlock(Kcb);
1885
1886 /* Clear the cell */
1887 Kcb->KeyCell = HCELL_NIL;
1888 }
1889 }
1890 else
1891 {
1892 /* Fail */
1893 Status = STATUS_CANNOT_DELETE;
1894 }
1895
1896 /* Release the cell */
1897 HvReleaseCell(Hive, Cell);
1898
1899 /* Release flush lock */
1900 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1901
1902 /* Release the KCB locks */
1903 Quickie2:
1904 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1905
1906 /* Release hive lock */
1907 CmpUnlockRegistry();
1908 return Status;
1909 }
1910
1911 NTSTATUS
1912 NTAPI
1913 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1914 IN BOOLEAN ExclusiveLock)
1915 {
1916 PCMHIVE CmHive;
1917 NTSTATUS Status = STATUS_SUCCESS;
1918 PHHIVE Hive;
1919
1920 /* Ignore flushes until we're ready */
1921 if (CmpNoWrite) return STATUS_SUCCESS;
1922
1923 /* Get the hives */
1924 Hive = Kcb->KeyHive;
1925 CmHive = (PCMHIVE)Hive;
1926
1927 /* Check if this is the master hive */
1928 if (CmHive == CmiVolatileHive)
1929 {
1930 /* Flush all the hives instead */
1931 CmpDoFlushAll(FALSE);
1932 }
1933 else
1934 {
1935 /* Don't touch the hive */
1936 CmpLockHiveFlusherExclusive(CmHive);
1937 ASSERT(CmHive->ViewLock);
1938 KeAcquireGuardedMutex(CmHive->ViewLock);
1939 CmHive->ViewLockOwner = KeGetCurrentThread();
1940
1941 /* Will the hive shrink? */
1942 if (HvHiveWillShrink(Hive))
1943 {
1944 /* I don't believe the current Hv does shrinking */
1945 ASSERT(FALSE);
1946 }
1947 else
1948 {
1949 /* Now we can release views */
1950 ASSERT(CmHive->ViewLock);
1951 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1952 ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
1953 KeReleaseGuardedMutex(CmHive->ViewLock);
1954 }
1955
1956 /* Flush only this hive */
1957 if (!HvSyncHive(Hive))
1958 {
1959 /* Fail */
1960 Status = STATUS_REGISTRY_IO_FAILED;
1961 }
1962
1963 /* Release the flush lock */
1964 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1965 }
1966
1967 /* Return the status */
1968 return Status;
1969 }
1970
1971 NTSTATUS
1972 NTAPI
1973 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1974 IN POBJECT_ATTRIBUTES SourceFile,
1975 IN ULONG Flags,
1976 IN PCM_KEY_BODY KeyBody)
1977 {
1978 SECURITY_QUALITY_OF_SERVICE ServiceQos;
1979 SECURITY_CLIENT_CONTEXT ClientSecurityContext;
1980 HANDLE KeyHandle;
1981 BOOLEAN Allocate = TRUE;
1982 PCMHIVE CmHive, LoadedHive;
1983 NTSTATUS Status;
1984 CM_PARSE_CONTEXT ParseContext;
1985
1986 /* Check if we have a trust key */
1987 if (KeyBody)
1988 {
1989 /* Fail */
1990 DPRINT("Trusted classes not yet supported\n");
1991 }
1992
1993 /* Build a service QoS for a security context */
1994 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1995 ServiceQos.ImpersonationLevel = SecurityImpersonation;
1996 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1997 ServiceQos.EffectiveOnly = TRUE;
1998 Status = SeCreateClientSecurity(PsGetCurrentThread(),
1999 &ServiceQos,
2000 FALSE,
2001 &ClientSecurityContext);
2002 if (!NT_SUCCESS(Status))
2003 {
2004 /* Fail */
2005 DPRINT1("Security context failed\n");
2006 return Status;
2007 }
2008
2009 /* Open the target key */
2010 #if 0
2011 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
2012 #else
2013 RtlZeroMemory(&ParseContext, sizeof(ParseContext));
2014 ParseContext.CreateOperation = FALSE;
2015 Status = ObOpenObjectByName(TargetKey,
2016 CmpKeyObjectType,
2017 KernelMode,
2018 NULL,
2019 KEY_READ,
2020 &ParseContext,
2021 &KeyHandle);
2022 #endif
2023 if (!NT_SUCCESS(Status)) KeyHandle = NULL;
2024
2025 /* Open the hive */
2026 Status = CmpCmdHiveOpen(SourceFile,
2027 &ClientSecurityContext,
2028 &Allocate,
2029 &CmHive,
2030 0);
2031
2032 /* Get rid of the security context */
2033 SeDeleteClientSecurity(&ClientSecurityContext);
2034
2035 /* See if we failed */
2036 if (!NT_SUCCESS(Status))
2037 {
2038 /* See if the target already existed */
2039 if (KeyHandle)
2040 {
2041 /* Lock the registry */
2042 CmpLockRegistryExclusive();
2043
2044 /* Check if we are already loaded */
2045 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
2046 {
2047 /* That's okay then */
2048 ASSERT(LoadedHive);
2049 Status = STATUS_SUCCESS;
2050 }
2051
2052 /* Release the registry */
2053 CmpUnlockRegistry();
2054 }
2055
2056 /* Close the key handle if we had one */
2057 if (KeyHandle) ZwClose(KeyHandle);
2058 return Status;
2059 }
2060
2061 /* Lock the registry shared */
2062 CmpLockRegistry();
2063
2064 /* Lock loading */
2065 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
2066
2067 /* Lock the hive to this thread */
2068 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
2069 CmHive->CreatorOwner = KeGetCurrentThread();
2070
2071 /* Set flag */
2072 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
2073
2074 /* Link the hive */
2075 Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
2076 TargetKey->RootDirectory,
2077 CmHive,
2078 Allocate,
2079 TargetKey->SecurityDescriptor);
2080 if (NT_SUCCESS(Status))
2081 {
2082 /* Add to HiveList key */
2083 CmpAddToHiveFileList(CmHive);
2084
2085 /* Sync the hive if necessary */
2086 if (Allocate)
2087 {
2088 /* Sync it under the flusher lock */
2089 CmpLockHiveFlusherExclusive(CmHive);
2090 HvSyncHive(&CmHive->Hive);
2091 CmpUnlockHiveFlusher(CmHive);
2092 }
2093
2094 /* Release the hive */
2095 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
2096 CmHive->CreatorOwner = NULL;
2097
2098 /* Allow loads */
2099 ExReleasePushLock(&CmpLoadHiveLock);
2100 }
2101 else
2102 {
2103 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status);
2104 /* FIXME: TODO */
2105 ASSERT(FALSE);
2106 }
2107
2108 /* Is this first profile load? */
2109 if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
2110 {
2111 /* User is now logged on, set quotas */
2112 CmpProfileLoaded = TRUE;
2113 CmpSetGlobalQuotaAllowed();
2114 }
2115
2116 /* Unlock the registry */
2117 CmpUnlockRegistry();
2118
2119 /* Close handle and return */
2120 if (KeyHandle) ZwClose(KeyHandle);
2121 return Status;
2122 }
2123
2124 NTSTATUS
2125 NTAPI
2126 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2127 IN ULONG Flags)
2128 {
2129 UNIMPLEMENTED;
2130 return STATUS_NOT_IMPLEMENTED;
2131 }
2132
2133 ULONG
2134 NTAPI
2135 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
2136 IN BOOLEAN RemoveEmptyCacheEntries)
2137 {
2138 PCM_KEY_HASH Entry;
2139 PCM_KEY_CONTROL_BLOCK CachedKcb;
2140 PCM_KEY_CONTROL_BLOCK ParentKcb;
2141 ULONG ParentKeyCount;
2142 ULONG i, j;
2143 ULONG SubKeys = 0;
2144
2145 DPRINT("CmCountOpenSubKeys() called\n");
2146
2147 /* The root key is the only referenced key. There are no refereced sub keys. */
2148 if (RootKcb->RefCount == 1)
2149 {
2150 DPRINT("open sub keys: 0\n");
2151 return 0;
2152 }
2153
2154 /* Enumerate all hash lists */
2155 for (i = 0; i < CmpHashTableSize; i++)
2156 {
2157 /* Get the first cache entry */
2158 Entry = CmpCacheTable[i].Entry;
2159
2160 /* Enumerate all cache entries */
2161 while (Entry)
2162 {
2163 /* Get the KCB of the current cache entry */
2164 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
2165
2166 /* Check keys only that are subkeys to our root key */
2167 if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
2168 {
2169 /* Calculate the number of parent keys to the root key */
2170 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
2171
2172 /* Find a parent key that could be the root key */
2173 ParentKcb = CachedKcb;
2174 for (j = 0; j < ParentKeyCount; j++)
2175 {
2176 ParentKcb = ParentKcb->ParentKcb;
2177 }
2178
2179 /* Check whether the parent is the root key */
2180 if (ParentKcb == RootKcb)
2181 {
2182 DPRINT("Found a sub key \n");
2183 DPRINT("RefCount = %u\n", CachedKcb->RefCount);
2184
2185 if (CachedKcb->RefCount > 0)
2186 {
2187 /* Count the current hash entry if it is in use */
2188 SubKeys++;
2189 }
2190 else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
2191 {
2192 /* Remove the current key from the delayed close list */
2193 CmpRemoveFromDelayedClose(CachedKcb);
2194
2195 /* Remove the current cache entry */
2196 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
2197
2198 /* Restart, because the hash list has changed */
2199 Entry = CmpCacheTable[i].Entry;
2200 continue;
2201 }
2202 }
2203 }
2204
2205 /* Get the next cache entry */
2206 Entry = Entry->NextHash;
2207 }
2208 }
2209
2210 DPRINT("open sub keys: %u\n", SubKeys);
2211
2212 return SubKeys;
2213 }
2214
2215 HCELL_INDEX
2216 NTAPI
2217 CmpCopyCell(IN PHHIVE SourceHive,
2218 IN HCELL_INDEX SourceCell,
2219 IN PHHIVE DestinationHive,
2220 IN HSTORAGE_TYPE StorageType)
2221 {
2222 PCELL_DATA SourceData;
2223 PCELL_DATA DestinationData = NULL;
2224 HCELL_INDEX DestinationCell = HCELL_NIL;
2225 LONG DataSize;
2226 PAGED_CODE();
2227
2228 /* Get the data and the size of the source cell */
2229 SourceData = HvGetCell(SourceHive, SourceCell);
2230 DataSize = HvGetCellSize(SourceHive, SourceData);
2231
2232 /* Allocate a new cell in the destination hive */
2233 DestinationCell = HvAllocateCell(DestinationHive,
2234 DataSize,
2235 StorageType,
2236 HCELL_NIL);
2237 if (DestinationCell == HCELL_NIL) goto Cleanup;
2238
2239 /* Get the data of the destination cell */
2240 DestinationData = HvGetCell(DestinationHive, DestinationCell);
2241
2242 /* Copy the data from the source cell to the destination cell */
2243 RtlMoveMemory(DestinationData, SourceData, DataSize);
2244
2245 Cleanup:
2246
2247 /* Release the cells */
2248 if (SourceData) HvReleaseCell(SourceHive, SourceCell);
2249 if (DestinationData) HvReleaseCell(DestinationHive, DestinationCell);
2250
2251 /* Return the destination cell index */
2252 return DestinationCell;
2253 }
2254
2255 static
2256 NTSTATUS
2257 NTAPI
2258 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive,
2259 IN HCELL_INDEX SrcKeyCell,
2260 IN PHHIVE DestinationHive,
2261 IN HCELL_INDEX Parent,
2262 IN HSTORAGE_TYPE StorageType,
2263 OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2264 {
2265 NTSTATUS Status;
2266 PCM_KEY_NODE SrcNode;
2267 PCM_KEY_NODE DestNode = NULL;
2268 HCELL_INDEX NewKeyCell, SubKey, NewSubKey;
2269 ULONG Index, SubKeyCount;
2270 PAGED_CODE();
2271
2272 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n",
2273 SourceHive,
2274 SrcKeyCell,
2275 DestinationHive,
2276 Parent,
2277 StorageType,
2278 DestKeyCell);
2279
2280 /* Get the source cell node */
2281 SrcNode = HvGetCell(SourceHive, SrcKeyCell);
2282
2283 /* Sanity check */
2284 ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE);
2285
2286 /* Create a simple copy of the source key */
2287 NewKeyCell = CmpCopyCell(SourceHive,
2288 SrcKeyCell,
2289 DestinationHive,
2290 StorageType);
2291 if (NewKeyCell == HCELL_NIL)
2292 {
2293 /* Not enough storage space */
2294 Status = STATUS_INSUFFICIENT_RESOURCES;
2295 goto Cleanup;
2296 }
2297
2298 /* Get the destination cell node */
2299 DestNode = HvGetCell(DestinationHive, NewKeyCell);
2300
2301 /* Set the parent */
2302 DestNode->Parent = Parent;
2303
2304 // TODO: These should also be copied!
2305 DestNode->Security = DestNode->Class = HCELL_NIL;
2306
2307 /* Copy the value list */
2308 Status = CmpCopyKeyValueList(SourceHive,
2309 &SrcNode->ValueList,
2310 DestinationHive,
2311 &DestNode->ValueList,
2312 StorageType);
2313 if (!NT_SUCCESS(Status)) goto Cleanup;
2314
2315 /* Clear the invalid subkey index */
2316 DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0;
2317 DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL;
2318
2319 /* Calculate the total number of subkeys */
2320 SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile];
2321
2322 /* Loop through all the subkeys */
2323 for (Index = 0; Index < SubKeyCount; Index++)
2324 {
2325 /* Get the subkey */
2326 SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index);
2327 ASSERT(SubKey != HCELL_NIL);
2328
2329 /* Call the function recursively for the subkey */
2330 Status = CmpDeepCopyKeyInternal(SourceHive,
2331 SubKey,
2332 DestinationHive,
2333 NewKeyCell,
2334 StorageType,
2335 &NewSubKey);
2336 if (!NT_SUCCESS(Status)) goto Cleanup;
2337
2338 /* Add the copy of the subkey to the new key */
2339 if (!CmpAddSubKey(DestinationHive,
2340 NewKeyCell,
2341 NewSubKey))
2342 {
2343 Status = STATUS_INSUFFICIENT_RESOURCES;
2344 goto Cleanup;
2345 }
2346 }
2347
2348 /* Set the cell index if requested and return success */
2349 if (DestKeyCell) *DestKeyCell = NewKeyCell;
2350 Status = STATUS_SUCCESS;
2351
2352 Cleanup:
2353
2354 /* Release the cells */
2355 if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell);
2356 if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell);
2357
2358 return Status;
2359 }
2360
2361 NTSTATUS
2362 NTAPI
2363 CmpDeepCopyKey(IN PHHIVE SourceHive,
2364 IN HCELL_INDEX SrcKeyCell,
2365 IN PHHIVE DestinationHive,
2366 IN HSTORAGE_TYPE StorageType,
2367 OUT PHCELL_INDEX DestKeyCell OPTIONAL)
2368 {
2369 /* Call the internal function */
2370 return CmpDeepCopyKeyInternal(SourceHive,
2371 SrcKeyCell,
2372 DestinationHive,
2373 HCELL_NIL,
2374 StorageType,
2375 DestKeyCell);
2376 }
2377
2378 NTSTATUS
2379 NTAPI
2380 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
2381 IN HANDLE FileHandle,
2382 IN ULONG Flags)
2383 {
2384 NTSTATUS Status = STATUS_SUCCESS;
2385 PCMHIVE KeyHive = NULL;
2386 PAGED_CODE();
2387
2388 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags);
2389
2390 /* Lock the registry and KCB */
2391 CmpLockRegistry();
2392 CmpAcquireKcbLockShared(Kcb);
2393
2394 if (Kcb->Delete)
2395 {
2396 /* The source key has been deleted, do nothing */
2397 Status = STATUS_KEY_DELETED;
2398 goto Cleanup;
2399 }
2400
2401 if (Kcb->KeyHive == &CmiVolatileHive->Hive)
2402 {
2403 /* Keys that are directly in the master hive can't be saved */
2404 Status = STATUS_ACCESS_DENIED;
2405 goto Cleanup;
2406 }
2407
2408 /* Create a new hive that will hold the key */
2409 Status = CmpInitializeHive(&KeyHive,
2410 HINIT_CREATE,
2411 HIVE_VOLATILE,
2412 HFILE_TYPE_PRIMARY,
2413 NULL,
2414 NULL,
2415 NULL,
2416 NULL,
2417 NULL,
2418 0);
2419 if (!NT_SUCCESS(Status)) goto Cleanup;
2420
2421 /* Copy the key recursively into the new hive */
2422 Status = CmpDeepCopyKey(Kcb->KeyHive,
2423 Kcb->KeyCell,
2424 &KeyHive->Hive,
2425 Stable,
2426 &KeyHive->Hive.BaseBlock->RootCell);
2427 if (!NT_SUCCESS(Status)) goto Cleanup;
2428
2429 /* Set the primary handle of the hive */
2430 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle;
2431
2432 /* Dump the hive into the file */
2433 HvWriteHive(&KeyHive->Hive);
2434
2435 Cleanup:
2436
2437 /* Free the hive */
2438 if (KeyHive) CmpDestroyHive(KeyHive);
2439
2440 /* Release the locks */
2441 CmpReleaseKcbLock(Kcb);
2442 CmpUnlockRegistry();
2443
2444 return Status;
2445 }