server: Initial support for Wow64 registry redirection.
diff --git a/server/registry.c b/server/registry.c
index ea7ea20..ad57bd2 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -84,6 +84,7 @@
#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 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 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 @@
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 @@
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 @@
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 @@
}
/* 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 @@
}
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 @@
}
}
+ 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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
{
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 );