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