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