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