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)
10 /* See xdk/cmtypes.h */
11 #define REG_CREATED_NEW_KEY 1
12 #define REG_OPENED_EXISTING_KEY 2
14 #define REG_FORCE_UNLOAD 1
23 #define DPRINT(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
24 #define DPRINT1(fmt, ...) printf("(%s:%d) " fmt, __FILE__, __LINE__, ##__VA_ARGS__);
30 RetrieveCurrentModuleNTDirectory(
31 OUT PUNICODE_STRING NtPath
)
33 WCHAR ModulePath
[MAX_PATH
];
36 /* Retrieve the current path where the test is running */
37 GetModuleFileNameW(NULL
, ModulePath
, _countof(ModulePath
));
38 PathSep
= wcsrchr(ModulePath
, L
'\\');
40 PathSep
= ModulePath
+ wcslen(ModulePath
);
41 *PathSep
= UNICODE_NULL
;
43 /* Convert the path to NT format and work with it for now on */
44 return RtlDosPathNameToNtPathName_U(ModulePath
, NtPath
, NULL
, NULL
);
49 OUT PHANDLE KeyHandle
,
50 IN HANDLE RootKey OPTIONAL
,
51 IN PUNICODE_STRING KeyName
,
52 IN ULONG CreateOptions
,
53 OUT PULONG Disposition OPTIONAL
)
55 OBJECT_ATTRIBUTES ObjectAttributes
;
57 InitializeObjectAttributes(&ObjectAttributes
,
62 return NtCreateKey(KeyHandle
,
73 OUT PHANDLE KeyHandle
)
76 UNICODE_STRING KeyName
;
78 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
79 Status
= CreateRegKey(KeyHandle
,
82 REG_OPTION_NON_VOLATILE
,
84 if (!NT_SUCCESS(Status
))
87 NtFlushKey(KeyHandle
);
95 NtDeleteKey(KeyHandle
);
100 OpenDirectoryByHandleOrPath(
101 OUT PHANDLE RootPathHandle
,
102 IN HANDLE RootDirectory OPTIONAL
,
103 IN PUNICODE_STRING RootPath OPTIONAL
)
106 OBJECT_ATTRIBUTES ObjectAttributes
;
107 IO_STATUS_BLOCK IoStatusBlock
;
109 *RootPathHandle
= NULL
;
112 * RootDirectory and RootPath cannot be either both NULL
113 * or both non-NULL, when being specified.
115 if ((!RootDirectory
&& !RootPath
) ||
116 ( RootDirectory
&& RootPath
))
118 return STATUS_INVALID_PARAMETER
;
121 if (!RootDirectory
&& RootPath
)
123 /* Open the root directory path */
124 InitializeObjectAttributes(&ObjectAttributes
,
126 OBJ_CASE_INSENSITIVE
,
129 Status
= NtOpenFile(RootPathHandle
,
130 // FILE_TRAVERSE is needed to be able to use the handle as RootDirectory for future InitializeObjectAttributes calls.
131 FILE_LIST_DIRECTORY
| FILE_ADD_FILE
/* | FILE_ADD_SUBDIRECTORY */ | FILE_TRAVERSE
| SYNCHRONIZE
,
134 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
135 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
/* | FILE_OPEN_FOR_BACKUP_INTENT */);
136 if (!NT_SUCCESS(Status
))
138 DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath
, Status
);
142 /* Mark the handle as being opened locally */
143 *RootPathHandle
= (HANDLE
)((ULONG_PTR
)*RootPathHandle
| 1);
145 else if (RootDirectory
&& !RootPath
)
147 *RootPathHandle
= RootDirectory
;
149 // No other cases possible
151 return STATUS_SUCCESS
;
155 * Should be called under privileges
159 IN HANDLE RootDirectory OPTIONAL
,
160 IN PUNICODE_STRING RootPath OPTIONAL
,
161 IN PCWSTR RegistryKey
,
162 IN HANDLE ProtoKeyHandle
)
165 HANDLE RootPathHandle
, FileHandle
;
166 UNICODE_STRING FileName
;
167 OBJECT_ATTRIBUTES ObjectAttributes
;
168 IO_STATUS_BLOCK IoStatusBlock
;
170 /* Open the root directory */
171 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
172 if (!NT_SUCCESS(Status
))
174 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
178 /* Create the file */
179 RtlInitUnicodeString(&FileName
, RegistryKey
);
180 InitializeObjectAttributes(&ObjectAttributes
,
182 OBJ_CASE_INSENSITIVE
,
183 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
185 Status
= NtCreateFile(&FileHandle
,
186 FILE_GENERIC_WRITE
/* | DELETE */,
190 FILE_ATTRIBUTE_NORMAL
/* | FILE_FLAG_DELETE_ON_CLOSE */,
193 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
,
196 if (!NT_SUCCESS(Status
))
198 DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
202 /* Save the selected hive into the file */
203 Status
= NtSaveKeyEx(ProtoKeyHandle
, FileHandle
, REG_LATEST_FORMAT
);
204 if (!NT_SUCCESS(Status
))
206 DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
209 /* Close the file, the root directory (if opened locally), and return */
212 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
217 * Should be called under privileges
221 IN HANDLE RootDirectory OPTIONAL
,
222 IN PUNICODE_STRING RootPath OPTIONAL
,
224 IN BOOLEAN ForceDelete
) // ForceDelete can be used to delete read-only files
227 HANDLE RootPathHandle
;
228 UNICODE_STRING NtPath
;
229 OBJECT_ATTRIBUTES ObjectAttributes
;
230 IO_STATUS_BLOCK IoStatusBlock
;
232 FILE_DISPOSITION_INFORMATION FileDispInfo
;
233 BOOLEAN RetryOnce
= FALSE
;
235 /* Open the root directory */
236 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
237 if (!NT_SUCCESS(Status
))
239 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
243 /* Open the directory name that was passed in */
244 RtlInitUnicodeString(&NtPath
, FileName
);
245 InitializeObjectAttributes(&ObjectAttributes
,
247 OBJ_CASE_INSENSITIVE
,
251 Retry
: /* We go back there once if RetryOnce == TRUE */
252 Status
= NtOpenFile(&FileHandle
,
253 DELETE
| FILE_READ_ATTRIBUTES
|
254 (RetryOnce
? FILE_WRITE_ATTRIBUTES
: 0),
257 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
258 FILE_NON_DIRECTORY_FILE
| FILE_OPEN_FOR_BACKUP_INTENT
);
259 if (!NT_SUCCESS(Status
))
261 DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status
);
267 FILE_BASIC_INFORMATION FileInformation
;
269 Status
= NtQueryInformationFile(FileHandle
,
272 sizeof(FILE_BASIC_INFORMATION
),
273 FileBasicInformation
);
274 if (!NT_SUCCESS(Status
))
276 DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status
);
281 FileInformation
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
282 Status
= NtSetInformationFile(FileHandle
,
285 sizeof(FILE_BASIC_INFORMATION
),
286 FileBasicInformation
);
288 if (!NT_SUCCESS(Status
))
290 DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status
);
295 /* Ask for the file to be deleted */
296 FileDispInfo
.DeleteFile
= TRUE
;
297 Status
= NtSetInformationFile(FileHandle
,
300 sizeof(FILE_DISPOSITION_INFORMATION
),
301 FileDispositionInformation
);
304 if (!NT_SUCCESS(Status
))
305 DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName
, Status
);
307 // FIXME: Check the precise value of Status!
308 if (!NT_SUCCESS(Status
) && ForceDelete
&& !RetryOnce
)
315 /* Return result to the caller */
320 * Should be called under privileges
324 IN HANDLE RootKey OPTIONAL
,
325 IN PCWSTR RegMountPoint
,
326 IN HANDLE RootDirectory OPTIONAL
,
327 IN PUNICODE_STRING RootPath OPTIONAL
,
328 IN PCWSTR RegistryKey
)
331 HANDLE RootPathHandle
;
332 UNICODE_STRING KeyName
, FileName
;
333 OBJECT_ATTRIBUTES KeyObjectAttributes
;
334 OBJECT_ATTRIBUTES FileObjectAttributes
;
336 /* Open the root directory */
337 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
338 if (!NT_SUCCESS(Status
))
340 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
344 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
345 InitializeObjectAttributes(&KeyObjectAttributes
,
347 OBJ_CASE_INSENSITIVE
,
351 RtlInitUnicodeString(&FileName
, RegistryKey
);
352 InitializeObjectAttributes(&FileObjectAttributes
,
354 OBJ_CASE_INSENSITIVE
,
355 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
358 /* Mount the registry hive in the registry namespace */
359 Status
= NtLoadKey(&KeyObjectAttributes
, &FileObjectAttributes
);
361 /* Close the root directory (if opened locally), and return */
362 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
367 * Should be called under privileges
371 IN HANDLE RootKey OPTIONAL
,
372 IN PCWSTR RegMountPoint
,
375 UNICODE_STRING KeyName
;
376 OBJECT_ATTRIBUTES ObjectAttributes
;
378 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
379 InitializeObjectAttributes(&ObjectAttributes
,
381 OBJ_CASE_INSENSITIVE
,
384 // return NtUnloadKey(&ObjectAttributes);
385 return NtUnloadKey2(&ObjectAttributes
, Flags
);
389 START_TEST(NtLoadUnloadKey
)
391 typedef struct _HIVE_LIST_ENTRY
394 PCWSTR RegMountPoint
;
395 } HIVE_LIST_ENTRY
, *PHIVE_LIST_ENTRY
;
397 static const HIVE_LIST_ENTRY RegistryHives
[] =
399 { L
"TestHive1", L
"\\Registry\\Machine\\TestHive1" },
400 { L
"TestHive2", L
"\\Registry\\Machine\\TestHive2" },
404 UNICODE_STRING NtTestPath
;
405 UNICODE_STRING KeyName
;
409 BOOLEAN PrivilegeSet
[2] = {FALSE
, FALSE
};
410 WCHAR PathBuffer
[MAX_PATH
];
412 /* Retrieve our current directory */
413 RetrieveCurrentModuleNTDirectory(&NtTestPath
);
415 /* Acquire restore privilege */
416 Status
= RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[0]);
417 if (!NT_SUCCESS(Status
))
419 skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
420 /* Exit prematurely here.... */
422 RtlFreeUnicodeString(&NtTestPath
);
426 /* Acquire backup privilege */
427 Status
= RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[1]);
428 if (!NT_SUCCESS(Status
))
430 skip("RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
431 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, PrivilegeSet
[0], FALSE
, &PrivilegeSet
[0]);
432 /* Exit prematurely here.... */
434 RtlFreeUnicodeString(&NtTestPath
);
438 /* Create the template proto-hive */
439 Status
= CreateProtoHive(&KeyHandle
);
440 if (!NT_SUCCESS(Status
))
442 skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status
);
446 /* Create two registry hive files from it */
447 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
449 Status
= CreateRegistryFile(NULL
, &NtTestPath
,
450 RegistryHives
[i
].HiveName
,
452 if (!NT_SUCCESS(Status
))
454 DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
455 /* Exit prematurely here.... */
460 /* That is now done, remove the proto-hive */
461 DestroyProtoHive(KeyHandle
);
463 /* Exit prematurely here if we failed */
464 if (!NT_SUCCESS(Status
))
468 /***********************************************************************************************/
471 /* Now, mount the first hive */
472 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
474 RegistryHives
[0].HiveName
);
475 if (!NT_SUCCESS(Status
))
477 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
478 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
481 /* Create or open a key inside the mounted hive */
482 StringCchPrintfW(PathBuffer
, _countof(PathBuffer
), L
"%s\\%s", RegistryHives
[0].RegMountPoint
, L
"MyKey_1");
483 RtlInitUnicodeString(&KeyName
, PathBuffer
);
486 Status
= CreateRegKey(&KeyHandle
,
489 REG_OPTION_NON_VOLATILE
,
491 if (!NT_SUCCESS(Status
))
493 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
497 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
499 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
503 /* The key handle must be valid here */
504 Status
= NtFlushKey(KeyHandle
);
505 ok_ntstatus(Status
, STATUS_SUCCESS
);
507 /* Attempt to unmount the hive, with the handle key still opened */
508 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, 0); // Same as NtUnloadKey(&ObjectAttributes);
509 DPRINT1("Unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
510 ok_ntstatus(Status
, STATUS_CANNOT_DELETE
);
512 /* The key handle should still be valid here */
513 Status
= NtFlushKey(KeyHandle
);
514 ok_ntstatus(Status
, STATUS_SUCCESS
);
516 #if 0 // Currently, leads to memory corruption !!!!!
518 /* Force-unmount the hive, with the handle key still opened */
519 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, REG_FORCE_UNLOAD
);
520 DPRINT1("Force-unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
521 ok_hex(Status
, STATUS_SUCCESS
);
523 /* The key handle should not be valid anymore */
524 Status
= NtFlushKey(KeyHandle
);
525 if (Status
!= STATUS_KEY_DELETED
/* Win2k3 */ &&
526 Status
!= STATUS_HIVE_UNLOADED
/* Win7+ */)
528 ok_ntstatus(Status
, STATUS_KEY_DELETED
);
533 /* The key handle should not be valid anymore */
534 Status
= NtDeleteKey(KeyHandle
);
535 ok_ntstatus(Status
, STATUS_SUCCESS
);
537 /* Close by principle the handle, but should this fail? */
538 Status
= NtClose(KeyHandle
);
539 ok_ntstatus(Status
, STATUS_SUCCESS
);
542 /***********************************************************************************************/
545 /* Now, mount the first hive, again */
546 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
548 RegistryHives
[0].HiveName
);
549 if (!NT_SUCCESS(Status
))
551 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
552 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
555 /* Create or open a key inside the mounted hive */
556 StringCchPrintfW(PathBuffer
, _countof(PathBuffer
), L
"%s\\%s", RegistryHives
[0].RegMountPoint
, L
"MyKey_2");
557 RtlInitUnicodeString(&KeyName
, PathBuffer
);
560 Status
= CreateRegKey(&KeyHandle
,
563 REG_OPTION_NON_VOLATILE
,
565 if (!NT_SUCCESS(Status
))
567 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
571 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
573 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
577 /* The key handle must be valid here */
578 Status
= NtFlushKey(KeyHandle
);
579 ok_ntstatus(Status
, STATUS_SUCCESS
);
581 /* Delete the key, this should succeed */
582 Status
= NtDeleteKey(KeyHandle
);
583 ok_ntstatus(Status
, STATUS_SUCCESS
);
585 /* Close the handle, this should succeed */
586 Status
= NtClose(KeyHandle
);
587 ok_ntstatus(Status
, STATUS_SUCCESS
);
589 /* Attempt to unmount the hive (no forcing), this should succeed */
590 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, 0); // Same as NtUnloadKey(&ObjectAttributes);
591 DPRINT1("Unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
592 ok_ntstatus(Status
, STATUS_SUCCESS
);
594 /* Force-unmount the hive (it is already unmounted), this should fail */
595 Status
= DisconnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
, REG_FORCE_UNLOAD
);
596 DPRINT1("Force-unmounting '%S' %s\n", RegistryHives
[0].RegMountPoint
, NT_SUCCESS(Status
) ? "succeeded" : "failed");
597 ok_hex(Status
, STATUS_INVALID_PARAMETER
);
600 /* Close by principle the handle, but should this fail? */
601 Status
= NtClose(KeyHandle
);
602 ok_ntstatus(Status
, STATUS_SUCCESS
);
606 /***********************************************************************************************/
611 /* Destroy the hive files */
612 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
614 Status
= MyDeleteFile(NULL
, &NtTestPath
,
615 RegistryHives
[i
].HiveName
, TRUE
);
616 if (!NT_SUCCESS(Status
))
617 DPRINT1("MyDeleteFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
620 /* Remove restore and backup privileges */
621 RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, PrivilegeSet
[1], FALSE
, &PrivilegeSet
[1]);
622 RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, PrivilegeSet
[0], FALSE
, &PrivilegeSet
[0]);
624 RtlFreeUnicodeString(&NtTestPath
);