| /* |
| * Registry Functions |
| * |
| * Copyright 1996 Marcus Meissner |
| * Copyright 1998 Matthew Becker |
| * Copyright 1999 Sylvain St-Germain |
| * |
| * December 21, 1997 - Kevin Cozens |
| * Fixed bugs in the _w95_loadreg() function. Added extra information |
| * regarding the format of the Windows '95 registry files. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * NOTES |
| * When changing this file, please re-run the regtest program to ensure |
| * the conditions are handled properly. |
| * |
| * TODO |
| * Security access |
| * Option handling |
| * Time for RegEnumKey*, RegQueryInfoKey* |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #ifdef HAVE_UNISTD_H |
| # include <unistd.h> |
| #endif |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #ifdef HAVE_SYS_MMAN_H |
| # include <sys/mman.h> |
| #endif |
| #ifdef HAVE_SYS_IOCTL_H |
| #include <sys/ioctl.h> |
| #endif |
| #ifdef HAVE_LINUX_HDREG_H |
| # include <linux/hdreg.h> |
| #endif |
| |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| #include "ntstatus.h" |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winerror.h" |
| #include "winioctl.h" |
| #include "ntddscsi.h" |
| |
| #include "wine/winbase16.h" |
| #include "wine/library.h" |
| #include "wine/server.h" |
| #include "wine/unicode.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(reg); |
| |
| #define SAVE_GLOBAL_REGBRANCH_USER_DEFAULT "/wine.userreg" |
| #define SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE "/wine.systemreg" |
| |
| #define MAX_PATHNAME_LEN 1024 |
| |
| static const WCHAR ClassesRootW[] = {'M','a','c','h','i','n','e','\\', |
| 'S','o','f','t','w','a','r','e','\\', |
| 'C','l','a','s','s','e','s',0}; |
| |
| #define IS_OPTION_FALSE(ch) \ |
| ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0') |
| |
| /* _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; |
| } |
| |
| /* _xstrdup [Internal] */ |
| static LPSTR _xstrdup(LPCSTR str) |
| { |
| LPSTR ret; |
| size_t len = strlen(str) + 1; |
| |
| if (!str) return NULL; |
| ret = _xmalloc( len ); |
| memcpy( ret, str, len ); |
| return ret; |
| } |
| |
| /* convert ansi string to unicode [Internal] */ |
| static LPWSTR _strdupnAtoW(LPCSTR strA,size_t lenA) |
| { |
| LPWSTR ret; |
| DWORD len; |
| |
| if (!strA) return NULL; |
| if (RtlMultiByteToUnicodeSize( &len, strA, lenA ) != STATUS_SUCCESS) return NULL; |
| |
| ret = _xmalloc(len+sizeof(WCHAR)); |
| RtlMultiByteToUnicodeN(ret, len, NULL, strA, lenA); |
| ret[len / sizeof(WCHAR)] = 0; |
| return ret; |
| } |
| |
| /* 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,const char escape[2]) |
| { |
| static const char escapes[32] = ".......abtnvfr.............e...."; |
| char buffer[256]; |
| LPSTR pos = buffer; |
| int count = 0; |
| |
| for (; len; str++, len--) |
| { |
| if (pos > buffer + sizeof(buffer) - 8) |
| { |
| fwrite( buffer, pos - buffer, 1, f ); |
| count += pos - buffer; |
| pos = buffer; |
| } |
| 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; |
| } |
| 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; |
| } |
| fwrite( buffer, pos - buffer, 1, f ); |
| count += pos - buffer; |
| return count; |
| } |
| |
| /* convert ansi string to unicode and dump with proper escaping [Internal] */ |
| static int _dump_strAtoW(LPCSTR strA,size_t len,FILE *f,const char escape[2]) |
| { |
| 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 */ |
| }; |
| |
| /* dump a value to a text file */ |
| /* FIXME: this code duplicates server/registry.c */ |
| static void _dump_value(struct key_value *value,FILE *f) |
| { |
| int i, count; |
| |
| 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; |
| } |
| fputc( '\n', f ); |
| } |
| |
| /******************************************************************/ |
| /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */ |
| /* |
| reghack - windows 3.11 registry data format demo program. |
| |
| 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. |
| |
| The header is obvious from the struct header. The taboff1 and taboff2 |
| fields are always 0x20, and their usage is unknown. |
| |
| The 8-byte entry table has various entry types. |
| |
| 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. |
| |
| 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. |
| |
| 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. |
| |
| Tor Sjøwall, tor@sn.no |
| */ |
| |
| /* 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 */ |
| }; |
| |
| /* generic format of table entries */ |
| struct _w31_tabent { |
| unsigned short w0, w1, w2, w3; |
| }; |
| |
| /* 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 */ |
| }; |
| |
| /* 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 */ |
| }; |
| |
| /* 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 */ |
| }; |
| |
| /* recursive helper function to display a directory tree [Internal] */ |
| static void _w31_dumptree(unsigned short idx, unsigned char *txt, |
| struct _w31_tabent *tab, struct _w31_header *head, |
| HKEY hkey, ULONG lastmodified, int level) |
| { |
| static const WCHAR classesW[] = {'.','c','l','a','s','s','e','s',0}; |
| struct _w31_dirent *dir; |
| struct _w31_keyent *key; |
| struct _w31_valent *val; |
| HKEY subkey = 0; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW, valueW; |
| static WCHAR tail[400]; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = hkey; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| RtlInitUnicodeString( &valueW, NULL ); |
| |
| while (idx!=0) { |
| dir=(struct _w31_dirent*)&tab[idx]; |
| |
| if (dir->key_idx) { |
| DWORD len; |
| key = (struct _w31_keyent*)&tab[dir->key_idx]; |
| |
| RtlMultiByteToUnicodeN( tail, sizeof(tail)-sizeof(WCHAR), &len, |
| &txt[key->string_off], key->length); |
| tail[len/sizeof(WCHAR)] = 0; |
| |
| /* all toplevel entries AND the entries in the |
| * toplevel subdirectory belong to \SOFTWARE\Classes |
| */ |
| if (!level && !strcmpW(tail,classesW)) |
| { |
| _w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1); |
| idx=dir->sibling_idx; |
| continue; |
| } |
| |
| if (subkey) NtClose( subkey ); |
| RtlInitUnicodeString( &nameW, tail ); |
| if (NtCreateKey( &subkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) subkey = 0; |
| |
| /* only add if leaf node or valued node */ |
| if (dir->value_idx!=0||dir->child_idx==0) { |
| if (dir->value_idx) { |
| DWORD len; |
| val=(struct _w31_valent*)&tab[dir->value_idx]; |
| RtlMultiByteToUnicodeN( tail, sizeof(tail) - sizeof(WCHAR), &len, |
| &txt[val->string_off], val->length); |
| tail[len/sizeof(WCHAR)] = 0; |
| NtSetValueKey( subkey, &valueW, 0, REG_SZ, tail, len + sizeof(WCHAR) ); |
| } |
| } |
| } 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) NtClose( subkey ); |
| } |
| |
| |
| /****************************************************************************** |
| * _w31_loadreg [Internal] |
| */ |
| static void _w31_loadreg( const WCHAR *path ) |
| { |
| HANDLE hf; |
| HKEY root; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| struct _w31_header head; |
| struct _w31_tabent* tab = NULL; |
| unsigned char* txt = NULL; |
| unsigned int len; |
| ULONG lastmodified; |
| NTSTATUS status; |
| IO_STATUS_BLOCK iosb; |
| FILE_POSITION_INFORMATION fpi; |
| FILE_BASIC_INFORMATION fbi; |
| |
| TRACE("(void)\n"); |
| |
| hf = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); |
| if (hf==INVALID_HANDLE_VALUE) return; |
| |
| /* read & dump header */ |
| if (NtReadFile(hf, 0, NULL, NULL, &iosb, |
| &head, sizeof(head), NULL, NULL) != STATUS_SUCCESS || |
| iosb.Information != sizeof(head)) |
| { |
| ERR("reg.dat is too short.\n"); |
| goto done; |
| } |
| if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie)) != 0) |
| { |
| ERR("reg.dat has bad signature.\n"); |
| goto done; |
| } |
| |
| len = head.tabcnt * sizeof(struct _w31_tabent); |
| /* read and dump index table */ |
| tab = _xmalloc(len); |
| if (NtReadFile(hf, 0, NULL, NULL, &iosb, |
| tab, len, NULL, NULL) != STATUS_SUCCESS || |
| iosb.Information != len) |
| { |
| ERR("couldn't read index table (%d bytes).\n",len); |
| goto done; |
| } |
| |
| /* read text */ |
| txt = _xmalloc(head.textsize); |
| fpi.CurrentByteOffset.u.LowPart = head.textoff; |
| fpi.CurrentByteOffset.u.HighPart = 0; |
| if (NtSetInformationFile(hf, &iosb, &fpi, sizeof(fpi), |
| FilePositionInformation) != STATUS_SUCCESS) |
| { |
| ERR("couldn't seek to textblock.\n"); |
| goto done; |
| } |
| status = NtReadFile(hf, 0, NULL, NULL, &iosb, txt, head.textsize, NULL, NULL); |
| if (!(status == STATUS_SUCCESS || status == STATUS_END_OF_FILE) || |
| iosb.Information != head.textsize) |
| { |
| ERR("textblock too short (%d instead of %ld).\n", len, head.textsize); |
| goto done; |
| } |
| if (NtQueryInformationFile(hf, &iosb, &fbi, sizeof(fbi), |
| FileBasicInformation) != STATUS_SUCCESS) |
| { |
| ERR("Couldn't get basic information.\n"); |
| goto done; |
| } |
| RtlTimeToSecondsSince1970(&fbi.LastWriteTime, &lastmodified); |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| RtlInitUnicodeString( &nameW, ClassesRootW ); |
| |
| if (!NtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) |
| { |
| _w31_dumptree(tab[0].w1,txt,tab,&head,root,lastmodified,0); |
| NtClose( root ); |
| } |
| done: |
| if (tab) free(tab); |
| if (txt) free(txt); |
| NtClose(hf); |
| return; |
| } |
| |
| /***********************************************************************************/ |
| /* windows 95 registry loader */ |
| /***********************************************************************************/ |
| |
| /* SECTION 1: main header |
| * |
| * once at offset 0 |
| */ |
| #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 */ |
| } _w95creg; |
| |
| /* SECTION 2: Directory information (tree structure) |
| * |
| * once on offset 0x20 |
| * |
| * structure: [rgkn][dke]* (repeat till last_dke is reached) |
| */ |
| #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]; |
| } _w95rgkn; |
| |
| /* Disk Key Entry Structure |
| * |
| * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the |
| * hive itself. It looks the same like other keys. Even the ID-number can |
| * be any value. |
| * |
| * The "hash"-value is a value representing the key's name. Windows will not |
| * search for the name, but for a matching hash-value. if it finds one, it |
| * will compare the actual string info, otherwise continue with the next key. |
| * To calculate the hash initialize a D-Word with 0 and add all ASCII-values |
| * of the string which are smaller than 0x80 (128) to this D-Word. |
| * |
| * If you want to modify key names, also modify the hash-values, since they |
| * cannot be found again (although they would be displayed in REGEDIT) |
| * End of list-pointers are filled with 0xFFFFFFFF |
| * |
| * Disk keys are layed out flat ... But, sometimes, nrLS and nrMS are both |
| * 0xFFFF, which means skipping over nextkeyoffset bytes (including this |
| * structure) and reading another RGDB_section. |
| * |
| * The last DKE (see field last_dke in _w95_rgkn) has only 3 DWORDs with |
| * 0x80000000 (EOL indicator ?) as x1, the hash value and 0xFFFFFFFF as x3. |
| * The remaining space between last_dke and the offset calculated from |
| * rgkn->size seems to be free for use for more dke:s. |
| * So it seems if more dke:s are added, they are added to that space and |
| * last_dke is grown, and in case that "free" space is out, the space |
| * gets grown and rgkn->size gets adjusted. |
| * |
| * 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 */ |
| } _w95dke; |
| |
| /* SECTION 3: key information, values and data |
| * |
| * structure: |
| * section: [blocks]* (repeat creg->rgdb_num times) |
| * blocks: [rgdb] [subblocks]* (repeat till block size reached ) |
| * subblocks: [dkh] [dkv]* (repeat dkh->values times ) |
| * |
| * An interesting relationship exists in RGDB_section. The DWORD value |
| * at offset 0x10 equals the one at offset 0x04 minus the one at offset 0x08. |
| * I have no idea at the moment what this means. (Kevin Cozens) |
| */ |
| |
| /* 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 */ |
| } _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 */ |
| } _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 */ |
| } _w95dkv; |
| |
| /****************************************************************************** |
| * _w95_lookup_dkh [Internal] |
| * |
| * seeks the dkh belonging to a dke |
| */ |
| 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 */ |
| } |
| |
| 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; |
| } |
| |
| /****************************************************************************** |
| * _w95_dump_dkv [Internal] |
| */ |
| static int _w95_dump_dkv(_w95dkh *dkh,int nrLS,int nrMS,FILE *f) |
| { |
| _w95dkv * dkv; |
| int i; |
| |
| /* first value block */ |
| dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14); |
| |
| /* loop through 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_dump_dke [Internal] |
| */ |
| static int _w95_dump_dke(LPCSTR key_name,_w95creg *creg,_w95rgkn *rgkn,_w95dke *dke,FILE *f,int level) |
| { |
| _w95dkh * dkh; |
| LPSTR new_key_name = NULL; |
| |
| /* 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; |
| } |
| |
| /* 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 */ |
| size_t len = strlen(key_name); |
| new_key_name = _xmalloc(len+dkh->keynamelen+2); |
| memcpy( new_key_name, key_name, len ); |
| if (len) new_key_name[len++] = '\\'; |
| memcpy( new_key_name + len, dkh->name, dkh->keynamelen ); |
| new_key_name[len + dkh->keynamelen] = 0; |
| |
| /* 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 = _xstrdup(key_name); |
| |
| /* 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 */ |
| |
| /***********************************************************************************/ |
| /* 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) |
| */ |
| 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; |
| |
| /* |
| list of subkeys without hash |
| |
| li --+-->nk |
| | |
| +-->nk |
| */ |
| typedef struct { |
| WORD id; /* 0x00 0x696c */ |
| WORD nr_keys; |
| DWORD off_nk[1]; |
| } nt_li; |
| |
| /* |
| this is a intermediate node |
| |
| 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; |
| |
| 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; |
| |
| /* |
| * 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; |
| |
| if (vk->id != NT_REG_VALUE_BLOCK_ID) { |
| ERR("unknown block found (0x%04x), please report!\n", vk->id); |
| return FALSE; |
| } |
| |
| 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(LPCSTR 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(LPCSTR 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; |
| } |
| |
| /* 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_dump_lf(key_name, base, li->nr_keys, (nt_lf*)li, f, level)) goto error; |
| } |
| } else goto error2; |
| |
| return TRUE; |
| |
| error2: |
| if (lf->id == 0x686c) |
| FIXME("unknown Win XP node id 0x686c: do we need to add support for it ?\n"); |
| else |
| 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; |
| } |
| |
| /* _nt_dump_nk [Internal] */ |
| static int _nt_dump_nk(LPCSTR key_name,char *base,nt_nk *nk,FILE *f,int level) |
| { |
| unsigned int n; |
| DWORD *vl; |
| LPSTR new_key_name = NULL; |
| |
| TRACE("%s\n", key_name); |
| |
| if (nk->SubBlockId != NT_REG_KEY_BLOCK_ID) { |
| ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId); |
| return FALSE; |
| } |
| |
| 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; |
| } |
| |
| /* create the new key */ |
| if (level <= 0) { |
| /* create new subkey name */ |
| size_t len = strlen(key_name); |
| new_key_name = _xmalloc( len+nk->name_len+2 ); |
| memcpy( new_key_name, key_name, len ); |
| if (len) new_key_name[len++] = '\\'; |
| memcpy( new_key_name + len, nk->name, nk->name_len ); |
| new_key_name[len + nk->name_len] = 0; |
| |
| /* 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"); |
| } |
| |
| /* 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 = _xstrdup(key_name); |
| |
| /* 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; |
| } |
| } |
| |
| free(new_key_name); |
| return TRUE; |
| } |
| |
| /* end nt loader */ |
| |
| /****************************************************************************** |
| * _allocate_default_keys [Internal] |
| * Registry initialisation, allocates some default keys. |
| */ |
| static void _allocate_default_keys(void) |
| { |
| static const WCHAR StatDataW[] = {'D','y','n','D','a','t','a','\\', |
| 'P','e','r','f','S','t','a','t','s','\\', |
| 'S','t','a','t','D','a','t','a',0}; |
| HKEY hkey; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| |
| TRACE("(void)\n"); |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| RtlInitUnicodeString( &nameW, StatDataW ); |
| if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) NtClose( hkey ); |
| } |
| |
| static void get_windows_dir(WCHAR* buffer, unsigned len) |
| { |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW, keyW; |
| HKEY hkey; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| if (RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\Software\\Wine\\Wine\\Config\\wine" )) |
| { |
| if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) |
| { |
| char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)]; |
| DWORD count; |
| |
| RtlCreateUnicodeStringFromAsciiz( &keyW, "Windows"); |
| if (!NtQueryValueKey( hkey, &keyW, KeyValuePartialInformation, |
| tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| memcpy(buffer, str, min(((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->DataLength, len)); |
| } |
| RtlFreeUnicodeString( &keyW ); |
| } |
| RtlFreeUnicodeString( &nameW ); |
| } |
| } |
| |
| |
| #define REG_DONTLOAD -1 |
| #define REG_WIN31 0 |
| #define REG_WIN95 1 |
| #define REG_WINNT 2 |
| |
| /* return the type of native registry [Internal] */ |
| static int _get_reg_type(const WCHAR* windir) |
| { |
| WCHAR tmp[MAX_PATHNAME_LEN]; |
| int ret = REG_WIN31; |
| static const WCHAR nt_reg_pathW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','s','y','s','t','e','m',0}; |
| static const WCHAR win9x_reg_pathW[] = {'\\','s','y','s','t','e','m','.','d','a','t',0}; |
| |
| /* test %windir%/system32/config/system --> winnt */ |
| strcpyW(tmp, windir); |
| strcatW(tmp, nt_reg_pathW); |
| if(GetFileAttributesW(tmp) != INVALID_FILE_ATTRIBUTES) |
| ret = REG_WINNT; |
| else |
| { |
| /* test %windir%/system.dat --> win95 */ |
| strcpyW(tmp, windir); |
| strcatW(tmp, win9x_reg_pathW); |
| if(GetFileAttributesW(tmp) != INVALID_FILE_ATTRIBUTES) |
| ret = REG_WIN95; |
| } |
| |
| return ret; |
| } |
| |
| /* load the registry file in wine format [Internal] */ |
| static void load_wine_registry(HKEY hkey,LPCSTR fn) |
| { |
| WCHAR *buffer; |
| HANDLE file; |
| DWORD len; |
| UNICODE_STRING name; |
| OBJECT_ATTRIBUTES attr; |
| IO_STATUS_BLOCK io; |
| |
| len = MultiByteToWideChar( CP_UNIXCP, 0, fn, -1, NULL, 0 ); |
| if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return; |
| MultiByteToWideChar( CP_UNIXCP, 0, fn, -1, buffer, len ); |
| RtlInitUnicodeString( &name, buffer ); |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.Attributes = 0; |
| attr.ObjectName = &name; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| if (!NtOpenFile( &file, GENERIC_READ, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, |
| FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT )) |
| { |
| SERVER_START_REQ( load_registry ) |
| { |
| req->hkey = hkey; |
| req->file = file; |
| wine_server_call( req ); |
| } |
| SERVER_END_REQ; |
| CloseHandle( file ); |
| } |
| HeapFree( GetProcessHeap(), 0, buffer ); |
| } |
| |
| /* 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 ((*f = fdopen(tmp_fd,"w")) == NULL) { |
| ERR("Unexpected error while fdopen() call: %s\n",strerror(errno)); |
| close(tmp_fd); |
| free(ret); |
| return NULL; |
| } |
| |
| return ret; |
| } |
| |
| /* convert win95 native registry file to wine format [Internal] */ |
| static LPSTR _convert_win95_registry_to_wine_format(LPCWSTR fn, int level) |
| { |
| HANDLE hFile, hMapping; |
| FILE *f; |
| void *base; |
| LPSTR ret = NULL; |
| OBJECT_ATTRIBUTES attr; |
| LARGE_INTEGER lg_int; |
| NTSTATUS nts; |
| SIZE_T len; |
| |
| _w95creg *creg; |
| _w95rgkn *rgkn; |
| _w95dke *dke, *root_dke; |
| |
| hFile = CreateFileW( fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); |
| if ( hFile == INVALID_HANDLE_VALUE ) return NULL; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = NULL; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| lg_int.QuadPart = 0; |
| nts = NtCreateSection( &hMapping, |
| STANDARD_RIGHTS_REQUIRED|SECTION_QUERY|SECTION_MAP_READ, |
| &attr, &lg_int, PAGE_READONLY, SEC_COMMIT, hFile ); |
| if (nts != STATUS_SUCCESS) goto error1; |
| |
| base = NULL; len = 0; |
| nts = NtMapViewOfSection( hMapping, GetCurrentProcess(), |
| &base, 0, 0, &lg_int, &len, ViewShare, 0, |
| PAGE_READONLY); |
| NtClose( hMapping ); |
| if (nts != STATUS_SUCCESS) goto error1; |
| |
| /* control signature */ |
| if (*(LPDWORD)base != W95_REG_CREG_ID) { |
| ERR("unable to load native win95 registry file %s: unknown signature.\n", |
| debugstr_w(fn)); |
| goto error; |
| } |
| |
| 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", debugstr_w(fn)); |
| ERR("Please report this.\n"); |
| ERR("Make a backup of the file, run a good reg cleaner program and try again!\n"); |
| } |
| |
| NtUnmapViewOfSection( GetCurrentProcess(), base ); |
| error1: |
| NtClose(hFile); |
| return ret; |
| } |
| |
| /* convert winnt native registry file to wine format [Internal] */ |
| static LPSTR _convert_winnt_registry_to_wine_format(LPCWSTR fn, int level) |
| { |
| FILE *f; |
| void *base; |
| LPSTR ret = NULL; |
| HANDLE hFile; |
| HANDLE hMapping; |
| OBJECT_ATTRIBUTES attr; |
| LARGE_INTEGER lg_int; |
| NTSTATUS nts; |
| SIZE_T len; |
| |
| nt_regf *regf; |
| nt_hbin *hbin; |
| nt_hbin_sub *hbin_sub; |
| nt_nk *nk; |
| |
| TRACE("%s\n", debugstr_w(fn)); |
| |
| hFile = CreateFileW( fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); |
| if ( hFile == INVALID_HANDLE_VALUE ) return NULL; |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = NULL; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| lg_int.QuadPart = 0; |
| nts = NtCreateSection( &hMapping, |
| STANDARD_RIGHTS_REQUIRED|SECTION_QUERY|SECTION_MAP_READ, |
| &attr, &lg_int, PAGE_READONLY, SEC_COMMIT, hFile ); |
| if (nts != STATUS_SUCCESS) goto error1; |
| |
| base = NULL; len = 0; |
| nts = NtMapViewOfSection( hMapping, GetCurrentProcess(), |
| &base, 0, 0, &lg_int, &len, ViewShare, 0, |
| PAGE_READONLY); |
| NtClose( hMapping ); |
| if (nts != STATUS_SUCCESS) goto error1; |
| |
| /* control signature */ |
| if (*(LPDWORD)base != NT_REG_HEADER_BLOCK_ID) { |
| ERR("unable to load native winnt registry file %s: unknown signature.\n", |
| debugstr_w(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: |
| NtUnmapViewOfSection( GetCurrentProcess(), base ); |
| error1: |
| NtClose(hFile); |
| return ret; |
| } |
| |
| /* convert native registry to wine format and load it via server call [Internal] */ |
| static void _convert_and_load_native_registry(LPCWSTR 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 successfully converted to %s and loaded to registry.\n", |
| debugstr_w(fn), tmp); |
| unlink(tmp); |
| } |
| else WARN("Unable to convert %s (doesn't exist?)\n", debugstr_w(fn)); |
| free(tmp); |
| } |
| |
| /* load all native windows registry files [Internal] */ |
| static void _load_windows_registry( HKEY hkey_local_machine, HKEY hkey_current_user, |
| HKEY hkey_users_default ) |
| { |
| int reg_type; |
| WCHAR windir[MAX_PATHNAME_LEN]; |
| WCHAR path[MAX_PATHNAME_LEN]; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| HKEY hkey, profile_key; |
| char tmp[1024]; |
| DWORD dummy; |
| |
| static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\', |
| 'S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\','W','i','n','e','\\', |
| 'C','o','n','f','i','g','\\','W','i','n','e',0}; |
| static const WCHAR ProfileW[] = {'P','r','o','f','i','l','e',0}; |
| static const WCHAR System[] = {'M','a','c','h','i','n','e','\\','S','y','s','t','e','m',0}; |
| static const WCHAR Software[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e',0}; |
| static const WCHAR Clone[] = {'M','a','c','h','i','n','e','\\', |
| 'S','y','s','t','e','m','\\', |
| 'C','l','o','n','e',0}; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| RtlInitUnicodeString( &nameW, WineW ); |
| if (NtCreateKey( &profile_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) profile_key = 0; |
| |
| get_windows_dir(windir, sizeof(windir)); |
| |
| reg_type = _get_reg_type(windir); |
| switch (reg_type) { |
| case REG_WINNT: { |
| static const WCHAR ntuser_datW[] = {'\\','n','t','u','s','e','r','.','d','a','t',0}; |
| static const WCHAR defaultW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','d','e','f','a','u','l','t',0}; |
| static const WCHAR systemW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','s','y','s','t','e','m',0}; |
| static const WCHAR softwareW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','s','o','f','t','w','a','r','e',0}; |
| static const WCHAR samW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','s','a','m',0}; |
| static const WCHAR securityW[] = {'\\','s','y','s','t','e','m','3','2','\\','c','o','n','f','i','g','\\','s','e','c','u','r','i','t','y',0}; |
| |
| /* user specific ntuser.dat */ |
| RtlInitUnicodeString( &nameW, ProfileW ); |
| if (profile_key && !NtQueryValueKey( profile_key, &nameW, KeyValuePartialInformation, |
| tmp, sizeof(tmp), &dummy )) |
| { |
| strcpyW(path, (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data); |
| strcatW(path, ntuser_datW); |
| _convert_and_load_native_registry(path,hkey_current_user,REG_WINNT,1); |
| } |
| else |
| { |
| 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"); |
| break; |
| } |
| |
| /* default user.dat */ |
| if (hkey_users_default) { |
| strcpyW(path, windir); |
| strcatW(path, defaultW); |
| _convert_and_load_native_registry(path,hkey_users_default,REG_WINNT,1); |
| } |
| |
| /* |
| * FIXME |
| * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet |
| */ |
| RtlInitUnicodeString( &nameW, System ); |
| if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) |
| { |
| strcpyW(path, windir); |
| strcatW(path, systemW); |
| _convert_and_load_native_registry(path,hkey,REG_WINNT,1); |
| NtClose( hkey ); |
| } |
| RtlInitUnicodeString( &nameW, Software ); |
| if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) |
| { |
| strcpyW(path, windir); |
| strcatW(path, softwareW); |
| _convert_and_load_native_registry(path,hkey,REG_WINNT,1); |
| NtClose( hkey ); |
| } |
| |
| strcpyW(path, windir); |
| strcatW(path, samW); |
| _convert_and_load_native_registry(path,hkey_local_machine,REG_WINNT,0); |
| |
| strcpyW(path,windir); |
| strcatW(path, securityW); |
| _convert_and_load_native_registry(path,hkey_local_machine,REG_WINNT,0); |
| |
| /* this key is generated when the nt-core booted successfully */ |
| RtlInitUnicodeString( &nameW, Clone ); |
| if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) NtClose( hkey ); |
| break; |
| } |
| |
| case REG_WIN95: |
| { |
| static const WCHAR system_1stW[] = {'c',':','\\','s','y','s','t','e','m','.','1','s','t',0}; |
| static const WCHAR system_datW[] = {'\\','s','y','s','t','e','m','.','d','a','t',0}; |
| static const WCHAR classes_datW[] = {'\\','c','l','a','s','s','e','s','.','d','a','t',0}; |
| static const WCHAR user_datW[] = {'\\','u','s','e','r','.','d','a','t',0}; |
| |
| _convert_and_load_native_registry(system_1stW,hkey_local_machine,REG_WIN95,0); |
| |
| strcpyW(path, windir); |
| strcatW(path, system_datW); |
| _convert_and_load_native_registry(path,hkey_local_machine,REG_WIN95,0); |
| |
| RtlInitUnicodeString( &nameW, ClassesRootW ); |
| if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) |
| { |
| strcpyW(path, windir); |
| strcatW(path, classes_datW); |
| _convert_and_load_native_registry(path,hkey,REG_WIN95,0); |
| NtClose( hkey ); |
| } |
| |
| RtlInitUnicodeString( &nameW, ProfileW ); |
| if (profile_key && !NtQueryValueKey( profile_key, &nameW, KeyValuePartialInformation, |
| tmp, sizeof(tmp), &dummy )) |
| { |
| /* user specific user.dat */ |
| strcpyW(path, (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data); |
| strcatW(path, user_datW); |
| _convert_and_load_native_registry(path,hkey_current_user,REG_WIN95,1); |
| |
| /* default user.dat */ |
| if (hkey_users_default) { |
| strcpyW(path, windir); |
| strcatW(path, user_datW); |
| _convert_and_load_native_registry(path,hkey_users_default,REG_WIN95,1); |
| } |
| } else { |
| strcpyW(path, windir); |
| strcatW(path, user_datW); |
| _convert_and_load_native_registry(path,hkey_current_user,REG_WIN95,1); |
| } |
| break; |
| } |
| |
| case REG_WIN31: |
| { |
| static const WCHAR reg_datW[] = {'\\','r','e','g','.','d','a','t',0}; |
| /* FIXME: here we should convert to *.reg file supported by server and call REQ_LOAD_REGISTRY, see REG_WIN95 case */ |
| strcpyW(path, windir); |
| strcatW(path, reg_datW); |
| _w31_loadreg( path ); |
| break; |
| } |
| |
| case REG_DONTLOAD: |
| TRACE("REG_DONTLOAD\n"); |
| break; |
| |
| default: |
| ERR("switch: no match (%d)\n",reg_type); |
| break; |
| |
| } |
| if (profile_key) NtClose( profile_key ); |
| } |
| |
| |
| |
| /****************************************************************** |
| * init_cdrom_registry |
| * |
| * Initializes registry to contain scsi info about the cdrom in NT. |
| * All devices (even not real scsi ones) have this info in NT. |
| * TODO: for now it only works for non scsi devices |
| * NOTE: programs usually read these registry entries after sending the |
| * IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom |
| */ |
| static void init_cdrom_registry( HANDLE handle ) |
| { |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| WCHAR dataW[50]; |
| DWORD lenW; |
| char buffer[40]; |
| DWORD value; |
| const char *data; |
| HKEY scsiKey; |
| HKEY portKey; |
| HKEY busKey; |
| HKEY targetKey; |
| DWORD disp; |
| IO_STATUS_BLOCK io; |
| SCSI_ADDRESS scsi_addr; |
| |
| if (NtDeviceIoControlFile( handle, 0, NULL, NULL, &io, IOCTL_SCSI_GET_ADDRESS, |
| NULL, 0, &scsi_addr, sizeof(scsi_addr) )) |
| return; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| /* Ensure there is Scsi key */ |
| if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\HARDWARE\\DEVICEMAP\\Scsi" ) || |
| NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0, |
| NULL, REG_OPTION_VOLATILE, &disp )) |
| { |
| ERR("Cannot create DEVICEMAP\\Scsi registry key\n" ); |
| return; |
| } |
| RtlFreeUnicodeString( &nameW ); |
| |
| snprintf(buffer,sizeof(buffer),"Scsi Port %d",scsi_addr.PortNumber); |
| attr.RootDirectory = scsiKey; |
| if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) || |
| NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0, |
| NULL, REG_OPTION_VOLATILE, &disp )) |
| { |
| ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" ); |
| return; |
| } |
| RtlFreeUnicodeString( &nameW ); |
| |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" ); |
| data = "atapi"; |
| RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data)); |
| NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW ); |
| RtlFreeUnicodeString( &nameW ); |
| value = 10; |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" ); |
| NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD)); |
| RtlFreeUnicodeString( &nameW ); |
| value = 0; |
| #ifdef HDIO_GET_DMA |
| { |
| int fd, dma; |
| if (!wine_server_handle_to_fd( handle, 0, &fd, NULL, NULL )) |
| { |
| if (ioctl(fd,HDIO_GET_DMA, &dma) != -1) value = dma; |
| wine_server_release_fd( handle, fd ); |
| } |
| } |
| #endif |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" ); |
| NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD)); |
| RtlFreeUnicodeString( &nameW ); |
| |
| snprintf(buffer,40,"Scsi Bus %d", scsi_addr.PathId); |
| attr.RootDirectory = portKey; |
| if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) || |
| NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0, |
| NULL, REG_OPTION_VOLATILE, &disp )) |
| { |
| ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" ); |
| return; |
| } |
| RtlFreeUnicodeString( &nameW ); |
| |
| attr.RootDirectory = busKey; |
| if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) || |
| NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0, |
| NULL, REG_OPTION_VOLATILE, &disp )) |
| { |
| ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" ); |
| return; |
| } |
| RtlFreeUnicodeString( &nameW ); |
| NtClose( targetKey ); |
| |
| snprintf(buffer,40,"Target Id %d", scsi_addr.TargetId); |
| attr.RootDirectory = busKey; |
| if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) || |
| NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0, |
| NULL, REG_OPTION_VOLATILE, &disp )) |
| { |
| ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" ); |
| return; |
| } |
| RtlFreeUnicodeString( &nameW ); |
| |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" ); |
| data = "CdRomPeripheral"; |
| RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data)); |
| NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW ); |
| RtlFreeUnicodeString( &nameW ); |
| /* FIXME - maybe read the real identifier?? */ |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" ); |
| data = "Wine CDROM"; |
| RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data)); |
| NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW ); |
| RtlFreeUnicodeString( &nameW ); |
| /* FIXME - we always use Cdrom0 - do not know about the nt behaviour */ |
| RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" ); |
| data = "Cdrom0"; |
| RtlMultiByteToUnicodeN( dataW, 50, &lenW, data, strlen(data)); |
| NtSetValueKey( targetKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW ); |
| RtlFreeUnicodeString( &nameW ); |
| |
| NtClose( targetKey ); |
| NtClose( busKey ); |
| NtClose( portKey ); |
| NtClose( scsiKey ); |
| } |
| |
| |
| /* create the hardware registry branch */ |
| static void create_hardware_branch(void) |
| { |
| int i; |
| HANDLE handle; |
| char drive[] = "\\\\.\\A:"; |
| |
| /* create entries for cdroms */ |
| for (i = 0; i < 26; i++) |
| { |
| drive[4] = 'A' + i; |
| handle = CreateFileA( drive, 0, 0, NULL, OPEN_EXISTING, 0, 0 ); |
| if (handle == INVALID_HANDLE_VALUE) continue; |
| init_cdrom_registry( handle ); |
| CloseHandle( handle ); |
| } |
| } |
| |
| |
| /* convert the drive type entries from the old format to the new one */ |
| static void convert_drive_types(void) |
| { |
| static const WCHAR TypeW[] = {'T','y','p','e',0}; |
| static const WCHAR drive_types_keyW[] = {'M','a','c','h','i','n','e','\\', |
| 'S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\', |
| 'D','r','i','v','e','s',0 }; |
| WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\','W','i','n','e','\\', |
| 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0}; |
| char tmp[32*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)]; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| DWORD dummy; |
| ULONG disp; |
| HKEY hkey_old, hkey_new; |
| int i; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| RtlInitUnicodeString( &nameW, drive_types_keyW ); |
| |
| if (NtCreateKey( &hkey_new, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp )) return; |
| if (disp != REG_CREATED_NEW_KEY) |
| { |
| NtClose( hkey_new ); |
| return; |
| } |
| |
| for (i = 0; i < 26; i++) |
| { |
| RtlInitUnicodeString( &nameW, driveW ); |
| nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i; |
| if (NtOpenKey( &hkey_old, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue; |
| RtlInitUnicodeString( &nameW, TypeW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy )) |
| { |
| WCHAR valueW[] = {'A',':',0}; |
| WCHAR *type = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| |
| valueW[0] = 'A' + i; |
| RtlInitUnicodeString( &nameW, valueW ); |
| NtSetValueKey( hkey_new, &nameW, 0, REG_SZ, type, (strlenW(type) + 1) * sizeof(WCHAR) ); |
| MESSAGE( "Converted drive type to new entry HKLM\\Software\\Wine\\Drives \"%c:\" = %s\n", |
| 'A' + i, debugstr_w(type) ); |
| } |
| NtClose( hkey_old ); |
| } |
| NtClose( hkey_new ); |
| } |
| |
| |
| /* convert the environment variable entries from the old format to the new one */ |
| static void convert_environment( HKEY hkey_current_user ) |
| { |
| static const WCHAR wineW[] = {'M','a','c','h','i','n','e','\\', |
| 'S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\','W','i','n','e','\\', |
| 'C','o','n','f','i','g','\\','W','i','n','e',0}; |
| static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0}; |
| static const WCHAR systemW[] = {'s','y','s','t','e','m',0}; |
| static const WCHAR windirW[] = {'w','i','n','d','i','r',0}; |
| static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0}; |
| static const WCHAR envW[] = {'E','n','v','i','r','o','n','m','e','n','t',0}; |
| static const WCHAR tempW[] = {'T','E','M','P',0}; |
| static const WCHAR tmpW[] = {'T','M','P',0}; |
| static const WCHAR pathW[] = {'P','A','T','H',0}; |
| static const WCHAR profileW[] = {'p','r','o','f','i','l','e',0}; |
| static const WCHAR userprofileW[] = {'U','S','E','R','P','R','O','F','I','L','E',0}; |
| |
| char buffer[1024*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)]; |
| KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| DWORD dummy; |
| ULONG disp; |
| HKEY hkey_old, hkey_env; |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| RtlInitUnicodeString( &nameW, wineW ); |
| |
| if (NtOpenKey( &hkey_old, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) return; |
| |
| attr.RootDirectory = hkey_current_user; |
| RtlInitUnicodeString( &nameW, envW ); |
| if (NtCreateKey( &hkey_env, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &disp )) |
| { |
| NtClose( hkey_old ); |
| return; |
| } |
| if (disp != REG_CREATED_NEW_KEY) goto done; |
| |
| /* convert TEMP */ |
| RtlInitUnicodeString( &nameW, tempW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &dummy )) |
| { |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| RtlInitUnicodeString( &nameW, tmpW ); |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| MESSAGE( "Converted temp dir to new entry HKCU\\Environment \"TEMP\" = %s\n", |
| debugstr_w( (WCHAR*)info->Data ) ); |
| } |
| |
| /* convert PATH */ |
| RtlInitUnicodeString( &nameW, pathW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &dummy )) |
| { |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| MESSAGE( "Converted path dir to new entry HKCU\\Environment \"PATH\" = %s\n", |
| debugstr_w( (WCHAR*)info->Data ) ); |
| } |
| |
| /* convert USERPROFILE */ |
| RtlInitUnicodeString( &nameW, profileW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &dummy )) |
| { |
| RtlInitUnicodeString( &nameW, userprofileW ); |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| MESSAGE( "Converted profile dir to new entry HKCU\\Environment \"USERPROFILE\" = %s\n", |
| debugstr_w( (WCHAR*)info->Data ) ); |
| } |
| |
| /* convert windir */ |
| RtlInitUnicodeString( &nameW, windowsW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &dummy )) |
| { |
| RtlInitUnicodeString( &nameW, windirW ); |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| MESSAGE( "Converted windows dir to new entry HKCU\\Environment \"windir\" = %s\n", |
| debugstr_w( (WCHAR*)info->Data ) ); |
| } |
| |
| /* convert winsysdir */ |
| RtlInitUnicodeString( &nameW, systemW ); |
| if (!NtQueryValueKey( hkey_old, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &dummy )) |
| { |
| RtlInitUnicodeString( &nameW, winsysdirW ); |
| NtSetValueKey( hkey_env, &nameW, 0, info->Type, info->Data, info->DataLength ); |
| MESSAGE( "Converted system dir to new entry HKCU\\Environment \"winsysdir\" = %s\n", |
| debugstr_w( (WCHAR*)info->Data ) ); |
| } |
| |
| done: |
| NtClose( hkey_old ); |
| NtClose( hkey_env ); |
| } |
| |
| |
| /* load all registry (native and global and home) */ |
| void SHELL_LoadRegistry( void ) |
| { |
| HKEY hkey_local_machine, hkey_users, hkey_users_default, hkey_current_user, hkey_config; |
| OBJECT_ATTRIBUTES attr; |
| UNICODE_STRING nameW; |
| DWORD count; |
| ULONG dispos; |
| BOOL res; |
| int all, period; |
| char tmp[1024]; |
| |
| static const WCHAR MachineW[] = {'M','a','c','h','i','n','e',0}; |
| static const WCHAR UserW[] = {'U','s','e','r',0}; |
| static const WCHAR DefaultW[] = {'.','D','e','f','a','u','l','t',0}; |
| static const WCHAR RegistryW[] = {'M','a','c','h','i','n','e','\\', |
| 'S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\', |
| 'W','i','n','e','\\', |
| 'C','o','n','f','i','g','\\', |
| 'R','e','g','i','s','t','r','y',0}; |
| static const WCHAR load_win_reg_filesW[] = {'L','o','a','d','W','i','n','d','o','w','s','R','e','g','i','s','t','r','y','F','i','l','e','s',0}; |
| static const WCHAR load_global_reg_filesW[] = {'L','o','a','d','G','l','o','b','a','l','R','e','g','i','s','t','r','y','F','i','l','e','s',0}; |
| static const WCHAR SaveOnlyUpdatedKeysW[] = {'S','a','v','e','O','n','l','y','U','p','d','a','t','e','d','K','e','y','s',0}; |
| static const WCHAR PeriodicSaveW[] = {'P','e','r','i','o','d','i','c','S','a','v','e',0}; |
| static const WCHAR GlobalRegistryDirW[] = {'G','l','o','b','a','l','R','e','g','i','s','t','r','y','D','i','r',0}; |
| |
| TRACE("(void)\n"); |
| |
| attr.Length = sizeof(attr); |
| attr.RootDirectory = 0; |
| attr.ObjectName = &nameW; |
| attr.Attributes = 0; |
| attr.SecurityDescriptor = NULL; |
| attr.SecurityQualityOfService = NULL; |
| |
| RtlInitUnicodeString( &nameW, UserW ); |
| NtCreateKey( &hkey_users, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &dispos ); |
| if (dispos == REG_OPENED_EXISTING_KEY) |
| { |
| /* someone else already loaded the registry */ |
| NtClose( hkey_users ); |
| return; |
| } |
| |
| RtlInitUnicodeString( &nameW, MachineW ); |
| NtCreateKey( &hkey_local_machine, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ); |
| |
| attr.RootDirectory = hkey_users; |
| RtlInitUnicodeString( &nameW, DefaultW ); |
| if (NtCreateKey( &hkey_users_default, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) |
| { |
| ERR("Cannot create HKEY_USERS/.Default\n" ); |
| ExitProcess(1); |
| } |
| RtlOpenCurrentUser( KEY_ALL_ACCESS, &hkey_current_user ); |
| |
| _allocate_default_keys(); |
| |
| attr.RootDirectory = 0; |
| RtlInitUnicodeString( &nameW, RegistryW ); |
| if (NtOpenKey( &hkey_config, KEY_ALL_ACCESS, &attr )) hkey_config = 0; |
| |
| /* load windows registry if required */ |
| |
| res = TRUE; |
| attr.RootDirectory = hkey_config; |
| RtlInitUnicodeString( &nameW, load_win_reg_filesW ); |
| if (!NtQueryValueKey( hkey_config, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| res = !IS_OPTION_FALSE(str[0]); |
| } |
| if (res) _load_windows_registry( hkey_local_machine, hkey_current_user, hkey_users_default ); |
| |
| /* load global registry if required */ |
| |
| res = TRUE; |
| RtlInitUnicodeString( &nameW, load_global_reg_filesW ); |
| if (!NtQueryValueKey( hkey_config, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| res = !IS_OPTION_FALSE(str[0]); |
| } |
| if (res) |
| { |
| /* load global registry files (stored in /etc/wine) */ |
| char *p, configfile[MAX_PATHNAME_LEN]; |
| |
| /* Override ETCDIR? */ |
| configfile[0] = 0; |
| RtlInitUnicodeString( &nameW, GlobalRegistryDirW ); |
| if (!NtQueryValueKey( hkey_config, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| RtlUnicodeToMultiByteN( configfile, sizeof(configfile), NULL, |
| str, (strlenW(str) + 1) * sizeof(WCHAR)); |
| } |
| if (configfile[0] != '/') strcpy(configfile, ETCDIR); |
| |
| TRACE("GlobalRegistryDir is '%s'.\n", configfile); |
| |
| /* Load the global HKU hive directly from sysconfdir */ |
| p = configfile + strlen(configfile); |
| strcpy(p, SAVE_GLOBAL_REGBRANCH_USER_DEFAULT); |
| load_wine_registry( hkey_users, configfile ); |
| |
| /* Load the global machine defaults directly from sysconfdir */ |
| strcpy(p, SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE); |
| load_wine_registry( hkey_local_machine, configfile ); |
| } |
| |
| /* setup registry saving */ |
| |
| all = FALSE; |
| RtlInitUnicodeString( &nameW, SaveOnlyUpdatedKeysW ); |
| if (!NtQueryValueKey( hkey_config, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| all = IS_OPTION_FALSE(str[0]); |
| } |
| |
| period = 0; |
| RtlInitUnicodeString( &nameW, PeriodicSaveW ); |
| if (!NtQueryValueKey( hkey_config, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &count )) |
| { |
| WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data; |
| period = (int)strtolW(str, NULL, 10); |
| } |
| |
| /* load home registry and set saving level (0 for saving everything, |
| * 1 for saving only modified keys) */ |
| |
| SERVER_START_REQ( load_user_registries ) |
| { |
| req->hkey = hkey_current_user; |
| req->saving = !all; |
| req->period = period * 1000; |
| wine_server_call( req ); |
| } |
| SERVER_END_REQ; |
| |
| /* create hardware registry branch */ |
| |
| create_hardware_branch(); |
| |
| /* convert keys from config file to new registry format */ |
| |
| convert_drive_types(); |
| convert_environment( hkey_current_user ); |
| |
| NtClose(hkey_users_default); |
| NtClose(hkey_current_user); |
| NtClose(hkey_users); |
| NtClose(hkey_local_machine); |
| NtClose(hkey_config); |
| } |