1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for NtSetValueKey
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 #include <winreg.h>
11 
12 START_TEST(NtSetValueKey)
13 {
14     NTSTATUS Status;
15     HANDLE ParentKeyHandle;
16     HANDLE KeyHandle;
17     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SOFTWARE\\ntdll-apitest-NtSetValueKey");
18     OBJECT_ATTRIBUTES ObjectAttributes;
19     UNICODE_STRING ValueName;
20     WCHAR Default[] = L"Default";
21     WCHAR Hello[] = L"Hello";
22     WCHAR Empty[] = L"";
23     NTSTATUS QueryStatus;
24     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
25     ULONG PartialInfoLength;
26     ULONG ResultLength;
27     const struct
28     {
29         ULONG Type;
30         PVOID Data;
31         ULONG DataSize;
32         NTSTATUS StatusExisting;
33         NTSTATUS StatusNew;
34         NTSTATUS StatusExisting2;
35         NTSTATUS StatusNew2;
36     } Tests[] =
37     {
38         { REG_NONE,   NULL,                 0,             STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Empty REG_NONE value */
39         { REG_SZ,     Hello,                sizeof(Hello), STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Regular string */
40         { REG_SZ,     Empty,                sizeof(Empty), STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Empty string */
41         { REG_SZ,     NULL,                 0,             STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Zero length */
42         { REG_SZ,     Hello,                0,             STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Zero length, non-null data */
43         { REG_SZ,     (PVOID)(LONG_PTR)-4,  0,             STATUS_SUCCESS,                STATUS_SUCCESS                }, /* Zero length, kernel data */
44         { REG_SZ,     NULL,                 1,             STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION       }, /* Non-zero length (odd), null data */
45         { REG_SZ,     NULL,                 2,             STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION       }, /* Non-zero length (even), null data */
46         { REG_SZ,     NULL,                 4,             STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION       }, /* CM_KEY_VALUE_SMALL, null data */
47         { REG_SZ,     NULL,                 5,             STATUS_INVALID_PARAMETER,      STATUS_ACCESS_VIOLATION,         /* CM_KEY_VALUE_SMALL+1, null data */
48                                                            STATUS_ACCESS_VIOLATION,       STATUS_INSUFFICIENT_RESOURCES },        /* win7 */
49         { REG_SZ,     NULL,                 6,             STATUS_INVALID_PARAMETER,      STATUS_ACCESS_VIOLATION,         /* CM_KEY_VALUE_SMALL+2, null data */
50                                                            STATUS_ACCESS_VIOLATION,       STATUS_INSUFFICIENT_RESOURCES },        /* win7 */
51         { REG_SZ,     NULL,                 0x7fff0000,    STATUS_INVALID_PARAMETER,      STATUS_INSUFFICIENT_RESOURCES,   /* MI_USER_PROBE_ADDRESS, null data */
52                                                            STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES },        /* win7 */
53         { REG_SZ,     NULL,                 0x7fff0001,    STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION,         /* MI_USER_PROBE_ADDRESS+1, null data */
54                                                            STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES },        /* win7 */
55         { REG_SZ,     NULL,                 0x7fffffff,    STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION,         /* <2GB, null data */
56                                                            STATUS_INVALID_PARAMETER,      STATUS_INVALID_PARAMETER      },        /* win7 */
57         { REG_SZ,     NULL,                 0x80000000,    STATUS_ACCESS_VIOLATION,       STATUS_ACCESS_VIOLATION,         /* 2GB, null data */
58                                                            STATUS_INVALID_PARAMETER,      STATUS_INVALID_PARAMETER      },        /* win7 */
59         { REG_BINARY, NULL,                 5,             STATUS_INVALID_PARAMETER,      STATUS_ACCESS_VIOLATION,         /* ROSTESTS-200 */
60                                                            STATUS_ACCESS_VIOLATION,       STATUS_INSUFFICIENT_RESOURCES },        /* win7 */
61     };
62     ULONG i;
63 
64     Status = RtlOpenCurrentUser(READ_CONTROL, &ParentKeyHandle);
65     ok(Status == STATUS_SUCCESS, "RtlOpenCurrentUser returned %lx\n", Status);
66     if (!NT_SUCCESS(Status))
67     {
68         skip("No user key handle\n");
69         return;
70     }
71 
72     InitializeObjectAttributes(&ObjectAttributes,
73                                &KeyName,
74                                OBJ_CASE_INSENSITIVE,
75                                ParentKeyHandle,
76                                NULL);
77     Status = NtCreateKey(&KeyHandle,
78                          KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE,
79                          &ObjectAttributes,
80                          0,
81                          NULL,
82                          REG_OPTION_VOLATILE,
83                          NULL);
84     ok(Status == STATUS_SUCCESS, "NtCreateKey returned %lx\n", Status);
85     if (!NT_SUCCESS(Status))
86     {
87         NtClose(ParentKeyHandle);
88         skip("No key handle\n");
89         return;
90     }
91 
92     PartialInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[128]);
93     PartialInfo = HeapAlloc(GetProcessHeap(), 0, PartialInfoLength);
94     if (PartialInfo == NULL)
95     {
96         NtDeleteKey(KeyHandle);
97         NtClose(KeyHandle);
98         NtClose(ParentKeyHandle);
99         skip("No key handle\n");
100         return;
101     }
102 
103     for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
104     {
105         /*
106          * Existing value
107          */
108         /* Make sure it exists */
109         RtlInitUnicodeString(&ValueName, L"ExistingValue");
110         Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, Default, sizeof(Default));
111         ok(Status == STATUS_SUCCESS, "[%lu] NtSetValueKey failed with %lx", i, Status);
112 
113         /* Set it */
114         Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
115         if (Status == Tests[i].StatusExisting2)
116             ok(Status == Tests[i].StatusExisting || Status == Tests[i].StatusExisting2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
117                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting, Tests[i].StatusExisting2);
118         else
119             ok(Status == Tests[i].StatusExisting, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
120                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting);
121 
122         /* Check it */
123         RtlZeroMemory(PartialInfo, PartialInfoLength);
124         QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
125         ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
126            Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
127         if (NT_SUCCESS(QueryStatus))
128         {
129             if (NT_SUCCESS(Status))
130             {
131                 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
132                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
133                 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
134                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
135                 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
136                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
137                 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
138                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
139             }
140             else
141             {
142                 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
143                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
144                 ok(PartialInfo->Type == REG_SZ, "[%lu, %p, %lu] Type = %lu\n",
145                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
146                 ok(PartialInfo->DataLength == sizeof(Default), "[%lu, %p, %lu] DataLength = %lu\n",
147                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
148                 ok(!memcmp(PartialInfo->Data, Default, sizeof(Default)), "[%lu, %p, %lu] Data does not match default\n",
149                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
150             }
151         }
152 
153         /*
154          * New value
155          */
156         /* Make sure it doesn't exist */
157         RtlInitUnicodeString(&ValueName, L"NewValue");
158         Status = NtDeleteValueKey(KeyHandle, &ValueName);
159         ok(Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND,
160            "[%lu] NtDeleteValueKey failed with %lx", i, Status);
161 
162         /* Set it */
163         Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
164         if (Tests[i].StatusNew2)
165             ok(Status == Tests[i].StatusNew || Status == Tests[i].StatusNew2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
166                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew, Tests[i].StatusNew2);
167         else
168             ok(Status == Tests[i].StatusNew, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
169                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew);
170 
171         /* Check it */
172         RtlZeroMemory(PartialInfo, PartialInfoLength);
173         QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
174         if (NT_SUCCESS(Status))
175         {
176             ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
177                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
178             if (NT_SUCCESS(QueryStatus))
179             {
180                 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
181                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
182                 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
183                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
184                 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
185                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
186                 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
187                    Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
188             }
189         }
190         else
191         {
192             ok(QueryStatus == STATUS_OBJECT_NAME_NOT_FOUND, "[%lu, %p, %lu] QueryStatus = %lx\n",
193                Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
194         }
195     }
196 
197     HeapFree(GetProcessHeap(), 0, PartialInfo);
198     Status = NtDeleteKey(KeyHandle);
199     ok(Status == STATUS_SUCCESS, "NtDeleteKey returned %lx\n", Status);
200     Status = NtClose(KeyHandle);
201     ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
202     Status = NtClose(ParentKeyHandle);
203     ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
204 }
205