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