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