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