Fixed regression in loading of builtin apps from the system dir when
[wine] / dlls / kernel / vxd.c
1 /*
2  * Win32 VxD functions
3  *
4  * Copyright 1998 Marcus Meissner
5  * Copyright 1998 Ulrich Weigand
6  * Copyright 1998 Patrik Stridvall
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdlib.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <sys/types.h>
31 #include <string.h>
32 #include <stdarg.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winreg.h"
37 #include "winerror.h"
38 #include "winnls.h"
39 #include "ntstatus.h"
40 #include "winnt.h"
41 #include "winternl.h"
42 #include "kernel_private.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(vxd);
46
47 /*
48  * VMM VxDCall service names are (mostly) taken from Stan Mitchell's
49  * "Inside the Windows 95 File System"
50  */
51
52 #define N_VMM_SERVICE 41
53
54 static const char * const VMM_Service_Name[N_VMM_SERVICE] =
55 {
56     "PageReserve",            /* 0x0000 */
57     "PageCommit",             /* 0x0001 */
58     "PageDecommit",           /* 0x0002 */
59     "PagerRegister",          /* 0x0003 */
60     "PagerQuery",             /* 0x0004 */
61     "HeapAllocate",           /* 0x0005 */
62     "ContextCreate",          /* 0x0006 */
63     "ContextDestroy",         /* 0x0007 */
64     "PageAttach",             /* 0x0008 */
65     "PageFlush",              /* 0x0009 */
66     "PageFree",               /* 0x000A */
67     "ContextSwitch",          /* 0x000B */
68     "HeapReAllocate",         /* 0x000C */
69     "PageModifyPermissions",  /* 0x000D */
70     "PageQuery",              /* 0x000E */
71     "GetCurrentContext",      /* 0x000F */
72     "HeapFree",               /* 0x0010 */
73     "RegOpenKey",             /* 0x0011 */
74     "RegCreateKey",           /* 0x0012 */
75     "RegCloseKey",            /* 0x0013 */
76     "RegDeleteKey",           /* 0x0014 */
77     "RegSetValue",            /* 0x0015 */
78     "RegDeleteValue",         /* 0x0016 */
79     "RegQueryValue",          /* 0x0017 */
80     "RegEnumKey",             /* 0x0018 */
81     "RegEnumValue",           /* 0x0019 */
82     "RegQueryValueEx",        /* 0x001A */
83     "RegSetValueEx",          /* 0x001B */
84     "RegFlushKey",            /* 0x001C */
85     "RegQueryInfoKey",        /* 0x001D */
86     "GetDemandPageInfo",      /* 0x001E */
87     "BlockOnID",              /* 0x001F */
88     "SignalID",               /* 0x0020 */
89     "RegLoadKey",             /* 0x0021 */
90     "RegUnLoadKey",           /* 0x0022 */
91     "RegSaveKey",             /* 0x0023 */
92     "RegRemapPreDefKey",      /* 0x0024 */
93     "PageChangePager",        /* 0x0025 */
94     "RegQueryMultipleValues", /* 0x0026 */
95     "RegReplaceKey",          /* 0x0027 */
96     "<KERNEL32.101>"          /* 0x0028 -- What does this do??? */
97 };
98
99 /* PageReserve arena values */
100 #define PR_PRIVATE    0x80000400 /* anywhere in private arena */
101 #define PR_SHARED     0x80060000 /* anywhere in shared arena */
102 #define PR_SYSTEM     0x80080000 /* anywhere in system arena */
103
104 /* PageReserve flags */
105 #define PR_FIXED      0x00000008 /* don't move during PageReAllocate */
106 #define PR_4MEG       0x00000001 /* allocate on 4mb boundary */
107 #define PR_STATIC     0x00000010 /* see PageReserve documentation */
108
109 /* PageCommit default pager handle values */
110 #define PD_ZEROINIT   0x00000001 /* swappable zero-initialized pages */
111 #define PD_NOINIT     0x00000002 /* swappable uninitialized pages */
112 #define PD_FIXEDZERO  0x00000003 /* fixed zero-initialized pages */
113 #define PD_FIXED      0x00000004 /* fixed uninitialized pages */
114
115 /* PageCommit flags */
116 #define PC_FIXED      0x00000008 /* pages are permanently locked */
117 #define PC_LOCKED     0x00000080 /* pages are made present and locked */
118 #define PC_LOCKEDIFDP 0x00000100 /* pages are locked if swap via DOS */
119 #define PC_WRITEABLE  0x00020000 /* make the pages writeable */
120 #define PC_USER       0x00040000 /* make the pages ring 3 accessible */
121 #define PC_INCR       0x40000000 /* increment "pagerdata" each page */
122 #define PC_PRESENT    0x80000000 /* make pages initially present */
123 #define PC_STATIC     0x20000000 /* allow commit in PR_STATIC object */
124 #define PC_DIRTY      0x08000000 /* make pages initially dirty */
125 #define PC_CACHEDIS   0x00100000 /* Allocate uncached pages - new for WDM */
126 #define PC_CACHEWT    0x00080000 /* Allocate write through cache pages - new for WDM */
127 #define PC_PAGEFLUSH  0x00008000 /* Touch device mapped pages on alloc - new for WDM */
128
129 /* PageCommitContig additional flags */
130 #define PCC_ZEROINIT  0x00000001 /* zero-initialize new pages */
131 #define PCC_NOLIN     0x10000000 /* don't map to any linear address */
132
133
134 /* Pop a DWORD from the 32-bit stack */
135 static inline DWORD stack32_pop( CONTEXT86 *context )
136 {
137     DWORD ret = *(DWORD *)context->Esp;
138     context->Esp += sizeof(DWORD);
139     return ret;
140 }
141
142
143 /******************************************************************************
144  * The following is a massive duplication of the advapi32 code.
145  * Unfortunately sharing the code is not possible since the native
146  * Win95 advapi32 depends on it. Someday we should probably stop
147  * supporting native Win95 advapi32 altogether...
148  */
149
150
151 #define HKEY_SPECIAL_ROOT_FIRST   HKEY_CLASSES_ROOT
152 #define HKEY_SPECIAL_ROOT_LAST    HKEY_DYN_DATA
153 #define NB_SPECIAL_ROOT_KEYS      ((UINT)HKEY_SPECIAL_ROOT_LAST - (UINT)HKEY_SPECIAL_ROOT_FIRST + 1)
154
155 static HKEY special_root_keys[NB_SPECIAL_ROOT_KEYS];
156
157 static const WCHAR name_CLASSES_ROOT[] =
158     {'M','a','c','h','i','n','e','\\',
159      'S','o','f','t','w','a','r','e','\\',
160      'C','l','a','s','s','e','s',0};
161 static const WCHAR name_LOCAL_MACHINE[] =
162     {'M','a','c','h','i','n','e',0};
163 static const WCHAR name_USERS[] =
164     {'U','s','e','r',0};
165 static const WCHAR name_PERFORMANCE_DATA[] =
166     {'P','e','r','f','D','a','t','a',0};
167 static const WCHAR name_CURRENT_CONFIG[] =
168     {'M','a','c','h','i','n','e','\\',
169      'S','y','s','t','e','m','\\',
170      'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
171      'H','a','r','d','w','a','r','e','P','r','o','f','i','l','e','s','\\',
172      'C','u','r','r','e','n','t',0};
173 static const WCHAR name_DYN_DATA[] =
174     {'D','y','n','D','a','t','a',0};
175
176 #define DECL_STR(key) { sizeof(name_##key)-sizeof(WCHAR), sizeof(name_##key), (LPWSTR)name_##key }
177 static UNICODE_STRING root_key_names[NB_SPECIAL_ROOT_KEYS] =
178 {
179     DECL_STR(CLASSES_ROOT),
180     { 0, 0, NULL },         /* HKEY_CURRENT_USER is determined dynamically */
181     DECL_STR(LOCAL_MACHINE),
182     DECL_STR(USERS),
183     DECL_STR(PERFORMANCE_DATA),
184     DECL_STR(CURRENT_CONFIG),
185     DECL_STR(DYN_DATA)
186 };
187 #undef DECL_STR
188
189
190 /* check if value type needs string conversion (Ansi<->Unicode) */
191 inline static int is_string( DWORD type )
192 {
193     return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ);
194 }
195
196 /* create one of the HKEY_* special root keys */
197 static HKEY create_special_root_hkey( HKEY hkey, DWORD access )
198 {
199     HKEY ret = 0;
200     int idx = (UINT)hkey - (UINT)HKEY_SPECIAL_ROOT_FIRST;
201
202     if (hkey == HKEY_CURRENT_USER)
203     {
204         if (RtlOpenCurrentUser( access, &hkey )) return 0;
205     }
206     else
207     {
208         OBJECT_ATTRIBUTES attr;
209
210         attr.Length = sizeof(attr);
211         attr.RootDirectory = 0;
212         attr.ObjectName = &root_key_names[idx];
213         attr.Attributes = 0;
214         attr.SecurityDescriptor = NULL;
215         attr.SecurityQualityOfService = NULL;
216         if (NtCreateKey( &hkey, access, &attr, 0, NULL, 0, NULL )) return 0;
217     }
218
219     if (!(ret = InterlockedCompareExchangePointer( (PVOID) &special_root_keys[idx], hkey, 0 )))
220         ret = hkey;
221     else
222         NtClose( hkey );  /* somebody beat us to it */
223     return ret;
224 }
225
226 /* map the hkey from special root to normal key if necessary */
227 inline static HKEY get_special_root_hkey( HKEY hkey )
228 {
229     HKEY ret = hkey;
230
231     if ((hkey >= HKEY_SPECIAL_ROOT_FIRST) && (hkey <= HKEY_SPECIAL_ROOT_LAST))
232     {
233         if (!(ret = special_root_keys[(UINT)hkey - (UINT)HKEY_SPECIAL_ROOT_FIRST]))
234             ret = create_special_root_hkey( hkey, KEY_ALL_ACCESS );
235     }
236     return ret;
237 }
238
239
240 /******************************************************************************
241  *           VMM_RegCreateKeyA
242  */
243 static DWORD VMM_RegCreateKeyA( HKEY hkey, LPCSTR name, PHKEY retkey )
244 {
245     OBJECT_ATTRIBUTES attr;
246     UNICODE_STRING nameW;
247     ANSI_STRING nameA;
248     NTSTATUS status;
249
250     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
251
252     attr.Length = sizeof(attr);
253     attr.RootDirectory = hkey;
254     attr.ObjectName = &nameW;
255     attr.Attributes = 0;
256     attr.SecurityDescriptor = NULL;
257     attr.SecurityQualityOfService = NULL;
258     RtlInitAnsiString( &nameA, name );
259
260     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
261     {
262         status = NtCreateKey( retkey, KEY_ALL_ACCESS, &attr, 0, NULL,
263                               REG_OPTION_NON_VOLATILE, NULL );
264         RtlFreeUnicodeString( &nameW );
265     }
266     return RtlNtStatusToDosError( status );
267 }
268
269
270 /******************************************************************************
271  *           VMM_RegOpenKeyExA
272  */
273 DWORD WINAPI VMM_RegOpenKeyExA(HKEY hkey, LPCSTR name, DWORD reserved, REGSAM access, PHKEY retkey)
274 {
275     OBJECT_ATTRIBUTES attr;
276     UNICODE_STRING nameW;
277     STRING nameA;
278     NTSTATUS status;
279
280     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
281
282     attr.Length = sizeof(attr);
283     attr.RootDirectory = hkey;
284     attr.ObjectName = &nameW;
285     attr.Attributes = 0;
286     attr.SecurityDescriptor = NULL;
287     attr.SecurityQualityOfService = NULL;
288
289     RtlInitAnsiString( &nameA, name );
290     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
291     {
292         status = NtOpenKey( retkey, access, &attr );
293         RtlFreeUnicodeString( &nameW );
294     }
295     return RtlNtStatusToDosError( status );
296 }
297
298
299 /******************************************************************************
300  *           VMM_RegCloseKey
301  */
302 static DWORD VMM_RegCloseKey( HKEY hkey )
303 {
304     if (!hkey || hkey >= (HKEY)0x80000000) return ERROR_SUCCESS;
305     return RtlNtStatusToDosError( NtClose( hkey ) );
306 }
307
308
309 /******************************************************************************
310  *           VMM_RegDeleteKeyA
311  */
312 static DWORD VMM_RegDeleteKeyA( HKEY hkey, LPCSTR name )
313 {
314     DWORD ret;
315     HKEY tmp;
316
317     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
318
319     if (!name || !*name) return RtlNtStatusToDosError( NtDeleteKey( hkey ) );
320     if (!(ret = VMM_RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
321     {
322         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
323         NtClose( tmp );
324     }
325     return ret;
326 }
327
328
329 /******************************************************************************
330  *           VMM_RegSetValueExA
331  */
332 static DWORD VMM_RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
333                                  CONST BYTE *data, DWORD count )
334 {
335     UNICODE_STRING nameW;
336     ANSI_STRING nameA;
337     WCHAR *dataW = NULL;
338     NTSTATUS status;
339
340     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
341
342     if (is_string(type))
343     {
344         DWORD lenW;
345
346         if (count)
347         {
348             /* if user forgot to count terminating null, add it (yes NT does this) */
349             if (data[count-1] && !data[count]) count++;
350         }
351         RtlMultiByteToUnicodeSize( &lenW, data, count );
352         if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW ))) return ERROR_OUTOFMEMORY;
353         RtlMultiByteToUnicodeN( dataW, lenW, NULL, data, count );
354         count = lenW;
355         data = (BYTE *)dataW;
356     }
357
358     RtlInitAnsiString( &nameA, name );
359     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
360     {
361         status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
362         RtlFreeUnicodeString( &nameW );
363     }
364     if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
365     return RtlNtStatusToDosError( status );
366 }
367
368
369 /******************************************************************************
370  *           VMM_RegSetValueA
371  */
372 static DWORD VMM_RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
373 {
374     HKEY subkey = hkey;
375     DWORD ret;
376
377     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
378
379     if (name && name[0])  /* need to create the subkey */
380     {
381         if ((ret = VMM_RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
382     }
383     ret = VMM_RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
384     if (subkey != hkey) NtClose( subkey );
385     return ret;
386 }
387
388
389 /******************************************************************************
390  *           VMM_RegDeleteValueA
391  */
392 static DWORD VMM_RegDeleteValueA( HKEY hkey, LPCSTR name )
393 {
394     UNICODE_STRING nameW;
395     STRING nameA;
396     NTSTATUS status;
397
398     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
399
400     RtlInitAnsiString( &nameA, name );
401     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
402     {
403         status = NtDeleteValueKey( hkey, &nameW );
404         RtlFreeUnicodeString( &nameW );
405     }
406     return RtlNtStatusToDosError( status );
407 }
408
409
410 /******************************************************************************
411  *           VMM_RegQueryValueExA
412  */
413 static DWORD VMM_RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
414                                    LPBYTE data, LPDWORD count )
415 {
416     NTSTATUS status;
417     ANSI_STRING nameA;
418     UNICODE_STRING nameW;
419     DWORD total_size;
420     char buffer[256], *buf_ptr = buffer;
421     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
422     static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data );
423
424     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
425     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
426
427     RtlInitAnsiString( &nameA, name );
428     if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
429         return RtlNtStatusToDosError(status);
430
431     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
432                               buffer, sizeof(buffer), &total_size );
433     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
434
435     /* we need to fetch the contents for a string type even if not requested,
436      * because we need to compute the length of the ASCII string. */
437     if (data || is_string(info->Type))
438     {
439         /* retry with a dynamically allocated buffer */
440         while (status == STATUS_BUFFER_OVERFLOW)
441         {
442             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
443             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
444             {
445                 status = STATUS_NO_MEMORY;
446                 goto done;
447             }
448             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
449             status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
450                                       buf_ptr, total_size, &total_size );
451         }
452
453         if (!status)
454         {
455             if (is_string(info->Type))
456             {
457                 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
458                                                  (total_size - info_size) /sizeof(WCHAR),
459                                                  NULL, 0, NULL, NULL );
460                 if (data && len)
461                 {
462                     if (len > *count) status = STATUS_BUFFER_OVERFLOW;
463                     else
464                     {
465                         WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
466                                              (total_size - info_size) /sizeof(WCHAR),
467                                              data, len, NULL, NULL );
468                         /* if the type is REG_SZ and data is not 0-terminated
469                          * and there is enough space in the buffer NT appends a \0 */
470                         if (len < *count && data[len-1]) data[len] = 0;
471                     }
472                 }
473                 total_size = len + info_size;
474             }
475             else if (data)
476             {
477                 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
478                 else memcpy( data, buf_ptr + info_size, total_size - info_size );
479             }
480         }
481         else if (status != STATUS_BUFFER_OVERFLOW) goto done;
482     }
483
484     if (type) *type = info->Type;
485     if (count) *count = total_size - info_size;
486
487  done:
488     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
489     RtlFreeUnicodeString( &nameW );
490     return RtlNtStatusToDosError(status);
491 }
492
493
494 /******************************************************************************
495  *           VMM_RegQueryValueA
496  */
497 static DWORD VMM_RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
498 {
499     DWORD ret;
500     HKEY subkey = hkey;
501
502     if (name && name[0])
503     {
504         if ((ret = VMM_RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, &subkey )) != ERROR_SUCCESS)
505             return ret;
506     }
507     ret = VMM_RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
508     if (subkey != hkey) NtClose( subkey );
509     if (ret == ERROR_FILE_NOT_FOUND)
510     {
511         /* return empty string if default value not found */
512         if (data) *data = 0;
513         if (count) *count = 1;
514         ret = ERROR_SUCCESS;
515     }
516     return ret;
517 }
518
519
520 /******************************************************************************
521  *           VMM_RegEnumValueA
522  */
523 static DWORD VMM_RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
524                                 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
525 {
526     NTSTATUS status;
527     DWORD total_size;
528     char buffer[256], *buf_ptr = buffer;
529     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
530     static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name );
531
532     TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
533           hkey, index, value, val_count, reserved, type, data, count );
534
535     /* NT only checks count, not val_count */
536     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
537     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
538
539     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
540     if (data) total_size += *count;
541     total_size = min( sizeof(buffer), total_size );
542
543     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
544                                   buffer, total_size, &total_size );
545     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
546
547     /* we need to fetch the contents for a string type even if not requested,
548      * because we need to compute the length of the ASCII string. */
549     if (value || data || is_string(info->Type))
550     {
551         /* retry with a dynamically allocated buffer */
552         while (status == STATUS_BUFFER_OVERFLOW)
553         {
554             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
555             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
556                 return ERROR_NOT_ENOUGH_MEMORY;
557             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
558             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
559                                           buf_ptr, total_size, &total_size );
560         }
561
562         if (status) goto done;
563
564         if (is_string(info->Type))
565         {
566             DWORD len;
567             RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
568                                        total_size - info->DataOffset );
569             if (data && len)
570             {
571                 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
572                 else
573                 {
574                     RtlUnicodeToMultiByteN( data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset),
575                                             total_size - info->DataOffset );
576                     /* if the type is REG_SZ and data is not 0-terminated
577                      * and there is enough space in the buffer NT appends a \0 */
578                     if (len < *count && data[len-1]) data[len] = 0;
579                 }
580             }
581             info->DataLength = len;
582         }
583         else if (data)
584         {
585             if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
586             else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
587         }
588
589         if (value && !status)
590         {
591             DWORD len;
592
593             RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
594             if (len >= *val_count)
595             {
596                 status = STATUS_BUFFER_OVERFLOW;
597                 if (*val_count)
598                 {
599                     len = *val_count - 1;
600                     RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
601                     value[len] = 0;
602                 }
603             }
604             else
605             {
606                 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
607                 value[len] = 0;
608                 *val_count = len;
609             }
610         }
611     }
612     else status = STATUS_SUCCESS;
613
614     if (type) *type = info->Type;
615     if (count) *count = info->DataLength;
616
617  done:
618     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
619     return RtlNtStatusToDosError(status);
620 }
621
622
623 /******************************************************************************
624  *           VMM_RegEnumKeyA
625  */
626 static DWORD VMM_RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
627 {
628     NTSTATUS status;
629     char buffer[256], *buf_ptr = buffer;
630     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
631     DWORD total_size;
632
633     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
634
635     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
636                              buffer, sizeof(buffer), &total_size );
637
638     while (status == STATUS_BUFFER_OVERFLOW)
639     {
640         /* retry with a dynamically allocated buffer */
641         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
642         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
643             return ERROR_NOT_ENOUGH_MEMORY;
644         info = (KEY_NODE_INFORMATION *)buf_ptr;
645         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
646                                  buf_ptr, total_size, &total_size );
647     }
648
649     if (!status)
650     {
651         DWORD len;
652
653         RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
654         if (len >= name_len) status = STATUS_BUFFER_OVERFLOW;
655         else
656         {
657             RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
658             name[len] = 0;
659         }
660     }
661
662     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
663     return RtlNtStatusToDosError( status );
664 }
665
666
667 /******************************************************************************
668  *           VMM_RegQueryInfoKeyA
669  *
670  * NOTE: This VxDCall takes only a subset of the parameters that the
671  * corresponding Win32 API call does. The implementation in Win95
672  * ADVAPI32 sets all output parameters not mentioned here to zero.
673  */
674 static DWORD VMM_RegQueryInfoKeyA( HKEY hkey, LPDWORD subkeys, LPDWORD max_subkey,
675                                    LPDWORD values, LPDWORD max_value, LPDWORD max_data )
676 {
677     NTSTATUS status;
678     KEY_FULL_INFORMATION info;
679     DWORD total_size;
680
681     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
682
683     status = NtQueryKey( hkey, KeyFullInformation, &info, sizeof(info), &total_size );
684     if (status && status != STATUS_BUFFER_OVERFLOW) return RtlNtStatusToDosError( status );
685
686     if (subkeys) *subkeys = info.SubKeys;
687     if (max_subkey) *max_subkey = info.MaxNameLen;
688     if (values) *values = info.Values;
689     if (max_value) *max_value = info.MaxValueNameLen;
690     if (max_data) *max_data = info.MaxValueDataLen;
691     return ERROR_SUCCESS;
692 }
693
694
695 /***********************************************************************
696  *           VxDCall_VMM
697  */
698 static DWORD VxDCall_VMM( DWORD service, CONTEXT86 *context )
699 {
700     switch ( LOWORD(service) )
701     {
702     case 0x0011:  /* RegOpenKey */
703     {
704         HKEY    hkey       = (HKEY)  stack32_pop( context );
705         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
706         PHKEY   retkey     = (PHKEY)stack32_pop( context );
707         return VMM_RegOpenKeyExA( hkey, lpszSubKey, 0, KEY_ALL_ACCESS, retkey );
708     }
709
710     case 0x0012:  /* RegCreateKey */
711     {
712         HKEY    hkey       = (HKEY)  stack32_pop( context );
713         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
714         PHKEY   retkey     = (PHKEY)stack32_pop( context );
715         return VMM_RegCreateKeyA( hkey, lpszSubKey, retkey );
716     }
717
718     case 0x0013:  /* RegCloseKey */
719     {
720         HKEY    hkey       = (HKEY)stack32_pop( context );
721         return VMM_RegCloseKey( hkey );
722     }
723
724     case 0x0014:  /* RegDeleteKey */
725     {
726         HKEY    hkey       = (HKEY)  stack32_pop( context );
727         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
728         return VMM_RegDeleteKeyA( hkey, lpszSubKey );
729     }
730
731     case 0x0015:  /* RegSetValue */
732     {
733         HKEY    hkey       = (HKEY)  stack32_pop( context );
734         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
735         DWORD   dwType     = (DWORD) stack32_pop( context );
736         LPCSTR  lpszData   = (LPCSTR)stack32_pop( context );
737         DWORD   cbData     = (DWORD) stack32_pop( context );
738         return VMM_RegSetValueA( hkey, lpszSubKey, dwType, lpszData, cbData );
739     }
740
741     case 0x0016:  /* RegDeleteValue */
742     {
743         HKEY    hkey       = (HKEY) stack32_pop( context );
744         LPSTR   lpszValue  = (LPSTR)stack32_pop( context );
745         return VMM_RegDeleteValueA( hkey, lpszValue );
746     }
747
748     case 0x0017:  /* RegQueryValue */
749     {
750         HKEY    hkey       = (HKEY)   stack32_pop( context );
751         LPSTR   lpszSubKey = (LPSTR)  stack32_pop( context );
752         LPSTR   lpszData   = (LPSTR)  stack32_pop( context );
753         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
754         return VMM_RegQueryValueA( hkey, lpszSubKey, lpszData, lpcbData );
755     }
756
757     case 0x0018:  /* RegEnumKey */
758     {
759         HKEY    hkey       = (HKEY) stack32_pop( context );
760         DWORD   iSubkey    = (DWORD)stack32_pop( context );
761         LPSTR   lpszName   = (LPSTR)stack32_pop( context );
762         DWORD   lpcchName  = (DWORD)stack32_pop( context );
763         return VMM_RegEnumKeyA( hkey, iSubkey, lpszName, lpcchName );
764     }
765
766     case 0x0019:  /* RegEnumValue */
767     {
768         HKEY    hkey       = (HKEY)   stack32_pop( context );
769         DWORD   iValue     = (DWORD)  stack32_pop( context );
770         LPSTR   lpszValue  = (LPSTR)  stack32_pop( context );
771         LPDWORD lpcchValue = (LPDWORD)stack32_pop( context );
772         LPDWORD lpReserved = (LPDWORD)stack32_pop( context );
773         LPDWORD lpdwType   = (LPDWORD)stack32_pop( context );
774         LPBYTE  lpbData    = (LPBYTE) stack32_pop( context );
775         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
776         return VMM_RegEnumValueA( hkey, iValue, lpszValue, lpcchValue,
777                                   lpReserved, lpdwType, lpbData, lpcbData );
778     }
779
780     case 0x001A:  /* RegQueryValueEx */
781     {
782         HKEY    hkey       = (HKEY)   stack32_pop( context );
783         LPSTR   lpszValue  = (LPSTR)  stack32_pop( context );
784         LPDWORD lpReserved = (LPDWORD)stack32_pop( context );
785         LPDWORD lpdwType   = (LPDWORD)stack32_pop( context );
786         LPBYTE  lpbData    = (LPBYTE) stack32_pop( context );
787         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
788         return VMM_RegQueryValueExA( hkey, lpszValue, lpReserved,
789                                      lpdwType, lpbData, lpcbData );
790     }
791
792     case 0x001B:  /* RegSetValueEx */
793     {
794         HKEY    hkey       = (HKEY)  stack32_pop( context );
795         LPSTR   lpszValue  = (LPSTR) stack32_pop( context );
796         DWORD   dwReserved = (DWORD) stack32_pop( context );
797         DWORD   dwType     = (DWORD) stack32_pop( context );
798         LPBYTE  lpbData    = (LPBYTE)stack32_pop( context );
799         DWORD   cbData     = (DWORD) stack32_pop( context );
800         return VMM_RegSetValueExA( hkey, lpszValue, dwReserved,
801                                    dwType, lpbData, cbData );
802     }
803
804     case 0x001C:  /* RegFlushKey */
805     {
806         HKEY hkey = (HKEY)stack32_pop( context );
807         FIXME( "RegFlushKey(%p): stub\n", hkey );
808         return ERROR_SUCCESS;
809     }
810
811     case 0x001D:  /* RegQueryInfoKey */
812     {
813         /* NOTE: This VxDCall takes only a subset of the parameters that the
814                  corresponding Win32 API call does. The implementation in Win95
815                  ADVAPI32 sets all output parameters not mentioned here to zero. */
816
817         HKEY    hkey              = (HKEY)   stack32_pop( context );
818         LPDWORD lpcSubKeys        = (LPDWORD)stack32_pop( context );
819         LPDWORD lpcchMaxSubKey    = (LPDWORD)stack32_pop( context );
820         LPDWORD lpcValues         = (LPDWORD)stack32_pop( context );
821         LPDWORD lpcchMaxValueName = (LPDWORD)stack32_pop( context );
822         LPDWORD lpcchMaxValueData = (LPDWORD)stack32_pop( context );
823         return VMM_RegQueryInfoKeyA( hkey, lpcSubKeys, lpcchMaxSubKey,
824                                      lpcValues, lpcchMaxValueName, lpcchMaxValueData );
825     }
826
827     case 0x0021:  /* RegLoadKey */
828     {
829         HKEY    hkey       = (HKEY)  stack32_pop( context );
830         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
831         LPCSTR  lpszFile   = (LPCSTR)stack32_pop( context );
832         FIXME("RegLoadKey(%p,%s,%s): stub\n",hkey, debugstr_a(lpszSubKey), debugstr_a(lpszFile));
833         return ERROR_SUCCESS;
834     }
835
836     case 0x0022:  /* RegUnLoadKey */
837     {
838         HKEY    hkey       = (HKEY)  stack32_pop( context );
839         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
840         FIXME("RegUnLoadKey(%p,%s): stub\n",hkey, debugstr_a(lpszSubKey));
841         return ERROR_SUCCESS;
842     }
843
844     case 0x0023:  /* RegSaveKey */
845     {
846         HKEY    hkey       = (HKEY)  stack32_pop( context );
847         LPCSTR  lpszFile   = (LPCSTR)stack32_pop( context );
848         LPSECURITY_ATTRIBUTES sa = (LPSECURITY_ATTRIBUTES)stack32_pop( context );
849         FIXME("RegSaveKey(%p,%s,%p): stub\n",hkey, debugstr_a(lpszFile),sa);
850         return ERROR_SUCCESS;
851     }
852
853 #if 0 /* Functions are not yet implemented in misc/registry.c */
854     case 0x0024:  /* RegRemapPreDefKey */
855     case 0x0026:  /* RegQueryMultipleValues */
856 #endif
857
858     case 0x0027:  /* RegReplaceKey */
859     {
860         HKEY    hkey       = (HKEY)  stack32_pop( context );
861         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
862         LPCSTR  lpszNewFile= (LPCSTR)stack32_pop( context );
863         LPCSTR  lpszOldFile= (LPCSTR)stack32_pop( context );
864         FIXME("RegReplaceKey(%p,%s,%s,%s): stub\n", hkey, debugstr_a(lpszSubKey),
865               debugstr_a(lpszNewFile),debugstr_a(lpszOldFile));
866         return ERROR_SUCCESS;
867     }
868
869     case 0x0000: /* PageReserve */
870       {
871         LPVOID address;
872         LPVOID ret;
873         DWORD psize = getpagesize();
874         ULONG page   = (ULONG) stack32_pop( context );
875         ULONG npages = (ULONG) stack32_pop( context );
876         ULONG flags  = (ULONG) stack32_pop( context );
877
878         TRACE("PageReserve: page: %08lx, npages: %08lx, flags: %08lx partial stub!\n",
879               page, npages, flags );
880
881         if ( page == PR_SYSTEM ) {
882           ERR("Can't reserve ring 1 memory\n");
883           return -1;
884         }
885         /* FIXME: This has to be handled separately for the separate
886            address-spaces we now have */
887         if ( page == PR_PRIVATE || page == PR_SHARED ) page = 0;
888         /* FIXME: Handle flags in some way */
889         address = (LPVOID )(page * psize);
890         ret = VirtualAlloc ( address, ( npages * psize ), MEM_RESERVE, 0 );
891         TRACE("PageReserve: returning: %08lx\n", (DWORD )ret );
892         if ( ret == NULL )
893           return -1;
894         else
895           return (DWORD )ret;
896       }
897
898     case 0x0001: /* PageCommit */
899       {
900         LPVOID address;
901         LPVOID ret;
902         DWORD virt_perm;
903         DWORD psize = getpagesize();
904         ULONG page   = (ULONG) stack32_pop( context );
905         ULONG npages = (ULONG) stack32_pop( context );
906         ULONG hpd  = (ULONG) stack32_pop( context );
907         ULONG pagerdata   = (ULONG) stack32_pop( context );
908         ULONG flags  = (ULONG) stack32_pop( context );
909
910         TRACE("PageCommit: page: %08lx, npages: %08lx, hpd: %08lx pagerdata: "
911               "%08lx, flags: %08lx partial stub\n",
912               page, npages, hpd, pagerdata, flags );
913
914         if ( flags & PC_USER )
915           if ( flags & PC_WRITEABLE )
916             virt_perm = PAGE_EXECUTE_READWRITE;
917           else
918             virt_perm = PAGE_EXECUTE_READ;
919         else
920           virt_perm = PAGE_NOACCESS;
921
922         address = (LPVOID )(page * psize);
923         ret = VirtualAlloc ( address, ( npages * psize ), MEM_COMMIT, virt_perm );
924         TRACE("PageCommit: Returning: %08lx\n", (DWORD )ret );
925         return (DWORD )ret;
926
927       }
928     case 0x0002: /* PageDecommit */
929       {
930         LPVOID address;
931         BOOL ret;
932         DWORD psize = getpagesize();
933         ULONG page = (ULONG) stack32_pop( context );
934         ULONG npages = (ULONG) stack32_pop( context );
935         ULONG flags = (ULONG) stack32_pop( context );
936
937         TRACE("PageDecommit: page: %08lx, npages: %08lx, flags: %08lx partial stub\n",
938               page, npages, flags );
939         address = (LPVOID )( page * psize );
940         ret = VirtualFree ( address, ( npages * psize ), MEM_DECOMMIT );
941         TRACE("PageDecommit: Returning: %s\n", ret ? "TRUE" : "FALSE" );
942         return ret;
943       }
944     case 0x000d: /* PageModifyPermissions */
945       {
946         DWORD pg_old_perm;
947         DWORD pg_new_perm;
948         DWORD virt_old_perm;
949         DWORD virt_new_perm;
950         MEMORY_BASIC_INFORMATION mbi;
951         LPVOID address;
952         DWORD psize = getpagesize();
953         ULONG page = stack32_pop ( context );
954         ULONG npages = stack32_pop ( context );
955         ULONG permand = stack32_pop ( context );
956         ULONG permor = stack32_pop ( context );
957
958         TRACE("PageModifyPermissions %08lx %08lx %08lx %08lx partial stub\n",
959               page, npages, permand, permor );
960         address = (LPVOID )( page * psize );
961
962         VirtualQuery ( address, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ));
963         virt_old_perm = mbi.Protect;
964
965         switch ( virt_old_perm & mbi.Protect ) {
966         case PAGE_READONLY:
967         case PAGE_EXECUTE:
968         case PAGE_EXECUTE_READ:
969           pg_old_perm = PC_USER;
970           break;
971         case PAGE_READWRITE:
972         case PAGE_WRITECOPY:
973         case PAGE_EXECUTE_READWRITE:
974         case PAGE_EXECUTE_WRITECOPY:
975           pg_old_perm = PC_USER | PC_WRITEABLE;
976           break;
977         case PAGE_NOACCESS:
978         default:
979           pg_old_perm = 0;
980           break;
981         }
982         pg_new_perm = pg_old_perm;
983         pg_new_perm &= permand & ~PC_STATIC;
984         pg_new_perm |= permor  & ~PC_STATIC;
985
986         virt_new_perm = ( virt_old_perm )  & ~0xff;
987         if ( pg_new_perm & PC_USER )
988         {
989           if ( pg_new_perm & PC_WRITEABLE )
990             virt_new_perm |= PAGE_EXECUTE_READWRITE;
991           else
992             virt_new_perm |= PAGE_EXECUTE_READ;
993         }
994
995         if ( ! VirtualProtect ( address, ( npages * psize ), virt_new_perm, &virt_old_perm ) ) {
996           ERR("Can't change page permissions for %08lx\n", (DWORD )address );
997           return 0xffffffff;
998         }
999         TRACE("Returning: %08lx\n", pg_old_perm );
1000         return pg_old_perm;
1001       }
1002     case 0x000a: /* PageFree */
1003       {
1004         BOOL ret;
1005         LPVOID hmem = (LPVOID) stack32_pop( context );
1006         DWORD flags = (DWORD ) stack32_pop( context );
1007
1008         TRACE("PageFree: hmem: %08lx, flags: %08lx partial stub\n",
1009               (DWORD )hmem, flags );
1010
1011         ret = VirtualFree ( hmem, 0, MEM_RELEASE );
1012         context->Eax = ret;
1013         TRACE("Returning: %d\n", ret );
1014
1015         return 0;
1016       }
1017     case 0x001e: /* GetDemandPageInfo */
1018       {
1019          DWORD dinfo = (DWORD)stack32_pop( context );
1020          DWORD flags = (DWORD)stack32_pop( context );
1021
1022          /* GetDemandPageInfo is supposed to fill out the struct at
1023           * "dinfo" with various low-level memory management information.
1024           * Apps are certainly not supposed to call this, although it's
1025           * demoed and documented by Pietrek on pages 441-443 of "Windows
1026           * 95 System Programming Secrets" if any program needs a real
1027           * implementation of this.
1028           */
1029
1030          FIXME("GetDemandPageInfo(%08lx %08lx): stub!\n", dinfo, flags);
1031
1032          return 0;
1033       }
1034     default:
1035         if (LOWORD(service) < N_VMM_SERVICE)
1036             FIXME( "Unimplemented service %s (%08lx)\n",
1037                           VMM_Service_Name[LOWORD(service)], service);
1038         else
1039             FIXME( "Unknown service %08lx\n", service);
1040         break;
1041     }
1042
1043     return 0xffffffff;  /* FIXME */
1044 }
1045
1046
1047 /********************************************************************************
1048  *      VxDCall_VWin32
1049  *
1050  *  Service numbers taken from page 448 of Pietrek's "Windows 95 System
1051  *  Programming Secrets".  Parameters from experimentation on real Win98.
1052  *
1053  */
1054
1055 static DWORD VxDCall_VWin32( DWORD service, CONTEXT86 *context )
1056 {
1057     switch ( LOWORD(service) )
1058     {
1059     case 0x0000: /* GetVersion */
1060     {
1061         DWORD vers = GetVersion();
1062         return (LOBYTE(vers) << 8) | HIBYTE(vers);
1063     }
1064     break;
1065
1066     case 0x0020: /* Get VMCPD Version */
1067     {
1068         DWORD parm = (DWORD) stack32_pop(context);
1069
1070         FIXME("Get VMCPD Version(%08lx): partial stub!\n", parm);
1071
1072         /* FIXME: This is what Win98 returns, it may
1073          *        not be correct in all situations.
1074          *        It makes Bleem! happy though.
1075          */
1076
1077         return 0x0405;
1078     }
1079
1080     case 0x0029: /* Int31/DPMI dispatch */
1081     {
1082         DWORD callnum = (DWORD) stack32_pop(context);
1083         DWORD parm    = (DWORD) stack32_pop(context);
1084
1085         TRACE("Int31/DPMI dispatch(%08lx)\n", callnum);
1086
1087         context->Eax = callnum;
1088         context->Ecx = parm;
1089         INSTR_CallBuiltinHandler( context, 0x31 );
1090
1091         return LOWORD(context->Eax);
1092     }
1093     break;
1094
1095     case 0x002a: /* Int41 dispatch - parm = int41 service number */
1096     {
1097         DWORD callnum = (DWORD) stack32_pop(context);
1098
1099         return callnum; /* FIXME: should really call INT_Int41Handler() */
1100     }
1101     break;
1102
1103     default:
1104       FIXME("Unknown VWin32 service %08lx\n", service);
1105       break;
1106     }
1107
1108     return 0xffffffff;
1109 }
1110
1111
1112 /***********************************************************************
1113  *              VxDCall0 (KERNEL32.1)
1114  *              VxDCall1 (KERNEL32.2)
1115  *              VxDCall2 (KERNEL32.3)
1116  *              VxDCall3 (KERNEL32.4)
1117  *              VxDCall4 (KERNEL32.5)
1118  *              VxDCall5 (KERNEL32.6)
1119  *              VxDCall6 (KERNEL32.7)
1120  *              VxDCall7 (KERNEL32.8)
1121  *              VxDCall8 (KERNEL32.9)
1122  */
1123 void VxDCall( DWORD service, CONTEXT86 *context )
1124 {
1125     DWORD ret;
1126
1127     TRACE( "(%08lx, ...)\n", service);
1128
1129     switch(HIWORD(service))
1130     {
1131     case 0x0001:  /* VMM */
1132         ret = VxDCall_VMM( service, context );
1133         break;
1134     case 0x002a:  /* VWIN32 */
1135         ret = VxDCall_VWin32( service, context );
1136         break;
1137     default:
1138         FIXME( "Unknown/unimplemented VxD (%08lx)\n", service);
1139         ret = 0xffffffff; /* FIXME */
1140         break;
1141     }
1142     context->Eax = ret;
1143 }
1144
1145
1146 /***********************************************************************
1147  *              OpenVxDHandle (KERNEL32.@)
1148  *
1149  *      This function is supposed to return the corresponding Ring 0
1150  *      ("kernel") handle for a Ring 3 handle in Win9x.
1151  *      Evidently, Wine will have problems with this. But we try anyway,
1152  *      maybe it helps...
1153  */
1154 HANDLE WINAPI OpenVxDHandle(HANDLE hHandleRing3)
1155 {
1156     FIXME( "(%p), stub! (returning Ring 3 handle instead of Ring 0)\n", hHandleRing3);
1157     return hHandleRing3;
1158 }