ntdll: Fix the return value of NtOpenKey for some invalid parameters.
[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     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
355
356     /* Length > sizeof(OBJECT_ATTRIBUTES) */
357     attr.Length *= 2;
358     status = pNtOpenKey(&key, am, &attr);
359     ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
360 }
361
362 static void test_NtCreateKey(void)
363 {
364     /*Create WineTest*/
365     OBJECT_ATTRIBUTES attr;
366     HANDLE key;
367     ACCESS_MASK am = GENERIC_ALL;
368     NTSTATUS status;
369
370     /* All NULL */
371     status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
372     ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
373        "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
374
375     /* Only the key */
376     status = pNtCreateKey(&key, 0, NULL, 0, 0, 0, 0);
377     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
378         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
379
380     /* Only accessmask */
381     status = pNtCreateKey(NULL, am, NULL, 0, 0, 0, 0);
382     ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
383        "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
384
385     /* Key and accessmask */
386     status = pNtCreateKey(&key, am, NULL, 0, 0, 0, 0);
387     ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
388         "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
389
390     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
391
392     /* Only attributes */
393     status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
394     ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
395
396     status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
397     ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
398
399     /* Length > sizeof(OBJECT_ATTRIBUTES) */
400     attr.Length *= 2;
401     status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
402     ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
403
404     pNtClose(key);
405 }
406
407 static void test_NtSetValueKey(void)
408 {
409     HANDLE key;
410     NTSTATUS status;
411     OBJECT_ATTRIBUTES attr;
412     ACCESS_MASK am = KEY_WRITE;
413     UNICODE_STRING ValName;
414     DWORD data = 711;
415
416     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
417     status = pNtOpenKey(&key, am, &attr);
418     ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
419
420     pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
421     status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &data, sizeof(data));
422     ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
423     pRtlFreeUnicodeString(&ValName);
424
425     pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
426     status = pNtSetValueKey(key, &ValName, 0, REG_SZ, (VOID*)stringW, STR_TRUNC_SIZE);
427     ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
428     pRtlFreeUnicodeString(&ValName);
429
430     pNtClose(key);
431 }
432
433 static void test_RtlOpenCurrentUser(void)
434 {
435     NTSTATUS status;
436     HANDLE handle;
437     status=pRtlOpenCurrentUser(KEY_READ, &handle);
438     ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
439     pNtClose(handle);
440 }
441
442 static void test_RtlCheckRegistryKey(void)
443 {
444     NTSTATUS status;
445
446     status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer);
447     ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08x\n", status);
448
449     status = pRtlCheckRegistryKey((RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL), winetestpath.Buffer);
450     ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08x\n", status);
451 }
452
453 static void test_NtFlushKey(void)
454 {
455     NTSTATUS status;
456     HANDLE hkey;
457     OBJECT_ATTRIBUTES attr;
458     ACCESS_MASK am = KEY_ALL_ACCESS;
459
460     status = pNtFlushKey(NULL);
461     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
462
463     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
464     pNtOpenKey(&hkey, am, &attr);
465
466     status = pNtFlushKey(hkey);
467     ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
468
469     pNtClose(hkey);
470 }
471
472 static void test_NtQueryValueKey(void)
473 {
474     HANDLE key;
475     NTSTATUS status;
476     OBJECT_ATTRIBUTES attr;
477     UNICODE_STRING ValName;
478     KEY_VALUE_BASIC_INFORMATION *basic_info;
479     KEY_VALUE_PARTIAL_INFORMATION *partial_info;
480     KEY_VALUE_FULL_INFORMATION *full_info;
481     DWORD len, expected;
482
483     pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
484
485     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
486     status = pNtOpenKey(&key, KEY_READ, &attr);
487     ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
488
489     len = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
490     basic_info = HeapAlloc(GetProcessHeap(), 0, len);
491     status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
492     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
493     ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
494     ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
495     ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
496     ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
497
498     basic_info = HeapReAlloc(GetProcessHeap(), 0, basic_info, len);
499     status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
500     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
501     ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
502     ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
503     ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
504     ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
505     ok(!memcmp(basic_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
506     HeapFree(GetProcessHeap(), 0, basic_info);
507
508     len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
509     partial_info = HeapAlloc(GetProcessHeap(), 0, len);
510     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
511     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
512     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
513     ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
514     ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
515     ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
516
517     partial_info = HeapReAlloc(GetProcessHeap(), 0, partial_info, len);
518     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
519     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
520     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
521     ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
522     ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
523     ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
524     ok(*(DWORD *)partial_info->Data == 711, "incorrect Data returned: 0x%x\n", *(DWORD *)partial_info->Data);
525     HeapFree(GetProcessHeap(), 0, partial_info);
526
527     len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
528     full_info = HeapAlloc(GetProcessHeap(), 0, len);
529     status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
530     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
531     ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
532     ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
533     ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
534     ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
535     ok(len == FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength,
536         "NtQueryValueKey returned wrong len %d\n", len);
537     len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength;
538
539     full_info = HeapReAlloc(GetProcessHeap(), 0, full_info, len);
540     status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
541     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
542     ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
543     ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
544     ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
545     ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
546     ok(!memcmp(full_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
547     ok(*(DWORD *)((char *)full_info + full_info->DataOffset) == 711, "incorrect Data returned: 0x%x\n",
548         *(DWORD *)((char *)full_info + full_info->DataOffset));
549     HeapFree(GetProcessHeap(), 0, full_info);
550
551     pRtlFreeUnicodeString(&ValName);
552     pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
553
554     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
555     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
556     partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
557     memset(partial_info, 0xbd, len+1);
558     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
559     ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
560     ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
561     ok(partial_info->Type == REG_SZ, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
562     ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
563     ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
564     ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
565
566     expected = len;
567     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len);
568     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
569     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
570     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len);
571     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
572     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
573     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len);
574     ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
575     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
576     status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len);
577     ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status);
578     ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
579
580     HeapFree(GetProcessHeap(), 0, partial_info);
581
582     pRtlFreeUnicodeString(&ValName);
583     pNtClose(key);
584 }
585
586 static void test_NtDeleteKey(void)
587 {
588     NTSTATUS status;
589     HANDLE hkey;
590     OBJECT_ATTRIBUTES attr;
591     ACCESS_MASK am = KEY_ALL_ACCESS;
592
593     status = pNtDeleteKey(NULL);
594     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
595
596     InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
597     status = pNtOpenKey(&hkey, am, &attr);
598
599     status = pNtDeleteKey(hkey);
600     ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
601 }
602
603 static void test_RtlpNtQueryValueKey(void)
604 {
605     NTSTATUS status;
606
607     status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL);
608     ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
609 }
610
611 static void test_symlinks(void)
612 {
613     static const WCHAR linkW[] = {'l','i','n','k',0};
614     static const WCHAR valueW[] = {'v','a','l','u','e',0};
615     static const WCHAR symlinkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
616     static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
617     static UNICODE_STRING null_str;
618     char buffer[1024];
619     KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
620     WCHAR *target;
621     UNICODE_STRING symlink_str, link_str, target_str, value_str;
622     HANDLE root, key, link;
623     OBJECT_ATTRIBUTES attr;
624     NTSTATUS status;
625     DWORD target_len, len, dw;
626
627     pRtlInitUnicodeString( &link_str, linkW );
628     pRtlInitUnicodeString( &symlink_str, symlinkW );
629     pRtlInitUnicodeString( &target_str, targetW + 1 );
630     pRtlInitUnicodeString( &value_str, valueW );
631
632     target_len = winetestpath.Length + sizeof(targetW);
633     target = pRtlAllocateHeap( GetProcessHeap(), 0, target_len + sizeof(targetW) /*for loop test*/ );
634     memcpy( target, winetestpath.Buffer, winetestpath.Length );
635     memcpy( target + winetestpath.Length/sizeof(WCHAR), targetW, sizeof(targetW) );
636
637     attr.Length = sizeof(attr);
638     attr.RootDirectory = 0;
639     attr.Attributes = 0;
640     attr.ObjectName = &winetestpath;
641     attr.SecurityDescriptor = NULL;
642     attr.SecurityQualityOfService = NULL;
643
644     status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
645     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
646
647     attr.RootDirectory = root;
648     attr.ObjectName = &link_str;
649     status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
650     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
651
652     /* REG_SZ is not allowed */
653     status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len );
654     ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
655     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
656     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
657     /* other values are not allowed */
658     status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
659     ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
660
661     /* try opening the target through the link */
662
663     attr.ObjectName = &link_str;
664     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
665     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
666
667     attr.ObjectName = &target_str;
668     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
669     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
670
671     dw = 0xbeef;
672     status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
673     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
674     pNtClose( key );
675
676     attr.ObjectName = &link_str;
677     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
678     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
679
680     len = sizeof(buffer);
681     status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
682     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
683     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
684
685     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
686     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
687
688     /* REG_LINK can be created in non-link keys */
689     status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
690     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
691     len = sizeof(buffer);
692     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
693     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
694     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
695         "wrong len %u\n", len );
696     status = pNtDeleteValueKey( key, &symlink_str );
697     ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status );
698
699     pNtClose( key );
700
701     attr.Attributes = 0;
702     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
703     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
704
705     len = sizeof(buffer);
706     status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
707     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
708     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
709
710     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
711     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
712     pNtClose( key );
713
714     /* now open the symlink itself */
715
716     attr.RootDirectory = root;
717     attr.Attributes = OBJ_OPENLINK;
718     attr.ObjectName = &link_str;
719     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
720     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
721
722     len = sizeof(buffer);
723     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
724     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
725     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
726         "wrong len %u\n", len );
727     pNtClose( key );
728
729     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
730     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
731     len = sizeof(buffer);
732     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
733     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
734     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
735         "wrong len %u\n", len );
736     pNtClose( key );
737
738     /* reopen the link from itself */
739
740     attr.RootDirectory = link;
741     attr.Attributes = OBJ_OPENLINK;
742     attr.ObjectName = &null_str;
743     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
744     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
745     len = sizeof(buffer);
746     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
747     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
748     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
749         "wrong len %u\n", len );
750     pNtClose( key );
751
752     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
753     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
754     len = sizeof(buffer);
755     status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
756     ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
757     ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
758         "wrong len %u\n", len );
759     pNtClose( key );
760
761     if (0)  /* crashes the Windows kernel in most versions */
762     {
763         attr.Attributes = 0;
764         status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
765         ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
766         len = sizeof(buffer);
767         status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
768         ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
769         pNtClose( key );
770
771         status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
772         ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
773         len = sizeof(buffer);
774         status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
775         ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
776         pNtClose( key );
777     }
778
779     /* target with terminating null doesn't work */
780     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len );
781     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
782     attr.RootDirectory = root;
783     attr.Attributes = 0;
784     attr.ObjectName = &link_str;
785     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
786     ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
787
788     /* relative symlink, works only on win2k */
789     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) );
790     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
791     attr.ObjectName = &link_str;
792     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
793     ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND,
794         "NtOpenKey wrong status 0x%08x\n", status );
795
796     status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
797     ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
798
799     status = pNtDeleteKey( link );
800     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
801     pNtClose( link );
802
803     attr.ObjectName = &target_str;
804     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
805     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
806     status = pNtDeleteKey( key );
807     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
808     pNtClose( key );
809
810     /* symlink loop */
811
812     status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
813     ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
814     memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) );
815     status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK,
816         target, target_len + sizeof(targetW) - sizeof(WCHAR) );
817     ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
818
819     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
820     ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG,
821         "NtOpenKey failed: 0x%08x\n", status );
822
823     attr.Attributes = OBJ_OPENLINK;
824     status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
825     ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
826     pNtClose( key );
827
828     status = pNtDeleteKey( link );
829     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
830     pNtClose( link );
831
832     status = pNtDeleteKey( root );
833     ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
834     pNtClose( root );
835
836     pRtlFreeHeap(GetProcessHeap(), 0, target);
837 }
838
839 START_TEST(reg)
840 {
841     static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
842     if(!InitFunctionPtrs())
843         return;
844     pRtlFormatCurrentUserKeyPath(&winetestpath);
845     winetestpath.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer,
846                            winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR));
847     winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR);
848
849     pRtlAppendUnicodeToString(&winetestpath, winetest);
850
851     test_NtCreateKey();
852     test_NtOpenKey();
853     test_NtSetValueKey();
854     test_RtlCheckRegistryKey();
855     test_RtlOpenCurrentUser();
856     test_RtlQueryRegistryValues();
857     test_RtlpNtQueryValueKey();
858     test_NtFlushKey();
859     test_NtQueryValueKey();
860     test_NtDeleteKey();
861     test_symlinks();
862
863     pRtlFreeUnicodeString(&winetestpath);
864
865     FreeLibrary(hntdll);
866 }