[NTDLL_APITEST]
[reactos.git] / rostests / apitests / ntdll / RtlGetFullPathName_UstrEx.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for RtlGetFullPathName_UstrEx
5 * PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
6 */
7
8 #include <apitest.h>
9
10 #define WIN32_NO_STATUS
11 #include <ndk/rtlfuncs.h>
12
13 /*
14 NTSTATUS
15 NTAPI
16 RtlGetFullPathName_UstrEx(
17 IN PUNICODE_STRING FileName,
18 IN PUNICODE_STRING StaticString,
19 IN PUNICODE_STRING DynamicString,
20 IN PUNICODE_STRING *StringUsed,
21 IN PSIZE_T FilePartSize OPTIONAL,
22 OUT PBOOLEAN NameInvalid,
23 OUT RTL_PATH_TYPE* PathType,
24 OUT PSIZE_T LengthNeeded OPTIONAL
25 );
26 */
27
28 NTSTATUS
29 (NTAPI
30 *pRtlGetFullPathName_UstrEx)(
31 IN PUNICODE_STRING FileName,
32 IN PUNICODE_STRING StaticString,
33 IN PUNICODE_STRING DynamicString,
34 IN PUNICODE_STRING *StringUsed,
35 IN PSIZE_T FilePartSize OPTIONAL,
36 OUT PBOOLEAN NameInvalid,
37 OUT RTL_PATH_TYPE* PathType,
38 OUT PSIZE_T LengthNeeded OPTIONAL
39 );
40
41 #define ok_eq_ustr(str1, str2) do { \
42 ok((str1)->Buffer == (str2)->Buffer, "Buffer modified\n"); \
43 ok((str1)->Length == (str2)->Length, "Length modified\n"); \
44 ok((str1)->MaximumLength == (str2)->MaximumLength, "MaximumLength modified\n"); \
45 } while (0)
46
47 static
48 BOOLEAN
49 CheckStringBuffer(
50 PCUNICODE_STRING String,
51 PCWSTR Expected)
52 {
53 SIZE_T ExpectedLength = wcslen(Expected) * sizeof(WCHAR);
54 SIZE_T EqualLength;
55 BOOLEAN Result = TRUE;
56 SIZE_T i;
57
58 if (String->Length != ExpectedLength)
59 {
60 ok(0, "String length is %u, expected %lu\n", String->Length, (ULONG)ExpectedLength);
61 Result = FALSE;
62 }
63
64 EqualLength = RtlCompareMemory(String->Buffer, Expected, ExpectedLength);
65 if (EqualLength != ExpectedLength)
66 {
67 ok(0, "String is '%wZ', expected '%S'\n", String, Expected);
68 Result = FALSE;
69 }
70
71 if (String->Buffer[String->Length / sizeof(WCHAR)] != UNICODE_NULL)
72 {
73 ok(0, "Not null terminated\n");
74 Result = FALSE;
75 }
76
77 /* the function nulls the rest of the buffer! */
78 for (i = String->Length + sizeof(UNICODE_NULL); i < String->MaximumLength; i++)
79 {
80 UCHAR Char = ((PUCHAR)String->Buffer)[i];
81 if (Char != 0)
82 {
83 ok(0, "Found 0x%x at offset %lu, expected 0x%x\n", Char, (ULONG)i, 0);
84 /* don't count this as a failure unless the string was actually wrong */
85 //Result = FALSE;
86 /* don't flood the log */
87 break;
88 }
89 }
90
91 return Result;
92 }
93
94 static
95 BOOLEAN
96 CheckBuffer(
97 PVOID Buffer,
98 SIZE_T Size,
99 UCHAR Value)
100 {
101 PUCHAR Array = Buffer;
102 SIZE_T i;
103
104 for (i = 0; i < Size; i++)
105 {
106 if (Array[i] != Value)
107 {
108 trace("Expected %x, found %x at offset %lu\n", Value, Array[i], (ULONG)i);
109 return FALSE;
110 }
111 }
112 return TRUE;
113 }
114
115 #define RtlPathTypeNotSet 123
116
117 /* winetest_platform is "windows" for us, so broken() doesn't do what it should :( */
118 #undef broken
119 #define broken(x) 0
120
121 typedef enum
122 {
123 PrefixNone,
124 PrefixCurrentDrive,
125 PrefixCurrentPath,
126 PrefixCurrentPathWithoutLastPart
127 } PREFIX_TYPE;
128
129 static
130 VOID
131 RunTestCases(VOID)
132 {
133 /* TODO: don't duplicate this in the other tests */
134 /* TODO: Drive Relative tests don't work yet if the current drive isn't C: */
135 struct
136 {
137 PCWSTR FileName;
138 PREFIX_TYPE PrefixType;
139 PCWSTR FullPathName;
140 RTL_PATH_TYPE PathType;
141 PREFIX_TYPE FilePartPrefixType;
142 SIZE_T FilePartSize;
143 } TestCases[] =
144 {
145 { L"C:", PrefixCurrentPath, L"", RtlPathTypeDriveRelative, PrefixCurrentPathWithoutLastPart },
146 { L"C:\\", PrefixNone, L"C:\\", RtlPathTypeDriveAbsolute },
147 { L"C:\\test", PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive },
148 { L"C:\\test\\", PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute },
149 { L"C:/test/", PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute },
150
151 { L"C:\\\\test", PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive },
152 { L"test", PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) },
153 { L"\\test", PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive },
154 { L"/test", PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive },
155 { L".\\test", PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) },
156
157 { L"\\.", PrefixCurrentDrive, L"", RtlPathTypeRooted },
158 { L"\\.\\", PrefixCurrentDrive, L"", RtlPathTypeRooted },
159 { L"\\\\.", PrefixNone, L"\\\\.\\", RtlPathTypeRootLocalDevice },
160 { L"\\\\.\\", PrefixNone, L"\\\\.\\", RtlPathTypeLocalDevice },
161 { L"\\\\.\\Something\\", PrefixNone, L"\\\\.\\Something\\", RtlPathTypeLocalDevice },
162
163 { L"\\??\\", PrefixCurrentDrive, L"??\\", RtlPathTypeRooted },
164 { L"\\??\\C:", PrefixCurrentDrive, L"??\\C:", RtlPathTypeRooted, PrefixCurrentDrive, 3 * sizeof(WCHAR) },
165 { L"\\??\\C:\\", PrefixCurrentDrive, L"??\\C:\\", RtlPathTypeRooted },
166 { L"\\??\\C:\\test", PrefixCurrentDrive, L"??\\C:\\test", RtlPathTypeRooted, PrefixCurrentDrive, 6 * sizeof(WCHAR) },
167 { L"\\??\\C:\\test\\", PrefixCurrentDrive, L"??\\C:\\test\\", RtlPathTypeRooted },
168
169 { L"\\\\??\\", PrefixNone, L"\\\\??\\", RtlPathTypeUncAbsolute },
170 { L"\\\\??\\C:", PrefixNone, L"\\\\??\\C:", RtlPathTypeUncAbsolute },
171 { L"\\\\??\\C:\\", PrefixNone, L"\\\\??\\C:\\", RtlPathTypeUncAbsolute },
172 { L"\\\\??\\C:\\test", PrefixNone, L"\\\\??\\C:\\test", RtlPathTypeUncAbsolute, PrefixNone, sizeof(L"\\\\??\\C:\\") },
173 { L"\\\\??\\C:\\test\\", PrefixNone, L"\\\\??\\C:\\test\\", RtlPathTypeUncAbsolute },
174 };
175 NTSTATUS Status;
176 UNICODE_STRING FileName;
177 UNICODE_STRING FullPathName;
178 WCHAR FullPathNameBuffer[MAX_PATH];
179 UNICODE_STRING TempString;
180 PUNICODE_STRING StringUsed;
181 SIZE_T FilePartSize;
182 BOOLEAN NameInvalid;
183 RTL_PATH_TYPE PathType;
184 SIZE_T LengthNeeded;
185 WCHAR ExpectedPathName[MAX_PATH];
186 SIZE_T ExpectedFilePartSize;
187 const INT TestCount = sizeof(TestCases) / sizeof(TestCases[0]);
188 INT i;
189 BOOLEAN Okay;
190
191 for (i = 0; i < TestCount; i++)
192 {
193 switch (TestCases[i].PrefixType)
194 {
195 case PrefixNone:
196 ExpectedPathName[0] = UNICODE_NULL;
197 break;
198 case PrefixCurrentDrive:
199 GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName);
200 ExpectedPathName[3] = UNICODE_NULL;
201 break;
202 case PrefixCurrentPath:
203 {
204 ULONG Length;
205 Length = GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName);
206 if (Length == 3 && TestCases[i].FullPathName[0])
207 ExpectedPathName[2] = UNICODE_NULL;
208 break;
209 }
210 default:
211 skip("Invalid test!\n");
212 continue;
213 }
214 wcscat(ExpectedPathName, TestCases[i].FullPathName);
215 RtlInitUnicodeString(&FileName, TestCases[i].FileName);
216 RtlInitEmptyUnicodeString(&FullPathName, FullPathNameBuffer, sizeof(FullPathNameBuffer));
217 RtlFillMemory(FullPathName.Buffer, FullPathName.MaximumLength, 0xAA);
218 TempString = FileName;
219 PathType = RtlPathTypeNotSet;
220 StringUsed = InvalidPointer;
221 FilePartSize = 1234;
222 NameInvalid = (BOOLEAN)-1;
223 LengthNeeded = 1234;
224 StartSeh()
225 Status = pRtlGetFullPathName_UstrEx(&FileName,
226 &FullPathName,
227 NULL,
228 &StringUsed,
229 &FilePartSize,
230 &NameInvalid,
231 &PathType,
232 &LengthNeeded);
233 ok(Status == STATUS_SUCCESS, "status = %lx\n", Status);
234 EndSeh(STATUS_SUCCESS);
235 ok_eq_ustr(&FileName, &TempString);
236 ok(FullPathName.Buffer == FullPathNameBuffer, "Buffer modified\n");
237 ok(FullPathName.MaximumLength == sizeof(FullPathNameBuffer), "MaximumLength modified\n");
238 Okay = CheckStringBuffer(&FullPathName, ExpectedPathName);
239 ok(Okay, "Wrong path name '%wZ', expected '%S'\n", &FullPathName, ExpectedPathName);
240 ok(StringUsed == &FullPathName, "StringUsed = %p, expected %p\n", StringUsed, &FullPathName);
241 switch (TestCases[i].FilePartPrefixType)
242 {
243 case PrefixNone:
244 ExpectedFilePartSize = 0;
245 break;
246 case PrefixCurrentDrive:
247 ExpectedFilePartSize = sizeof(L"C:\\");
248 break;
249 case PrefixCurrentPath:
250 ExpectedFilePartSize = GetCurrentDirectoryW(0, NULL) * sizeof(WCHAR);
251 if (ExpectedFilePartSize == sizeof(L"C:\\"))
252 ExpectedFilePartSize -= sizeof(WCHAR);
253 break;
254 case PrefixCurrentPathWithoutLastPart:
255 {
256 WCHAR CurrentPath[MAX_PATH];
257 PCWSTR BackSlash;
258 ExpectedFilePartSize = GetCurrentDirectoryW(sizeof(CurrentPath) / sizeof(WCHAR), CurrentPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
259 if (ExpectedFilePartSize == sizeof(L"C:\\"))
260 ExpectedFilePartSize = 0;
261 else
262 {
263 BackSlash = wcsrchr(CurrentPath, L'\\');
264 if (BackSlash)
265 ExpectedFilePartSize -= wcslen(BackSlash + 1) * sizeof(WCHAR);
266 else
267 ok(0, "GetCurrentDirectory returned %S\n", CurrentPath);
268 }
269 break;
270 }
271 default:
272 skip("Invalid test!\n");
273 continue;
274 }
275 ExpectedFilePartSize += TestCases[i].FilePartSize;
276 if (ExpectedFilePartSize != 0)
277 ExpectedFilePartSize = (ExpectedFilePartSize - sizeof(UNICODE_NULL)) / sizeof(WCHAR);
278 ok(FilePartSize == ExpectedFilePartSize,
279 "FilePartSize = %lu, expected %lu\n", (ULONG)FilePartSize, (ULONG)ExpectedFilePartSize);
280 ok(NameInvalid == FALSE, "NameInvalid = %u\n", NameInvalid);
281 ok(PathType == TestCases[i].PathType, "PathType = %d, expected %d\n", PathType, TestCases[i].PathType);
282 ok(LengthNeeded == 0, "LengthNeeded = %lu\n", (ULONG)LengthNeeded);
283 }
284 }
285
286 START_TEST(RtlGetFullPathName_UstrEx)
287 {
288 NTSTATUS Status;
289 UNICODE_STRING FileName;
290 UNICODE_STRING TempString;
291 UNICODE_STRING StaticString;
292 PUNICODE_STRING StringUsed;
293 SIZE_T FilePartSize;
294 BOOLEAN NameInvalid;
295 BOOLEAN NameInvalidArray[sizeof(ULONGLONG)];
296 RTL_PATH_TYPE PathType;
297 SIZE_T LengthNeeded;
298 BOOLEAN Okay;
299
300 pRtlGetFullPathName_UstrEx = (PVOID)GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetFullPathName_UstrEx");
301 if (!pRtlGetFullPathName_UstrEx)
302 {
303 skip("RtlGetFullPathName_UstrEx unavailable\n");
304 return;
305 }
306
307 /* NULL parameters */
308 StartSeh()
309 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
310 EndSeh(STATUS_ACCESS_VIOLATION);
311
312 RtlInitUnicodeString(&FileName, NULL);
313 TempString = FileName;
314 StartSeh()
315 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
316 EndSeh(STATUS_ACCESS_VIOLATION);
317 ok_eq_ustr(&FileName, &TempString);
318
319 RtlInitUnicodeString(&FileName, L"");
320 TempString = FileName;
321 StartSeh()
322 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
323 EndSeh(STATUS_ACCESS_VIOLATION);
324 ok_eq_ustr(&FileName, &TempString);
325
326 PathType = RtlPathTypeNotSet;
327 StartSeh()
328 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, NULL, NULL, NULL, &PathType, NULL);
329 EndSeh(STATUS_ACCESS_VIOLATION);
330 ok(PathType == RtlPathTypeUnknown ||
331 broken(PathType == RtlPathTypeNotSet) /* Win7 */, "PathType = %d\n", PathType);
332
333 /* Check what else is initialized before it crashes */
334 PathType = RtlPathTypeNotSet;
335 StringUsed = InvalidPointer;
336 FilePartSize = 1234;
337 NameInvalid = (BOOLEAN)-1;
338 LengthNeeded = 1234;
339 StartSeh()
340 pRtlGetFullPathName_UstrEx(NULL, NULL, NULL, &StringUsed, &FilePartSize, &NameInvalid, &PathType, &LengthNeeded);
341 EndSeh(STATUS_ACCESS_VIOLATION);
342 ok(StringUsed == NULL, "StringUsed = %p\n", StringUsed);
343 ok(FilePartSize == 0, "FilePartSize = %lu\n", (ULONG)FilePartSize);
344 ok(NameInvalid == FALSE, "NameInvalid = %u\n", NameInvalid);
345 ok(PathType == RtlPathTypeUnknown ||
346 broken(PathType == RtlPathTypeNotSet) /* Win7 */, "PathType = %d\n", PathType);
347 ok(LengthNeeded == 0, "LengthNeeded = %lu\n", (ULONG)LengthNeeded);
348
349 RtlInitUnicodeString(&FileName, L"");
350 TempString = FileName;
351 StringUsed = InvalidPointer;
352 FilePartSize = 1234;
353 NameInvalid = (BOOLEAN)-1;
354 LengthNeeded = 1234;
355 StartSeh()
356 pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, &StringUsed, &FilePartSize, &NameInvalid, NULL, &LengthNeeded);
357 EndSeh(STATUS_ACCESS_VIOLATION);
358 ok_eq_ustr(&FileName, &TempString);
359 ok(StringUsed == NULL, "StringUsed = %p\n", StringUsed);
360 ok(FilePartSize == 0, "FilePartSize = %lu\n", (ULONG)FilePartSize);
361 ok(NameInvalid == FALSE ||
362 broken(NameInvalid == (BOOLEAN)-1) /* Win7 */, "NameInvalid = %u\n", NameInvalid);
363 ok(LengthNeeded == 0, "LengthNeeded = %lu\n", (ULONG)LengthNeeded);
364
365 /* This is the first one that doesn't crash. FileName and PathType cannot be NULL */
366 RtlInitUnicodeString(&FileName, NULL);
367 TempString = FileName;
368 PathType = RtlPathTypeNotSet;
369 StartSeh()
370 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL);
371 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status);
372 EndSeh(STATUS_SUCCESS);
373 ok_eq_ustr(&FileName, &TempString);
374 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType);
375
376 RtlInitUnicodeString(&FileName, L"");
377 TempString = FileName;
378 PathType = RtlPathTypeNotSet;
379 StartSeh()
380 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL);
381 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status);
382 EndSeh(STATUS_SUCCESS);
383 ok_eq_ustr(&FileName, &TempString);
384 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType);
385
386 /* Show that NameInvalid is indeed BOOLEAN */
387 RtlInitUnicodeString(&FileName, L"");
388 TempString = FileName;
389 PathType = RtlPathTypeNotSet;
390 RtlFillMemory(NameInvalidArray, sizeof(NameInvalidArray), 0x55);
391 StartSeh()
392 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NameInvalidArray, &PathType, NULL);
393 ok(Status == STATUS_OBJECT_NAME_INVALID, "status = %lx\n", Status);
394 EndSeh(STATUS_SUCCESS);
395 ok_eq_ustr(&FileName, &TempString);
396 ok(PathType == RtlPathTypeUnknown, "PathType = %d\n", PathType);
397 ok(NameInvalidArray[0] == FALSE, "NameInvalid = %u\n", NameInvalidArray[0]);
398 Okay = CheckBuffer(NameInvalidArray + 1, sizeof(NameInvalidArray) - sizeof(NameInvalidArray[0]), 0x55);
399 ok(Okay, "CheckBuffer failed\n");
400
401 /* Give it a valid path */
402 RtlInitUnicodeString(&FileName, L"C:\\test");
403 TempString = FileName;
404 PathType = RtlPathTypeNotSet;
405 StartSeh()
406 Status = pRtlGetFullPathName_UstrEx(&FileName, NULL, NULL, NULL, NULL, NULL, &PathType, NULL);
407 ok(Status == STATUS_BUFFER_TOO_SMALL, "status = %lx\n", Status);
408 EndSeh(STATUS_SUCCESS);
409 ok_eq_ustr(&FileName, &TempString);
410 ok(PathType == RtlPathTypeDriveAbsolute, "PathType = %d\n", PathType);
411
412 /* Zero-length static string */
413 RtlInitUnicodeString(&FileName, L"C:\\test");
414 TempString = FileName;
415 RtlInitUnicodeString(&StaticString, NULL);
416 PathType = RtlPathTypeNotSet;
417 StartSeh()
418 Status = pRtlGetFullPathName_UstrEx(&FileName, &StaticString, NULL, NULL, NULL, NULL, &PathType, NULL);
419 ok(Status == STATUS_BUFFER_TOO_SMALL, "status = %lx\n", Status);
420 EndSeh(STATUS_SUCCESS);
421 ok_eq_ustr(&FileName, &TempString);
422 ok(PathType == RtlPathTypeDriveAbsolute, "PathType = %d\n", PathType);
423
424 /* TODO: play around with StaticString and DynamicString */
425
426 /* Check the actual functionality with different paths */
427 RunTestCases();
428 }