1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for RtlQueryRegistryValues
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #define KMT_EMULATE_KERNEL
9 #include <kmt_test.h>
10 
11 #ifndef RTL_NUMBER_OF
12 #define RTL_NUMBER_OF(x) (sizeof(x) / sizeof(x[0]))
13 #endif
14 
15 typedef struct
16 {
17     PCWSTR ValueName;
18     ULONG ValueType;
19     PVOID ValueData;
20     ULONG ValueLength;
21 } EXPECTED_VALUE, *PEXPECTED_VALUE;
22 
23 typedef struct
24 {
25     ULONG Count;
26     ULONG CurrentIndex;
27     EXPECTED_VALUE Values[20];
28 } EXPECTED_VALUES, *PEXPECTED_VALUES;
29 
30 //static RTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
31 static
32 NTSTATUS
33 NTAPI
34 QueryRoutine(
35     _In_ PWSTR ValueName,
36     _In_ ULONG ValueType,
37     _In_ PVOID ValueData,
38     _In_ ULONG ValueLength,
39     _In_ PVOID Context,
40     _In_ PVOID EntryContext)
41 {
42     PEXPECTED_VALUES ExpectedValues = Context;
43     PEXPECTED_VALUE Expected;
44     SIZE_T EqualBytes;
45 
46     ok(ExpectedValues->CurrentIndex < ExpectedValues->Count,
47        "Call number %lu, expected only %lu\n",
48        ExpectedValues->CurrentIndex, ExpectedValues->Count);
49     if (!skip(ExpectedValues->CurrentIndex < ExpectedValues->Count, "Out of bounds\n"))
50     {
51         Expected = &ExpectedValues->Values[ExpectedValues->CurrentIndex];
52         if (EntryContext)
53             ok_eq_pointer(EntryContext, Expected);
54         ok_eq_wstr(ValueName, Expected->ValueName);
55         ok_eq_ulong(ValueType, Expected->ValueType);
56         ok_eq_ulong(ValueLength, Expected->ValueLength);
57         EqualBytes = RtlCompareMemory(ValueData,
58                                       Expected->ValueData,
59                                       min(ValueLength, Expected->ValueLength));
60         ok_eq_size(EqualBytes, Expected->ValueLength);
61     }
62 
63     ExpectedValues->CurrentIndex++;
64     return STATUS_SUCCESS;
65 }
66 
67 static
68 VOID
69 TestRtlQueryRegistryValues(
70     _In_ HANDLE KeyHandle)
71 {
72     NTSTATUS Status;
73     UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"TestValue");
74     RTL_QUERY_REGISTRY_TABLE QueryTable[] =
75     {
76         { QueryRoutine, 0, L"TestValue", NULL, REG_NONE, NULL, 0 },
77         { NULL }
78     };
79     EXPECTED_VALUES Expected;
80     typedef struct
81     {
82         PWSTR Str;
83         ULONG Len;
84     } STR_AND_LEN;
85 #define CONST_STR_AND_LEN(d) { (d), sizeof(d) }
86 #define CSAL CONST_STR_AND_LEN
87 
88 #define NO_AUTO_LEN     1
89 #define NO_DEFAULT      2
90 #define AUTO_DIFFERS    4
91 #define DEFAULT_DIFFERS 8
92     struct
93     {
94         STR_AND_LEN Value;
95         ULONG ExpectedCount;
96         STR_AND_LEN Expected[20];
97         ULONG Flags;
98         ULONG DefaultExpectedCount;
99         STR_AND_LEN DefaultExpected[20];
100 
101     } Tests[] =
102     {
103         { { NULL, 0 },                      0, { { NULL, 0 } },                                     NO_AUTO_LEN | NO_DEFAULT },
104         { CSAL(L""),                        0, { { NULL, 0 } },                                     NO_AUTO_LEN },
105         { CSAL(L"\0"),                      1, { CSAL(L"") },
106           AUTO_DIFFERS | DEFAULT_DIFFERS,   0, { { NULL, 0 } } },
107         { CSAL(L"String"),                  1, { CSAL(L"String") },                                 NO_AUTO_LEN },
108         { CSAL(L"String\0"),                1, { CSAL(L"String") }                                              },
109         { CSAL(L"String1\0String2"),        2, { CSAL(L"String1"), CSAL(L"String2") },              NO_AUTO_LEN },
110         { CSAL(L"String1\0String2\0"),      2, { CSAL(L"String1"), CSAL(L"String2") }                           },
111         { CSAL(L"String1\0\0String3"),      3, { CSAL(L"String1"), CSAL(L""), CSAL(L"String3") },   NO_AUTO_LEN },
112         { CSAL(L"String1\0\0String3\0"),    3, { CSAL(L"String1"), CSAL(L""), CSAL(L"String3") },
113           AUTO_DIFFERS,                     1, { CSAL(L"String1") } },
114     };
115 
116 #define DO_QUERY(ExpectedArray, ExpectedCount) do                       \
117     {                                                                   \
118         ULONG _i;                                                       \
119         ULONG _ExpectedCount = (ExpectedCount);                         \
120         for (_i = 0; _i < _ExpectedCount; _i++)                         \
121         {                                                               \
122             Expected.Values[_i].ValueName = ValueName.Buffer;           \
123             Expected.Values[_i].ValueType = REG_SZ;                     \
124             Expected.Values[_i].ValueData = (ExpectedArray)[_i].Str;    \
125             Expected.Values[_i].ValueLength = (ExpectedArray)[_i].Len;  \
126         }                                                               \
127         Expected.CurrentIndex = 0;                                      \
128         Expected.Count = _ExpectedCount;                                \
129         if (_ExpectedCount == 1)                                        \
130             QueryTable[0].EntryContext = &Expected.Values[0];           \
131         else                                                            \
132             QueryTable[0].EntryContext = NULL;                          \
133         Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,            \
134                                         (PCWSTR)KeyHandle,              \
135                                         QueryTable,                     \
136                                         &Expected,                      \
137                                         NULL);                          \
138         ok_eq_hex(Status, STATUS_SUCCESS);                              \
139         ok_eq_ulong(Expected.CurrentIndex, Expected.Count);             \
140     } while(0)
141 
142     ULONG TestCount = RTL_NUMBER_OF(Tests);
143     ULONG i;
144 
145     for (i = 0; i < TestCount; i++)
146     {
147         trace("Set: %lu\n", i);
148         Status = ZwSetValueKey(KeyHandle,
149                                &ValueName,
150                                0,
151                                REG_MULTI_SZ,
152                                Tests[i].Value.Str,
153                                Tests[i].Value.Len);
154         ok_eq_hex(Status, STATUS_SUCCESS);
155 
156         DO_QUERY(Tests[i].Expected, Tests[i].ExpectedCount);
157     }
158 
159     /* Delete value to test default values */
160     Status = ZwDeleteValueKey(KeyHandle, &ValueName);
161     ok(Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND,
162        "ZwDeleteValueKey returned %lx\n", Status);
163 
164     /* Default: REG_NONE */
165     DO_QUERY((STR_AND_LEN *)NULL, 0);
166 
167     for (i = 0; i < TestCount; i++)
168     {
169         if (Tests[i].Flags & NO_DEFAULT)
170             continue;
171         trace("Default: %lu\n", i);
172         QueryTable[0].DefaultType = REG_MULTI_SZ;
173         QueryTable[0].DefaultData = Tests[i].Value.Str;
174         QueryTable[0].DefaultLength = Tests[i].Value.Len;
175 
176         if (Tests[i].Flags & DEFAULT_DIFFERS)
177             DO_QUERY(Tests[i].DefaultExpected, Tests[i].DefaultExpectedCount);
178         else
179             DO_QUERY(Tests[i].Expected, Tests[i].ExpectedCount);
180     }
181 
182     for (i = 0; i < TestCount; i++)
183     {
184         if (Tests[i].Flags & NO_AUTO_LEN)
185             continue;
186         trace("Auto: %lu\n", i);
187         QueryTable[0].DefaultType = REG_MULTI_SZ;
188         QueryTable[0].DefaultData = Tests[i].Value.Str;
189         QueryTable[0].DefaultLength = 0;
190 
191         if (Tests[i].Flags & AUTO_DIFFERS)
192             DO_QUERY(Tests[i].DefaultExpected, Tests[i].DefaultExpectedCount);
193         else
194             DO_QUERY(Tests[i].Expected, Tests[i].ExpectedCount);
195     }
196 }
197 
198 START_TEST(RtlRegistry)
199 {
200     NTSTATUS Status;
201     UNICODE_STRING KeyName;
202     OBJECT_ATTRIBUTES ObjectAttributes;
203     HANDLE SoftwareHandle;
204     HANDLE KeyHandle;
205 
206     RtlInitUnicodeString(&KeyName, L"\\Registry\\MACHINE\\Software");
207     InitializeObjectAttributes(&ObjectAttributes,
208                                &KeyName,
209                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
210                                NULL,
211                                NULL);
212     Status = ZwOpenKey(&SoftwareHandle,
213                        KEY_CREATE_SUB_KEY,
214                        &ObjectAttributes);
215     ok_eq_hex(Status, STATUS_SUCCESS);
216     if (skip(NT_SUCCESS(Status) && SoftwareHandle != NULL, "No software key\n"))
217         return;
218 
219     RtlInitUnicodeString(&KeyName, L"RtlRegistryKmtestKey");
220     InitializeObjectAttributes(&ObjectAttributes,
221                                &KeyName,
222                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
223                                SoftwareHandle,
224                                NULL);
225     Status = ZwCreateKey(&KeyHandle,
226                          KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE,
227                          &ObjectAttributes,
228                          0,
229                          NULL,
230                          REG_OPTION_VOLATILE,
231                          NULL);
232     ok_eq_hex(Status, STATUS_SUCCESS);
233 
234     if (!skip(NT_SUCCESS(Status) && KeyHandle != NULL, "No test key\n"))
235     {
236         TestRtlQueryRegistryValues(KeyHandle);
237 
238         Status = ZwDeleteKey(KeyHandle);
239         ok_eq_hex(Status, STATUS_SUCCESS);
240         Status = ZwClose(KeyHandle);
241         ok_eq_hex(Status, STATUS_SUCCESS);
242     }
243 }
244