Use struct unicode_str instead of null-terminated strings where
possible, and remove constraints on total key path length.

diff --git a/server/registry.c b/server/registry.c
index b34301d..eef7dc9 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -19,8 +19,6 @@
  */
 
 /* To do:
- * - behavior with deleted keys
- * - values larger than request buffer
  * - symbolic links
  */
 
@@ -65,6 +63,8 @@
     struct object     obj;         /* object header */
     WCHAR            *name;        /* key name */
     WCHAR            *class;       /* key class */
+    unsigned short    namelen;     /* length of key name */
+    unsigned short    classlen;    /* length of class name */
     struct key       *parent;      /* parent key */
     int               last_subkey; /* last in use subkey */
     int               nb_subkeys;  /* count of allocated subkeys */
@@ -86,7 +86,8 @@
 struct key_value
 {
     WCHAR            *name;    /* value name */
-    int               type;    /* value type */
+    unsigned short    namelen; /* length of value name */
+    unsigned short    type;    /* value type */
     size_t            len;     /* value data length in bytes */
     void             *data;    /* pointer to value data */
 };
@@ -94,6 +95,8 @@
 #define MIN_SUBKEYS  8   /* min. number of allocated subkeys per key */
 #define MIN_VALUES   8   /* min. number of allocated values per key */
 
+#define MAX_NAME_LEN  MAX_PATH  /* max. length of a key name */
+#define MAX_VALUE_LEN MAX_PATH  /* max. length of a value name */
 
 /* the root of the registry tree */
 static struct key *root_key;
@@ -118,12 +121,13 @@
 /* information about a file being loaded */
 struct file_load_info
 {
-    FILE *file;    /* input file */
-    char *buffer;  /* line buffer */
-    int   len;     /* buffer length */
-    int   line;    /* current input line */
-    WCHAR *tmp;    /* temp buffer to use while parsing input */
-    size_t tmplen; /* length of temp buffer */
+    const char *filename; /* input file name */
+    FILE       *file;     /* input file */
+    char       *buffer;   /* line buffer */
+    int         len;      /* buffer length */
+    int         line;     /* current input line */
+    WCHAR      *tmp;      /* temp buffer to use while parsing input */
+    size_t      tmplen;   /* length of temp buffer */
 };
 
 
@@ -169,7 +173,7 @@
         dump_path( key->parent, base, f );
         fprintf( f, "\\\\" );
     }
-    dump_strW( key->name, strlenW(key->name), f, "[]" );
+    dump_strW( key->name, key->namelen / sizeof(WCHAR), f, "[]" );
 }
 
 /* dump a value to a text file */
@@ -178,10 +182,10 @@
     unsigned int i;
     int count;
 
-    if (value->name[0])
+    if (value->namelen)
     {
         fputc( '\"', f );
-        count = 1 + dump_strW( value->name, strlenW(value->name), f, "\"\"" );
+        count = 1 + dump_strW( value->name, value->namelen / sizeof(WCHAR), f, "\"\"" );
         count += fprintf( f, "\"=" );
     }
     else count = fprintf( f, "@=" );
@@ -313,7 +317,7 @@
     if (key->class) free( key->class );
     for (i = 0; i <= key->last_value; i++)
     {
-        free( key->values[i].name );
+        if (key->values[i].name) free( key->values[i].name );
         if (key->values[i].data) free( key->values[i].data );
     }
     if (key->values) free( key->values );
@@ -331,81 +335,59 @@
     }
 }
 
-/* duplicate a key path */
-/* returns a pointer to a static buffer, so only useable once per request */
-static WCHAR *copy_path( const WCHAR *path, size_t len, int skip_root )
+/* get the request vararg as registry path */
+inline static void get_req_path( struct unicode_str *str, int skip_root )
 {
-    static WCHAR buffer[MAX_PATH+1];
-    static const WCHAR root_name[] = { '\\','R','e','g','i','s','t','r','y','\\',0 };
+    static const WCHAR root_name[] = { '\\','R','e','g','i','s','t','r','y','\\' };
 
-    if (len > sizeof(buffer)-sizeof(buffer[0]))
-    {
-        set_error( STATUS_BUFFER_OVERFLOW );
-        return NULL;
-    }
-    memcpy( buffer, path, len );
-    buffer[len / sizeof(WCHAR)] = 0;
-    if (skip_root && !strncmpiW( buffer, root_name, 10 )) return buffer + 10;
-    return buffer;
-}
+    str->str = get_req_data();
+    str->len = (get_req_data_size() / sizeof(WCHAR)) * sizeof(WCHAR);
 
