Added support for registry values larger than the server buffer.
When loading a registry file, automatically determine overlap between
key name and file contents based on the first key name.
Removed v1 saving code.
Save USER\.Default separately into ~/.wine/userdef.reg.
diff --git a/server/registry.c b/server/registry.c
index 6d6b034..e4bcf21 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -73,17 +73,6 @@
#define IS_ROOT_HKEY(h) (((h) >= HKEY_ROOT_FIRST) && ((h) <= HKEY_ROOT_LAST))
static struct key *root_keys[NB_ROOT_KEYS];
-static const char * const root_key_names[NB_ROOT_KEYS] =
-{
- "HKEY_CLASSES_ROOT",
- "HKEY_CURRENT_USER",
- "HKEY_LOCAL_MACHINE",
- "HKEY_USERS",
- "HKEY_PERFORMANCE_DATA",
- "HKEY_CURRENT_CONFIG",
- "HKEY_DYN_DATA"
-};
-
/* keys saving level */
/* current_level is the level that is put into all newly created or modified keys */
@@ -91,8 +80,6 @@
static int current_level;
static int saving_level;
-static int saving_version = 1; /* file format version */
-
static struct timeval next_save_time; /* absolute time of next periodic save */
static int save_period; /* delay between periodic saves (ms) */
static struct timeout_user *save_timeout_user; /* saving timer */
@@ -165,14 +152,7 @@
dump_path( key->parent, base, f );
fprintf( f, "\\\\" );
}
-
- if (key->name) dump_strW( key->name, strlenW(key->name), f, "[]" );
- else /* root key */
- {
- int i;
- for (i = 0; i < NB_ROOT_KEYS; i++)
- if (root_keys[i] == key) fprintf( f, "%s", root_key_names[i] );
- }
+ dump_strW( key->name, strlenW(key->name), f, "[]" );
}
/* dump a value to a text file */
@@ -274,7 +254,7 @@
struct key *key = (struct key *)obj;
assert( obj->ops == &key_ops );
- free( key->name );
+ if (key->name) free( key->name );
if (key->class) free( key->class );
for (i = 0; i <= key->last_value; i++)
{
@@ -345,7 +325,6 @@
struct key *key;
if ((key = (struct key *)alloc_object( &key_ops, -1 )))
{
- key->name = NULL;
key->class = NULL;
key->flags = 0;
key->last_subkey = -1;
@@ -357,7 +336,7 @@
key->level = current_level;
key->modif = modif;
key->parent = NULL;
- if (name && !(key->name = strdupW( name )))
+ if (!(key->name = strdupW( name )))
{
release_object( key );
key = NULL;
@@ -719,16 +698,42 @@
}
/* set a key value */
-static void set_value( struct key *key, WCHAR *name, int type, int datalen, void *data )
+static void set_value( struct key *key, WCHAR *name, int type, unsigned int total_len,
+ unsigned int offset, unsigned int data_len, void *data )
{
struct key_value *value;
void *ptr = NULL;
-
- /* first copy the data */
- if (datalen)
+
+ if (data_len + offset > total_len)
{
- if (!(ptr = mem_alloc( datalen ))) return;
- memcpy( ptr, data, datalen );
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+
+ if (offset) /* adding data to an existing value */
+ {
+ int index;
+ if (!(value = find_value( key, name, &index )))
+ {
+ set_error( STATUS_OBJECT_NAME_NOT_FOUND );
+ return;
+ }
+ if (value->len != total_len)
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+ memcpy( (char *)value->data + offset, data, data_len );
+ if (debug_level > 1) dump_operation( key, value, "Set" );
+ return;
+ }
+
+ /* first copy the data */
+ if (total_len)
+ {
+ if (!(ptr = mem_alloc( total_len ))) return;
+ memcpy( ptr, data, data_len );
+ if (data_len < total_len) memset( (char *)ptr + data_len, 0, total_len - data_len );
}
if (!(value = insert_value( key, name )))
@@ -738,14 +743,15 @@
}
if (value->data) free( value->data ); /* already existing, free previous data */
value->type = type;
- value->len = datalen;
+ value->len = total_len;
value->data = ptr;
touch_key( key );
if (debug_level > 1) dump_operation( key, value, "Set" );
}
/* get a key value */
-static void get_value( struct key *key, WCHAR *name, int *type, int *len, void *data )
+static void get_value( struct key *key, WCHAR *name, unsigned int offset,
+ unsigned int maxlen, int *type, int *len, void *data )
{
struct key_value *value;
int index;
@@ -754,7 +760,11 @@
{
*type = value->type;
*len = value->len;
- if (value->data) memcpy( data, value->data, value->len );
+ if (value->data && offset < value->len)
+ {
+ if (maxlen > value->len - offset) maxlen = value->len - offset;
+ memcpy( data, (char *)value->data + offset, maxlen );
+ }
if (debug_level > 1) dump_operation( key, value, "Get" );
}
else
@@ -765,7 +775,8 @@
}
/* enumerate a key value */
-static void enum_value( struct key *key, int i, WCHAR *name, int *type, int *len, void *data )
+static void enum_value( struct key *key, int i, WCHAR *name, unsigned int offset,
+ unsigned int maxlen, int *type, int *len, void *data )
{
struct key_value *value;
@@ -776,7 +787,11 @@
strcpyW( name, value->name );
*type = value->type;
*len = value->len;
- if (value->data) memcpy( data, value->data, value->len );
+ if (value->data && offset < value->len)
+ {
+ if (maxlen > value->len - offset) maxlen = value->len - offset;
+ memcpy( data, (char *)value->data + offset, maxlen );
+ }
if (debug_level > 1) dump_operation( key, value, "Enum" );
}
}
@@ -890,9 +905,17 @@
break;
/* dynamically generated keys */
case HKEY_PERFORMANCE_DATA:
+ {
+ static const WCHAR name[] = { 'P','E','R','F','D','A','T','A',0 }; /* FIXME */
+ key = alloc_key( name, time(NULL) );
+ }
+ break;
case HKEY_DYN_DATA:
- key = alloc_key( NULL, time(NULL) );
- break;
+ {
+ static const WCHAR name[] = { 'D','Y','N','D','A','T','A',0 }; /* FIXME */
+ key = alloc_key( name, time(NULL) );
+ }
+ break;
default:
key = NULL;
assert(0);
@@ -1067,7 +1090,8 @@
}
/* load and create a key from the input file */
-static struct key *load_key( struct key *base, const char *buffer, struct file_load_info *info )
+static struct key *load_key( struct key *base, const char *buffer, unsigned int options,
+ int prefix_len, struct file_load_info *info )
{
WCHAR *p;
int res, len, modif;
@@ -1082,8 +1106,20 @@
}
if (!sscanf( buffer + res, " %d", &modif )) modif = time(NULL);
- for (p = (WCHAR *)info->tmp; *p; p++) if (*p == '\\') { p++; break; }
- return create_key( base, p, len - ((char *)p - info->tmp), NULL, 0, modif, &res );
+ p = (WCHAR *)info->tmp;
+ while (prefix_len && *p) { if (*p++ == '\\') prefix_len--; }
+
+ if (!*p)
+ {
+ if (prefix_len > 1)
+ {
+ file_read_error( "Malformed key", info );
+ return NULL;
+ }
+ /* empty key name, return base key */
+ return (struct key *)grab_object( base );
+ }
+ return create_key( base, p, len - ((char *)p - info->tmp), NULL, options, modif, &res );
}
/* parse a comma-separated list of hex digits */
@@ -1198,12 +1234,41 @@
return 0;
}
+/* return the length (in path elements) of name that is part of the key name */
+/* for instance if key is USER\foo\bar and name is foo\bar\baz, return 2 */
+static int get_prefix_len( struct key *key, const char *name, struct file_load_info *info )
+{
+ WCHAR *p;
+ int res;
+ int len = strlen(name) * sizeof(WCHAR);
+ if (!get_file_tmp_space( info, len )) return NULL;
+
+ if ((res = parse_strW( (WCHAR *)info->tmp, &len, name, ']' )) == -1)
+ {
+ file_read_error( "Malformed key", info );
+ return NULL;
+ }
+ for (p = (WCHAR *)info->tmp; *p; p++) if (*p == '\\') break;
+ *p = 0;
+ for (res = 1; key; res++)
+ {
+ if (!strcmpiW( (WCHAR *)info->tmp, key->name )) break;
+ key = key->parent;
+ }
+ if (!key) res = 0; /* no matching name */
+ return res;
+}
+
/* load all the keys from the input file */
static void load_keys( struct key *key, FILE *f )
{
struct key *subkey = NULL;
struct file_load_info info;
char *p;
+ unsigned int options = 0;
+ int prefix_len = -1; /* number of key name prefixes to skip */
+
+ if (key->flags & KEY_VOLATILE) options |= REG_OPTION_VOLATILE;
info.file = f;
info.len = 4;
@@ -1230,7 +1295,9 @@
{
case '[': /* new key */
if (subkey) release_object( subkey );
- subkey = load_key( key, p + 1, &info );
+ if (prefix_len == -1) prefix_len = get_prefix_len( key, p + 1, &info );
+ if (!(subkey = load_key( key, p + 1, options, prefix_len, &info )))
+ file_read_error( "Error creating key", &info );
break;
case '@': /* default value */
case '\"': /* value */
@@ -1288,50 +1355,14 @@
return max;
}
-/* dump a string to a registry save file in the old v1 format */
-static void save_string_v1( LPCWSTR str, FILE *f, int len )
+/* save a registry branch to a file */
+static void save_all_subkeys( struct key *key, FILE *f )
{
- if (!str) return;
- while ((len == -1) ? *str : (*str && len--))
- {
- if ((*str > 0x7f) || (*str == '\n') || (*str == '='))
- fprintf( f, "\\u%04x", *str );
- else
- {
- if (*str == '\\') fputc( '\\', f );
- fputc( (char)*str, f );
- }
- str++;
- }
-}
-
-/* save a registry and all its subkeys to a text file in the old v1 format */
-static void save_subkeys_v1( struct key *key, int nesting, FILE *f )
-{
- int i, j;
-
- if (key->flags & KEY_VOLATILE) return;
- if (key->level < saving_level) return;
- for (i = 0; i <= key->last_value; i++)
- {
- struct key_value *value = &key->values[i];
- for (j = nesting; j > 0; j --) fputc( '\t', f );
- save_string_v1( value->name, f, -1 );
- fprintf( f, "=%d,%d,", value->type, 0 );
- if (value->type == REG_SZ || value->type == REG_EXPAND_SZ)
- save_string_v1( (LPWSTR)value->data, f, value->len / 2 );
- else
- for (j = 0; j < value->len; j++)
- fprintf( f, "%02x", *((unsigned char *)value->data + j) );
- fputc( '\n', f );
- }
- for (i = 0; i <= key->last_subkey; i++)
- {
- for (j = nesting; j > 0; j --) fputc( '\t', f );
- save_string_v1( key->subkeys[i]->name, f, -1 );
- fputc( '\n', f );
- save_subkeys_v1( key->subkeys[i], nesting + 1, f );
- }
+ fprintf( f, "WINE REGISTRY Version 2\n" );
+ fprintf( f, ";; All keys relative to " );
+ dump_path( key, NULL, f );
+ fprintf( f, "\n" );
+ save_subkeys( key, key, f );
}
/* save a registry branch to a file handle */
@@ -1353,13 +1384,7 @@
FILE *f = fdopen( fd, "w" );
if (f)
{
- fprintf( f, "WINE REGISTRY Version %d\n", saving_version );
- if (saving_version == 2) save_subkeys( key, key, f );
- else
- {
- update_level( key );
- save_subkeys_v1( key, 0, f );
- }
+ save_all_subkeys( key, f );
if (fclose( f )) file_set_error();
}
else
@@ -1446,13 +1471,7 @@
dump_operation( key, NULL, "saving" );
}
- fprintf( f, "WINE REGISTRY Version %d\n", saving_version );
- if (saving_version == 2) save_subkeys( key, key, f );
- else
- {
- update_level( key );
- save_subkeys_v1( key, 0, f );
- }
+ save_all_subkeys( key, f );
ret = !fclose(f);
if (tmp)
@@ -1594,16 +1613,14 @@
DECL_HANDLER(set_key_value)
{
struct key *key;
- int max = get_req_size( req, req->data, sizeof(req->data[0]) );
- int datalen = req->len;
- if (datalen > max)
- {
- set_error( STATUS_NO_MEMORY ); /* FIXME */
- return;
- }
+ unsigned int max = get_req_size( req, req->data, sizeof(req->data[0]) );
+ unsigned int datalen = req->len;
+
+ if (datalen > max) datalen = max;
if ((key = get_hkey_obj( req->hkey, KEY_SET_VALUE )))
{
- set_value( key, copy_path( req->name ), req->type, datalen, req->data );
+ set_value( key, copy_path( req->name ), req->type, req->total,
+ req->offset, datalen, req->data );
release_object( key );
}
}
@@ -1612,11 +1629,13 @@
DECL_HANDLER(get_key_value)
{
struct key *key;
+ unsigned int max = get_req_size( req, req->data, sizeof(req->data[0]) );
req->len = 0;
if ((key = get_hkey_obj( req->hkey, KEY_QUERY_VALUE )))
{
- get_value( key, copy_path( req->name ), &req->type, &req->len, req->data );
+ get_value( key, copy_path( req->name ), req->offset, max,
+ &req->type, &req->len, req->data );
release_object( key );
}
}
@@ -1625,12 +1644,14 @@
DECL_HANDLER(enum_key_value)
{
struct key *key;
+ unsigned int max = get_req_size( req, req->data, sizeof(req->data[0]) );
req->len = 0;
req->name[0] = 0;
if ((key = get_hkey_obj( req->hkey, KEY_QUERY_VALUE )))
{
- enum_value( key, req->index, req->name, &req->type, &req->len, req->data );
+ enum_value( key, req->index, req->name, req->offset, max,
+ &req->type, &req->len, req->data );
release_object( key );
}
}
@@ -1682,7 +1703,6 @@
{
current_level = req->current;
saving_level = req->saving;
- saving_version = req->version;
/* set periodic save timer */