1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for RtlDosSearchPath_U
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 /*
11 ULONG
12 NTAPI
13 RtlDosSearchPath_U(
14     IN PCWSTR Path,
15     IN PCWSTR FileName,
16     IN PCWSTR Extension,
17     IN ULONG BufferSize,
18     OUT PWSTR Buffer,
19     OUT PWSTR *PartName
20 );
21 */
22 
23 #define PrintablePointer(p) ((p) == InvalidPointer ? NULL : (p))
24 
25 static
26 BOOLEAN
27 CheckStringBuffer(
28     PCWSTR Buffer,
29     SIZE_T Length,
30     SIZE_T MaximumLength,
31     PCWSTR Expected)
32 {
33     SIZE_T ExpectedLength = wcslen(Expected) * sizeof(WCHAR);
34     SIZE_T EqualLength;
35     BOOLEAN Result = TRUE;
36     SIZE_T i;
37 
38     if (Length != ExpectedLength)
39     {
40         ok(0, "String length is %lu, expected %lu\n", (ULONG)Length, (ULONG)ExpectedLength);
41         Result = FALSE;
42     }
43 
44     EqualLength = RtlCompareMemory(Buffer, Expected, Length);
45     if (EqualLength != Length)
46     {
47         ok(0, "String is '%S', expected '%S'\n", Buffer, Expected);
48         Result = FALSE;
49     }
50 
51     if (Buffer[Length / sizeof(WCHAR)] != UNICODE_NULL)
52     {
53         ok(0, "Not null terminated\n");
54         Result = FALSE;
55     }
56 
57     /* the function nulls the rest of the buffer! */
58     for (i = Length + sizeof(UNICODE_NULL); i < MaximumLength; i++)
59     {
60         UCHAR Char = ((PUCHAR)Buffer)[i];
61         if (Char != 0)
62         {
63             ok(0, "Found 0x%x at offset %lu, expected 0x%x\n", Char, (ULONG)i, 0);
64             /* don't count this as a failure unless the string was actually wrong */
65             //Result = FALSE;
66             /* don't flood the log */
67             break;
68         }
69     }
70 
71     return Result;
72 }
73 
74 static
75 BOOLEAN
76 CheckBuffer(
77     PVOID Buffer,
78     SIZE_T Size,
79     UCHAR Value)
80 {
81     PUCHAR Array = Buffer;
82     SIZE_T i;
83 
84     for (i = 0; i < Size; i++)
85         if (Array[i] != Value)
86         {
87             trace("Expected %x, found %x at offset %lu\n", Value, Array[i], (ULONG)i);
88             return FALSE;
89         }
90     return TRUE;
91 }
92 
93 static
94 VOID
95 RunTestCases(
96     PCWSTR CustomPath)
97 {
98     struct
99     {
100         PCWSTR SearchPath;
101         PCWSTR FileName;
102         PCWSTR Extension;
103         PCWSTR ResultPath;
104         PCWSTR ResultFileName;
105     } Tests[] =
106     {
107         { L"",                 L"",                     NULL,    NULL,                           NULL },
108         { L"C:\\%ls\\Folder1", L"File1",                NULL,    L"C:\\%ls\\Folder1\\",          L"File1" },
109         /* No path: current directory */
110         { L"",                 L"File1",                NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
111         /* Full path as FileName */
112         { L"",                 L"C:\\",                 NULL,    L"C:\\",                        NULL },
113         { L"",                 L"C:\\%ls\\Folder1",     NULL,    L"C:\\%ls\\",                   L"Folder1" },
114         /* No FileName */
115         { L"C:\\",             L"",                     NULL,    L"C:\\",                        NULL },
116         { L"C:\\%ls\\Folder1", L"",                     NULL,    L"C:\\%ls\\Folder1\\",          NULL },
117         /* Full path as FileName */
118         { L"", L"C:\\%ls\\Folder1\\SomeProgram.exe",    NULL,    L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
119         { L"", L"C:\\%ls\\Folder1\\SomeProgram.exe",    L".exe", L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
120         { L"", L"C:\\%ls\\Folder1\\SomeProgram",        NULL,    NULL,                           NULL },
121         // 10
122         { L"", L"C:\\%ls\\Folder1\\SomeProgram",        L".exe", NULL,                           NULL },
123         /* Both SearchPath and FileName */
124         { L"C:\\%ls\\Folder1\\", L"SomeProgram.exe",    NULL,    L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
125         { L"C:\\%ls\\Folder1\\", L"SomeProgram.exe",    L".exe", L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
126         { L"C:\\%ls\\Folder1\\", L"SomeProgram",        NULL,    NULL,                           NULL },
127         { L"C:\\%ls\\Folder1\\", L"SomeProgram",        L".exe", L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
128         { L"C:\\%ls\\Folder1",   L"SomeProgram.exe",    NULL,    L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
129         { L"C:\\%ls\\Folder1",   L"SomeProgram.exe",    L".exe", L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
130         { L"C:\\%ls\\Folder1",   L"SomeProgram",        NULL,    NULL,                           NULL },
131         { L"C:\\%ls\\Folder1",   L"SomeProgram",        L".exe", L"C:\\%ls\\Folder1\\",          L"SomeProgram.exe" },
132         /* Full path to file in SearchPath doesn't work */
133         { L"C:\\%ls\\Folder1\\SomeProgram.exe", L"",    NULL,    NULL,                           NULL },
134         // 20
135         { L"C:\\%ls\\Folder1\\SomeProgram.exe", L"",    L".exe", NULL,                           NULL },
136         { L"C:\\%ls\\Folder1\\SomeProgram",     L"",    NULL,    NULL,                           NULL },
137         { L"C:\\%ls\\Folder1\\SomeProgram",     L"",    L".exe", NULL,                           NULL },
138         /* */
139         { L"C:\\%ls\\Folder1",          L"File1",       NULL,    L"C:\\%ls\\Folder1\\",          L"File1" },
140         { L"C:\\%ls\\CurrentDirectory", L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
141         { L"C:\\%ls\\Folder1 ",         L"File1",       NULL,    NULL,                           NULL },
142         { L"C:\\%ls\\CurrentDirectory ",L"File1",       NULL,    NULL,                           NULL },
143         { L" C:\\%ls\\Folder1",         L"File1",       NULL,    NULL,                           NULL },
144         { L" C:\\%ls\\CurrentDirectory",L"File1",       NULL,    NULL,                           NULL },
145         { L" C:\\%ls\\Folder1 ",        L"File1",       NULL,    NULL,                           NULL },
146         // 30
147         { L" C:\\%ls\\CurrentDirectory ",L"File1",      NULL,    NULL,                           NULL },
148         /* Multiple search paths */
149         { L"C:\\%ls\\Folder1;C:\\%ls\\CurrentDirectory",
150                                         L"File1",       NULL,    L"C:\\%ls\\Folder1\\",          L"File1" },
151         { L"C:\\%ls\\CurrentDirectory;C:\\%ls\\Folder1",
152                                         L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
153         { L"C:\\%ls\\CurrentDirectory ; C:\\%ls\\Folder1",
154                                         L"File1",       NULL,    NULL,                           NULL },
155         { L"C:\\%ls\\CurrentDirectory ;C:\\%ls\\Folder1",
156                                         L"File1",       NULL,    L"C:\\%ls\\Folder1\\",          L"File1" },
157         { L"C:\\%ls\\CurrentDirectory; C:\\%ls\\Folder1",
158                                         L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
159         { L";C:\\%ls\\Folder1",         L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
160         { L";C:\\%ls\\Folder1;",        L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
161         { L";C:\\%ls\\Folder1;",        L"File1",       NULL,    L"C:\\%ls\\CurrentDirectory\\", L"File1" },
162         { L"C:\\%ls\\Folder1",          L"OnlyInCurr",  NULL,    NULL,                           NULL },
163         // 40
164         { L"",                          L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
165         { L"",                          L"OnlyInCurr ", NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
166         { L"",                          L" OnlyInCurr", NULL,    NULL,                           NULL },
167         { L" ",                         L"OnlyInCurr",  NULL,    NULL,                           NULL },
168         { L";",                         L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
169         { L"; ",                        L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
170         { L" ;",                        L"OnlyInCurr",  NULL,    NULL,                           NULL },
171         { L" ; ",                       L"OnlyInCurr",  NULL,    NULL,                           NULL },
172         { L";C:\\%ls\\Folder1",         L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
173         { L"C:\\%ls\\Folder1;",         L"OnlyInCurr",  NULL,    NULL,                           NULL },
174         // 50
175         { L"C:\\%ls\\Folder1;;",        L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
176         { L";C:\\%ls\\Folder1;",        L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
177         { L"C:\\%ls\\Folder1;C:\\%ls\\Folder2",
178                                         L"OnlyInCurr",  NULL,    NULL,                           NULL },
179         { L";C:\\%ls\\Folder1;C:\\%ls\\Folder2",
180                                         L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
181         { L"C:\\%ls\\Folder1;;C:\\%ls\\Folder2",
182                                         L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
183         { L"C:\\%ls\\Folder1;C:\\%ls\\Folder2;",
184                                         L"OnlyInCurr",  NULL,    NULL,                           NULL },
185         { L"C:\\%ls\\Folder1;C:\\%ls\\Folder2;;",
186                                         L"OnlyInCurr",  NULL,    L"C:\\%ls\\CurrentDirectory\\", L"OnlyInCurr" },
187         /* Spaces in FileName! */
188         { L"", L"C:\\%ls\\Folder1\\SomeProgram With Spaces",
189                                                         L".exe", NULL,                           NULL },
190         { L"", L"C:\\%ls\\Folder1\\SomeProgram With Spaces.exe",
191                                                         L".exe", NULL,                           NULL },
192         { L"", L"C:\\%ls\\Folder1\\Program",            L".exe", NULL,                           NULL },
193         // 60
194         { L"", L"C:\\%ls\\Folder1\\Program.exe",        L".exe", L"C:\\%ls\\Folder1\\",          L"Program.exe" },
195         { L"", L"C:\\%ls\\Folder1\\Program With",       L".exe", NULL,                           NULL },
196         { L"", L"C:\\%ls\\Folder1\\Program With.exe",   L".exe", L"C:\\%ls\\Folder1\\",          L"Program With.exe" },
197         { L"", L"C:\\%ls\\Folder1\\Program With Spaces",L".exe", NULL,                           NULL },
198         { L"", L"C:\\%ls\\Folder1\\Program With Spaces.exe",
199                                                         L".exe", L"C:\\%ls\\Folder1\\",          L"Program With Spaces.exe" },
200         /* Same tests with path in SearchPath - now extensions are appended */
201         { L"C:\\%ls\\Folder1", L"SomeProgram With Spaces",
202                                                         L".exe", NULL,                           NULL },
203         { L"C:\\%ls\\Folder1", L"SomeProgram With Spaces.exe",
204                                                         L".exe", NULL,                           NULL },
205         { L"C:\\%ls\\Folder1", L"Program",              L".exe", L"C:\\%ls\\Folder1\\",          L"Program.exe" },
206         { L"C:\\%ls\\Folder1", L"Program.exe",          L".exe", L"C:\\%ls\\Folder1\\",          L"Program.exe" },
207         { L"C:\\%ls\\Folder1", L"Program With",         L".exe", L"C:\\%ls\\Folder1\\",          L"Program With.exe" },
208         // 70
209         { L"C:\\%ls\\Folder1", L"Program With.exe",     L".exe", L"C:\\%ls\\Folder1\\",          L"Program With.exe" },
210         { L"C:\\%ls\\Folder1", L"Program With Spaces",  L".exe", L"C:\\%ls\\Folder1\\",          L"Program With Spaces.exe" },
211         { L"C:\\%ls\\Folder1", L"Program With Spaces.exe",
212                                                         L".exe", L"C:\\%ls\\Folder1\\",          L"Program With Spaces.exe" },
213     };
214 
215     ULONG i;
216     ULONG Length;
217     PWSTR PartName;
218     WCHAR SearchPath[MAX_PATH];
219     WCHAR FileName[MAX_PATH];
220     WCHAR ResultPath[MAX_PATH];
221     WCHAR Buffer[MAX_PATH];
222     BOOLEAN Okay;
223 
224     for (i = 0; i < sizeof(Tests) / sizeof(Tests[0]); i++)
225     {
226         swprintf(SearchPath, Tests[i].SearchPath, CustomPath, CustomPath, CustomPath, CustomPath);
227         swprintf(FileName, Tests[i].FileName, CustomPath, CustomPath, CustomPath, CustomPath);
228         RtlFillMemory(Buffer, sizeof(Buffer), 0x55);
229         PartName = InvalidPointer;
230 
231         StartSeh()
232             Length = RtlDosSearchPath_U(SearchPath,
233                                         FileName,
234                                         Tests[i].Extension,
235                                         sizeof(Buffer),
236                                         Buffer,
237                                         &PartName);
238         EndSeh(STATUS_SUCCESS);
239 
240         if (Tests[i].ResultPath)
241         {
242             swprintf(ResultPath, Tests[i].ResultPath, CustomPath, CustomPath, CustomPath, CustomPath);
243             if (Tests[i].ResultFileName)
244             {
245                 ok(PartName == &Buffer[wcslen(ResultPath)],
246                    "PartName = %p (%ls), expected %p\n",
247                    PartName, PrintablePointer(PartName), &Buffer[wcslen(ResultPath)]);
248                 wcscat(ResultPath, Tests[i].ResultFileName);
249             }
250             else
251             {
252                 ok(PartName == NULL,
253                    "PartName = %p (%ls), expected NULL\n",
254                    PartName, PrintablePointer(PartName));
255             }
256             Okay = CheckStringBuffer(Buffer, Length, sizeof(Buffer), ResultPath);
257             ok(Okay == TRUE, "CheckStringBuffer failed. Got '%ls', expected '%ls'\n", Buffer, ResultPath);
258         }
259         else
260         {
261             Okay = CheckBuffer(Buffer, sizeof(Buffer), 0x55);
262             ok(Okay == TRUE, "CheckBuffer failed\n");
263             ok(Length == 0, "Length = %lu\n", Length);
264             ok(PartName == InvalidPointer,
265                "PartName = %p (%ls), expected %p\n",
266                PartName, PrintablePointer(PartName), InvalidPointer);
267         }
268     }
269 }
270 
271 #define MAKE_DIRECTORY(path)                                                \
272 do {                                                                        \
273     swprintf(FileName, path, CustomPath);                                   \
274     Success = CreateDirectoryW(FileName, NULL);                             \
275     ok(Success, "CreateDirectory failed, results might not be accurate\n"); \
276 } while (0)
277 
278 #define MAKE_FILE(path)                                                     \
279 do {                                                                        \
280     swprintf(FileName, path, CustomPath);                                   \
281     Handle = CreateFileW(FileName, 0, 0, NULL, CREATE_NEW, 0, NULL);        \
282     ok(Handle != INVALID_HANDLE_VALUE,                                      \
283        "CreateFile failed, results might not be accurate\n");               \
284     if (Handle != INVALID_HANDLE_VALUE) CloseHandle(Handle);                \
285 } while (0)
286 
287 #define DELETE_DIRECTORY(path)                                              \
288 do {                                                                        \
289     swprintf(FileName, path, CustomPath);                                   \
290     Success = RemoveDirectoryW(FileName);                                   \
291     ok(Success,                                                             \
292        "RemoveDirectory failed (%lu), test might leave stale directory\n",  \
293        GetLastError());                                                     \
294 } while (0)
295 
296 #define DELETE_FILE(path)                                                   \
297 do {                                                                        \
298     swprintf(FileName, path, CustomPath);                                   \
299     Success = DeleteFileW(FileName);                                        \
300     ok(Success,                                                             \
301        "DeleteFile failed (%lu), test might leave stale file\n",            \
302        GetLastError());                                                     \
303 } while (0)
304 
305 START_TEST(RtlDosSearchPath_U)
306 {
307     ULONG Length = 0;
308     WCHAR Buffer[MAX_PATH];
309     PWSTR PartName;
310     BOOLEAN Okay;
311     BOOL Success;
312     WCHAR FileName[MAX_PATH];
313     WCHAR CustomPath[MAX_PATH] = L"RtlDosSearchPath_U_TestPath";
314     HANDLE Handle;
315 
316     swprintf(FileName, L"C:\\%ls", CustomPath);
317     /* Make sure this directory doesn't exist */
318     while (GetFileAttributesW(FileName) != INVALID_FILE_ATTRIBUTES)
319     {
320         wcscat(CustomPath, L"X");
321         swprintf(FileName, L"C:\\%ls", CustomPath);
322     }
323     Success = CreateDirectoryW(FileName, NULL);
324     ok(Success, "CreateDirectory failed, results might not be accurate\n");
325 
326     MAKE_DIRECTORY(L"C:\\%ls\\Folder1");
327     MAKE_DIRECTORY(L"C:\\%ls\\Folder2");
328     MAKE_DIRECTORY(L"C:\\%ls\\CurrentDirectory");
329     Success = SetCurrentDirectoryW(FileName);
330     ok(Success, "SetCurrentDirectory failed\n");
331     MAKE_FILE(L"C:\\%ls\\Folder1\\File1");
332     MAKE_FILE(L"C:\\%ls\\Folder1\\SomeProgram.exe");
333     MAKE_FILE(L"C:\\%ls\\Folder1\\SomeProgram2.exe");
334     MAKE_FILE(L"C:\\%ls\\Folder1\\SomeProgram2.exe.exe");
335     MAKE_FILE(L"C:\\%ls\\Folder1\\SomeProgram3.exe.exe");
336     MAKE_FILE(L"C:\\%ls\\Folder1\\Program.exe");
337     MAKE_FILE(L"C:\\%ls\\Folder1\\Program With.exe");
338     MAKE_FILE(L"C:\\%ls\\Folder1\\Program With Spaces.exe");
339     MAKE_FILE(L"C:\\%ls\\CurrentDirectory\\File1");
340     MAKE_FILE(L"C:\\%ls\\CurrentDirectory\\OnlyInCurr");
341 
342     /* NULL parameters */
343     StartSeh() RtlDosSearchPath_U(NULL, NULL, NULL, 0, NULL  , NULL); EndSeh(STATUS_ACCESS_VIOLATION);
344     StartSeh() RtlDosSearchPath_U(NULL, L"" , NULL, 0, NULL  , NULL); EndSeh(STATUS_ACCESS_VIOLATION);
345     StartSeh() RtlDosSearchPath_U(NULL, L"" , NULL, 0, Buffer, NULL); EndSeh(STATUS_ACCESS_VIOLATION);
346     StartSeh() RtlDosSearchPath_U(NULL, L"" , NULL, 1, Buffer, NULL); EndSeh(STATUS_ACCESS_VIOLATION);
347     StartSeh() RtlDosSearchPath_U(NULL, L"" , NULL, 2, Buffer, NULL); EndSeh(STATUS_ACCESS_VIOLATION);
348     StartSeh() RtlDosSearchPath_U(L"" , NULL, NULL, 0, NULL  , NULL); EndSeh(STATUS_ACCESS_VIOLATION);
349 
350     /* Empty strings - first one that doesn't crash */
351     StartSeh()
352         Length = RtlDosSearchPath_U(L"", L"", NULL, 0, NULL, NULL);
353         ok(Length == 0, "Length %lu\n", Length);
354     EndSeh(STATUS_SUCCESS);
355 
356     /* Check what's initialized */
357     PartName = InvalidPointer;
358     StartSeh()
359         Length = RtlDosSearchPath_U(L"", L"", NULL, 0, NULL, &PartName);
360         ok(Length == 0, "Length = %lu\n", Length);
361     EndSeh(STATUS_SUCCESS);
362     ok(PartName == InvalidPointer, "PartName = %p\n", PartName);
363 
364     RtlFillMemory(Buffer, sizeof(Buffer), 0x55);
365     StartSeh()
366         Length = RtlDosSearchPath_U(L"", L"", NULL, sizeof(Buffer), Buffer, NULL);
367         ok(Length == 0, "Length %lu\n", Length);
368     EndSeh(STATUS_SUCCESS);
369     Okay = CheckBuffer(Buffer, sizeof(Buffer), 0x55);
370     ok(Okay, "CheckBuffer failed\n");
371 
372     PartName = InvalidPointer;
373     RtlFillMemory(Buffer, sizeof(Buffer), 0x55);
374     StartSeh()
375         Length = RtlDosSearchPath_U(L"", L"", NULL, sizeof(Buffer), Buffer, &PartName);
376         ok(Length == 0, "Length %lu\n", Length);
377     EndSeh(STATUS_SUCCESS);
378     ok(PartName == InvalidPointer, "PartName = %p\n", PartName);
379     Okay = CheckBuffer(Buffer, sizeof(Buffer), 0x55);
380     ok(Okay, "CheckBuffer failed\n");
381 
382     /* Now test the actual functionality */
383     RunTestCases(CustomPath);
384 
385     /*
386      * Clean up test folder - We can't delete it
387      * if our current directory is inside.
388      */
389     SetCurrentDirectoryW(L"C:\\");
390     DELETE_FILE(L"C:\\%ls\\CurrentDirectory\\OnlyInCurr");
391     DELETE_FILE(L"C:\\%ls\\CurrentDirectory\\File1");
392     DELETE_FILE(L"C:\\%ls\\Folder1\\Program With Spaces.exe");
393     DELETE_FILE(L"C:\\%ls\\Folder1\\Program With.exe");
394     DELETE_FILE(L"C:\\%ls\\Folder1\\Program.exe");
395     DELETE_FILE(L"C:\\%ls\\Folder1\\SomeProgram3.exe.exe");
396     DELETE_FILE(L"C:\\%ls\\Folder1\\SomeProgram2.exe.exe");
397     DELETE_FILE(L"C:\\%ls\\Folder1\\SomeProgram2.exe");
398     DELETE_FILE(L"C:\\%ls\\Folder1\\SomeProgram.exe");
399     DELETE_FILE(L"C:\\%ls\\Folder1\\File1");
400     DELETE_DIRECTORY(L"C:\\%ls\\CurrentDirectory");
401     DELETE_DIRECTORY(L"C:\\%ls\\Folder2");
402     DELETE_DIRECTORY(L"C:\\%ls\\Folder1");
403     DELETE_DIRECTORY(L"C:\\%ls");
404 }
405