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