Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
57f8eb71
Commit
57f8eb71
authored
Dec 15, 2000
by
Martin Pilka
Committed by
Alexandre Julliard
Dec 15, 2000
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- registry loading speed up (reduced client <-> server communication)
- make the code a bit more understandable
parent
4f12e61c
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1288 additions
and
1372 deletions
+1288
-1372
registry.c
misc/registry.c
+1288
-1372
No files found.
misc/registry.c
View file @
57f8eb71
...
...
@@ -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_InitRegistryS
aving
(
HKEY
hkey_users_default
)
/* configure save files and start the periodic saving timer
[Internal]
*/
static
void
_init_registry_s
aving
(
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]
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment