Commit 57f8eb71 authored by Martin Pilka's avatar Martin Pilka Committed by Alexandre Julliard

- registry loading speed up (reduced client <-> server communication)

- make the code a bit more understandable
parent 4f12e61c
...@@ -25,21 +25,10 @@ ...@@ -25,21 +25,10 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#ifdef HAVE_SYS_ERRNO_H
#include <sys/errno.h>
#endif
#include <sys/types.h> #include <sys/types.h>
#include <fcntl.h>
#include <sys/fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <time.h> #include <fcntl.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine/winbase16.h"
#include "winerror.h" #include "winerror.h"
#include "file.h" #include "file.h"
#include "heap.h" #include "heap.h"
...@@ -47,718 +36,376 @@ ...@@ -47,718 +36,376 @@
#include "options.h" #include "options.h"
#include "winreg.h" #include "winreg.h"
#include "server.h" #include "server.h"
#include "services.h" #ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
#include "winnt.h"
DEFAULT_DEBUG_CHANNEL(reg);
static void REGISTRY_Init(void); DEFAULT_DEBUG_CHANNEL(reg);
/* FIXME: following defines should be configured global ... */
#define SAVE_USERS_DEFAULT ETCDIR"/wine.userreg" /* FIXME: following defines should be configured global */
#define SAVE_LOCAL_MACHINE_DEFAULT ETCDIR"/wine.systemreg" #define SAVE_GLOBAL_REGBRANCH_USER_DEFAULT ETCDIR"/wine.userreg"
#define SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE ETCDIR"/wine.systemreg"
/* relative in ~user/.wine/ : */ /* relative in ~user/.wine/ : */
#define SAVE_CURRENT_USER "user.reg" #define SAVE_LOCAL_REGBRANCH_CURRENT_USER "user.reg"
#define SAVE_DEFAULT_USER "userdef.reg" #define SAVE_LOCAL_REGBRANCH_USER_DEFAULT "userdef.reg"
#define SAVE_LOCAL_USERS_DEFAULT "wine.userreg" #define SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE "system.reg"
#define SAVE_LOCAL_MACHINE "system.reg"
/* _xmalloc [Internal] */
static void *xmalloc( size_t size ) static void *_xmalloc( size_t size )
{ {
void *res; void *res;
res = malloc (size ? size : 1); res = malloc (size ? size : 1);
if (res == NULL) { if (res == NULL) {
WARN("Virtual memory exhausted.\n"); WARN("Virtual memory exhausted.\n");
exit (1); exit (1);
} }
return res; return res;
}
/******************************************************************************
* REGISTRY_Init [Internal]
* Registry initialisation, allocates some default keys.
*/
static void REGISTRY_Init(void) {
HKEY hkey;
char buf[200];
TRACE("(void)\n");
RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
RegCloseKey(hkey);
/* This was an Open, but since it is called before the real registries
are loaded, it was changed to a Create - MTB 980507*/
RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
RegCloseKey(hkey);
/* \\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion
* CurrentVersion
* CurrentBuildNumber
* CurrentType
* string RegisteredOwner
* string RegisteredOrganization
*
*/
/* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
* string SysContact
* string SysLocation
* SysServices
*/
if (-1!=gethostname(buf,200)) {
RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
RegCloseKey(hkey);
}
} }
/* _strdupnA [Internal] */
/************************ LOAD Registry Function ****************************/ static LPSTR _strdupnA(LPCSTR str,size_t len)
/******************************************************************************
* _find_or_add_key [Internal]
*/
static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
{ {
HKEY subkey; LPSTR ret;
if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
if (keyname) free( keyname );
return subkey;
}
/****************************************************************************** if (!str) return NULL;
* _find_or_add_value [Internal] ret = _xmalloc( len + 1 );
*/ memcpy( ret, str, len );
static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len ) ret[len] = 0x00;
{ return ret;
RegSetValueExW( hkey, name, 0, type, data, len );
if (name) free( name );
if (data) free( data );
} }
/* convert ansi string to unicode [Internal] */
/****************************************************************************** static LPWSTR _strdupnAtoW(LPCSTR strA,size_t lenA)
* _wine_read_line [Internal]
*
* reads a line including dynamically enlarging the readbuffer and throwing
* away comments
*/
static int _wine_read_line( FILE *F, char **buf, int *len )
{ {
char *s,*curread; LPWSTR ret;
int mylen,curoff; size_t lenW;
curread = *buf; if (!strA) return NULL;
mylen = *len; lenW = MultiByteToWideChar(CP_ACP,0,strA,lenA,NULL,0);
**buf = '\0'; ret = _xmalloc(lenW*sizeof(WCHAR)+sizeof(WCHAR));
while (1) { MultiByteToWideChar(CP_ACP,0,strA,lenA,ret,lenW);
while (1) { ret[lenW] = 0;
s=fgets(curread,mylen,F); return ret;
if (s==NULL)
return 0; /* EOF */
if (NULL==(s=strchr(curread,'\n'))) {
/* buffer wasn't large enough */
curoff = strlen(*buf);
curread = realloc(*buf,*len*2);
if(curread == NULL) {
WARN("Out of memory");
return 0;
}
*buf = curread;
curread+= curoff;
mylen = *len; /* we filled up the buffer and
* got new '*len' bytes to fill
*/
*len = *len * 2;
} else {
*s='\0';
break;
}
}
/* throw away comments */
if (**buf=='#' || **buf==';') {
curread = *buf;
mylen = *len;
continue;
}
if (s) /* got end of line */
break;
}
return 1;
} }
/* dump a Unicode string with proper escaping [Internal] */
/****************************************************************************** /* FIXME: this code duplicates server/unicode.c */
* _wine_read_USTRING [Internal] static int _dump_strW(const WCHAR *str,size_t len,FILE *f,char escape[2])
*
* converts a char* into a UNICODE string (up to a special char)
* and returns the position exactly after that string
*/
static char* _wine_read_USTRING( char *buf, LPWSTR *str )
{ {
char *s; static const char escapes[32] = ".......abtnvfr.............e....";
LPWSTR ws; char buffer[256];
LPSTR pos = buffer;
/* read up to "=" or "\0" or "\n" */ int count = 0;
s = buf;
*str = (LPWSTR)xmalloc(2*strlen(buf)+2);
ws = *str;
while (*s && (*s!='\n') && (*s!='=')) {
if (*s!='\\')
*ws++=*((unsigned char*)s++);
else {
s++;
if (!*s) {
/* Dangling \ ... may only happen if a registry
* write was short. FIXME: What to do?
*/
break;
}
if (*s=='\\') {
*ws++='\\';
s++;
continue;
}
if (*s!='u') {
WARN("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
*ws++='\\';
*ws++=*s++;
} else {
char xbuf[5];
int wc;
s++;
memcpy(xbuf,s,4);xbuf[4]='\0';
if (!sscanf(xbuf,"%x",&wc))
WARN("Strange escape sequence %s found in |%s|\n",xbuf,buf);
s+=4;
*ws++ =(unsigned short)wc;
}
}
}
*ws = 0;
return s;
}
for (; len; str++, len--)
/****************************************************************************** {
* _wine_loadsubkey [Internal] if (pos > buffer + sizeof(buffer) - 8)
* {
* NOTES fwrite( buffer, pos - buffer, 1, f );
* It seems like this is returning a boolean. Should it? count += pos - buffer;
* pos = buffer;
* RETURNS
* Success: 1
* Failure: 0
*/
static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
{
HKEY subkey;
int i;
char *s;
LPWSTR name;
TRACE("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
/* Good. We already got a line here ... so parse it */
subkey = 0;
while (1) {
i=0;s=*buf;
while (*s=='\t') {
s++;
i++;
} }
if (i>level) { if (*str > 127) /* hex escape */
if (!subkey) { {
WARN("Got a subhierarchy without resp. key?\n"); if (len > 1 && str[1] < 128 && isxdigit((char)str[1]))
return 0; pos += sprintf( pos, "\\x%04x", *str );
} else
if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen)) pos += sprintf( pos, "\\x%x", *str );
if (!_wine_read_line(F,buf,buflen))
goto done;
continue; continue;
} }
if (*str < 32) /* octal or C escape */
/* let the caller handle this line */ {
if (i<level || **buf=='\0') if (!*str && len == 1) continue; /* do not output terminating NULL */
goto done; if (escapes[*str] != '.')
pos += sprintf( pos, "\\%c", escapes[*str] );
/* it can be: a value or a keyname. Parse the name first */ else if (len > 1 && str[1] >= '0' && str[1] <= '7')
s=_wine_read_USTRING(s,&name); pos += sprintf( pos, "\\%03o", *str );
else
/* switch() default: hack to avoid gotos */ pos += sprintf( pos, "\\%o", *str );
switch (0) { continue;
default: }
if (*s=='\0') { if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\';
if (subkey) RegCloseKey( subkey ); *pos++ = *str;
subkey=_find_or_add_key(hkey,name);
} else {
LPBYTE data;
int len,lastmodified,type;
if (*s!='=') {
WARN("Unexpected character: %c\n",*s);
break;
}
s++;
if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
break;
}
/* skip the 2 , */
s=strchr(s,',');s++;
s=strchr(s,',');
if (!s++) {
WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
break;
}
if (type == REG_SZ || type == REG_EXPAND_SZ) {
s=_wine_read_USTRING(s,(LPWSTR*)&data);
len = lstrlenW((LPWSTR)data)*2+2;
} else {
len=strlen(s)/2;
data = (LPBYTE)xmalloc(len+1);
for (i=0;i<len;i++) {
data[i]=0;
if (*s>='0' && *s<='9')
data[i]=(*s-'0')<<4;
if (*s>='a' && *s<='f')
data[i]=(*s-'a'+'\xa')<<4;
if (*s>='A' && *s<='F')
data[i]=(*s-'A'+'\xa')<<4;
s++;
if (*s>='0' && *s<='9')
data[i]|=*s-'0';
if (*s>='a' && *s<='f')
data[i]|=*s-'a'+'\xa';
if (*s>='A' && *s<='F')
data[i]|=*s-'A'+'\xa';
s++;
}
}
_find_or_add_value(hkey,name,type,data,len);
}
}
/* read the next line */
if (!_wine_read_line(F,buf,buflen))
goto done;
} }
done: fwrite( buffer, pos - buffer, 1, f );
if (subkey) RegCloseKey( subkey ); count += pos - buffer;
return 1; return count;
} }
/* convert ansi string to unicode and dump with proper escaping [Internal] */
/****************************************************************************** static int _dump_strAtoW(LPCSTR strA,size_t len,FILE *f,char escape[2])
* _wine_loadsubreg [Internal]
*/
static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
{ {
int ver; WCHAR *strW;
char *buf; int ret;
int buflen;
if (strA == NULL) return 0;
buf=xmalloc(10);buflen=10; strW = _strdupnAtoW(strA,len);
if (!_wine_read_line(F,&buf,&buflen)) { ret = _dump_strW(strW,len,f,escape);
free(buf); free(strW);
return 0; return ret;
}
if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
free(buf);
return 0;
}
if (ver!=1) {
if (ver == 2) /* new version */
{
HANDLE file;
if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, -1, TRUE )) != INVALID_HANDLE_VALUE)
{
SERVER_START_REQ
{
struct load_registry_request *req = server_alloc_req( sizeof(*req), 0 );
req->hkey = hkey;
req->file = file;
server_call( REQ_LOAD_REGISTRY );
}
SERVER_END_REQ;
CloseHandle( file );
}
free( buf );
return 1;
}
else
{
TRACE("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
free(buf);
return 0;
}
}
if (!_wine_read_line(F,&buf,&buflen)) {
free(buf);
return 0;
}
if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
free(buf);
return 0;
}
free(buf);
return 1;
} }
/* a key value */
/* FIXME: this code duplicates server/registry.c */
struct key_value {
WCHAR *nameW; /* value name */
int type; /* value type */
size_t len; /* value data length in bytes */
void *data; /* pointer to value data */
};
/****************************************************************************** /* dump a value to a text file */
* _wine_loadreg [Internal] /* FIXME: this code duplicates server/registry.c */
*/ static void _dump_value(struct key_value *value,FILE *f)
static int _wine_loadreg( HKEY hkey, char *fn )
{ {
FILE *F; int i, count;
TRACE("(%x,%s)\n",hkey,debugstr_a(fn)); if (value->nameW[0]) {
fputc( '\"', f );
F = fopen(fn,"rb"); count = 1 + _dump_strW(value->nameW,strlenW(value->nameW),f,"\"\"");
if (F==NULL) { count += fprintf( f, "\"=" );
WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) ); }
return -1; else count = fprintf( f, "@=" );
switch(value->type) {
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
if (value->type != REG_SZ) fprintf( f, "str(%d):", value->type );
fputc( '\"', f );
if (value->data) _dump_strW(value->data,value->len/sizeof(WCHAR),f,"\"\"");
fputc( '\"', f );
break;
case REG_DWORD:
if (value->len == sizeof(DWORD)) {
DWORD dw;
memcpy( &dw, value->data, sizeof(DWORD) );
fprintf( f, "dword:%08lx", dw );
break;
}
/* else fall through */
default:
if (value->type == REG_BINARY) count += fprintf( f, "hex:" );
else count += fprintf( f, "hex(%x):", value->type );
for (i = 0; i < value->len; i++) {
count += fprintf( f, "%02x", *((unsigned char *)value->data + i) );
if (i < value->len-1) {
fputc( ',', f );
if (++count > 76) {
fprintf( f, "\\\n " );
count = 2;
}
}
}
break;
} }
_wine_loadsubreg(F,hkey,fn); fputc( '\n', f );
fclose(F);
return 0;
} }
/* NT REGISTRY LOADER */ /******************************************************************/
/* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
#ifdef HAVE_SYS_MMAN_H /*
# include <sys/mman.h> reghack - windows 3.11 registry data format demo program.
#endif
#ifndef MAP_FAILED
#define MAP_FAILED ((LPVOID)-1)
#endif
#define NT_REG_BLOCK_SIZE 0x1000
#define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
#define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
#define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
#define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
/* subblocks of nk */
#define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
#define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
#define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
#define NT_REG_KEY_BLOCK_TYPE 0x20 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
#define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c a combined hash table and tree description, and finally a text table.
typedef struct The header is obvious from the struct header. The taboff1 and taboff2
{ fields are always 0x20, and their usage is unknown.
DWORD id; /* 0x66676572 'regf'*/
DWORD uk1; /* 0x04 */
DWORD uk2; /* 0x08 */
FILETIME DateModified; /* 0x0c */
DWORD uk3; /* 0x14 */
DWORD uk4; /* 0x18 */
DWORD uk5; /* 0x1c */
DWORD uk6; /* 0x20 */
DWORD RootKeyBlock; /* 0x24 */
DWORD BlockSize; /* 0x28 */
DWORD uk7[116];
DWORD Checksum; /* at offset 0x1FC */
} nt_regf;
typedef struct The 8-byte entry table has various entry types.
{
DWORD blocksize;
BYTE data[1];
} nt_hbin_sub;
typedef struct tabent[0] is a root index. The second word has the index of the root of
{ the directory.
DWORD id; /* 0x6E696268 'hbin' */ tabent[1..hashsize] is a hash table. The first word in the hash entry is
DWORD off_prev; the index of the key/value that has that hash. Data with the same
DWORD off_next; hash value are on a circular list. The other three words in the
DWORD uk1; hash entry are always zero.
DWORD uk2; /* 0x10 */ tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
DWORD uk3; /* 0x14 */ entry: dirent and keyent/valent. They are identified by context.
DWORD uk4; /* 0x18 */ tabent[freeidx] is the first free entry. The first word in a free entry
DWORD size; /* 0x1C */ is the index of the next free entry. The last has 0 as a link.
nt_hbin_sub hbin_sub; /* 0x20 */ The other three words in the free list are probably irrelevant.
} nt_hbin;
/* Entries in text table are preceded by a word at offset-2. This word
* the value_list consists of offsets to the values (vk) has the value (2*index)+1, where index is the referring keyent/valent
*/ entry in the table. I have no suggestion for the 2* and the +1.
typedef struct Following the word, there are N bytes of data, as per the keyent/valent
{ entry length. The offset of the keyent/valent entry is from the start
WORD SubBlockId; /* 0x00 0x6B6E */ of the text table to the first data byte.
WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
FILETIME writetime; /* 0x04 */
DWORD uk1; /* 0x0C */
DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
DWORD nr_subkeys; /* 0x14 number of sub-Keys */
DWORD uk8; /* 0x18 */
DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
DWORD uk2; /* 0x20 */
DWORD nr_values; /* 0x24 number of values */
DWORD valuelist_off; /* 0x28 Offset of the Value-List */
DWORD off_sk; /* 0x2c Offset of the sk-Record */
DWORD off_class; /* 0x30 Offset of the Class-Name */
DWORD uk3; /* 0x34 */
DWORD uk4; /* 0x38 */
DWORD uk5; /* 0x3c */
DWORD uk6; /* 0x40 */
DWORD uk7; /* 0x44 */
WORD name_len; /* 0x48 name-length */
WORD class_len; /* 0x4a class-name length */
char name[1]; /* 0x4c key-name */
} nt_nk;
typedef struct This information is not available from Microsoft. The data format is
{ deduced from the reg.dat file by me. Mistakes may
DWORD off_nk; /* 0x00 */ have been made. I claim no rights and give no guarantees for this program.
DWORD name; /* 0x04 */
} hash_rec;
typedef struct Tor Sjøwall, tor@sn.no
{ */
WORD id; /* 0x00 0x666c */
WORD nr_keys; /* 0x06 */
hash_rec hash_rec[1];
} nt_lf;
/* /* reg.dat header format */
list of subkeys without hash struct _w31_header {
char cookie[8]; /* 'SHCC3.10' */
unsigned long taboff1; /* offset of hash table (??) = 0x20 */
unsigned long taboff2; /* offset of index table (??) = 0x20 */
unsigned long tabcnt; /* number of entries in index table */
unsigned long textoff; /* offset of text part */
unsigned long textsize; /* byte size of text part */
unsigned short hashsize; /* hash size */
unsigned short freeidx; /* free index */
};
li --+-->nk /* generic format of table entries */
| struct _w31_tabent {
+-->nk unsigned short w0, w1, w2, w3;
*/ };
typedef struct
{
WORD id; /* 0x00 0x696c */
WORD nr_keys;
DWORD off_nk[1];
} nt_li;
/* /* directory tabent: */
this is a intermediate node struct _w31_dirent {
unsigned short sibling_idx; /* table index of sibling dirent */
unsigned short child_idx; /* table index of child dirent */
unsigned short key_idx; /* table index of key keyent */
unsigned short value_idx; /* table index of value valent */
};
ri --+-->li--+-->nk /* key tabent: */
| + struct _w31_keyent {
| +-->nk unsigned short hash_idx; /* hash chain index for string */
| unsigned short refcnt; /* reference count */
+-->li--+-->nk unsigned short length; /* length of string */
+ unsigned short string_off; /* offset of string in text table */
+-->nk };
*/
typedef struct
{
WORD id; /* 0x00 0x6972 */
WORD nr_li; /* 0x02 number off offsets */
DWORD off_li[1]; /* 0x04 points to li */
} nt_ri;
typedef struct /* value tabent: */
{ struct _w31_valent {
WORD id; /* 0x00 'vk' */ unsigned short hash_idx; /* hash chain index for string */
WORD nam_len; unsigned short refcnt; /* reference count */
DWORD data_len; unsigned short length; /* length of string */
DWORD data_off; unsigned short string_off; /* offset of string in text table */
DWORD type; };
WORD flag;
WORD uk1;
char name[1];
} nt_vk;
LPSTR _strdupnA( LPCSTR str, int len ) /* recursive helper function to display a directory tree [Internal] */
void _w31_dumptree(unsigned short idx,unsigned char *txt,struct _w31_tabent *tab,struct _w31_header *head,HKEY hkey,time_t lastmodified, int level)
{ {
LPSTR ret; struct _w31_dirent *dir;
struct _w31_keyent *key;
if (!str) return NULL; struct _w31_valent *val;
ret = xmalloc( len + 1 ); HKEY subkey = 0;
memcpy( ret, str, len ); static char tail[400];
ret[len] = 0x00;
return ret; while (idx!=0) {
dir=(struct _w31_dirent*)&tab[idx];
if (dir->key_idx) {
key = (struct _w31_keyent*)&tab[dir->key_idx];
memcpy(tail,&txt[key->string_off],key->length);
tail[key->length]='\0';
/* all toplevel entries AND the entries in the
* toplevel subdirectory belong to \SOFTWARE\Classes
*/
if (!level && !strcmp(tail,".classes")) {
_w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
idx=dir->sibling_idx;
continue;
}
if (subkey) RegCloseKey( subkey );
if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
/* only add if leaf node or valued node */
if (dir->value_idx!=0||dir->child_idx==0) {
if (dir->value_idx) {
val=(struct _w31_valent*)&tab[dir->value_idx];
memcpy(tail,&txt[val->string_off],val->length);
tail[val->length]='\0';
RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
}
}
} else TRACE("strange: no directory key name, idx=%04x\n", idx);
_w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
idx=dir->sibling_idx;
}
if (subkey) RegCloseKey( subkey );
} }
static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level);
/*
* gets a value
*
* vk->flag:
* 0 value is a default value
* 1 the value has a name
*
* vk->data_len
* len of the whole data block
* - reg_sz (unicode)
* bytes including the terminating \0 = 2*(number_of_chars+1)
* - reg_dword, reg_binary:
* if highest bit of data_len is set data_off contains the value
*/
static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
{
WCHAR name [256];
DWORD len, ret;
BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
if (!(len = MultiByteToWideChar( CP_ACP, 0, vk->name, vk->nam_len, name, 256 )) && vk->nam_len)
{
ERR("name too large '%.*s' (%d)\n", vk->nam_len, vk->name, vk->nam_len );
return FALSE;
}
name[len] = 0;
ret = RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type, /******************************************************************************
(vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata, * _w31_loadreg [Internal]
(vk->data_len & 0x7fffffff) );
if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
return TRUE;
error:
ERR("unknown block found (0x%04x), please report!\n", vk->id);
return FALSE;
}
/*
* get the subkeys
*
* this structure contains the hash of a keyname and points to all
* subkeys
*
* exception: if the id is 'il' there are no hash values and every
* dword is a offset
*/ */
static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level) void _w31_loadreg(void)
{
int i;
if (lf->id == NT_REG_HASH_BLOCK_ID)
{
if (subkeys != lf->nr_keys) goto error1;
for (i=0; i<lf->nr_keys; i++)
{
if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
}
}
else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
{
nt_li * li = (nt_li*)lf;
if (subkeys != li->nr_keys) goto error1;
for (i=0; i<li->nr_keys; i++)
{
if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+li->off_nk[i]+4), level)) goto error;
}
}
else if (lf->id == NT_REG_RI_BLOCK_ID) /* ri */
{
nt_ri * ri = (nt_ri*)lf;
int li_subkeys = 0;
/* count all subkeys */
for (i=0; i<ri->nr_li; i++)
{
nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
li_subkeys += li->nr_keys;
}
/* check number */
if (subkeys != li_subkeys) goto error1;
/* loop through the keys */
for (i=0; i<ri->nr_li; i++)
{
nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
if (!_nt_parse_lf(hkey, base, li->nr_keys, (nt_lf*)li, level)) goto error;
}
}
else
{
goto error2;
}
return TRUE;
error2: ERR("unknown node id 0x%04x, please report!\n", lf->id);
return TRUE;
error1: ERR("registry file corrupt! (inconsistent number of subkeys)\n");
return FALSE;
error: ERR("error reading lf block\n");
return FALSE;
}
static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
{ {
char * name; HFILE hf;
unsigned int n; struct _w31_header head;
DWORD * vl; struct _w31_tabent *tab;
HKEY subkey = hkey; unsigned char *txt;
unsigned int len;
if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID) OFSTRUCT ofs;
{ BY_HANDLE_FILE_INFORMATION hfinfo;
ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId); time_t lastmodified;
goto error;
} TRACE("(void)\n");
if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) && hf = OpenFile("reg.dat",&ofs,OF_READ);
(((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) if (hf==HFILE_ERROR) return;
{
ERR("registry file corrupt!\n"); /* read & dump header */
goto error; if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
} ERR("reg.dat is too short.\n");
_lclose(hf);
/* create the new key */ return;
if(level <= 0) }
{ if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
name = _strdupnA( nk->name, nk->name_len); ERR("reg.dat has bad signature.\n");
if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; } _lclose(hf);
free(name); return;
} }
/* loop through the subkeys */ len = head.tabcnt * sizeof(struct _w31_tabent);
if (nk->nr_subkeys) /* read and dump index table */
{ tab = _xmalloc(len);
nt_lf * lf = (nt_lf*)(base+nk->lf_off+4); if (len!=_lread(hf,tab,len)) {
if (!_nt_parse_lf(subkey, base, nk->nr_subkeys, lf, level-1)) goto error1; ERR("couldn't read %d bytes.\n",len);
} free(tab);
_lclose(hf);
return;
}
/* loop trough the value list */ /* read text */
vl = (DWORD *)(base+nk->valuelist_off+4); txt = _xmalloc(head.textsize);
for (n=0; n<nk->nr_values; n++) if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
{ ERR("couldn't seek to textblock.\n");
nt_vk * vk = (nt_vk*)(base+vl[n]+4); free(tab);
if (!_nt_parse_vk(subkey, base, vk)) goto error1; free(txt);
} _lclose(hf);
return;
}
if (head.textsize!=_lread(hf,txt,head.textsize)) {
ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
free(tab);
free(txt);
_lclose(hf);
return;
}
/* Don't close the subkey if it is the hkey that was passed if (!GetFileInformationByHandle(hf,&hfinfo)) {
* (i.e. Level was <= 0) ERR("GetFileInformationByHandle failed?.\n");
*/ free(tab);
if( subkey!=hkey ) RegCloseKey(subkey); free(txt);
return TRUE; _lclose(hf);
return;
error1: RegCloseKey(subkey); }
error: return FALSE; lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
_w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
free(tab);
free(txt);
_lclose(hf);
return;
} }
/* end nt loader */ /***********************************************************************************/
/* windows 95 registry loader */
/* windows 95 registry loader */ /***********************************************************************************/
/* SECTION 1: main header /* SECTION 1: main header
* *
...@@ -766,16 +413,15 @@ error: return FALSE; ...@@ -766,16 +413,15 @@ error: return FALSE;
*/ */
#define W95_REG_CREG_ID 0x47455243 #define W95_REG_CREG_ID 0x47455243
typedef struct typedef struct {
{ DWORD id; /* "CREG" = W95_REG_CREG_ID */
DWORD id; /* "CREG" = W95_REG_CREG_ID */ DWORD version; /* ???? 0x00010000 */
DWORD version; /* ???? 0x00010000 */ DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */ DWORD uk2; /* 0x0c */
DWORD uk2; /* 0x0c */ WORD rgdb_num; /* 0x10 # of RGDB-blocks */
WORD rgdb_num; /* 0x10 # of RGDB-blocks */ WORD uk3;
WORD uk3; DWORD uk[3];
DWORD uk[3]; /* rgkn */
/* rgkn */
} _w95creg; } _w95creg;
/* SECTION 2: Directory information (tree structure) /* SECTION 2: Directory information (tree structure)
...@@ -786,13 +432,12 @@ typedef struct ...@@ -786,13 +432,12 @@ typedef struct
*/ */
#define W95_REG_RGKN_ID 0x4e4b4752 #define W95_REG_RGKN_ID 0x4e4b4752
typedef struct typedef struct {
{ DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
DWORD id; /*"RGKN" = W95_REG_RGKN_ID */ DWORD size; /* Size of the RGKN-block */
DWORD size; /* Size of the RGKN-block */ DWORD root_off; /* Rel. Offset of the root-record */
DWORD root_off; /* Rel. Offset of the root-record */ DWORD last_dke; /* Offset to last DKE ? */
DWORD last_dke; /* Offset to last DKE ? */ DWORD uk[4];
DWORD uk[4];
} _w95rgkn; } _w95rgkn;
/* Disk Key Entry Structure /* Disk Key Entry Structure
...@@ -826,16 +471,15 @@ typedef struct ...@@ -826,16 +471,15 @@ typedef struct
* there is a one to one relationship between dke and dkh * there is a one to one relationship between dke and dkh
*/ */
/* key struct, once per key */ /* key struct, once per key */
typedef struct typedef struct {
{ DWORD x1; /* Free entry indicator(?) */
DWORD x1; /* Free entry indicator(?) */ DWORD hash; /* sum of bytes of keyname */
DWORD hash; /* sum of bytes of keyname */ DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */ DWORD prevlvl; /* offset of previous key */
DWORD prevlvl; /* offset of previous key */ DWORD nextsub; /* offset of child key */
DWORD nextsub; /* offset of child key */ DWORD next; /* offset of sibling key */
DWORD next; /* offset of sibling key */ WORD nrLS; /* id inside the rgdb block */
WORD nrLS; /* id inside the rgdb block */ WORD nrMS; /* number of the rgdb block */
WORD nrMS; /* number of the rgdb block */
} _w95dke; } _w95dke;
/* SECTION 3: key information, values and data /* SECTION 3: key information, values and data
...@@ -853,42 +497,39 @@ typedef struct ...@@ -853,42 +497,39 @@ typedef struct
/* block header, once per block */ /* block header, once per block */
#define W95_REG_RGDB_ID 0x42444752 #define W95_REG_RGDB_ID 0x42444752
typedef struct typedef struct {
{ DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */
DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */ DWORD size; /* 0x04 */
DWORD size; /* 0x04 */ DWORD uk1; /* 0x08 */
DWORD uk1; /* 0x08 */ DWORD uk2; /* 0x0c */
DWORD uk2; /* 0x0c */ DWORD uk3; /* 0x10 */
DWORD uk3; /* 0x10 */ DWORD uk4; /* 0x14 */
DWORD uk4; /* 0x14 */ DWORD uk5; /* 0x18 */
DWORD uk5; /* 0x18 */ DWORD uk6; /* 0x1c */
DWORD uk6; /* 0x1c */ /* dkh */
/* dkh */
} _w95rgdb; } _w95rgdb;
/* Disk Key Header structure (RGDB part), once per key */ /* Disk Key Header structure (RGDB part), once per key */
typedef struct typedef struct {
{ DWORD nextkeyoff; /* 0x00 offset to next dkh */
DWORD nextkeyoff; /* 0x00 offset to next dkh */ WORD nrLS; /* 0x04 id inside the rgdb block */
WORD nrLS; /* 0x04 id inside the rgdb block */ WORD nrMS; /* 0x06 number of the rgdb block */
WORD nrMS; /* 0x06 number of the rgdb block */ DWORD bytesused; /* 0x08 */
DWORD bytesused; /* 0x08 */ WORD keynamelen; /* 0x0c len of name */
WORD keynamelen; /* 0x0c len of name */ WORD values; /* 0x0e number of values */
WORD values; /* 0x0e number of values */ DWORD xx1; /* 0x10 */
DWORD xx1; /* 0x10 */ char name[1]; /* 0x14 */
char name[1]; /* 0x14 */ /* dkv */ /* 0x14 + keynamelen */
/* dkv */ /* 0x14 + keynamelen */
} _w95dkh; } _w95dkh;
/* Disk Key Value structure, once per value */ /* Disk Key Value structure, once per value */
typedef struct typedef struct {
{ DWORD type; /* 0x00 */
DWORD type; /* 0x00 */ DWORD x1; /* 0x04 */
DWORD x1; /* 0x04 */ WORD valnamelen; /* 0x08 length of name, 0 is default key */
WORD valnamelen; /* 0x08 length of name, 0 is default key */ WORD valdatalen; /* 0x0A length of data */
WORD valdatalen; /* 0x0A length of data */ char name[1]; /* 0x0c */
char name[1]; /* 0x0c */ /* raw data */ /* 0x0c + valnamelen */
/* raw data */ /* 0x0c + valnamelen */
} _w95dkv; } _w95dkv;
/****************************************************************************** /******************************************************************************
...@@ -896,488 +537,484 @@ typedef struct ...@@ -896,488 +537,484 @@ typedef struct
* *
* seeks the dkh belonging to a dke * seeks the dkh belonging to a dke
*/ */
static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS) static _w95dkh *_w95_lookup_dkh(_w95creg *creg,int nrLS,int nrMS)
{ {
_w95rgdb * rgdb; _w95rgdb * rgdb;
_w95dkh * dkh; _w95dkh * dkh;
int i; int i;
/* get the beginning of the rgdb datastore */
rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);
/* check: requested block < last_block) */
if (creg->rgdb_num <= nrMS)
{
ERR("registry file corrupt! requested block no. beyond end.\n");
goto error;
}
/* find the right block */
for(i=0; i<nrMS ;i++)
{
if(rgdb->id != W95_REG_RGDB_ID) /* check the magic */
{
ERR("registry file corrupt! bad magic 0x%08lx\n", rgdb->id);
goto error;
}
rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
}
dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */ /* get the beginning of the rgdb datastore */
rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);
do /* check: requested block < last_block) */
{ if (creg->rgdb_num <= nrMS) {
if(nrLS==dkh->nrLS ) return dkh; ERR("registry file corrupt! requested block no. beyond end.\n");
dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */ goto error;
} while ((char *)dkh < ((char*)rgdb+rgdb->size)); }
/* find the right block */
for(i=0; i<nrMS ;i++) {
if(rgdb->id != W95_REG_RGDB_ID) { /* check the magic */
ERR("registry file corrupt! bad magic 0x%08lx\n", rgdb->id);
goto error;
}
rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
}
dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
do {
if(nrLS==dkh->nrLS ) return dkh;
dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
} while ((char *)dkh < ((char*)rgdb+rgdb->size));
error:
return NULL;
}
error: return NULL;
}
/****************************************************************************** /******************************************************************************
* _w95_parse_dkv [Internal] * _w95_dump_dkv [Internal]
*/ */
static int _w95_parse_dkv ( static int _w95_dump_dkv(_w95dkh *dkh,int nrLS,int nrMS,FILE *f)
HKEY hkey,
_w95dkh * dkh,
int nrLS,
int nrMS )
{ {
_w95dkv * dkv; _w95dkv * dkv;
int i; int i;
DWORD ret;
char * name; /* first value block */
dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
/* first value block */
dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14); /* loop trought the values */
for (i=0; i< dkh->values; i++) {
/* loop trought the values */ struct key_value value;
for (i=0; i< dkh->values; i++) WCHAR *pdata;
{
name = _strdupnA(dkv->name, dkv->valnamelen); value.nameW = _strdupnAtoW(dkv->name,dkv->valnamelen);
ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen); value.type = dkv->type;
if (ret) FIXME("RegSetValueEx returned: 0x%08lx\n", ret); value.len = dkv->valdatalen;
free (name);
value.data = &(dkv->name[dkv->valnamelen]);
/* next value */ pdata = NULL;
dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c); if ( (value.type==REG_SZ) || (value.type==REG_EXPAND_SZ) || (value.type==REG_MULTI_SZ) ) {
} pdata = _strdupnAtoW(value.data,value.len);
return TRUE; value.len *= 2;
}
if (pdata != NULL) value.data = pdata;
_dump_value(&value,f);
free(value.nameW);
if (pdata != NULL) free(pdata);
/* next value */
dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
}
return TRUE;
} }
/****************************************************************************** /******************************************************************************
* _w95_parse_dke [Internal] * _w95_dump_dke [Internal]
*/ */
static int _w95_parse_dke( static int _w95_dump_dke(LPSTR key_name,_w95creg *creg,_w95rgkn *rgkn,_w95dke *dke,FILE *f,int level)
HKEY hkey,
_w95creg * creg,
_w95rgkn *rgkn,
_w95dke * dke,
int level )
{ {
_w95dkh * dkh; _w95dkh * dkh;
HKEY hsubkey = hkey; LPSTR new_key_name = NULL;
char * name;
int ret = FALSE;
/* special root key */
if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
{
/* parse the one subkey */
if (dke->nextsub != 0xffffffff)
{
return _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level);
}
/* has no sibling keys */
goto error;
}
/* search subblock */ /* special root key */
if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS))) if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
{ {
fprintf(stderr, "dke pointing to missing dkh !\n"); /* parse the one subkey */
goto error; if (dke->nextsub != 0xffffffff) return _w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level);
} /* has no sibling keys */
return FALSE;
}
if ( level <= 0 ) /* search subblock */
{ if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS))) {
/* walk sibling keys */ ERR("dke pointing to missing dkh !\n");
if (dke->next != 0xffffffff ) return FALSE;
{ }
if (!_w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level)) goto error;
} if (level <= 0) {
/* create new subkey name */
/* create subkey and insert values */ new_key_name = _strdupnA(key_name,strlen(key_name)+dkh->keynamelen+1);
name = _strdupnA( dkh->name, dkh->keynamelen); if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; } strncat(new_key_name,dkh->name,dkh->keynamelen);
free(name);
if (!_w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS)) goto error1; /* walk sibling keys */
} if (dke->next != 0xffffffff ) {
if (!_w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next),f,level)) {
/* next sub key */ free(new_key_name);
if (dke->nextsub != 0xffffffff) return FALSE;
{ }
if (!_w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1)) goto error1; }
}
/* write the key path (something like [Software\\Microsoft\\..]) only if:
1) key has some values
2) key has no values and no subkeys
*/
if (dkh->values > 0) {
/* there are some values */
fprintf(f,"\n[");
_dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
fprintf(f,"]\n");
if (!_w95_dump_dkv(dkh, dke->nrLS, dke->nrMS,f)) {
free(new_key_name);
return FALSE;
}
}
if ((dke->nextsub == 0xffffffff) && (dkh->values == 0)) {
/* no subkeys and no values */
fprintf(f,"\n[");
_dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
fprintf(f,"]\n");
}
} else new_key_name = _strdupnA(key_name,strlen(key_name));
ret = TRUE; /* next sub key */
error1: if (hsubkey != hkey) RegCloseKey(hsubkey); if (dke->nextsub != 0xffffffff) {
error: return ret; if (!_w95_dump_dke(new_key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level-1)) {
free(new_key_name);
return FALSE;
}
}
free(new_key_name);
return TRUE;
} }
/* end windows 95 loader */ /* end windows 95 loader */
/****************************************************************************** /***********************************************************************************/
* NativeRegLoadKey [Internal] /* windows NT registry loader */
* /***********************************************************************************/
* Loads a native registry file (win95/nt)
* hkey root key /* NT REGISTRY LOADER */
* fn filename
* level number of levels to cut away (eg. ".Default" in user.dat) #ifdef HAVE_SYS_MMAN_H
* # include <sys/mman.h>
* this function intentionally uses unix file functions to make it possible #endif
* to move it to a seperate registry helper programm
#ifndef MAP_FAILED
#define MAP_FAILED ((LPVOID)-1)
#endif
#define NT_REG_BLOCK_SIZE 0x1000
#define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
#define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
#define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
#define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
/* subblocks of nk */
#define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
#define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
#define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
#define NT_REG_KEY_BLOCK_TYPE 0x20
#define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
typedef struct {
DWORD id; /* 0x66676572 'regf'*/
DWORD uk1; /* 0x04 */
DWORD uk2; /* 0x08 */
FILETIME DateModified; /* 0x0c */
DWORD uk3; /* 0x14 */
DWORD uk4; /* 0x18 */
DWORD uk5; /* 0x1c */
DWORD uk6; /* 0x20 */
DWORD RootKeyBlock; /* 0x24 */
DWORD BlockSize; /* 0x28 */
DWORD uk7[116];
DWORD Checksum; /* at offset 0x1FC */
} nt_regf;
typedef struct {
DWORD blocksize;
BYTE data[1];
} nt_hbin_sub;
typedef struct {
DWORD id; /* 0x6E696268 'hbin' */
DWORD off_prev;
DWORD off_next;
DWORD uk1;
DWORD uk2; /* 0x10 */
DWORD uk3; /* 0x14 */
DWORD uk4; /* 0x18 */
DWORD size; /* 0x1C */
nt_hbin_sub hbin_sub; /* 0x20 */
} nt_hbin;
/*
* the value_list consists of offsets to the values (vk)
*/ */
static int NativeRegLoadKey( HKEY hkey, char* fn, int level ) typedef struct {
{ WORD SubBlockId; /* 0x00 0x6B6E */
int fd = 0; WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
struct stat st; FILETIME writetime; /* 0x04 */
DOS_FULL_NAME full_name; DWORD uk1; /* 0x0C */
int ret = FALSE; DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
void * base; DWORD nr_subkeys; /* 0x14 number of sub-Keys */
char *filetype = "unknown"; DWORD uk8; /* 0x18 */
DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE; DWORD uk2; /* 0x20 */
DWORD nr_values; /* 0x24 number of values */
/* map the registry into the memory */ DWORD valuelist_off; /* 0x28 Offset of the Value-List */
if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE; DWORD off_sk; /* 0x2c Offset of the sk-Record */
if ((fstat(fd, &st) == -1)) goto error; DWORD off_class; /* 0x30 Offset of the Class-Name */
if (!st.st_size) goto error; DWORD uk3; /* 0x34 */
if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error; DWORD uk4; /* 0x38 */
DWORD uk5; /* 0x3c */
switch (*(LPDWORD)base) DWORD uk6; /* 0x40 */
{ DWORD uk7; /* 0x44 */
/* windows 95 'CREG' */ WORD name_len; /* 0x48 name-length */
case W95_REG_CREG_ID: WORD class_len; /* 0x4a class-name length */
{ char name[1]; /* 0x4c key-name */
_w95creg *creg; } nt_nk;
_w95rgkn *rgkn;
_w95dke *dke, *root_dke; typedef struct {
creg = base; DWORD off_nk; /* 0x00 */
filetype = "win95"; DWORD name; /* 0x04 */
TRACE("Loading %s registry '%s' '%s'\n", filetype, fn, full_name.long_name); } hash_rec;
/* load the header (rgkn) */ typedef struct {
rgkn = (_w95rgkn*)(creg + 1); WORD id; /* 0x00 0x666c */
if (rgkn->id != W95_REG_RGKN_ID) WORD nr_keys; /* 0x06 */
{ hash_rec hash_rec[1];
ERR("second IFF header not RGKN, but %lx\n", rgkn->id); } nt_lf;
goto error1;
}
if (rgkn->root_off != 0x20)
{
ERR("rgkn->root_off not 0x20, please report !\n");
goto error1;
}
if (rgkn->last_dke > rgkn->size)
{
ERR("registry file corrupt! last_dke > size!\n");
goto error1;
}
/* verify last dke */
dke = (_w95dke*)((char*)rgkn + rgkn->last_dke);
if (dke->x1 != 0x80000000)
{ /* wrong magic */
ERR("last dke invalid !\n");
goto error1;
}
if (rgkn->size > creg->rgdb_off)
{
ERR("registry file corrupt! rgkn size > rgdb_off !\n");
goto error1;
}
root_dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
if ( (root_dke->prevlvl != 0xffffffff)
|| (root_dke->next != 0xffffffff) )
{
ERR("registry file corrupt! invalid root dke !\n");
goto error1;
}
ret = _w95_parse_dke(hkey, creg, rgkn, root_dke, level);
}
break;
/* nt 'regf'*/
case NT_REG_HEADER_BLOCK_ID:
{
nt_regf * regf;
nt_hbin * hbin;
nt_hbin_sub * hbin_sub;
nt_nk* nk;
filetype = "NT";
TRACE("Loading %s registry '%s' '%s'\n", filetype, fn, full_name.long_name);
/* start block */
regf = base;
/* hbin block */
hbin = (nt_hbin*)((char*) base + 0x1000);
if (hbin->id != NT_REG_POOL_BLOCK_ID)
{
ERR( "hbin block invalid\n");
goto error1;
}
/* hbin_sub block */
hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
{
ERR( "hbin_sub block invalid\n");
goto error1;
}
/* nk block */
nk = (nt_nk*)&(hbin_sub->data[0]);
if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
{
ERR( "special nk block not found\n");
goto error1;
}
ret = _nt_parse_nk (hkey, (char *) base + 0x1000, nk, level);
}
break;
default:
{
ERR("unknown registry signature !\n");
goto error1;
}
}
error1: if(!ret)
{
ERR("error loading %s registry file %s\n",
filetype, full_name.long_name);
if (!strcmp(filetype, "win95"))
ERR("Please report to a.mohr@mailto.de.\n");
ERR("Make a backup of the file, run a good reg cleaner program and try again !\n");
}
munmap(base, st.st_size);
error: close(fd);
return ret;
}
/* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjwall, tor@sn.no */
/* /*
reghack - windows 3.11 registry data format demo program. list of subkeys without hash
The reg.dat file has 3 parts, a header, a table of 8-byte entries that is li --+-->nk
a combined hash table and tree description, and finally a text table. |
+-->nk
*/
typedef struct {
WORD id; /* 0x00 0x696c */
WORD nr_keys;
DWORD off_nk[1];
} nt_li;
The header is obvious from the struct header. The taboff1 and taboff2 /*
fields are always 0x20, and their usage is unknown. this is a intermediate node
The 8-byte entry table has various entry types. ri --+-->li--+-->nk
| +
| +-->nk
|
+-->li--+-->nk
+
+-->nk
*/
typedef struct {
WORD id; /* 0x00 0x6972 */
WORD nr_li; /* 0x02 number off offsets */
DWORD off_li[1]; /* 0x04 points to li */
} nt_ri;
tabent[0] is a root index. The second word has the index of the root of typedef struct {
the directory. WORD id; /* 0x00 'vk' */
tabent[1..hashsize] is a hash table. The first word in the hash entry is WORD nam_len;
the index of the key/value that has that hash. Data with the same DWORD data_len;
hash value are on a circular list. The other three words in the DWORD data_off;
hash entry are always zero. DWORD type;
tabent[hashsize..tabcnt] is the tree structure. There are two kinds of WORD flag;
entry: dirent and keyent/valent. They are identified by context. WORD uk1;
tabent[freeidx] is the first free entry. The first word in a free entry char name[1];
is the index of the next free entry. The last has 0 as a link. } nt_vk;
The other three words in the free list are probably irrelevant.
Entries in text table are preceded by a word at offset-2. This word /*
has the value (2*index)+1, where index is the referring keyent/valent * gets a value
entry in the table. I have no suggestion for the 2* and the +1. *
Following the word, there are N bytes of data, as per the keyent/valent * vk->flag:
entry length. The offset of the keyent/valent entry is from the start * 0 value is a default value
of the text table to the first data byte. * 1 the value has a name
*
* vk->data_len
* len of the whole data block
* - reg_sz (unicode)
* bytes including the terminating \0 = 2*(number_of_chars+1)
* - reg_dword, reg_binary:
* if highest bit of data_len is set data_off contains the value
*/
static int _nt_dump_vk(LPSTR key_name, char *base, nt_vk *vk,FILE *f)
{
BYTE *pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
struct key_value value;
This information is not available from Microsoft. The data format is if (vk->id != NT_REG_VALUE_BLOCK_ID) {
deduced from the reg.dat file by me. Mistakes may ERR("unknown block found (0x%04x), please report!\n", vk->id);
have been made. I claim no rights and give no guarantees for this program. return FALSE;
}
Tor Sjwall, tor@sn.no value.nameW = _strdupnAtoW(vk->name,vk->nam_len);
*/ value.type = vk->type;
value.len = (vk->data_len & 0x7fffffff);
value.data = (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata;
_dump_value(&value,f);
free(value.nameW);
return TRUE;
}
/* it's called from _nt_dump_lf() */
static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level);
/*
* get the subkeys
*
* this structure contains the hash of a keyname and points to all
* subkeys
*
* exception: if the id is 'il' there are no hash values and every
* dword is a offset
*/
static int _nt_dump_lf(LPSTR key_name, char *base, int subkeys, nt_lf *lf, FILE *f, int level)
{
int i;
if (lf->id == NT_REG_HASH_BLOCK_ID) {
if (subkeys != lf->nr_keys) goto error1;
for (i=0; i<lf->nr_keys; i++)
if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), f, level)) goto error;
} else if (lf->id == NT_REG_NOHASH_BLOCK_ID) {
nt_li * li = (nt_li*)lf;
if (subkeys != li->nr_keys) goto error1;
for (i=0; i<li->nr_keys; i++)
if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+li->off_nk[i]+4), f, level)) goto error;
} else if (lf->id == NT_REG_RI_BLOCK_ID) { /* ri */
nt_ri * ri = (nt_ri*)lf;
int li_subkeys = 0;
/* count all subkeys */
for (i=0; i<ri->nr_li; i++) {
nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
li_subkeys += li->nr_keys;
}
/* reg.dat header format */ /* check number */
struct _w31_header { if (subkeys != li_subkeys) goto error1;
char cookie[8]; /* 'SHCC3.10' */
unsigned long taboff1; /* offset of hash table (??) = 0x20 */
unsigned long taboff2; /* offset of index table (??) = 0x20 */
unsigned long tabcnt; /* number of entries in index table */
unsigned long textoff; /* offset of text part */
unsigned long textsize; /* byte size of text part */
unsigned short hashsize; /* hash size */
unsigned short freeidx; /* free index */
};
/* generic format of table entries */ /* loop through the keys */
struct _w31_tabent { for (i=0; i<ri->nr_li; i++) {
unsigned short w0, w1, w2, w3; nt_li *li = (nt_li*)(base+ri->off_li[i]+4);
}; if (!_nt_dump_lf(key_name, base, li->nr_keys, (nt_lf*)li, f, level)) goto error;
}
} else goto error2;
/* directory tabent: */ return TRUE;
struct _w31_dirent {
unsigned short sibling_idx; /* table index of sibling dirent */
unsigned short child_idx; /* table index of child dirent */
unsigned short key_idx; /* table index of key keyent */
unsigned short value_idx; /* table index of value valent */
};
/* key tabent: */ error2:
struct _w31_keyent { ERR("unknown node id 0x%04x, please report!\n", lf->id);
unsigned short hash_idx; /* hash chain index for string */ return TRUE;
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
/* value tabent: */ error1:
struct _w31_valent { ERR("registry file corrupt! (inconsistent number of subkeys)\n");
unsigned short hash_idx; /* hash chain index for string */ return FALSE;
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
/* recursive helper function to display a directory tree */ error:
void ERR("error reading lf block\n");
__w31_dumptree( unsigned short idx, return FALSE;
unsigned char *txt,
struct _w31_tabent *tab,
struct _w31_header *head,
HKEY hkey,
time_t lastmodified,
int level
) {
struct _w31_dirent *dir;
struct _w31_keyent *key;
struct _w31_valent *val;
HKEY subkey = 0;
static char tail[400];
while (idx!=0) {
dir=(struct _w31_dirent*)&tab[idx];
if (dir->key_idx) {
key = (struct _w31_keyent*)&tab[dir->key_idx];
memcpy(tail,&txt[key->string_off],key->length);
tail[key->length]='\0';
/* all toplevel entries AND the entries in the
* toplevel subdirectory belong to \SOFTWARE\Classes
*/
if (!level && !strcmp(tail,".classes")) {
__w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
idx=dir->sibling_idx;
continue;
}
if (subkey) RegCloseKey( subkey );
if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
/* only add if leaf node or valued node */
if (dir->value_idx!=0||dir->child_idx==0) {
if (dir->value_idx) {
val=(struct _w31_valent*)&tab[dir->value_idx];
memcpy(tail,&txt[val->string_off],val->length);
tail[val->length]='\0';
RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
}
}
} else {
TRACE("strange: no directory key name, idx=%04x\n", idx);
}
__w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
idx=dir->sibling_idx;
}
if (subkey) RegCloseKey( subkey );
} }
/* _nt_dump_nk [Internal] */
static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level)
{
unsigned int n;
DWORD *vl;
LPSTR new_key_name = NULL;
/******************************************************************************
* _w31_loadreg [Internal]
*/
void _w31_loadreg(void) {
HFILE hf;
struct _w31_header head;
struct _w31_tabent *tab;
unsigned char *txt;
unsigned int len;
OFSTRUCT ofs;
BY_HANDLE_FILE_INFORMATION hfinfo;
time_t lastmodified;
TRACE("(void)\n"); if (nk->SubBlockId != NT_REG_KEY_BLOCK_ID) {
ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
return FALSE;
}
hf = OpenFile("reg.dat",&ofs,OF_READ); if ((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) && (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) {
if (hf==HFILE_ERROR) ERR("registry file corrupt!\n");
return; return FALSE;
}
/* read & dump header */ /* create the new key */
if (sizeof(head)!=_lread(hf,&head,sizeof(head))) { if (level <= 0) {
ERR("reg.dat is too short.\n"); /* create new subkey name */
_lclose(hf); new_key_name = _strdupnA(key_name,strlen(key_name)+nk->name_len+1);
return; if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
} strncat(new_key_name,nk->name,nk->name_len);
if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
ERR("reg.dat has bad signature.\n"); /* write the key path (something like [Software\\Microsoft\\..]) only if:
_lclose(hf); 1) key has some values
return; 2) key has no values and no subkeys
} */
if (nk->nr_values > 0) {
/* there are some values */
fprintf(f,"\n[");
_dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
fprintf(f,"]\n");
}
if ((nk->nr_subkeys == 0) && (nk->nr_values == 0)) {
/* no subkeys and no values */
fprintf(f,"\n[");
_dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
fprintf(f,"]\n");
}
len = head.tabcnt * sizeof(struct _w31_tabent); /* loop trough the value list */
/* read and dump index table */ vl = (DWORD *)(base+nk->valuelist_off+4);
tab = xmalloc(len); for (n=0; n<nk->nr_values; n++) {
if (len!=_lread(hf,tab,len)) { nt_vk * vk = (nt_vk*)(base+vl[n]+4);
ERR("couldn't read %d bytes.\n",len); if (!_nt_dump_vk(new_key_name, base, vk, f)) {
free(tab); free(new_key_name);
_lclose(hf); return FALSE;
return; }
} }
} else new_key_name = _strdupnA(key_name,strlen(key_name));
/* read text */ /* loop through the subkeys */
txt = xmalloc(head.textsize); if (nk->nr_subkeys) {
if (-1==_llseek(hf,head.textoff,SEEK_SET)) { nt_lf *lf = (nt_lf*)(base+nk->lf_off+4);
ERR("couldn't seek to textblock.\n"); if (!_nt_dump_lf(new_key_name, base, nk->nr_subkeys, lf, f, level-1)) {
free(tab); free(new_key_name);
free(txt); return FALSE;
_lclose(hf); }
return; }
}
if (head.textsize!=_lread(hf,txt,head.textsize)) {
ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
free(tab);
free(txt);
_lclose(hf);
return;
}
if (!GetFileInformationByHandle(hf,&hfinfo)) { free(new_key_name);
ERR("GetFileInformationByHandle failed?.\n"); return TRUE;
free(tab);
free(txt);
_lclose(hf);
return;
}
lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
__w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
free(tab);
free(txt);
_lclose(hf);
return;
} }
/* end nt loader */
static void save_at_exit( HKEY hkey, const char *path ) /**********************************************************************************
* _set_registry_levels [Internal]
*
* set level to 0 for loading system files
* set level to 1 for loading user files
*/
static void _set_registry_levels(int level,int saving,int period)
{ {
const char *confdir = get_config_dir(); SERVER_START_REQ
size_t len = strlen(confdir) + strlen(path) + 2;
if (len > REQUEST_MAX_VAR_SIZE)
{ {
struct set_registry_levels_request *req = server_alloc_req( sizeof(*req), 0 );
req->current = level;
req->saving = saving;
req->period = period;
server_call( REQ_SET_REGISTRY_LEVELS );
}
SERVER_END_REQ;
}
/* _save_at_exit [Internal] */
static void _save_at_exit(HKEY hkey,LPCSTR path)
{
LPCSTR confdir = get_config_dir();
size_t len = strlen(confdir) + strlen(path) + 2;
if (len > REQUEST_MAX_VAR_SIZE) {
ERR( "config dir '%s' too long\n", confdir ); ERR( "config dir '%s' too long\n", confdir );
return; return;
} }
...@@ -1391,271 +1028,550 @@ static void save_at_exit( HKEY hkey, const char *path ) ...@@ -1391,271 +1028,550 @@ static void save_at_exit( HKEY hkey, const char *path )
SERVER_END_REQ; SERVER_END_REQ;
} }
/* configure save files and start the periodic saving timer */ /* configure save files and start the periodic saving timer [Internal] */
static void SHELL_InitRegistrySaving( HKEY hkey_users_default ) static void _init_registry_saving( HKEY hkey_users_default )
{ {
int all = PROFILE_GetWineIniBool( "registry", "SaveOnlyUpdatedKeys", 1 ); int all;
int period = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 ); int period;
all = PROFILE_GetWineIniBool("registry","SaveOnlyUpdatedKeys",1);
period = PROFILE_GetWineIniInt("registry","PeriodicSave",0);
/* set saving level (0 for saving everything, 1 for saving only modified keys) */ /* set saving level (0 for saving everything, 1 for saving only modified keys) */
SERVER_START_REQ _set_registry_levels(1,!all,period*1000);
{
struct set_registry_levels_request *req = server_alloc_req( sizeof(*req), 0 );
req->current = 1;
req->saving = !all;
req->period = period * 1000;
server_call( REQ_SET_REGISTRY_LEVELS );
}
SERVER_END_REQ;
if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1)) if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1))
{ {
save_at_exit( HKEY_CURRENT_USER, SAVE_CURRENT_USER ); _save_at_exit(HKEY_CURRENT_USER,SAVE_LOCAL_REGBRANCH_CURRENT_USER );
save_at_exit( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE ); _save_at_exit(HKEY_LOCAL_MACHINE,SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
save_at_exit( hkey_users_default, SAVE_DEFAULT_USER ); _save_at_exit(hkey_users_default,SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
} }
}
}
/********************************************************************************** /******************************************************************************
* SetLoadLevel [Internal] * _allocate_default_keys [Internal]
* * Registry initialisation, allocates some default keys.
* set level to 0 for loading system files
* set level to 1 for loading user files
*/ */
static void SetLoadLevel(int level) static void _allocate_default_keys(void) {
{ HKEY hkey;
SERVER_START_REQ char buf[200];
{
struct set_registry_levels_request *req = server_alloc_req( sizeof(*req), 0 );
req->current = level; TRACE("(void)\n");
req->saving = 0;
req->period = 0;
server_call( REQ_SET_REGISTRY_LEVELS );
}
SERVER_END_REQ;
}
/********************************************************************************** RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
* SHELL_LoadRegistry [Internal] RegCloseKey(hkey);
*/
#define REG_DONTLOAD -1
#define REG_WIN31 0
#define REG_WIN95 1
#define REG_WINNT 2
void SHELL_LoadRegistry( void ) /* This was an Open, but since it is called before the real registries
{ are loaded, it was changed to a Create - MTB 980507*/
HKEY hkey; RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
char windir[MAX_PATHNAME_LEN]; RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
char path[MAX_PATHNAME_LEN]; RegCloseKey(hkey);
int systemtype = REG_WIN31;
HKEY hkey_users_default;
TRACE("(void)\n"); /* \\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion
* CurrentVersion
* CurrentBuildNumber
* CurrentType
* string RegisteredOwner
* string RegisteredOrganization
*
*/
/* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
* string SysContact
* string SysLocation
* SysServices
*/
if (-1!=gethostname(buf,200)) {
RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
RegCloseKey(hkey);
}
if (!CLIENT_IsBootThread()) return; /* already loaded */ RegCreateKeyA(HKEY_USERS,".Default",&hkey);
RegCloseKey(hkey);
}
REGISTRY_Init(); #define REG_DONTLOAD -1
SetLoadLevel(0); #define REG_WIN31 0
#define REG_WIN95 1
#define REG_WINNT 2
if (RegCreateKeyA(HKEY_USERS, ".Default", &hkey_users_default)) /* return the type of native registry [Internal] */
hkey_users_default = 0; static int _get_reg_type(void)
{
char windir[MAX_PATHNAME_LEN];
char tmp[MAX_PATHNAME_LEN];
int ret = REG_WIN31;
GetWindowsDirectoryA( windir, MAX_PATHNAME_LEN ); GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
if (PROFILE_GetWineIniBool( "Registry", "LoadWindowsRegistryFiles", 1))
{
/* test %windir%/system32/config/system --> winnt */ /* test %windir%/system32/config/system --> winnt */
strcpy(path, windir); strcpy(tmp, windir);
strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1); strncat(tmp, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(tmp) - 1);
if(GetFileAttributesA(path) != (DWORD)-1) if(GetFileAttributesA(tmp) != (DWORD)-1) {
{ ret = REG_WINNT;
systemtype = REG_WINNT;
} }
else else
{ {
/* test %windir%/system.dat --> win95 */ /* test %windir%/system.dat --> win95 */
strcpy(path, windir); strcpy(tmp, windir);
strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1); strncat(tmp, "\\system.dat", MAX_PATHNAME_LEN - strlen(tmp) - 1);
if(GetFileAttributesA(path) != (DWORD)-1) if(GetFileAttributesA(tmp) != (DWORD)-1) {
{ ret = REG_WIN95;
systemtype = REG_WIN95;
} }
} }
if ((systemtype==REG_WINNT) if ((ret == REG_WINNT) && (!PROFILE_GetWineIniString( "Wine", "Profile", "", tmp, MAX_PATHNAME_LEN))) {
&& (! PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)))
{
MESSAGE("When you are running with a native NT directory specify\n"); MESSAGE("When you are running with a native NT directory specify\n");
MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n"); MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
MESSAGE("registry (LoadWindowsRegistryFiles=N)\n"); MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
systemtype = REG_DONTLOAD; ret = REG_DONTLOAD;
} }
}
else return ret;
{ }
/* only wine registry */
systemtype = REG_DONTLOAD; #define WINE_REG_VER_ERROR -1
} #define WINE_REG_VER_1 0
#define WINE_REG_VER_2 1
switch (systemtype) #define WINE_REG_VER_OLD 2
{ #define WINE_REG_VER_UNKNOWN 3
case REG_WIN31:
_w31_loadreg(); /* return the version of wine registry file [Internal] */
break; static int _get_wine_registry_file_format_version(LPCSTR fn)
{
case REG_WIN95: FILE *f;
/* Load windows 95 entries */ char tmp[50];
NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0); int ver;
strcpy(path, windir); if ((f=fopen(fn,"rt")) == NULL) {
strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1); WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno));
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0); return WINE_REG_VER_ERROR;
}
if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
{ if (fgets(tmp,50,f) == NULL) {
/* user specific user.dat */ WARN("Error reading %s: %s\n",fn,strerror(errno));
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1); fclose(f);
if (!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 )) return WINE_REG_VER_ERROR;
{ }
MESSAGE("can't load win95 user-registry %s\n", path); fclose(f);
MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
} if (sscanf(tmp,"WINE REGISTRY Version %d",&ver) != 1) return WINE_REG_VER_UNKNOWN;
/* default user.dat */ switch (ver) {
if (hkey_users_default) case 1:
{ return WINE_REG_VER_1;
strcpy(path, windir); break;
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1); case 2:
NativeRegLoadKey(hkey_users_default, path, 1); return WINE_REG_VER_2;
} break;
} default:
else return WINE_REG_VER_UNKNOWN;
{ }
/* global user.dat */ }
strcpy(path, windir);
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1); /* load the registry file in wine format [Internal] */
NativeRegLoadKey(HKEY_CURRENT_USER, path, 1); static void load_wine_registry(HKEY hkey,LPCSTR fn)
} {
break; int file_format;
case REG_WINNT: file_format = _get_wine_registry_file_format_version(fn);
/* default user.dat */ switch (file_format) {
if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
{ case WINE_REG_VER_1:
strncat(path, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(path) - 1); WARN("Unable to load registry file %s: old format which is no longer supported.\n",fn);
if(!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 )) break;
{
MESSAGE("can't load NT user-registry %s\n", path); case WINE_REG_VER_2: {
MESSAGE("check wine.conf, section [Wine], value 'Profile'\n"); HANDLE file;
if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, -1, TRUE )) != INVALID_HANDLE_VALUE)
{
SERVER_START_REQ
{
struct load_registry_request *req = server_alloc_req( sizeof(*req), 0 );
req->hkey = hkey;
req->file = file;
server_call( REQ_LOAD_REGISTRY );
}
SERVER_END_REQ;
CloseHandle( file );
}
break;
} }
}
/* default user.dat */ case WINE_REG_VER_UNKNOWN:
if (hkey_users_default) WARN("Unable to load registry file %s: unknown format.\n",fn);
{ break;
strcpy(path, windir);
strncat(path, "\\system32\\config\\default", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(hkey_users_default, path, 1);
}
/* case WINE_REG_VER_ERROR:
* FIXME break;
* map HLM\System\ControlSet001 to HLM\System\CurrentControlSet }
*/ }
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM", &hkey)) /* generate and return the name of the tmp file and associated stream [Internal] */
{ static LPSTR _get_tmp_fn(FILE **f)
strcpy(path, windir); {
strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1); LPSTR ret;
NativeRegLoadKey(hkey, path, 1); int tmp_fd,count;
RegCloseKey(hkey);
} ret = _xmalloc(50);
for (count = 0;;) {
sprintf(ret,"/tmp/reg%lx%04x.tmp",(long)getpid(),count++);
if ((tmp_fd = open(ret,O_CREAT | O_EXCL | O_WRONLY,0666)) != -1) break;
if (errno != EEXIST) {
ERR("Unexpected error while open() call: %s\n",strerror(errno));
free(ret);
*f = NULL;
return NULL;
}
}
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE", &hkey)) if ((*f = fdopen(tmp_fd,"w")) == NULL) {
{ ERR("Unexpected error while fdopen() call: %s\n",strerror(errno));
strcpy(path, windir); close(tmp_fd);
strncat(path, "\\system32\\config\\software", MAX_PATHNAME_LEN - strlen(path) - 1); free(ret);
NativeRegLoadKey(hkey, path, 1); return NULL;
RegCloseKey(hkey); }
}
strcpy(path, windir); return ret;
strncat(path, "\\system32\\config\\sam", MAX_PATHNAME_LEN - strlen(path) - 1); }
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
strcpy(path, windir); /* convert win95 native registry file to wine format [Internal] */
strncat(path, "\\system32\\config\\security", MAX_PATHNAME_LEN - strlen(path) - 1); static LPSTR _convert_win95_registry_to_wine_format(LPCSTR fn,int level)
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0); {
int fd;
FILE *f;
DOS_FULL_NAME full_name;
void *base;
LPSTR ret = NULL;
struct stat st;
_w95creg *creg;
_w95rgkn *rgkn;
_w95dke *dke, *root_dke;
if (!DOSFS_GetFullName( fn, 0, &full_name )) return NULL;
/* map the registry into the memory */
if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return NULL;
if ((fstat(fd, &st) == -1)) goto error1;
if (!st.st_size) goto error1;
if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
/* control signature */
if (*(LPDWORD)base != W95_REG_CREG_ID) {
ERR("unable to load native win95 registry file %s: unknown signature.\n",fn);
goto error;
}
/* this key is generated when the nt-core booted successfully */ creg = base;
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey)) /* load the header (rgkn) */
RegCloseKey(hkey); rgkn = (_w95rgkn*)(creg + 1);
break; if (rgkn->id != W95_REG_RGKN_ID) {
} /* switch */ ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
goto error;
if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1)) }
{ if (rgkn->root_off != 0x20) {
/* ERR("rgkn->root_off not 0x20, please report !\n");
* Load the global HKU hive directly from sysconfdir goto error;
*/ }
_wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT ); if (rgkn->last_dke > rgkn->size)
{
/* ERR("registry file corrupt! last_dke > size!\n");
* Load the global machine defaults directly from sysconfdir goto error;
*/ }
_wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT ); /* verify last dke */
} dke = (_w95dke*)((char*)rgkn + rgkn->last_dke);
if (dke->x1 != 0x80000000)
SetLoadLevel(1); { /* wrong magic */
ERR("last dke invalid !\n");
/* goto error;
* Load the user saved registries }
*/ if (rgkn->size > creg->rgdb_off)
if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1)) {
{ ERR("registry file corrupt! rgkn size > rgdb_off !\n");
const char *confdir = get_config_dir(); goto error;
unsigned int len = strlen(confdir) + 20; }
char *fn = path; root_dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
if ( (root_dke->prevlvl != 0xffffffff) || (root_dke->next != 0xffffffff) )
if (len > sizeof(path)) fn = HeapAlloc( GetProcessHeap(), 0, len ); {
/* ERR("registry file corrupt! invalid root dke !\n");
* Load user's personal versions of global HKU/.Default keys goto error;
*/ }
if (fn)
{ if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
char *str; fprintf(f,"WINE REGISTRY Version 2");
strcpy( fn, confdir ); _w95_dump_dke("",creg,rgkn,root_dke,f,level);
str = fn + strlen(fn); fclose(f);
*str++ = '/';
error:
/* try to load HKU\.Default key only */ if(ret == NULL) {
strcpy( str, SAVE_DEFAULT_USER ); ERR("Unable to load native win95 registry file %s.\n",fn);
if (_wine_loadreg( hkey_users_default, fn )) ERR("Please report to a.mohr@mailto.de.\n");
{ ERR("Make a backup of the file, run a good reg cleaner program and try again!\n");
/* if not found load old file containing both HKU\.Default and HKU\user */ }
strcpy( str, SAVE_LOCAL_USERS_DEFAULT );
_wine_loadreg( HKEY_USERS, fn ); munmap(base, st.st_size);
} error1:
close(fd);
strcpy( str, SAVE_CURRENT_USER ); return ret;
_wine_loadreg( HKEY_CURRENT_USER, fn ); }
strcpy( str, SAVE_LOCAL_MACHINE ); /* convert winnt native registry file to wine format [Internal] */
_wine_loadreg( HKEY_LOCAL_MACHINE, fn ); static LPSTR _convert_winnt_registry_to_wine_format(LPCSTR fn,int level)
{
if (fn != path) HeapFree( GetProcessHeap(), 0, fn ); int fd;
} FILE *f;
} DOS_FULL_NAME full_name;
SHELL_InitRegistrySaving( hkey_users_default ); void *base;
RegCloseKey( hkey_users_default ); LPSTR ret = NULL;
struct stat st;
nt_regf *regf;
nt_hbin *hbin;
nt_hbin_sub *hbin_sub;
nt_nk *nk;
if (!DOSFS_GetFullName( fn, 0, &full_name )) return NULL;
/* map the registry into the memory */
if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return NULL;
if ((fstat(fd, &st) == -1)) goto error1;
if (!st.st_size) goto error1;
if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
/* control signature */
if (*(LPDWORD)base != NT_REG_HEADER_BLOCK_ID) {
ERR("unable to load native winnt registry file %s: unknown signature.\n",fn);
goto error;
}
/* start block */
regf = base;
/* hbin block */
hbin = (nt_hbin*)((char*) base + 0x1000);
if (hbin->id != NT_REG_POOL_BLOCK_ID) {
ERR( "hbin block invalid\n");
goto error;
}
/* hbin_sub block */
hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k')) {
ERR( "hbin_sub block invalid\n");
goto error;
}
/* nk block */
nk = (nt_nk*)&(hbin_sub->data[0]);
if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE) {
ERR( "special nk block not found\n");
goto error;
}
if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
fprintf(f,"WINE REGISTRY Version 2");
_nt_dump_nk("",(char*)base+0x1000,nk,f,level);
fclose(f);
error:
munmap(base,st.st_size);
error1:
close(fd);
return ret;
}
/* convert native native registry to wine format and load it via server call [Internal] */
static void _convert_and_load_native_registry(LPCSTR fn,HKEY hkey,int reg_type,int level)
{
LPSTR tmp = NULL;
switch (reg_type) {
case REG_WINNT:
/* FIXME: following function doesn't really convert yet */
tmp = _convert_winnt_registry_to_wine_format(fn,level);
break;
case REG_WIN95:
tmp = _convert_win95_registry_to_wine_format(fn,level);
break;
case REG_WIN31:
ERR("Don't know how to convert native 3.1 registry yet.\n");
break;
default:
ERR("Unknown registry format parameter (%d)\n",reg_type);
break;
}
if (tmp != NULL) {
load_wine_registry(hkey,tmp);
TRACE("File %s successfuly converted to %s and loaded to registry.\n",fn,tmp);
unlink(tmp);
}
else WARN("Unable to convert %s (not exist?)\n",fn);
free(tmp);
}
/* load all native windows registry files [Internal] */
static void _load_windows_registry( HKEY hkey_users_default )
{
int reg_type;
char windir[MAX_PATHNAME_LEN];
char path[MAX_PATHNAME_LEN];
GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
reg_type = _get_reg_type();
switch (reg_type) {
case REG_WINNT: {
HKEY hkey;
/* user specific ntuser.dat */
if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)) {
strcat(path,"\\ntuser.dat");
_convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WINNT,1);
}
/* default user.dat */
if (hkey_users_default) {
strcpy(path,windir);
strcat(path,"\\system32\\config\\default");
_convert_and_load_native_registry(path,hkey_users_default,REG_WINNT,1);
}
/*
* FIXME
* map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
*/
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM", &hkey)) {
strcpy(path,windir);
strcat(path,"\\system32\\config\\system");
_convert_and_load_native_registry(path,hkey,REG_WINNT,1);
RegCloseKey(hkey);
}
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE", &hkey)) {
strcpy(path,windir);
strcat(path,"\\system32\\config\\software");
_convert_and_load_native_registry(path,hkey,REG_WINNT,1);
RegCloseKey(hkey);
}
strcpy(path,windir);
strcat(path,"\\system32\\config\\sam");
_convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
strcpy(path,windir);
strcat(path,"\\system32\\config\\security");
_convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
/* this key is generated when the nt-core booted successfully */
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey)) RegCloseKey(hkey);
break;
}
case REG_WIN95:
_convert_and_load_native_registry("c:\\system.1st",HKEY_LOCAL_MACHINE,REG_WIN95,0);
strcpy(path,windir);
strcat(path,"\\system.dat");
_convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WIN95,0);
if (PROFILE_GetWineIniString("Wine","Profile","",path,MAX_PATHNAME_LEN)) {
/* user specific user.dat */
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
_convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
/* default user.dat */
if (hkey_users_default) {
strcpy(path,windir);
strcat(path,"\\user.dat");
_convert_and_load_native_registry(path,hkey_users_default,REG_WIN95,1);
}
} else {
strcpy(path,windir);
strcat(path,"\\user.dat");
_convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
}
break;
case REG_WIN31:
/* FIXME: here we should convert to *.reg file supported by server and call REQ_LOAD_REGISTRY, see REG_WIN95 case */
_w31_loadreg();
break;
case REG_DONTLOAD:
TRACE("REG_DONTLOAD\n");
break;
default:
ERR("switch: no match (%d)\n",reg_type);
break;
}
}
/* load global registry files (stored in /etc/wine) [Internal] */
static void _load_global_registry(void)
{
TRACE("(void)\n");
/* Load the global HKU hive directly from sysconfdir */
load_wine_registry( HKEY_USERS, SAVE_GLOBAL_REGBRANCH_USER_DEFAULT );
/* Load the global machine defaults directly from sysconfdir */
load_wine_registry( HKEY_LOCAL_MACHINE, SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE );
}
/* load home registry files (stored in ~/.wine) [Internal] */
static void _load_home_registry( HKEY hkey_users_default )
{
LPCSTR confdir = get_config_dir();
LPSTR tmp = _xmalloc(strlen(confdir)+20);
strcpy(tmp,confdir);
strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
load_wine_registry(hkey_users_default,tmp);
strcpy(tmp,confdir);
strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_CURRENT_USER);
load_wine_registry(HKEY_CURRENT_USER,tmp);
strcpy(tmp,confdir);
strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
load_wine_registry(HKEY_LOCAL_MACHINE,tmp);
free(tmp);
} }
/********************* API FUNCTIONS ***************************************/ /* load all registry (native and global and home) */
void SHELL_LoadRegistry( void )
{
HKEY hkey_users_default;
TRACE("(void)\n");
if (!CLIENT_IsBootThread()) return; /* already loaded */
if (!RegCreateKeyA(HKEY_USERS,".Default",&hkey_users_default)) hkey_users_default = 0;
_allocate_default_keys();
_set_registry_levels(0,0,0);
if (PROFILE_GetWineIniBool("Registry","LoadWindowsRegistryFiles",1))
_load_windows_registry( hkey_users_default );
if (PROFILE_GetWineIniBool("Registry","LoadGlobalRegistryFiles",1))
_load_global_registry();
_set_registry_levels(1,0,0);
if (PROFILE_GetWineIniBool("Registry","LoadHomeRegistryFiles",1))
_load_home_registry( hkey_users_default );
_init_registry_saving( hkey_users_default );
RegCloseKey(hkey_users_default);
}
/***************************************************************************/
/* API FUNCTIONS */
/***************************************************************************/
/****************************************************************************** /******************************************************************************
* RegFlushKey [KERNEL.227] [ADVAPI32.143] * RegFlushKey [KERNEL.227] [ADVAPI32.143]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment