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 | 110 ++++++++++++++++++++++++--------- server/request.h | 6 +- server/trace.c | 11 +++- 7 files changed, 115 insertions(+), 40 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..848e935b7da 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 & ~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 20cdf9b7b85..c8165532659 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 */ @@ -649,34 +650,6 @@ static int key_close_handle( struct object *obj, struct process *process, obj_ha return 1; /* ok to close */ }
-static void key_destroy( struct object *obj ) -{ - int i; - struct list *ptr; - struct key *key = (struct key *)obj; - assert( obj->ops == &key_ops ); - - free( key->class ); - for (i = 0; i <= key->last_value; i++) - { - free( key->values[i].name ); - free( key->values[i].data ); - } - free( key->values ); - for (i = 0; i <= key->last_subkey; i++) - { - key->subkeys[i]->obj.name->parent = NULL; - release_object( key->subkeys[i] ); - } - free( key->subkeys ); - /* unconditionally notify everything waiting on this key */ - while ((ptr = list_head( &key->notify_list ))) - { - struct notify *notify = LIST_ENTRY( ptr, struct notify, entry ); - do_notification( key, notify, 1 ); - } -} - /* allocate a key object */ static struct key *create_key_object( struct object *parent, const struct unicode_str *name, unsigned int attributes, unsigned int options, timeout_t modif, @@ -702,6 +675,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; @@ -1763,6 +1737,36 @@ 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 ) +{ + obj_handle_t handle; + + /* check if we are loading in \REGISTRY\A */ + if ( key->obj.name->parent != &application_hives->obj ) + { + set_error( STATUS_PRIVILEGE_NOT_HELD ); + return 0; + } + + load_registry( key, filename ); + if (get_error()) + { + delete_key( key, 1 ); + return 0; + } + + key->filename = strdup( filename ); + if (!key->filename) + { + delete_key( key, 1 ); + set_error( STATUS_NO_MEMORY ); + return 0; + } + + 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 ) { @@ -2123,6 +2127,48 @@ void flush_registry(void) if (fchdir( server_dir_fd ) == -1) fatal_error( "chdir to server dir: %s\n", strerror( errno )); }
+static void key_destroy( struct object *obj ) +{ + int i; + FILE *file; + struct list *ptr; + struct key *key = (struct key *)obj; + assert( obj->ops == &key_ops ); + + if (key->filename) + { + file = fopen( key->filename, "w" ); + if (file) + { + save_all_subkeys( key, file ); + fclose( file ); + } + + delete_key( key, 1 ); + free( key->filename ); + } + + free( key->class ); + for (i = 0; i <= key->last_value; i++) + { + free( key->values[i].name ); + free( key->values[i].data ); + } + free( key->values ); + for (i = 0; i <= key->last_subkey; i++) + { + key->subkeys[i]->obj.name->parent = NULL; + release_object( key->subkeys[i] ); + } + free( key->subkeys ); + /* unconditionally notify everything waiting on this key */ + while ((ptr = list_head( &key->notify_list ))) + { + struct notify *notify = LIST_ENTRY( ptr, struct notify, entry ); + do_notification( key, notify, 1 ); + } +} + /* determine if the thread is wow64 (32-bit client running on 64-bit prefix) */ static int is_wow64_thread( struct thread *thread ) { @@ -2294,7 +2340,7 @@ DECL_HANDLER(load_registry) req_data = get_req_data_after_objattr( objattr, &req_data_len ); if (!req_data) 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; @@ -2311,7 +2357,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,