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 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#ifdef HAVE_SYS_ERRNO_H
#include <sys/errno.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine/winbase16.h"
#include <fcntl.h>
#include "winerror.h"
#include "file.h"
#include "heap.h"
......@@ -47,718 +36,376 @@
#include "options.h"
#include "winreg.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);
/* FIXME: following defines should be configured global ... */
DEFAULT_DEBUG_CHANNEL(reg);
#define SAVE_USERS_DEFAULT ETCDIR"/wine.userreg"
#define SAVE_LOCAL_MACHINE_DEFAULT ETCDIR"/wine.systemreg"
/* FIXME: following defines should be configured global */
#define SAVE_GLOBAL_REGBRANCH_USER_DEFAULT ETCDIR"/wine.userreg"
#define SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE ETCDIR"/wine.systemreg"
/* relative in ~user/.wine/ : */
#define SAVE_CURRENT_USER "user.reg"
#define SAVE_DEFAULT_USER "userdef.reg"
#define SAVE_LOCAL_USERS_DEFAULT "wine.userreg"
#define SAVE_LOCAL_MACHINE "system.reg"
#define SAVE_LOCAL_REGBRANCH_CURRENT_USER "user.reg"
#define SAVE_LOCAL_REGBRANCH_USER_DEFAULT "userdef.reg"
#define SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE "system.reg"
static void *xmalloc( size_t size )
/* _xmalloc [Internal] */
static void *_xmalloc( size_t size )
{
void *res;
res = malloc (size ? size : 1);
if (res == NULL) {
WARN("Virtual memory exhausted.\n");
exit (1);
}
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);
}
}
/************************ LOAD Registry Function ****************************/
/******************************************************************************
* _find_or_add_key [Internal]
*/
static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
/* _strdupnA [Internal] */
static LPSTR _strdupnA(LPCSTR str,size_t len)
{
HKEY subkey;
if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
if (keyname) free( keyname );
return subkey;
}
LPSTR ret;
/******************************************************************************
* _find_or_add_value [Internal]
*/
static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
{
RegSetValueExW( hkey, name, 0, type, data, len );
if (name) free( name );
if (data) free( data );
if (!str) return NULL;
ret = _xmalloc( len + 1 );
memcpy( ret, str, len );
ret[len] = 0x00;
return ret;
}
/******************************************************************************
* _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 )
/* convert ansi string to unicode [Internal] */
static LPWSTR _strdupnAtoW(LPCSTR strA,size_t lenA)
{
char *s,*curread;
int mylen,curoff;
curread = *buf;
mylen = *len;
**buf = '\0';
while (1) {
while (1) {
s=fgets(curread,mylen,F);
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;
LPWSTR ret;
size_t lenW;
if (!strA) return NULL;
lenW = MultiByteToWideChar(CP_ACP,0,strA,lenA,NULL,0);
ret = _xmalloc(lenW*sizeof(WCHAR)+sizeof(WCHAR));
MultiByteToWideChar(CP_ACP,0,strA,lenA,ret,lenW);
ret[lenW] = 0;
return ret;
}
/******************************************************************************
* _wine_read_USTRING [Internal]
*
* 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 )
/* dump a Unicode string with proper escaping [Internal] */
/* FIXME: this code duplicates server/unicode.c */
static int _dump_strW(const WCHAR *str,size_t len,FILE *f,char escape[2])
{
char *s;
LPWSTR ws;
/* read up to "=" or "\0" or "\n" */
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;
}
static const char escapes[32] = ".......abtnvfr.............e....";
char buffer[256];
LPSTR pos = buffer;
int count = 0;
/******************************************************************************
* _wine_loadsubkey [Internal]
*
* NOTES
* It seems like this is returning a boolean. Should it?
*
* 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++;
for (; len; str++, len--)
{
if (pos > buffer + sizeof(buffer) - 8)
{
fwrite( buffer, pos - buffer, 1, f );
count += pos - buffer;
pos = buffer;
}
if (i>level) {
if (!subkey) {
WARN("Got a subhierarchy without resp. key?\n");
return 0;
}
if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
if (!_wine_read_line(F,buf,buflen))
goto done;
if (*str > 127) /* hex escape */
{
if (len > 1 && str[1] < 128 && isxdigit((char)str[1]))
pos += sprintf( pos, "\\x%04x", *str );
else
pos += sprintf( pos, "\\x%x", *str );
continue;
}
/* let the caller handle this line */
if (i<level || **buf=='\0')
goto done;
/* it can be: a value or a keyname. Parse the name first */
s=_wine_read_USTRING(s,&name);
/* switch() default: hack to avoid gotos */
switch (0) {
default:
if (*s=='\0') {
if (subkey) RegCloseKey( subkey );
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;
if (*str < 32) /* octal or C escape */
{
if (!*str && len == 1) continue; /* do not output terminating NULL */
if (escapes[*str] != '.')
pos += sprintf( pos, "\\%c", escapes[*str] );
else if (len > 1 && str[1] >= '0' && str[1] <= '7')
pos += sprintf( pos, "\\%03o", *str );
else
pos += sprintf( pos, "\\%o", *str );
continue;
}
if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\';
*pos++ = *str;
}
done:
if (subkey) RegCloseKey( subkey );
return 1;
fwrite( buffer, pos - buffer, 1, f );
count += pos - buffer;
return count;
}
/******************************************************************************
* _wine_loadsubreg [Internal]
*/
static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
/* 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])
{
int ver;
char *buf;
int buflen;
buf=xmalloc(10);buflen=10;
if (!_wine_read_line(F,&buf,&buflen)) {
free(buf);
return 0;
}
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;
WCHAR *strW;
int ret;
if (strA == NULL) return 0;
strW = _strdupnAtoW(strA,len);
ret = _dump_strW(strW,len,f,escape);
free(strW);
return ret;
}
/* 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 */
};
/******************************************************************************
* _wine_loadreg [Internal]
*/
static int _wine_loadreg( HKEY hkey, char *fn )
/* dump a value to a text file */
/* FIXME: this code duplicates server/registry.c */
static void _dump_value(struct key_value *value,FILE *f)
{
FILE *F;
int i, count;
TRACE("(%x,%s)\n",hkey,debugstr_a(fn));
F = fopen(fn,"rb");
if (F==NULL) {
WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
return -1;
if (value->nameW[0]) {
fputc( '\"', f );
count = 1 + _dump_strW(value->nameW,strlenW(value->nameW),f,"\"\"");
count += fprintf( f, "\"=" );
}
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);
fclose(F);
return 0;
fputc( '\n', f );
}
/* NT REGISTRY LOADER */
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#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 */
/******************************************************************/
/* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
/*
reghack - windows 3.11 registry data format demo program.
#define NT_REG_KEY_BLOCK_TYPE 0x20
#define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
a combined hash table and tree description, and finally a text table.
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;
The header is obvious from the struct header. The taboff1 and taboff2
fields are always 0x20, and their usage is unknown.
typedef struct
{
DWORD blocksize;
BYTE data[1];
} nt_hbin_sub;
The 8-byte entry table has various entry types.
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;
tabent[0] is a root index. The second word has the index of the root of
the directory.
tabent[1..hashsize] is a hash table. The first word in the hash entry is
the index of the key/value that has that hash. Data with the same
hash value are on a circular list. The other three words in the
hash entry are always zero.
tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
entry: dirent and keyent/valent. They are identified by context.
tabent[freeidx] is the first free entry. The first word in a free entry
is the index of the next free entry. The last has 0 as a link.
The other three words in the free list are probably irrelevant.
/*
* the value_list consists of offsets to the values (vk)
*/
typedef struct
{
WORD SubBlockId; /* 0x00 0x6B6E */
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;
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
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
entry length. The offset of the keyent/valent entry is from the start
of the text table to the first data byte.
typedef struct
{
DWORD off_nk; /* 0x00 */
DWORD name; /* 0x04 */
} hash_rec;
This information is not available from Microsoft. The data format is
deduced from the reg.dat file by me. Mistakes may
have been made. I claim no rights and give no guarantees for this program.
typedef struct
{
WORD id; /* 0x00 0x666c */
WORD nr_keys; /* 0x06 */
hash_rec hash_rec[1];
} nt_lf;
Tor Sjøwall, tor@sn.no
*/
/*
list of subkeys without hash
/* reg.dat header format */
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
|
+-->nk
*/
typedef struct
{
WORD id; /* 0x00 0x696c */
WORD nr_keys;
DWORD off_nk[1];
} nt_li;
/* generic format of table entries */
struct _w31_tabent {
unsigned short w0, w1, w2, w3;
};
/*
this is a intermediate node
/* directory tabent: */
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
| +
| +-->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;
/* key tabent: */
struct _w31_keyent {
unsigned short hash_idx; /* hash chain index for string */
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
typedef struct
{
WORD id; /* 0x00 'vk' */
WORD nam_len;
DWORD data_len;
DWORD data_off;
DWORD type;
WORD flag;
WORD uk1;
char name[1];
} nt_vk;
/* value tabent: */
struct _w31_valent {
unsigned short hash_idx; /* hash chain index for string */
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
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;
if (!str) return NULL;
ret = xmalloc( len + 1 );
memcpy( ret, str, len );
ret[len] = 0x00;
return ret;
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 );
}
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,
(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
/******************************************************************************
* _w31_loadreg [Internal]
*/
static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, 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_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)
void _w31_loadreg(void)
{
char * name;
unsigned int n;
DWORD * vl;
HKEY subkey = hkey;
if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID)
{
ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
goto error;
}
if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
(((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID))
{
ERR("registry file corrupt!\n");
goto error;
}
/* create the new key */
if(level <= 0)
{
name = _strdupnA( nk->name, nk->name_len);
if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
free(name);
}
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");
hf = OpenFile("reg.dat",&ofs,OF_READ);
if (hf==HFILE_ERROR) return;
/* read & dump header */
if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
ERR("reg.dat is too short.\n");
_lclose(hf);
return;
}
if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
ERR("reg.dat has bad signature.\n");
_lclose(hf);
return;
}
/* loop through the subkeys */
if (nk->nr_subkeys)
{
nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
if (!_nt_parse_lf(subkey, base, nk->nr_subkeys, lf, level-1)) goto error1;
}
len = head.tabcnt * sizeof(struct _w31_tabent);
/* read and dump index table */
tab = _xmalloc(len);
if (len!=_lread(hf,tab,len)) {
ERR("couldn't read %d bytes.\n",len);
free(tab);
_lclose(hf);
return;
}
/* loop trough the value list */
vl = (DWORD *)(base+nk->valuelist_off+4);
for (n=0; n<nk->nr_values; n++)
{
nt_vk * vk = (nt_vk*)(base+vl[n]+4);
if (!_nt_parse_vk(subkey, base, vk)) goto error1;
}
/* read text */
txt = _xmalloc(head.textsize);
if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
ERR("couldn't seek to textblock.\n");
free(tab);
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
* (i.e. Level was <= 0)
*/
if( subkey!=hkey ) RegCloseKey(subkey);
return TRUE;
error1: RegCloseKey(subkey);
error: return FALSE;
if (!GetFileInformationByHandle(hf,&hfinfo)) {
ERR("GetFileInformationByHandle failed?.\n");
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 */
/* windows 95 registry loader */
/***********************************************************************************/
/* windows 95 registry loader */
/***********************************************************************************/
/* SECTION 1: main header
*
......@@ -766,16 +413,15 @@ error: return FALSE;
*/
#define W95_REG_CREG_ID 0x47455243
typedef struct
{
DWORD id; /* "CREG" = W95_REG_CREG_ID */
DWORD version; /* ???? 0x00010000 */
DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
DWORD uk2; /* 0x0c */
WORD rgdb_num; /* 0x10 # of RGDB-blocks */
WORD uk3;
DWORD uk[3];
/* rgkn */
typedef struct {
DWORD id; /* "CREG" = W95_REG_CREG_ID */
DWORD version; /* ???? 0x00010000 */
DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
DWORD uk2; /* 0x0c */
WORD rgdb_num; /* 0x10 # of RGDB-blocks */
WORD uk3;
DWORD uk[3];
/* rgkn */
} _w95creg;
/* SECTION 2: Directory information (tree structure)
......@@ -786,13 +432,12 @@ typedef struct
*/
#define W95_REG_RGKN_ID 0x4e4b4752
typedef struct
{
DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
DWORD size; /* Size of the RGKN-block */
DWORD root_off; /* Rel. Offset of the root-record */
DWORD last_dke; /* Offset to last DKE ? */
DWORD uk[4];
typedef struct {
DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
DWORD size; /* Size of the RGKN-block */
DWORD root_off; /* Rel. Offset of the root-record */
DWORD last_dke; /* Offset to last DKE ? */
DWORD uk[4];
} _w95rgkn;
/* Disk Key Entry Structure
......@@ -826,16 +471,15 @@ typedef struct
* there is a one to one relationship between dke and dkh
*/
/* key struct, once per key */
typedef struct
{
DWORD x1; /* Free entry indicator(?) */
DWORD hash; /* sum of bytes of keyname */
DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
DWORD prevlvl; /* offset of previous key */
DWORD nextsub; /* offset of child key */
DWORD next; /* offset of sibling key */
WORD nrLS; /* id inside the rgdb block */
WORD nrMS; /* number of the rgdb block */
typedef struct {
DWORD x1; /* Free entry indicator(?) */
DWORD hash; /* sum of bytes of keyname */
DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
DWORD prevlvl; /* offset of previous key */
DWORD nextsub; /* offset of child key */
DWORD next; /* offset of sibling key */
WORD nrLS; /* id inside the rgdb block */
WORD nrMS; /* number of the rgdb block */
} _w95dke;
/* SECTION 3: key information, values and data
......@@ -853,42 +497,39 @@ typedef struct
/* block header, once per block */
#define W95_REG_RGDB_ID 0x42444752
typedef struct
{
DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */
DWORD size; /* 0x04 */
DWORD uk1; /* 0x08 */
DWORD uk2; /* 0x0c */
DWORD uk3; /* 0x10 */
DWORD uk4; /* 0x14 */
DWORD uk5; /* 0x18 */
DWORD uk6; /* 0x1c */
/* dkh */
typedef struct {
DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */
DWORD size; /* 0x04 */
DWORD uk1; /* 0x08 */
DWORD uk2; /* 0x0c */
DWORD uk3; /* 0x10 */
DWORD uk4; /* 0x14 */
DWORD uk5; /* 0x18 */
DWORD uk6; /* 0x1c */
/* dkh */
} _w95rgdb;
/* Disk Key Header structure (RGDB part), once per key */
typedef struct
{
DWORD nextkeyoff; /* 0x00 offset to next dkh */
WORD nrLS; /* 0x04 id inside the rgdb block */
WORD nrMS; /* 0x06 number of the rgdb block */
DWORD bytesused; /* 0x08 */
WORD keynamelen; /* 0x0c len of name */
WORD values; /* 0x0e number of values */
DWORD xx1; /* 0x10 */
char name[1]; /* 0x14 */
/* dkv */ /* 0x14 + keynamelen */
typedef struct {
DWORD nextkeyoff; /* 0x00 offset to next dkh */
WORD nrLS; /* 0x04 id inside the rgdb block */
WORD nrMS; /* 0x06 number of the rgdb block */
DWORD bytesused; /* 0x08 */
WORD keynamelen; /* 0x0c len of name */
WORD values; /* 0x0e number of values */
DWORD xx1; /* 0x10 */
char name[1]; /* 0x14 */
/* dkv */ /* 0x14 + keynamelen */
} _w95dkh;
/* Disk Key Value structure, once per value */
typedef struct
{
DWORD type; /* 0x00 */
DWORD x1; /* 0x04 */
WORD valnamelen; /* 0x08 length of name, 0 is default key */
WORD valdatalen; /* 0x0A length of data */
char name[1]; /* 0x0c */
/* raw data */ /* 0x0c + valnamelen */
typedef struct {
DWORD type; /* 0x00 */
DWORD x1; /* 0x04 */
WORD valnamelen; /* 0x08 length of name, 0 is default key */
WORD valdatalen; /* 0x0A length of data */
char name[1]; /* 0x0c */
/* raw data */ /* 0x0c + valnamelen */
} _w95dkv;
/******************************************************************************
......@@ -896,488 +537,484 @@ typedef struct
*
* 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;
_w95dkh * dkh;
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 */
}
_w95rgdb * rgdb;
_w95dkh * dkh;
int i;
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
{
if(nrLS==dkh->nrLS ) return dkh;
dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
} while ((char *)dkh < ((char*)rgdb+rgdb->size));
/* 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 */
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 (
HKEY hkey,
_w95dkh * dkh,
int nrLS,
int nrMS )
static int _w95_dump_dkv(_w95dkh *dkh,int nrLS,int nrMS,FILE *f)
{
_w95dkv * dkv;
int i;
DWORD ret;
char * name;
/* first value block */
dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
/* loop trought the values */
for (i=0; i< dkh->values; i++)
{
name = _strdupnA(dkv->name, dkv->valnamelen);
ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen);
if (ret) FIXME("RegSetValueEx returned: 0x%08lx\n", ret);
free (name);
/* next value */
dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
}
return TRUE;
_w95dkv * dkv;
int i;
/* first value block */
dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
/* loop trought the values */
for (i=0; i< dkh->values; i++) {
struct key_value value;
WCHAR *pdata;
value.nameW = _strdupnAtoW(dkv->name,dkv->valnamelen);
value.type = dkv->type;
value.len = dkv->valdatalen;
value.data = &(dkv->name[dkv->valnamelen]);
pdata = NULL;
if ( (value.type==REG_SZ) || (value.type==REG_EXPAND_SZ) || (value.type==REG_MULTI_SZ) ) {
pdata = _strdupnAtoW(value.data,value.len);
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(
HKEY hkey,
_w95creg * creg,
_w95rgkn *rgkn,
_w95dke * dke,
int level )
static int _w95_dump_dke(LPSTR key_name,_w95creg *creg,_w95rgkn *rgkn,_w95dke *dke,FILE *f,int level)
{
_w95dkh * dkh;
HKEY hsubkey = hkey;
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;
}
_w95dkh * dkh;
LPSTR new_key_name = NULL;
/* search subblock */
if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
{
fprintf(stderr, "dke pointing to missing dkh !\n");
goto error;
}
/* 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_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level);
/* has no sibling keys */
return FALSE;
}
if ( level <= 0 )
{
/* walk sibling keys */
if (dke->next != 0xffffffff )
{
if (!_w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level)) goto error;
}
/* create subkey and insert values */
name = _strdupnA( dkh->name, dkh->keynamelen);
if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
free(name);
if (!_w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS)) goto error1;
}
/* next sub key */
if (dke->nextsub != 0xffffffff)
{
if (!_w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1)) goto error1;
}
/* search subblock */
if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS))) {
ERR("dke pointing to missing dkh !\n");
return FALSE;
}
if (level <= 0) {
/* create new subkey name */
new_key_name = _strdupnA(key_name,strlen(key_name)+dkh->keynamelen+1);
if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
strncat(new_key_name,dkh->name,dkh->keynamelen);
/* walk sibling keys */
if (dke->next != 0xffffffff ) {
if (!_w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next),f,level)) {
free(new_key_name);
return FALSE;
}
}
/* 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;
error1: if (hsubkey != hkey) RegCloseKey(hsubkey);
error: return ret;
/* next sub key */
if (dke->nextsub != 0xffffffff) {
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 */
/******************************************************************************
* NativeRegLoadKey [Internal]
*
* Loads a native registry file (win95/nt)
* hkey root key
* fn filename
* level number of levels to cut away (eg. ".Default" in user.dat)
*
* this function intentionally uses unix file functions to make it possible
* to move it to a seperate registry helper programm
/***********************************************************************************/
/* windows NT registry loader */
/***********************************************************************************/
/* NT REGISTRY LOADER */
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#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
#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 )
{
int fd = 0;
struct stat st;
DOS_FULL_NAME full_name;
int ret = FALSE;
void * base;
char *filetype = "unknown";
if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
/* map the registry into the memory */
if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
if ((fstat(fd, &st) == -1)) goto error;
if (!st.st_size) goto error;
if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
switch (*(LPDWORD)base)
{
/* windows 95 'CREG' */
case W95_REG_CREG_ID:
{
_w95creg *creg;
_w95rgkn *rgkn;
_w95dke *dke, *root_dke;
creg = base;
filetype = "win95";
TRACE("Loading %s registry '%s' '%s'\n", filetype, fn, full_name.long_name);
/* load the header (rgkn) */
rgkn = (_w95rgkn*)(creg + 1);
if (rgkn->id != W95_REG_RGKN_ID)
{
ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
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;
}
typedef struct {
WORD SubBlockId; /* 0x00 0x6B6E */
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 {
DWORD off_nk; /* 0x00 */
DWORD name; /* 0x04 */
} hash_rec;
typedef struct {
WORD id; /* 0x00 0x666c */
WORD nr_keys; /* 0x06 */
hash_rec hash_rec[1];
} nt_lf;
/* 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
a combined hash table and tree description, and finally a text table.
li --+-->nk
|
+-->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
the directory.
tabent[1..hashsize] is a hash table. The first word in the hash entry is
the index of the key/value that has that hash. Data with the same
hash value are on a circular list. The other three words in the
hash entry are always zero.
tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
entry: dirent and keyent/valent. They are identified by context.
tabent[freeidx] is the first free entry. The first word in a free entry
is the index of the next free entry. The last has 0 as a link.
The other three words in the free list are probably irrelevant.
typedef struct {
WORD id; /* 0x00 'vk' */
WORD nam_len;
DWORD data_len;
DWORD data_off;
DWORD type;
WORD flag;
WORD uk1;
char name[1];
} nt_vk;
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
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
entry length. The offset of the keyent/valent entry is from the start
of the text table to the first data byte.
/*
* 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_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
deduced from the reg.dat file by me. Mistakes may
have been made. I claim no rights and give no guarantees for this program.
if (vk->id != NT_REG_VALUE_BLOCK_ID) {
ERR("unknown block found (0x%04x), please report!\n", vk->id);
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 */
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 */
};
/* check number */
if (subkeys != li_subkeys) goto error1;
/* generic format of table entries */
struct _w31_tabent {
unsigned short w0, w1, w2, w3;
};
/* 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_dump_lf(key_name, base, li->nr_keys, (nt_lf*)li, f, level)) goto error;
}
} else goto error2;
/* directory tabent: */
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 */
};
return TRUE;
/* key tabent: */
struct _w31_keyent {
unsigned short hash_idx; /* hash chain index for string */
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
error2:
ERR("unknown node id 0x%04x, please report!\n", lf->id);
return TRUE;
/* value tabent: */
struct _w31_valent {
unsigned short hash_idx; /* hash chain index for string */
unsigned short refcnt; /* reference count */
unsigned short length; /* length of string */
unsigned short string_off; /* offset of string in text table */
};
error1:
ERR("registry file corrupt! (inconsistent number of subkeys)\n");
return FALSE;
/* recursive helper function to display a directory tree */
void
__w31_dumptree( unsigned short idx,
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 );
error:
ERR("error reading lf block\n");
return FALSE;
}
/* _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 (hf==HFILE_ERROR)
return;
if ((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) && (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) {
ERR("registry file corrupt!\n");
return FALSE;
}
/* read & dump header */
if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
ERR("reg.dat is too short.\n");
_lclose(hf);
return;
}
if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
ERR("reg.dat has bad signature.\n");
_lclose(hf);
return;
}
/* create the new key */
if (level <= 0) {
/* create new subkey name */
new_key_name = _strdupnA(key_name,strlen(key_name)+nk->name_len+1);
if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
strncat(new_key_name,nk->name,nk->name_len);
/* write the key path (something like [Software\\Microsoft\\..]) only if:
1) key has some values
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);
/* read and dump index table */
tab = xmalloc(len);
if (len!=_lread(hf,tab,len)) {
ERR("couldn't read %d bytes.\n",len);
free(tab);
_lclose(hf);
return;
}
/* loop trough the value list */
vl = (DWORD *)(base+nk->valuelist_off+4);
for (n=0; n<nk->nr_values; n++) {
nt_vk * vk = (nt_vk*)(base+vl[n]+4);
if (!_nt_dump_vk(new_key_name, base, vk, f)) {
free(new_key_name);
return FALSE;
}
}
} else new_key_name = _strdupnA(key_name,strlen(key_name));
/* read text */
txt = xmalloc(head.textsize);
if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
ERR("couldn't seek to textblock.\n");
free(tab);
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;
}
/* loop through the subkeys */
if (nk->nr_subkeys) {
nt_lf *lf = (nt_lf*)(base+nk->lf_off+4);
if (!_nt_dump_lf(new_key_name, base, nk->nr_subkeys, lf, f, level-1)) {
free(new_key_name);
return FALSE;
}
}
if (!GetFileInformationByHandle(hf,&hfinfo)) {
ERR("GetFileInformationByHandle failed?.\n");
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;
free(new_key_name);
return TRUE;
}
/* 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();
size_t len = strlen(confdir) + strlen(path) + 2;
if (len > REQUEST_MAX_VAR_SIZE)
SERVER_START_REQ
{
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 );
return;
}
......@@ -1391,271 +1028,550 @@ static void save_at_exit( HKEY hkey, const char *path )
SERVER_END_REQ;
}
/* configure save files and start the periodic saving timer */
static void SHELL_InitRegistrySaving( HKEY hkey_users_default )
/* configure save files and start the periodic saving timer [Internal] */
static void _init_registry_saving( HKEY hkey_users_default )
{
int all = PROFILE_GetWineIniBool( "registry", "SaveOnlyUpdatedKeys", 1 );
int period = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 );
int all;
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) */
SERVER_START_REQ
{
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;
_set_registry_levels(1,!all,period*1000);
if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1))
{
save_at_exit( HKEY_CURRENT_USER, SAVE_CURRENT_USER );
save_at_exit( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE );
save_at_exit( hkey_users_default, SAVE_DEFAULT_USER );
_save_at_exit(HKEY_CURRENT_USER,SAVE_LOCAL_REGBRANCH_CURRENT_USER );
_save_at_exit(HKEY_LOCAL_MACHINE,SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
_save_at_exit(hkey_users_default,SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
}
}
}
/**********************************************************************************
* SetLoadLevel [Internal]
*
* set level to 0 for loading system files
* set level to 1 for loading user files
/******************************************************************************
* _allocate_default_keys [Internal]
* Registry initialisation, allocates some default keys.
*/
static void SetLoadLevel(int level)
{
SERVER_START_REQ
{
struct set_registry_levels_request *req = server_alloc_req( sizeof(*req), 0 );
static void _allocate_default_keys(void) {
HKEY hkey;
char buf[200];
req->current = level;
req->saving = 0;
req->period = 0;
server_call( REQ_SET_REGISTRY_LEVELS );
}
SERVER_END_REQ;
}
TRACE("(void)\n");
/**********************************************************************************
* SHELL_LoadRegistry [Internal]
*/
#define REG_DONTLOAD -1
#define REG_WIN31 0
#define REG_WIN95 1
#define REG_WINNT 2
RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
RegCloseKey(hkey);
void SHELL_LoadRegistry( void )
{
HKEY hkey;
char windir[MAX_PATHNAME_LEN];
char path[MAX_PATHNAME_LEN];
int systemtype = REG_WIN31;
HKEY hkey_users_default;
/* 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);
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();
SetLoadLevel(0);
#define REG_DONTLOAD -1
#define REG_WIN31 0
#define REG_WIN95 1
#define REG_WINNT 2
if (RegCreateKeyA(HKEY_USERS, ".Default", &hkey_users_default))
hkey_users_default = 0;
/* return the type of native registry [Internal] */
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 */
strcpy(path, windir);
strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
if(GetFileAttributesA(path) != (DWORD)-1)
{
systemtype = REG_WINNT;
strcpy(tmp, windir);
strncat(tmp, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(tmp) - 1);
if(GetFileAttributesA(tmp) != (DWORD)-1) {
ret = REG_WINNT;
}
else
{
/* test %windir%/system.dat --> win95 */
strcpy(path, windir);
strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
if(GetFileAttributesA(path) != (DWORD)-1)
{
systemtype = REG_WIN95;
strcpy(tmp, windir);
strncat(tmp, "\\system.dat", MAX_PATHNAME_LEN - strlen(tmp) - 1);
if(GetFileAttributesA(tmp) != (DWORD)-1) {
ret = REG_WIN95;
}
}
if ((systemtype==REG_WINNT)
&& (! PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)))
{
if ((ret == REG_WINNT) && (!PROFILE_GetWineIniString( "Wine", "Profile", "", tmp, MAX_PATHNAME_LEN))) {
MESSAGE("When you are running with a native NT directory specify\n");
MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
systemtype = REG_DONTLOAD;
ret = REG_DONTLOAD;
}
}
else
{
/* only wine registry */
systemtype = REG_DONTLOAD;
}
switch (systemtype)
{
case REG_WIN31:
_w31_loadreg();
break;
case REG_WIN95:
/* Load windows 95 entries */
NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
strcpy(path, windir);
strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
{
/* user specific user.dat */
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
if (!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
{
MESSAGE("can't load win95 user-registry %s\n", path);
MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
}
/* default user.dat */
if (hkey_users_default)
{
strcpy(path, windir);
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(hkey_users_default, path, 1);
}
}
else
{
/* global user.dat */
strcpy(path, windir);
strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(HKEY_CURRENT_USER, path, 1);
}
break;
case REG_WINNT:
/* default user.dat */
if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
{
strncat(path, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
if(!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
{
MESSAGE("can't load NT user-registry %s\n", path);
MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
return ret;
}
#define WINE_REG_VER_ERROR -1
#define WINE_REG_VER_1 0
#define WINE_REG_VER_2 1
#define WINE_REG_VER_OLD 2
#define WINE_REG_VER_UNKNOWN 3
/* return the version of wine registry file [Internal] */
static int _get_wine_registry_file_format_version(LPCSTR fn)
{
FILE *f;
char tmp[50];
int ver;
if ((f=fopen(fn,"rt")) == NULL) {
WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno));
return WINE_REG_VER_ERROR;
}
if (fgets(tmp,50,f) == NULL) {
WARN("Error reading %s: %s\n",fn,strerror(errno));
fclose(f);
return WINE_REG_VER_ERROR;
}
fclose(f);
if (sscanf(tmp,"WINE REGISTRY Version %d",&ver) != 1) return WINE_REG_VER_UNKNOWN;
switch (ver) {
case 1:
return WINE_REG_VER_1;
break;
case 2:
return WINE_REG_VER_2;
break;
default:
return WINE_REG_VER_UNKNOWN;
}
}
/* load the registry file in wine format [Internal] */
static void load_wine_registry(HKEY hkey,LPCSTR fn)
{
int file_format;
file_format = _get_wine_registry_file_format_version(fn);
switch (file_format) {
case WINE_REG_VER_1:
WARN("Unable to load registry file %s: old format which is no longer supported.\n",fn);
break;
case WINE_REG_VER_2: {
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 */
if (hkey_users_default)
{
strcpy(path, windir);
strncat(path, "\\system32\\config\\default", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(hkey_users_default, path, 1);
}
case WINE_REG_VER_UNKNOWN:
WARN("Unable to load registry file %s: unknown format.\n",fn);
break;
/*
* FIXME
* map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
*/
case WINE_REG_VER_ERROR:
break;
}
}
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM", &hkey))
{
strcpy(path, windir);
strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(hkey, path, 1);
RegCloseKey(hkey);
}
/* generate and return the name of the tmp file and associated stream [Internal] */
static LPSTR _get_tmp_fn(FILE **f)
{
LPSTR ret;
int tmp_fd,count;
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))
{
strcpy(path, windir);
strncat(path, "\\system32\\config\\software", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(hkey, path, 1);
RegCloseKey(hkey);
}
if ((*f = fdopen(tmp_fd,"w")) == NULL) {
ERR("Unexpected error while fdopen() call: %s\n",strerror(errno));
close(tmp_fd);
free(ret);
return NULL;
}
strcpy(path, windir);
strncat(path, "\\system32\\config\\sam", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
return ret;
}
strcpy(path, windir);
strncat(path, "\\system32\\config\\security", MAX_PATHNAME_LEN - strlen(path) - 1);
NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
/* convert win95 native registry file to wine format [Internal] */
static LPSTR _convert_win95_registry_to_wine_format(LPCSTR fn,int level)
{
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 */
if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey))
RegCloseKey(hkey);
break;
} /* switch */
if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
{
/*
* Load the global HKU hive directly from sysconfdir
*/
_wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
/*
* Load the global machine defaults directly from sysconfdir
*/
_wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
}
SetLoadLevel(1);
/*
* Load the user saved registries
*/
if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
{
const char *confdir = get_config_dir();
unsigned int len = strlen(confdir) + 20;
char *fn = path;
if (len > sizeof(path)) fn = HeapAlloc( GetProcessHeap(), 0, len );
/*
* Load user's personal versions of global HKU/.Default keys
*/
if (fn)
{
char *str;
strcpy( fn, confdir );
str = fn + strlen(fn);
*str++ = '/';
/* try to load HKU\.Default key only */
strcpy( str, SAVE_DEFAULT_USER );
if (_wine_loadreg( hkey_users_default, fn ))
{
/* if not found load old file containing both HKU\.Default and HKU\user */
strcpy( str, SAVE_LOCAL_USERS_DEFAULT );
_wine_loadreg( HKEY_USERS, fn );
}
strcpy( str, SAVE_CURRENT_USER );
_wine_loadreg( HKEY_CURRENT_USER, fn );
strcpy( str, SAVE_LOCAL_MACHINE );
_wine_loadreg( HKEY_LOCAL_MACHINE, fn );
if (fn != path) HeapFree( GetProcessHeap(), 0, fn );
}
}
SHELL_InitRegistrySaving( hkey_users_default );
RegCloseKey( hkey_users_default );
creg = base;
/* load the header (rgkn) */
rgkn = (_w95rgkn*)(creg + 1);
if (rgkn->id != W95_REG_RGKN_ID) {
ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
goto error;
}
if (rgkn->root_off != 0x20) {
ERR("rgkn->root_off not 0x20, please report !\n");
goto error;
}
if (rgkn->last_dke > rgkn->size)
{
ERR("registry file corrupt! last_dke > size!\n");
goto error;
}
/* verify last dke */
dke = (_w95dke*)((char*)rgkn + rgkn->last_dke);
if (dke->x1 != 0x80000000)
{ /* wrong magic */
ERR("last dke invalid !\n");
goto error;
}
if (rgkn->size > creg->rgdb_off)
{
ERR("registry file corrupt! rgkn size > rgdb_off !\n");
goto error;
}
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 error;
}
if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
fprintf(f,"WINE REGISTRY Version 2");
_w95_dump_dke("",creg,rgkn,root_dke,f,level);
fclose(f);
error:
if(ret == NULL) {
ERR("Unable to load native win95 registry file %s.\n",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");
}
munmap(base, st.st_size);
error1:
close(fd);
return ret;
}
/* convert winnt native registry file to wine format [Internal] */
static LPSTR _convert_winnt_registry_to_wine_format(LPCSTR fn,int level)
{
int fd;
FILE *f;
DOS_FULL_NAME full_name;
void *base;
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]
......
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