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
34 #define DPRINT(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
35 #define DPRINT1(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
41 RetrieveCurrentModuleNTDirectory(
42 OUT PUNICODE_STRING NtPath
)
44 WCHAR ModulePath
[MAX_PATH
];
47 /* Retrieve the current path where the test is running */
48 GetModuleFileNameW(NULL
, ModulePath
, _countof(ModulePath
));
49 PathSep
= wcsrchr(ModulePath
, L
'\\');
51 PathSep
= ModulePath
+ wcslen(ModulePath
);
52 *PathSep
= UNICODE_NULL
;
54 /* Convert the path to NT format and work with it for now on */
55 return RtlDosPathNameToNtPathName_U(ModulePath
, NtPath
, NULL
, NULL
);
60 OUT PHANDLE KeyHandle
,
61 IN HANDLE RootKey OPTIONAL
,
62 IN PUNICODE_STRING KeyName
,
63 IN ULONG CreateOptions
,
64 OUT PULONG Disposition OPTIONAL
)
66 OBJECT_ATTRIBUTES ObjectAttributes
;
68 InitializeObjectAttributes(&ObjectAttributes
,
73 return NtCreateKey(KeyHandle
,
84 OUT PHANDLE KeyHandle
)
87 UNICODE_STRING KeyName
;
89 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
90 Status
= CreateRegKey(KeyHandle
,
93 REG_OPTION_NON_VOLATILE
,
95 if (!NT_SUCCESS(Status
))
98 NtFlushKey(KeyHandle
);
106 NtDeleteKey(KeyHandle
);
111 OpenDirectoryByHandleOrPath(
112 OUT PHANDLE RootPathHandle
,
113 IN HANDLE RootDirectory OPTIONAL
,
114 IN PUNICODE_STRING RootPath OPTIONAL
)
117 OBJECT_ATTRIBUTES ObjectAttributes
;
118 IO_STATUS_BLOCK IoStatusBlock
;
120 *RootPathHandle
= NULL
;
123 * RootDirectory and RootPath cannot be either both NULL
124 * or both non-NULL, when being specified.
126 if ((!RootDirectory
&& !RootPath
) ||
127 ( RootDirectory
&& RootPath
))
129 return STATUS_INVALID_PARAMETER
;
132 if (!RootDirectory
&& RootPath
)
134 /* Open the root directory path */
135 InitializeObjectAttributes(&ObjectAttributes
,
137 OBJ_CASE_INSENSITIVE
,
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
,
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
))
149 DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath
, Status
);
153 /* Mark the handle as being opened locally */
154 *RootPathHandle
= (HANDLE
)((ULONG_PTR
)*RootPathHandle
| 1);
156 else if (RootDirectory
&& !RootPath
)
158 *RootPathHandle
= RootDirectory
;
160 // No other cases possible
162 return STATUS_SUCCESS
;
166 * Should be called under privileges
170 IN HANDLE RootDirectory OPTIONAL
,
171 IN PUNICODE_STRING RootPath OPTIONAL
,
172 IN PCWSTR RegistryKey
,
173 IN HANDLE ProtoKeyHandle
)
176 HANDLE RootPathHandle
, FileHandle
;
177 UNICODE_STRING FileName
;
178 OBJECT_ATTRIBUTES ObjectAttributes
;
179 IO_STATUS_BLOCK IoStatusBlock
;
181 /* Open the root directory */
182 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
183 if (!NT_SUCCESS(Status
))
185 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
189 /* Create the file */
190 RtlInitUnicodeString(&FileName
, RegistryKey
);
191 InitializeObjectAttributes(&ObjectAttributes
,
193 OBJ_CASE_INSENSITIVE
,
194 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
196 Status
= NtCreateFile(&FileHandle
,
197 FILE_GENERIC_WRITE
/* | DELETE */,
201 FILE_ATTRIBUTE_NORMAL
/* | FILE_FLAG_DELETE_ON_CLOSE */,
204 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
,
207 if (!NT_SUCCESS(Status
))
209 DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
213 /* Save the selected hive into the file */
214 Status
= NtSaveKeyEx(ProtoKeyHandle
, FileHandle
, REG_LATEST_FORMAT
);
215 if (!NT_SUCCESS(Status
))
217 DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
220 /* Close the file, the root directory (if opened locally), and return */
223 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
228 * Should be called under privileges
232 IN HANDLE RootDirectory OPTIONAL
,
233 IN PUNICODE_STRING RootPath OPTIONAL
,
235 IN BOOLEAN ForceDelete
) // ForceDelete can be used to delete read-only files
238 HANDLE RootPathHandle
;
239 UNICODE_STRING NtPath
;
240 OBJECT_ATTRIBUTES ObjectAttributes
;
241 IO_STATUS_BLOCK IoStatusBlock
;
243 FILE_DISPOSITION_INFORMATION FileDispInfo
;
244 BOOLEAN RetryOnce
= FALSE
;
246 /* Open the root directory */
247 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
248 if (!NT_SUCCESS(Status
))
250 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
254 /* Open the directory name that was passed in */
255 RtlInitUnicodeString(&NtPath
, FileName
);
256 InitializeObjectAttributes(&ObjectAttributes
,
258 OBJ_CASE_INSENSITIVE
,
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),
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
))
272 DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status
);
278 FILE_BASIC_INFORMATION FileInformation
;
280 Status
= NtQueryInformationFile(FileHandle
,
283 sizeof(FILE_BASIC_INFORMATION
),
284 FileBasicInformation
);
285 if (!NT_SUCCESS(Status
))
287 DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status
);
292 FileInformation
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
293 Status
= NtSetInformationFile(FileHandle
,
296 sizeof(FILE_BASIC_INFORMATION
),
297 FileBasicInformation
);
299 if (!NT_SUCCESS(Status
))
301 DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status
);
306 /* Ask for the file to be deleted */
307 FileDispInfo
.DeleteFile
= TRUE
;
308 Status
= NtSetInformationFile(FileHandle
,
311 sizeof(FILE_DISPOSITION_INFORMATION
),
312 FileDispositionInformation
);
315 if (!NT_SUCCESS(Status
))
316 DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName
, Status
);
318 // FIXME: Check the precise value of Status!
319 if (!NT_SUCCESS(Status
) && ForceDelete
&& !RetryOnce
)
326 /* Return result to the caller */
331 * Should be called under privileges
335 IN HANDLE RootKey OPTIONAL
,
336 IN PCWSTR RegMountPoint
,
337 IN HANDLE RootDirectory OPTIONAL
,
338 IN PUNICODE_STRING RootPath OPTIONAL
,
339 IN PCWSTR RegistryKey
)
342 HANDLE RootPathHandle
;
343 UNICODE_STRING KeyName
, FileName
;
344 OBJECT_ATTRIBUTES KeyObjectAttributes
;
345 OBJECT_ATTRIBUTES FileObjectAttributes
;
347 /* Open the root directory */
348 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
349 if (!NT_SUCCESS(Status
))
351 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
355 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
356 InitializeObjectAttributes(&KeyObjectAttributes
,
358 OBJ_CASE_INSENSITIVE
,
362 RtlInitUnicodeString(&FileName
, RegistryKey
);
363 InitializeObjectAttributes(&FileObjectAttributes
,
365 OBJ_CASE_INSENSITIVE
,
366 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
369 /* Mount the registry hive in the registry namespace */
370 Status
= NtLoadKey(&KeyObjectAttributes
, &FileObjectAttributes
);
372 /* Close the root directory (if opened locally), and return */
373 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
378 * Should be called under privileges
382 IN HANDLE RootKey OPTIONAL
,
383 IN PCWSTR RegMountPoint
,
386 UNICODE_STRING KeyName
;
387 OBJECT_ATTRIBUTES ObjectAttributes
;
389 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
390 InitializeObjectAttributes(&ObjectAttributes
,
392 OBJ_CASE_INSENSITIVE
,
395 // return NtUnloadKey(&ObjectAttributes);
396 return NtUnloadKey2(&ObjectAttributes
, Flags
);
400 START_TEST(NtLoadUnloadKey
)
402 typedef struct _HIVE_LIST_ENTRY
405 PCWSTR RegMountPoint
;
406 } HIVE_LIST_ENTRY
, *PHIVE_LIST_ENTRY
;
408 static const HIVE_LIST_ENTRY RegistryHives
[] =
410 { L
"TestHive1", L
"\\Registry\\Machine\\TestHive1" },
411 { L
"TestHive2", L
"\\Registry\\Machine\\TestHive2" },
415 UNICODE_STRING NtTestPath
;
416 UNICODE_STRING KeyName
;
420 BOOLEAN PrivilegeSet
[2] = {FALSE
, FALSE
};
421 WCHAR PathBuffer
[MAX_PATH
];
423 /* Retrieve our current directory */
424 RetrieveCurrentModuleNTDirectory(&NtTestPath
);
426 /* Acquire restore privilege */
427 Status
= RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[0]);
428 if (!NT_SUCCESS(Status
))
430 skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
431 /* Exit prematurely here.... */
433 RtlFreeUnicodeString(&NtTestPath
);
437 /* Acquire backup privilege */
438 Status
= RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[1]);
439 if (!NT_SUCCESS(Status
))
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.... */
445 RtlFreeUnicodeString(&NtTestPath
);
449 /* Create the template proto-hive */
450 Status
= CreateProtoHive(&KeyHandle
);
451 if (!NT_SUCCESS(Status
))
453 skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status
);
457 /* Create two registry hive files from it */
458 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
460 Status
= CreateRegistryFile(NULL
, &NtTestPath
,
461 RegistryHives
[i
].HiveName
,
463 if (!NT_SUCCESS(Status
))
465 DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
466 /* Exit prematurely here.... */
471 /* That is now done, remove the proto-hive */
472 DestroyProtoHive(KeyHandle
);
474 /* Exit prematurely here if we failed */
475 if (!NT_SUCCESS(Status
))
479 /***********************************************************************************************/
482 /* Now, mount the first hive */
483 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
485 RegistryHives
[0].HiveName
);
486 if (!NT_SUCCESS(Status
))
488 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
489 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
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
);
497 Status
= CreateRegKey(&KeyHandle
,
500 REG_OPTION_NON_VOLATILE
,
502 if (!NT_SUCCESS(Status
))
504 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
508 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
510 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
514 /* The key handle must be valid here */
515 Status
= NtFlushKey(KeyHandle
);
516 ok_ntstatus(Status
, STATUS_SUCCESS
);
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
);
523 /* The key handle should still be valid here */
524 Status
= NtFlushKey(KeyHandle
);
525 ok_ntstatus(Status
, STATUS_SUCCESS
);
527 #if 0 // Currently, leads to memory corruption !!!!!
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
);
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+ */)
539 ok_ntstatus(Status
, STATUS_KEY_DELETED
);
544 /* The key handle should not be valid anymore */
545 Status
= NtDeleteKey(KeyHandle
);
546 ok_ntstatus(Status
, STATUS_SUCCESS
);
548 /* Close by principle the handle, but should this fail? */
549 Status
= NtClose(KeyHandle
);
550 ok_ntstatus(Status
, STATUS_SUCCESS
);
553 /***********************************************************************************************/
556 /* Now, mount the first hive, again */
557 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
559 RegistryHives
[0].HiveName
);
560 if (!NT_SUCCESS(Status
))
562 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
563 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
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
);
571 Status
= CreateRegKey(&KeyHandle
,
574 REG_OPTION_NON_VOLATILE
,
576 if (!NT_SUCCESS(Status
))
578 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
582 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
584 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
588 /* The key handle must be valid here */
589 Status
= NtFlushKey(KeyHandle
);
590 ok_ntstatus(Status
, STATUS_SUCCESS
);
592 /* Delete the key, this should succeed */
593 Status
= NtDeleteKey(KeyHandle
);
594 ok_ntstatus(Status
, STATUS_SUCCESS
);
596 /* Close the handle, this should succeed */
597 Status
= NtClose(KeyHandle
);
598 ok_ntstatus(Status
, STATUS_SUCCESS
);
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
);
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
);
611 /* Close by principle the handle, but should this fail? */
612 Status
= NtClose(KeyHandle
);
613 ok_ntstatus(Status
, STATUS_SUCCESS
);
617 /***********************************************************************************************/
622 /* Destroy the hive files */
623 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
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
);
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]);
635 RtlFreeUnicodeString(&NtTestPath
);