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         ULONG Line;
143         PCWSTR FileName;
144         PREFIX_TYPE PrefixType;
145         PCWSTR FullPathName;
146         RTL_PATH_TYPE PathType;
147         PREFIX_TYPE FilePartPrefixType;
148         SIZE_T FilePartSize;
149     } TestCases[] =
150     {
151         { __LINE__, L"C:",                 PrefixCurrentPath, L"", RtlPathTypeDriveRelative, PrefixCurrentPathWithoutLastPart },
152         { __LINE__, L"C:\\",               PrefixNone, L"C:\\", RtlPathTypeDriveAbsolute },
153         { __LINE__, L"C:\\test",           PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive },
154         { __LINE__, L"C:\\test\\",         PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute },
155         { __LINE__, L"C:/test/",           PrefixNone, L"C:\\test\\", RtlPathTypeDriveAbsolute },
156 
157         { __LINE__, L"C:\\\\test",         PrefixNone, L"C:\\test", RtlPathTypeDriveAbsolute, PrefixCurrentDrive },
158         { __LINE__, L"test",               PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) },
159         { __LINE__, L"\\test",             PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive },
160         { __LINE__, L"/test",              PrefixCurrentDrive, L"test", RtlPathTypeRooted, PrefixCurrentDrive },
161         { __LINE__, L".\\test",            PrefixCurrentPath, L"\\test", RtlPathTypeRelative, PrefixCurrentPath, sizeof(WCHAR) },
162 
163         { __LINE__, L"\\.",                PrefixCurrentDrive, L"", RtlPathTypeRooted },
164         { __LINE__, L"\\.\\",              PrefixCurrentDrive, L"", RtlPathTypeRooted },
165         { __LINE__, L"\\\\.",              PrefixNone, L"\\\\.\\", RtlPathTypeRootLocalDevice },
166         { __LINE__, L"\\\\.\\",            PrefixNone, L"\\\\.\\", RtlPathTypeLocalDevice },
167         { __LINE__, L"\\\\.\\Something\\", PrefixNone, L"\\\\.\\Something\\", RtlPathTypeLocalDevice },
168 
169         { __LINE__, L"\\??\\",             PrefixCurrentDrive, L"??\\", RtlPathTypeRooted },
170         { __LINE__, L"\\??\\C:",           PrefixCurrentDrive, L"??\\C:", RtlPathTypeRooted, PrefixCurrentDrive, 3 * sizeof(WCHAR) },
171         { __LINE__, L"\\??\\C:\\",         PrefixCurrentDrive, L"??\\C:\\", RtlPathTypeRooted },
172         { __LINE__, L"\\??\\C:\\test",     PrefixCurrentDrive, L"??\\C:\\test", RtlPathTypeRooted, PrefixCurrentDrive, 6 * sizeof(WCHAR) },
173         { __LINE__, L"\\??\\C:\\test\\",   PrefixCurrentDrive, L"??\\C:\\test\\", RtlPathTypeRooted },
174 
175         { __LINE__, L"\\\\??\\",           PrefixNone, L"\\\\??\\", RtlPathTypeUncAbsolute },
176         { __LINE__, L"\\\\??\\C:",         PrefixNone, L"\\\\??\\C:", RtlPathTypeUncAbsolute },
177         { __LINE__, L"\\\\??\\C:\\",       PrefixNone, L"\\\\??\\C:\\", RtlPathTypeUncAbsolute },
178         { __LINE__, L"\\\\??\\C:\\test",   PrefixNone, L"\\\\??\\C:\\test", RtlPathTypeUncAbsolute, PrefixNone, sizeof(L"\\\\??\\C:\\") },
179         { __LINE__, L"\\\\??\\C:\\test\\", PrefixNone, L"\\\\??\\C:\\test\\", RtlPathTypeUncAbsolute },
180     };
181     ULONG Length;
182     UNICODE_STRING FileName;
183     WCHAR FullPathNameBuffer[MAX_PATH];
184     UNICODE_STRING TempString;
185     const WCHAR *ShortName;
186     BOOLEAN NameInvalid;
187     PATH_TYPE_AND_UNKNOWN PathType;
188     WCHAR ExpectedPathName[MAX_PATH];
189     SIZE_T ExpectedFilePartSize;
190     const WCHAR *ExpectedShortName;
191     const INT TestCount = sizeof(TestCases) / sizeof(TestCases[0]);
192     INT i;
193     BOOLEAN Okay;
194 
195     for (i = 0; i < TestCount; i++)
196     {
197         trace("i = %d\n", i);
198         switch (TestCases[i].PrefixType)
199         {
200             case PrefixNone:
201                 ExpectedPathName[0] = UNICODE_NULL;
202                 break;
203             case PrefixCurrentDrive:
204                 GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName);
205                 ExpectedPathName[3] = UNICODE_NULL;
206                 break;
207             case PrefixCurrentPath:
208             {
209                 ULONG Length;
210                 Length = GetCurrentDirectoryW(sizeof(ExpectedPathName) / sizeof(WCHAR), ExpectedPathName);
211                 if (Length == 3 && TestCases[i].FullPathName[0])
212                     ExpectedPathName[2] = UNICODE_NULL;
213                 break;
214             }
215             default:
216                 skip("Invalid test!\n");
217                 continue;
218         }
219         wcscat(ExpectedPathName, TestCases[i].FullPathName);
220         RtlInitUnicodeString(&FileName, TestCases[i].FileName);
221         RtlFillMemory(FullPathNameBuffer, sizeof(FullPathNameBuffer), 0xAA);
222         TempString = FileName;
223         PathType.Type = RtlPathTypeNotSet;
224         PathType.Unknown = 1234;
225         ShortName = InvalidPointer;
226         NameInvalid = (BOOLEAN)-1;
227         Length = 1234;
228         StartSeh()
229             Length = RtlGetFullPathName_Ustr(&FileName,
230                                              sizeof(FullPathNameBuffer),
231                                              FullPathNameBuffer,
232                                              &ShortName,
233                                              &NameInvalid,
234                                              &PathType);
235         EndSeh(STATUS_SUCCESS);
236         ok_eq_ustr(&FileName, &TempString);
237         Okay = CheckStringBuffer(FullPathNameBuffer, Length, sizeof(FullPathNameBuffer), ExpectedPathName);
238         ok(Okay, "Line %lu: Wrong path name '%S', expected '%S'\n", TestCases[i].Line, FullPathNameBuffer, ExpectedPathName);
239         switch (TestCases[i].FilePartPrefixType)
240         {
241             case PrefixNone:
242                 ExpectedFilePartSize = 0;
243                 break;
244             case PrefixCurrentDrive:
245                 ExpectedFilePartSize = sizeof(L"C:\\");
246                 break;
247             case PrefixCurrentPath:
248                 ExpectedFilePartSize = GetCurrentDirectoryW(0, NULL) * sizeof(WCHAR);
249                 if (ExpectedFilePartSize == sizeof(L"C:\\"))
250                     ExpectedFilePartSize -= sizeof(WCHAR);
251                 break;
252             case PrefixCurrentPathWithoutLastPart:
253             {
254                 WCHAR CurrentPath[MAX_PATH];
255                 PCWSTR BackSlash;
256                 ExpectedFilePartSize = GetCurrentDirectoryW(sizeof(CurrentPath) / sizeof(WCHAR), CurrentPath) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
257                 if (ExpectedFilePartSize == sizeof(L"C:\\"))
258                     ExpectedFilePartSize = 0;
259                 else
260                 {
261                     BackSlash = wcsrchr(CurrentPath, L'\\');
262                     if (BackSlash)
263                         ExpectedFilePartSize -= wcslen(BackSlash + 1) * sizeof(WCHAR);
264                     else
265                         ok(0, "Line %lu: GetCurrentDirectory returned %S\n", TestCases[i].Line, CurrentPath);
266                 }
267                 break;
268             }
269             default:
270                 skip("Invalid test!\n");
271                 continue;
272         }
273         ExpectedFilePartSize += TestCases[i].FilePartSize;
274         if (ExpectedFilePartSize == 0)
275         {
276             ExpectedShortName = NULL;
277         }
278         else
279         {
280             ExpectedFilePartSize = (ExpectedFilePartSize - sizeof(UNICODE_NULL)) / sizeof(WCHAR);
281             ExpectedShortName = FullPathNameBuffer + ExpectedFilePartSize;
282         }
283         ok(ShortName == ExpectedShortName,
284             "ShortName = %p, expected %p\n", ShortName, ExpectedShortName);
285         ok(NameInvalid == FALSE, "Line %lu: NameInvalid = %u\n", TestCases[i].Line, NameInvalid);
286         ok(PathType.Type == TestCases[i].PathType, "Line %lu: PathType = %d, expected %d\n", TestCases[i].Line, PathType.Type, TestCases[i].PathType);
287         ok(PathType.Unknown == 1234 ||
288             broken(PathType.Unknown == 0) ||
289             broken(PathType.Unknown == 32), "Line %lu: Unknown = %lu\n", TestCases[i].Line, PathType.Unknown);
290     }
291 }
292 
293 START_TEST(RtlGetFullPathName_Ustr)
294 {
295     ULONG Length;
296     UNICODE_STRING FileName;
297     UNICODE_STRING TempString;
298     PCWSTR ShortName;
299     BOOLEAN NameInvalid;
300     BOOLEAN NameInvalidArray[sizeof(ULONGLONG)];
301     PATH_TYPE_AND_UNKNOWN PathType;
302     BOOLEAN Okay;
303 
304     if (!RtlGetFullPathName_Ustr)
305     {
306         RtlGetFullPathName_Ustr = (PVOID)GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetFullPathName_Ustr");
307         if (!RtlGetFullPathName_Ustr)
308         {
309             skip("RtlGetFullPathName_Ustr unavailable\n");
310             return;
311         }
312     }
313 
314     /* NULL parameters */
315     StartSeh()
316         RtlGetFullPathName_Ustr(NULL, 0, NULL, NULL, NULL, NULL);
317     EndSeh(STATUS_ACCESS_VIOLATION);
318 
319     RtlInitUnicodeString(&FileName, NULL);
320     TempString = FileName;
321     StartSeh()
322         RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NULL, NULL);
323     EndSeh(STATUS_ACCESS_VIOLATION);
324     ok_eq_ustr(&FileName, &TempString);
325 
326     RtlInitUnicodeString(&FileName, L"");
327     TempString = FileName;
328     StartSeh()
329         RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NULL, NULL);
330     EndSeh(STATUS_ACCESS_VIOLATION);
331     ok_eq_ustr(&FileName, &TempString);
332 
333     PathType.Type = RtlPathTypeNotSet;
334     PathType.Unknown = 1234;
335     StartSeh()
336         RtlGetFullPathName_Ustr(NULL, 0, NULL, NULL, NULL, &PathType);
337     EndSeh(STATUS_ACCESS_VIOLATION);
338     ok(PathType.Type == RtlPathTypeUnknown, "PathType = %d\n", PathType.Type);
339     ok(PathType.Unknown == 1234 ||
340         broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
341 
342     /* check what else is initialized before it crashes */
343     PathType.Type = RtlPathTypeNotSet;
344     PathType.Unknown = 1234;
345     ShortName = InvalidPointer;
346     NameInvalid = (BOOLEAN)-1;
347     StartSeh()
348         RtlGetFullPathName_Ustr(NULL, 0, NULL, &ShortName, &NameInvalid, &PathType);
349     EndSeh(STATUS_ACCESS_VIOLATION);
350     ok(NameInvalid == FALSE, "NameInvalid = %u\n", NameInvalid);
351     ok(ShortName == InvalidPointer ||
352         broken(ShortName == NULL) /* Win7 */, "ShortName = %p\n", ShortName);
353     ok(PathType.Type == RtlPathTypeUnknown, "PathType = %d\n", PathType.Type);
354     ok(PathType.Unknown == 1234 ||
355        broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
356 
357     RtlInitUnicodeString(&FileName, L"");
358     TempString = FileName;
359     ShortName = InvalidPointer;
360     NameInvalid = (BOOLEAN)-1;
361     StartSeh()
362         RtlGetFullPathName_Ustr(&FileName, 0, NULL, &ShortName, &NameInvalid, NULL);
363     EndSeh(STATUS_ACCESS_VIOLATION);
364     ok_eq_ustr(&FileName, &TempString);
365     ok(ShortName == InvalidPointer ||
366         broken(ShortName == NULL) /* Win7 */, "ShortName = %p\n", ShortName);
367     ok(NameInvalid == FALSE, "NameInvalid = %u\n", NameInvalid);
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.Type = RtlPathTypeNotSet;
373     PathType.Unknown = 1234;
374     StartSeh()
375         Length = RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NULL, &PathType);
376         ok(Length == 0, "Length = %lu\n", Length);
377     EndSeh(STATUS_SUCCESS);
378     ok_eq_ustr(&FileName, &TempString);
379     ok(PathType.Type == RtlPathTypeUnknown, "PathType = %d\n", PathType.Type);
380     ok(PathType.Unknown == 1234 ||
381        broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
382 
383     RtlInitUnicodeString(&FileName, L"");
384     TempString = FileName;
385     PathType.Type = RtlPathTypeNotSet;
386     PathType.Unknown = 1234;
387     StartSeh()
388         Length = RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NULL, &PathType);
389         ok(Length == 0, "Length = %lu\n", Length);
390     EndSeh(STATUS_SUCCESS);
391     ok_eq_ustr(&FileName, &TempString);
392     ok(PathType.Type == RtlPathTypeUnknown, "PathType = %d\n", PathType.Type);
393     ok(PathType.Unknown == 1234 ||
394        broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
395 
396     /* Show that NameInvalid is indeed BOOLEAN */
397     RtlInitUnicodeString(&FileName, L"");
398     TempString = FileName;
399     PathType.Type = RtlPathTypeNotSet;
400     PathType.Unknown = 1234;
401     RtlFillMemory(NameInvalidArray, sizeof(NameInvalidArray), 0x55);
402     StartSeh()
403         Length = RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NameInvalidArray, &PathType);
404         ok(Length == 0, "Length = %lu\n", Length);
405     EndSeh(STATUS_SUCCESS);
406     ok_eq_ustr(&FileName, &TempString);
407     ok(PathType.Type == RtlPathTypeUnknown, "PathType = %d\n", PathType.Type);
408     ok(PathType.Unknown == 1234 ||
409        broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
410     ok(NameInvalidArray[0] == FALSE, "NameInvalid = %u\n", NameInvalidArray[0]);
411     Okay = CheckBuffer(NameInvalidArray + 1, sizeof(NameInvalidArray) - sizeof(NameInvalidArray[0]), 0x55);
412     ok(Okay, "CheckBuffer failed\n");
413 
414     /* Give it a valid path */
415     RtlInitUnicodeString(&FileName, L"C:\\test");
416     TempString = FileName;
417     PathType.Type = RtlPathTypeNotSet;
418     PathType.Unknown = 1234;
419     StartSeh()
420         Length = RtlGetFullPathName_Ustr(&FileName, 0, NULL, NULL, NULL, &PathType);
421         ok(Length == sizeof(L"C:\\test"), "Length = %lu\n", Length);
422     EndSeh(STATUS_SUCCESS);
423     ok_eq_ustr(&FileName, &TempString);
424     ok(PathType.Type == RtlPathTypeDriveAbsolute, "PathType = %d\n", PathType.Type);
425     ok(PathType.Unknown == 1234 ||
426        broken(PathType.Unknown == 0) /* Win7 */, "Unknown = %lu\n", PathType.Unknown);
427 
428     /* check the actual functionality with different paths */
429     RunTestCases();
430 }
431