-/* copy a path from the request buffer */
-static WCHAR *copy_req_path( size_t len, int skip_root )
-{
-    const WCHAR *name_ptr = get_req_data();
-    if (len > get_req_data_size())
+    if (skip_root && str->len >= sizeof(root_name) &&
+        !memicmpW( str->str, root_name, sizeof(root_name)/sizeof(WCHAR) ))
     {
-        fatal_protocol_error( current, "copy_req_path: invalid length %d/%d\n",
-                              len, get_req_data_size() );
-        return NULL;
+        str->str += sizeof(root_name)/sizeof(WCHAR);
+        str->len -= sizeof(root_name);
     }
-    return copy_path( name_ptr, len, skip_root );
 }
 
 /* return the next token in a given path */
-/* returns a pointer to a static buffer, so only useable once per request */
-static WCHAR *get_path_token( WCHAR *initpath )
+/* token->str must point inside the path, or be NULL for the first call */
+static struct unicode_str *get_path_token( const struct unicode_str *path, struct unicode_str *token )
 {
-    static WCHAR *path;
-    WCHAR *ret;
+    size_t i = 0, len = path->len / sizeof(WCHAR);
 
-    if (initpath)
+    if (!token->str)  /* first time */
     {
         /* path cannot start with a backslash */
-        if (*initpath == '\\')
+        if (len && path->str[0] == '\\')
         {
             set_error( STATUS_OBJECT_PATH_INVALID );
             return NULL;
         }
-        path = initpath;
     }
-    else while (*path == '\\') path++;
-
-    ret = path;
-    while (*path && *path != '\\') path++;
-    if (*path) *path++ = 0;
-    return ret;
-}
-
-/* duplicate a Unicode string from the request buffer */
-static WCHAR *req_strdupW( const void *req, const WCHAR *str, size_t len )
-{
-    WCHAR *name;
-    if ((name = mem_alloc( len + sizeof(WCHAR) )) != NULL)
+    else
     {
-        memcpy( name, str, len );
-        name[len / sizeof(WCHAR)] = 0;
+        i = token->str - path->str;
+        i += token->len / sizeof(WCHAR);
+        while (i < len && path->str[i] == '\\') i++;
     }
-    return name;
+    token->str = path->str + i;
+    while (i < len && path->str[i] != '\\') i++;
+    token->len = (path->str + i - token->str) * sizeof(WCHAR);
+    return token;
 }
 
 /* allocate a key object */
