xref: /reactos/ntoskrnl/config/ntapi.c (revision 84ccccab)
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;
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 */
1553