ntdll: Fix the return status of NtQueryValueKey for a too small buffer.
[wine] / dlls / ntdll / tests / reg.c
1 /* Unit test suite for Rtl* Registry API functions
2  *
3  * Copyright 2003 Thomas Mertes
4  * Copyright 2005 Brad DeMorrow
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTE: I don't test every RelativeTo value because it would be redundant, all calls go through
21  * helper function RTL_GetKeyHandle().--Brad DeMorrow
22  *
23  */
24
25 #include "ntdll_test.h"
26 #include "winternl.h"
27 #include "stdio.h"
28 #include "winnt.h"
29 #include "winnls.h"
30 #include "stdlib.h"
31
32 /* A test string */
33 static const WCHAR stringW[] = {'s', 't', 'r', 'i', 'n', 'g', 'W', 0};
34 /* A size, in bytes, short enough to cause truncation of the above */
35 #define STR_TRUNC_SIZE (sizeof(stringW)-2*sizeof(*stringW))
36
37 #ifndef __WINE_WINTERNL_H
38
39 /* RtlQueryRegistryValues structs and defines */
40 #define RTL_REGISTRY_ABSOLUTE             0
41 #define RTL_REGISTRY_SERVICES             1
42 #define RTL_REGISTRY_CONTROL              2
43 #define RTL_REGISTRY_WINDOWS_NT           3
44 #define RTL_REGISTRY_DEVICEMAP            4
45 #define RTL_REGISTRY_USER                 5
46
47 #define RTL_REGISTRY_HANDLE       0x40000000
48 #define RTL_REGISTRY_OPTIONAL     0x80000000
49
50 #define RTL_QUERY_REGISTRY_SUBKEY         0x00000001
51 #define RTL_QUERY_REGISTRY_TOPKEY         0x00000002
52 #define RTL_QUERY_REGISTRY_REQUIRED       0x00000004
53 #define RTL_QUERY_REGISTRY_NOVALUE        0x00000008
54 #define RTL_QUERY_REGISTRY_NOEXPAND       0x00000010
55 #define RTL_QUERY_REGISTRY_DIRECT         0x00000020
56 #define RTL_QUERY_REGISTRY_DELETE         0x00000040
57
58 typedef NTSTATUS (WINAPI *PRTL_QUERY_REGISTRY_ROUTINE)( PCWSTR  ValueName,
59                                                         ULONG  ValueType,
60                                                         PVOID  ValueData,
61                                                         ULONG  ValueLength,
62                                                         PVOID  Context,
63                                                         PVOID  EntryContext);
64
65 typedef struct _RTL_QUERY_REGISTRY_TABLE {
66   PRTL_QUERY_REGISTRY_ROUTINE  QueryRoutine;
67   ULONG  Flags;
68   PWSTR  Name;
69   PVOID  EntryContext;
70   ULONG  DefaultType;
71   PVOID  DefaultData;
72   ULONG  DefaultLength;
73 } RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;
74
75 typedef struct _KEY_VALUE_BASIC_INFORMATION {
76     ULONG TitleIndex;
77     ULONG Type;
78     ULONG NameLength;
79     WCHAR Name[1];
80 } KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;
81
82 typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
83     ULONG TitleIndex;
84     ULONG Type;
85     ULONG DataLength;
86     UCHAR Data[1];
87 } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
88
89 typedef struct _KEY_VALUE_FULL_INFORMATION {
90     ULONG TitleIndex;
91     ULONG Type;
92     ULONG DataOffset;
93     ULONG DataLength;
94     ULONG NameLength;
95     WCHAR Name[1];
96 } KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
97
98 typedef enum _KEY_VALUE_INFORMATION_CLASS {
99     KeyValueBasicInformation,
100     KeyValueFullInformation,
101     KeyValuePartialInformation,
102     KeyValueFullInformationAlign64,
103     KeyValuePartialInformationAlign64
104 } KEY_VALUE_INFORMATION_CLASS;
105
106 #define InitializeObjectAttributes(p,n,a,r,s) \
107     do { \
108         (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
109         (p)->RootDirectory = r; \
110         (p)->Attributes = a; \
111         (p)->ObjectName = n; \
112         (p)->SecurityDescriptor = s; \
113         (p)->SecurityQualityOfService = NULL; \
114     } while (0)
115
116 #endif
117
118 static NTSTATUS (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
119 static void     (WINAPI * pRtlInitUnicodeString)(PUNICODE_STRING,PCWSTR);
120 static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
121 static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
122 static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
123 static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
124 static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, PHANDLE);
125 static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
126 static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
127 static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
128 static NTSTATUS (WINAPI * pNtFlushKey)(HANDLE);
129 static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
130 static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
131                              ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
132                              PULONG dispos );
133 static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
134 static NTSTATUS (WINAPI * pNtSetValueKey)(HANDLE, const PUNICODE_STRING, ULONG,
135                                ULONG, const void*, ULONG  );
136 static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
137 static NTSTATUS (WINAPI * pRtlCreateUnicodeString)( PUNICODE_STRING, LPCWSTR);
138 static LPVOID   (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
139 static NTSTATUS (WINAPI * pRtlAppendUnicodeToString)(PUNICODE_STRING, PCWSTR);
140 static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRING, BOOL);
141 static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
142 static LPVOID   (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
143 static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
144 static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*,void *);
145
146 static HMODULE hntdll = 0;
147 static int CurrentTest = 0;
148 static UNICODE_STRING winetestpath;
149
150 #define NTDLL_GET_PROC(func) \
151     p ## func = (void*)GetProcAddress(hntdll, #func); \
152     if(!p ## func) { \
153         trace("GetProcAddress(%s) failed\n", #func); \
154         FreeLibrary(hntdll); \
155         return FALSE; \
156     }
157
158 static BOOL InitFunctionPtrs(void)
159 {
160     hntdll = LoadLibraryA("ntdll.dll");
161     if(!hntdll) {
162         trace("Could not load ntdll.dll\n");
163         return FALSE;
164     }
165     NTDLL_GET_PROC(RtlInitUnicodeString)
166     NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
167     NTDLL_GET_PROC(RtlCreateUnicodeString)
168     NTDLL_GET_PROC(RtlFreeUnicodeString)
169     NTDLL_GET_PROC(NtDeleteValueKey)
170     NTDLL_GET_PROC(RtlQueryRegistryValues)
171     NTDLL_GET_PROC(RtlCheckRegistryKey)
172     NTDLL_GET_PROC(RtlOpenCurrentUser)
173     NTDLL_GET_PROC(NtClose)
174     NTDLL_GET_PROC(NtDeleteValueKey)
175     NTDLL_GET_PROC(NtCreateKey)
176     NTDLL_GET_PROC(NtFlushKey)
177     NTDLL_GET_PROC(NtDeleteKey)
178     NTDLL_GET_PROC(NtQueryValueKey)
179     NTDLL_GET_PROC(NtSetValueKey)
180     NTDLL_GET_PROC(NtOpenKey)
181     NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
182     NTDLL_GET_PROC(RtlReAllocateHeap)
183     NTDLL_GET_PROC(RtlAppendUnicodeToString)
184     NTDLL_GET_PROC(RtlUnicodeStringToAnsiString)
185     NTDLL_GET_PROC(RtlFreeHeap)
186     NTDLL_GET_PROC(RtlAllocateHeap)
187     NTDLL_GET_PROC(RtlZeroMemory)
188     NTDLL_GET_PROC(RtlpNtQueryValueKey)
189     return TRUE;
190 }
191 #undef NTDLL_GET_PROC
192
193 static NTSTATUS WINAPI QueryRoutine (IN PCWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData,
194                               IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext)
195 {
196     NTSTATUS ret = STATUS_SUCCESS;
197     int ValueNameLength = 0;
198     LPSTR ValName = 0;
199     trace("**Test %d**\n", CurrentTest);
200
201     if(ValueName)
202     {
203         ValueNameLength = lstrlenW(ValueName);
204
205         ValName = pRtlAllocateHeap(GetProcessHeap(), 0, ValueNameLength);
206
207         WideCharToMultiByte(0, 0, ValueName, ValueNameLength+1,ValName, ValueNameLength, 0, 0);
208
209         trace("ValueName: %s\n", ValName);
210     }
211     else
212         trace("ValueName: (null)\n");
213
214     switch(ValueType)
215     {
216             case REG_NONE:
217                 trace("ValueType: REG_NONE\n");
218                 trace("ValueData: %p\n", ValueData);
219                 break;
220
221             case REG_BINARY:
222                 trace("ValueType: REG_BINARY\n");
223                 trace("ValueData: %p\n", ValueData);
224                 break;
225
226             case REG_SZ:
227                 trace("ValueType: REG_SZ\n");
228                 trace("ValueData: %s\n", (char*)ValueData);
229                 break;
230
231             case REG_MULTI_SZ:
232                 trace("ValueType: REG_MULTI_SZ\n");
233                 trace("ValueData: %s\n", (char*)ValueData);
234                 break;
235
236             case REG_EXPAND_SZ:
237                 trace("ValueType: REG_EXPAND_SZ\n");
238                 trace("ValueData: %s\n", (char*)ValueData);
239                 break;
240
241             case REG_DWORD:
242                 trace("ValueType: REG_DWORD\n");
243                 trace("ValueData: %p\n", ValueData);
244                 break;
245     };
246     trace("ValueLength: %d\n", (int)ValueLength);
247
248     if(CurrentTest == 0)
249         ok(1, "\n"); /*checks that QueryRoutine is called*/
250     if(CurrentTest > 7)
251         ok(!1, "Invalid Test Specified!\n");
252
253     CurrentTest++;
254
255     if(ValName)
256         pRtlFreeHeap(GetProcessHeap(), 0, ValName);
257
258     return ret;
259 }
260
261 static void test_RtlQueryRegistryValues(void)
262 {
263
264     /*
265     ******************************
266     *       QueryTable Flags     *
267     ******************************
268     *RTL_QUERY_REGISTRY_SUBKEY   * Name is the name of a subkey relative to Path
269     *RTL_QUERY_REGISTRY_TOPKEY   * Resets location to original RelativeTo and Path
270     *RTL_QUERY_REGISTRY_REQUIRED * Key required. returns STATUS_OBJECT_NAME_NOT_FOUND if not present
271     *RTL_QUERY_REGISTRY_NOVALUE  * We just want a call-back
272     *RTL_QUERY_REGISTRY_NOEXPAND * Don't expand the variables!
273     *RTL_QUERY_REGISTRY_DIRECT   * Results of query will be stored in EntryContext(QueryRoutine ignored)
274     *RTL_QUERY_REGISTRY_DELETE   * Delete value key after query
275     ******************************
276
277
278     **Test layout(numbered according to CurrentTest value)**
279     0)NOVALUE           Just make sure call-back works
280     1)Null Name         See if QueryRoutine is called for every value in current key
281     2)SUBKEY            See if we can use SUBKEY to change the current path on the fly
282     3)REQUIRED          Test for value that's not there
283     4)NOEXPAND          See if it will return multiple strings(no expand should split strings up)
284     5)DIRECT            Make it store data directly in EntryContext and not call QueryRoutine
285     6)DefaultType       Test return values when key isn't present
286     7)DefaultValue      Test Default Value returned with key isn't present(and no REQUIRED flag set)
287     8)DefaultLength     Test Default Length with DefaultType = REG_SZ
288    9)DefaultLength      Test Default Length with DefaultType = REG_MULTI_SZ
289    10)DefaultLength     Test Default Length with DefaultType = REG_EXPAND_SZ
290    11)DefaultData       Test whether DefaultData is used while DefaultType = REG_NONE(shouldn't be)
291    12)Delete            Try to delete value key
292
293     */
294     NTSTATUS status;
295     ULONG RelativeTo;
296
297     PRTL_QUERY_REGISTRY_TABLE QueryTable = NULL;
298     RelativeTo = RTL_REGISTRY_ABSOLUTE;/*Only using absolute - no need to test all relativeto variables*/
299
300     QueryTable = pRtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_QUERY_REGISTRY_TABLE)*26);
301
302     pRtlZeroMemory( QueryTable, sizeof(RTL_QUERY_REGISTRY_TABLE) * 26);
303
304     QueryTable[0].QueryRoutine = QueryRoutine;
305     QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOVALUE;
306     QueryTable[0].Name = NULL;
307     QueryTable[0].EntryContext = NULL;
308     QueryTable[0].DefaultType = REG_BINARY;
309     QueryTable[0].DefaultData = NULL;
310     QueryTable[0].DefaultLength = 100;
311
312     QueryTable[1].QueryRoutine = QueryRoutine;
313     QueryTable[1].Flags = 0;
314     QueryTable[1].Name = NULL;
315     QueryTable[1].EntryContext = 0;
316     QueryTable[1].DefaultType = REG_NONE;
317     QueryTable[1].DefaultData = NULL;
318     QueryTable[1].DefaultLength = 0;
319
320     QueryTable[2].QueryRoutine = NULL;
321     QueryTable[2].Flags = 0;
322     QueryTable[2].Name = NULL;
323     QueryTable[2].EntryContext = 0;
324     QueryTable[2].DefaultType = REG_NONE;
325     QueryTable[2].DefaultData = NULL;
326     QueryTable[2].DefaultLength = 0;
327
328     status = pRtlQueryRegistryValues(RelativeTo, winetestpath.Buffer, QueryTable, 0, 0);
329     ok(status == STATUS_SUCCESS, "RtlQueryRegistryValues return: 0x%08x\n", status);
330
331     pRtlFreeHeap(GetProcessHeap(), 0, QueryTable);
332 }
333
334 static void test_NtOpenKey(void)
335 {
336     HANDLE key;
337     NTSTATUS status;
338     OBJECT_ATTRIBUTES attr;
339     ACCESS_MASK am = KEY_READ;
340
341     /* All NULL */
342     status = pNtOpenKey(NULL, 0, NULL);
343     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
344
345     /* NULL attributes */
346     status = pNtOpenKey(&key, 0, NULL);
347     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
348         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
349
350     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
351
352     /* NULL key */
353     status = pNtOpenKey(NULL, am, &attr);
354     todo_wine
355         ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
356
357     /* Length > sizeof(OBJECT_ATTRIBUTES) */
358     attr.Length *= 2;
359     status = pNtOpenKey(&key, am, &attr);
360     todo_wine
361         ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
362 }
363
364 static void test_NtCreateKey(void)
365 {
366     /*Create WineTest*/
367     OBJECT_ATTRIBUTES attr;
368     HANDLE key;
369     ACCESS_MASK am = GENERIC_ALL;
370     NTSTATUS status;
371
372     /* All NULL */
373     status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
374     ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
375        "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
376
377     /* Only the key */
378     status = pNtCreateKey(&key, 0, NULL, 0, 0, 0, 0);
379     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
380         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
381
382     /* Only accessmask */
383     status = pNtCreateKey(NULL, am, NULL, 0, 0, 0, 0);
384     ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
385        "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
386
387     /* Key and accessmask */
388     status = pNtCreateKey(&key, am, NULL, 0, 0, 0, 0);
389     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
390         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
391
392     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
393
394     /* Only attributes */
395     status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
396     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
397
398     status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
399     ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
400
401     /* Length > sizeof(OBJECT_ATTRIBUTES) */
402     attr.Length *= 2;
403     status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
404     ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
405
406     pNtClose(key);
407 }
408
409 static void test_NtSetValueKey(void)
410 {
411     HANDLE key;
412     NTSTATUS status;
413     OBJECT_ATTRIBUTES attr;
414     ACCESS_MASK am = KEY_WRITE;
415     UNICODE_STRING ValName;
416     DWORD data = 711;
417
418     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
419     status = pNtOpenKey(&key, am, &attr);
420     ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
421
422     pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
423     status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &data, sizeof(data));
424     ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
425     pRtlFreeUnicodeString(&ValName);
426
427     pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
428     status = pNtSetValueKey(key, &ValName, 0, REG_SZ, (VOID*)stringW, STR_TRUNC_SIZE);
429     ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
430     pRtlFreeUnicodeString(&ValName);
431
432     pNtClose(key);
433 }
434
435 static void test_RtlOpenCurrentUser(void)
436 {
437     NTSTATUS status;
438     HANDLE handle;
439     status=pRtlOpenCurrentUser(KEY_READ, &handle);
440     ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
441     pNtClose(handle);
442 }
443
444 static void test_RtlCheckRegistryKey(void)
445 {
446     NTSTATUS status;
447
448     status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer);
449     ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08x\n", status);
450
451     status = pRtlCheckRegistryKey((RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL), winetestpath.Buffer);
452     ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08x\n", status);
453 }
454
455 static void test_NtFlushKey(void)
456 {
457     NTSTATUS status;
458     HANDLE hkey;
459     OBJECT_ATTRIBUTES attr;
460     ACCESS_MASK am = KEY_ALL_ACCESS;
461
462     status = pNtFlushKey(NULL);
463     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
464
465     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
466     pNtOpenKey(&hkey, am, &attr);
467
468     status = pNtFlushKey(hkey);
469     ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
470
471     pNtClose(hkey);
472 }
473
474 static void test_NtQueryValueKey(void)
475 {
476     HANDLE key;
477     NTSTATUS status;
478     OBJECT_ATTRIBUTES attr;
479     UNICODE_STRING ValName;
480     KEY_VALUE_BASIC_INFORMATION *basic_info;
481     KEY_VALUE_PARTIAL_INFORMATION *partial_info;
482     KEY_VALUE_FULL_INFORMATION *full_info;
483     DWORD len, expected;
484
485     pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
486
487     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
488     status = pNtOpenKey(&key, KEY_READ, &attr);
489     ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
490
491     len = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
492     basic_info = HeapAlloc(GetProcessHeap(), 0, len);
493     status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
494     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
495     ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
496     ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
497     ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
498     ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
499
500     basic_info = HeapReAlloc(GetProcessHeap(), 0, basic_info, len);
501     status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
502     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
503     ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
504     ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
505     ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
506     ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
507     ok(!memcmp(basic_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
508     HeapFree(GetProcessHeap(), 0, basic_info);
509
510     len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
511     partial_info = HeapAlloc(GetProcessHeap(), 0, len);
512     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
513     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
514     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
515     ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
516     ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
517     ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
518
519     partial_info = HeapReAlloc(GetProcessHeap(), 0, partial_info, len);
520     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
521     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
522     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
523     ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
524     ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
525     ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
526     ok(*(DWORD *)partial_info->Data == 711, "incorrect Data returned: 0x%x\n", *(DWORD *)partial_info->Data);
527     HeapFree(GetProcessHeap(), 0, partial_info);
528
529     len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
530     full_info = HeapAlloc(GetProcessHeap(), 0, len);
531     status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
532     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
533     ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
534     ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
535     ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
536     ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
537     ok(len == FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength,
538         "NtQueryValueKey returned wrong len %d\n", len);
539     len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength;
540
541     full_info = HeapReAlloc(GetProcessHeap(), 0, full_info, len);
542     status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
543     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
544     ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
545     ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
546     ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
547     ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
548     ok(!memcmp(full_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
549     ok(*(DWORD *)((char *)full_info + full_info->DataOffset) == 711, "incorrect Data returned: 0x%x\n",
550         *(DWORD *)((char *)full_info + full_info->DataOffset));
551     HeapFree(GetProcessHeap(), 0, full_info);
552
553     pRtlFreeUnicodeString(&ValName);
554     pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
555
556     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
557     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
558     partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
559     memset(partial_info, 0xbd, len+1);
560     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
561     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
562     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
563     ok(partial_info->Type == REG_SZ, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
564     ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
565     ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
566     ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
567
568     expected = len;
569     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len);
570     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
571     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
572     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len);
573     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
574     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
575     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len);
576     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
577     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
578     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len);
579     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status);
580     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
581
582     HeapFree(GetProcessHeap(), 0, partial_info);
583
584     pRtlFreeUnicodeString(&ValName);
585     pNtClose(key);
586 }
587
588 static void test_NtDeleteKey(void)
589 {
590     NTSTATUS status;
591     HANDLE hkey;
592     OBJECT_ATTRIBUTES attr;
593     ACCESS_MASK am = KEY_ALL_ACCESS;
594
595     status = pNtDeleteKey(NULL);
596     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
597
598     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
599     status = pNtOpenKey(&hkey, am, &attr);
600
601     status = pNtDeleteKey(hkey);
602     ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
603 }
604
605 static void test_RtlpNtQueryValueKey(void)
606 {
607     NTSTATUS status;
608
609     status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL);
610     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
611 }
612
613 static void test_symlinks(void)
614 {
615     static const WCHAR linkW[] = {'l','i','n','k',0};
616     static const WCHAR valueW[] = {'v','a','l','u','e',0};
617     static const WCHAR symlinkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
618     static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
619     static UNICODE_STRING null_str;
620     char buffer[1024];
621     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
622     WCHAR *target;
623     UNICODE_STRING symlink_str, link_str, target_str, value_str;
624     HANDLE root, key, link;
625     OBJECT_ATTRIBUTES attr;
626     NTSTATUS status;
627     DWORD target_len, len, dw;
628
629     pRtlInitUnicodeString( &link_str, linkW );
630     pRtlInitUnicodeString( &symlink_str, symlinkW );
631     pRtlInitUnicodeString( &target_str, targetW + 1 );
632     pRtlInitUnicodeString( &value_str, valueW );
633
634     target_len = winetestpath.Length + sizeof(targetW);
635     target = pRtlAllocateHeap( GetProcessHeap(), 0, target_len + sizeof(targetW) /*for loop test*/ );
636     memcpy( target, winetestpath.Buffer, winetestpath.Length );
637     memcpy( target + winetestpath.Length/sizeof(WCHAR), targetW, sizeof(targetW) );
638
639     attr.Length = sizeof(attr);
640     attr.RootDirectory = 0;
641     attr.Attributes = 0;
642     attr.ObjectName = &winetestpath;
643     attr.SecurityDescriptor = NULL;
644     attr.SecurityQualityOfService = NULL;
645
646     status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
647     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
648
649     attr.RootDirectory = root;
650     attr.ObjectName = &link_str;
651     status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
652     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
653
654     /* REG_SZ is not allowed */
655     status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len );
656     ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
657     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
658     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
659     /* other values are not allowed */
660     status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
661     ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
662
663     /* try opening the target through the link */
664
665     attr.ObjectName = &link_str;
666     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
667     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
668
669     attr.ObjectName = &target_str;
670     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
671     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
672
673     dw = 0xbeef;
674     status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
675     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
676     pNtClose( key );
677
678     attr.ObjectName = &link_str;
679     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
680     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
681
682     len = sizeof(buffer);
683     status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
684     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
685     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
686
687     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
688     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
689
690     /* REG_LINK can be created in non-link keys */
691     status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
692     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
693     len = sizeof(buffer);
694     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
695     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
696     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
697         "wrong len %u\n", len );
698     status = pNtDeleteValueKey( key, &symlink_str );
699     ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status );
700
701     pNtClose( key );
702
703     attr.Attributes = 0;
704     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
705     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
706
707     len = sizeof(buffer);
708     status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
709     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
710     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
711
712     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
713     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
714     pNtClose( key );
715
716     /* now open the symlink itself */
717
718     attr.RootDirectory = root;
719     attr.Attributes = OBJ_OPENLINK;
720     attr.ObjectName = &link_str;
721     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
722     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
723
724     len = sizeof(buffer);
725     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
726     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
727     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
728         "wrong len %u\n", len );
729     pNtClose( key );
730
731     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
732     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
733     len = sizeof(buffer);
734     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
735     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
736     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
737         "wrong len %u\n", len );
738     pNtClose( key );
739
740     /* reopen the link from itself */
741
742     attr.RootDirectory = link;
743     attr.Attributes = OBJ_OPENLINK;
744     attr.ObjectName = &null_str;
745     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
746     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
747     len = sizeof(buffer);
748     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
749     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
750     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
751         "wrong len %u\n", len );
752     pNtClose( key );
753
754     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
755     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
756     len = sizeof(buffer);
757     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
758     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
759     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
760         "wrong len %u\n", len );
761     pNtClose( key );
762
763     if (0)  /* crashes the Windows kernel in most versions */
764     {
765         attr.Attributes = 0;
766         status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
767         ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
768         len = sizeof(buffer);
769         status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
770         ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
771         pNtClose( key );
772
773         status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
774         ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
775         len = sizeof(buffer);
776         status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
777         ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
778         pNtClose( key );
779     }
780
781     /* target with terminating null doesn't work */
782     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len );
783     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
784     attr.RootDirectory = root;
785     attr.Attributes = 0;
786     attr.ObjectName = &link_str;
787     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
788     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
789
790     /* relative symlink, works only on win2k */
791     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) );
792     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
793     attr.ObjectName = &link_str;
794     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
795     ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND,
796         "NtOpenKey wrong status 0x%08x\n", status );
797
798     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
799     ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
800
801     status = pNtDeleteKey( link );
802     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
803     pNtClose( link );
804
805     attr.ObjectName = &target_str;
806     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
807     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
808     status = pNtDeleteKey( key );
809     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
810     pNtClose( key );
811
812     /* symlink loop */
813
814     status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
815     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
816     memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) );
817     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK,
818         target, target_len + sizeof(targetW) - sizeof(WCHAR) );
819     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
820
821     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
822     ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG,
823         "NtOpenKey failed: 0x%08x\n", status );
824
825     attr.Attributes = OBJ_OPENLINK;
826     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
827     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
828     pNtClose( key );
829
830     status = pNtDeleteKey( link );
831     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
832     pNtClose( link );
833
834     status = pNtDeleteKey( root );
835     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
836     pNtClose( root );
837
838     pRtlFreeHeap(GetProcessHeap(), 0, target);
839 }
840
841 START_TEST(reg)
842 {
843     static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
844     if(!InitFunctionPtrs())
845         return;
846     pRtlFormatCurrentUserKeyPath(&winetestpath);
847     winetestpath.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer,
848                            winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR));
849     winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR);
850
851     pRtlAppendUnicodeToString(&winetestpath, winetest);
852
853     test_NtCreateKey();
854     test_NtOpenKey();
855     test_NtSetValueKey();
856     test_RtlCheckRegistryKey();
857     test_RtlOpenCurrentUser();
858     test_RtlQueryRegistryValues();
859     test_RtlpNtQueryValueKey();
860     test_NtFlushKey();
861     test_NtQueryValueKey();
862     test_NtDeleteKey();
863     test_symlinks();
864
865     pRtlFreeUnicodeString(&winetestpath);
866
867     FreeLibrary(hntdll);
868 }