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__);
28 static NTSTATUS (NTAPI
*pNtUnloadKey2
)(POBJECT_ATTRIBUTES
, ULONG
);
31 RetrieveCurrentModuleNTDirectory(
32 OUT PUNICODE_STRING NtPath
)
34 WCHAR ModulePath
[MAX_PATH
];
37 /* Retrieve the current path where the test is running */
38 GetModuleFileNameW(NULL
, ModulePath
, _countof(ModulePath
));
39 PathSep
= wcsrchr(ModulePath
, L
'\\');
41 PathSep
= ModulePath
+ wcslen(ModulePath
);
42 *PathSep
= UNICODE_NULL
;
44 /* Convert the path to NT format and work with it for now on */
45 return RtlDosPathNameToNtPathName_U(ModulePath
, NtPath
, NULL
, NULL
);
50 OUT PHANDLE KeyHandle
,
51 IN HANDLE RootKey OPTIONAL
,
52 IN PUNICODE_STRING KeyName
,
53 IN ULONG CreateOptions
,
54 OUT PULONG Disposition OPTIONAL
)
56 OBJECT_ATTRIBUTES ObjectAttributes
;
58 InitializeObjectAttributes(&ObjectAttributes
,
63 return NtCreateKey(KeyHandle
,
74 OUT PHANDLE KeyHandle
)
77 UNICODE_STRING KeyName
;
79 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\SYSTEM\\$$$PROTO.HIV");
80 Status
= CreateRegKey(KeyHandle
,
83 REG_OPTION_NON_VOLATILE
,
85 if (!NT_SUCCESS(Status
))
88 NtFlushKey(KeyHandle
);
96 NtDeleteKey(KeyHandle
);
101 OpenDirectoryByHandleOrPath(
102 OUT PHANDLE RootPathHandle
,
103 IN HANDLE RootDirectory OPTIONAL
,
104 IN PUNICODE_STRING RootPath OPTIONAL
)
107 OBJECT_ATTRIBUTES ObjectAttributes
;
108 IO_STATUS_BLOCK IoStatusBlock
;
110 *RootPathHandle
= NULL
;
113 * RootDirectory and RootPath cannot be either both NULL
114 * or both non-NULL, when being specified.
116 if ((!RootDirectory
&& !RootPath
) ||
117 ( RootDirectory
&& RootPath
))
119 return STATUS_INVALID_PARAMETER
;
122 if (!RootDirectory
&& RootPath
)
124 /* Open the root directory path */
125 InitializeObjectAttributes(&ObjectAttributes
,
127 OBJ_CASE_INSENSITIVE
,
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
,
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
))
139 DPRINT1("NtOpenFile(%wZ) failed, Status 0x%08lx\n", RootPath
, Status
);
143 /* Mark the handle as being opened locally */
144 *RootPathHandle
= (HANDLE
)((ULONG_PTR
)*RootPathHandle
| 1);
146 else if (RootDirectory
&& !RootPath
)
148 *RootPathHandle
= RootDirectory
;
150 // No other cases possible
152 return STATUS_SUCCESS
;
156 * Should be called under privileges
160 IN HANDLE RootDirectory OPTIONAL
,
161 IN PUNICODE_STRING RootPath OPTIONAL
,
162 IN PCWSTR RegistryKey
,
163 IN HANDLE ProtoKeyHandle
)
166 HANDLE RootPathHandle
, FileHandle
;
167 UNICODE_STRING FileName
;
168 OBJECT_ATTRIBUTES ObjectAttributes
;
169 IO_STATUS_BLOCK IoStatusBlock
;
171 /* Open the root directory */
172 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
173 if (!NT_SUCCESS(Status
))
175 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
179 /* Create the file */
180 RtlInitUnicodeString(&FileName
, RegistryKey
);
181 InitializeObjectAttributes(&ObjectAttributes
,
183 OBJ_CASE_INSENSITIVE
,
184 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
186 Status
= NtCreateFile(&FileHandle
,
187 FILE_GENERIC_WRITE
/* | DELETE */,
191 FILE_ATTRIBUTE_NORMAL
/* | FILE_FLAG_DELETE_ON_CLOSE */,
194 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
,
197 if (!NT_SUCCESS(Status
))
199 DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
203 /* Save the selected hive into the file */
204 Status
= NtSaveKeyEx(ProtoKeyHandle
, FileHandle
, REG_LATEST_FORMAT
);
205 if (!NT_SUCCESS(Status
))
207 DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName
, Status
);
210 /* Close the file, the root directory (if opened locally), and return */
213 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
218 * Should be called under privileges
222 IN HANDLE RootDirectory OPTIONAL
,
223 IN PUNICODE_STRING RootPath OPTIONAL
,
225 IN BOOLEAN ForceDelete
) // ForceDelete can be used to delete read-only files
228 HANDLE RootPathHandle
;
229 UNICODE_STRING NtPath
;
230 OBJECT_ATTRIBUTES ObjectAttributes
;
231 IO_STATUS_BLOCK IoStatusBlock
;
233 FILE_DISPOSITION_INFORMATION FileDispInfo
;
234 BOOLEAN RetryOnce
= FALSE
;
236 /* Open the root directory */
237 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
238 if (!NT_SUCCESS(Status
))
240 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
244 /* Open the directory name that was passed in */
245 RtlInitUnicodeString(&NtPath
, FileName
);
246 InitializeObjectAttributes(&ObjectAttributes
,
248 OBJ_CASE_INSENSITIVE
,
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),
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
))
262 DPRINT1("NtOpenFile failed with Status 0x%08lx\n", Status
);
268 FILE_BASIC_INFORMATION FileInformation
;
270 Status
= NtQueryInformationFile(FileHandle
,
273 sizeof(FILE_BASIC_INFORMATION
),
274 FileBasicInformation
);
275 if (!NT_SUCCESS(Status
))
277 DPRINT1("NtQueryInformationFile failed with Status 0x%08lx\n", Status
);
282 FileInformation
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
283 Status
= NtSetInformationFile(FileHandle
,
286 sizeof(FILE_BASIC_INFORMATION
),
287 FileBasicInformation
);
289 if (!NT_SUCCESS(Status
))
291 DPRINT1("NtSetInformationFile failed with Status 0x%08lx\n", Status
);
296 /* Ask for the file to be deleted */
297 FileDispInfo
.DeleteFile
= TRUE
;
298 Status
= NtSetInformationFile(FileHandle
,
301 sizeof(FILE_DISPOSITION_INFORMATION
),
302 FileDispositionInformation
);
305 if (!NT_SUCCESS(Status
))
306 DPRINT1("Deletion of file '%S' failed, Status 0x%08lx\n", FileName
, Status
);
308 // FIXME: Check the precise value of Status!
309 if (!NT_SUCCESS(Status
) && ForceDelete
&& !RetryOnce
)
316 /* Return result to the caller */
321 * Should be called under privileges
325 IN HANDLE RootKey OPTIONAL
,
326 IN PCWSTR RegMountPoint
,
327 IN HANDLE RootDirectory OPTIONAL
,
328 IN PUNICODE_STRING RootPath OPTIONAL
,
329 IN PCWSTR RegistryKey
)
332 HANDLE RootPathHandle
;
333 UNICODE_STRING KeyName
, FileName
;
334 OBJECT_ATTRIBUTES KeyObjectAttributes
;
335 OBJECT_ATTRIBUTES FileObjectAttributes
;
337 /* Open the root directory */
338 Status
= OpenDirectoryByHandleOrPath(&RootPathHandle
, RootDirectory
, RootPath
);
339 if (!NT_SUCCESS(Status
))
341 DPRINT1("OpenDirectoryByHandleOrPath failed, Status 0x%08lx\n", Status
);
345 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
346 InitializeObjectAttributes(&KeyObjectAttributes
,
348 OBJ_CASE_INSENSITIVE
,
352 RtlInitUnicodeString(&FileName
, RegistryKey
);
353 InitializeObjectAttributes(&FileObjectAttributes
,
355 OBJ_CASE_INSENSITIVE
,
356 (HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1), // Remove the opened-locally flag
359 /* Mount the registry hive in the registry namespace */
360 Status
= NtLoadKey(&KeyObjectAttributes
, &FileObjectAttributes
);
362 /* Close the root directory (if opened locally), and return */
363 if ((ULONG_PTR
)RootPathHandle
& 1) NtClose((HANDLE
)((ULONG_PTR
)RootPathHandle
& ~1));
368 * Should be called under privileges
372 IN HANDLE RootKey OPTIONAL
,
373 IN PCWSTR RegMountPoint
,
376 UNICODE_STRING KeyName
;
377 OBJECT_ATTRIBUTES ObjectAttributes
;
379 RtlInitUnicodeString(&KeyName
, RegMountPoint
);
380 InitializeObjectAttributes(&ObjectAttributes
,
382 OBJ_CASE_INSENSITIVE
,
387 win_skip("NtUnloadKey2 unavailable, using NtUnloadKey. Flags %lu\n", Flags
);
388 return NtUnloadKey(&ObjectAttributes
);
390 return pNtUnloadKey2(&ObjectAttributes
, Flags
);
394 START_TEST(NtLoadUnloadKey
)
396 typedef struct _HIVE_LIST_ENTRY
399 PCWSTR RegMountPoint
;
402 static const HIVE_LIST_ENTRY RegistryHives
[] =
404 { L
"TestHive1", L
"\\Registry\\Machine\\TestHive1" },
405 { L
"TestHive2", L
"\\Registry\\Machine\\TestHive2" },
409 UNICODE_STRING NtTestPath
;
410 UNICODE_STRING KeyName
;
414 BOOLEAN PrivilegeSet
[2] = {FALSE
, FALSE
};
415 WCHAR PathBuffer
[MAX_PATH
];
417 pNtUnloadKey2
= (PVOID
)GetProcAddress(GetModuleHandleW(L
"ntdll.dll"), "NtUnloadKey2");
419 /* Retrieve our current directory */
420 RetrieveCurrentModuleNTDirectory(&NtTestPath
);
422 /* Acquire restore privilege */
423 Status
= RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[0]);
424 if (!NT_SUCCESS(Status
))
426 skip("RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE) failed (Status 0x%08lx)\n", Status
);
427 /* Exit prematurely here.... */
429 RtlFreeUnicodeString(&NtTestPath
);
433 /* Acquire backup privilege */
434 Status
= RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE
, TRUE
, FALSE
, &PrivilegeSet
[1]);
435 if (!NT_SUCCESS(Status
))
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.... */
441 RtlFreeUnicodeString(&NtTestPath
);
445 /* Create the template proto-hive */
446 Status
= CreateProtoHive(&KeyHandle
);
447 if (!NT_SUCCESS(Status
))
449 skip("CreateProtoHive() failed to create the proto-hive; Status 0x%08lx\n", Status
);
453 /* Create two registry hive files from it */
454 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
456 Status
= CreateRegistryFile(NULL
, &NtTestPath
,
457 RegistryHives
[i
].HiveName
,
459 if (!NT_SUCCESS(Status
))
461 DPRINT1("CreateRegistryFile(%S) failed, Status 0x%08lx\n", RegistryHives
[i
].HiveName
, Status
);
462 /* Exit prematurely here.... */
467 /* That is now done, remove the proto-hive */
468 DestroyProtoHive(KeyHandle
);
470 /* Exit prematurely here if we failed */
471 if (!NT_SUCCESS(Status
))
475 /***********************************************************************************************/
478 /* Now, mount the first hive */
479 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
481 RegistryHives
[0].HiveName
);
482 if (!NT_SUCCESS(Status
))
484 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
485 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
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
);
493 Status
= CreateRegKey(&KeyHandle
,
496 REG_OPTION_NON_VOLATILE
,
498 if (!NT_SUCCESS(Status
))
500 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
504 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
506 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
510 /* The key handle must be valid here */
511 Status
= NtFlushKey(KeyHandle
);
512 ok_ntstatus(Status
, STATUS_SUCCESS
);
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
);
519 /* The key handle should still be valid here */
520 Status
= NtFlushKey(KeyHandle
);
521 ok_ntstatus(Status
, STATUS_SUCCESS
);
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
);
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+ */)
533 ok_ntstatus(Status
, STATUS_KEY_DELETED
);
536 /* The key handle should not be valid anymore */
537 Status
= NtDeleteKey(KeyHandle
);
538 ok_ntstatus(Status
, STATUS_SUCCESS
);
540 /* Close by principle the handle, but should this fail? */
541 Status
= NtClose(KeyHandle
);
542 ok_ntstatus(Status
, STATUS_SUCCESS
);
545 /***********************************************************************************************/
548 /* Now, mount the first hive, again */
549 Status
= ConnectRegistry(NULL
, RegistryHives
[0].RegMountPoint
,
551 RegistryHives
[0].HiveName
);
552 if (!NT_SUCCESS(Status
))
554 DPRINT1("ConnectRegistry('%wZ\\%S', '%S') failed, Status 0x%08lx\n",
555 &NtTestPath
, RegistryHives
[0].HiveName
, RegistryHives
[0].RegMountPoint
, Status
);
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
);
563 Status
= CreateRegKey(&KeyHandle
,
566 REG_OPTION_NON_VOLATILE
,
568 if (!NT_SUCCESS(Status
))
570 DPRINT1("CreateRegKey(%wZ) failed (Status %lx)\n", &KeyName
, Status
);
574 DPRINT1("CreateRegKey(%wZ) succeeded to %s the key (Status %lx)\n",
576 Disposition
== REG_CREATED_NEW_KEY
? "create" : /* REG_OPENED_EXISTING_KEY */ "open",
580 /* The key handle must be valid here */
581 Status
= NtFlushKey(KeyHandle
);
582 ok_ntstatus(Status
, STATUS_SUCCESS
);
584 /* Delete the key, this should succeed */
585 Status
= NtDeleteKey(KeyHandle
);
586 ok_ntstatus(Status
, STATUS_SUCCESS
);
588 /* Close the handle, this should succeed */
589 Status
= NtClose(KeyHandle
);
590 ok_ntstatus(Status
, STATUS_SUCCESS
);
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
);
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
);
603 /* Close by principle the handle, but should this fail? */
604 Status
= NtClose(KeyHandle
);
605 ok_ntstatus(Status
, STATUS_SUCCESS
);
609 /***********************************************************************************************/
614 /* Destroy the hive files */
615 for (i
= 0; i
< _countof(RegistryHives
); ++i
)
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
);
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]);
627 RtlFreeUnicodeString(&NtTestPath
);