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)
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>
21 /* See xdk/cmtypes.h */
22 #define REG_CREATED_NEW_KEY 1
23 #define REG_OPENED_EXISTING_KEY 2
25 #define REG_FORCE_UNLOAD 1
27 /* Vista+ ntstatus.h */
28 #define STATUS_HIVE_UNLOADED ((NTSTATUS)0xC0000425)
37 #define DPRINT(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
38 #define DPRINT1(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
44 RetrieveCurrentModuleNTDirectory(
45 OUT PUNICODE_STRING NtPath
)
47 WCHAR ModulePath
[MAX_PATH
];
50 /* Retrieve the current path where the test is running */
51 GetModuleFileNameW(NULL
, ModulePath
, _countof(ModulePath
));
52 PathSep
= wcsrchr(ModulePath
, L
'\\');
54 PathSep
= ModulePath
+ wcslen(ModulePath
);
55 *PathSep
= UNICODE_NULL
;
57 /* Convert the path to NT format and work with it for now on */
58 return RtlDosPathNameToNtPathName_U(ModulePath
, NtPath
, NULL
, NULL
);
63 OUT PHANDLE KeyHandle
,
64 IN HANDLE RootKey OPTIONAL
,
65 IN PUNICODE_STRING KeyName
,
66 IN ULONG CreateOptions
,
67 OUT PULONG Disposition OPTIONAL
)
69 OBJECT_ATTRIBUTES ObjectAttributes
;
71 InitializeObjectAttributes(&ObjectAttributes
,
76 return NtCreateKey(KeyHandle
,
87 OUT PHANDLE KeyHandle
)
90 UNICODE_STRING KeyName
;
92 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
93 Status
= CreateRegKey(KeyHandle
,
96 REG_OPTION_NON_VOLATILE
,
98 if (!NT_SUCCESS(Status
))
101 NtFlushKey(KeyHandle
);
109 NtDeleteKey(KeyHandle
);
114 OpenDirectoryByHandleOrPath(
115 OUT PHANDLE RootPathHandle
,
116 IN HANDLE RootDirectory OPTIONAL
,
117 IN PUNICODE_STRING RootPath OPTIONAL
)
120 OBJECT_ATTRIBUTES ObjectAttributes
;
121 IO_STATUS_BLOCK IoStatusBlock
;
123 *RootPathHandle
= NULL
;
126 * RootDirectory and RootPath cannot be either both NULL
127 * or both non-NULL, when being specified.
129 if ((!RootDirectory
&& !RootPath
) ||
130 ( RootDirectory
&& RootPath
))
132 return STATUS_INVALID_PARAMETER
;
135 if (!RootDirectory
&& RootPath
)
137 /* Open the root directory path */
138 InitializeObjectAttributes(&ObjectAttributes
,
140 OBJ_CASE_INSENSITIVE
,
143 Status
= NtOpenFile(RootPathHandle
,
144 // FILE_TRAVERSE is needed to be able to use the handle as RootDirectory for future InitializeObjectAttributes calls.
145 FILE_LIST_DIRECTORY
| FILE_ADD_FILE
/* | FILE_ADD_SUBDIRECTORY */ | FILE_TRAVERSE
| SYNCHRONIZE
,
148 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
149 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
/* | FILE_OPEN_FOR_BACKUP_INTENT */);
150 if (!NT_SUCCESS(Status
))
152 DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath
, Status
);
156 /* Mark the handle as being opened locally */
157 *RootPathHandle
= (HANDLE
)((ULONG_PTR
)*RootPathHandle
| 1);
159 else if (RootDirectory
&& !RootPath
)
161 *RootPathHandle
= RootDirectory
;
163 // No other cases possible
165 return STATUS_SUCCESS
;
169 * Should be called under privileges
173 IN HANDLE RootDirectory OPTIONAL
,
174 IN PUNICODE_STRING RootPath OPTIONAL
,
175 IN PCWSTR RegistryKey
,
176 IN HANDLE ProtoKeyHandle
)
179 HANDLE RootPathHandle
, FileHandle
;
180 UNICODE_STRING FileName
;
181 OBJECT_ATTRIBUTES ObjectAttributes
;
182 IO_STATUS_BLOCK IoStatusBlock
;
184 /* Open the root directory */
185 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
186 if (!NT_SUCCESS(Status
))
188 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
192 /* Create the file */
193 RtlInitUnicodeString(&FileName
, RegistryKey
);
194 InitializeObjectAttributes(&ObjectAttributes
,
196 OBJ_CASE_INSENSITIVE
,
197 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
199 Status
= NtCreateFile(&FileHandle
,
200 FILE_GENERIC_WRITE
/* | DELETE */,
204 FILE_ATTRIBUTE_NORMAL
/* | FILE_FLAG_DELETE_ON_CLOSE */,
207 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
,
210 if (!NT_SUCCESS(Status
))
212 DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
216 /* Save the selected hive into the file */
217 Status
= NtSaveKeyEx(ProtoKeyHandle
, FileHandle
, REG_LATEST_FORMAT
);
218 if (!NT_SUCCESS(Status
))
220 DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
223 /* Close the file, the root directory (if opened locally), and return */
226 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
231 * Should be called under privileges
235 IN HANDLE RootDirectory OPTIONAL
,
236 IN PUNICODE_STRING RootPath OPTIONAL
,
238 IN BOOLEAN ForceDelete
) // ForceDelete can be used to delete read-only files
241 HANDLE RootPathHandle
;
242 UNICODE_STRING NtPath
;
243 OBJECT_ATTRIBUTES ObjectAttributes
;
244 IO_STATUS_BLOCK IoStatusBlock
;
246 FILE_DISPOSITION_INFORMATION FileDispInfo
;
247 BOOLEAN RetryOnce
= FALSE
;
249 /* Open the root directory */
250 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
251 if (!NT_SUCCESS(Status
))
253 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
257 /* Open the directory name that was passed in */
258 RtlInitUnicodeString(&NtPath
, FileName
);
259 InitializeObjectAttributes(&ObjectAttributes
,
261 OBJ_CASE_INSENSITIVE
,
265 Retry
: /* We go back there once if RetryOnce == TRUE */
266 Status
= NtOpenFile(&FileHandle
,
267 DELETE
| FILE_READ_ATTRIBUTES
|
268 (RetryOnce
? FILE_WRITE_ATTRIBUTES
: 0),
271 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
272 FILE_NON_DIRECTORY_FILE
| FILE_OPEN_FOR_BACKUP_INTENT
);
273 if (!NT_SUCCESS(Status
))
275 DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status
);
281 FILE_BASIC_INFORMATION FileInformation
;
283 Status
= NtQueryInformationFile(FileHandle
,
286 sizeof(FILE_BASIC_INFORMATION
),
287 FileBasicInformation
);
288 if (!NT_SUCCESS(Status
))
290 DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status
);
295 FileInformation
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
296 Status
= NtSetInformationFile(FileHandle
,
299 sizeof(FILE_BASIC_INFORMATION
),
300 FileBasicInformation
);
302 if (!NT_SUCCESS(Status
))
304 DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status
);
309 /* Ask for the file to be deleted */
310 FileDispInfo
.DeleteFile
= TRUE
;
311 Status
= NtSetInformationFile(FileHandle
,
314 sizeof(FILE_DISPOSITION_INFORMATION
),
315 FileDispositionInformation
);
318 if (!NT_SUCCESS(Status
))
319 DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName
, Status
);
321 // FIXME: Check the precise value of Status!
322 if (!NT_SUCCESS(Status
) && ForceDelete
&& !RetryOnce
)
329 /* Return result to the caller */
334 * Should be called under privileges
338 IN HANDLE RootKey OPTIONAL
,
339 IN PCWSTR RegMountPoint
,
340 IN HANDLE RootDirectory OPTIONAL
,
341 IN PUNICODE_STRING RootPath OPTIONAL
,
342 IN PCWSTR RegistryKey
)
345 HANDLE RootPathHandle
;
346 UNICODE_STRING KeyName
, FileName
;
347 OBJECT_ATTRIBUTES KeyObjectAttributes
;
348 OBJECT_ATTRIBUTES FileObjectAttributes
;
350 /* Open the root directory */
351 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
352 if (!NT_SUCCESS(Status
))
354 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
358 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
359 InitializeObjectAttributes(&KeyObjectAttributes
,
361 OBJ_CASE_INSENSITIVE
,
365 RtlInitUnicodeString(&FileName
, RegistryKey
);
366 InitializeObjectAttributes(&FileObjectAttributes
,
368 OBJ_CASE_INSENSITIVE
,
369 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
372 /* Mount the registry hive in the registry namespace */
373 Status
= NtLoadKey(&KeyObjectAttributes
, &FileObjectAttributes
);
375 /* Close the root directory (if opened locally), and return */
376 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
381 * Should be called under privileges
385 IN HANDLE RootKey OPTIONAL
,
386 IN PCWSTR RegMountPoint
,
389 UNICODE_STRING KeyName
;
390 OBJECT_ATTRIBUTES ObjectAttributes
;
392 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
393 InitializeObjectAttributes(&ObjectAttributes
,
395 OBJ_CASE_INSENSITIVE
,
398 // return NtUnloadKey(&ObjectAttributes);
399 return NtUnloadKey2(&ObjectAttributes
, Flags
);
403 START_TEST(NtLoadUnloadKey
)
405 typedef struct _HIVE_LIST_ENTRY
408 PCWSTR RegMountPoint
;
409 } HIVE_LIST_ENTRY
, *PHIVE_LIST_ENTRY
;
411 static const HIVE_LIST_ENTRY RegistryHives
[] =
413 { L
"TestHive1", L
"\\Registry\\Machine\\TestHive1" },
414 { L
"TestHive2", L
"\\Registry\\Machine\\TestHive2" },
418 UNICODE_STRING NtTestPath
;
419 UNICODE_STRING KeyName
;
423 BOOLEAN PrivilegeSet
[2] = {FALSE
, FALSE
};
424 WCHAR PathBuffer
[MAX_PATH
];
426 /* Retrieve our current directory */
427 RetrieveCurrentModuleNTDirectory(&NtTestPath
);
429 /* Acquire restore privilege */
430 Status
= RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[0]);
431 if (!NT_SUCCESS(Status
))
433 skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
434 /* Exit prematurely here.... */
436 RtlFreeUnicodeString(&NtTestPath
);
440 /* Acquire backup privilege */
441 Status
= RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[1]);
442 if (!NT_SUCCESS(Status
))
444 skip("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
445 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, PrivilegeSet
[0], FALSE
, &PrivilegeSet
[0]);
446 /* Exit prematurely here.... */
448 RtlFreeUnicodeString(&NtTestPath
);
452 /* Create the template proto-hive */
453 Status
= CreateProtoHive(&KeyHandle
);
454 if (!NT_SUCCESS(Status
))
456 skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status
);
460 /* Create two registry hive files from it */
461 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
463 Status
= CreateRegistryFile(NULL
, &NtTestPath
,
464 RegistryHives
[i
].HiveName
,
466 if (!NT_SUCCESS(Status
))
468 DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
469 /* Exit prematurely here.... */
474 /* That is now done, remove the proto-hive */
475 DestroyProtoHive(KeyHandle
);
477 /* Exit prematurely here if we failed */
478 if (!NT_SUCCESS(Status
))
482 /***********************************************************************************************/
485 /* Now, mount the first hive */
486 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
488 RegistryHives
[0].HiveName
);
489 if (!NT_SUCCESS(Status
))
491 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
492 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
495 /* Create or open a key inside the mounted hive */
496 StringCchPrintfW(PathBuffer
, _countof(PathBuffer
), L
"%s\\%s", RegistryHives
[0].RegMountPoint
, L
"MyKey_1");
497 RtlInitUnicodeString(&KeyName
, PathBuffer
);
500 Status
= CreateRegKey(&KeyHandle
,
503 REG_OPTION_NON_VOLATILE
,
505 if (!NT_SUCCESS(Status
))
507 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
511 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
513 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
517 /* The key handle must be valid here */
518 Status
= NtFlushKey(KeyHandle
);
519 ok_ntstatus(Status
, STATUS_SUCCESS
);
521 /* Attempt to unmount the hive, with the handle key still opened */
522 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, 0); // Same as NtUnloadKey(&ObjectAttributes);
523 DPRINT1("Unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
524 ok_ntstatus(Status
, STATUS_CANNOT_DELETE
);
526 /* The key handle should still be valid here */
527 Status
= NtFlushKey(KeyHandle
);
528 ok_ntstatus(Status
, STATUS_SUCCESS
);
530 /* Force-unmount the hive, with the handle key still opened */
531 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, REG_FORCE_UNLOAD
);
532 DPRINT1("Force-unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
533 ok_hex(Status
, STATUS_SUCCESS
);
535 /* The key handle should not be valid anymore */
536 Status
= NtFlushKey(KeyHandle
);
537 if (Status
!= STATUS_KEY_DELETED
/* Win2k3 */ &&
538 Status
!= STATUS_HIVE_UNLOADED
/* Win7+ */)
540 ok_ntstatus(Status
, STATUS_KEY_DELETED
);
543 /* The key handle should not be valid anymore */
544 Status
= NtDeleteKey(KeyHandle
);
545 ok_ntstatus(Status
, STATUS_SUCCESS
);
547 /* Close by principle the handle, but should this fail? */
548 Status
= NtClose(KeyHandle
);
549 ok_ntstatus(Status
, STATUS_SUCCESS
);
552 /***********************************************************************************************/
555 /* Now, mount the first hive, again */
556 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
558 RegistryHives
[0].HiveName
);
559 if (!NT_SUCCESS(Status
))
561 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
562 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
565 /* Create or open a key inside the mounted hive */
566 StringCchPrintfW(PathBuffer
, _countof(PathBuffer
), L
"%s\\%s", RegistryHives
[0].RegMountPoint
, L
"MyKey_2");
567 RtlInitUnicodeString(&KeyName
, PathBuffer
);
570 Status
= CreateRegKey(&KeyHandle
,
573 REG_OPTION_NON_VOLATILE
,
575 if (!NT_SUCCESS(Status
))
577 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
581 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
583 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
587 /* The key handle must be valid here */
588 Status
= NtFlushKey(KeyHandle
);
589 ok_ntstatus(Status
, STATUS_SUCCESS
);
591 /* Delete the key, this should succeed */
592 Status
= NtDeleteKey(KeyHandle
);
593 ok_ntstatus(Status
, STATUS_SUCCESS
);
595 /* Close the handle, this should succeed */
596 Status
= NtClose(KeyHandle
);
597 ok_ntstatus(Status
, STATUS_SUCCESS
);
599 /* Attempt to unmount the hive (no forcing), this should succeed */
600 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, 0); // Same as NtUnloadKey(&ObjectAttributes);
601 DPRINT1("Unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
602 ok_ntstatus(Status
, STATUS_SUCCESS
);
604 /* Force-unmount the hive (it is already unmounted), this should fail */
605 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, REG_FORCE_UNLOAD
);
606 DPRINT1("Force-unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
607 ok_hex(Status
, STATUS_INVALID_PARAMETER
);
610 /* Close by principle the handle, but should this fail? */
611 Status
= NtClose(KeyHandle
);
612 ok_ntstatus(Status
, STATUS_SUCCESS
);
616 /***********************************************************************************************/
621 /* Destroy the hive files */
622 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
624 Status
= MyDeleteFile(NULL
, &NtTestPath
,
625 RegistryHives
[i
].HiveName
, TRUE
);
626 if (!NT_SUCCESS(Status
))
627 DPRINT1("MyDeleteFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
630 /* Remove restore and backup privileges */
631 RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, PrivilegeSet
[1], FALSE
, &PrivilegeSet
[1]);
632 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, PrivilegeSet
[0], FALSE
, &PrivilegeSet
[0]);
634 RtlFreeUnicodeString(&NtTestPath
);