[NTOSKRNL]
[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 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include "ntoskrnl.h"
13 #define NDEBUG
14 #include "debug.h"
15
16 /* FUNCTIONS *****************************************************************/
17
18 BOOLEAN
19 NTAPI
20 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle,
21 IN POBJECT_ATTRIBUTES SourceFile,
22 OUT PCMHIVE *CmHive)
23 {
24 NTSTATUS Status;
25 PCM_KEY_BODY KeyBody;
26 PCMHIVE Hive;
27 BOOLEAN Loaded = FALSE;
28 PAGED_CODE();
29
30 /* Sanity check */
31 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
32
33 /* Reference the handle */
34 Status = ObReferenceObjectByHandle(KeyHandle,
35 0,
36 CmpKeyObjectType,
37 KernelMode,
38 (PVOID)&KeyBody,
39 NULL);
40 if (!NT_SUCCESS(Status)) return Loaded;
41
42 /* Don't touch deleted KCBs */
43 if (KeyBody->KeyControlBlock->Delete) return Loaded;
44
45 Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
46
47 /* Must be the root key */
48 if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) ||
49 !(Hive->FileUserName.Buffer))
50 {
51 /* It isn't */
52 ObDereferenceObject(KeyBody);
53 return Loaded;
54 }
55
56 /* Now compare the name of the file */
57 if (!RtlCompareUnicodeString(&Hive->FileUserName,
58 SourceFile->ObjectName,
59 TRUE))
60 {
61 /* Same file found */
62 Loaded = TRUE;
63 *CmHive = Hive;
64
65 /* If the hive is frozen, not sure what to do */
66 if (Hive->Frozen)
67 {
68 /* FIXME: TODO */
69 DPRINT1("ERROR: Hive is frozen\n");
70 while (TRUE);
71 }
72 }
73
74 /* Dereference and return result */
75 ObDereferenceObject(KeyBody);
76 return Loaded;
77 }
78
79 BOOLEAN
80 NTAPI
81 CmpDoFlushAll(IN BOOLEAN ForceFlush)
82 {
83 PLIST_ENTRY NextEntry;
84 PCMHIVE Hive;
85 NTSTATUS Status;
86 BOOLEAN Result = TRUE;
87
88 /* Make sure that the registry isn't read-only now */
89 if (CmpNoWrite) return TRUE;
90
91 /* Otherwise, acquire the hive list lock and disable force flush */
92 CmpForceForceFlush = FALSE;
93 ExAcquirePushLockShared(&CmpHiveListHeadLock);
94
95 /* Loop the hive list */
96 NextEntry = CmpHiveListHead.Flink;
97 while (NextEntry != &CmpHiveListHead)
98 {
99 /* Get the hive */
100 Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
101 if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
102 {
103 /* Acquire the flusher lock */
104 CmpLockHiveFlusherExclusive(Hive);
105
106 /* Check for illegal state */
107 if ((ForceFlush) && (Hive->UseCount))
108 {
109 /* Registry needs to be locked down */
110 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK();
111 DPRINT1("FIXME: Hive is damaged and needs fixup\n");
112 while (TRUE);
113 }
114
115 /* Only sync if we are forced to or if it won't cause a hive shrink */
116 if ((ForceFlush) || (!HvHiveWillShrink(&Hive->Hive)))
117 {
118 /* Do the sync */
119 Status = HvSyncHive(&Hive->Hive);
120
121 /* If something failed - set the flag and continue looping */
122 if (!NT_SUCCESS(Status)) Result = FALSE;
123 }
124 else
125 {
126 /* We won't flush if the hive might shrink */
127 Result = FALSE;
128 CmpForceForceFlush = TRUE;
129 }
130
131 /* Release the flusher lock */
132 CmpUnlockHiveFlusher(Hive);
133 }
134
135 /* Try the next entry */
136 NextEntry = NextEntry->Flink;
137 }
138
139 /* Release lock and return */
140 ExReleasePushLock(&CmpHiveListHeadLock);
141 return Result;
142 }
143
144 NTSTATUS
145 NTAPI
146 CmpSetValueKeyNew(IN PHHIVE Hive,
147 IN PCM_KEY_NODE Parent,
148 IN PUNICODE_STRING ValueName,
149 IN ULONG Index,
150 IN ULONG Type,
151 IN PVOID Data,
152 IN ULONG DataSize,
153 IN ULONG StorageType,
154 IN ULONG SmallData)
155 {
156 PCELL_DATA CellData;
157 HCELL_INDEX ValueCell;
158 NTSTATUS Status;
159
160 /* Check if we already have a value list */
161 if (Parent->ValueList.Count)
162 {
163 /* Then make sure it's valid and dirty it */
164 ASSERT(Parent->ValueList.List != HCELL_NIL);
165 if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE))
166 {
167 /* Fail if we're out of space for log changes */
168 return STATUS_NO_LOG_SPACE;
169 }
170 }
171
172 /* Allocate a value cell */
173 ValueCell = HvAllocateCell(Hive,
174 FIELD_OFFSET(CM_KEY_VALUE, Name) +
175 CmpNameSize(Hive, ValueName),
176 StorageType,
177 HCELL_NIL);
178 if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
179
180 /* Get the actual data for it */
181 CellData = HvGetCell(Hive, ValueCell);
182 if (!CellData) ASSERT(FALSE);
183
184 /* Now we can release it, make sure it's also dirty */
185 HvReleaseCell(Hive, ValueCell);
186 ASSERT(HvIsCellDirty(Hive, ValueCell));
187
188 /* Set it up and copy the name */
189 CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
190 _SEH2_TRY
191 {
192 /* This can crash since the name is coming from user-mode */
193 CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
194 CellData->u.KeyValue.Name,
195 ValueName);
196 }
197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
198 {
199 /* Fail */
200 DPRINT1("Invalid user data!\n");
201 HvFreeCell(Hive, ValueCell);
202 _SEH2_YIELD(return _SEH2_GetExceptionCode());
203 }
204 _SEH2_END;
205
206 /* Check for compressed name */
207 if (CellData->u.KeyValue.NameLength < ValueName->Length)
208 {
209 /* This is a compressed name */
210 CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
211 }
212 else
213 {
214 /* No flags to set */
215 CellData->u.KeyValue.Flags = 0;
216 }
217
218 /* Check if this is a normal key */
219 if (DataSize > CM_KEY_VALUE_SMALL)
220 {
221 /* Build a data cell for it */
222 Status = CmpSetValueDataNew(Hive,
223 Data,
224 DataSize,
225 StorageType,
226 ValueCell,
227 &CellData->u.KeyValue.Data);
228 if (!NT_SUCCESS(Status))
229 {
230 /* We failed, free the cell */
231 HvFreeCell(Hive, ValueCell);
232 return Status;
233 }
234
235 /* Otherwise, set the data length, and make sure the data is dirty */
236 CellData->u.KeyValue.DataLength = DataSize;
237 ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
238 }
239 else
240 {
241 /* This is a small key, set the data directly inside */
242 CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
243 CellData->u.KeyValue.Data = SmallData;
244 }
245
246 /* Set the type now */
247 CellData->u.KeyValue.Type = Type;
248
249 /* Add this value cell to the child list */
250 Status = CmpAddValueToList(Hive,
251 ValueCell,
252 Index,
253 StorageType,
254 &Parent->ValueList);
255
256 /* If we failed, free the entire cell, including the data */
257 if (!NT_SUCCESS(Status))
258 {
259 /* Overwrite the status with a known one */
260 CmpFreeValue(Hive, ValueCell);
261 Status = STATUS_INSUFFICIENT_RESOURCES;
262 }
263
264 /* Return Status */
265 return Status;
266 }
267
268 NTSTATUS
269 NTAPI
270 CmpSetValueKeyExisting(IN PHHIVE Hive,
271 IN HCELL_INDEX OldChild,
272 IN PCM_KEY_VALUE Value,
273 IN ULONG Type,
274 IN PVOID Data,
275 IN ULONG DataSize,
276 IN ULONG StorageType,
277 IN ULONG TempData)
278 {
279 HCELL_INDEX DataCell, NewCell;
280 PCELL_DATA CellData;
281 ULONG Length;
282 BOOLEAN WasSmall, IsSmall;
283
284 /* Registry writes must be blocked */
285 CMP_ASSERT_FLUSH_LOCK(Hive);
286
287 /* Mark the old child cell dirty */
288 if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE;
289
290 /* See if this is a small or normal key */
291 WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
292
293 /* See if our new data can fit in a small key */
294 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
295
296 /* Big keys are unsupported */
297 ASSERT_VALUE_BIG(Hive, Length);
298 ASSERT_VALUE_BIG(Hive, DataSize);
299
300 /* Mark the old value dirty */
301 if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE;
302
303 /* Check if we have a small key */
304 if (IsSmall)
305 {
306 /* Check if we had a normal key with some data in it */
307 if (!(WasSmall) && (Length > 0))
308 {
309 /* Free the previous data */
310 CmpFreeValueData(Hive, Value->Data, Length);
311 }
312
313 /* Write our data directly */
314 Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
315 Value->Data = TempData;
316 Value->Type = Type;
317 return STATUS_SUCCESS;
318 }
319
320 /* We have a normal key. Was the old cell also normal and had data? */
321 if (!(WasSmall) && (Length > 0))
322 {
323 /* Get the current data cell and actual data inside it */
324 DataCell = Value->Data;
325 ASSERT(DataCell != HCELL_NIL);
326 CellData = HvGetCell(Hive, DataCell);
327 if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
328
329 /* Immediately release the cell */
330 HvReleaseCell(Hive, DataCell);
331
332 /* Make sure that the data cell actually has a size */
333 ASSERT(HvGetCellSize(Hive, CellData) > 0);
334
335 /* Check if the previous data cell could fit our new data */
336 if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
337 {
338 /* Re-use it then */
339 NewCell = DataCell;
340 }
341 else
342 {
343 /* Otherwise, re-allocate the current data cell */
344 NewCell = HvReallocateCell(Hive, DataCell, DataSize);
345 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
346 }
347 }
348 else
349 {
350 /* This was a small key, or a key with no data, allocate a cell */
351 NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
352 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
353 }
354
355 /* Now get the actual data for our data cell */
356 CellData = HvGetCell(Hive, NewCell);
357 if (!CellData) ASSERT(FALSE);
358
359 /* Release it immediately */
360 HvReleaseCell(Hive, NewCell);
361
362 /* Copy our data into the data cell's buffer, and set up the value */
363 RtlCopyMemory(CellData, Data, DataSize);
364 Value->Data = NewCell;
365 Value->DataLength = DataSize;
366 Value->Type = Type;
367
368 /* Return success */
369 ASSERT(HvIsCellDirty(Hive, NewCell));
370 return STATUS_SUCCESS;
371 }
372
373 NTSTATUS
374 NTAPI
375 CmpQueryKeyData(IN PHHIVE Hive,
376 IN PCM_KEY_NODE Node,
377 IN KEY_INFORMATION_CLASS KeyInformationClass,
378 IN OUT PVOID KeyInformation,
379 IN ULONG Length,
380 IN OUT PULONG ResultLength)
381 {
382 NTSTATUS Status;
383 ULONG Size, SizeLeft, MinimumSize;
384 PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
385 USHORT NameLength;
386
387 /* Check if the value is compressed */
388 if (Node->Flags & KEY_COMP_NAME)
389 {
390 /* Get the compressed name size */
391 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
392 }
393 else
394 {
395 /* Get the real size */
396 NameLength = Node->NameLength;
397 }
398
399 /* Check what kind of information is being requested */
400 switch (KeyInformationClass)
401 {
402 /* Basic information */
403 case KeyBasicInformation:
404
405 /* This is the size we need */
406 Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
407
408 /* And this is the minimum we can work with */
409 MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
410
411 /* Let the caller know and assume success */
412 *ResultLength = Size;
413 Status = STATUS_SUCCESS;
414
415 /* Check if the bufer we got is too small */
416 if (Length < MinimumSize)
417 {
418 /* Let the caller know and fail */
419 Status = STATUS_BUFFER_TOO_SMALL;
420 break;
421 }
422
423 /* Copy the basic information */
424 Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
425 Info->KeyBasicInformation.TitleIndex = 0;
426 Info->KeyBasicInformation.NameLength = NameLength;
427
428 /* Only the name is left */
429 SizeLeft = Length - MinimumSize;
430 Size = NameLength;
431
432 /* Check if we don't have enough space for the name */
433 if (SizeLeft < Size)
434 {
435 /* Truncate the name we'll return, and tell the caller */
436 Size = SizeLeft;
437 Status = STATUS_BUFFER_OVERFLOW;
438 }
439
440 /* Check if this is a compressed key */
441 if (Node->Flags & KEY_COMP_NAME)
442 {
443 /* Copy the compressed name */
444 CmpCopyCompressedName(Info->KeyBasicInformation.Name,
445 SizeLeft,
446 Node->Name,
447 Node->NameLength);
448 }
449 else
450 {
451 /* Otherwise, copy the raw name */
452 RtlCopyMemory(Info->KeyBasicInformation.Name,
453 Node->Name,
454 Size);
455 }
456 break;
457
458 /* Node information */
459 case KeyNodeInformation:
460
461 /* Calculate the size we need */
462 Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
463 NameLength +
464 Node->ClassLength;
465
466 /* And the minimum size we can support */
467 MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
468
469 /* Return the size to the caller and assume succes */
470 *ResultLength = Size;
471 Status = STATUS_SUCCESS;
472
473 /* Check if the caller's buffer is too small */
474 if (Length < MinimumSize)
475 {
476 /* Let them know, and fail */
477 Status = STATUS_BUFFER_TOO_SMALL;
478 break;
479 }
480
481 /* Copy the basic information */
482 Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
483 Info->KeyNodeInformation.TitleIndex = 0;
484 Info->KeyNodeInformation.ClassLength = Node->ClassLength;
485 Info->KeyNodeInformation.NameLength = NameLength;
486
487 /* Now the name is left */
488 SizeLeft = Length - MinimumSize;
489 Size = NameLength;
490
491 /* Check if the name can fit entirely */
492 if (SizeLeft < Size)
493 {
494 /* It can't, we'll have to truncate. Tell the caller */
495 Size = SizeLeft;
496 Status = STATUS_BUFFER_OVERFLOW;
497 }
498
499 /* Check if the key node name is compressed */
500 if (Node->Flags & KEY_COMP_NAME)
501 {
502 /* Copy the compressed name */
503 CmpCopyCompressedName(Info->KeyNodeInformation.Name,
504 SizeLeft,
505 Node->Name,
506 Node->NameLength);
507 }
508 else
509 {
510 /* It isn't, so copy the raw name */
511 RtlCopyMemory(Info->KeyNodeInformation.Name,
512 Node->Name,
513 Size);
514 }
515
516 /* Check if the node has a class */
517 if (Node->ClassLength > 0)
518 {
519 /* It does. We don't support these yet */
520 ASSERTMSG("Classes not supported\n", FALSE);
521 }
522 else
523 {
524 /* It doesn't, so set offset to -1, not 0! */
525 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
526 }
527 break;
528
529 /* Full information requsted */
530 case KeyFullInformation:
531
532 /* This is the size we need */
533 Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
534 Node->ClassLength;
535
536 /* This is what we can work with */
537 MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
538
539 /* Return it to caller and assume success */
540 *ResultLength = Size;
541 Status = STATUS_SUCCESS;
542
543 /* Check if the caller's buffer is to small */
544 if (Length < MinimumSize)
545 {
546 /* Let them know and fail */
547 Status = STATUS_BUFFER_TOO_SMALL;
548 break;
549 }
550
551 /* Now copy all the basic information */
552 Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
553 Info->KeyFullInformation.TitleIndex = 0;
554 Info->KeyFullInformation.ClassLength = Node->ClassLength;
555 Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
556 Node->SubKeyCounts[Volatile];
557 Info->KeyFullInformation.Values = Node->ValueList.Count;
558 Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
559 Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
560 Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
561 Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
562
563 /* Check if we have a class */
564 if (Node->ClassLength > 0)
565 {
566 /* We do, but we currently don't support this */
567 ASSERTMSG("Classes not supported\n", FALSE);
568 }
569 else
570 {
571 /* We don't have a class, so set offset to -1, not 0! */
572 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
573 }
574 break;
575
576 /* Any other class that got sent here is invalid! */
577 default:
578
579 /* Set failure code */
580 Status = STATUS_INVALID_PARAMETER;
581 break;
582 }
583
584 /* Return status */
585 return Status;
586 }
587
588 NTSTATUS
589 NTAPI
590 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
591 IN PUNICODE_STRING ValueName,
592 IN ULONG Type,
593 IN PVOID Data,
594 IN ULONG DataLength)
595 {
596 PHHIVE Hive = NULL;
597 PCM_KEY_NODE Parent;
598 PCM_KEY_VALUE Value = NULL;
599 HCELL_INDEX CurrentChild, Cell;
600 NTSTATUS Status;
601 BOOLEAN Found, Result;
602 ULONG Count, ChildIndex, SmallData, Storage;
603 VALUE_SEARCH_RETURN_TYPE SearchResult;
604 BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE;
605 HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL;
606
607 /* Acquire hive and KCB lock */
608 CmpLockRegistry();
609 CmpAcquireKcbLockShared(Kcb);
610
611 /* Sanity check */
612 ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
613
614 /* Don't touch deleted KCBs */
615 DoAgain:
616 if (Kcb->Delete)
617 {
618 /* Fail */
619 Status = STATUS_KEY_DELETED;
620 goto Quickie;
621 }
622
623 /* Don't let anyone mess with symlinks */
624 if ((Kcb->Flags & KEY_SYM_LINK) &&
625 ((Type != REG_LINK) ||
626 !(ValueName) ||
627 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE))))
628 {
629 /* Invalid modification of a symlink key */
630 Status = STATUS_ACCESS_DENIED;
631 goto Quickie;
632 }
633
634 /* Check if this is the first attempt */
635 if (FirstTry)
636 {
637 /* Search for the value in the cache */
638 SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb,
639 ValueName,
640 Type,
641 Data,
642 DataLength);
643 if (SearchResult == SearchNeedExclusiveLock)
644 {
645 /* Try again with the exclusive lock */
646 CmpConvertKcbSharedToExclusive(Kcb);
647 goto DoAgain;
648 }
649 else if (SearchResult == SearchSuccess)
650 {
651 /* We don't actually need to do anything! */
652 Status = STATUS_SUCCESS;
653 goto Quickie;
654 }
655
656 /* We need the exclusive KCB lock now */
657 if (!(CmpIsKcbLockedExclusive(Kcb)) &&
658 !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
659 {
660 /* Acquire exclusive lock */
661 CmpConvertKcbSharedToExclusive(Kcb);
662 }
663
664 /* Cache lookup failed, so don't try it next time */
665 FirstTry = FALSE;
666
667 /* Now grab the flush lock since the key will be modified */
668 ASSERT(FlusherLocked == FALSE);
669 CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive);
670 FlusherLocked = TRUE;
671 goto DoAgain;
672 }
673 else
674 {
675 /* Get pointer to key cell */
676 Hive = Kcb->KeyHive;
677 Cell = Kcb->KeyCell;
678
679 /* Get the parent */
680 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
681 ASSERT(Parent);
682 ParentCell = Cell;
683
684 /* Prepare to scan the key node */
685 Count = Parent->ValueList.Count;
686 Found = FALSE;
687 if (Count > 0)
688 {
689 /* Try to find the existing name */
690 Result = CmpFindNameInList(Hive,
691 &Parent->ValueList,
692 ValueName,
693 &ChildIndex,
694 &CurrentChild);
695 if (!Result)
696 {
697 /* Fail */
698 Status = STATUS_INSUFFICIENT_RESOURCES;
699 goto Quickie;
700 }
701
702 /* Check if we found something */
703 if (CurrentChild != HCELL_NIL)
704 {
705 /* Release existing child */
706 if (ChildCell != HCELL_NIL)
707 {
708 HvReleaseCell(Hive, ChildCell);
709 ChildCell = HCELL_NIL;
710 }
711
712 /* Get its value */
713 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
714 if (!Value)
715 {
716 /* Fail */
717 Status = STATUS_INSUFFICIENT_RESOURCES;
718 goto Quickie;
719 }
720
721 /* Remember that we found it */
722 ChildCell = CurrentChild;
723 Found = TRUE;
724 }
725 }
726 else
727 {
728 /* No child list, we'll need to add it */
729 ChildIndex = 0;
730 }
731 }
732
733 /* Should only get here on the second pass */
734 ASSERT(FirstTry == FALSE);
735
736 /* The KCB must be locked exclusive at this point */
737 CMP_ASSERT_KCB_LOCK(Kcb);
738
739 /* Mark the cell dirty */
740 if (!HvMarkCellDirty(Hive, Cell, FALSE))
741 {
742 /* Not enough log space, fail */
743 Status = STATUS_NO_LOG_SPACE;
744 goto Quickie;
745 }
746
747 /* Get the storage type */
748 Storage = HvGetCellType(Cell);
749
750 /* Check if this is small data */
751 SmallData = 0;
752 if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
753 {
754 /* Need SEH because user data may be invalid */
755 _SEH2_TRY
756 {
757 /* Copy it */
758 RtlCopyMemory(&SmallData, Data, DataLength);
759 }
760 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
761 {
762 /* Return failure code */
763 Status = _SEH2_GetExceptionCode();
764 _SEH2_YIELD(goto Quickie);
765 }
766 _SEH2_END;
767 }
768
769 /* Check if we didn't find a matching key */
770 if (!Found)
771 {
772 /* Call the internal routine */
773 Status = CmpSetValueKeyNew(Hive,
774 Parent,
775 ValueName,
776 ChildIndex,
777 Type,
778 Data,
779 DataLength,
780 Storage,
781 SmallData);
782 }
783 else
784 {
785 /* Call the internal routine */
786 Status = CmpSetValueKeyExisting(Hive,
787 CurrentChild,
788 Value,
789 Type,
790 Data,
791 DataLength,
792 Storage,
793 SmallData);
794 }
795
796 /* Check for success */
797 if (NT_SUCCESS(Status))
798 {
799 /* Check if the maximum value name length changed */
800 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
801 if (Parent->MaxValueNameLen < ValueName->Length)
802 {
803 /* Set the new values */
804 Parent->MaxValueNameLen = ValueName->Length;
805 Kcb->KcbMaxValueNameLen = ValueName->Length;
806 }
807
808 /* Check if the maximum data length changed */
809 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
810 if (Parent->MaxValueDataLen < DataLength)
811 {
812 /* Update it */
813 Parent->MaxValueDataLen = DataLength;
814 Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
815 }
816
817 /* Save the write time */
818 KeQuerySystemTime(&Parent->LastWriteTime);
819 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
820
821 /* Check if the cell is cached */
822 if ((Found) && (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)))
823 {
824 /* Shouldn't happen */
825 ASSERT(FALSE);
826 }
827 else
828 {
829 /* Cleanup the value cache */
830 CmpCleanUpKcbValueCache(Kcb);
831
832 /* Sanity checks */
833 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
834 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
835
836 /* Set the value cache */
837 Kcb->ValueCache.Count = Parent->ValueList.Count;
838 Kcb->ValueCache.ValueList = Parent->ValueList.List;
839 }
840
841 /* Notify registered callbacks */
842 CmpReportNotify(Kcb,
843 Hive,
844 Kcb->KeyCell,
845 REG_NOTIFY_CHANGE_LAST_SET);
846 }
847
848 /* Release the cells */
849 Quickie:
850 if ((ParentCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ParentCell);
851 if ((ChildCell != HCELL_NIL) && (Hive)) HvReleaseCell(Hive, ChildCell);
852
853 /* Release the locks */
854 if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive);
855 CmpReleaseKcbLock(Kcb);
856 CmpUnlockRegistry();
857 return Status;
858 }
859
860 NTSTATUS
861 NTAPI
862 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
863 IN UNICODE_STRING ValueName)
864 {
865 NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
866 PHHIVE Hive;
867 PCM_KEY_NODE Parent;
868 HCELL_INDEX ChildCell, Cell;
869 PCHILD_LIST ChildList;
870 PCM_KEY_VALUE Value = NULL;
871 ULONG ChildIndex;
872 BOOLEAN Result;
873
874 /* Acquire hive lock */
875 CmpLockRegistry();
876
877 /* Lock KCB exclusively */
878 CmpAcquireKcbLockExclusive(Kcb);
879
880 /* Don't touch deleted keys */
881 if (Kcb->Delete)
882 {
883 /* Undo everything */
884 CmpReleaseKcbLock(Kcb);
885 CmpUnlockRegistry();
886 return STATUS_KEY_DELETED;
887 }
888
889 /* Get the hive and the cell index */
890 Hive = Kcb->KeyHive;
891 Cell = Kcb->KeyCell;
892
893 /* Lock flushes */
894 CmpLockHiveFlusherShared((PCMHIVE)Hive);
895
896 /* Get the parent key node */
897 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
898 ASSERT(Parent);
899
900 /* Get the value list and check if it has any entries */
901 ChildList = &Parent->ValueList;
902 if (ChildList->Count)
903 {
904 /* Try to find this value */
905 Result = CmpFindNameInList(Hive,
906 ChildList,
907 &ValueName,
908 &ChildIndex,
909 &ChildCell);
910 if (!Result)
911 {
912 /* Fail */
913 Status = STATUS_INSUFFICIENT_RESOURCES;
914 goto Quickie;
915 }
916
917 /* Value not found, return error */
918 if (ChildCell == HCELL_NIL) goto Quickie;
919
920 /* We found the value, mark all relevant cells dirty */
921 if (!((HvMarkCellDirty(Hive, Cell, FALSE)) &&
922 (HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) &&
923 (HvMarkCellDirty(Hive, ChildCell, FALSE))))
924 {
925 /* Not enough log space, fail */
926 Status = STATUS_NO_LOG_SPACE;
927 goto Quickie;
928 }
929
930 /* Get the key value */
931 Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
932 ASSERT(Value);
933
934 /* Mark it and all related data as dirty */
935 if (!CmpMarkValueDataDirty(Hive, Value))
936 {
937 /* Not enough log space, fail */
938 Status = STATUS_NO_LOG_SPACE;
939 goto Quickie;
940 }
941
942 /* Ssanity checks */
943 ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
944 ASSERT(HvIsCellDirty(Hive, ChildCell));
945
946 /* Remove the value from the child list */
947 Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
948 if (!NT_SUCCESS(Status))
949 {
950 /* Set known error */
951 Status = STATUS_INSUFFICIENT_RESOURCES;
952 goto Quickie;
953 }
954
955 /* Remove the value and its data itself */
956 if (!CmpFreeValue(Hive, ChildCell))
957 {
958 /* Failed to free the value, fail */
959 Status = STATUS_INSUFFICIENT_RESOURCES;
960 goto Quickie;
961 }
962
963 /* Set the last write time */
964 KeQuerySystemTime(&Parent->LastWriteTime);
965 Kcb->KcbLastWriteTime = Parent->LastWriteTime;
966
967 /* Sanity check */
968 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
969 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
970 ASSERT(HvIsCellDirty(Hive, Cell));
971
972 /* Check if the value list is empty now */
973 if (!Parent->ValueList.Count)
974 {
975 /* Then clear key node data */
976 Parent->MaxValueNameLen = 0;
977 Parent->MaxValueDataLen = 0;
978 Kcb->KcbMaxValueNameLen = 0;
979 Kcb->KcbMaxValueDataLen = 0;
980 }
981
982 /* Cleanup the value cache */
983 CmpCleanUpKcbValueCache(Kcb);
984
985 /* Sanity checks */
986 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
987 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
988
989 /* Set the value cache */
990 Kcb->ValueCache.Count = ChildList->Count;
991 Kcb->ValueCache.ValueList = ChildList->List;
992
993 /* Notify registered callbacks */
994 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET);
995
996 /* Change default Status to success */
997 Status = STATUS_SUCCESS;
998 }
999
1000 Quickie:
1001 /* Release the parent cell, if any */
1002 if (Parent) HvReleaseCell(Hive, Cell);
1003
1004 /* Check if we had a value */
1005 if (Value)
1006 {
1007 /* Release the child cell */
1008 ASSERT(ChildCell != HCELL_NIL);
1009 HvReleaseCell(Hive, ChildCell);
1010 }
1011
1012 /* Release locks */
1013 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1014 CmpReleaseKcbLock(Kcb);
1015 CmpUnlockRegistry();
1016 return Status;
1017 }
1018
1019 NTSTATUS
1020 NTAPI
1021 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1022 IN UNICODE_STRING ValueName,
1023 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1024 IN PVOID KeyValueInformation,
1025 IN ULONG Length,
1026 IN PULONG ResultLength)
1027 {
1028 NTSTATUS Status;
1029 PCM_KEY_VALUE ValueData;
1030 ULONG Index;
1031 BOOLEAN ValueCached = FALSE;
1032 PCM_CACHED_VALUE *CachedValue;
1033 HCELL_INDEX CellToRelease;
1034 VALUE_SEARCH_RETURN_TYPE Result;
1035 PHHIVE Hive;
1036 PAGED_CODE();
1037
1038 /* Acquire hive lock */
1039 CmpLockRegistry();
1040
1041 /* Lock the KCB shared */
1042 CmpAcquireKcbLockShared(Kcb);
1043
1044 /* Don't touch deleted keys */
1045 DoAgain:
1046 if (Kcb->Delete)
1047 {
1048 /* Undo everything */
1049 CmpReleaseKcbLock(Kcb);
1050 CmpUnlockRegistry();
1051 return STATUS_KEY_DELETED;
1052 }
1053
1054 /* We don't deal with this yet */
1055 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1056 {
1057 /* Shouldn't happen */
1058 ASSERT(FALSE);
1059 }
1060
1061 /* Get the hive */
1062 Hive = Kcb->KeyHive;
1063
1064 /* Find the key value */
1065 Result = CmpFindValueByNameFromCache(Kcb,
1066 &ValueName,
1067 &CachedValue,
1068 &Index,
1069 &ValueData,
1070 &ValueCached,
1071 &CellToRelease);
1072 if (Result == SearchNeedExclusiveLock)
1073 {
1074 /* Check if we need an exclusive lock */
1075 ASSERT(CellToRelease == HCELL_NIL);
1076 ASSERT(ValueData == NULL);
1077
1078 /* Try with exclusive KCB lock */
1079 CmpConvertKcbSharedToExclusive(Kcb);
1080 goto DoAgain;
1081 }
1082
1083 if (Result == SearchSuccess)
1084 {
1085 /* Sanity check */
1086 ASSERT(ValueData != NULL);
1087
1088 /* User data, protect against exceptions */
1089 _SEH2_TRY
1090 {
1091 /* Query the information requested */
1092 Result = CmpQueryKeyValueData(Kcb,
1093 CachedValue,
1094 ValueData,
1095 ValueCached,
1096 KeyValueInformationClass,
1097 KeyValueInformation,
1098 Length,
1099 ResultLength,
1100 &Status);
1101 if (Result == SearchNeedExclusiveLock)
1102 {
1103 /* Release the value cell */
1104 if (CellToRelease != HCELL_NIL)
1105 {
1106 HvReleaseCell(Hive, CellToRelease);
1107 CellToRelease = HCELL_NIL;
1108 }
1109
1110 /* Try with exclusive KCB lock */
1111 CmpConvertKcbSharedToExclusive(Kcb);
1112 goto DoAgain;
1113 }
1114 }
1115 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1116 {
1117 Status = _SEH2_GetExceptionCode();
1118 }
1119 _SEH2_END;
1120 }
1121 else
1122 {
1123 /* Failed to find the value */
1124 Status = STATUS_OBJECT_NAME_NOT_FOUND;
1125 }
1126
1127 /* If we have a cell to release, do so */
1128 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1129
1130 /* Release locks */
1131 CmpReleaseKcbLock(Kcb);
1132 CmpUnlockRegistry();
1133 return Status;
1134 }
1135
1136 NTSTATUS
1137 NTAPI
1138 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1139 IN ULONG Index,
1140 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
1141 IN PVOID KeyValueInformation,
1142 IN ULONG Length,
1143 IN PULONG ResultLength)
1144 {
1145 NTSTATUS Status;
1146 PHHIVE Hive;
1147 PCM_KEY_NODE Parent;
1148 HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
1149 VALUE_SEARCH_RETURN_TYPE Result;
1150 BOOLEAN IndexIsCached, ValueIsCached = FALSE;
1151 PCELL_DATA CellData;
1152 PCM_CACHED_VALUE *CachedValue;
1153 PCM_KEY_VALUE ValueData = NULL;
1154 PAGED_CODE();
1155
1156 /* Acquire hive lock */
1157 CmpLockRegistry();
1158
1159 /* Lock the KCB shared */
1160 CmpAcquireKcbLockShared(Kcb);
1161
1162 /* Don't touch deleted keys */
1163 DoAgain:
1164 if (Kcb->Delete)
1165 {
1166 /* Undo everything */
1167 CmpReleaseKcbLock(Kcb);
1168 CmpUnlockRegistry();
1169 return STATUS_KEY_DELETED;
1170 }
1171
1172 /* Get the hive and parent */
1173 Hive = Kcb->KeyHive;
1174 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1175 ASSERT(Parent);
1176
1177 /* FIXME: Lack of cache? */
1178 if (Kcb->ValueCache.Count != Parent->ValueList.Count)
1179 {
1180 DPRINT1("HACK: Overriding value cache count\n");
1181 Kcb->ValueCache.Count = Parent->ValueList.Count;
1182 }
1183
1184 /* Make sure the index is valid */
1185 if (Index >= Kcb->ValueCache.Count)
1186 {
1187 /* Release the cell and fail */
1188 HvReleaseCell(Hive, Kcb->KeyCell);
1189 Status = STATUS_NO_MORE_ENTRIES;
1190 goto Quickie;
1191 }
1192
1193 /* We don't deal with this yet */
1194 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)
1195 {
1196 /* Shouldn't happen */
1197 ASSERT(FALSE);
1198 }
1199
1200 /* Find the value list */
1201 Result = CmpGetValueListFromCache(Kcb,
1202 &CellData,
1203 &IndexIsCached,
1204 &CellToRelease);
1205 if (Result == SearchNeedExclusiveLock)
1206 {
1207 /* Check if we need an exclusive lock */
1208 ASSERT(CellToRelease == HCELL_NIL);
1209 HvReleaseCell(Hive, Kcb->KeyCell);
1210
1211 /* Try with exclusive KCB lock */
1212 CmpConvertKcbSharedToExclusive(Kcb);
1213 goto DoAgain;
1214 }
1215 else if (Result != SearchSuccess)
1216 {
1217 /* Sanity check */
1218 ASSERT(CellData == NULL);
1219
1220 /* Release the cell and fail */
1221 Status = STATUS_INSUFFICIENT_RESOURCES;
1222 goto Quickie;
1223 }
1224
1225 /* Now get the key value */
1226 Result = CmpGetValueKeyFromCache(Kcb,
1227 CellData,
1228 Index,
1229 &CachedValue,
1230 &ValueData,
1231 IndexIsCached,
1232 &ValueIsCached,
1233 &CellToRelease2);
1234 if (Result == SearchNeedExclusiveLock)
1235 {
1236 /* Cleanup state */
1237 ASSERT(CellToRelease2 == HCELL_NIL);
1238 if (CellToRelease)
1239 {
1240 HvReleaseCell(Hive, CellToRelease);
1241 CellToRelease = HCELL_NIL;
1242 }
1243 HvReleaseCell(Hive, Kcb->KeyCell);
1244
1245 /* Try with exclusive KCB lock */
1246 CmpConvertKcbSharedToExclusive(Kcb);
1247 goto DoAgain;
1248 }
1249 else if (Result != SearchSuccess)
1250 {
1251 /* Sanity check */
1252 ASSERT(ValueData == NULL);
1253
1254 /* Release the cells and fail */
1255 Status = STATUS_INSUFFICIENT_RESOURCES;
1256 goto Quickie;
1257 }
1258
1259 /* User data, need SEH */
1260 _SEH2_TRY
1261 {
1262 /* Query the information requested */
1263 Result = CmpQueryKeyValueData(Kcb,
1264 CachedValue,
1265 ValueData,
1266 ValueIsCached,
1267 KeyValueInformationClass,
1268 KeyValueInformation,
1269 Length,
1270 ResultLength,
1271 &Status);
1272 if (Result == SearchNeedExclusiveLock)
1273 {
1274 /* Cleanup state */
1275 if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2);
1276 HvReleaseCell(Hive, Kcb->KeyCell);
1277 if (CellToRelease) HvReleaseCell(Hive, CellToRelease);
1278
1279 /* Try with exclusive KCB lock */
1280 CmpConvertKcbSharedToExclusive(Kcb);
1281 goto DoAgain;
1282 }
1283 }
1284 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1285 {
1286 /* Get exception code */
1287 Status = _SEH2_GetExceptionCode();
1288 }
1289 _SEH2_END;
1290
1291 Quickie:
1292 /* If we have a cell to release, do so */
1293 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
1294
1295 /* Release the parent cell */
1296 HvReleaseCell(Hive, Kcb->KeyCell);
1297
1298 /* If we have a cell to release, do so */
1299 if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
1300
1301 /* Release locks */
1302 CmpReleaseKcbLock(Kcb);
1303 CmpUnlockRegistry();
1304 return Status;
1305 }
1306
1307 NTSTATUS
1308 NTAPI
1309 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1310 IN KEY_INFORMATION_CLASS KeyInformationClass,
1311 IN PVOID KeyInformation,
1312 IN ULONG Length,
1313 IN PULONG ResultLength)
1314 {
1315 NTSTATUS Status;
1316 PHHIVE Hive;
1317 PCM_KEY_NODE Parent;
1318 HV_TRACK_CELL_REF CellReferences = {0};
1319
1320 /* Acquire hive lock */
1321 CmpLockRegistry();
1322
1323 /* Lock KCB shared */
1324 CmpAcquireKcbLockShared(Kcb);
1325
1326 /* Don't touch deleted keys */
1327 if (Kcb->Delete)
1328 {
1329 /* Fail */
1330 Status = STATUS_KEY_DELETED;
1331 goto Quickie;
1332 }
1333
1334 /* Check what class we got */
1335 switch (KeyInformationClass)
1336 {
1337 /* Typical information */
1338 case KeyFullInformation:
1339 case KeyBasicInformation:
1340 case KeyNodeInformation:
1341
1342 /* Get the hive and parent */
1343 Hive = Kcb->KeyHive;
1344 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1345 ASSERT(Parent);
1346
1347 /* Track cell references */
1348 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell))
1349 {
1350 /* Not enough memory to track references */
1351 Status = STATUS_INSUFFICIENT_RESOURCES;
1352 }
1353 else
1354 {
1355 /* Call the internal API */
1356 Status = CmpQueryKeyData(Hive,
1357 Parent,
1358 KeyInformationClass,
1359 KeyInformation,
1360 Length,
1361 ResultLength);
1362 }
1363 break;
1364
1365 /* Unsupported classes for now */
1366 case KeyNameInformation:
1367 case KeyCachedInformation:
1368 case KeyFlagsInformation:
1369
1370 /* Print message and fail */
1371 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1372 Status = STATUS_NOT_IMPLEMENTED;
1373 break;
1374
1375 /* Illegal classes */
1376 default:
1377
1378 /* Print message and fail */
1379 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
1380 Status = STATUS_INVALID_INFO_CLASS;
1381 break;
1382 }
1383
1384 Quickie:
1385 /* Release references */
1386 HvReleaseFreeCellRefArray(&CellReferences);
1387
1388 /* Release locks */
1389 CmpReleaseKcbLock(Kcb);
1390 CmpUnlockRegistry();
1391 return Status;
1392 }
1393
1394 NTSTATUS
1395 NTAPI
1396 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1397 IN ULONG Index,
1398 IN KEY_INFORMATION_CLASS KeyInformationClass,
1399 IN PVOID KeyInformation,
1400 IN ULONG Length,
1401 IN PULONG ResultLength)
1402 {
1403 NTSTATUS Status;
1404 PHHIVE Hive;
1405 PCM_KEY_NODE Parent, Child;
1406 HCELL_INDEX ChildCell;
1407 HV_TRACK_CELL_REF CellReferences = {0};
1408
1409 /* Acquire hive lock */
1410 CmpLockRegistry();
1411
1412 /* Lock the KCB shared */
1413 CmpAcquireKcbLockShared(Kcb);
1414
1415 /* Don't touch deleted keys */
1416 if (Kcb->Delete)
1417 {
1418 /* Undo everything */
1419 Status = STATUS_KEY_DELETED;
1420 goto Quickie;
1421 }
1422
1423 /* Get the hive and parent */
1424 Hive = Kcb->KeyHive;
1425 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
1426 ASSERT(Parent);
1427
1428 /* Get the child cell */
1429 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1430
1431 /* Release the parent cell */
1432 HvReleaseCell(Hive, Kcb->KeyCell);
1433
1434 /* Check if we found the child */
1435 if (ChildCell == HCELL_NIL)
1436 {
1437 /* We didn't, fail */
1438 Status = STATUS_NO_MORE_ENTRIES;
1439 goto Quickie;
1440 }
1441
1442 /* Now get the actual child node */
1443 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1444 ASSERT(Child);
1445
1446 /* Track references */
1447 if (!HvTrackCellRef(&CellReferences, Hive, ChildCell))
1448 {
1449 /* Can't allocate memory for tracking */
1450 Status = STATUS_INSUFFICIENT_RESOURCES;
1451 goto Quickie;
1452 }
1453
1454 /* Data can be user-mode, use SEH */
1455 _SEH2_TRY
1456 {
1457 /* Query the data requested */
1458 Status = CmpQueryKeyData(Hive,
1459 Child,
1460 KeyInformationClass,
1461 KeyInformation,
1462 Length,
1463 ResultLength);
1464 }
1465 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1466 {
1467 /* Fail with exception code */
1468 Status = _SEH2_GetExceptionCode();
1469 _SEH2_YIELD(goto Quickie);
1470 }
1471 _SEH2_END;
1472
1473 Quickie:
1474 /* Release references */
1475 HvReleaseFreeCellRefArray(&CellReferences);
1476
1477 /* Release locks */
1478 CmpReleaseKcbLock(Kcb);
1479 CmpUnlockRegistry();
1480 return Status;
1481 }
1482
1483 NTSTATUS
1484 NTAPI
1485 CmDeleteKey(IN PCM_KEY_BODY KeyBody)
1486 {
1487 NTSTATUS Status;
1488 PHHIVE Hive;
1489 PCM_KEY_NODE Node, Parent;
1490 HCELL_INDEX Cell, ParentCell;
1491 PCM_KEY_CONTROL_BLOCK Kcb;
1492
1493 /* Acquire hive lock */
1494 CmpLockRegistry();
1495
1496 /* Get the kcb */
1497 Kcb = KeyBody->KeyControlBlock;
1498
1499 /* Don't allow deleting the root */
1500 if (!Kcb->ParentKcb)
1501 {
1502 /* Fail */
1503 CmpUnlockRegistry();
1504 return STATUS_CANNOT_DELETE;
1505 }
1506
1507 /* Lock parent and child */
1508 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1509
1510 /* Check if we're already being deleted */
1511 if (Kcb->Delete)
1512 {
1513 /* Don't do it twice */
1514 Status = STATUS_SUCCESS;
1515 goto Quickie2;
1516 }
1517
1518 /* Get the hive and node */
1519 Hive = Kcb->KeyHive;
1520 Cell = Kcb->KeyCell;
1521
1522 /* Lock flushes */
1523 CmpLockHiveFlusherShared((PCMHIVE)Hive);
1524
1525 /* Get the key node */
1526 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1527 ASSERT(Node);
1528
1529 /* Sanity check */
1530 ASSERT(Node->Flags == Kcb->Flags);
1531
1532 /* Check if we don't have any children */
1533 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) &&
1534 !(Node->Flags & KEY_NO_DELETE))
1535 {
1536 /* Send notification to registered callbacks */
1537 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME);
1538
1539 /* Get the parent and free the cell */
1540 ParentCell = Node->Parent;
1541 Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1542 if (NT_SUCCESS(Status))
1543 {
1544 /* Flush any notifications */
1545 CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE);
1546
1547 /* Clean up information we have on the subkey */
1548 CmpCleanUpSubKeyInfo(Kcb->ParentKcb);
1549
1550 /* Get the parent node */
1551 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1552 if (Parent)
1553 {
1554 /* Update the maximum name length */
1555 Kcb->ParentKcb->KcbMaxNameLen = Parent->MaxNameLen;
1556
1557 /* Make sure we're dirty */
1558 ASSERT(HvIsCellDirty(Hive, ParentCell));
1559
1560 /* Update the write time */
1561 KeQuerySystemTime(&Parent->LastWriteTime);
1562 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime;
1563
1564 /* Release the cell */
1565 HvReleaseCell(Hive, ParentCell);
1566 }
1567
1568 /* Set the KCB in delete mode and remove it */
1569 Kcb->Delete = TRUE;
1570 CmpRemoveKeyControlBlock(Kcb);
1571
1572 /* Clear the cell */
1573 Kcb->KeyCell = HCELL_NIL;
1574 }
1575 }
1576 else
1577 {
1578 /* Fail */
1579 Status = STATUS_CANNOT_DELETE;
1580 }
1581
1582 /* Release the cell */
1583 HvReleaseCell(Hive, Cell);
1584
1585 /* Release flush lock */
1586 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1587
1588 /* Release the KCB locks */
1589 Quickie2:
1590 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey);
1591
1592 /* Release hive lock */
1593 CmpUnlockRegistry();
1594 return Status;
1595 }
1596
1597 NTSTATUS
1598 NTAPI
1599 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1600 IN BOOLEAN ExclusiveLock)
1601 {
1602 PCMHIVE CmHive;
1603 NTSTATUS Status = STATUS_SUCCESS;
1604 PHHIVE Hive;
1605
1606 /* Ignore flushes until we're ready */
1607 if (CmpNoWrite) return STATUS_SUCCESS;
1608
1609 /* Get the hives */
1610 Hive = Kcb->KeyHive;
1611 CmHive = (PCMHIVE)Hive;
1612
1613 /* Check if this is the master hive */
1614 if (CmHive == CmiVolatileHive)
1615 {
1616 /* Flush all the hives instead */
1617 CmpDoFlushAll(FALSE);
1618 }
1619 else
1620 {
1621 /* Don't touch the hive */
1622 CmpLockHiveFlusherExclusive(CmHive);
1623 ASSERT(CmHive->ViewLock);
1624 KeAcquireGuardedMutex(CmHive->ViewLock);
1625 CmHive->ViewLockOwner = KeGetCurrentThread();
1626
1627 /* Will the hive shrink? */
1628 if (HvHiveWillShrink(Hive))
1629 {
1630 /* I don't believe the current Hv does shrinking */
1631 ASSERT(FALSE);
1632 }
1633 else
1634 {
1635 /* Now we can release views */
1636 ASSERT(CmHive->ViewLock);
1637 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive);
1638 ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner);
1639 KeReleaseGuardedMutex(CmHive->ViewLock);
1640 }
1641
1642 /* Flush only this hive */
1643 if (!HvSyncHive(Hive))
1644 {
1645 /* Fail */
1646 Status = STATUS_REGISTRY_IO_FAILED;
1647 }
1648
1649 /* Release the flush lock */
1650 CmpUnlockHiveFlusher((PCMHIVE)Hive);
1651 }
1652
1653 /* Return the status */
1654 return Status;
1655 }
1656
1657 NTSTATUS
1658 NTAPI
1659 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1660 IN POBJECT_ATTRIBUTES SourceFile,
1661 IN ULONG Flags,
1662 IN PCM_KEY_BODY KeyBody)
1663 {
1664 SECURITY_QUALITY_OF_SERVICE ServiceQos;
1665 SECURITY_CLIENT_CONTEXT ClientSecurityContext;
1666 HANDLE KeyHandle;
1667 BOOLEAN Allocate = TRUE;
1668 PCMHIVE CmHive, LoadedHive;
1669 NTSTATUS Status;
1670 CM_PARSE_CONTEXT ParseContext;
1671
1672 /* Check if we have a trust key */
1673 if (KeyBody)
1674 {
1675 /* Fail */
1676 DPRINT1("Trusted classes not yet supported\n");
1677 return STATUS_NOT_IMPLEMENTED;
1678 }
1679
1680 /* Build a service QoS for a security context */
1681 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1682 ServiceQos.ImpersonationLevel = SecurityImpersonation;
1683 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1684 ServiceQos.EffectiveOnly = TRUE;
1685 Status = SeCreateClientSecurity(PsGetCurrentThread(),
1686 &ServiceQos,
1687 FALSE,
1688 &ClientSecurityContext);
1689 if (!NT_SUCCESS(Status))
1690 {
1691 /* Fail */
1692 DPRINT1("Security context failed\n");
1693 return Status;
1694 }
1695
1696 /* Open the target key */
1697 #if 0
1698 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
1699 #else
1700 RtlZeroMemory(&ParseContext, sizeof(ParseContext));
1701 ParseContext.CreateOperation = FALSE;
1702 Status = ObOpenObjectByName(TargetKey,
1703 CmpKeyObjectType,
1704 KernelMode,
1705 NULL,
1706 KEY_READ,
1707 &ParseContext,
1708 &KeyHandle);
1709 #endif
1710 if (!NT_SUCCESS(Status)) KeyHandle = NULL;
1711
1712 /* Open the hive */
1713 Status = CmpCmdHiveOpen(SourceFile,
1714 &ClientSecurityContext,
1715 &Allocate,
1716 &CmHive,
1717 0);
1718
1719 /* Get rid of the security context */
1720 SeDeleteClientSecurity(&ClientSecurityContext);
1721
1722 /* See if we failed */
1723 if (!NT_SUCCESS(Status))
1724 {
1725 /* See if the target already existed */
1726 if (KeyHandle)
1727 {
1728 /* Lock the registry */
1729 CmpLockRegistryExclusive();
1730
1731 /* Check if we are already loaded */
1732 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive))
1733 {
1734 /* That's okay then */
1735 ASSERT(LoadedHive);
1736 Status = STATUS_SUCCESS;
1737 }
1738
1739 /* Release the registry */
1740 CmpUnlockRegistry();
1741 }
1742
1743 /* Close the key handle if we had one */
1744 if (KeyHandle) ZwClose(KeyHandle);
1745 return Status;
1746 }
1747
1748 /* Lock the registry shared */
1749 CmpLockRegistry();
1750
1751 /* Lock loading */
1752 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1753
1754 /* Lock the hive to this thread */
1755 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
1756 CmHive->CreatorOwner = KeGetCurrentThread();
1757
1758 /* Set flag */
1759 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
1760
1761 /* Link the hive */
1762 Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
1763 TargetKey->RootDirectory,
1764 CmHive,
1765 Allocate,
1766 TargetKey->SecurityDescriptor);
1767 if (NT_SUCCESS(Status))
1768 {
1769 /* Add to HiveList key */
1770 CmpAddToHiveFileList(CmHive);
1771
1772 /* Sync the hive if necessary */
1773 if (Allocate)
1774 {
1775 /* Sync it under the flusher lock */
1776 CmpLockHiveFlusherExclusive(CmHive);
1777 HvSyncHive(&CmHive->Hive);
1778 CmpUnlockHiveFlusher(CmHive);
1779 }
1780
1781 /* Release the hive */
1782 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
1783 CmHive->CreatorOwner = NULL;
1784
1785 /* Allow loads */
1786 ExReleasePushLock(&CmpLoadHiveLock);
1787 }
1788 else
1789 {
1790 /* FIXME: TODO */
1791 ASSERT(FALSE);
1792 }
1793
1794 /* Is this first profile load? */
1795 if (!(CmpProfileLoaded) && !(CmpWasSetupBoot))
1796 {
1797 /* User is now logged on, set quotas */
1798 CmpProfileLoaded = TRUE;
1799 CmpSetGlobalQuotaAllowed();
1800 }
1801
1802 /* Unlock the registry */
1803 CmpUnlockRegistry();
1804
1805 /* Close handle and return */
1806 if (KeyHandle) ZwClose(KeyHandle);
1807 return Status;
1808 }
1809
1810 NTSTATUS
1811 NTAPI
1812 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1813 IN ULONG Flags)
1814 {
1815 UNIMPLEMENTED;
1816 return STATUS_NOT_IMPLEMENTED;
1817 }
1818
1819 ULONG
1820 NTAPI
1821 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb,
1822 IN BOOLEAN RemoveEmptyCacheEntries)
1823 {
1824 PCM_KEY_HASH Entry;
1825 PCM_KEY_CONTROL_BLOCK CachedKcb;
1826 PCM_KEY_CONTROL_BLOCK ParentKcb;
1827 USHORT ParentKeyCount;
1828 USHORT j;
1829 ULONG i;
1830 ULONG SubKeys = 0;
1831
1832 DPRINT("CmCountOpenSubKeys() called\n");
1833
1834 /* The root key is the only referenced key. There are no refereced sub keys. */
1835 if (RootKcb->RefCount == 1)
1836 {
1837 DPRINT("open sub keys: 0\n");
1838 return 0;
1839 }
1840
1841 /* Enumerate all hash lists */
1842 for (i = 0; i < CmpHashTableSize; i++)
1843 {
1844 /* Get the first cache entry */
1845 Entry = CmpCacheTable[i].Entry;
1846
1847 /* Enumerate all cache entries */
1848 while (Entry)
1849 {
1850 /* Get the KCB of the current cache entry */
1851 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash);
1852
1853 /* Check keys only that are subkeys to our root key */
1854 if (CachedKcb->TotalLevels > RootKcb->TotalLevels)
1855 {
1856 /* Calculate the number of parent keys to the root key */
1857 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels;
1858
1859 /* Find a parent key that could be the root key */
1860 ParentKcb = CachedKcb;
1861 for (j = 0; j < ParentKeyCount; j++)
1862 {
1863 ParentKcb = ParentKcb->ParentKcb;
1864 }
1865
1866 /* Check whether the parent is the root key */
1867 if (ParentKcb == RootKcb)
1868 {
1869 DPRINT("Found a sub key \n");
1870 DPRINT("RefCount = %u\n", CachedKcb->RefCount);
1871
1872 if (CachedKcb->RefCount > 0)
1873 {
1874 /* Count the current hash entry if it is in use */
1875 SubKeys++;
1876 }
1877 else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries == TRUE))
1878 {
1879 /* Remove the current key from the delayed close list */
1880 CmpRemoveFromDelayedClose(CachedKcb);
1881
1882 /* Remove the current cache entry */
1883 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE);
1884
1885 /* Restart, because the hash list has changed */
1886 Entry = CmpCacheTable[i].Entry;
1887 continue;
1888 }
1889 }
1890 }
1891
1892 /* Get the next cache entry */
1893 Entry = Entry->NextHash;
1894 }
1895 }
1896
1897 DPRINT("open sub keys: %u\n", SubKeys);
1898
1899 return SubKeys;
1900 }