[PATCH v4 0/5] MR820: Implement application hives in the registry
-- v4: server: Create \REGISTRY\A hive. ntdll: Implement roothandle parameter for NtLoadKeyEx. ntdll: Rename key variable and file parameter. ntdll/test: Test for NtRegLoadKey roothandle parameter. winnt.h: Create definition for REG_LOAD_APP_KEY. https://gitlab.winehq.org/wine/wine/-/merge_requests/820
From: Santino Mazza <smazza(a)codeweavers.com> Note: This is not the official name for this definition, as I'm aware, I just created it to prevent magic values inside application hives implementation. --- include/winnt.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/winnt.h b/include/winnt.h index c0599a5f757..375c8468cfd 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5517,6 +5517,7 @@ typedef struct _TAPE_GET_MEDIA_PARAMETERS { #define REG_REFRESH_HIVE 0x00000002 #define REG_NO_LAZY_FLUSH 0x00000004 #define REG_FORCE_RESTORE 0x00000008 +#define REG_LOAD_APP_KEY 0x00000010 #define KEY_READ ((STANDARD_RIGHTS_READ| \ KEY_QUERY_VALUE| \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/820
From: Santino Mazza <smazza(a)codeweavers.com> Windows 7 apparently has a different order or number of parameters, so I had to use broken(). --- dlls/ntdll/tests/reg.c | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index f3c4eb15da0..3785ce8740a 100644 --- a/dlls/ntdll/tests/reg.c +++ b/dlls/ntdll/tests/reg.c @@ -153,6 +153,7 @@ static NTSTATUS (WINAPI * pNtNotifyChangeKey)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOI static NTSTATUS (WINAPI * pNtNotifyChangeMultipleKeys)(HANDLE,ULONG,OBJECT_ATTRIBUTES*,HANDLE,PIO_APC_ROUTINE, void*,IO_STATUS_BLOCK*,ULONG,BOOLEAN,void*,ULONG,BOOLEAN); static NTSTATUS (WINAPI * pNtWaitForSingleObject)(HANDLE,BOOLEAN,const LARGE_INTEGER*); +static NTSTATUS (WINAPI * pNtLoadKeyEx)(const OBJECT_ATTRIBUTES*,OBJECT_ATTRIBUTES*,ULONG,HANDLE,HANDLE,ACCESS_MASK,HANDLE*,IO_STATUS_BLOCK*); static HMODULE hntdll = 0; static int CurrentTest = 0; @@ -205,6 +206,7 @@ static BOOL InitFunctionPtrs(void) NTDLL_GET_PROC(RtlpNtQueryValueKey) NTDLL_GET_PROC(RtlOpenCurrentUser) NTDLL_GET_PROC(NtWaitForSingleObject) + NTDLL_GET_PROC(NtLoadKeyEx); /* optional functions */ pNtQueryLicenseValue = (void *)GetProcAddress(hntdll, "NtQueryLicenseValue"); @@ -2268,6 +2270,100 @@ static void test_NtRenameKey(void) pNtClose(key); } +static BOOL set_privileges(LPCSTR privilege, BOOL set) +{ + TOKEN_PRIVILEGES tp; + HANDLE hToken; + LUID luid; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + return FALSE; + + if(!LookupPrivilegeValueA(NULL, privilege, &luid)) + { + CloseHandle(hToken); + return FALSE; + } + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + + if (set) + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + if (GetLastError() != ERROR_SUCCESS) + { + CloseHandle(hToken); + return FALSE; + } + + CloseHandle(hToken); + return TRUE; +} + +static void test_NtRegLoadKeyEx(void) +{ + NTSTATUS status; + OBJECT_ATTRIBUTES file_attr, key_attr; + WCHAR temp_path[MAX_PATH], hivefile_path[MAX_PATH]; + UNICODE_STRING hivefile_pathW, key_pathW; + HANDLE key = 0; + + GetTempPathW(sizeof(temp_path), temp_path); + GetTempFileNameW(temp_path, L"key", 0, hivefile_path); + DeleteFileW(hivefile_path); + RtlDosPathNameToNtPathName_U(hivefile_path, &hivefile_pathW, NULL, NULL); + + if (!set_privileges(SE_RESTORE_NAME, TRUE) || + !set_privileges(SE_BACKUP_NAME, TRUE)) + { + win_skip("Failed to set SE_RESTORE_NAME and SE_BACKUP_NAME privileges, skipping tests\n"); + RtlFreeUnicodeString(&hivefile_pathW); + return; + } + + /* Generate hive file */ + InitializeObjectAttributes(&key_attr, &winetestpath, 0, NULL, NULL); + status = pNtCreateKey(&key, KEY_ALL_ACCESS, &key_attr, 0, 0, 0, 0); + ok(status == ERROR_SUCCESS, "couldn't create key 0x%lx\n", status); + status = RegSaveKeyW(key, hivefile_path, NULL); + ok(status == ERROR_SUCCESS, "couldn't save key %ld\n", status); + status = pNtDeleteKey(key); + ok(status == ERROR_SUCCESS, "couldn't delete key 0x%lx\n", status); + key = 0; + + /* Test for roothandle parameter with no flags */ + pRtlFormatCurrentUserKeyPath(&key_pathW); + key_pathW.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, key_pathW.Buffer, + key_pathW.MaximumLength + sizeof(key_pathW)*sizeof(WCHAR)); + key_pathW.MaximumLength = key_pathW.MaximumLength + sizeof(key_pathW)*sizeof(WCHAR); + pRtlAppendUnicodeToString(&key_pathW, L"TestKey"); + + InitializeObjectAttributes(&file_attr, &hivefile_pathW, 0, NULL, NULL); + key_attr.ObjectName = &key_pathW; + status = pNtLoadKeyEx(&key_attr, &file_attr, 0, NULL, NULL, KEY_READ, &key, NULL); + todo_wine ok(status == STATUS_INVALID_PARAMETER_7 || status == STATUS_INVALID_PARAMETER_6 /* win7 */, "got 0x%lx\n", status); + ok(!key, "key is null\n"); + if (key) pNtClose(key); + RtlFreeUnicodeString(&key_pathW); + + /* Test for roothandle parameter with REG_LOAD_APP_KEY */ + RtlCreateUnicodeString(&key_pathW, L"\\REGISTRY\\A\\TestKey"); + status = pNtLoadKeyEx(&key_attr, &file_attr, REG_LOAD_APP_KEY, NULL, NULL, KEY_READ, &key, NULL); + todo_wine ok(status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_VIOLATION) /* win7 */, "got 0x%lx\n", status); + todo_wine ok(key != NULL || broken(key == NULL) /* win7 */, "key is null\n"); + if (key) pNtClose(key); + RtlFreeUnicodeString(&key_pathW); + + set_privileges(SE_RESTORE_NAME, FALSE); + set_privileges(SE_BACKUP_NAME, FALSE); + RtlFreeUnicodeString(&hivefile_pathW); + DeleteFileW(hivefile_path); +} + START_TEST(reg) { static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0}; @@ -2298,6 +2394,7 @@ START_TEST(reg) test_symlinks(); test_redirection(); test_NtRenameKey(); + test_NtRegLoadKeyEx(); pRtlFreeUnicodeString(&winetestpath); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/820
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/ntdll/unix/registry.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index 797e32a5bf1..f61b892875f 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -696,18 +696,18 @@ NTSTATUS WINAPI NtLoadKey2( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *fi /****************************************************************************** * NtLoadKeyEx (NTDLL.@) */ -NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *file, ULONG flags, HANDLE trustkey, +NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTES *file_attr, ULONG flags, HANDLE trustkey, HANDLE event, ACCESS_MASK access, HANDLE *roothandle, IO_STATUS_BLOCK *iostatus ) { NTSTATUS ret; - HANDLE key; + HANDLE file; data_size_t len; struct object_attributes *objattr; char *unix_name; UNICODE_STRING nt_name; - OBJECT_ATTRIBUTES new_attr = *file; + OBJECT_ATTRIBUTES new_attr = *file_attr; - TRACE( "(%p,%p,0x%x,%p,%p,0x%x,%p,%p)\n", attr, file, flags, trustkey, event, access, roothandle, iostatus ); + TRACE( "(%p,%p,0x%x,%p,%p,0x%x,%p,%p)\n", key_attr, file_attr, flags, trustkey, event, access, roothandle, iostatus ); if (flags) FIXME( "flags %x not handled\n", flags ); if (trustkey) FIXME("trustkey parameter not supported\n"); @@ -719,7 +719,7 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *f get_redirect( &new_attr, &nt_name ); if (!(ret = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN ))) { - ret = open_unix_file( &key, unix_name, GENERIC_READ | SYNCHRONIZE, + ret = open_unix_file( &file, unix_name, GENERIC_READ | SYNCHRONIZE, &new_attr, 0, 0, FILE_OPEN, 0, NULL, 0 ); free( unix_name ); } @@ -727,19 +727,19 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *attr, OBJECT_ATTRIBUTES *f if (ret) return ret; - if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + if ((ret = alloc_object_attributes( key_attr, &objattr, &len ))) return ret; objattr->attributes |= OBJ_OPENIF | OBJ_CASE_INSENSITIVE; SERVER_START_REQ( load_registry ) { - req->file = wine_server_obj_handle( key ); + req->file = wine_server_obj_handle( file ); wine_server_add_data( req, objattr, len ); ret = wine_server_call( req ); if (ret == STATUS_OBJECT_NAME_EXISTS) ret = STATUS_SUCCESS; } SERVER_END_REQ; - NtClose( key ); + NtClose( file ); free( objattr ); return ret; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/820
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/ntdll/unix/registry.c | 10 +++++++--- include/wine/server_protocol.h | 6 +++++- server/protocol.def | 3 +++ server/registry.c | 1 + server/request.h | 5 ++++- server/trace.c | 8 +++++++- 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index f61b892875f..f1e5305cecb 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -700,7 +700,7 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTE HANDLE event, ACCESS_MASK access, HANDLE *roothandle, IO_STATUS_BLOCK *iostatus ) { NTSTATUS ret; - HANDLE file; + HANDLE file, key; data_size_t len; struct object_attributes *objattr; char *unix_name; @@ -712,10 +712,10 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTE if (flags) FIXME( "flags %x not handled\n", flags ); if (trustkey) FIXME("trustkey parameter not supported\n"); if (event) FIXME("event parameter not supported\n"); - if (access) FIXME("access parameter not supported\n"); - if (roothandle) FIXME("roothandle is not filled\n"); if (iostatus) FIXME("iostatus is not filled\n"); + if (roothandle && !(flags & REG_LOAD_APP_KEY)) return STATUS_INVALID_PARAMETER_7; + get_redirect( &new_attr, &nt_name ); if (!(ret = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN ))) { @@ -733,12 +733,16 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTE SERVER_START_REQ( load_registry ) { req->file = wine_server_obj_handle( file ); + req->access = access; wine_server_add_data( req, objattr, len ); ret = wine_server_call( req ); + key = wine_server_ptr_handle( reply->hkey ); if (ret == STATUS_OBJECT_NAME_EXISTS) ret = STATUS_SUCCESS; } SERVER_END_REQ; + if (roothandle) *roothandle = key; + else NtClose( key ); NtClose( file ); free( objattr ); return ret; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index fb3168c4a6a..0fc9cbcafbb 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2361,11 +2361,15 @@ struct load_registry_request { struct request_header __header; obj_handle_t file; + unsigned int access; /* VARARG(objattr,object_attributes); */ + char __pad_20[4]; }; struct load_registry_reply { struct reply_header __header; + obj_handle_t hkey; + char __pad_12[4]; }; @@ -6324,7 +6328,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 755 +#define SERVER_PROTOCOL_VERSION 756 /* ### protocol_version end ### */ diff --git a/server/protocol.def b/server/protocol.def index d828d41d1f7..470242187cd 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1826,7 +1826,10 @@ struct process_info /* Load a registry branch from a file */ @REQ(load_registry) obj_handle_t file; /* file to load from */ + unsigned int access; /* wanted access rights */ VARARG(objattr,object_attributes); /* object attributes */ +(a)REPLY + obj_handle_t hkey; /* handle to root key */ @END diff --git a/server/registry.c b/server/registry.c index 96ba18a0a5a..2817db327e0 100644 --- a/server/registry.c +++ b/server/registry.c @@ -2298,6 +2298,7 @@ DECL_HANDLER(load_registry) if ((key = create_key( parent, &name, 0, KEY_WOW64_64KEY, 0, sd ))) { load_registry( key, req->file ); + reply->hkey = alloc_handle( current->process, key, req->access, objattr->attributes ); release_object( key ); } if (parent) release_object( parent ); diff --git a/server/request.h b/server/request.h index 9e943cceb3c..edb69ee137d 100644 --- a/server/request.h +++ b/server/request.h @@ -1231,7 +1231,10 @@ C_ASSERT( sizeof(struct enum_key_value_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct delete_key_value_request, hkey) == 12 ); C_ASSERT( sizeof(struct delete_key_value_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct load_registry_request, file) == 12 ); -C_ASSERT( sizeof(struct load_registry_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct load_registry_request, access) == 16 ); +C_ASSERT( sizeof(struct load_registry_request) == 24 ); +C_ASSERT( FIELD_OFFSET(struct load_registry_reply, hkey) == 8 ); +C_ASSERT( sizeof(struct load_registry_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct unload_registry_request, parent) == 12 ); C_ASSERT( FIELD_OFFSET(struct unload_registry_request, attributes) == 16 ); C_ASSERT( sizeof(struct unload_registry_request) == 24 ); diff --git a/server/trace.c b/server/trace.c index c6a324bb905..329ee3a7b9f 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2407,9 +2407,15 @@ static void dump_delete_key_value_request( const struct delete_key_value_request static void dump_load_registry_request( const struct load_registry_request *req ) { fprintf( stderr, " file=%04x", req->file ); + fprintf( stderr, ", access=%08x", req->access ); dump_varargs_object_attributes( ", objattr=", cur_size ); } +static void dump_load_registry_reply( const struct load_registry_reply *req ) +{ + fprintf( stderr, " hkey=%04x", req->hkey ); +} + static void dump_unload_registry_request( const struct unload_registry_request *req ) { fprintf( stderr, " parent=%04x", req->parent ); @@ -4869,7 +4875,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_key_value_reply, (dump_func)dump_enum_key_value_reply, NULL, - NULL, + (dump_func)dump_load_registry_reply, NULL, NULL, NULL, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/820
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=123494 Your paranoid android. === debian11 (32 bit report) === ntdll: reg.c:2348: Test succeeded inside todo block: got 0xc00000f5 === debian11 (32 bit zh:CN report) === ntdll: reg.c:2348: Test succeeded inside todo block: got 0xc00000f5 === debian11 (32 bit WoW report) === ntdll: reg.c:2348: Test succeeded inside todo block: got 0xc00000f5 === debian11 (64 bit WoW report) === ntdll: reg.c:2348: Test succeeded inside todo block: got 0xc00000f5
From: Santino Mazza <smazza(a)codeweavers.com> --- dlls/ntdll/tests/reg.c | 6 +++--- server/registry.c | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index 3785ce8740a..8f29e2f0903 100644 --- a/dlls/ntdll/tests/reg.c +++ b/dlls/ntdll/tests/reg.c @@ -2345,7 +2345,7 @@ static void test_NtRegLoadKeyEx(void) InitializeObjectAttributes(&file_attr, &hivefile_pathW, 0, NULL, NULL); key_attr.ObjectName = &key_pathW; status = pNtLoadKeyEx(&key_attr, &file_attr, 0, NULL, NULL, KEY_READ, &key, NULL); - todo_wine ok(status == STATUS_INVALID_PARAMETER_7 || status == STATUS_INVALID_PARAMETER_6 /* win7 */, "got 0x%lx\n", status); + ok(status == STATUS_INVALID_PARAMETER_7 || status == STATUS_INVALID_PARAMETER_6 /* win7 */, "got 0x%lx\n", status); ok(!key, "key is null\n"); if (key) pNtClose(key); RtlFreeUnicodeString(&key_pathW); @@ -2353,8 +2353,8 @@ static void test_NtRegLoadKeyEx(void) /* Test for roothandle parameter with REG_LOAD_APP_KEY */ RtlCreateUnicodeString(&key_pathW, L"\\REGISTRY\\A\\TestKey"); status = pNtLoadKeyEx(&key_attr, &file_attr, REG_LOAD_APP_KEY, NULL, NULL, KEY_READ, &key, NULL); - todo_wine ok(status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_VIOLATION) /* win7 */, "got 0x%lx\n", status); - todo_wine ok(key != NULL || broken(key == NULL) /* win7 */, "key is null\n"); + ok(status == STATUS_SUCCESS || broken(status == STATUS_ACCESS_VIOLATION) /* win7 */, "got 0x%lx\n", status); + ok(key != NULL || broken(key == NULL) /* win7 */, "key is null\n"); if (key) pNtClose(key); RtlFreeUnicodeString(&key_pathW); diff --git a/server/registry.c b/server/registry.c index 2817db327e0..c413d0062b8 100644 --- a/server/registry.c +++ b/server/registry.c @@ -1853,10 +1853,13 @@ void init_registry(void) 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', 'P','e','r','f','l','i','b','\\', '0','0','9'}; + static const WCHAR application[] = {'A'}; + static const struct unicode_str root_name = { REGISTRY, sizeof(REGISTRY) }; static const struct unicode_str HKLM_name = { HKLM, sizeof(HKLM) }; static const struct unicode_str HKU_name = { HKU_default, sizeof(HKU_default) }; static const struct unicode_str perflib_name = { perflib, sizeof(perflib) }; + static const struct unicode_str application_name = { application, sizeof(application) }; WCHAR *current_user_path; struct unicode_str current_user_str; @@ -1934,6 +1937,11 @@ void init_registry(void) release_object( key ); } + /* create application hive */ + if (!(key = create_key_recursive( root_key, &application_name, current_time ))) + fatal_error( "could not create \\REGISTRY\\A registry key\n" ); + release_object(key); + release_object( hklm ); release_object( hkcu ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/820
The commit `server: Create \REGISTRY\A hive.` is required because apparently the roothandle paramter is only valid with the REG_LOAD_APP_KEY flag. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/820#note_8651
participants (3)
-
Marvin -
Santino Mazza -
Santino Mazza (@tati1454)