- Implement NtCreateKey using the new parse routine.
[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 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14
15 /* FUNCTIONS *****************************************************************/
16
17 BOOLEAN
18 NTAPI
19 CmpDoFlushAll(IN BOOLEAN ForceFlush)
20 {
21 NTSTATUS Status;
22 PLIST_ENTRY NextEntry;
23 PCMHIVE Hive;
24 BOOLEAN Result = TRUE;
25
26 /* Make sure that the registry isn't read-only now */
27 if (CmpNoWrite) return TRUE;
28
29 /* Otherwise, acquire the hive list lock and disable force flush */
30 CmpForceForceFlush = FALSE;
31 ExAcquirePushLockShared(&CmpHiveListHeadLock);
32
33 /* Loop the hive list */
34 NextEntry = CmpHiveListHead.Flink;
35 while (NextEntry != &CmpHiveListHead)
36 {
37 /* Get the hive */
38 Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
39 if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))
40 {
41 /* Do the sync */
42 Status = HvSyncHive(&Hive->Hive);
43 if (!NT_SUCCESS(Status)) Result = FALSE;
44 }
45
46 /* Try the next entry */
47 NextEntry = NextEntry->Flink;
48 }
49
50 /* Release lock and return */
51 ExReleasePushLock(&CmpHiveListHeadLock);
52 return Result;
53 }
54
55 NTSTATUS
56 NTAPI
57 CmpSetValueKeyNew(IN PHHIVE Hive,
58 IN PCM_KEY_NODE Parent,
59 IN PUNICODE_STRING ValueName,
60 IN ULONG Index,
61 IN ULONG Type,
62 IN PVOID Data,
63 IN ULONG DataSize,
64 IN ULONG StorageType,
65 IN ULONG SmallData)
66 {
67 PCELL_DATA CellData;
68 HCELL_INDEX ValueCell;
69 NTSTATUS Status;
70
71 /* Check if we already have a value list */
72 if (Parent->ValueList.Count)
73 {
74 /* Then make sure it's valid and dirty it */
75 ASSERT(Parent->ValueList.List != HCELL_NIL);
76 HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
77 }
78
79 /* Allocate avalue cell */
80 ValueCell = HvAllocateCell(Hive,
81 FIELD_OFFSET(CM_KEY_VALUE, Name) +
82 CmpNameSize(Hive, ValueName),
83 StorageType,
84 HCELL_NIL);
85 if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
86
87 /* Get the actual data for it */
88 CellData = HvGetCell(Hive, ValueCell);
89 if (!CellData) ASSERT(FALSE);
90
91 /* Now we can release it, make sure it's also dirty */
92 HvReleaseCell(Hive, ValueCell);
93 ASSERT(HvIsCellDirty(Hive, ValueCell));
94
95 /* Set it up and copy the name */
96 CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
97 CellData->u.KeyValue.Flags = 0;
98 CellData->u.KeyValue.Type = Type;
99 CellData->u.KeyValue.NameLength = CmpCopyName(Hive,
100 CellData->u.KeyValue.Name,
101 ValueName);
102 if (CellData->u.KeyValue.NameLength < ValueName->Length)
103 {
104 /* This is a compressed name */
105 CellData->u.KeyValue.Flags = VALUE_COMP_NAME;
106 }
107
108 /* Check if this is a normal key */
109 if (DataSize > CM_KEY_VALUE_SMALL)
110 {
111 /* Build a data cell for it */
112 Status = CmpSetValueDataNew(Hive,
113 Data,
114 DataSize,
115 StorageType,
116 ValueCell,
117 &CellData->u.KeyValue.Data);
118 if (!NT_SUCCESS(Status))
119 {
120 /* We failed, free the cell */
121 HvFreeCell(Hive, ValueCell);
122 return Status;
123 }
124
125 /* Otherwise, set the data length, and make sure the data is dirty */
126 CellData->u.KeyValue.DataLength = DataSize;
127 ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data));
128 }
129 else
130 {
131 /* This is a small key, set the data directly inside */
132 CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
133 CellData->u.KeyValue.Data = SmallData;
134 }
135
136 /* Add this value cell to the child list */
137 Status = CmpAddValueToList(Hive,
138 ValueCell,
139 Index,
140 StorageType,
141 &Parent->ValueList);
142
143 /* If we failed, free the entire cell, including the data */
144 if (!NT_SUCCESS(Status)) CmpFreeValue(Hive, ValueCell);
145
146 /* Return Status */
147 return Status;
148 }
149
150 NTSTATUS
151 NTAPI
152 CmpSetValueKeyExisting(IN PHHIVE Hive,
153 IN HCELL_INDEX OldChild,
154 IN PCM_KEY_VALUE Value,
155 IN ULONG Type,
156 IN PVOID Data,
157 IN ULONG DataSize,
158 IN ULONG StorageType,
159 IN ULONG TempData)
160 {
161 HCELL_INDEX DataCell, NewCell;
162 PCELL_DATA CellData;
163 ULONG Length;
164 BOOLEAN WasSmall, IsSmall;
165
166 /* Mark the old child cell dirty */
167 HvMarkCellDirty(Hive, OldChild, FALSE);
168
169 /* See if this is a small or normal key */
170 WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength);
171
172 /* See if our new data can fit in a small key */
173 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
174
175 /* Big keys are unsupported */
176 ASSERT_VALUE_BIG(Hive, Length);
177 ASSERT_VALUE_BIG(Hive, DataSize);
178
179 /* Mark the old value dirty */
180 CmpMarkValueDataDirty(Hive, Value);
181
182 /* Check if we have a small key */
183 if (IsSmall)
184 {
185 /* Check if we had a normal key with some data in it */
186 if (!(WasSmall) && (Length > 0))
187 {
188 /* Free the previous data */
189 CmpFreeValueData(Hive, Value->Data, Length);
190 }
191
192 /* Write our data directly */
193 Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
194 Value->Data = TempData;
195 Value->Type = Type;
196 return STATUS_SUCCESS;
197 }
198 else
199 {
200 /* We have a normal key. Was the old cell also normal and had data? */
201 if (!(WasSmall) && (Length > 0))
202 {
203 /* Get the current data cell and actual data inside it */
204 DataCell = Value->Data;
205 ASSERT(DataCell != HCELL_NIL);
206 CellData = HvGetCell(Hive, DataCell);
207 if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
208
209 /* Immediately release the cell */
210 HvReleaseCell(Hive, DataCell);
211
212 /* Make sure that the data cell actually has a size */
213 ASSERT(HvGetCellSize(Hive, CellData) > 0);
214
215 /* Check if the previous data cell could fit our new data */
216 if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData)))
217 {
218 /* Re-use it then */
219 NewCell = DataCell;
220 }
221 else
222 {
223 /* Otherwise, re-allocate the current data cell */
224 NewCell = HvReallocateCell(Hive, DataCell, DataSize);
225 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
226 }
227 }
228 else
229 {
230 /* This was a small key, or a key with no data, allocate a cell */
231 NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
232 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
233 }
234
235 /* Now get the actual data for our data cell */
236 CellData = HvGetCell(Hive, NewCell);
237 if (!CellData) ASSERT(FALSE);
238
239 /* Release it immediately */
240 HvReleaseCell(Hive, NewCell);
241
242 /* Copy our data into the data cell's buffer, and set up the value */
243 RtlCopyMemory(CellData, Data, DataSize);
244 Value->Data = NewCell;
245 Value->DataLength = DataSize;
246 Value->Type = Type;
247
248 /* Return success */
249 ASSERT(HvIsCellDirty(Hive, NewCell));
250 return STATUS_SUCCESS;
251 }
252 }
253
254 NTSTATUS
255 NTAPI
256 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
257 IN PUNICODE_STRING ValueName,
258 IN ULONG Type,
259 IN PVOID Data,
260 IN ULONG DataLength)
261 {
262 PHHIVE Hive;
263 PCM_KEY_NODE Parent;
264 PCM_KEY_VALUE Value = NULL;
265 HCELL_INDEX CurrentChild, Cell;
266 NTSTATUS Status;
267 BOOLEAN Found, Result;
268 ULONG Count, ChildIndex, SmallData, Storage;
269
270 /* Acquire hive lock exclusively */
271 KeEnterCriticalRegion();
272 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
273
274 /* Get pointer to key cell */
275 Hive = Kcb->KeyHive;
276 Cell = Kcb->KeyCell;
277
278 /* Prepare to scan the key node */
279 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
280 Count = Parent->ValueList.Count;
281 Found = FALSE;
282 if (Count > 0)
283 {
284 /* Try to find the existing name */
285 Result = CmpFindNameInList(Hive,
286 &Parent->ValueList,
287 ValueName,
288 &ChildIndex,
289 &CurrentChild);
290 if (!Result)
291 {
292 /* Fail */
293 Status = STATUS_INSUFFICIENT_RESOURCES;
294 goto Quickie;
295 }
296
297 /* Check if we found something */
298 if (CurrentChild != HCELL_NIL)
299 {
300 /* Get its value */
301 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild);
302 if (!Value)
303 {
304 /* Fail */
305 Status = STATUS_INSUFFICIENT_RESOURCES;
306 goto Quickie;
307 }
308
309 /* Remember that we found it */
310 Found = TRUE;
311 }
312 }
313 else
314 {
315 /* No child list, we'll need to add it */
316 ChildIndex = 0;
317 }
318
319 /* Mark the cell dirty */
320 HvMarkCellDirty(Hive, Cell, FALSE);
321
322 /* Get the storage type */
323 Storage = HvGetCellType(Cell);
324
325 /* Check if this is small data */
326 SmallData = 0;
327 if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0))
328 {
329 /* Copy it */
330 RtlCopyMemory(&SmallData, Data, DataLength);
331 }
332
333 /* Check if we didn't find a matching key */
334 if (!Found)
335 {
336 /* Call the internal routine */
337 Status = CmpSetValueKeyNew(Hive,
338 Parent,
339 ValueName,
340 ChildIndex,
341 Type,
342 Data,
343 DataLength,
344 Storage,
345 SmallData);
346 }
347 else
348 {
349 /* Call the internal routine */
350 Status = CmpSetValueKeyExisting(Hive,
351 CurrentChild,
352 Value,
353 Type,
354 Data,
355 DataLength,
356 Storage,
357 SmallData);
358 }
359
360 /* Mark link key */
361 if ((Type == REG_LINK) &&
362 (_wcsicmp(ValueName->Buffer, L"SymbolicLinkValue") == 0))
363 {
364 Parent->Flags |= KEY_SYM_LINK;
365 }
366
367 /* Check for success */
368 Quickie:
369 if (NT_SUCCESS(Status))
370 {
371 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
372 if (Parent->MaxValueNameLen < ValueName->Length)
373 {
374 Parent->MaxValueNameLen = ValueName->Length;
375 Kcb->KcbMaxValueNameLen = ValueName->Length;
376 }
377
378 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
379 if (Parent->MaxValueDataLen < DataLength)
380 {
381 Parent->MaxValueDataLen = DataLength;
382 Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen;
383 }
384
385 /* Save the write time */
386 KeQuerySystemTime(&Parent->LastWriteTime);
387 KeQuerySystemTime(&Kcb->KcbLastWriteTime);
388 }
389
390 /* Release the lock */
391 ExReleaseResourceLite(&CmpRegistryLock);
392 KeLeaveCriticalRegion();
393 return Status;
394 }
395
396 NTSTATUS
397 NTAPI
398 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
399 IN UNICODE_STRING ValueName)
400 {
401 NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
402 PHHIVE Hive;
403 PCM_KEY_NODE Parent;
404 HCELL_INDEX ChildCell, Cell;
405 PCHILD_LIST ChildList;
406 PCM_KEY_VALUE Value = NULL;
407 ULONG ChildIndex;
408 BOOLEAN Result;
409
410 /* Acquire hive lock */
411 KeEnterCriticalRegion();
412 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
413
414 /* Get the hive and the cell index */
415 Hive = Kcb->KeyHive;
416 Cell = Kcb->KeyCell;
417
418 /* Get the parent key node */
419 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
420 if (!Parent)
421 {
422 /* Fail */
423 Status = STATUS_INSUFFICIENT_RESOURCES;
424 goto Quickie;
425 }
426
427 /* Get the value list and check if it has any entries */
428 ChildList = &Parent->ValueList;
429 if (ChildList->Count)
430 {
431 /* Try to find this value */
432 Result = CmpFindNameInList(Hive,
433 ChildList,
434 &ValueName,
435 &ChildIndex,
436 &ChildCell);
437 if (!Result)
438 {
439 /* Fail */
440 Status = STATUS_INSUFFICIENT_RESOURCES;
441 goto Quickie;
442 }
443
444 /* Value not found, return error */
445 if (ChildCell == HCELL_NIL) goto Quickie;
446
447 /* We found the value, mark all relevant cells dirty */
448 HvMarkCellDirty(Hive, Cell, FALSE);
449 HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE);
450 HvMarkCellDirty(Hive, ChildCell, FALSE);
451
452 /* Get the key value */
453 Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
454 if (!Value) ASSERT(FALSE);
455
456 /* Mark it and all related data as dirty */
457 CmpMarkValueDataDirty(Hive, Value);
458
459 /* Ssanity checks */
460 ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List));
461 ASSERT(HvIsCellDirty(Hive, ChildCell));
462
463 /* Remove the value from the child list */
464 Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList);
465 if(!NT_SUCCESS(Status)) goto Quickie;
466
467 /* Remove the value and its data itself */
468 if (!CmpFreeValue(Hive, ChildCell))
469 {
470 /* Failed to free the value, fail */
471 Status = STATUS_INSUFFICIENT_RESOURCES;
472 goto Quickie;
473 }
474
475 /* Set the last write time */
476 KeQuerySystemTime(&Parent->LastWriteTime);
477 KeQuerySystemTime(&Kcb->KcbLastWriteTime);
478
479 /* Sanity check */
480 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen);
481 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen);
482 ASSERT(HvIsCellDirty(Hive, Cell));
483
484 /* Check if the value list is empty now */
485 if (!Parent->ValueList.Count)
486 {
487 /* Then clear key node data */
488 Parent->MaxValueNameLen = 0;
489 Parent->MaxValueDataLen = 0;
490 Kcb->KcbMaxValueNameLen = 0;
491 Kcb->KcbMaxValueDataLen = 0;
492 }
493
494 /* Change default Status to success */
495 Status = STATUS_SUCCESS;
496 }
497
498 Quickie:
499 /* Release the parent cell, if any */
500 if (Parent) HvReleaseCell(Hive, Cell);
501
502 /* Check if we had a value */
503 if (Value)
504 {
505 /* Release the child cell */
506 ASSERT(ChildCell != HCELL_NIL);
507 HvReleaseCell(Hive, ChildCell);
508 }
509
510 /* Release hive lock */
511 ExReleaseResourceLite(&CmpRegistryLock);
512 KeLeaveCriticalRegion();
513 return Status;
514 }
515
516 NTSTATUS
517 NTAPI
518 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
519 IN UNICODE_STRING ValueName,
520 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
521 IN PVOID KeyValueInformation,
522 IN ULONG Length,
523 IN PULONG ResultLength)
524 {
525 NTSTATUS Status;
526 PCM_KEY_VALUE ValueData;
527 ULONG Index;
528 BOOLEAN ValueCached = FALSE;
529 PCM_CACHED_VALUE *CachedValue;
530 HCELL_INDEX CellToRelease;
531 VALUE_SEARCH_RETURN_TYPE Result;
532 PHHIVE Hive;
533 PAGED_CODE();
534
535 /* Acquire hive lock */
536 KeEnterCriticalRegion();
537 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
538
539 /* Get the hive */
540 Hive = Kcb->KeyHive;
541
542 /* Find the key value */
543 Result = CmpFindValueByNameFromCache(Kcb,
544 &ValueName,
545 &CachedValue,
546 &Index,
547 &ValueData,
548 &ValueCached,
549 &CellToRelease);
550 if (Result == SearchSuccess)
551 {
552 /* Sanity check */
553 ASSERT(ValueData != NULL);
554
555 /* Query the information requested */
556 Result = CmpQueryKeyValueData(Kcb,
557 CachedValue,
558 ValueData,
559 ValueCached,
560 KeyValueInformationClass,
561 KeyValueInformation,
562 Length,
563 ResultLength,
564 &Status);
565 }
566 else
567 {
568 /* Failed to find the value */
569 Status = STATUS_OBJECT_NAME_NOT_FOUND;
570 }
571
572 /* If we have a cell to release, do so */
573 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
574
575 /* Release hive lock */
576 ExReleaseResourceLite(&CmpRegistryLock);
577 KeLeaveCriticalRegion();
578 return Status;
579 }
580
581 NTSTATUS
582 NTAPI
583 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
584 IN ULONG Index,
585 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
586 IN PVOID KeyValueInformation,
587 IN ULONG Length,
588 IN PULONG ResultLength)
589 {
590 NTSTATUS Status;
591 PHHIVE Hive;
592 PCM_KEY_NODE Parent;
593 HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL;
594 VALUE_SEARCH_RETURN_TYPE Result;
595 BOOLEAN IndexIsCached, ValueIsCached = FALSE;
596 PCELL_DATA CellData;
597 PCM_CACHED_VALUE *CachedValue;
598 PCM_KEY_VALUE ValueData;
599 PAGED_CODE();
600
601 /* Acquire hive lock */
602 KeEnterCriticalRegion();
603 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
604
605 /* Get the hive and parent */
606 Hive = Kcb->KeyHive;
607 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
608 if (!Parent)
609 {
610 /* Fail */
611 Status = STATUS_INSUFFICIENT_RESOURCES;
612 goto Quickie;
613 }
614
615 /* Make sure the index is valid */
616 //if (Index >= Kcb->ValueCache.Count)
617 if (Index >= Parent->ValueList.Count)
618 {
619 /* Release the cell and fail */
620 HvReleaseCell(Hive, Kcb->KeyCell);
621 Status = STATUS_NO_MORE_ENTRIES;
622 goto Quickie;
623 }
624
625 /* Find the value list */
626 Result = CmpGetValueListFromCache(Kcb,
627 &CellData,
628 &IndexIsCached,
629 &CellToRelease);
630 if (Result != SearchSuccess)
631 {
632 /* Sanity check */
633 ASSERT(CellData == NULL);
634
635 /* Release the cell and fail */
636 Status = STATUS_INSUFFICIENT_RESOURCES;
637 goto Quickie;
638 }
639
640 /* Now get the key value */
641 Result = CmpGetValueKeyFromCache(Kcb,
642 CellData,
643 Index,
644 &CachedValue,
645 &ValueData,
646 IndexIsCached,
647 &ValueIsCached,
648 &CellToRelease2);
649 if (Result != SearchSuccess)
650 {
651 /* Sanity check */
652 ASSERT(CellToRelease2 == HCELL_NIL);
653
654 /* Release the cells and fail */
655 Status = STATUS_INSUFFICIENT_RESOURCES;
656 goto Quickie;
657 }
658
659 /* Query the information requested */
660 Result = CmpQueryKeyValueData(Kcb,
661 CachedValue,
662 ValueData,
663 ValueIsCached,
664 KeyValueInformationClass,
665 KeyValueInformation,
666 Length,
667 ResultLength,
668 &Status);
669
670 Quickie:
671 /* If we have a cell to release, do so */
672 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease);
673
674 /* Release the parent cell */
675 HvReleaseCell(Hive, Kcb->KeyCell);
676
677 /* If we have a cell to release, do so */
678 if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2);
679
680 /* Release hive lock */
681 ExReleaseResourceLite(&CmpRegistryLock);
682 KeLeaveCriticalRegion();
683 return Status;
684 }
685
686 NTSTATUS
687 NTAPI
688 CmpQueryKeyData(IN PHHIVE Hive,
689 IN PCM_KEY_NODE Node,
690 IN KEY_INFORMATION_CLASS KeyInformationClass,
691 IN OUT PVOID KeyInformation,
692 IN ULONG Length,
693 IN OUT PULONG ResultLength)
694 {
695 NTSTATUS Status;
696 ULONG Size, SizeLeft, MinimumSize;
697 PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation;
698 USHORT NameLength;
699
700 /* Check if the value is compressed */
701 if (Node->Flags & KEY_COMP_NAME)
702 {
703 /* Get the compressed name size */
704 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
705 }
706 else
707 {
708 /* Get the real size */
709 NameLength = Node->NameLength;
710 }
711
712 /* Check what kind of information is being requested */
713 switch (KeyInformationClass)
714 {
715 /* Basic information */
716 case KeyBasicInformation:
717
718 /* This is the size we need */
719 Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength;
720
721 /* And this is the minimum we can work with */
722 MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
723
724 /* Let the caller know and assume success */
725 *ResultLength = Size;
726 Status = STATUS_SUCCESS;
727
728 /* Check if the bufer we got is too small */
729 if (Length < MinimumSize)
730 {
731 /* Let the caller know and fail */
732 Status = STATUS_BUFFER_TOO_SMALL;
733 break;
734 }
735
736 /* Copy the basic information */
737 Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime;
738 Info->KeyBasicInformation.TitleIndex = 0;
739 Info->KeyBasicInformation.NameLength = NameLength;
740
741 /* Only the name is left */
742 SizeLeft = Length - MinimumSize;
743 Size = NameLength;
744
745 /* Check if we don't have enough space for the name */
746 if (SizeLeft < Size)
747 {
748 /* Truncate the name we'll return, and tell the caller */
749 Size = SizeLeft;
750 Status = STATUS_BUFFER_OVERFLOW;
751 }
752
753 /* Check if this is a compressed key */
754 if (Node->Flags & KEY_COMP_NAME)
755 {
756 /* Copy the compressed name */
757 CmpCopyCompressedName(Info->KeyBasicInformation.Name,
758 SizeLeft,
759 Node->Name,
760 Node->NameLength);
761 }
762 else
763 {
764 /* Otherwise, copy the raw name */
765 RtlCopyMemory(Info->KeyBasicInformation.Name,
766 Node->Name,
767 Size);
768 }
769 break;
770
771 /* Node information */
772 case KeyNodeInformation:
773
774 /* Calculate the size we need */
775 Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) +
776 NameLength +
777 Node->ClassLength;
778
779 /* And the minimum size we can support */
780 MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name);
781
782 /* Return the size to the caller and assume succes */
783 *ResultLength = Size;
784 Status = STATUS_SUCCESS;
785
786 /* Check if the caller's buffer is too small */
787 if (Length < MinimumSize)
788 {
789 /* Let them know, and fail */
790 Status = STATUS_BUFFER_TOO_SMALL;
791 break;
792 }
793
794 /* Copy the basic information */
795 Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime;
796 Info->KeyNodeInformation.TitleIndex = 0;
797 Info->KeyNodeInformation.ClassLength = Node->ClassLength;
798 Info->KeyNodeInformation.NameLength = NameLength;
799
800 /* Now the name is left */
801 SizeLeft = Length - MinimumSize;
802 Size = NameLength;
803
804 /* Check if the name can fit entirely */
805 if (SizeLeft < Size)
806 {
807 /* It can't, we'll have to truncate. Tell the caller */
808 Size = SizeLeft;
809 Status = STATUS_BUFFER_OVERFLOW;
810 }
811
812 /* Check if the key node name is compressed */
813 if (Node->Flags & KEY_COMP_NAME)
814 {
815 /* Copy the compressed name */
816 CmpCopyCompressedName(Info->KeyNodeInformation.Name,
817 SizeLeft,
818 Node->Name,
819 Node->NameLength);
820 }
821 else
822 {
823 /* It isn't, so copy the raw name */
824 RtlCopyMemory(Info->KeyNodeInformation.Name,
825 Node->Name,
826 Size);
827 }
828
829 /* Check if the node has a class */
830 if (Node->ClassLength > 0)
831 {
832 /* It does. We don't support these yet */
833 ASSERTMSG("Classes not supported\n", FALSE);
834 }
835 else
836 {
837 /* It doesn't, so set offset to -1, not 0! */
838 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
839 }
840 break;
841
842 /* Full information requsted */
843 case KeyFullInformation:
844
845 /* This is the size we need */
846 Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) +
847 Node->ClassLength;
848
849 /* This is what we can work with */
850 MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class);
851
852 /* Return it to caller and assume success */
853 *ResultLength = Size;
854 Status = STATUS_SUCCESS;
855
856 /* Check if the caller's buffer is to small */
857 if (Length < MinimumSize)
858 {
859 /* Let them know and fail */
860 Status = STATUS_BUFFER_TOO_SMALL;
861 break;
862 }
863
864 /* Now copy all the basic information */
865 Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime;
866 Info->KeyFullInformation.TitleIndex = 0;
867 Info->KeyFullInformation.ClassLength = Node->ClassLength;
868 Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] +
869 Node->SubKeyCounts[Volatile];
870 Info->KeyFullInformation.Values = Node->ValueList.Count;
871 Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen;
872 Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen;
873 Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen;
874 Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen;
875
876 /* Check if we have a class */
877 if (Node->ClassLength > 0)
878 {
879 /* We do, but we currently don't support this */
880 ASSERTMSG("Classes not supported\n", FALSE);
881 }
882 else
883 {
884 /* We don't have a class, so set offset to -1, not 0! */
885 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF;
886 }
887 break;
888
889 /* Any other class that got sent here is invalid! */
890 default:
891
892 /* Set failure code */
893 Status = STATUS_INVALID_PARAMETER;
894 break;
895 }
896
897 /* Return status */
898 return Status;
899 }
900
901 NTSTATUS
902 NTAPI
903 CmQueryKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
904 IN KEY_INFORMATION_CLASS KeyInformationClass,
905 IN PVOID KeyInformation,
906 IN ULONG Length,
907 IN PULONG ResultLength)
908 {
909 NTSTATUS Status;
910 PHHIVE Hive;
911 PCM_KEY_NODE Parent;
912
913 /* Acquire hive lock */
914 KeEnterCriticalRegion();
915 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
916
917 /* Get the hive and parent */
918 Hive = Kcb->KeyHive;
919 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
920 if (!Parent)
921 {
922 /* Fail */
923 Status = STATUS_INSUFFICIENT_RESOURCES;
924 goto Quickie;
925 }
926
927 /* Check what class we got */
928 switch (KeyInformationClass)
929 {
930 /* Typical information */
931 case KeyFullInformation:
932 case KeyBasicInformation:
933 case KeyNodeInformation:
934
935 /* Call the internal API */
936 Status = CmpQueryKeyData(Hive,
937 Parent,
938 KeyInformationClass,
939 KeyInformation,
940 Length,
941 ResultLength);
942 break;
943
944 /* Unsupported classes for now */
945 case KeyNameInformation:
946 case KeyCachedInformation:
947 case KeyFlagsInformation:
948
949 /* Print message and fail */
950 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
951 Status = STATUS_NOT_IMPLEMENTED;
952 break;
953
954 /* Illegal classes */
955 default:
956
957 /* Print message and fail */
958 DPRINT1("Unsupported class: %d!\n", KeyInformationClass);
959 Status = STATUS_INVALID_INFO_CLASS;
960 break;
961 }
962
963 Quickie:
964 /* Release hive lock */
965 ExReleaseResourceLite(&CmpRegistryLock);
966 KeLeaveCriticalRegion();
967 return Status;
968 }
969
970 NTSTATUS
971 NTAPI
972 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
973 IN ULONG Index,
974 IN KEY_INFORMATION_CLASS KeyInformationClass,
975 IN PVOID KeyInformation,
976 IN ULONG Length,
977 IN PULONG ResultLength)
978 {
979 NTSTATUS Status;
980 PHHIVE Hive;
981 PCM_KEY_NODE Parent, Child;
982 HCELL_INDEX ChildCell;
983
984 /* Acquire hive lock */
985 KeEnterCriticalRegion();
986 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
987
988 /* Get the hive and parent */
989 Hive = Kcb->KeyHive;
990 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell);
991 if (!Parent)
992 {
993 /* Fail */
994 Status = STATUS_INSUFFICIENT_RESOURCES;
995 goto Quickie;
996 }
997
998 /* Get the child cell */
999 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index);
1000
1001 /* Release the parent cell */
1002 HvReleaseCell(Hive, Kcb->KeyCell);
1003
1004 /* Check if we found the child */
1005 if (ChildCell == HCELL_NIL)
1006 {
1007 /* We didn't, fail */
1008 Status = STATUS_NO_MORE_ENTRIES;
1009 goto Quickie;
1010 }
1011
1012 /* Now get the actual child node */
1013 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell);
1014 if (!Child)
1015 {
1016 /* Fail */
1017 Status = STATUS_INSUFFICIENT_RESOURCES;
1018 goto Quickie;
1019 }
1020
1021 /* Query the data requested */
1022 Status = CmpQueryKeyData(Hive,
1023 Child,
1024 KeyInformationClass,
1025 KeyInformation,
1026 Length,
1027 ResultLength);
1028
1029 Quickie:
1030 /* Release hive lock */
1031 ExReleaseResourceLite(&CmpRegistryLock);
1032 KeLeaveCriticalRegion();
1033 return Status;
1034 }
1035
1036 NTSTATUS
1037 NTAPI
1038 CmDeleteKey(IN PCM_KEY_CONTROL_BLOCK Kcb)
1039 {
1040 NTSTATUS Status;
1041 PHHIVE Hive;
1042 PCM_KEY_NODE Node, Parent;
1043 HCELL_INDEX Cell, ParentCell;
1044
1045 /* Acquire hive lock */
1046 KeEnterCriticalRegion();
1047 ExAcquireResourceExclusiveLite(&CmpRegistryLock, TRUE);
1048
1049 /* Get the hive and node */
1050 Hive = Kcb->KeyHive;
1051 Cell = Kcb->KeyCell;
1052
1053 /* Get the key node */
1054 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
1055 if (!Node)
1056 {
1057 /* Fail */
1058 Status = STATUS_INSUFFICIENT_RESOURCES;
1059 goto Quickie;
1060 }
1061
1062 /* Check if we have no parent */
1063 if (!Node->Parent)
1064 {
1065 /* This is an attempt to delete \Registry itself! */
1066 Status = STATUS_CANNOT_DELETE;
1067 goto Quickie;
1068 }
1069
1070 /* Check if we're already being deleted */
1071 if (Kcb->Delete)
1072 {
1073 /* Don't do it twice */
1074 Status = STATUS_SUCCESS;
1075 goto Quickie;
1076 }
1077
1078 /* Sanity check */
1079 ASSERT(Node->Flags == Kcb->Flags);
1080
1081 /* Check if we don't have any children */
1082 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]))
1083 {
1084 /* Get the parent and free the cell */
1085 ParentCell = Node->Parent;
1086 Status = CmpFreeKeyByCell(Hive, Cell, TRUE);
1087 if (NT_SUCCESS(Status))
1088 {
1089 /* Get the parent node */
1090 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell);
1091 if (Parent)
1092 {
1093 /* Update the maximum name length */
1094 Kcb->ParentKcb->KcbMaxNameLen = Parent->MaxNameLen;
1095
1096 /* Make sure we're dirty */
1097 ASSERT(HvIsCellDirty(Hive, ParentCell));
1098
1099 /* Update the write time */
1100 KeQuerySystemTime(&Parent->LastWriteTime);
1101 KeQuerySystemTime(&Kcb->ParentKcb->KcbLastWriteTime);
1102
1103 /* Release the cell */
1104 HvReleaseCell(Hive, ParentCell);
1105 }
1106
1107 /* Set the KCB in delete mode and remove it */
1108 Kcb->Delete = TRUE;
1109 CmpRemoveKeyControlBlock(Kcb);
1110
1111 /* Clear the cell */
1112 Kcb->KeyCell = HCELL_NIL;
1113 }
1114 }
1115 else
1116 {
1117 /* Fail */
1118 Status = STATUS_CANNOT_DELETE;
1119 }
1120
1121 /* Flush the registry */
1122 CmpLazyFlush();
1123
1124 Quickie:
1125 /* Release the cell */
1126 HvReleaseCell(Hive, Cell);
1127
1128 /* Release hive lock */
1129 ExReleaseResourceLite(&CmpRegistryLock);
1130 KeLeaveCriticalRegion();
1131 return Status;
1132 }
1133
1134 NTSTATUS
1135 NTAPI
1136 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb,
1137 IN BOOLEAN ExclusiveLock)
1138 {
1139 PCMHIVE CmHive;
1140 NTSTATUS Status = STATUS_SUCCESS;
1141 PHHIVE Hive;
1142
1143 /* Get the hives */
1144 Hive = Kcb->KeyHive;
1145 CmHive = (PCMHIVE)Hive;
1146
1147 /* Check if this is the master hive */
1148 if (CmHive == CmiVolatileHive)
1149 {
1150 /* Flush all the hives instead */
1151 CmpDoFlushAll(FALSE);
1152 }
1153 else
1154 {
1155 /* Flush only this hive */
1156 if (!HvSyncHive(Hive))
1157 {
1158 /* Fail */
1159 Status = STATUS_REGISTRY_IO_FAILED;
1160 }
1161 }
1162
1163 /* Return the status */
1164 return Status;
1165 }
1166
1167 NTSTATUS
1168 NTAPI
1169 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey,
1170 IN POBJECT_ATTRIBUTES SourceFile,
1171 IN ULONG Flags,
1172 IN PCM_KEY_BODY KeyBody)
1173 {
1174 SECURITY_QUALITY_OF_SERVICE ServiceQos;
1175 SECURITY_CLIENT_CONTEXT ClientSecurityContext;
1176 HANDLE KeyHandle;
1177 BOOLEAN Allocate = TRUE;
1178 PCMHIVE CmHive;
1179 NTSTATUS Status;
1180
1181 /* Check if we have a trust key */
1182 if (KeyBody)
1183 {
1184 /* Fail */
1185 DPRINT1("Trusted classes not yet supported\n");
1186 return STATUS_NOT_IMPLEMENTED;
1187 }
1188
1189 /* Build a service QoS for a security context */
1190 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
1191 ServiceQos.ImpersonationLevel = SecurityImpersonation;
1192 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
1193 ServiceQos.EffectiveOnly = TRUE;
1194 Status = SeCreateClientSecurity(PsGetCurrentThread(),
1195 &ServiceQos,
1196 FALSE,
1197 &ClientSecurityContext);
1198 if (!NT_SUCCESS(Status))
1199 {
1200 /* Fail */
1201 DPRINT1("Security context failed\n");
1202 return Status;
1203 }
1204
1205 /* Open the target key */
1206 Status = ZwOpenKey(&KeyHandle, KEY_READ, TargetKey);
1207 if (!NT_SUCCESS(Status)) KeyHandle = NULL;
1208
1209 /* Open the hive */
1210 Status = CmpCmdHiveOpen(SourceFile,
1211 &ClientSecurityContext,
1212 &Allocate,
1213 &CmHive,
1214 0);
1215
1216 /* Get rid of the security context */
1217 SeDeleteClientSecurity(&ClientSecurityContext);
1218
1219 /* See if we failed */
1220 if (!NT_SUCCESS(Status))
1221 {
1222 /* See if the target already existed */
1223 if (KeyHandle)
1224 {
1225 /* Lock the registry */
1226 CmpLockRegistryExclusive();
1227
1228 /* FIXME: Check if we are already loaded */
1229
1230 /* Release the registry */
1231 CmpUnlockRegistry();
1232 }
1233
1234 /* Close the key handle if we had one */
1235 if (KeyHandle) ZwClose(KeyHandle);
1236 DPRINT1("Failed: %lx\n", Status);
1237 return Status;
1238 }
1239
1240 /* Lock the registry shared */
1241 //CmpLockRegistry();
1242
1243 /* Lock the hive to this thread */
1244 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING;
1245 CmHive->CreatorOwner = KeGetCurrentThread();
1246
1247 /* Set flag */
1248 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
1249
1250 /* Link the hive */
1251 Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
1252 TargetKey->RootDirectory,
1253 CmHive,
1254 FALSE,
1255 TargetKey->SecurityDescriptor);
1256 if (NT_SUCCESS(Status))
1257 {
1258 /* FIXME: Add to HiveList key */
1259
1260 /* Sync the hive if necessary */
1261 if (Allocate)
1262 {
1263 /* Sync it */
1264 HvSyncHive(&CmHive->Hive);
1265 }
1266
1267 /* Release the hive */
1268 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING;
1269 CmHive->CreatorOwner = NULL;
1270 }
1271 else
1272 {
1273 /* FIXME: TODO */
1274
1275 }
1276
1277 /* Unlock the registry */
1278 //CmpUnlockRegistry();
1279
1280 /* Close handle and return */
1281 if (KeyHandle) ZwClose(KeyHandle);
1282 return Status;
1283 }