- define additional shell paths for CSIDL_... constants
[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 "wine/winbase16.h"
43 #include "kernel_private.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  *           VMM_RegFlushKey
311  */
312 static DWORD VMM_RegFlushKey( HKEY hkey )
313 {
314     return RtlNtStatusToDosError( NtFlushKey( hkey ) );
315 }
316
317
318 /******************************************************************************
319  *           VMM_RegDeleteKeyA
320  */
321 static DWORD VMM_RegDeleteKeyA( HKEY hkey, LPCSTR name )
322 {
323     DWORD ret;
324     HKEY tmp;
325
326     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
327
328     if (!name || !*name) return RtlNtStatusToDosError( NtDeleteKey( hkey ) );
329     if (!(ret = VMM_RegOpenKeyExA( hkey, name, 0, 0, &tmp )))
330     {
331         ret = RtlNtStatusToDosError( NtDeleteKey( tmp ) );
332         NtClose( tmp );
333     }
334     return ret;
335 }
336
337
338 /******************************************************************************
339  *           VMM_RegSetValueExA
340  */
341 static DWORD VMM_RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
342                                  CONST BYTE *data, DWORD count )
343 {
344     UNICODE_STRING nameW;
345     ANSI_STRING nameA;
346     WCHAR *dataW = NULL;
347     NTSTATUS status;
348
349     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
350
351     if (is_string(type))
352     {
353         DWORD lenW;
354
355         if (count)
356         {
357             /* if user forgot to count terminating null, add it (yes NT does this) */
358             if (data[count-1] && !data[count]) count++;
359         }
360         RtlMultiByteToUnicodeSize( &lenW, data, count );
361         if (!(dataW = HeapAlloc( GetProcessHeap(), 0, lenW ))) return ERROR_OUTOFMEMORY;
362         RtlMultiByteToUnicodeN( dataW, lenW, NULL, data, count );
363         count = lenW;
364         data = (BYTE *)dataW;
365     }
366
367     RtlInitAnsiString( &nameA, name );
368     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
369     {
370         status = NtSetValueKey( hkey, &nameW, 0, type, data, count );
371         RtlFreeUnicodeString( &nameW );
372     }
373     if (dataW) HeapFree( GetProcessHeap(), 0, dataW );
374     return RtlNtStatusToDosError( status );
375 }
376
377
378 /******************************************************************************
379  *           VMM_RegSetValueA
380  */
381 static DWORD VMM_RegSetValueA( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
382 {
383     HKEY subkey = hkey;
384     DWORD ret;
385
386     if (type != REG_SZ) return ERROR_INVALID_PARAMETER;
387
388     if (name && name[0])  /* need to create the subkey */
389     {
390         if ((ret = VMM_RegCreateKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret;
391     }
392     ret = VMM_RegSetValueExA( subkey, NULL, 0, REG_SZ, (LPBYTE)data, strlen(data)+1 );
393     if (subkey != hkey) NtClose( subkey );
394     return ret;
395 }
396
397
398 /******************************************************************************
399  *           VMM_RegDeleteValueA
400  */
401 static DWORD VMM_RegDeleteValueA( HKEY hkey, LPCSTR name )
402 {
403     UNICODE_STRING nameW;
404     STRING nameA;
405     NTSTATUS status;
406
407     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
408
409     RtlInitAnsiString( &nameA, name );
410     if (!(status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
411     {
412         status = NtDeleteValueKey( hkey, &nameW );
413         RtlFreeUnicodeString( &nameW );
414     }
415     return RtlNtStatusToDosError( status );
416 }
417
418
419 /******************************************************************************
420  *           VMM_RegQueryValueExA
421  */
422 static DWORD VMM_RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
423                                    LPBYTE data, LPDWORD count )
424 {
425     NTSTATUS status;
426     ANSI_STRING nameA;
427     UNICODE_STRING nameW;
428     DWORD total_size;
429     char buffer[256], *buf_ptr = buffer;
430     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
431     static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data );
432
433     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
434     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
435
436     RtlInitAnsiString( &nameA, name );
437     if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
438         return RtlNtStatusToDosError(status);
439
440     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
441                               buffer, sizeof(buffer), &total_size );
442     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
443
444     /* we need to fetch the contents for a string type even if not requested,
445      * because we need to compute the length of the ASCII string. */
446     if (data || is_string(info->Type))
447     {
448         /* retry with a dynamically allocated buffer */
449         while (status == STATUS_BUFFER_OVERFLOW)
450         {
451             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
452             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
453             {
454                 status = STATUS_NO_MEMORY;
455                 goto done;
456             }
457             info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr;
458             status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
459                                       buf_ptr, total_size, &total_size );
460         }
461
462         if (!status)
463         {
464             if (is_string(info->Type))
465             {
466                 DWORD len = WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
467                                                  (total_size - info_size) /sizeof(WCHAR),
468                                                  NULL, 0, NULL, NULL );
469                 if (data && len)
470                 {
471                     if (len > *count) status = STATUS_BUFFER_OVERFLOW;
472                     else
473                     {
474                         WideCharToMultiByte( CP_ACP, 0, (WCHAR *)(buf_ptr + info_size),
475                                              (total_size - info_size) /sizeof(WCHAR),
476                                              data, len, NULL, NULL );
477                         /* if the type is REG_SZ and data is not 0-terminated
478                          * and there is enough space in the buffer NT appends a \0 */
479                         if (len < *count && data[len-1]) data[len] = 0;
480                     }
481                 }
482                 total_size = len + info_size;
483             }
484             else if (data)
485             {
486                 if (total_size - info_size > *count) status = STATUS_BUFFER_OVERFLOW;
487                 else memcpy( data, buf_ptr + info_size, total_size - info_size );
488             }
489         }
490         else if (status != STATUS_BUFFER_OVERFLOW) goto done;
491     }
492
493     if (type) *type = info->Type;
494     if (count) *count = total_size - info_size;
495
496  done:
497     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
498     RtlFreeUnicodeString( &nameW );
499     return RtlNtStatusToDosError(status);
500 }
501
502
503 /******************************************************************************
504  *           VMM_RegQueryValueA
505  */
506 static DWORD VMM_RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count )
507 {
508     DWORD ret;
509     HKEY subkey = hkey;
510
511     if (name && name[0])
512     {
513         if ((ret = VMM_RegOpenKeyExA( hkey, name, 0, KEY_ALL_ACCESS, &subkey )) != ERROR_SUCCESS)
514             return ret;
515     }
516     ret = VMM_RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, count );
517     if (subkey != hkey) NtClose( subkey );
518     if (ret == ERROR_FILE_NOT_FOUND)
519     {
520         /* return empty string if default value not found */
521         if (data) *data = 0;
522         if (count) *count = 1;
523         ret = ERROR_SUCCESS;
524     }
525     return ret;
526 }
527
528
529 /******************************************************************************
530  *           VMM_RegEnumValueA
531  */
532 static DWORD VMM_RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
533                                 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
534 {
535     NTSTATUS status;
536     DWORD total_size;
537     char buffer[256], *buf_ptr = buffer;
538     KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;
539     static const int info_size = offsetof( KEY_VALUE_FULL_INFORMATION, Name );
540
541     TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
542           hkey, index, value, val_count, reserved, type, data, count );
543
544     /* NT only checks count, not val_count */
545     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
546     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
547
548     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
549     if (data) total_size += *count;
550     total_size = min( sizeof(buffer), total_size );
551
552     status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
553                                   buffer, total_size, &total_size );
554     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
555
556     /* we need to fetch the contents for a string type even if not requested,
557      * because we need to compute the length of the ASCII string. */
558     if (value || data || is_string(info->Type))
559     {
560         /* retry with a dynamically allocated buffer */
561         while (status == STATUS_BUFFER_OVERFLOW)
562         {
563             if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
564             if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
565                 return ERROR_NOT_ENOUGH_MEMORY;
566             info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr;
567             status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
568                                           buf_ptr, total_size, &total_size );
569         }
570
571         if (status) goto done;
572
573         if (is_string(info->Type))
574         {
575             DWORD len;
576             RtlUnicodeToMultiByteSize( &len, (WCHAR *)(buf_ptr + info->DataOffset),
577                                        total_size - info->DataOffset );
578             if (data && len)
579             {
580                 if (len > *count) status = STATUS_BUFFER_OVERFLOW;
581                 else
582                 {
583                     RtlUnicodeToMultiByteN( data, len, NULL, (WCHAR *)(buf_ptr + info->DataOffset),
584                                             total_size - info->DataOffset );
585                     /* if the type is REG_SZ and data is not 0-terminated
586                      * and there is enough space in the buffer NT appends a \0 */
587                     if (len < *count && data[len-1]) data[len] = 0;
588                 }
589             }
590             info->DataLength = len;
591         }
592         else if (data)
593         {
594             if (total_size - info->DataOffset > *count) status = STATUS_BUFFER_OVERFLOW;
595             else memcpy( data, buf_ptr + info->DataOffset, total_size - info->DataOffset );
596         }
597
598         if (value && !status)
599         {
600             DWORD len;
601
602             RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
603             if (len >= *val_count)
604             {
605                 status = STATUS_BUFFER_OVERFLOW;
606                 if (*val_count)
607                 {
608                     len = *val_count - 1;
609                     RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
610                     value[len] = 0;
611                 }
612             }
613             else
614             {
615                 RtlUnicodeToMultiByteN( value, len, NULL, info->Name, info->NameLength );
616                 value[len] = 0;
617                 *val_count = len;
618             }
619         }
620     }
621     else status = STATUS_SUCCESS;
622
623     if (type) *type = info->Type;
624     if (count) *count = info->DataLength;
625
626  done:
627     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
628     return RtlNtStatusToDosError(status);
629 }
630
631
632 /******************************************************************************
633  *           VMM_RegEnumKeyA
634  */
635 static DWORD VMM_RegEnumKeyA( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
636 {
637     NTSTATUS status;
638     char buffer[256], *buf_ptr = buffer;
639     KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)buffer;
640     DWORD total_size;
641
642     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
643
644     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
645                              buffer, sizeof(buffer), &total_size );
646
647     while (status == STATUS_BUFFER_OVERFLOW)
648     {
649         /* retry with a dynamically allocated buffer */
650         if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
651         if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size )))
652             return ERROR_NOT_ENOUGH_MEMORY;
653         info = (KEY_NODE_INFORMATION *)buf_ptr;
654         status = NtEnumerateKey( hkey, index, KeyNodeInformation,
655                                  buf_ptr, total_size, &total_size );
656     }
657
658     if (!status)
659     {
660         DWORD len;
661
662         RtlUnicodeToMultiByteSize( &len, info->Name, info->NameLength );
663         if (len >= name_len) status = STATUS_BUFFER_OVERFLOW;
664         else
665         {
666             RtlUnicodeToMultiByteN( name, len, NULL, info->Name, info->NameLength );
667             name[len] = 0;
668         }
669     }
670
671     if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr );
672     return RtlNtStatusToDosError( status );
673 }
674
675
676 /******************************************************************************
677  *           VMM_RegQueryInfoKeyA
678  *
679  * NOTE: This VxDCall takes only a subset of the parameters that the
680  * corresponding Win32 API call does. The implementation in Win95
681  * ADVAPI32 sets all output parameters not mentioned here to zero.
682  */
683 static DWORD VMM_RegQueryInfoKeyA( HKEY hkey, LPDWORD subkeys, LPDWORD max_subkey,
684                                    LPDWORD values, LPDWORD max_value, LPDWORD max_data )
685 {
686     NTSTATUS status;
687     KEY_FULL_INFORMATION info;
688     DWORD total_size;
689
690     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
691
692     status = NtQueryKey( hkey, KeyFullInformation, &info, sizeof(info), &total_size );
693     if (status && status != STATUS_BUFFER_OVERFLOW) return RtlNtStatusToDosError( status );
694
695     if (subkeys) *subkeys = info.SubKeys;
696     if (max_subkey) *max_subkey = info.MaxNameLen;
697     if (values) *values = info.Values;
698     if (max_value) *max_value = info.MaxValueNameLen;
699     if (max_data) *max_data = info.MaxValueDataLen;
700     return ERROR_SUCCESS;
701 }
702
703
704 /***********************************************************************
705  *           VxDCall_VMM
706  */
707 static DWORD VxDCall_VMM( DWORD service, CONTEXT86 *context )
708 {
709     switch ( LOWORD(service) )
710     {
711     case 0x0011:  /* RegOpenKey */
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_RegOpenKeyExA( hkey, lpszSubKey, 0, KEY_ALL_ACCESS, retkey );
717     }
718
719     case 0x0012:  /* RegCreateKey */
720     {
721         HKEY    hkey       = (HKEY)  stack32_pop( context );
722         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
723         PHKEY   retkey     = (PHKEY)stack32_pop( context );
724         return VMM_RegCreateKeyA( hkey, lpszSubKey, retkey );
725     }
726
727     case 0x0013:  /* RegCloseKey */
728     {
729         HKEY    hkey       = (HKEY)stack32_pop( context );
730         return VMM_RegCloseKey( hkey );
731     }
732
733     case 0x0014:  /* RegDeleteKey */
734     {
735         HKEY    hkey       = (HKEY)  stack32_pop( context );
736         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
737         return VMM_RegDeleteKeyA( hkey, lpszSubKey );
738     }
739
740     case 0x0015:  /* RegSetValue */
741     {
742         HKEY    hkey       = (HKEY)  stack32_pop( context );
743         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
744         DWORD   dwType     = (DWORD) stack32_pop( context );
745         LPCSTR  lpszData   = (LPCSTR)stack32_pop( context );
746         DWORD   cbData     = (DWORD) stack32_pop( context );
747         return VMM_RegSetValueA( hkey, lpszSubKey, dwType, lpszData, cbData );
748     }
749
750     case 0x0016:  /* RegDeleteValue */
751     {
752         HKEY    hkey       = (HKEY) stack32_pop( context );
753         LPSTR   lpszValue  = (LPSTR)stack32_pop( context );
754         return VMM_RegDeleteValueA( hkey, lpszValue );
755     }
756
757     case 0x0017:  /* RegQueryValue */
758     {
759         HKEY    hkey       = (HKEY)   stack32_pop( context );
760         LPSTR   lpszSubKey = (LPSTR)  stack32_pop( context );
761         LPSTR   lpszData   = (LPSTR)  stack32_pop( context );
762         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
763         return VMM_RegQueryValueA( hkey, lpszSubKey, lpszData, lpcbData );
764     }
765
766     case 0x0018:  /* RegEnumKey */
767     {
768         HKEY    hkey       = (HKEY) stack32_pop( context );
769         DWORD   iSubkey    = (DWORD)stack32_pop( context );
770         LPSTR   lpszName   = (LPSTR)stack32_pop( context );
771         DWORD   lpcchName  = (DWORD)stack32_pop( context );
772         return VMM_RegEnumKeyA( hkey, iSubkey, lpszName, lpcchName );
773     }
774
775     case 0x0019:  /* RegEnumValue */
776     {
777         HKEY    hkey       = (HKEY)   stack32_pop( context );
778         DWORD   iValue     = (DWORD)  stack32_pop( context );
779         LPSTR   lpszValue  = (LPSTR)  stack32_pop( context );
780         LPDWORD lpcchValue = (LPDWORD)stack32_pop( context );
781         LPDWORD lpReserved = (LPDWORD)stack32_pop( context );
782         LPDWORD lpdwType   = (LPDWORD)stack32_pop( context );
783         LPBYTE  lpbData    = (LPBYTE) stack32_pop( context );
784         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
785         return VMM_RegEnumValueA( hkey, iValue, lpszValue, lpcchValue,
786                                   lpReserved, lpdwType, lpbData, lpcbData );
787     }
788
789     case 0x001A:  /* RegQueryValueEx */
790     {
791         HKEY    hkey       = (HKEY)   stack32_pop( context );
792         LPSTR   lpszValue  = (LPSTR)  stack32_pop( context );
793         LPDWORD lpReserved = (LPDWORD)stack32_pop( context );
794         LPDWORD lpdwType   = (LPDWORD)stack32_pop( context );
795         LPBYTE  lpbData    = (LPBYTE) stack32_pop( context );
796         LPDWORD lpcbData   = (LPDWORD)stack32_pop( context );
797         return VMM_RegQueryValueExA( hkey, lpszValue, lpReserved,
798                                      lpdwType, lpbData, lpcbData );
799     }
800
801     case 0x001B:  /* RegSetValueEx */
802     {
803         HKEY    hkey       = (HKEY)  stack32_pop( context );
804         LPSTR   lpszValue  = (LPSTR) stack32_pop( context );
805         DWORD   dwReserved = (DWORD) stack32_pop( context );
806         DWORD   dwType     = (DWORD) stack32_pop( context );
807         LPBYTE  lpbData    = (LPBYTE)stack32_pop( context );
808         DWORD   cbData     = (DWORD) stack32_pop( context );
809         return VMM_RegSetValueExA( hkey, lpszValue, dwReserved,
810                                    dwType, lpbData, cbData );
811     }
812
813     case 0x001C:  /* RegFlushKey */
814     {
815         HKEY hkey = (HKEY)stack32_pop( context );
816         return VMM_RegFlushKey( hkey );
817     }
818
819     case 0x001D:  /* RegQueryInfoKey */
820     {
821         /* NOTE: This VxDCall takes only a subset of the parameters that the
822                  corresponding Win32 API call does. The implementation in Win95
823                  ADVAPI32 sets all output parameters not mentioned here to zero. */
824
825         HKEY    hkey              = (HKEY)   stack32_pop( context );
826         LPDWORD lpcSubKeys        = (LPDWORD)stack32_pop( context );
827         LPDWORD lpcchMaxSubKey    = (LPDWORD)stack32_pop( context );
828         LPDWORD lpcValues         = (LPDWORD)stack32_pop( context );
829         LPDWORD lpcchMaxValueName = (LPDWORD)stack32_pop( context );
830         LPDWORD lpcchMaxValueData = (LPDWORD)stack32_pop( context );
831         return VMM_RegQueryInfoKeyA( hkey, lpcSubKeys, lpcchMaxSubKey,
832                                      lpcValues, lpcchMaxValueName, lpcchMaxValueData );
833     }
834
835     case 0x0021:  /* RegLoadKey */
836     {
837         HKEY    hkey       = (HKEY)  stack32_pop( context );
838         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
839         LPCSTR  lpszFile   = (LPCSTR)stack32_pop( context );
840         FIXME("RegLoadKey(%p,%s,%s): stub\n",hkey, debugstr_a(lpszSubKey), debugstr_a(lpszFile));
841         return ERROR_SUCCESS;
842     }
843
844     case 0x0022:  /* RegUnLoadKey */
845     {
846         HKEY    hkey       = (HKEY)  stack32_pop( context );
847         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
848         FIXME("RegUnLoadKey(%p,%s): stub\n",hkey, debugstr_a(lpszSubKey));
849         return ERROR_SUCCESS;
850     }
851
852     case 0x0023:  /* RegSaveKey */
853     {
854         HKEY    hkey       = (HKEY)  stack32_pop( context );
855         LPCSTR  lpszFile   = (LPCSTR)stack32_pop( context );
856         LPSECURITY_ATTRIBUTES sa = (LPSECURITY_ATTRIBUTES)stack32_pop( context );
857         FIXME("RegSaveKey(%p,%s,%p): stub\n",hkey, debugstr_a(lpszFile),sa);
858         return ERROR_SUCCESS;
859     }
860
861 #if 0 /* Functions are not yet implemented in misc/registry.c */
862     case 0x0024:  /* RegRemapPreDefKey */
863     case 0x0026:  /* RegQueryMultipleValues */
864 #endif
865
866     case 0x0027:  /* RegReplaceKey */
867     {
868         HKEY    hkey       = (HKEY)  stack32_pop( context );
869         LPCSTR  lpszSubKey = (LPCSTR)stack32_pop( context );
870         LPCSTR  lpszNewFile= (LPCSTR)stack32_pop( context );
871         LPCSTR  lpszOldFile= (LPCSTR)stack32_pop( context );
872         FIXME("RegReplaceKey(%p,%s,%s,%s): stub\n", hkey, debugstr_a(lpszSubKey),
873               debugstr_a(lpszNewFile),debugstr_a(lpszOldFile));
874         return ERROR_SUCCESS;
875     }
876
877     case 0x0000: /* PageReserve */
878       {
879         LPVOID address;
880         LPVOID ret;
881         DWORD psize = getpagesize();
882         ULONG page   = (ULONG) stack32_pop( context );
883         ULONG npages = (ULONG) stack32_pop( context );
884         ULONG flags  = (ULONG) stack32_pop( context );
885
886         TRACE("PageReserve: page: %08lx, npages: %08lx, flags: %08lx partial stub!\n",
887               page, npages, flags );
888
889         if ( page == PR_SYSTEM ) {
890           ERR("Can't reserve ring 1 memory\n");
891           return -1;
892         }
893         /* FIXME: This has to be handled separately for the separate
894            address-spaces we now have */
895         if ( page == PR_PRIVATE || page == PR_SHARED ) page = 0;
896         /* FIXME: Handle flags in some way */
897         address = (LPVOID )(page * psize);
898         ret = VirtualAlloc ( address, ( npages * psize ), MEM_RESERVE, 0 );
899         TRACE("PageReserve: returning: %08lx\n", (DWORD )ret );
900         if ( ret == NULL )
901           return -1;
902         else
903           return (DWORD )ret;
904       }
905
906     case 0x0001: /* PageCommit */
907       {
908         LPVOID address;
909         LPVOID ret;
910         DWORD virt_perm;
911         DWORD psize = getpagesize();
912         ULONG page   = (ULONG) stack32_pop( context );
913         ULONG npages = (ULONG) stack32_pop( context );
914         ULONG hpd  = (ULONG) stack32_pop( context );
915         ULONG pagerdata   = (ULONG) stack32_pop( context );
916         ULONG flags  = (ULONG) stack32_pop( context );
917
918         TRACE("PageCommit: page: %08lx, npages: %08lx, hpd: %08lx pagerdata: "
919               "%08lx, flags: %08lx partial stub\n",
920               page, npages, hpd, pagerdata, flags );
921
922         if ( flags & PC_USER )
923           if ( flags & PC_WRITEABLE )
924             virt_perm = PAGE_EXECUTE_READWRITE;
925           else
926             virt_perm = PAGE_EXECUTE_READ;
927         else
928           virt_perm = PAGE_NOACCESS;
929
930         address = (LPVOID )(page * psize);
931         ret = VirtualAlloc ( address, ( npages * psize ), MEM_COMMIT, virt_perm );
932         TRACE("PageCommit: Returning: %08lx\n", (DWORD )ret );
933         return (DWORD )ret;
934
935       }
936     case 0x0002: /* PageDecommit */
937       {
938         LPVOID address;
939         BOOL ret;
940         DWORD psize = getpagesize();
941         ULONG page = (ULONG) stack32_pop( context );
942         ULONG npages = (ULONG) stack32_pop( context );
943         ULONG flags = (ULONG) stack32_pop( context );
944
945         TRACE("PageDecommit: page: %08lx, npages: %08lx, flags: %08lx partial stub\n",
946               page, npages, flags );
947         address = (LPVOID )( page * psize );
948         ret = VirtualFree ( address, ( npages * psize ), MEM_DECOMMIT );
949         TRACE("PageDecommit: Returning: %s\n", ret ? "TRUE" : "FALSE" );
950         return ret;
951       }
952     case 0x000d: /* PageModifyPermissions */
953       {
954         DWORD pg_old_perm;
955         DWORD pg_new_perm;
956         DWORD virt_old_perm;
957         DWORD virt_new_perm;
958         MEMORY_BASIC_INFORMATION mbi;
959         LPVOID address;
960         DWORD psize = getpagesize();
961         ULONG page = stack32_pop ( context );
962         ULONG npages = stack32_pop ( context );
963         ULONG permand = stack32_pop ( context );
964         ULONG permor = stack32_pop ( context );
965
966         TRACE("PageModifyPermissions %08lx %08lx %08lx %08lx partial stub\n",
967               page, npages, permand, permor );
968         address = (LPVOID )( page * psize );
969
970         VirtualQuery ( address, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ));
971         virt_old_perm = mbi.Protect;
972
973         switch ( virt_old_perm & mbi.Protect ) {
974         case PAGE_READONLY:
975         case PAGE_EXECUTE:
976         case PAGE_EXECUTE_READ:
977           pg_old_perm = PC_USER;
978           break;
979         case PAGE_READWRITE:
980         case PAGE_WRITECOPY:
981         case PAGE_EXECUTE_READWRITE:
982         case PAGE_EXECUTE_WRITECOPY:
983           pg_old_perm = PC_USER | PC_WRITEABLE;
984           break;
985         case PAGE_NOACCESS:
986         default:
987           pg_old_perm = 0;
988           break;
989         }
990         pg_new_perm = pg_old_perm;
991         pg_new_perm &= permand & ~PC_STATIC;
992         pg_new_perm |= permor  & ~PC_STATIC;
993
994         virt_new_perm = ( virt_old_perm )  & ~0xff;
995         if ( pg_new_perm & PC_USER )
996         {
997           if ( pg_new_perm & PC_WRITEABLE )
998             virt_new_perm |= PAGE_EXECUTE_READWRITE;
999           else
1000             virt_new_perm |= PAGE_EXECUTE_READ;
1001         }
1002
1003         if ( ! VirtualProtect ( address, ( npages * psize ), virt_new_perm, &virt_old_perm ) ) {
1004           ERR("Can't change page permissions for %08lx\n", (DWORD )address );
1005           return 0xffffffff;
1006         }
1007         TRACE("Returning: %08lx\n", pg_old_perm );
1008         return pg_old_perm;
1009       }
1010     case 0x000a: /* PageFree */
1011       {
1012         BOOL ret;
1013         LPVOID hmem = (LPVOID) stack32_pop( context );
1014         DWORD flags = (DWORD ) stack32_pop( context );
1015
1016         TRACE("PageFree: hmem: %08lx, flags: %08lx partial stub\n",
1017               (DWORD )hmem, flags );
1018
1019         ret = VirtualFree ( hmem, 0, MEM_RELEASE );
1020         context->Eax = ret;
1021         TRACE("Returning: %d\n", ret );
1022
1023         return 0;
1024       }
1025     case 0x001e: /* GetDemandPageInfo */
1026       {
1027          DWORD dinfo = (DWORD)stack32_pop( context );
1028          DWORD flags = (DWORD)stack32_pop( context );
1029
1030          /* GetDemandPageInfo is supposed to fill out the struct at
1031           * "dinfo" with various low-level memory management information.
1032           * Apps are certainly not supposed to call this, although it's
1033           * demoed and documented by Pietrek on pages 441-443 of "Windows
1034           * 95 System Programming Secrets" if any program needs a real
1035           * implementation of this.
1036           */
1037
1038          FIXME("GetDemandPageInfo(%08lx %08lx): stub!\n", dinfo, flags);
1039
1040          return 0;
1041       }
1042     default:
1043         if (LOWORD(service) < N_VMM_SERVICE)
1044             FIXME( "Unimplemented service %s (%08lx)\n",
1045                           VMM_Service_Name[LOWORD(service)], service);
1046         else
1047             FIXME( "Unknown service %08lx\n", service);
1048         break;
1049     }
1050
1051     return 0xffffffff;  /* FIXME */
1052 }
1053
1054
1055 /********************************************************************************
1056  *      VxDCall_VWin32
1057  *
1058  *  Service numbers taken from page 448 of Pietrek's "Windows 95 System
1059  *  Programming Secrets".  Parameters from experimentation on real Win98.
1060  *
1061  */
1062
1063 static DWORD VxDCall_VWin32( DWORD service, CONTEXT86 *context )
1064 {
1065     switch ( LOWORD(service) )
1066     {
1067     case 0x0000: /* GetVersion */
1068     {
1069         DWORD vers = GetVersion();
1070         return (LOBYTE(vers) << 8) | HIBYTE(vers);
1071     }
1072     break;
1073
1074     case 0x0020: /* Get VMCPD Version */
1075     {
1076         DWORD parm = (DWORD) stack32_pop(context);
1077
1078         FIXME("Get VMCPD Version(%08lx): partial stub!\n", parm);
1079
1080         /* FIXME: This is what Win98 returns, it may
1081          *        not be correct in all situations.
1082          *        It makes Bleem! happy though.
1083          */
1084
1085         return 0x0405;
1086     }
1087
1088     case 0x0029: /* Int31/DPMI dispatch */
1089     {
1090         DWORD callnum = (DWORD) stack32_pop(context);
1091         DWORD parm    = (DWORD) stack32_pop(context);
1092
1093         TRACE("Int31/DPMI dispatch(%08lx)\n", callnum);
1094
1095         context->Eax = callnum;
1096         context->Ecx = parm;
1097         INSTR_CallBuiltinHandler( context, 0x31 );
1098
1099         return LOWORD(context->Eax);
1100     }
1101     break;
1102
1103     case 0x002a: /* Int41 dispatch - parm = int41 service number */
1104     {
1105         DWORD callnum = (DWORD) stack32_pop(context);
1106
1107         return callnum; /* FIXME: should really call INT_Int41Handler() */
1108     }
1109     break;
1110
1111     default:
1112       FIXME("Unknown VWin32 service %08lx\n", service);
1113       break;
1114     }
1115
1116     return 0xffffffff;
1117 }
1118
1119
1120 /***********************************************************************
1121  *              VxDCall0 (KERNEL32.1)
1122  *              VxDCall1 (KERNEL32.2)
1123  *              VxDCall2 (KERNEL32.3)
1124  *              VxDCall3 (KERNEL32.4)
1125  *              VxDCall4 (KERNEL32.5)
1126  *              VxDCall5 (KERNEL32.6)
1127  *              VxDCall6 (KERNEL32.7)
1128  *              VxDCall7 (KERNEL32.8)
1129  *              VxDCall8 (KERNEL32.9)
1130  */
1131 void VxDCall( DWORD service, CONTEXT86 *context )
1132 {
1133     DWORD ret;
1134
1135     TRACE( "(%08lx, ...)\n", service);
1136
1137     switch(HIWORD(service))
1138     {
1139     case 0x0001:  /* VMM */
1140         ret = VxDCall_VMM( service, context );
1141         break;
1142     case 0x002a:  /* VWIN32 */
1143         ret = VxDCall_VWin32( service, context );
1144         break;
1145     default:
1146         FIXME( "Unknown/unimplemented VxD (%08lx)\n", service);
1147         ret = 0xffffffff; /* FIXME */
1148         break;
1149     }
1150     context->Eax = ret;
1151 }
1152
1153
1154 /***********************************************************************
1155  *              OpenVxDHandle (KERNEL32.@)
1156  *
1157  *      This function is supposed to return the corresponding Ring 0
1158  *      ("kernel") handle for a Ring 3 handle in Win9x.
1159  *      Evidently, Wine will have problems with this. But we try anyway,
1160  *      maybe it helps...
1161  */
1162 HANDLE WINAPI OpenVxDHandle(HANDLE hHandleRing3)
1163 {
1164     FIXME( "(%p), stub! (returning Ring 3 handle instead of Ring 0)\n", hHandleRing3);
1165     return hHandleRing3;
1166 }