1 /*
2  * Unit test suite for ntdll path functions
3  *
4  * Copyright 2002 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "ntdll_test.h"
22 
23 static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
24                                                    LPCSTR src, DWORD srclen );
25 static NTSTATUS (WINAPI *pRtlUnicodeToMultiByteN)(LPSTR,DWORD,LPDWORD,LPCWSTR,DWORD);
26 static UINT (WINAPI *pRtlDetermineDosPathNameType_U)( PCWSTR path );
27 static ULONG (WINAPI *pRtlIsDosDeviceName_U)( PCWSTR dos_name );
28 static NTSTATUS (WINAPI *pRtlOemStringToUnicodeString)(UNICODE_STRING *, const STRING *, BOOLEAN );
29 static BOOLEAN (WINAPI *pRtlIsNameLegalDOS8Dot3)(const UNICODE_STRING*,POEM_STRING,PBOOLEAN);
30 static DWORD (WINAPI *pRtlGetFullPathName_U)(const WCHAR*,ULONG,WCHAR*,WCHAR**);
31 static NTSTATUS (WINAPI *pRtlDosPathNameToNtPathName_U_WithStatus)(const WCHAR*, UNICODE_STRING*, WCHAR**, CURDIR*);
32 
33 static void test_RtlDetermineDosPathNameType_U(void)
34 {
35     struct test
36     {
37         const char *path;
38         UINT ret;
39     };
40 
41     static const struct test tests[] =
42     {
43         { "\\\\foo", 1 },
44         { "//foo", 1 },
45         { "\\/foo", 1 },
46         { "/\\foo", 1 },
47         { "\\\\", 1 },
48         { "//", 1 },
49         { "c:\\foo", 2 },
50         { "c:/foo", 2 },
51         { "c://foo", 2 },
52         { "c:\\", 2 },
53         { "c:/", 2 },
54         { "c:foo", 3 },
55         { "c:f\\oo", 3 },
56         { "c:foo/bar", 3 },
57         { "\\foo", 4 },
58         { "/foo", 4 },
59         { "\\", 4 },
60         { "/", 4 },
61         { "foo", 5 },
62         { "", 5 },
63         { "\0:foo", 5 },
64         { "\\\\.\\foo", 6 },
65         { "//./foo", 6 },
66         { "/\\./foo", 6 },
67         { "\\\\.foo", 1 },
68         { "//.foo", 1 },
69         { "\\\\.", 7 },
70         { "//.", 7 },
71         { NULL, 0 }
72     };
73 
74     const struct test *test;
75     WCHAR buffer[MAX_PATH];
76     UINT ret;
77 
78     if (!pRtlDetermineDosPathNameType_U)
79     {
80         win_skip("RtlDetermineDosPathNameType_U is not available\n");
81         return;
82     }
83 
84     for (test = tests; test->path; test++)
85     {
86         pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
87         ret = pRtlDetermineDosPathNameType_U( buffer );
88         ok( ret == test->ret, "Wrong result %d/%d for %s\n", ret, test->ret, test->path );
89     }
90 }
91 
92 
93 static void test_RtlIsDosDeviceName_U(void)
94 {
95     struct test
96     {
97         const char *path;
98         WORD pos;
99         WORD len;
100         BOOL fails;
101     };
102 
103     static const struct test tests[] =
104     {
105         { "\\\\.\\CON",    8, 6, TRUE },  /* fails on win8 */
106         { "\\\\.\\con",    8, 6, TRUE },  /* fails on win8 */
107         { "\\\\.\\CON2",   0, 0 },
108         { "",              0, 0 },
109         { "\\\\foo\\nul",  0, 0 },
110         { "c:\\nul:",      6, 6 },
111         { "c:\\nul\\",     0, 0 },
112         { "c:\\nul\\foo",  0, 0 },
113         { "c:\\nul::",     6, 6, TRUE },  /* fails on nt4 */
114         { "c:\\nul::::::", 6, 6, TRUE },  /* fails on nt4 */
115         { "c:prn     ",    4, 6 },
116         { "c:prn.......",  4, 6 },
117         { "c:prn... ...",  4, 6 },
118         { "c:NUL  ....  ", 4, 6, TRUE },  /* fails on nt4 */
119         { "c: . . .",      0, 0 },
120         { "c:",            0, 0 },
121         { " . . . :",      0, 0 },
122         { ":",             0, 0 },
123         { "c:nul. . . :",  4, 6 },
124         { "c:nul . . :",   4, 6, TRUE },  /* fails on nt4 */
125         { "c:nul0",        0, 0 },
126         { "c:prn:aaa",     4, 6, TRUE },  /* fails on win9x */
127         { "c:PRN:.txt",    4, 6 },
128         { "c:aux:.txt...", 4, 6 },
129         { "c:prn:.txt:",   4, 6 },
130         { "c:nul:aaa",     4, 6, TRUE },  /* fails on win9x */
131         { "con:",          0, 6 },
132         { "lpt1:",         0, 8 },
133         { "c:com5:",       4, 8 },
134         { "CoM4:",         0, 8 },
135         { "lpt9:",         0, 8 },
136         { "c:\\lpt0.txt",  0, 0 },
137         { "c:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
138           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
139           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
140           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
141           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
142           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
143           "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\nul.txt", 1000, 6 },
144         // ReactOS r54114
145         { "c:\\nul",       6, 6 },
146         { NULL, 0 }
147     };
148 
149     const struct test *test;
150     WCHAR buffer[2000];
151     ULONG ret;
152 
153     if (!pRtlIsDosDeviceName_U)
154     {
155         win_skip("RtlIsDosDeviceName_U is not available\n");
156         return;
157     }
158 
159     for (test = tests; test->path; test++)
160     {
161         pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
162         ret = pRtlIsDosDeviceName_U( buffer );
163         ok( ret == MAKELONG( test->len, test->pos ) ||
164             (test->fails && broken( ret == 0 )),
165             "Wrong result (%d,%d)/(%d,%d) for %s\n",
166             HIWORD(ret), LOWORD(ret), test->pos, test->len, test->path );
167     }
168 }
169 
170 static void test_RtlIsNameLegalDOS8Dot3(void)
171 {
172     struct test
173     {
174         const char *path;
175         BOOLEAN result;
176         BOOLEAN spaces;
177     };
178 
179     static const struct test tests[] =
180     {
181         { "12345678",     TRUE,  FALSE },
182         { "123 5678",     TRUE,  TRUE  },
183         { "12345678.",    FALSE, 2 /*not set*/ },
184         { "1234 678.",    FALSE, 2 /*not set*/ },
185         { "12345678.a",   TRUE,  FALSE },
186         { "12345678.a ",  FALSE, 2 /*not set*/ },
187         { "12345678.a c", TRUE,  TRUE  },
188         { " 2345678.a ",  FALSE, 2 /*not set*/ },
189         { "1 345678.abc", TRUE,  TRUE },
190         { "1      8.a c", TRUE,  TRUE },
191         { "1 3 5 7 .abc", FALSE, 2 /*not set*/ },
192         { "12345678.  c", TRUE,  TRUE },
193         { "123456789.a",  FALSE, 2 /*not set*/ },
194         { "12345.abcd",   FALSE, 2 /*not set*/ },
195         { "12345.ab d",   FALSE, 2 /*not set*/ },
196         { ".abc",         FALSE, 2 /*not set*/ },
197         { "12.abc.d",     FALSE, 2 /*not set*/ },
198         { ".",            TRUE,  FALSE },
199         { "..",           TRUE,  FALSE },
200         { "...",          FALSE, 2 /*not set*/ },
201         { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", FALSE, 2 /*not set*/ },
202         { NULL, 0 }
203     };
204 
205     const struct test *test;
206     UNICODE_STRING ustr;
207     OEM_STRING oem, oem_ret;
208     WCHAR buffer[200];
209     char buff2[12];
210     BOOLEAN ret, spaces;
211 
212     if (!pRtlIsNameLegalDOS8Dot3)
213     {
214         win_skip("RtlIsNameLegalDOS8Dot3 is not available\n");
215         return;
216     }
217 
218     ustr.MaximumLength = sizeof(buffer);
219     ustr.Buffer = buffer;
220     for (test = tests; test->path; test++)
221     {
222         char path[100];
223         strcpy(path, test->path);
224         oem.Buffer = path;
225         oem.Length = strlen(test->path);
226         oem.MaximumLength = oem.Length + 1;
227         pRtlOemStringToUnicodeString( &ustr, &oem, FALSE );
228         spaces = 2;
229         oem_ret.Length = oem_ret.MaximumLength = sizeof(buff2);
230         oem_ret.Buffer = buff2;
231         ret = pRtlIsNameLegalDOS8Dot3( &ustr, &oem_ret, &spaces );
232         ok( ret == test->result, "Wrong result %d/%d for '%s'\n", ret, test->result, test->path );
233         ok( spaces == test->spaces, "Wrong spaces value %d/%d for '%s'\n", spaces, test->spaces, test->path );
234         if (strlen(test->path) <= 12)
235         {
236             char str[13];
237             int i;
238             strcpy( str, test->path );
239             for (i = 0; str[i]; i++) str[i] = toupper(str[i]);
240             ok( oem_ret.Length == strlen(test->path), "Wrong length %d/%d for '%s'\n",
241                 oem_ret.Length, lstrlenA(test->path), test->path );
242             ok( !memcmp( oem_ret.Buffer, str, oem_ret.Length ),
243                 "Wrong string '%.*s'/'%s'\n", oem_ret.Length, oem_ret.Buffer, str );
244         }
245     }
246 }
247 static void test_RtlGetFullPathName_U(void)
248 {
249     static const WCHAR emptyW[] = {0};
250     static const WCHAR deadbeefW[] = {'d','e','a','d','b','e','e','f',0};
251 
252     struct test
253     {
254         const char *path;
255         const char *rname;
256         const char *rfile;
257         const char *alt_rname;
258         const char *alt_rfile;
259     };
260 
261     static const struct test tests[] =
262         {
263             { "c:/test",                     "c:\\test",         "test"},
264             { "c:/test     ",                "c:\\test",         "test"},
265             { "c:/test.",                    "c:\\test",         "test"},
266             { "c:/test  ....   ..   ",       "c:\\test",         "test"},
267             { "c:/test/  ....   ..   ",      "c:\\test\\",       NULL},
268             { "c:/test/..",                  "c:\\",             NULL},
269             { "c:/test/.. ",                 "c:\\test\\",       NULL},
270             { "c:/TEST",                     "c:\\TEST",         "TEST"},
271             { "c:/test/file",                "c:\\test\\file",   "file"},
272             { "c:/test./file",               "c:\\test\\file",   "file"},
273             { "c:/test.. /file",             "c:\\test.. \\file","file"},
274             { "c:/test/././file",            "c:\\test\\file",   "file"},
275             { "c:/test\\.\\.\\file",         "c:\\test\\file",   "file"},
276             { "c:/test/\\.\\.\\file",        "c:\\test\\file",   "file"},
277             { "c:/test\\\\.\\.\\file",       "c:\\test\\file",   "file"},
278             { "c:/test\\test1\\..\\.\\file", "c:\\test\\file",   "file"},
279             { "c:///test\\.\\.\\file//",     "c:\\test\\file\\", NULL,
280                                              "c:\\test\\file",   "file"},  /* nt4 */
281             { "c:///test\\..\\file\\..\\//", "c:\\",             NULL},
282             { "c:/test../file",              "c:\\test.\\file",  "file",
283                                              "c:\\test..\\file", "file"},  /* vista */
284             { "c:\\test",                    "c:\\test",         "test"},
285             { NULL, NULL, NULL}
286         };
287 
288     const struct test *test;
289     WCHAR pathbufW[2*MAX_PATH], rbufferW[MAX_PATH];
290     CHAR  rbufferA[MAX_PATH], rfileA[MAX_PATH];
291     ULONG ret;
292     WCHAR *file_part;
293     DWORD reslen;
294     UINT len;
295 
296     if (!pRtlGetFullPathName_U)
297     {
298         win_skip("RtlGetFullPathName_U is not available\n");
299         return;
300     }
301 
302     file_part = (WCHAR *)0xdeadbeef;
303     lstrcpyW(rbufferW, deadbeefW);
304     ret = pRtlGetFullPathName_U(NULL, MAX_PATH, rbufferW, &file_part);
305     ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %u\n", ret);
306     ok(!lstrcmpW(rbufferW, deadbeefW),
307        "Expected the output buffer to be untouched, got %s\n", wine_dbgstr_w(rbufferW));
308     ok(file_part == (WCHAR *)0xdeadbeef ||
309        file_part == NULL, /* Win7 */
310        "Expected file part pointer to be untouched, got %p\n", file_part);
311 
312     file_part = (WCHAR *)0xdeadbeef;
313     lstrcpyW(rbufferW, deadbeefW);
314     ret = pRtlGetFullPathName_U(emptyW, MAX_PATH, rbufferW, &file_part);
315     ok(!ret, "Expected RtlGetFullPathName_U to return 0, got %u\n", ret);
316     ok(!lstrcmpW(rbufferW, deadbeefW),
317        "Expected the output buffer to be untouched, got %s\n", wine_dbgstr_w(rbufferW));
318     ok(file_part == (WCHAR *)0xdeadbeef ||
319        file_part == NULL, /* Win7 */
320        "Expected file part pointer to be untouched, got %p\n", file_part);
321 
322     for (test = tests; test->path; test++)
323     {
324         len= strlen(test->rname) * sizeof(WCHAR);
325         pRtlMultiByteToUnicodeN(pathbufW , sizeof(pathbufW), NULL, test->path, strlen(test->path)+1 );
326         ret = pRtlGetFullPathName_U( pathbufW,MAX_PATH, rbufferW, &file_part);
327         ok( ret == len || (test->alt_rname && ret == strlen(test->alt_rname)*sizeof(WCHAR)),
328             "Wrong result %d/%d for \"%s\"\n", ret, len, test->path );
329         ok(pRtlUnicodeToMultiByteN(rbufferA,MAX_PATH,&reslen,rbufferW,(lstrlenW(rbufferW) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
330            "RtlUnicodeToMultiByteN failed\n");
331         ok(!lstrcmpA(rbufferA,test->rname) || (test->alt_rname && !lstrcmpA(rbufferA,test->alt_rname)),
332            "Got \"%s\" expected \"%s\"\n",rbufferA,test->rname);
333         if (file_part)
334         {
335             ok(pRtlUnicodeToMultiByteN(rfileA,MAX_PATH,&reslen,file_part,(lstrlenW(file_part) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
336                "RtlUnicodeToMultiByteN failed\n");
337             ok((test->rfile && !lstrcmpA(rfileA,test->rfile)) ||
338                (test->alt_rfile && !lstrcmpA(rfileA,test->alt_rfile)),
339                "Got \"%s\" expected \"%s\"\n",rfileA,test->rfile);
340         }
341         else
342         {
343             ok( !test->rfile, "Got NULL expected \"%s\"\n", test->rfile );
344         }
345     }
346 }
347 
348 static void test_RtlDosPathNameToNtPathName_U_WithStatus(void)
349 {
350     static const WCHAR emptyW[] = { 0 };
351     WCHAR path[MAX_PATH];
352     UNICODE_STRING nameW;
353     NTSTATUS status;
354 
355     if (!pRtlDosPathNameToNtPathName_U_WithStatus)
356     {
357         win_skip("RtlDosPathNameToNtPathName_U_WithStatus() is not supported.\n");
358         return;
359     }
360 
361     GetCurrentDirectoryW( MAX_PATH, path );
362 
363     status = pRtlDosPathNameToNtPathName_U_WithStatus( path, &nameW, NULL, NULL );
364     ok(!status, "Failed convert to nt path, %#x.\n", status);
365 
366     status = pRtlDosPathNameToNtPathName_U_WithStatus( NULL, &nameW, NULL, NULL );
367     ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */,
368         "Unexpected status %#x.\n", status);
369 
370     status = pRtlDosPathNameToNtPathName_U_WithStatus( emptyW, &nameW, NULL, NULL );
371     ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */,
372         "Unexpected status %#x.\n", status);
373 
374     RtlFreeUnicodeString( &nameW );
375 }
376 
377 START_TEST(path)
378 {
379     HMODULE mod = GetModuleHandleA("ntdll.dll");
380     if (!mod)
381     {
382         win_skip("Not running on NT, skipping tests\n");
383         return;
384     }
385 
386     pRtlMultiByteToUnicodeN = (void *)GetProcAddress(mod,"RtlMultiByteToUnicodeN");
387     pRtlUnicodeToMultiByteN = (void *)GetProcAddress(mod,"RtlUnicodeToMultiByteN");
388     pRtlDetermineDosPathNameType_U = (void *)GetProcAddress(mod,"RtlDetermineDosPathNameType_U");
389     pRtlIsDosDeviceName_U = (void *)GetProcAddress(mod,"RtlIsDosDeviceName_U");
390     pRtlOemStringToUnicodeString = (void *)GetProcAddress(mod,"RtlOemStringToUnicodeString");
391     pRtlIsNameLegalDOS8Dot3 = (void *)GetProcAddress(mod,"RtlIsNameLegalDOS8Dot3");
392     pRtlGetFullPathName_U = (void *)GetProcAddress(mod,"RtlGetFullPathName_U");
393     pRtlDosPathNameToNtPathName_U_WithStatus = (void *)GetProcAddress(mod, "RtlDosPathNameToNtPathName_U_WithStatus");
394 
395     test_RtlDetermineDosPathNameType_U();
396     test_RtlIsDosDeviceName_U();
397     test_RtlIsNameLegalDOS8Dot3();
398     test_RtlGetFullPathName_U();
399     test_RtlDosPathNameToNtPathName_U_WithStatus();
400 }
401