1 /*
2  * PROJECT:         ReactOS API Tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for NtLoadKey and NtUnloadKey
5  * PROGRAMMERS:     Hermes Belusca-Maito (hermes.belusca@sfr.fr)
6  */
7 
8 #include "precomp.h"
9 
10 /* See xdk/cmtypes.h */
11 #define REG_CREATED_NEW_KEY     1
12 #define REG_OPENED_EXISTING_KEY 2
13 
14 #define REG_FORCE_UNLOAD        1
15 
16 #if 1
17 
18     #define NDEBUG
19     #include <debug.h>
20 
21 #else
22 
23     #define DPRINT(fmt, ...)  printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
24     #define DPRINT1(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
25 
26 #endif
27 
28 static NTSTATUS (NTAPI *pNtUnloadKey2)(POBJECT_ATTRIBUTES, ULONG);
29 
30 static BOOLEAN
RetrieveCurrentModuleNTDirectory(OUT PUNICODE_STRING NtPath)31 RetrieveCurrentModuleNTDirectory(
32     OUT PUNICODE_STRING NtPath)
33 {
34     WCHAR ModulePath[MAX_PATH];
35     PWSTR PathSep;
36 
37     /* Retrieve the current path where the test is running */
38     GetModuleFileNameW(NULL, ModulePath, _countof(ModulePath));
39     PathSep = wcsrchr(ModulePath, L'\\');
40     if (!PathSep)
41         PathSep = ModulePath + wcslen(ModulePath);
42     *PathSep = UNICODE_NULL;
43 
44     /* Convert the path to NT format and work with it for now on */
45     return RtlDosPathNameToNtPathName_U(ModulePath, NtPath, NULL, NULL);
46 }
47 
48 static NTSTATUS
CreateRegKey(OUT PHANDLE KeyHandle,IN HANDLE RootKey OPTIONAL,IN PUNICODE_STRING KeyName,IN ULONG CreateOptions,OUT PULONG Disposition OPTIONAL)49 CreateRegKey(
50     OUT PHANDLE KeyHandle,
51     IN HANDLE RootKey OPTIONAL,
52     IN PUNICODE_STRING KeyName,
53     IN ULONG CreateOptions,
54     OUT PULONG Disposition OPTIONAL)
55 {
56     OBJECT_ATTRIBUTES ObjectAttributes;
57 
58     InitializeObjectAttributes(&ObjectAttributes,
59                                KeyName,
60                                OBJ_CASE_INSENSITIVE,
61                                RootKey,
62                                NULL);
63     return NtCreateKey(KeyHandle,
64                        KEY_ALL_ACCESS,
65                        &ObjectAttributes,
66                        0,
67                        NULL,
68                        CreateOptions,
69                        Disposition);
70 }
71 
72 static NTSTATUS
CreateProtoHive(OUT PHANDLE KeyHandle)73 CreateProtoHive(
74     OUT PHANDLE KeyHandle)
75 {
76     NTSTATUS Status;
77     UNICODE_STRING KeyName;
78 
79     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
80     Status = CreateRegKey(KeyHandle,
81                           NULL,
82                           &KeyName,
83                           REG_OPTION_NON_VOLATILE,
84                           NULL);
85     if (!NT_SUCCESS(Status))
86         return Status;
87 
88     NtFlushKey(KeyHandle);
89     return Status;
90 }
91 
92 static VOID
DestroyProtoHive(IN HANDLE KeyHandle)93 DestroyProtoHive(
94     IN HANDLE KeyHandle)
95 {
96     NtDeleteKey(KeyHandle);
97     NtClose(KeyHandle);
98 }
99 
100 static NTSTATUS
OpenDirectoryByHandleOrPath(OUT PHANDLE RootPathHandle,IN HANDLE RootDirectory OPTIONAL,IN PUNICODE_STRING RootPath OPTIONAL)101 OpenDirectoryByHandleOrPath(
102     OUT PHANDLE RootPathHandle,
103     IN HANDLE RootDirectory OPTIONAL,
104     IN PUNICODE_STRING RootPath OPTIONAL)
105 {
106     NTSTATUS Status;
107     OBJECT_ATTRIBUTES ObjectAttributes;
108     IO_STATUS_BLOCK IoStatusBlock;
109 
110     *RootPathHandle = NULL;
111 
112     /*
113      * RootDirectory and RootPath cannot be either both NULL
114      * or both non-NULL, when being specified.
115      */
116     if ((!RootDirectory && !RootPath) ||
117         ( RootDirectory &&  RootPath))
118     {
119         return STATUS_INVALID_PARAMETER;
120     }
121 
122     if (!RootDirectory && RootPath)
123     {
124         /* Open the root directory path */
125         InitializeObjectAttributes(&ObjectAttributes,
126                                    RootPath,
127                                    OBJ_CASE_INSENSITIVE,
128                                    NULL,
129                                    NULL);
130         Status = NtOpenFile(RootPathHandle,
131                             // FILE_TRAVERSE is needed to be able to use the handle as RootDirectory for future InitializeObjectAttributes calls.
132                             FILE_LIST_DIRECTORY | FILE_ADD_FILE /* | FILE_ADD_SUBDIRECTORY */ | FILE_TRAVERSE | SYNCHRONIZE,
133                             &ObjectAttributes,
134                             &IoStatusBlock,
135                             FILE_SHARE_READ | FILE_SHARE_WRITE,
136                             FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE /* | FILE_OPEN_FOR_BACKUP_INTENT */);
137         if (!NT_SUCCESS(Status))
138         {
139             DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath, Status);
140             return Status;
141         }
142 
143         /* Mark the handle as being opened locally */
144         *RootPathHandle = (HANDLE)((ULONG_PTR)*RootPathHandle | 1);
145     }
146     else if (RootDirectory && !RootPath)
147     {
148         *RootPathHandle = RootDirectory;
149     }
150     // No other cases possible
151 
152     return STATUS_SUCCESS;
153 }
154 
155 /*
156  * Should be called under privileges
157  */
158 static NTSTATUS
CreateRegistryFile(IN HANDLE RootDirectory OPTIONAL,IN PUNICODE_STRING RootPath OPTIONAL,IN PCWSTR RegistryKey,IN HANDLE ProtoKeyHandle)159 CreateRegistryFile(
160     IN HANDLE RootDirectory OPTIONAL,
161     IN PUNICODE_STRING RootPath OPTIONAL,
162     IN PCWSTR RegistryKey,
163     IN HANDLE ProtoKeyHandle)
164 {
165     NTSTATUS Status;
166     HANDLE RootPathHandle, FileHandle;
167     UNICODE_STRING FileName;
168     OBJECT_ATTRIBUTES ObjectAttributes;
169     IO_STATUS_BLOCK IoStatusBlock;
170 
171     /* Open the root directory */
172     Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath);
173     if (!NT_SUCCESS(Status))
174     {
175         DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status);
176         return Status;
177     }
178 
179     /* Create the file */
180     RtlInitUnicodeString(&FileName, RegistryKey);
181     InitializeObjectAttributes(&ObjectAttributes,
182                                &FileName,
183                                OBJ_CASE_INSENSITIVE,
184                                (HANDLE)((ULONG_PTR)RootPathHandle & ~1), // Remove the opened-locally flag
185                                NULL);
186     Status = NtCreateFile(&FileHandle,
187                           FILE_GENERIC_WRITE /* | DELETE */,
188                           &ObjectAttributes,
189                           &IoStatusBlock,
190                           NULL,
191                           FILE_ATTRIBUTE_NORMAL /* | FILE_FLAG_DELETE_ON_CLOSE */,
192                           0,
193                           FILE_OVERWRITE_IF,
194                           FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
195                           NULL,
196                           0);
197     if (!NT_SUCCESS(Status))
198     {
199         DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
200         goto Cleanup;
201     }
202 
203     /* Save the selected hive into the file */
204     Status = NtSaveKeyEx(ProtoKeyHandle, FileHandle, REG_LATEST_FORMAT);
205     if (!NT_SUCCESS(Status))
206     {
207         DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
208     }
209 
210     /* Close the file, the root directory (if opened locally), and return */
211     NtClose(FileHandle);
212 Cleanup:
213     if ((ULONG_PTR)RootPathHandle & 1) NtClose((HANDLE)((ULONG_PTR)RootPathHandle & ~1));
214     return Status;
215 }
216 
217 /*
218  * Should be called under privileges
219  */
220 static NTSTATUS
MyDeleteFile(IN HANDLE RootDirectory OPTIONAL,IN PUNICODE_STRING RootPath OPTIONAL,IN PCWSTR FileName,IN BOOLEAN ForceDelete)221 MyDeleteFile(
222     IN HANDLE RootDirectory OPTIONAL,
223     IN PUNICODE_STRING RootPath OPTIONAL,
224     IN PCWSTR FileName,
225     IN BOOLEAN ForceDelete) // ForceDelete can be used to delete read-only files
226 {
227     NTSTATUS Status;
228     HANDLE RootPathHandle;
229     UNICODE_STRING NtPath;
230     OBJECT_ATTRIBUTES ObjectAttributes;
231     IO_STATUS_BLOCK IoStatusBlock;
232     HANDLE FileHandle;
233     FILE_DISPOSITION_INFORMATION FileDispInfo;
234     BOOLEAN RetryOnce = FALSE;
235 
236     /* Open the root directory */
237     Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath);
238     if (!NT_SUCCESS(Status))
239     {
240         DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status);
241         return Status;
242     }
243 
244     /* Open the directory name that was passed in */
245     RtlInitUnicodeString(&NtPath, FileName);
246     InitializeObjectAttributes(&ObjectAttributes,
247                                &NtPath,
248                                OBJ_CASE_INSENSITIVE,
249                                RootPathHandle,
250                                NULL);
251 
252 Retry: /* We go back there once if RetryOnce == TRUE */
253     Status = NtOpenFile(&FileHandle,
254                         DELETE | FILE_READ_ATTRIBUTES |
255                         (RetryOnce ? FILE_WRITE_ATTRIBUTES : 0),
256                         &ObjectAttributes,
257                         &IoStatusBlock,
258                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
259                         FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
260     if (!NT_SUCCESS(Status))
261     {
262         DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status);
263         return Status;
264     }
265 
266     if (RetryOnce)
267     {
268         FILE_BASIC_INFORMATION FileInformation;
269 
270         Status = NtQueryInformationFile(FileHandle,
271                                         &IoStatusBlock,
272                                         &FileInformation,
273                                         sizeof(FILE_BASIC_INFORMATION),
274                                         FileBasicInformation);
275         if (!NT_SUCCESS(Status))
276         {
277             DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status);
278             NtClose(FileHandle);
279             return Status;
280         }
281 
282         FileInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
283         Status = NtSetInformationFile(FileHandle,
284                                       &IoStatusBlock,
285                                       &FileInformation,
286                                       sizeof(FILE_BASIC_INFORMATION),
287                                       FileBasicInformation);
288         NtClose(FileHandle);
289         if (!NT_SUCCESS(Status))
290         {
291             DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status);
292             return Status;
293         }
294     }
295 
296     /* Ask for the file to be deleted */
297     FileDispInfo.DeleteFile = TRUE;
298     Status = NtSetInformationFile(FileHandle,
299                                   &IoStatusBlock,
300                                   &FileDispInfo,
301                                   sizeof(FILE_DISPOSITION_INFORMATION),
302                                   FileDispositionInformation);
303     NtClose(FileHandle);
304 
305     if (!NT_SUCCESS(Status))
306         DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName, Status);
307 
308     // FIXME: Check the precise value of Status!
309     if (!NT_SUCCESS(Status) && ForceDelete && !RetryOnce)
310     {
311         /* Retry once */
312         RetryOnce = TRUE;
313         goto Retry;
314     }
315 
316     /* Return result to the caller */
317     return Status;
318 }
319 
320 /*
321  * Should be called under privileges
322  */
323 static NTSTATUS
ConnectRegistry(IN HANDLE RootKey OPTIONAL,IN PCWSTR RegMountPoint,IN HANDLE RootDirectory OPTIONAL,IN PUNICODE_STRING RootPath OPTIONAL,IN PCWSTR RegistryKey)324 ConnectRegistry(
325     IN HANDLE RootKey OPTIONAL,
326     IN PCWSTR RegMountPoint,
327     IN HANDLE RootDirectory OPTIONAL,
328     IN PUNICODE_STRING RootPath OPTIONAL,
329     IN PCWSTR RegistryKey)
330 {
331     NTSTATUS Status;
332     HANDLE RootPathHandle;
333     UNICODE_STRING KeyName, FileName;
334     OBJECT_ATTRIBUTES KeyObjectAttributes;
335     OBJECT_ATTRIBUTES FileObjectAttributes;
336 
337     /* Open the root directory */
338     Status = OpenDirectoryByHandleOrPath(&RootPathHandle, RootDirectory, RootPath);
339     if (!NT_SUCCESS(Status))
340     {
341         DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status);
342         return Status;
343     }
344 
345     RtlInitUnicodeString(&KeyName, RegMountPoint);
346     InitializeObjectAttributes(&KeyObjectAttributes,
347                                &KeyName,
348                                OBJ_CASE_INSENSITIVE,
349                                RootKey,
350                                NULL);
351 
352     RtlInitUnicodeString(&FileName, RegistryKey);
353     InitializeObjectAttributes(&FileObjectAttributes,
354                                &FileName,
355                                OBJ_CASE_INSENSITIVE,
356                                (HANDLE)((ULONG_PTR)RootPathHandle & ~1), // Remove the opened-locally flag
357                                NULL);
358 
359     /* Mount the registry hive in the registry namespace */
360     Status = NtLoadKey(&KeyObjectAttributes, &FileObjectAttributes);
361 
362     /* Close the root directory (if opened locally), and return */
363     if ((ULONG_PTR)RootPathHandle & 1) NtClose((HANDLE)((ULONG_PTR)RootPathHandle & ~1));
364     return Status;
365 }
366 
367 /*
368  * Should be called under privileges
369  */
370 static NTSTATUS
DisconnectRegistry(IN HANDLE RootKey OPTIONAL,IN PCWSTR RegMountPoint,IN ULONG Flags)371 DisconnectRegistry(
372     IN HANDLE RootKey OPTIONAL,
373     IN PCWSTR RegMountPoint,
374     IN ULONG Flags)
375 {
376     UNICODE_STRING KeyName;
377     OBJECT_ATTRIBUTES ObjectAttributes;
378 
379     RtlInitUnicodeString(&KeyName, RegMountPoint);
380     InitializeObjectAttributes(&ObjectAttributes,
381                                &KeyName,
382                                OBJ_CASE_INSENSITIVE,
383                                RootKey,
384                                NULL);
385     if (!pNtUnloadKey2)
386     {
387         win_skip("NtUnloadKey2 unavailable, using NtUnloadKey. Flags %lu\n", Flags);
388         return NtUnloadKey(&ObjectAttributes);
389     }
390     return pNtUnloadKey2(&ObjectAttributes, Flags);
391 }
392 
393 
START_TEST(NtLoadUnloadKey)394 START_TEST(NtLoadUnloadKey)
395 {
396     typedef struct _HIVE_LIST_ENTRY
397     {
398         PCWSTR HiveName;
399         PCWSTR RegMountPoint;
400     } HIVE_LIST_ENTRY;
401 
402     static const HIVE_LIST_ENTRY RegistryHives[] =
403     {
404         { L"TestHive1", L"\\Registry\\Machine\\TestHive1" },
405         { L"TestHive2", L"\\Registry\\Machine\\TestHive2" },
406     };
407 
408     NTSTATUS Status;
409     UNICODE_STRING NtTestPath;
410     UNICODE_STRING KeyName;
411     HANDLE KeyHandle;
412     ULONG Disposition;
413     UINT i;
414     BOOLEAN PrivilegeSet[2] = {FALSE, FALSE};
415     WCHAR PathBuffer[MAX_PATH];
416 
417     pNtUnloadKey2 = (PVOID)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtUnloadKey2");
418 
419     /* Retrieve our current directory */
420     RetrieveCurrentModuleNTDirectory(&NtTestPath);
421 
422     /* Acquire restore privilege */
423     Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[0]);
424     if (!NT_SUCCESS(Status))
425     {
426         skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
427         /* Exit prematurely here.... */
428         // goto Cleanup;
429         RtlFreeUnicodeString(&NtTestPath);
430         return;
431     }
432 
433     /* Acquire backup privilege */
434     Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &PrivilegeSet[1]);
435     if (!NT_SUCCESS(Status))
436     {
437         skip("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status);
438         RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
439         /* Exit prematurely here.... */
440         // goto Cleanup;
441         RtlFreeUnicodeString(&NtTestPath);
442         return;
443     }
444 
445     /* Create the template proto-hive */
446     Status = CreateProtoHive(&KeyHandle);
447     if (!NT_SUCCESS(Status))
448     {
449         skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status);
450         goto Cleanup;
451     }
452 
453     /* Create two registry hive files from it */
454     for (i = 0; i < _countof(RegistryHives); ++i)
455     {
456         Status = CreateRegistryFile(NULL, &NtTestPath,
457                                     RegistryHives[i].HiveName,
458                                     KeyHandle);
459         if (!NT_SUCCESS(Status))
460         {
461             DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
462             /* Exit prematurely here.... */
463             break;
464         }
465     }
466 
467     /* That is now done, remove the proto-hive */
468     DestroyProtoHive(KeyHandle);
469 
470     /* Exit prematurely here if we failed */
471     if (!NT_SUCCESS(Status))
472         goto Cleanup;
473 
474 
475 /***********************************************************************************************/
476 
477 
478     /* Now, mount the first hive */
479     Status = ConnectRegistry(NULL, RegistryHives[0].RegMountPoint,
480                              NULL, &NtTestPath,
481                              RegistryHives[0].HiveName);
482     if (!NT_SUCCESS(Status))
483     {
484         DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
485                 &NtTestPath, RegistryHives[0].HiveName, RegistryHives[0].RegMountPoint, Status);
486     }
487 
488     /* Create or open a key inside the mounted hive */
489     StringCchPrintfW(PathBuffer, _countof(PathBuffer), L"%s\\%s", RegistryHives[0].RegMountPoint, L"MyKey_1");
490     RtlInitUnicodeString(&KeyName, PathBuffer);
491 
492     KeyHandle = NULL;
493     Status = CreateRegKey(&KeyHandle,
494                           NULL,
495                           &KeyName,
496                           REG_OPTION_NON_VOLATILE,
497                           &Disposition);
498     if (!NT_SUCCESS(Status))
499     {
500         DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
501     }
502     else
503     {
504         DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
505                 &KeyName,
506                 Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
507                 Status);
508     }
509 
510     /* The key handle must be valid here */
511     Status = NtFlushKey(KeyHandle);
512     ok_ntstatus(Status, STATUS_SUCCESS);
513 
514     /* Attempt to unmount the hive, with the handle key still opened */
515     Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, 0); // Same as NtUnloadKey(&ObjectAttributes);
516     DPRINT1("Unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed");
517     ok_ntstatus(Status, STATUS_CANNOT_DELETE);
518 
519     /* The key handle should still be valid here */
520     Status = NtFlushKey(KeyHandle);
521     ok_ntstatus(Status, STATUS_SUCCESS);
522 
523     /* Force-unmount the hive, with the handle key still opened */
524     Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, REG_FORCE_UNLOAD);
525     DPRINT1("Force-unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed");
526     ok_hex(Status, STATUS_SUCCESS);
527 
528     /* The key handle should not be valid anymore */
529     Status = NtFlushKey(KeyHandle);
530     if (Status != STATUS_KEY_DELETED    /* Win2k3 */ &&
531         Status != STATUS_HIVE_UNLOADED  /* Win7+  */)
532     {
533         ok_ntstatus(Status, STATUS_KEY_DELETED);
534     }
535 
536     /* The key handle should not be valid anymore */
537     Status = NtDeleteKey(KeyHandle);
538     ok_ntstatus(Status, STATUS_SUCCESS);
539 
540     /* Close by principle the handle, but should this fail? */
541     Status = NtClose(KeyHandle);
542     ok_ntstatus(Status, STATUS_SUCCESS);
543 
544 
545 /***********************************************************************************************/
546 
547 
548     /* Now, mount the first hive, again */
549     Status = ConnectRegistry(NULL, RegistryHives[0].RegMountPoint,
550                              NULL, &NtTestPath,
551                              RegistryHives[0].HiveName);
552     if (!NT_SUCCESS(Status))
553     {
554         DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
555                 &NtTestPath, RegistryHives[0].HiveName, RegistryHives[0].RegMountPoint, Status);
556     }
557 
558     /* Create or open a key inside the mounted hive */
559     StringCchPrintfW(PathBuffer, _countof(PathBuffer), L"%s\\%s", RegistryHives[0].RegMountPoint, L"MyKey_2");
560     RtlInitUnicodeString(&KeyName, PathBuffer);
561 
562     KeyHandle = NULL;
563     Status = CreateRegKey(&KeyHandle,
564                           NULL,
565                           &KeyName,
566                           REG_OPTION_NON_VOLATILE,
567                           &Disposition);
568     if (!NT_SUCCESS(Status))
569     {
570         DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
571     }
572     else
573     {
574         DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
575                 &KeyName,
576                 Disposition == REG_CREATED_NEW_KEY ? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
577                 Status);
578     }
579 
580     /* The key handle must be valid here */
581     Status = NtFlushKey(KeyHandle);
582     ok_ntstatus(Status, STATUS_SUCCESS);
583 
584     /* Delete the key, this should succeed */
585     Status = NtDeleteKey(KeyHandle);
586     ok_ntstatus(Status, STATUS_SUCCESS);
587 
588     /* Close the handle, this should succeed */
589     Status = NtClose(KeyHandle);
590     ok_ntstatus(Status, STATUS_SUCCESS);
591 
592     /* Attempt to unmount the hive (no forcing), this should succeed */
593     Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, 0); // Same as NtUnloadKey(&ObjectAttributes);
594     DPRINT1("Unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed");
595     ok_ntstatus(Status, STATUS_SUCCESS);
596 
597     /* Force-unmount the hive (it is already unmounted), this should fail */
598     Status = DisconnectRegistry(NULL, RegistryHives[0].RegMountPoint, REG_FORCE_UNLOAD);
599     DPRINT1("Force-unmounting '%S' %s\n", RegistryHives[0].RegMountPoint, NT_SUCCESS(Status) ? "succeeded" : "failed");
600     ok_hex(Status, STATUS_INVALID_PARAMETER);
601 
602 #if 0
603     /* Close by principle the handle, but should this fail? */
604     Status = NtClose(KeyHandle);
605     ok_ntstatus(Status, STATUS_SUCCESS);
606 #endif
607 
608 
609 /***********************************************************************************************/
610 
611 
612 Cleanup:
613 
614     /* Destroy the hive files */
615     for (i = 0; i < _countof(RegistryHives); ++i)
616     {
617         Status = MyDeleteFile(NULL, &NtTestPath,
618                               RegistryHives[i].HiveName, TRUE);
619         if (!NT_SUCCESS(Status))
620             DPRINT1("MyDeleteFile(%S) failed, Status 0x%08lx\n", RegistryHives[i].HiveName, Status);
621     }
622 
623     /* Remove restore and backup privileges */
624     RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, PrivilegeSet[1], FALSE, &PrivilegeSet[1]);
625     RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, PrivilegeSet[0], FALSE, &PrivilegeSet[0]);
626 
627     RtlFreeUnicodeString(&NtTestPath);
628 }
629