From 9e6b1d14ce04decf7c8dc07e7aebf46b04544a2b Mon Sep 17 00:00:00 2001 From: Juergen Schmied Date: Sat, 11 Dec 1999 23:22:52 +0000 Subject: [PATCH] Added loader for NT registry files. --- documentation/wine.conf.man.in | 59 ++++++ misc/registry.c | 357 ++++++++++++++++++++++++++++++++- wine.ini | 10 +- 3 files changed, 423 insertions(+), 3 deletions(-) diff --git a/documentation/wine.conf.man.in b/documentation/wine.conf.man.in index d159c27799f..9e647a89c4a 100644 --- a/documentation/wine.conf.man.in +++ b/documentation/wine.conf.man.in @@ -253,6 +253,65 @@ default: none default: Win31 .br Use Win95-like window displays or Win3.1-like window displays. +.PP +.B [Registry] +.br +.I format: AltCurrentUserFile= +.br +alternate registry file name: HKEY_CURRENT_USER +.PP +.I format: AltUserFile= +.br +alternate registry file name: HKKEY_USERS +.PP +.I format: AltLocalMachineFile= +.br +alternate registry file name: HKEY_LOCAL_MASCHINE +.PP +.I format: LoadAltRegistryFiles= +.br +Load above registries. +.PP +.I format: WritetoAltRegistryFiles= +.br +TRY to write all changes to alt registries +.PP +.I format: LoadGlobalRegistryFiles= +.br +Global registries (stored in /etc) +.PP +.I format: LoadHomeRegistryFiles= +.br +Home registries (stored in ~user/.wine/) +.PP +.I format: WritetoHomeRegistryFiles= +.br +TRY to write all changes to alt registries +.PP +.I format: LoadWin311RegistryFiles= +.br +Windows 3.1 registry files stored in windows directory +.PP +.I format: LoadWin95RegistryFiles= +.br +Windows 95 registry files stored in windows directory and c: +.PP +.I format: LoadWinNTRegistryFiles= +.br +Windows NT registry files stored in /system32/config and //user.dat. (not yet completely implemented) +.PP +.I format: NTUser= +.br +Needed for building path to the user registry file: /profiles//ntuser.dat +.PP +To avoid interferences between all registrys you should only activate the win311, win95 or the winnt registry. +.PP +booleans: Y/y/T/t/1 are true, N/n/F/f/0 are false. +.br +Defaults are read all, write to Home and Alt +.PP +Note: it is pointless to specify alt files and neither load nor write to them. +.PP .SH SAMPLE CONFIGURATION FILE A sample configuration file is distributed as .B wine.ini diff --git a/misc/registry.c b/misc/registry.c index a29bcf1e013..948f4647bac 100644 --- a/misc/registry.c +++ b/misc/registry.c @@ -732,7 +732,322 @@ static void _copy_registry( HKEY from, HKEY to ) } } } +/* NT REGISTRY LOADER */ +#include +#ifndef MAP_FAILED +#define MAP_FAILED ((LPVOID)-1) +#endif + +#define LONG_DUMP 1 + +#define REG_BLOCK_SIZE 0x1000 + +#define REG_HEADER_BLOCK_ID 0x66676572 /* regf */ +#define REG_POOL_BLOCK_ID 0x6E696268 /* hbin */ +#define REG_KEY_BLOCK_ID 0x6b6e +#define REG_VALUE_BLOCK_ID 0x6b76 +#define REG_HASH_BLOCK_ID 0x666c +#define REG_NOHASH_BLOCK_ID 0x696c +#define REG_KEY_BLOCK_TYPE 0x20 +#define 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) + */ +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; + +typedef struct +{ + WORD id; /* 0x00 0x696c */ + WORD nr_keys; + DWORD off_nk[1]; +} nt_il; + +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; + +#define vk_sz 0x0001 +#define vk_expsz 0x0002 +#define vk_bin 0x0003 +#define vk_dword 0x0004 +#define vk_multisz 0x0007 +#define vk_u2 0x0008 +#define vk_u1 0x000a + +LPSTR _strdupnA( LPCSTR str, int len ) +{ + LPSTR ret; + + if (!str) return NULL; + ret = malloc( len + 1 ); + lstrcpynA( ret, str, len ); + ret[len] = 0x00; + return ret; +} + +int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level); +int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk, int level); +int _nt_parse_lf(HKEY hkey, char * base, 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 + */ +int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk, int level) +{ + WCHAR name [256]; + BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */ + + if(vk->id != REG_VALUE_BLOCK_ID) goto error; + + lstrcpynAtoW(name, vk->name, vk->nam_len+1); + + if (RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type, + (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata, + (vk->data_len & 0x7fffffff) )) goto error; + return TRUE; +error: + ERR_(reg)("vk block invalid\n"); + 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 + */ +int _nt_parse_lf(HKEY hkey, char * base, nt_lf * lf, int level) +{ + int i; + + if (lf->id == REG_HASH_BLOCK_ID) + { + for (i=0; inr_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 == REG_NOHASH_BLOCK_ID) + { + for (i=0; inr_keys; i++) + { + if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+((nt_il*)lf)->off_nk[i]+4), level)) goto error; + } + } + return TRUE; + +error: ERR_(reg)("error reading lf block\n"); + return FALSE; +} + +int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level) +{ + char * name; + int i; + DWORD * vl; + HKEY subkey; + + if(nk->SubBlockId != REG_KEY_BLOCK_ID) goto error; + if((nk->Type!=REG_ROOT_KEY_BLOCK_TYPE) && + (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != REG_KEY_BLOCK_ID)) goto error; + + /* create the new key */ + name = _strdupnA( nk->name, nk->name_len+1); + if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; } + free(name); + + /* loop through the subkeys */ + if (nk->nr_subkeys) + { + nt_lf * lf = (nt_lf*)(base+nk->lf_off+4); + if (nk->nr_subkeys != lf->nr_keys) goto error1; + if (!_nt_parse_lf(subkey, base, lf, level+1)) goto error1; + } + + /* loop trough the value list */ + vl = (DWORD *)(base+nk->valuelist_off+4); + for (i=0; inr_values; i++) + { + nt_vk * vk = (nt_vk*)(base+vl[i]+4); + if (!_nt_parse_vk(subkey, base, vk, level+1 )) goto error1; + } + + RegCloseKey(subkey); + return TRUE; + +error1: RegCloseKey(subkey); +error: ERR_(reg)("error reading nk block\n"); + return FALSE; +} + +/* + * this function intentionally uses unix file functions to make it possible + * to move it to a seperate registry helper programm + */ +static int _nt_loadreg( HKEY hkey, char* fn ) +{ + void * base; + int len, fd; + struct stat st; + nt_regf * regf; + nt_hbin * hbin; + nt_hbin_sub * hbin_sub; + nt_nk* nk; + DOS_FULL_NAME full_name; + + if (!DOSFS_GetFullName( fn, 0, &full_name )); + + TRACE_(reg)("Loading NT registry database '%s' '%s'\n",fn, full_name.long_name); + + if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE; + if (fstat(fd, &st ) == -1) goto error1; + len = st.st_size; + if ((base=mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1; + + /* start block */ + regf = base; + if(regf->id != REG_HEADER_BLOCK_ID) /* 'regf' */ + { + ERR( "%s is not a nt-registry\n", fn); + goto error; + } + TRACE_(reg)( "%p [regf] offset=%lx size=%lx\n", regf, regf->RootKeyBlock, regf->BlockSize); + + /* hbin block */ + hbin = base + 0x1000; + if (hbin->id != REG_POOL_BLOCK_ID) + { + ERR_(reg)( "%s hbin block invalid\n", fn); + goto error; + } + TRACE_(reg)( "%p [hbin] prev=%lx next=%lx size=%lx\n", hbin, hbin->off_prev, hbin->off_next, hbin->size); + + /* hbin_sub block */ + hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub); + if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k')) + { + ERR_(reg)( "%s hbin_sub block invalid\n", fn); + goto error; + } + TRACE_(reg)( "%p [hbin sub] size=%lx\n", hbin_sub, hbin_sub->blocksize); + + /* nk block */ + nk = (nt_nk*)&(hbin_sub->data[0]); + if (nk->Type != REG_ROOT_KEY_BLOCK_TYPE) + { + ERR_(reg)( "%s special nk block not found\n", fn); + goto error; + } + + _nt_parse_nk (hkey, base+0x1000, nk, 0); + + munmap(base, len); + close(fd); + return 1; + +error: munmap(base, len); +error1: close(fd); + ERR_(reg)("error reading registry file\n"); + return 0; +} +/* end nt loader */ /* WINDOWS 95 REGISTRY LOADER */ /* @@ -1302,15 +1617,55 @@ void SHELL_LoadRegistry( void ) req->version = 1; server_call( REQ_SET_REGISTRY_LEVELS ); - if (PROFILE_GetWineIniBool ("registry", "LoadWindowsRegistryFiles", 1)) + if (PROFILE_GetWineIniBool ("registry", "LoadWin311RegistryFiles", 1)) { /* Load windows 3.1 entries */ _w31_loadreg(); + } + if (PROFILE_GetWineIniBool ("registry", "LoadWin95RegistryFiles", 1)) + { /* Load windows 95 entries */ _w95_loadreg("C:\\system.1st", HKEY_LOCAL_MACHINE); _w95_loadreg("system.dat", HKEY_LOCAL_MACHINE); _w95_loadreg("user.dat", HKEY_USERS); } + if (PROFILE_GetWineIniBool ("registry", "LoadWinNTRegistryFiles", 1)) + { + fn = xmalloc( MAX_PATHNAME_LEN ); + home = xmalloc ( MAX_PATHNAME_LEN ); + if ( PROFILE_GetWineIniString( "registry", "NTUser", "", home, MAX_PATHNAME_LEN - 1)) + { + GetWindowsDirectoryA( fn, MAX_PATHNAME_LEN ); + strncat(fn, "\\Profiles\\", MAX_PATHNAME_LEN - strlen(fn) - 1); + strncat(fn, home, MAX_PATHNAME_LEN - strlen(fn) - 1); + strncat(fn, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(fn) - 1); + _nt_loadreg( HKEY_USERS, fn ); + } + /* + * FIXME + * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet + */ + GetSystemDirectoryA( fn, MAX_PATHNAME_LEN ); + + strcpy(home, fn); + strncat(home, "\\config\\system", MAX_PATHNAME_LEN - strlen(home) - 1); + _nt_loadreg(HKEY_LOCAL_MACHINE, home); + + strcpy(home, fn); + strncat(home, "\\config\\software", MAX_PATHNAME_LEN - strlen(home) - 1); + _nt_loadreg(HKEY_LOCAL_MACHINE, home); + + strcpy(home, fn); + strncat(home, "\\config\\sam", MAX_PATHNAME_LEN - strlen(home) - 1); + _nt_loadreg(HKEY_LOCAL_MACHINE, home); + + strcpy(home, fn); + strncat(home, "\\config\\security", MAX_PATHNAME_LEN - strlen(home) - 1); + _nt_loadreg(HKEY_LOCAL_MACHINE, home); + + free (home); + free (fn); + } if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1)) { diff --git a/wine.ini b/wine.ini index 1296ed5f068..efad4d80542 100644 --- a/wine.ini +++ b/wine.ini @@ -144,8 +144,6 @@ AltLocalMachineFile= LoadGlobalRegistryFiles=Y ; Home registries (stored in ~user/.wine/) LoadHomeRegistryFiles=Y -; Windows registries in windows path, above -LoadWindowsRegistryFiles=Y ; Load above registries. LoadAltRegistryFiles=Y ; TRY to write all changes to home registries @@ -157,6 +155,14 @@ UseNewFormat=N ; Registry periodic save timeout in seconds ; PeriodicSave=600 +; Windows registries in windows path +LoadWin311RegistryFiles=Y +LoadWin95RegistryFiles=Y +LoadWinNTRegistryFiles=N +;user the private registry is taken from +;(profiles//ntuser.dat) +;NTUser=username + [Tweak.Layout] ;; WineLook=xxx (supported styles are 'Win31'(default), 'Win95', 'Win98') ;WineLook=Win95