-static struct key *alloc_key( const WCHAR *name, time_t modif )
+static struct key *alloc_key( const struct unicode_str *name, time_t modif )
 {
     struct key *key;
     if ((key = alloc_object( &key_ops )))
     {
+        key->name        = NULL;
         key->class       = NULL;
+        key->namelen     = name->len;
+        key->classlen    = 0;
         key->flags       = 0;
         key->last_subkey = -1;
         key->nb_subkeys  = 0;
@@ -416,7 +398,7 @@
         key->modif       = modif;
         key->parent      = NULL;
         list_init( &key->notify_list );
-        if (!(key->name = strdupW( name )))
+        if (name->len && !(key->name = memdup( name->str, name->len )))
         {
             release_object( key );
             key = NULL;
@@ -500,11 +482,17 @@
 }
 
 /* allocate a subkey for a given key, and return its index */
-static struct key *alloc_subkey( struct key *parent, const WCHAR *name, int index, time_t modif )
+static struct key *alloc_subkey( struct key *parent, const struct unicode_str *name,
+                                 int index, time_t modif )
 {
     struct key *key;
     int i;
 
+    if (name->len > MAX_NAME_LEN * sizeof(WCHAR))
+    {
+        set_error( STATUS_NAME_TOO_LONG );
+        return NULL;
+    }
     if (parent->last_subkey + 1 == parent->nb_subkeys)
     {
         /* need to grow the array */
@@ -550,16 +538,20 @@
 }
 
 /* find the named child of a given key and return its index */
-static struct key *find_subkey( const struct key *key, const WCHAR *name, int *index )
+static struct key *find_subkey( const struct key *key, const struct unicode_str *name, int *index )
 {
     int i, min, max, res;
+    size_t len;
 
     min = 0;
     max = key->last_subkey;
     while (min <= max)
     {
         i = (min + max) / 2;
-        if (!(res = strcmpiW( key->subkeys[i]->name, name )))
+        len = min( key->subkeys[i]->namelen, name->len );
+        res = memicmpW( key->subkeys[i]->name, name->str, len / sizeof(WCHAR) );
+        if (!res) res = key->subkeys[i]->namelen - name->len;
+        if (!res)
         {
             *index = i;
             return key->subkeys[i];
@@ -572,21 +564,21 @@
 }
 
 /* open a subkey */
-/* warning: the key name must be writeable (use copy_path) */
-static struct key *open_key( struct key *key, WCHAR *name )
+static struct key *open_key( struct key *key, const struct unicode_str *name )
 {
     int index;
-    WCHAR *path;
+    struct unicode_str token;
 
-    if (!(path = get_path_token( name ))) return NULL;
-    while (*path)
+    token.str = NULL;
+    if (!get_path_token( name, &token )) return NULL;
+    while (token.len)
     {
-        if (!(key = find_subkey( key, path, &index )))
+        if (!(key = find_subkey( key, &token, &index )))
         {
             set_error( STATUS_OBJECT_NAME_NOT_FOUND );
             break;
         }
-        path = get_path_token( NULL );
+        get_path_token( name, &token );
     }
 
     if (debug_level > 1) dump_operation( key, NULL, "Open" );
@@ -595,13 +587,12 @@
 }
 
 /* create a subkey */
-/* warning: the key name must be writeable (use copy_path) */
-static struct key *create_key( struct key *key, WCHAR *name, WCHAR *class,
-                               int flags, time_t modif, int *created )
+static struct key *create_key( struct key *key, const struct unicode_str *name,
+                               const struct unicode_str *class, int flags, time_t modif, int *created )
 {
     struct key *base;
-    int base_idx, index;
-    WCHAR *path;
+    int index;
+    struct unicode_str token;
 
     if (key->flags & KEY_DELETED) /* we cannot create a subkey under a deleted key */
     {
@@ -615,38 +606,44 @@
     }
     if (!modif) modif = time(NULL);
 
-    if (!(path = get_path_token( name ))) return NULL;
+    token.str = NULL;
+    if (!get_path_token( name, &token )) return NULL;
     *created = 0;
-    while (*path)
+    while (token.len)
     {
         struct key *subkey;
-        if (!(subkey = find_subkey( key, path, &index ))) break;
+        if (!(subkey = find_subkey( key, &token, &index ))) break;
         key = subkey;
-        path = get_path_token( NULL );
+        get_path_token( name, &token );
     }
 
     /* create the remaining part */
 
-    if (!*path) goto done;
+    if (!token.len) goto done;
     *created = 1;
     if (flags & KEY_DIRTY) make_dirty( key );
+    if (!(key = alloc_subkey( key, &token, index, modif ))) return NULL;
     base = key;
-    base_idx = index;
-    key = alloc_subkey( key, path, index, modif );
-    while (key)
+    for (;;)
     {
         key->flags |= flags;
-        path = get_path_token( NULL );
-        if (!*path) goto done;
+        get_path_token( name, &token );
+        if (!token.len) break;
         /* we know the index is always 0 in a new key */
-        key = alloc_subkey( key, path, 0, modif );
+        if (!(key = alloc_subkey( key, &token, 0, modif )))
+        {
+            free_subkey( base, index );
+            return NULL;
+        }
     }
-    if (base_idx != -1) free_subkey( base, base_idx );
-    return NULL;
 
  done:
     if (debug_level > 1) dump_operation( key, NULL, "Create" );
-    if (class) key->class = strdupW(class);
+    if (class && class->len)
+    {
+        key->classlen = class->len;
+        if (!(key->class = memdup( class->str, key->classlen ))) key->classlen = 0;
+    }
     grab_object( key );
     return key;
 }
@@ -659,7 +656,7 @@
     size_t len, namelen, classlen;
     int max_subkey = 0, max_class = 0;
     int max_value = 0, max_data = 0;
-    WCHAR *data;
+    char *data;
 
     if (index != -1)  /* -1 means use the specified key directly */
     {
@@ -671,8 +668,8 @@
         key = key->subkeys[index];
     }
 
-    namelen = strlenW(key->name) * sizeof(WCHAR);
-    classlen = key->class ? strlenW(key->class) * sizeof(WCHAR) : 0;
+    namelen = key->namelen;
+    classlen = key->classlen;
 
     switch(info_class)
     {
@@ -689,15 +686,14 @@
         for (i = 0; i <= key->last_subkey; i++)
         {
             struct key *subkey = key->subkeys[i];
-            len = strlenW( subkey->name );
+            len = subkey->namelen / sizeof(WCHAR);
             if (len > max_subkey) max_subkey = len;
-            if (!subkey->class) continue;
-            len = strlenW( subkey->class );
+            len = subkey->classlen / sizeof(WCHAR);
             if (len > max_class) max_class = len;
         }
         for (i = 0; i <= key->last_value; i++)
         {
-            len = strlenW( key->values[i].name );
+            len = key->values[i].namelen / sizeof(WCHAR);
             if (len > max_value) max_value = len;
             len = key->values[i].len;
             if (len > max_data) max_data = len;
@@ -724,7 +720,7 @@
         {
             reply->namelen = namelen;
             memcpy( data, key->name, namelen );
-            memcpy( (char *)data + namelen, key->class, len - namelen );
+            memcpy( data + namelen, key->class, len - namelen );
         }
         else
         {
@@ -800,16 +796,20 @@
 }
 
 /* find the named value of a given key and return its index in the array */
-static struct key_value *find_value( const struct key *key, const WCHAR *name, int *index )
+static struct key_value *find_value( const struct key *key, const struct unicode_str *name, int *index )
 {
     int i, min, max, res;
+    size_t len;
 
     min = 0;
     max = key->last_value;
     while (min <= max)
     {
         i = (min + max) / 2;
-        if (!(res = strcmpiW( key->values[i].name, name )))
+        len = min( key->values[i].namelen, name->len );
+        res = memicmpW( key->values[i].name, name->str, len / sizeof(WCHAR) );
+        if (!res) res = key->values[i].namelen - name->len;
+        if (!res)
         {
             *index = i;
             return &key->values[i];
@@ -822,27 +822,34 @@
 }
 
 /* insert a new value; the index must have been returned by find_value */
-static struct key_value *insert_value( struct key *key, const WCHAR *name, int index )
+static struct key_value *insert_value( struct key *key, const struct unicode_str *name, int index )
 {
     struct key_value *value;
-    WCHAR *new_name;
+    WCHAR *new_name = NULL;
     int i;
 
+    if (name->len > MAX_VALUE_LEN * sizeof(WCHAR))
+    {
+        set_error( STATUS_NAME_TOO_LONG );
+        return NULL;
+    }
     if (key->last_value + 1 == key->nb_values)
     {
         if (!grow_values( key )) return NULL;
     }
-    if (!(new_name = strdupW(name))) return NULL;
+    if (name->len && !(new_name = memdup( name->str, name->len ))) return NULL;
     for (i = ++key->last_value; i > index; i--) key->values[i] = key->values[i - 1];
     value = &key->values[index];
-    value->name = new_name;
-    value->len  = 0;
-    value->data = NULL;
+    value->name    = new_name;
+    value->namelen = name->len;
+    value->len     = 0;
+    value->data    = NULL;
     return value;
 }
 
 /* set a key value */
-static void set_value( struct key *key, WCHAR *name, int type, const void *data, size_t len )
+static void set_value( struct key *key, const struct unicode_str *name,
+                       int type, const void *data, size_t len )
 {
     struct key_value *value;
     void *ptr = NULL;
@@ -879,7 +886,7 @@
 }
 
 /* get a key value */
-static void get_value( struct key *key, const WCHAR *name, int *type, unsigned int *len )
+static void get_value( struct key *key, const struct unicode_str *name, int *type, unsigned int *len )
 {
     struct key_value *value;
     int index;
@@ -911,7 +918,7 @@
 
         value = &key->values[i];
         reply->type = value->type;
-        namelen = strlenW( value->name ) * sizeof(WCHAR);
+        namelen = value->namelen;
 
         switch(info_class)
         {
@@ -950,7 +957,7 @@
 }
 
 /* delete a value */
-static void delete_value( struct key *key, const WCHAR *name )
+static void delete_value( struct key *key, const struct unicode_str *name )
 {
     struct key_value *value;
     int i, index, nb_values;
@@ -961,7 +968,7 @@
         return;
     }
     if (debug_level > 1) dump_operation( key, value, "Delete" );
-    free( value->name );
+    if (value->name) free( value->name );
     if (value->data) free( value->data );
     for (i = index; i < key->last_value; i++) key->values[i] = key->values[i + 1];
     key->last_value--;
@@ -1038,14 +1045,17 @@
 /* report an error while loading an input file */
 static void file_read_error( const char *err, struct file_load_info *info )
 {
-    fprintf( stderr, "Line %d: %s '%s'\n", info->line, err, info->buffer );
+    if (info->filename)
+        fprintf( stderr, "%s:%d: %s '%s'\n", info->filename, info->line, err, info->buffer );
+    else
+        fprintf( stderr, "<fd>:%d: %s '%s'\n", info->line, err, info->buffer );
 }
 
 /* parse an escaped string back into Unicode */
 /* return the number of chars read from the input, or -1 on output overflow */
-static int parse_strW( WCHAR *dest, int *len, const char *src, char endchar )
+static int parse_strW( WCHAR *dest, size_t *len, const char *src, char endchar )
 {
-    int count = sizeof(WCHAR);  /* for terminating null */
+    size_t count = sizeof(WCHAR);  /* for terminating null */
     const char *p = src;
     while (*p && *p != endchar)
     {
@@ -1138,10 +1148,11 @@
                              int prefix_len, struct file_load_info *info,
                              int default_modif )
 {
-    WCHAR *p, *name;
-    int res, len, modif;
+    WCHAR *p;
+    struct unicode_str name;
+    int res, modif;
+    size_t len = strlen(buffer) * sizeof(WCHAR);
 
-    len = strlen(buffer) * sizeof(WCHAR);
     if (!get_file_tmp_space( info, len )) return NULL;
 
     if ((res = parse_strW( info->tmp, &len, buffer, ']' )) == -1)
@@ -1164,19 +1175,16 @@
         /* empty key name, return base key */
         return (struct key *)grab_object( base );
     }
-    if (!(name = copy_path( p, len - (p - info->tmp) * sizeof(WCHAR), 0 )))
-    {
-        file_read_error( "Key is too long", info );
-        return NULL;
-    }
-    return create_key( base, name, NULL, flags, modif, &res );
+    name.str = p;
+    name.len = len - (p - info->tmp + 1) * sizeof(WCHAR);
+    return create_key( base, &name, NULL, flags, modif, &res );
 }
 
 /* parse a comma-separated list of hex digits */
-static int parse_hex( unsigned char *dest, int *len, const char *buffer )
+static int parse_hex( unsigned char *dest, size_t *len, const char *buffer )
 {
     const char *p = buffer;
-    int count = 0;
+    size_t count = 0;
     while (isxdigit(*p))
     {
         int val;
@@ -1194,30 +1202,32 @@
 }
 
 /* parse a value name and create the corresponding value */
-static struct key_value *parse_value_name( struct key *key, const char *buffer, int *len,
+static struct key_value *parse_value_name( struct key *key, const char *buffer, size_t *len,
                                            struct file_load_info *info )
 {
     struct key_value *value;
-    int index, maxlen;
+    struct unicode_str name;
+    int index;
+    size_t maxlen = strlen(buffer) * sizeof(WCHAR);
 
-    maxlen = strlen(buffer) * sizeof(WCHAR);
     if (!get_file_tmp_space( info, maxlen )) return NULL;
+    name.str = info->tmp;
     if (buffer[0] == '@')
     {
-        info->tmp[0] = 0;
+        name.len = 0;
         *len = 1;
     }
     else
     {
         if ((*len = parse_strW( info->tmp, &maxlen, buffer + 1, '\"' )) == -1) goto error;
+        name.len = maxlen - sizeof(WCHAR);
         (*len)++;  /* for initial quote */
     }
     while (isspace(buffer[*len])) (*len)++;
     if (buffer[*len] != '=') goto error;
     (*len)++;
     while (isspace(buffer[*len])) (*len)++;
-    if (!(value = find_value( key, info->tmp, &index )))
-        value = insert_value( key, info->tmp, index );
+    if (!(value = find_value( key, &name, &index ))) value = insert_value( key, &name, index );
     return value;
 
  error:
@@ -1230,8 +1240,8 @@
 {
     DWORD dw;
     void *ptr, *newptr;
-    int maxlen, len, res;
-    int type, parse_type;
+    int res, type, parse_type;
+    size_t maxlen, len;
     struct key_value *value;
 
     if (!(value = parse_value_name( key, buffer, &len, info ))) return 0;
@@ -1296,7 +1306,8 @@
 {
     WCHAR *p;
     int res;
-    int len = strlen(name) * sizeof(WCHAR);
+    size_t len = strlen(name) * sizeof(WCHAR);
+
     if (!get_file_tmp_space( info, len )) return 0;
 
     if ((res = parse_strW( info->tmp, &len, name, ']' )) == -1)
@@ -1305,10 +1316,10 @@
         return 0;
     }
     for (p = info->tmp; *p; p++) if (*p == '\\') break;
-    *p = 0;
+    len = (p - info->tmp) * sizeof(WCHAR);
     for (res = 1; key != root_key; res++)
     {
-        if (!strcmpiW( info->tmp, key->name )) break;
+        if (len == key->namelen && !memicmpW( info->tmp, key->name, len / sizeof(WCHAR) )) break;
         key = key->parent;
     }
     if (key == root_key) res = 0;  /* no matching name */
@@ -1317,7 +1328,7 @@
 
 /* load all the keys from the input file */
 /* prefix_len is the number of key name prefixes to skip, or -1 for autodetection */
-static void load_keys( struct key *key, FILE *f, int prefix_len )
+static void load_keys( struct key *key, const char *filename, FILE *f, int prefix_len )
 {
     struct key *subkey = NULL;
     struct file_load_info info;
@@ -1325,6 +1336,7 @@
     int default_modif = time(NULL);
     int flags = (key->flags & KEY_VOLATILE) ? KEY_VOLATILE : KEY_DIRTY;
 
+    info.filename = filename;
     info.file   = f;
     info.len    = 4;
     info.tmplen = 4;
@@ -1390,7 +1402,7 @@
         FILE *f = fdopen( fd, "r" );
         if (f)
         {
-            load_keys( key, f, -1 );
+            load_keys( key, NULL, f, -1 );
             fclose( f );
         }
         else file_set_error();
@@ -1404,24 +1416,22 @@
 
     if ((f = fopen( filename, "r" )))
     {
-        load_keys( key, f, 0 );
+        load_keys( key, filename, f, 0 );
         fclose( f );
         if (get_error() == STATUS_NOT_REGISTRY_FILE)
-            fatal_error( "%s is not a valid registry file\n", filename );
-        if (get_error())
-            fatal_error( "loading %s failed with error %x\n", filename, get_error() );
+        {
+            fprintf( stderr, "%s is not a valid registry file\n", filename );
+            return;
+        }
     }
 
-    if (!(key->flags & KEY_VOLATILE))
-    {
-        assert( save_branch_count < MAX_SAVE_BRANCH_INFO );
+    assert( save_branch_count < MAX_SAVE_BRANCH_INFO );
 
-        if ((save_branch_info[save_branch_count].path = strdup( filename )))
-            save_branch_info[save_branch_count++].key = (struct key *)grab_object( key );
-    }
+    if ((save_branch_info[save_branch_count].path = strdup( filename )))
+        save_branch_info[save_branch_count++].key = (struct key *)grab_object( key );
 }
 
-static WCHAR *format_user_registry_path( const SID *sid )
+static WCHAR *format_user_registry_path( const SID *sid, struct unicode_str *path )
 {
     static const WCHAR prefixW[] = {'U','s','e','r','\\','S',0};
     static const WCHAR formatW[] = {'-','%','u',0};
@@ -1439,16 +1449,22 @@
     for (i = 0; i < sid->SubAuthorityCount; i++)
         p += sprintfW( p, formatW, sid->SubAuthority[i] );
 
-    return memdup( buffer, (p + 1 - buffer) * sizeof(WCHAR) );
+    path->len = (p - buffer) * sizeof(WCHAR);
+    path->str = p = memdup( buffer, path->len );
+    return p;
 }
 
 /* registry initialisation */
 void init_registry(void)
 {
-    static const WCHAR root_name[] = { 0 };
     static const WCHAR HKLM[] = { 'M','a','c','h','i','n','e' };
     static const WCHAR HKU_default[] = { 'U','s','e','r','\\','.','D','e','f','a','u','l','t' };
+    static const struct unicode_str root_name = { NULL, 0 };
+    static const struct unicode_str HKLM_name = { HKLM, sizeof(HKLM) };
+    static const struct unicode_str HKU_name = { HKU_default, sizeof(HKU_default) };
+
     WCHAR *current_user_path;
+    struct unicode_str current_user_str;
 
     const char *config = wine_get_config_dir();
     char *p, *filename;
@@ -1456,7 +1472,7 @@
     int dummy;
 
     /* create the root key */
-    root_key = alloc_key( root_name, time(NULL) );
+    root_key = alloc_key( &root_name, time(NULL) );
     assert( root_key );
 
     if (!(filename = malloc( strlen(config) + 16 ))) fatal_error( "out of memory\n" );
@@ -1465,8 +1481,7 @@
 
     /* load system.reg into Registry\Machine */
 
-    if (!(key = create_key( root_key, copy_path( HKLM, sizeof(HKLM), 0 ),
-                            NULL, 0, time(NULL), &dummy )))
+    if (!(key = create_key( root_key, &HKLM_name, NULL, 0, time(NULL), &dummy )))
         fatal_error( "could not create Machine registry key\n" );
 
     strcpy( p, "/system.reg" );
@@ -1475,8 +1490,7 @@
 
     /* load userdef.reg into Registry\User\.Default */
 
-    if (!(key = create_key( root_key, copy_path( HKU_default, sizeof(HKU_default), 0 ),
-                            NULL, 0, time(NULL), &dummy )))
+    if (!(key = create_key( root_key, &HKU_name, NULL, 0, time(NULL), &dummy )))
         fatal_error( "could not create User\\.Default registry key\n" );
 
     strcpy( p, "/userdef.reg" );
@@ -1486,9 +1500,9 @@
     /* load user.reg into HKEY_CURRENT_USER */
 
     /* FIXME: match default user in token.c. should get from process token instead */
-    current_user_path = format_user_registry_path( security_interactive_sid );
+    current_user_path = format_user_registry_path( security_interactive_sid, &current_user_str );
     if (!current_user_path ||
-        !(key = create_key( root_key, current_user_path, NULL, 0, time(NULL), &dummy )))
+        !(key = create_key( root_key, &current_user_str, NULL, 0, time(NULL), &dummy )))
         fatal_error( "could not create HKEY_CURRENT_USER registry key\n" );
     free( current_user_path );
     strcpy( p, "/user.reg" );
@@ -1686,32 +1700,33 @@
 DECL_HANDLER(create_key)
 {
     struct key *key = NULL, *parent;
+    struct unicode_str name, class;
     unsigned int access = req->access;
-    WCHAR *name, *class;
 
     if (access & MAXIMUM_ALLOWED) access = KEY_ALL_ACCESS;  /* FIXME: needs general solution */
     reply->hkey = 0;
-    if (!(name = copy_req_path( req->namelen, !req->parent ))) return;
+
+    if (req->namelen > get_req_data_size())
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    class.str = (const WCHAR *)get_req_data() + req->namelen / sizeof(WCHAR);
+    class.len = ((get_req_data_size() - req->namelen) / sizeof(WCHAR)) * sizeof(WCHAR);
+    get_req_path( &name, !req->parent );
+    if (name.str > class.str)
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    name.len = (class.str - name.str) * sizeof(WCHAR);
+
     /* NOTE: no access rights are required from the parent handle to create a key */
     if ((parent = get_hkey_obj( req->parent, 0 )))
     {
         int flags = (req->options & REG_OPTION_VOLATILE) ? KEY_VOLATILE : KEY_DIRTY;
 
-        if (req->namelen == get_req_data_size())  /* no class specified */
-        {
-            key = create_key( parent, name, NULL, flags, req->modif, &reply->created );
-        }
-        else
-        {
-            const WCHAR *class_ptr = (const WCHAR *)((const char *)get_req_data() + req->namelen);
-
-            if ((class = req_strdupW( req, class_ptr, get_req_data_size() - req->namelen )))
-            {
-                key = create_key( parent, name, class, flags, req->modif, &reply->created );
-                free( class );
-            }
-        }
-        if (key)
+        if ((key = create_key( parent, &name, &class, flags, req->modif, &reply->created )))
         {
             reply->hkey = alloc_handle( current->process, key, access, 0 );
             release_object( key );
@@ -1724,6 +1739,7 @@
 DECL_HANDLER(open_key)
 {
     struct key *key, *parent;
+    struct unicode_str name;
     unsigned int access = req->access;
 
     if (access & MAXIMUM_ALLOWED) access = KEY_ALL_ACCESS;  /* FIXME: needs general solution */
@@ -1731,8 +1747,8 @@
     /* NOTE: no access rights are required to open the parent key, only the child key */
     if ((parent = get_hkey_obj( req->parent, 0 )))
     {
-        WCHAR *name = copy_path( get_req_data(), get_req_data_size(), !req->parent );
-        if (name && (key = open_key( parent, name )))
+        get_req_path( &name, !req->parent );
+        if ((key = open_key( parent, &name )))
         {
             reply->hkey = alloc_handle( current->process, key, access, 0 );
             release_object( key );
@@ -1781,15 +1797,22 @@
 DECL_HANDLER(set_key_value)
 {
     struct key *key;
-    WCHAR *name;
+    struct unicode_str name;
 
-    if (!(name = copy_req_path( req->namelen, 0 ))) return;
+    if (req->namelen > get_req_data_size())
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return;
+    }
+    name.str = get_req_data();
+    name.len = (req->namelen / sizeof(WCHAR)) * sizeof(WCHAR);
+
     if ((key = get_hkey_obj( req->hkey, KEY_SET_VALUE )))
     {
         size_t datalen = get_req_data_size() - req->namelen;
         const char *data = (const char *)get_req_data() + req->namelen;
 
-        set_value( key, name, req->type, data, datalen );
+        set_value( key, &name, req->type, data, datalen );
         release_object( key );
     }
 }
@@ -1798,13 +1821,13 @@
 DECL_HANDLER(get_key_value)
 {
     struct key *key;
-    WCHAR *name;
+    struct unicode_str name;
 
     reply->total = 0;
-    if (!(name = copy_path( get_req_data(), get_req_data_size(), 0 ))) return;
     if ((key = get_hkey_obj( req->hkey, KEY_QUERY_VALUE )))
     {
-        get_value( key, name, &reply->type, &reply->total );
+        get_req_unicode_str( &name );
+        get_value( key, &name, &reply->type, &reply->total );
         release_object( key );
     }
 }
@@ -1824,16 +1847,13 @@
 /* delete a value of a registry key */
 DECL_HANDLER(delete_key_value)
 {
-    WCHAR *name;
     struct key *key;
+    struct unicode_str name;
 
     if ((key = get_hkey_obj( req->hkey, KEY_SET_VALUE )))
     {
-        if ((name = req_strdupW( req, get_req_data(), get_req_data_size() )))
-        {
-            delete_value( key, name );
-            free( name );
-        }
+        get_req_unicode_str( &name );
+        delete_value( key, &name );
         release_object( key );
     }
 }
@@ -1843,6 +1863,7 @@
 {
     struct key *key, *parent;
     struct token *token = thread_get_impersonation_token( current );
+    struct unicode_str name;
 
     const LUID_AND_ATTRIBUTES privs[] =
     {
@@ -1860,8 +1881,8 @@
     if ((parent = get_hkey_obj( req->hkey, 0 )))
     {
         int dummy;
-        WCHAR *name = copy_path( get_req_data(), get_req_data_size(), !req->hkey );
-        if (name && (key = create_key( parent, name, NULL, KEY_DIRTY, time(NULL), &dummy )))
+        get_req_path( &name, !req->hkey );
+        if ((key = create_key( parent, &name, NULL, KEY_DIRTY, time(NULL), &dummy )))
         {
             load_registry( key, req->file );
             release_object( key );