[NTOS] Limit the spam from NtNotifyChangeMultipleKeys.
[reactos.git] / ntoskrnl / config / ntapi.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/ntapi.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 BOOLEAN CmBootAcceptFirstTime = TRUE;
17 BOOLEAN CmFirstTime = TRUE;
18 extern ULONG InitSafeBootMode;
19
20 /* FUNCTIONS *****************************************************************/
21
22 NTSTATUS
23 NTAPI
24 NtCreateKey(OUT PHANDLE KeyHandle,
25 IN ACCESS_MASK DesiredAccess,
26 IN POBJECT_ATTRIBUTES ObjectAttributes,
27 IN ULONG TitleIndex,
28 IN PUNICODE_STRING Class OPTIONAL,
29 IN ULONG CreateOptions,
30 OUT PULONG Disposition OPTIONAL)
31 {
32 NTSTATUS Status;
33 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
34 CM_PARSE_CONTEXT ParseContext = {0};
35 HANDLE Handle;
36 PAGED_CODE();
37
38 DPRINT("NtCreateKey(Path: %wZ, Root %x, Access: %x, CreateOptions %x)\n",
39 ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory,
40 DesiredAccess, CreateOptions);
41
42 /* Ignore the WOW64 flag, it's not valid in the kernel */
43 DesiredAccess &= ~KEY_WOW64_RES;
44
45 /* Check for user-mode caller */
46 if (PreviousMode != KernelMode)
47 {
48 /* Prepare to probe parameters */
49 _SEH2_TRY
50 {
51 /* Check if we have a class */
52 if (Class)
53 {
54 /* Probe it */
55 ParseContext.Class = ProbeForReadUnicodeString(Class);
56 ProbeForRead(ParseContext.Class.Buffer,
57 ParseContext.Class.Length,
58 sizeof(WCHAR));
59 }
60
61 /* Probe the key handle */
62 ProbeForWriteHandle(KeyHandle);
63 *KeyHandle = NULL;
64
65 /* Probe object attributes */
66 ProbeForRead(ObjectAttributes,
67 sizeof(OBJECT_ATTRIBUTES),
68 sizeof(ULONG));
69
70 if (Disposition)
71 ProbeForWriteUlong(Disposition);
72 }
73 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
74 {
75 /* Return the exception code */
76 _SEH2_YIELD(return _SEH2_GetExceptionCode());
77 }
78 _SEH2_END;
79 }
80 else
81 {
82 /* Save the class directly */
83 if (Class) ParseContext.Class = *Class;
84 }
85
86 /* Setup the parse context */
87 ParseContext.CreateOperation = TRUE;
88 ParseContext.CreateOptions = CreateOptions;
89
90 /* Do the create */
91 Status = ObOpenObjectByName(ObjectAttributes,
92 CmpKeyObjectType,
93 PreviousMode,
94 NULL,
95 DesiredAccess,
96 &ParseContext,
97 &Handle);
98
99 _SEH2_TRY
100 {
101 /* Return data to user */
102 if (NT_SUCCESS(Status)) *KeyHandle = Handle;
103 if (Disposition) *Disposition = ParseContext.Disposition;
104 }
105 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
106 {
107 /* Get the status */
108 Status = _SEH2_GetExceptionCode();
109 }
110 _SEH2_END;
111
112 DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
113
114 /* Return status */
115 return Status;
116 }
117
118 NTSTATUS
119 NTAPI
120 NtOpenKey(OUT PHANDLE KeyHandle,
121 IN ACCESS_MASK DesiredAccess,
122 IN POBJECT_ATTRIBUTES ObjectAttributes)
123 {
124 CM_PARSE_CONTEXT ParseContext = {0};
125 HANDLE Handle;
126 NTSTATUS Status;
127 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
128 PAGED_CODE();
129 DPRINT("NtOpenKey(Path: %wZ, Root %x, Access: %x)\n",
130 ObjectAttributes->ObjectName, ObjectAttributes->RootDirectory, DesiredAccess);
131
132 /* Ignore the WOW64 flag, it's not valid in the kernel */
133 DesiredAccess &= ~KEY_WOW64_RES;
134
135 /* Check for user-mode caller */
136 if (PreviousMode != KernelMode)
137 {
138 /* Prepare to probe parameters */
139 _SEH2_TRY
140 {
141 /* Probe the key handle */
142 ProbeForWriteHandle(KeyHandle);
143 *KeyHandle = NULL;
144
145 /* Probe object attributes */
146 ProbeForRead(ObjectAttributes,
147 sizeof(OBJECT_ATTRIBUTES),
148 sizeof(ULONG));
149 }
150 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
151 {
152 /* Return the exception code */
153 _SEH2_YIELD(return _SEH2_GetExceptionCode());
154 }
155 _SEH2_END;
156 }
157
158 /* Just let the object manager handle this */
159 Status = ObOpenObjectByName(ObjectAttributes,
160 CmpKeyObjectType,
161 ExGetPreviousMode(),
162 NULL,
163 DesiredAccess,
164 &ParseContext,
165 &Handle);
166
167 /* Only do this if we succeeded */
168 if (NT_SUCCESS(Status))
169 {
170 _SEH2_TRY
171 {
172 /* Return the handle to caller */
173 *KeyHandle = Handle;
174 }
175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
176 {
177 /* Get the status */
178 Status = _SEH2_GetExceptionCode();
179 }
180 _SEH2_END;
181 }
182
183 DPRINT("Returning handle %x, Status %x.\n", Handle, Status);
184
185 /* Return status */
186 return Status;
187 }
188
189
190 NTSTATUS
191 NTAPI
192 NtDeleteKey(IN HANDLE KeyHandle)
193 {
194 PCM_KEY_BODY KeyObject;
195 NTSTATUS Status;
196 REG_DELETE_KEY_INFORMATION DeleteKeyInfo;
197 REG_POST_OPERATION_INFORMATION PostOperationInfo;
198 PAGED_CODE();
199 DPRINT("NtDeleteKey(KH 0x%p)\n", KeyHandle);
200
201 /* Verify that the handle is valid and is a registry key */
202 Status = ObReferenceObjectByHandle(KeyHandle,
203 DELETE,
204 CmpKeyObjectType,
205 ExGetPreviousMode(),
206 (PVOID*)&KeyObject,
207 NULL);
208 if (!NT_SUCCESS(Status)) return Status;
209
210 /* Setup the callback */
211 PostOperationInfo.Object = (PVOID)KeyObject;
212 DeleteKeyInfo.Object = (PVOID)KeyObject;
213 Status = CmiCallRegisteredCallbacks(RegNtPreDeleteKey, &DeleteKeyInfo);
214 if (NT_SUCCESS(Status))
215 {
216 /* Check if we are read-only */
217 if ((KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY) ||
218 (KeyObject->KeyControlBlock->ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY))
219 {
220 /* Fail */
221 Status = STATUS_ACCESS_DENIED;
222 }
223 else
224 {
225 /* Call the internal API */
226 Status = CmDeleteKey(KeyObject);
227 }
228
229 /* Do post callback */
230 PostOperationInfo.Status = Status;
231 CmiCallRegisteredCallbacks(RegNtPostDeleteKey, &PostOperationInfo);
232 }
233
234 /* Dereference the object */
235 ObDereferenceObject(KeyObject);
236 return Status;
237 }
238
239 NTSTATUS
240 NTAPI
241 NtEnumerateKey(IN HANDLE KeyHandle,
242 IN ULONG Index,
243 IN KEY_INFORMATION_CLASS KeyInformationClass,
244 OUT PVOID KeyInformation,
245 IN ULONG Length,
246 OUT PULONG ResultLength)
247 {
248 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
249 NTSTATUS Status;
250 PCM_KEY_BODY KeyObject;
251 REG_ENUMERATE_KEY_INFORMATION EnumerateKeyInfo;
252 REG_POST_OPERATION_INFORMATION PostOperationInfo;
253 PAGED_CODE();
254 DPRINT("NtEnumerateKey() KH 0x%p, Index 0x%x, KIC %d, Length %lu\n",
255 KeyHandle, Index, KeyInformationClass, Length);
256
257 /* Reject classes we don't know about */
258 if ((KeyInformationClass != KeyBasicInformation) &&
259 (KeyInformationClass != KeyNodeInformation) &&
260 (KeyInformationClass != KeyFullInformation))
261 {
262 /* Fail */
263 return STATUS_INVALID_PARAMETER;
264 }
265
266 /* Verify that the handle is valid and is a registry key */
267 Status = ObReferenceObjectByHandle(KeyHandle,
268 KEY_ENUMERATE_SUB_KEYS,
269 CmpKeyObjectType,
270 ExGetPreviousMode(),
271 (PVOID*)&KeyObject,
272 NULL);
273 if (!NT_SUCCESS(Status)) return Status;
274
275 if (PreviousMode != KernelMode)
276 {
277 _SEH2_TRY
278 {
279 ProbeForWriteUlong(ResultLength);
280 ProbeForWrite(KeyInformation,
281 Length,
282 sizeof(ULONG));
283 }
284 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
285 {
286 /* Dereference and return status */
287 ObDereferenceObject(KeyObject);
288 _SEH2_YIELD(return _SEH2_GetExceptionCode());
289 }
290 _SEH2_END;
291 }
292
293 /* Setup the callback */
294 PostOperationInfo.Object = (PVOID)KeyObject;
295 EnumerateKeyInfo.Object = (PVOID)KeyObject;
296 EnumerateKeyInfo.Index = Index;
297 EnumerateKeyInfo.KeyInformationClass = KeyInformationClass;
298 EnumerateKeyInfo.Length = Length;
299 EnumerateKeyInfo.ResultLength = ResultLength;
300
301 /* Do the callback */
302 Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateKey, &EnumerateKeyInfo);
303 if (NT_SUCCESS(Status))
304 {
305 /* Call the internal API */
306 Status = CmEnumerateKey(KeyObject->KeyControlBlock,
307 Index,
308 KeyInformationClass,
309 KeyInformation,
310 Length,
311 ResultLength);
312
313 /* Do the post callback */
314 PostOperationInfo.Status = Status;
315 CmiCallRegisteredCallbacks(RegNtPostEnumerateKey, &PostOperationInfo);
316 }
317
318 /* Dereference and return status */
319 ObDereferenceObject(KeyObject);
320 DPRINT("Returning status %x.\n", Status);
321 return Status;
322 }
323
324 NTSTATUS
325 NTAPI
326 NtEnumerateValueKey(IN HANDLE KeyHandle,
327 IN ULONG Index,
328 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
329 OUT PVOID KeyValueInformation,
330 IN ULONG Length,
331 OUT PULONG ResultLength)
332 {
333 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
334 NTSTATUS Status;
335 PCM_KEY_BODY KeyObject;
336 REG_ENUMERATE_VALUE_KEY_INFORMATION EnumerateValueKeyInfo;
337 REG_POST_OPERATION_INFORMATION PostOperationInfo;
338 PAGED_CODE();
339 DPRINT("NtEnumerateValueKey() KH 0x%p, Index 0x%x, KVIC %d, Length %lu\n",
340 KeyHandle, Index, KeyValueInformationClass, Length);
341
342 /* Reject classes we don't know about */
343 if ((KeyValueInformationClass != KeyValueBasicInformation) &&
344 (KeyValueInformationClass != KeyValueFullInformation) &&
345 (KeyValueInformationClass != KeyValuePartialInformation))
346 {
347 /* Fail */
348 return STATUS_INVALID_PARAMETER;
349 }
350
351 /* Verify that the handle is valid and is a registry key */
352 Status = ObReferenceObjectByHandle(KeyHandle,
353 KEY_QUERY_VALUE,
354 CmpKeyObjectType,
355 ExGetPreviousMode(),
356 (PVOID*)&KeyObject,
357 NULL);
358 if (!NT_SUCCESS(Status)) return Status;
359
360 if (PreviousMode != KernelMode)
361 {
362 _SEH2_TRY
363 {
364 ProbeForWriteUlong(ResultLength);
365 ProbeForWrite(KeyValueInformation,
366 Length,
367 sizeof(ULONG));
368 }
369 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
370 {
371 /* Dereference and return status */
372 ObDereferenceObject(KeyObject);
373 _SEH2_YIELD(return _SEH2_GetExceptionCode());
374 }
375 _SEH2_END;
376 }
377
378 /* Setup the callback */
379 PostOperationInfo.Object = (PVOID)KeyObject;
380 EnumerateValueKeyInfo.Object = (PVOID)KeyObject;
381 EnumerateValueKeyInfo.Index = Index;
382 EnumerateValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
383 EnumerateValueKeyInfo.KeyValueInformation = KeyValueInformation;
384 EnumerateValueKeyInfo.Length = Length;
385 EnumerateValueKeyInfo.ResultLength = ResultLength;
386
387 /* Do the callback */
388 Status = CmiCallRegisteredCallbacks(RegNtPreEnumerateValueKey,
389 &EnumerateValueKeyInfo);
390 if (NT_SUCCESS(Status))
391 {
392 /* Call the internal API */
393 Status = CmEnumerateValueKey(KeyObject->KeyControlBlock,
394 Index,
395 KeyValueInformationClass,
396 KeyValueInformation,
397 Length,
398 ResultLength);
399
400 /* Do the post callback */
401 PostOperationInfo.Status = Status;
402 CmiCallRegisteredCallbacks(RegNtPostEnumerateValueKey, &PostOperationInfo);
403 }
404
405 ObDereferenceObject(KeyObject);
406 return Status;
407 }
408
409 NTSTATUS
410 NTAPI
411 NtQueryKey(IN HANDLE KeyHandle,
412 IN KEY_INFORMATION_CLASS KeyInformationClass,
413 OUT PVOID KeyInformation,
414 IN ULONG Length,
415 OUT PULONG ResultLength)
416 {
417 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
418 NTSTATUS Status;
419 PCM_KEY_BODY KeyObject;
420 REG_QUERY_KEY_INFORMATION QueryKeyInfo;
421 REG_POST_OPERATION_INFORMATION PostOperationInfo;
422 OBJECT_HANDLE_INFORMATION HandleInfo;
423 PAGED_CODE();
424 DPRINT("NtQueryKey() KH 0x%p, KIC %d, Length %lu\n",
425 KeyHandle, KeyInformationClass, Length);
426
427 /* Reject invalid classes */
428 if ((KeyInformationClass != KeyBasicInformation) &&
429 (KeyInformationClass != KeyNodeInformation) &&
430 (KeyInformationClass != KeyFullInformation) &&
431 (KeyInformationClass != KeyNameInformation) &&
432 (KeyInformationClass != KeyCachedInformation) &&
433 (KeyInformationClass != KeyFlagsInformation))
434 {
435 /* Fail */
436 return STATUS_INVALID_PARAMETER;
437 }
438
439 /* Check if just the name is required */
440 if (KeyInformationClass == KeyNameInformation)
441 {
442 /* Ignore access level */
443 Status = ObReferenceObjectByHandle(KeyHandle,
444 0,
445 CmpKeyObjectType,
446 ExGetPreviousMode(),
447 (PVOID*)&KeyObject,
448 &HandleInfo);
449 if (NT_SUCCESS(Status))
450 {
451 /* At least a single bit of access is required */
452 if (!HandleInfo.GrantedAccess)
453 {
454 /* No such luck */
455 ObDereferenceObject(KeyObject);
456 Status = STATUS_ACCESS_DENIED;
457 }
458 }
459 }
460 else
461 {
462 /* Get a reference */
463 Status = ObReferenceObjectByHandle(KeyHandle,
464 KEY_QUERY_VALUE,
465 CmpKeyObjectType,
466 ExGetPreviousMode(),
467 (PVOID*)&KeyObject,
468 NULL);
469 }
470
471 /* Quit on failure */
472 if (!NT_SUCCESS(Status)) return Status;
473
474 if (PreviousMode != KernelMode)
475 {
476 _SEH2_TRY
477 {
478 ProbeForWriteUlong(ResultLength);
479 ProbeForWrite(KeyInformation,
480 Length,
481 sizeof(ULONG));
482 }
483 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
484 {
485 /* Dereference and return status */
486 ObDereferenceObject(KeyObject);
487 _SEH2_YIELD(return _SEH2_GetExceptionCode());
488 }
489 _SEH2_END;
490 }
491
492 /* Setup the callback */
493 PostOperationInfo.Object = (PVOID)KeyObject;
494 QueryKeyInfo.Object = (PVOID)KeyObject;
495 QueryKeyInfo.KeyInformationClass = KeyInformationClass;
496 QueryKeyInfo.KeyInformation = KeyInformation;
497 QueryKeyInfo.Length = Length;
498 QueryKeyInfo.ResultLength = ResultLength;
499
500 /* Do the callback */
501 Status = CmiCallRegisteredCallbacks(RegNtPreQueryKey, &QueryKeyInfo);
502 if (NT_SUCCESS(Status))
503 {
504 /* Call the internal API */
505 Status = CmQueryKey(KeyObject->KeyControlBlock,
506 KeyInformationClass,
507 KeyInformation,
508 Length,
509 ResultLength);
510
511 /* Do the post callback */
512 PostOperationInfo.Status = Status;
513 CmiCallRegisteredCallbacks(RegNtPostQueryKey, &PostOperationInfo);
514 }
515
516 /* Dereference and return status */
517 ObDereferenceObject(KeyObject);
518 return Status;
519 }
520
521 NTSTATUS
522 NTAPI
523 NtQueryValueKey(IN HANDLE KeyHandle,
524 IN PUNICODE_STRING ValueName,
525 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
526 OUT PVOID KeyValueInformation,
527 IN ULONG Length,
528 OUT PULONG ResultLength)
529 {
530 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
531 NTSTATUS Status;
532 PCM_KEY_BODY KeyObject;
533 REG_QUERY_VALUE_KEY_INFORMATION QueryValueKeyInfo;
534 REG_POST_OPERATION_INFORMATION PostOperationInfo;
535 UNICODE_STRING ValueNameCopy = *ValueName;
536 PAGED_CODE();
537 DPRINT("NtQueryValueKey() KH 0x%p, VN '%wZ', KVIC %d, Length %lu\n",
538 KeyHandle, ValueName, KeyValueInformationClass, Length);
539
540 /* Verify that the handle is valid and is a registry key */
541 Status = ObReferenceObjectByHandle(KeyHandle,
542 KEY_QUERY_VALUE,
543 CmpKeyObjectType,
544 ExGetPreviousMode(),
545 (PVOID*)&KeyObject,
546 NULL);
547 if (!NT_SUCCESS(Status)) return Status;
548
549 if (PreviousMode != KernelMode)
550 {
551 _SEH2_TRY
552 {
553 ProbeForWriteUlong(ResultLength);
554 ProbeForWrite(KeyValueInformation,
555 Length,
556 sizeof(ULONG));
557 }
558 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
559 {
560 /* Dereference and return status */
561 ObDereferenceObject(KeyObject);
562 _SEH2_YIELD(return _SEH2_GetExceptionCode());
563 }
564 _SEH2_END;
565 }
566
567 /* Make sure the name is aligned properly */
568 if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
569 {
570 /* It isn't, so we'll fail */
571 ObDereferenceObject(KeyObject);
572 return STATUS_INVALID_PARAMETER;
573 }
574 else
575 {
576 /* Ignore any null characters at the end */
577 while ((ValueNameCopy.Length) &&
578 !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
579 {
580 /* Skip it */
581 ValueNameCopy.Length -= sizeof(WCHAR);
582 }
583 }
584
585 /* Setup the callback */
586 PostOperationInfo.Object = (PVOID)KeyObject;
587 QueryValueKeyInfo.Object = (PVOID)KeyObject;
588 QueryValueKeyInfo.ValueName = &ValueNameCopy;
589 QueryValueKeyInfo.KeyValueInformationClass = KeyValueInformationClass;
590 QueryValueKeyInfo.Length = Length;
591 QueryValueKeyInfo.ResultLength = ResultLength;
592
593 /* Do the callback */
594 Status = CmiCallRegisteredCallbacks(RegNtPreQueryValueKey, &QueryValueKeyInfo);
595 if (NT_SUCCESS(Status))
596 {
597 /* Call the internal API */
598 Status = CmQueryValueKey(KeyObject->KeyControlBlock,
599 ValueNameCopy,
600 KeyValueInformationClass,
601 KeyValueInformation,
602 Length,
603 ResultLength);
604
605 /* Do the post callback */
606 PostOperationInfo.Status = Status;
607 CmiCallRegisteredCallbacks(RegNtPostQueryValueKey, &PostOperationInfo);
608 }
609
610 /* Dereference and return status */
611 ObDereferenceObject(KeyObject);
612 return Status;
613 }
614
615 NTSTATUS
616 NTAPI
617 NtSetValueKey(IN HANDLE KeyHandle,
618 IN PUNICODE_STRING ValueName,
619 IN ULONG TitleIndex,
620 IN ULONG Type,
621 IN PVOID Data,
622 IN ULONG DataSize)
623 {
624 NTSTATUS Status = STATUS_SUCCESS;
625 PCM_KEY_BODY KeyObject = NULL;
626 REG_SET_VALUE_KEY_INFORMATION SetValueKeyInfo;
627 REG_POST_OPERATION_INFORMATION PostOperationInfo;
628 UNICODE_STRING ValueNameCopy;
629 KPROCESSOR_MODE PreviousMode;
630
631 PAGED_CODE();
632
633 PreviousMode = ExGetPreviousMode();
634
635 if (!DataSize)
636 Data = NULL;
637
638 /* Probe and copy the data */
639 if ((PreviousMode != KernelMode) && (DataSize != 0))
640 {
641 PVOID DataCopy = ExAllocatePoolWithTag(PagedPool, DataSize, TAG_CM);
642 if (!DataCopy)
643 return STATUS_INSUFFICIENT_RESOURCES;
644 _SEH2_TRY
645 {
646 ProbeForRead(Data, DataSize, 1);
647 RtlCopyMemory(DataCopy, Data, DataSize);
648 }
649 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
650 {
651 Status = _SEH2_GetExceptionCode();
652 }
653 _SEH2_END;
654
655 if (!NT_SUCCESS(Status))
656 {
657 ExFreePoolWithTag(DataCopy, TAG_CM);
658 return Status;
659 }
660 Data = DataCopy;
661 }
662
663 /* Capture the string */
664 Status = ProbeAndCaptureUnicodeString(&ValueNameCopy, PreviousMode, ValueName);
665 if (!NT_SUCCESS(Status))
666 goto end;
667
668 DPRINT("NtSetValueKey() KH 0x%p, VN '%wZ', TI %x, T %lu, DS %lu\n",
669 KeyHandle, &ValueNameCopy, TitleIndex, Type, DataSize);
670
671 /* Verify that the handle is valid and is a registry key */
672 Status = ObReferenceObjectByHandle(KeyHandle,
673 KEY_SET_VALUE,
674 CmpKeyObjectType,
675 ExGetPreviousMode(),
676 (PVOID*)&KeyObject,
677 NULL);
678 if (!NT_SUCCESS(Status))
679 goto end;
680
681 /* Make sure the name is aligned, not too long, and the data under 4GB */
682 if ( (ValueNameCopy.Length > 32767) ||
683 ((ValueNameCopy.Length & (sizeof(WCHAR) - 1))) ||
684 (DataSize > 0x80000000))
685 {
686 /* Fail */
687 Status = STATUS_INVALID_PARAMETER;
688 goto end;
689 }
690
691 /* Ignore any null characters at the end */
692 while ((ValueNameCopy.Length) &&
693 !(ValueNameCopy.Buffer[ValueNameCopy.Length / sizeof(WCHAR) - 1]))
694 {
695 /* Skip it */
696 ValueNameCopy.Length -= sizeof(WCHAR);
697 }
698
699 /* Don't touch read-only keys */
700 if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
701 {
702 /* Fail */
703 Status = STATUS_ACCESS_DENIED;
704 goto end;
705 }
706
707 /* Setup callback */
708 PostOperationInfo.Object = (PVOID)KeyObject;
709 SetValueKeyInfo.Object = (PVOID)KeyObject;
710 SetValueKeyInfo.ValueName = &ValueNameCopy;
711 SetValueKeyInfo.TitleIndex = TitleIndex;
712 SetValueKeyInfo.Type = Type;
713 SetValueKeyInfo.Data = Data;
714 SetValueKeyInfo.DataSize = DataSize;
715
716 /* Do the callback */
717 Status = CmiCallRegisteredCallbacks(RegNtPreSetValueKey, &SetValueKeyInfo);
718 if (NT_SUCCESS(Status))
719 {
720 /* Call the internal API */
721 Status = CmSetValueKey(KeyObject->KeyControlBlock,
722 &ValueNameCopy,
723 Type,
724 Data,
725 DataSize);
726 }
727
728 /* Do the post-callback */
729 PostOperationInfo.Status = Status;
730 CmiCallRegisteredCallbacks(RegNtPostSetValueKey, &PostOperationInfo);
731
732 end:
733 /* Dereference and return status */
734 if (KeyObject)
735 ObDereferenceObject(KeyObject);
736 ReleaseCapturedUnicodeString(&ValueNameCopy, PreviousMode);
737 if ((PreviousMode != KernelMode) && Data)
738 ExFreePoolWithTag(Data, TAG_CM);
739 return Status;
740 }
741
742 NTSTATUS
743 NTAPI
744 NtDeleteValueKey(IN HANDLE KeyHandle,
745 IN PUNICODE_STRING ValueName)
746 {
747 PCM_KEY_BODY KeyObject;
748 NTSTATUS Status;
749 REG_DELETE_VALUE_KEY_INFORMATION DeleteValueKeyInfo;
750 REG_POST_OPERATION_INFORMATION PostOperationInfo;
751 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
752 UNICODE_STRING ValueNameCopy = *ValueName;
753 PAGED_CODE();
754
755 /* Verify that the handle is valid and is a registry key */
756 Status = ObReferenceObjectByHandle(KeyHandle,
757 KEY_SET_VALUE,
758 CmpKeyObjectType,
759 PreviousMode,
760 (PVOID *)&KeyObject,
761 NULL);
762 if (!NT_SUCCESS(Status)) return Status;
763
764 /* Don't touch read-only keys */
765 if (KeyObject->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
766 {
767 /* Fail */
768 ObDereferenceObject(KeyObject);
769 return STATUS_ACCESS_DENIED;
770 }
771
772 /* Make sure the name is aligned properly */
773 if ((ValueNameCopy.Length & (sizeof(WCHAR) - 1)))
774 {
775 /* It isn't, so we'll fail */
776 ObDereferenceObject(KeyObject);
777 return STATUS_INVALID_PARAMETER;
778 }
779
780 /* Do the callback */
781 DeleteValueKeyInfo.Object = (PVOID)KeyObject;
782 DeleteValueKeyInfo.ValueName = ValueName;
783 Status = CmiCallRegisteredCallbacks(RegNtPreDeleteValueKey,
784 &DeleteValueKeyInfo);
785 if (NT_SUCCESS(Status))
786 {
787 /* Call the internal API */
788 Status = CmDeleteValueKey(KeyObject->KeyControlBlock, ValueNameCopy);
789
790 /* Do the post callback */
791 PostOperationInfo.Object = (PVOID)KeyObject;
792 PostOperationInfo.Status = Status;
793 CmiCallRegisteredCallbacks(RegNtPostDeleteValueKey,
794 &PostOperationInfo);
795 }
796
797 /* Dereference the key body */
798 ObDereferenceObject(KeyObject);
799 return Status;
800 }
801
802 NTSTATUS
803 NTAPI
804 NtFlushKey(IN HANDLE KeyHandle)
805 {
806 NTSTATUS Status;
807 PCM_KEY_BODY KeyObject;
808 PAGED_CODE();
809
810 /* Get the key object */
811 Status = ObReferenceObjectByHandle(KeyHandle,
812 0,
813 CmpKeyObjectType,
814 ExGetPreviousMode(),
815 (PVOID*)&KeyObject,
816 NULL);
817 if (!NT_SUCCESS(Status)) return Status;
818
819 /* Lock the registry */
820 CmpLockRegistry();
821
822 /* Lock the KCB */
823 CmpAcquireKcbLockShared(KeyObject->KeyControlBlock);
824
825 /* Make sure KCB isn't deleted */
826 if (KeyObject->KeyControlBlock->Delete)
827 {
828 /* Fail */
829 Status = STATUS_KEY_DELETED;
830 }
831 else
832 {
833 /* Call the internal API */
834 Status = CmFlushKey(KeyObject->KeyControlBlock, FALSE);
835 }
836
837 /* Release the locks */
838 CmpReleaseKcbLock(KeyObject->KeyControlBlock);
839 CmpUnlockRegistry();
840
841 /* Dereference the object and return status */
842 ObDereferenceObject(KeyObject);
843 return Status;
844 }
845
846 NTSTATUS
847 NTAPI
848 NtLoadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
849 IN POBJECT_ATTRIBUTES FileObjectAttributes)
850 {
851 /* Call the newer API */
852 return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, 0, NULL);
853 }
854
855 NTSTATUS
856 NTAPI
857 NtLoadKey2(IN POBJECT_ATTRIBUTES KeyObjectAttributes,
858 IN POBJECT_ATTRIBUTES FileObjectAttributes,
859 IN ULONG Flags)
860 {
861 /* Call the newer API */
862 return NtLoadKeyEx(KeyObjectAttributes, FileObjectAttributes, Flags, NULL);
863 }
864
865 NTSTATUS
866 NTAPI
867 NtLoadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
868 IN POBJECT_ATTRIBUTES SourceFile,
869 IN ULONG Flags,
870 IN HANDLE TrustClassKey)
871 {
872 NTSTATUS Status;
873 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
874 PCM_KEY_BODY KeyBody = NULL;
875 PAGED_CODE();
876
877 /* Validate flags */
878 if (Flags & ~REG_NO_LAZY_FLUSH) return STATUS_INVALID_PARAMETER;
879
880 /* Validate privilege */
881 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
882 {
883 /* Fail */
884 DPRINT1("Restore Privilege missing!\n");
885 return STATUS_PRIVILEGE_NOT_HELD;
886 }
887
888 /* Block APCs */
889 KeEnterCriticalRegion();
890
891 /* Check if we have a trust class */
892 if (TrustClassKey)
893 {
894 /* Reference it */
895 Status = ObReferenceObjectByHandle(TrustClassKey,
896 0,
897 CmpKeyObjectType,
898 PreviousMode,
899 (PVOID *)&KeyBody,
900 NULL);
901 }
902
903 /* Call the internal API */
904 Status = CmLoadKey(TargetKey, SourceFile, Flags, KeyBody);
905
906 /* Dereference the trust key, if any */
907 if (KeyBody) ObDereferenceObject(KeyBody);
908
909 /* Bring back APCs */
910 KeLeaveCriticalRegion();
911
912 /* Return status */
913 return Status;
914 }
915
916 NTSTATUS
917 NTAPI
918 NtNotifyChangeKey(IN HANDLE KeyHandle,
919 IN HANDLE Event,
920 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
921 IN PVOID ApcContext OPTIONAL,
922 OUT PIO_STATUS_BLOCK IoStatusBlock,
923 IN ULONG CompletionFilter,
924 IN BOOLEAN WatchTree,
925 OUT PVOID Buffer,
926 IN ULONG Length,
927 IN BOOLEAN Asynchronous)
928 {
929 /* Call the newer API */
930 return NtNotifyChangeMultipleKeys(KeyHandle,
931 0,
932 NULL,
933 Event,
934 ApcRoutine,
935 ApcContext,
936 IoStatusBlock,
937 CompletionFilter,
938 WatchTree,
939 Buffer,
940 Length,
941 Asynchronous);
942 }
943
944 NTSTATUS
945 NTAPI
946 NtInitializeRegistry(IN USHORT Flag)
947 {
948 BOOLEAN SetupBoot;
949 NTSTATUS Status = STATUS_SUCCESS;
950 PAGED_CODE();
951
952 /* Always do this as kernel mode */
953 if (KeGetPreviousMode() == UserMode) return ZwInitializeRegistry(Flag);
954
955 /* Enough of the system has booted by now */
956 Ki386PerfEnd();
957
958 /* Validate flag */
959 if (Flag > CM_BOOT_FLAG_MAX) return STATUS_INVALID_PARAMETER;
960
961 /* Check if boot was accepted */
962 if ((Flag >= CM_BOOT_FLAG_ACCEPTED) && (Flag <= CM_BOOT_FLAG_MAX))
963 {
964 /* Only allow once */
965 if (!CmBootAcceptFirstTime) return STATUS_ACCESS_DENIED;
966 CmBootAcceptFirstTime = FALSE;
967
968 /* Get the control set accepted */
969 Flag -= CM_BOOT_FLAG_ACCEPTED;
970 if (Flag)
971 {
972 /* FIXME: Save the last known good boot */
973 //Status = CmpSaveBootControlSet(Flag);
974
975 /* Notify HAL */
976 HalEndOfBoot();
977
978 /* Enable lazy flush */
979 CmpHoldLazyFlush = FALSE;
980 CmpLazyFlush();
981 return Status;
982 }
983
984 /* Otherwise, invalid boot */
985 return STATUS_INVALID_PARAMETER;
986 }
987
988 /* Check if this was a setup boot */
989 SetupBoot = (Flag == CM_BOOT_FLAG_SETUP ? TRUE : FALSE);
990
991 /* Make sure we're only called once */
992 if (!CmFirstTime) return STATUS_ACCESS_DENIED;
993 CmFirstTime = FALSE;
994
995 /* Acquire registry lock */
996 //CmpLockRegistryExclusive();
997
998 /* Initialize the hives and lazy flusher */
999 CmpCmdInit(SetupBoot);
1000
1001 /* Save version data */
1002 CmpSetVersionData();
1003
1004 /* Release the registry lock */
1005 //CmpUnlockRegistry();
1006 return STATUS_SUCCESS;
1007 }
1008
1009 NTSTATUS
1010 NTAPI
1011 NtCompactKeys(IN ULONG Count,
1012 IN PHANDLE KeyArray)
1013 {
1014 UNIMPLEMENTED;
1015 return STATUS_NOT_IMPLEMENTED;
1016 }
1017
1018 NTSTATUS
1019 NTAPI
1020 NtCompressKey(IN HANDLE Key)
1021 {
1022 UNIMPLEMENTED;
1023 return STATUS_NOT_IMPLEMENTED;
1024 }
1025
1026 // FIXME: different for different windows versions!
1027 #define PRODUCT_ACTIVATION_VERSION 7749
1028
1029 NTSTATUS
1030 NTAPI
1031 NtLockProductActivationKeys(IN PULONG pPrivateVer,
1032 IN PULONG pSafeMode)
1033 {
1034 KPROCESSOR_MODE PreviousMode;
1035
1036 PreviousMode = ExGetPreviousMode();
1037 _SEH2_TRY
1038 {
1039 /* Check if the caller asked for the version */
1040 if (pPrivateVer != NULL)
1041 {
1042 /* For user mode, probe it */
1043 if (PreviousMode != KernelMode)
1044 {
1045 ProbeForRead(pPrivateVer, sizeof(ULONG), sizeof(ULONG));
1046 }
1047
1048 /* Return the expected version */
1049 *pPrivateVer = PRODUCT_ACTIVATION_VERSION;
1050 }
1051
1052 /* Check if the caller asked for safe mode mode state */
1053 if (pSafeMode != NULL)
1054 {
1055 /* For user mode, probe it */
1056 if (PreviousMode != KernelMode)
1057 {
1058 ProbeForRead(pSafeMode, sizeof(ULONG), sizeof(ULONG));
1059 }
1060
1061 /* Return the safe boot mode state */
1062 *pSafeMode = InitSafeBootMode;
1063 }
1064 }
1065 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1066 {
1067 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1068 }
1069 _SEH2_END;
1070
1071 return STATUS_SUCCESS;
1072 }
1073
1074 NTSTATUS
1075 NTAPI
1076 NtLockRegistryKey(IN HANDLE KeyHandle)
1077 {
1078 UNIMPLEMENTED;
1079 return STATUS_NOT_IMPLEMENTED;
1080 }
1081
1082 NTSTATUS
1083 NTAPI
1084 NtNotifyChangeMultipleKeys(IN HANDLE MasterKeyHandle,
1085 IN ULONG Count,
1086 IN POBJECT_ATTRIBUTES SlaveObjects,
1087 IN HANDLE Event,
1088 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
1089 IN PVOID ApcContext OPTIONAL,
1090 OUT PIO_STATUS_BLOCK IoStatusBlock,
1091 IN ULONG CompletionFilter,
1092 IN BOOLEAN WatchTree,
1093 OUT PVOID Buffer,
1094 IN ULONG Length,
1095 IN BOOLEAN Asynchronous)
1096 {
1097 UNIMPLEMENTED_ONCE;
1098 return STATUS_NOT_IMPLEMENTED;
1099 }
1100
1101 NTSTATUS
1102 NTAPI
1103 NtQueryMultipleValueKey(IN HANDLE KeyHandle,
1104 IN OUT PKEY_VALUE_ENTRY ValueList,
1105 IN ULONG NumberOfValues,
1106 OUT PVOID Buffer,
1107 IN OUT PULONG Length,
1108 OUT PULONG ReturnLength)
1109 {
1110 UNIMPLEMENTED;
1111 return STATUS_NOT_IMPLEMENTED;
1112 }
1113
1114 NTSTATUS
1115 NTAPI
1116 NtQueryOpenSubKeys(IN POBJECT_ATTRIBUTES TargetKey,
1117 OUT PULONG HandleCount)
1118 {
1119 KPROCESSOR_MODE PreviousMode;
1120 PCM_KEY_BODY KeyBody = NULL;
1121 HANDLE KeyHandle;
1122 NTSTATUS Status;
1123
1124 DPRINT("NtQueryOpenSubKeys()\n");
1125
1126 PAGED_CODE();
1127
1128 /* Get the processor mode */
1129 PreviousMode = KeGetPreviousMode();
1130
1131 if (PreviousMode != KernelMode)
1132 {
1133 /* Prepare to probe parameters */
1134 _SEH2_TRY
1135 {
1136 /* Probe target key */
1137 ProbeForRead(TargetKey,
1138 sizeof(OBJECT_ATTRIBUTES),
1139 sizeof(ULONG));
1140
1141 /* Probe handle count */
1142 ProbeForWriteUlong(HandleCount);
1143 }
1144 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1145 {
1146 /* Return the exception code */
1147 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1148 }
1149 _SEH2_END;
1150 }
1151
1152 /* Open a handle to the key */
1153 Status = ObOpenObjectByName(TargetKey,
1154 CmpKeyObjectType,
1155 PreviousMode,
1156 NULL,
1157 KEY_READ,
1158 NULL,
1159 &KeyHandle);
1160 if (NT_SUCCESS(Status))
1161 {
1162 /* Reference the key object */
1163 Status = ObReferenceObjectByHandle(KeyHandle,
1164 KEY_READ,
1165 CmpKeyObjectType,
1166 PreviousMode,
1167 (PVOID *)&KeyBody,
1168 NULL);
1169
1170 /* Close the handle */
1171 NtClose(KeyHandle);
1172 }
1173
1174 /* Fail, if the key object could not be referenced */
1175 if (!NT_SUCCESS(Status))
1176 return Status;
1177
1178 /* Lock the registry exclusively */
1179 CmpLockRegistryExclusive();
1180
1181 /* Fail, if we did not open a hive root key */
1182 if (KeyBody->KeyControlBlock->KeyCell !=
1183 KeyBody->KeyControlBlock->KeyHive->BaseBlock->RootCell)
1184 {
1185 DPRINT("Error: Key is not a hive root key!\n");
1186 CmpUnlockRegistry();
1187 ObDereferenceObject(KeyBody);
1188 return STATUS_INVALID_PARAMETER;
1189 }
1190
1191 /* Call the internal API */
1192 *HandleCount = CmCountOpenSubKeys(KeyBody->KeyControlBlock,
1193 FALSE);
1194
1195 /* Unlock the registry */
1196 CmpUnlockRegistry();
1197
1198 /* Dereference the key object */
1199 ObDereferenceObject(KeyBody);
1200
1201 DPRINT("Done.\n");
1202
1203 return Status;
1204 }
1205
1206 NTSTATUS
1207 NTAPI
1208 NtQueryOpenSubKeysEx(IN POBJECT_ATTRIBUTES TargetKey,
1209 IN ULONG BufferLength,
1210 IN PVOID Buffer,
1211 IN PULONG RequiredSize)
1212 {
1213 UNIMPLEMENTED;
1214 return STATUS_NOT_IMPLEMENTED;
1215 }
1216
1217 NTSTATUS
1218 NTAPI
1219 NtRenameKey(IN HANDLE KeyHandle,
1220 IN PUNICODE_STRING ReplacementName)
1221 {
1222 UNIMPLEMENTED;
1223 return STATUS_NOT_IMPLEMENTED;
1224 }
1225
1226 NTSTATUS
1227 NTAPI
1228 NtReplaceKey(IN POBJECT_ATTRIBUTES ObjectAttributes,
1229 IN HANDLE Key,
1230 IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
1231 {
1232 UNIMPLEMENTED;
1233 return STATUS_NOT_IMPLEMENTED;
1234 }
1235
1236 NTSTATUS
1237 NTAPI
1238 NtRestoreKey(IN HANDLE KeyHandle,
1239 IN HANDLE FileHandle,
1240 IN ULONG RestoreFlags)
1241 {
1242 UNIMPLEMENTED;
1243 return STATUS_NOT_IMPLEMENTED;
1244 }
1245
1246 NTSTATUS
1247 NTAPI
1248 NtSaveKey(IN HANDLE KeyHandle,
1249 IN HANDLE FileHandle)
1250 {
1251 /* Call the extended API */
1252 return NtSaveKeyEx(KeyHandle, FileHandle, REG_STANDARD_FORMAT);
1253 }
1254
1255 NTSTATUS
1256 NTAPI
1257 NtSaveKeyEx(IN HANDLE KeyHandle,
1258 IN HANDLE FileHandle,
1259 IN ULONG Flags)
1260 {
1261 NTSTATUS Status;
1262 PCM_KEY_BODY KeyObject;
1263 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1264
1265 PAGED_CODE();
1266
1267 DPRINT("NtSaveKeyEx(0x%p, 0x%p, %lu)\n", KeyHandle, FileHandle, Flags);
1268
1269 /* Verify the flags */
1270 if ((Flags != REG_STANDARD_FORMAT)
1271 && (Flags != REG_LATEST_FORMAT)
1272 && (Flags != REG_NO_COMPRESSION))
1273 {
1274 /* Only one of these values can be specified */
1275 return STATUS_INVALID_PARAMETER;
1276 }
1277
1278 /* Check for the SeBackupPrivilege */
1279 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1280 {
1281 return STATUS_PRIVILEGE_NOT_HELD;
1282 }
1283
1284 /* Verify that the handle is valid and is a registry key */
1285 Status = ObReferenceObjectByHandle(KeyHandle,
1286 KEY_READ,
1287 CmpKeyObjectType,
1288 PreviousMode,
1289 (PVOID*)&KeyObject,
1290 NULL);
1291 if (!NT_SUCCESS(Status)) return Status;
1292
1293 /* Call the internal API */
1294 Status = CmSaveKey(KeyObject->KeyControlBlock, FileHandle, Flags);
1295
1296 ObDereferenceObject(KeyObject);
1297 return Status;
1298 }
1299
1300 NTSTATUS
1301 NTAPI
1302 NtSaveMergedKeys(IN HANDLE HighPrecedenceKeyHandle,
1303 IN HANDLE LowPrecedenceKeyHandle,
1304 IN HANDLE FileHandle)
1305 {
1306 KPROCESSOR_MODE PreviousMode;
1307 PCM_KEY_BODY HighPrecedenceKeyObject = NULL;
1308 PCM_KEY_BODY LowPrecedenceKeyObject = NULL;
1309 NTSTATUS Status;
1310
1311 PAGED_CODE();
1312
1313 DPRINT("NtSaveMergedKeys(0x%p, 0x%p, 0x%p)\n",
1314 HighPrecedenceKeyHandle, LowPrecedenceKeyHandle, FileHandle);
1315
1316 PreviousMode = ExGetPreviousMode();
1317
1318 /* Check for the SeBackupPrivilege */
1319 if (!SeSinglePrivilegeCheck(SeBackupPrivilege, PreviousMode))
1320 {
1321 return STATUS_PRIVILEGE_NOT_HELD;
1322 }
1323
1324 /* Verify that the handles are valid and are registry keys */
1325 Status = ObReferenceObjectByHandle(HighPrecedenceKeyHandle,
1326 KEY_READ,
1327 CmpKeyObjectType,
1328 PreviousMode,
1329 (PVOID*)&HighPrecedenceKeyObject,
1330 NULL);
1331 if (!NT_SUCCESS(Status))
1332 goto done;
1333
1334 Status = ObReferenceObjectByHandle(LowPrecedenceKeyHandle,
1335 KEY_READ,
1336 CmpKeyObjectType,
1337 PreviousMode,
1338 (PVOID*)&LowPrecedenceKeyObject,
1339 NULL);
1340 if (!NT_SUCCESS(Status))
1341 goto done;
1342
1343 /* Call the internal API */
1344 Status = CmSaveMergedKeys(HighPrecedenceKeyObject->KeyControlBlock,
1345 LowPrecedenceKeyObject->KeyControlBlock,
1346 FileHandle);
1347
1348 done:
1349 if (LowPrecedenceKeyObject)
1350 ObDereferenceObject(LowPrecedenceKeyObject);
1351
1352 if (HighPrecedenceKeyObject)
1353 ObDereferenceObject(HighPrecedenceKeyObject);
1354
1355 return Status;
1356 }
1357
1358 NTSTATUS
1359 NTAPI
1360 NtSetInformationKey(IN HANDLE KeyHandle,
1361 IN KEY_SET_INFORMATION_CLASS KeyInformationClass,
1362 IN PVOID KeyInformation,
1363 IN ULONG KeyInformationLength)
1364 {
1365 UNIMPLEMENTED;
1366 return STATUS_NOT_IMPLEMENTED;
1367 }
1368
1369 NTSTATUS
1370 NTAPI
1371 NtUnloadKey(IN POBJECT_ATTRIBUTES KeyObjectAttributes)
1372 {
1373 return NtUnloadKey2(KeyObjectAttributes, 0);
1374 }
1375
1376 NTSTATUS
1377 NTAPI
1378 NtUnloadKey2(IN POBJECT_ATTRIBUTES TargetKey,
1379 IN ULONG Flags)
1380 {
1381 NTSTATUS Status;
1382 OBJECT_ATTRIBUTES ObjectAttributes;
1383 UNICODE_STRING ObjectName;
1384 CM_PARSE_CONTEXT ParseContext = {0};
1385 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1386 PCM_KEY_BODY KeyBody = NULL;
1387 ULONG ParentConv = 0, ChildConv = 0;
1388 HANDLE Handle;
1389
1390 PAGED_CODE();
1391
1392 /* Validate privilege */
1393 if (!SeSinglePrivilegeCheck(SeRestorePrivilege, PreviousMode))
1394 {
1395 /* Fail */
1396 DPRINT1("Restore Privilege missing!\n");
1397 return STATUS_PRIVILEGE_NOT_HELD;
1398 }
1399
1400 /* Check for user-mode caller */
1401 if (PreviousMode != KernelMode)
1402 {
1403 /* Prepare to probe parameters */
1404 _SEH2_TRY
1405 {
1406 /* Probe object attributes */
1407 ProbeForRead(TargetKey,
1408 sizeof(OBJECT_ATTRIBUTES),
1409 sizeof(ULONG));
1410
1411 ObjectAttributes = *TargetKey;
1412
1413 /* Probe the string */
1414 ProbeForReadUnicodeString(&TargetKey->ObjectName);
1415
1416 ObjectName = *TargetKey->ObjectName;
1417
1418 ProbeForRead(ObjectName.Buffer,
1419 ObjectName.Length,
1420 sizeof(WCHAR));
1421
1422 ObjectAttributes.ObjectName = &ObjectName;
1423 }
1424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1425 {
1426 /* Return the exception code */
1427 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1428 }
1429 _SEH2_END;
1430 }
1431 else
1432 {
1433 /* Save the target attributes directly */
1434 ObjectAttributes = *TargetKey;
1435 }
1436
1437 /* Setup the parse context */
1438 ParseContext.CreateOperation = TRUE;
1439 ParseContext.CreateOptions = REG_OPTION_BACKUP_RESTORE;
1440
1441 /* Do the create */
1442 Status = ObOpenObjectByName(&ObjectAttributes,
1443 CmpKeyObjectType,
1444 KernelMode,
1445 NULL,
1446 KEY_WRITE,
1447 &ParseContext,
1448 &Handle);
1449
1450 /* Return if failure encountered */
1451 if (!NT_SUCCESS(Status)) return Status;
1452
1453 /* Reference it */
1454 Status = ObReferenceObjectByHandle(Handle,
1455 KEY_WRITE,
1456 CmpKeyObjectType,
1457 KernelMode,
1458 (PVOID *)&KeyBody,
1459 NULL);
1460
1461 /* Close the handle */
1462 ZwClose(Handle);
1463
1464 /* Return if failure encountered */
1465 if (!NT_SUCCESS(Status)) return Status;
1466
1467 /* Acquire the lock depending on flags */
1468 if (Flags == REG_FORCE_UNLOAD)
1469 {
1470 /* Lock registry exclusively */
1471 CmpLockRegistryExclusive();
1472 }
1473 else
1474 {
1475 /* Lock registry */
1476 CmpLockRegistry();
1477
1478 /* Acquire the hive loading lock */
1479 ExAcquirePushLockExclusive(&CmpLoadHiveLock);
1480
1481 /* Lock parent and child */
1482 if (KeyBody->KeyControlBlock->ParentKcb)
1483 ParentConv = KeyBody->KeyControlBlock->ParentKcb->ConvKey;
1484 else
1485 ParentConv = KeyBody->KeyControlBlock->ConvKey;
1486
1487 ChildConv = KeyBody->KeyControlBlock->ConvKey;
1488
1489 CmpAcquireTwoKcbLocksExclusiveByKey(ChildConv, ParentConv);
1490 }
1491
1492 /* Check if it's being deleted already */
1493 if (KeyBody->KeyControlBlock->Delete)
1494 {
1495 /* Return appropriate status */
1496 Status = STATUS_KEY_DELETED;
1497 goto Quickie;
1498 }
1499
1500 /* Check if it's a readonly key */
1501 if (KeyBody->KeyControlBlock->ExtFlags & CM_KCB_READ_ONLY_KEY)
1502 {
1503 /* Return appropriate status */
1504 Status = STATUS_ACCESS_DENIED;
1505 goto Quickie;
1506 }
1507
1508 /* Call the internal API */
1509 Status = CmUnloadKey(KeyBody->KeyControlBlock,
1510 Flags);
1511
1512 /* Check if we failed, but really need to succeed */
1513 if ((Status == STATUS_CANNOT_DELETE) && (Flags == REG_FORCE_UNLOAD))
1514 {
1515 /* TODO: We should perform another attempt here */
1516 ASSERT(FALSE);
1517 }
1518
1519 /* If CmUnloadKey failed we need to unlock registry ourselves */
1520 if (!NT_SUCCESS(Status))
1521 {
1522 if (Flags != REG_FORCE_UNLOAD)
1523 {
1524 /* Release the KCB locks */
1525 CmpReleaseTwoKcbLockByKey(ChildConv, ParentConv);
1526
1527 /* Release the hive loading lock */
1528 ExReleasePushLockExclusive(&CmpLoadHiveLock);
1529 }
1530
1531 /* Unlock the registry */
1532 CmpUnlockRegistry();
1533 }
1534
1535 Quickie:
1536 /* Dereference the key */
1537 ObDereferenceObject(KeyBody);
1538
1539 /* Return status */
1540 return Status;
1541 }
1542
1543 NTSTATUS
1544 NTAPI
1545 NtUnloadKeyEx(IN POBJECT_ATTRIBUTES TargetKey,
1546 IN HANDLE Event)
1547 {
1548 UNIMPLEMENTED;
1549 return STATUS_NOT_IMPLEMENTED;
1550 }
1551
1552 /* EOF */