From: Santino Mazza smazza@codeweavers.com
--- dlls/ntdll/tests/reg.c | 4 +- dlls/ntdll/unix/registry.c | 12 ++++-- include/wine/server_protocol.h | 8 +++- server/protocol.def | 4 ++ server/registry.c | 73 +++++++++++++++++++++++++++++----- server/request.h | 6 ++- server/trace.c | 11 ++++- 7 files changed, 97 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index b618d879e57..a328d8e3cda 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 || broken(status == STATUS_INVALID_PARAMETER_6) /* win7 */, "got 0x%lx\n", status); + ok(status == STATUS_INVALID_PARAMETER_7 || broken(status == STATUS_INVALID_PARAMETER_6) /* win7 */, "got 0x%lx\n", status); if (status == STATUS_INVALID_PARAMETER_6) { win_skip("NtLoadKeyEx has a different order of parameters in this windows version\n"); @@ -2362,7 +2362,7 @@ static void test_NtRegLoadKeyEx(void) RtlCreateUnicodeString(&key_pathW, L"\REGISTRY\A\TestKey"); status = pNtLoadKeyEx(&key_attr, &file_attr, REG_APP_HIVE, NULL, NULL, KEY_READ, &key, NULL); ok(status == STATUS_SUCCESS, "got 0x%lx\n", status); - todo_wine ok(key != NULL, "key is null\n"); + ok(key != NULL, "key is null\n"); if (key) pNtClose(key); RtlFreeUnicodeString(&key_pathW);
diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index f434c380834..df9915a4849 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -700,6 +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 key; data_size_t len; struct object_attributes *objattr; char *unix_name; @@ -708,13 +709,13 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTE
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 (flags && (flags & ~REG_APP_HIVE)) 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_APP_HIVE)) return STATUS_INVALID_PARAMETER_7; + get_redirect( &new_attr, &nt_name ); ret = nt_to_unix_file_name( &new_attr, &unix_name, FILE_OPEN ); free( nt_name.Buffer ); @@ -730,13 +731,18 @@ NTSTATUS WINAPI NtLoadKeyEx( const OBJECT_ATTRIBUTES *key_attr, OBJECT_ATTRIBUTE
SERVER_START_REQ( load_registry ) { + req->flags = flags; + req->access = access; wine_server_add_data( req, objattr, len ); wine_server_add_data( req, unix_name, strlen( unix_name ) ); 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 ); free( objattr ); free( unix_name ); return ret; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 080fe4bb6ec..36fdd241499 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2360,13 +2360,17 @@ struct delete_key_value_reply struct load_registry_request { struct request_header __header; + unsigned int flags; + unsigned int access; /* VARARG(objattr,object_attributes); */ /* VARARG(filename,string); */ - char __pad_12[4]; + char __pad_20[4]; }; struct load_registry_reply { struct reply_header __header; + obj_handle_t hkey; + char __pad_12[4]; };
@@ -6325,7 +6329,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 757 +#define SERVER_PROTOCOL_VERSION 758
/* ### protocol_version end ### */
diff --git a/server/protocol.def b/server/protocol.def index 9c2a1af35c4..4e0c4b905db 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1825,8 +1825,12 @@ struct process_info
/* Load a registry branch from a file */ @REQ(load_registry) + unsigned int flags; /* flags */ + unsigned int access; /* wanted access rights */ VARARG(objattr,object_attributes); /* object attributes */ VARARG(filename,string); /* file to load name */ +@REPLY + obj_handle_t hkey; /* handle to root key */ @END
diff --git a/server/registry.c b/server/registry.c index ff47ae883b4..eec6425f8a6 100644 --- a/server/registry.c +++ b/server/registry.c @@ -90,6 +90,7 @@ struct key unsigned int flags; /* flags */ timeout_t modif; /* last modification time */ struct list notify_list; /* list of notifications */ + char *filename; /* loaded file name */ };
/* key flags */ @@ -637,15 +638,6 @@ static void key_unlink_name( struct object *obj, struct object_name *name ) } }
-/* close the notification associated with a handle */ -static int key_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) -{ - struct key * key = (struct key *) obj; - struct notify *notify = find_notify( key, process, handle ); - if (notify) do_notification( key, notify, 1 ); - return 1; /* ok to close */ -} - static void key_destroy( struct object *obj ) { int i; @@ -699,6 +691,7 @@ static struct key *create_key_object( struct object *parent, const struct unicod key->last_value = -1; key->values = NULL; key->modif = modif; + key->filename = NULL; list_init( &key->notify_list );
if (options & REG_OPTION_CREATE_LINK) key->flags |= KEY_SYMLINK; @@ -1760,6 +1753,37 @@ static void load_registry( struct key *key, const char *filename ) else file_set_error(); }
+static obj_handle_t load_app_registry( struct key *key, const char *filename, unsigned int access, unsigned int attributes ) +{ + static const WCHAR applicationhive_fullpath[] = {'\', 'R', 'E', 'G', 'I', 'S', 'T', 'R', 'Y', '\', 'A', '\'}; + WCHAR *key_fullpath; + obj_handle_t handle = 0; + data_size_t key_fullpath_size; + + /* check if we are loading in \REGISTRY\A */ + key_fullpath = key_get_full_name( &key->obj, &key_fullpath_size ); + if (key_fullpath_size < sizeof(applicationhive_fullpath) || + memcmp( key_fullpath, applicationhive_fullpath, sizeof(applicationhive_fullpath) )) + { + set_error( STATUS_PRIVILEGE_NOT_HELD ); + } + free( key_fullpath ); + if (get_error()) return 0; + + load_registry( key, filename ); + if (get_error()) + { + delete_key( key, 1 ); + return 0; + } + + key->filename = malloc( strlen( filename ) + 1 ); + strcpy( key->filename, filename ); + + handle = alloc_handle( current->process, key, access, attributes ); + return handle; +} + /* load one of the initial registry files */ static int load_init_registry_from_file( const char *filename, struct key *key ) { @@ -2120,6 +2144,29 @@ void flush_registry(void) if (fchdir( server_dir_fd ) == -1) fatal_error( "chdir to server dir: %s\n", strerror( errno )); }
+/* close the notification associated with a handle */ +static int key_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) +{ + struct key *key = (struct key *) obj; + FILE *file; + struct notify *notify = find_notify( key, process, handle ); + if (notify) do_notification( key, notify, 1 ); + if (key->filename) + { + file = fopen( key->filename, "w" ); + if (file) + { + save_all_subkeys( key, file ); + if (fclose( file )) file_set_error(); + } + else + file_set_error(); + delete_key( key, 1 ); + free( key->filename ); + } + return 1; /* ok to close */ +} + /* determine if the thread is wow64 (32-bit client running on 64-bit prefix) */ static int is_wow64_thread( struct thread *thread ) { @@ -2290,7 +2337,7 @@ DECL_HANDLER(load_registry)
if (!objattr) return;
- if (!thread_single_check_privilege( current, SeRestorePrivilege )) + if (!(req->flags & REG_APP_HIVE) && !thread_single_check_privilege( current, SeRestorePrivilege )) { set_error( STATUS_PRIVILEGE_NOT_HELD ); return; @@ -2307,7 +2354,11 @@ DECL_HANDLER(load_registry) memcpy( filename, req_data, req_data_len ); filename[req_data_len] = 0;
- load_registry( key, filename ); + if (req->flags & REG_APP_HIVE) + reply->hkey = load_app_registry( key, filename, req->access, objattr->attributes ); + else + load_registry( key, filename ); + free( filename ); } else diff --git a/server/request.h b/server/request.h index d14cede98be..faf66287b60 100644 --- a/server/request.h +++ b/server/request.h @@ -1230,7 +1230,11 @@ C_ASSERT( FIELD_OFFSET(struct enum_key_value_reply, namelen) == 16 ); 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( sizeof(struct load_registry_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct load_registry_request, flags) == 12 ); +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 5765ab40bd8..97435dc7493 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2406,10 +2406,17 @@ 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 ) { - dump_varargs_object_attributes( " objattr=", cur_size ); + fprintf( stderr, " flags=%08x", req->flags ); + fprintf( stderr, ", access=%08x", req->access ); + dump_varargs_object_attributes( ", objattr=", cur_size ); dump_varargs_string( ", filename=", 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 +4876,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,