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