From ac08b038f00ede310543d102dfeba18d34c9b566 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 2 Mar 2010 12:03:17 +0100 Subject: [PATCH] server: Initial support for Wow64 registry redirection. --- dlls/ntdll/tests/reg.c | 3 ++- server/registry.c | 48 +++++++++++++++++++++++++++++++++++++----- server/thread.c | 7 ++++++ server/thread.h | 1 + 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index d5342403604..4480bbf4472 100644 --- a/dlls/ntdll/tests/reg.c +++ b/dlls/ntdll/tests/reg.c @@ -1157,7 +1157,8 @@ static void test_redirection(void) dw = get_key_value( key, "Winetest", 0 ); ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw ); check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 ); - check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); + dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY ); + todo_wine ok( dw == 32, "wrong value %u\n", dw ); pNtClose( key ); status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 ); diff --git a/server/registry.c b/server/registry.c index ea7ea206764..ad57bd21d19 100644 --- a/server/registry.c +++ b/server/registry.c @@ -84,6 +84,7 @@ struct key #define KEY_DELETED 0x0002 /* key has been deleted */ #define KEY_DIRTY 0x0004 /* key has been modified */ #define KEY_SYMLINK 0x0008 /* key is a symbolic link */ +#define KEY_WOW64 0x0010 /* key contains a Wow6432Node subkey */ /* a key value */ struct key_value @@ -109,6 +110,7 @@ static const timeout_t save_period = 30 * -TICKS_PER_SEC; /* delay between peri static struct timeout_user *save_timeout_user; /* saving timer */ static const WCHAR root_name[] = { '\\','R','e','g','i','s','t','r','y','\\' }; +static const WCHAR wow6432node[] = {'W','o','w','6','4','3','2','N','o','d','e'}; static const WCHAR symlink_value[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e'}; static const struct unicode_str symlink_str = { symlink_value, sizeof(symlink_value) }; @@ -166,6 +168,12 @@ static const struct object_ops key_ops = }; +static inline int is_wow6432node( const WCHAR *name, unsigned int len ) +{ + return (len == sizeof(wow6432node) && + !memicmpW( name, wow6432node, sizeof(wow6432node)/sizeof(WCHAR) )); +} + /* * The registry text file format v2 used by this code is similar to the one * used by REGEDIT import/export functionality, with the following differences: @@ -528,6 +536,7 @@ static struct key *alloc_subkey( struct key *parent, const struct unicode_str *n for (i = ++parent->last_subkey; i > index; i--) parent->subkeys[i] = parent->subkeys[i-1]; parent->subkeys[index] = key; + if (is_wow6432node( key->name, key->namelen )) parent->flags |= KEY_WOW64; } return key; } @@ -546,6 +555,7 @@ static void free_subkey( struct key *parent, int index ) parent->last_subkey--; key->flags |= KEY_DELETED; key->parent = NULL; + if (is_wow6432node( key->name, key->namelen )) parent->flags &= ~KEY_WOW64; release_object( key ); /* try to shrink the array */ @@ -587,6 +597,22 @@ static struct key *find_subkey( const struct key *key, const struct unicode_str return NULL; } +/* return the wow64 variant of the key, or the key itself if none */ +static struct key *find_wow64_subkey( struct key *key, const struct unicode_str *name ) +{ + static const struct unicode_str wow6432node_str = { wow6432node, sizeof(wow6432node) }; + int index; + + if (!(key->flags & KEY_WOW64)) return key; + if (!is_wow6432node( name->str, name->len )) + { + key = find_subkey( key, &wow6432node_str, &index ); + assert( key ); /* if KEY_WOW64 is set we must find it */ + } + return key; +} + + /* follow a symlink and return the resolved key */ static struct key *follow_symlink( struct key *key, int iteration ) { @@ -618,13 +644,15 @@ static struct key *follow_symlink( struct key *key, int iteration ) } /* open a subkey */ -static struct key *open_key( struct key *key, const struct unicode_str *name, unsigned int attributes ) +static struct key *open_key( struct key *key, const struct unicode_str *name, unsigned int access, + unsigned int attributes ) { int index; struct unicode_str token; token.str = NULL; if (!get_path_token( name, &token )) return NULL; + if (access & KEY_WOW64_32KEY) key = find_wow64_subkey( key, &token ); while (token.len) { if (!(key = find_subkey( key, &token, &index ))) @@ -634,6 +662,7 @@ static struct key *open_key( struct key *key, const struct unicode_str *name, un } get_path_token( name, &token ); if (!token.len) break; + if (!(access & KEY_WOW64_64KEY)) key = find_wow64_subkey( key, &token ); if (!(key = follow_symlink( key, 0 ))) { set_error( STATUS_OBJECT_NAME_NOT_FOUND ); @@ -641,6 +670,7 @@ static struct key *open_key( struct key *key, const struct unicode_str *name, un } } + if (!(access & KEY_WOW64_64KEY)) key = find_wow64_subkey( key, &token ); if (!(attributes & OBJ_OPENLINK) && !(key = follow_symlink( key, 0 ))) { set_error( STATUS_OBJECT_NAME_NOT_FOUND ); @@ -654,7 +684,7 @@ static struct key *open_key( struct key *key, const struct unicode_str *name, un /* create a subkey */ static struct key *create_key( struct key *key, const struct unicode_str *name, const struct unicode_str *class, unsigned int options, - unsigned int attributes, int *created ) + unsigned int access, unsigned int attributes, int *created ) { int index; struct unicode_str token, next; @@ -668,6 +698,7 @@ static struct key *create_key( struct key *key, const struct unicode_str *name, token.str = NULL; if (!get_path_token( name, &token )) return NULL; *created = 0; + if (access & KEY_WOW64_32KEY) key = find_wow64_subkey( key, &token ); while (token.len) { struct key *subkey; @@ -675,6 +706,7 @@ static struct key *create_key( struct key *key, const struct unicode_str *name, key = subkey; get_path_token( name, &token ); if (!token.len) break; + if (!(access & KEY_WOW64_64KEY)) key = find_wow64_subkey( key, &token ); if (!(key = follow_symlink( key, 0 ))) { set_error( STATUS_OBJECT_NAME_NOT_FOUND ); @@ -684,6 +716,7 @@ static struct key *create_key( struct key *key, const struct unicode_str *name, if (!token.len) /* the key already exists */ { + if (!(access & KEY_WOW64_64KEY)) key = find_wow64_subkey( key, &token ); if (options & REG_OPTION_CREATE_LINK) { set_error( STATUS_OBJECT_NAME_COLLISION ); @@ -1786,6 +1819,8 @@ DECL_HANDLER(create_key) struct unicode_str name, class; unsigned int access = req->access; + if (!is_wow64_thread( current )) access = (access & ~KEY_WOW64_32KEY) | KEY_WOW64_64KEY; + reply->hkey = 0; if (req->namelen > get_req_data_size()) @@ -1806,7 +1841,8 @@ DECL_HANDLER(create_key) /* NOTE: no access rights are required from the parent handle to create a key */ if ((parent = get_parent_hkey_obj( req->parent ))) { - if ((key = create_key( parent, &name, &class, req->options, req->attributes, &reply->created ))) + if ((key = create_key( parent, &name, &class, req->options, access, + req->attributes, &reply->created ))) { reply->hkey = alloc_handle( current->process, key, access, req->attributes ); release_object( key ); @@ -1822,12 +1858,14 @@ DECL_HANDLER(open_key) struct unicode_str name; unsigned int access = req->access; + if (!is_wow64_thread( current )) access = (access & ~KEY_WOW64_32KEY) | KEY_WOW64_64KEY; + reply->hkey = 0; /* NOTE: no access rights are required to open the parent key, only the child key */ if ((parent = get_parent_hkey_obj( req->parent ))) { get_req_path( &name, !req->parent ); - if ((key = open_key( parent, &name, req->attributes ))) + if ((key = open_key( parent, &name, access, req->attributes ))) { reply->hkey = alloc_handle( current->process, key, access, req->attributes ); release_object( key ); @@ -1961,7 +1999,7 @@ DECL_HANDLER(load_registry) { int dummy; get_req_path( &name, !req->hkey ); - if ((key = create_key( parent, &name, NULL, 0, 0, &dummy ))) + if ((key = create_key( parent, &name, NULL, 0, KEY_WOW64_64KEY, 0, &dummy ))) { load_registry( key, req->file ); release_object( key ); diff --git a/server/thread.c b/server/thread.c index bdb6c3ce62b..a6bc55ae83c 100644 --- a/server/thread.c +++ b/server/thread.c @@ -67,6 +67,7 @@ static const unsigned int supported_cpus = CPU_FLAG(CPU_SPARC); #else #error Unsupported CPU #endif +#define CPU_64BIT_MASK CPU_FLAG(CPU_x86_64) /* thread queues */ @@ -408,6 +409,12 @@ struct thread *get_thread_from_pid( int pid ) return NULL; } +/* determine if the thread is wow64 (32-bit client running on 64-bit server) */ +int is_wow64_thread( struct thread *thread ) +{ + return (supported_cpus & CPU_64BIT_MASK) && !(CPU_FLAG(thread->process->cpu) & CPU_64BIT_MASK); +} + int set_thread_affinity( struct thread *thread, affinity_t affinity ) { int ret = 0; diff --git a/server/thread.h b/server/thread.h index 1e95732d3a0..045a43ca66c 100644 --- a/server/thread.h +++ b/server/thread.h @@ -118,6 +118,7 @@ extern int thread_add_inflight_fd( struct thread *thread, int client, int server extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct thread_snapshot *thread_snap( int *count ); extern struct token *thread_get_impersonation_token( struct thread *thread ); +extern int is_wow64_thread( struct thread *thread ); extern int set_thread_affinity( struct thread *thread, affinity_t affinity ); /* ptrace functions */