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