1 /*
2  * Unit test suite for volume functions
3  *
4  * Copyright 2006 Stefan Leichter
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 "precomp.h"
22 
23 #include <wine/ddk/ntddcdvd.h>
24 
25 #include <pshpack1.h>
26 struct COMPLETE_DVD_LAYER_DESCRIPTOR
27 {
28     DVD_DESCRIPTOR_HEADER Header;
29     DVD_LAYER_DESCRIPTOR Descriptor;
30     UCHAR Padding;
31 };
32 #include <poppack.h>
33 C_ASSERT(sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR) == 22);
34 
35 #include <pshpack1.h>
36 struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR
37 {
38     DVD_DESCRIPTOR_HEADER Header;
39     DVD_MANUFACTURER_DESCRIPTOR Descriptor;
40     UCHAR Padding;
41 };
42 #include <poppack.h>
43 C_ASSERT(sizeof(struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR) == 2053);
44 
45 static HINSTANCE hdll;
46 static BOOL (WINAPI * pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD);
47 static BOOL (WINAPI * pGetVolumeNameForVolumeMountPointW)(LPCWSTR, LPWSTR, DWORD);
48 static HANDLE (WINAPI *pFindFirstVolumeA)(LPSTR,DWORD);
49 static BOOL (WINAPI *pFindNextVolumeA)(HANDLE,LPSTR,DWORD);
50 static BOOL (WINAPI *pFindVolumeClose)(HANDLE);
51 static UINT (WINAPI *pGetLogicalDriveStringsA)(UINT,LPSTR);
52 static UINT (WINAPI *pGetLogicalDriveStringsW)(UINT,LPWSTR);
53 static BOOL (WINAPI *pGetVolumeInformationA)(LPCSTR, LPSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPSTR, DWORD);
54 static BOOL (WINAPI *pGetVolumePathNameA)(LPCSTR, LPSTR, DWORD);
55 static BOOL (WINAPI *pGetVolumePathNameW)(LPWSTR, LPWSTR, DWORD);
56 static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameA)(LPCSTR, LPSTR, DWORD, LPDWORD);
57 static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameW)(LPCWSTR, LPWSTR, DWORD, LPDWORD);
58 
59 /* ############################### */
60 
61 static void test_query_dos_deviceA(void)
62 {
63     char drivestr[] = "a:";
64     char *p, *buffer, buffer2[2000];
65     DWORD ret, ret2, buflen=32768;
66     BOOL found = FALSE;
67 
68     /* callers must guess the buffer size */
69     SetLastError(0xdeadbeef);
70     ret = QueryDosDeviceA( NULL, NULL, 0 );
71     ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
72        "QueryDosDeviceA(no buffer): returned %u, le=%u\n", ret, GetLastError());
73 
74     buffer = HeapAlloc( GetProcessHeap(), 0, buflen );
75     SetLastError(0xdeadbeef);
76     ret = QueryDosDeviceA( NULL, buffer, buflen );
77     ok((ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER),
78         "QueryDosDeviceA failed to return list, last error %u\n", GetLastError());
79 
80     if (ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
81         p = buffer;
82         for (;;) {
83             if (!strlen(p)) break;
84             ret2 = QueryDosDeviceA( p, buffer2, sizeof(buffer2) );
85             ok(ret2, "QueryDosDeviceA failed to return current mapping for %s, last error %u\n", p, GetLastError());
86             p += strlen(p) + 1;
87             if (ret <= (p-buffer)) break;
88         }
89     }
90 
91     for (;drivestr[0] <= 'z'; drivestr[0]++) {
92         /* Older W2K fails with ERROR_INSUFFICIENT_BUFFER when buflen is > 32767 */
93         ret = QueryDosDeviceA( drivestr, buffer, buflen - 1);
94         ok(ret || GetLastError() == ERROR_FILE_NOT_FOUND,
95             "QueryDosDeviceA failed to return current mapping for %s, last error %u\n", drivestr, GetLastError());
96         if(ret) {
97             for (p = buffer; *p; p++) *p = toupper(*p);
98             if (strstr(buffer, "HARDDISK") || strstr(buffer, "RAMDISK")) found = TRUE;
99         }
100     }
101     ok(found, "expected at least one devicename to contain HARDDISK or RAMDISK\n");
102     HeapFree( GetProcessHeap(), 0, buffer );
103 }
104 
105 static void test_define_dos_deviceA(void)
106 {
107     char drivestr[3];
108     char buf[MAX_PATH];
109     DWORD ret;
110 
111     /* Find an unused drive letter */
112     drivestr[1] = ':';
113     drivestr[2] = 0;
114     for (drivestr[0] = 'a'; drivestr[0] <= 'z'; drivestr[0]++) {
115         ret = QueryDosDeviceA( drivestr, buf, sizeof(buf));
116         if (!ret) break;
117     }
118     if (drivestr[0] > 'z') {
119         skip("can't test creating a dos drive, none available\n");
120         return;
121     }
122 
123     /* Map it to point to the current directory */
124     ret = GetCurrentDirectoryA(sizeof(buf), buf);
125     ok(ret, "GetCurrentDir\n");
126 
127     ret = DefineDosDeviceA(0, drivestr, buf);
128     todo_wine
129     ok(ret, "Could not make drive %s point to %s!\n", drivestr, buf);
130 
131     if (!ret) {
132         skip("can't test removing fake drive\n");
133     } else {
134 	ret = DefineDosDeviceA(DDD_REMOVE_DEFINITION, drivestr, NULL);
135 	ok(ret, "Could not remove fake drive %s!\n", drivestr);
136     }
137 }
138 
139 static void test_FindFirstVolume(void)
140 {
141     char volume[51];
142     HANDLE handle;
143 
144     /* not present before w2k */
145     if (!pFindFirstVolumeA) {
146         win_skip("FindFirstVolumeA not found\n");
147         return;
148     }
149 
150     handle = pFindFirstVolumeA( volume, 0 );
151     ok( handle == INVALID_HANDLE_VALUE, "succeeded with short buffer\n" );
152     ok( GetLastError() == ERROR_MORE_DATA ||  /* XP */
153         GetLastError() == ERROR_FILENAME_EXCED_RANGE,  /* Vista */
154         "wrong error %u\n", GetLastError() );
155     handle = pFindFirstVolumeA( volume, 49 );
156     ok( handle == INVALID_HANDLE_VALUE, "succeeded with short buffer\n" );
157     ok( GetLastError() == ERROR_FILENAME_EXCED_RANGE, "wrong error %u\n", GetLastError() );
158     handle = pFindFirstVolumeA( volume, 51 );
159     ok( handle != INVALID_HANDLE_VALUE, "failed err %u\n", GetLastError() );
160     if (handle != INVALID_HANDLE_VALUE)
161     {
162         do
163         {
164             ok( strlen(volume) == 49, "bad volume name %s\n", volume );
165             ok( !memcmp( volume, "\\\\?\\Volume{", 11 ), "bad volume name %s\n", volume );
166             ok( !memcmp( volume + 47, "}\\", 2 ), "bad volume name %s\n", volume );
167         } while (pFindNextVolumeA( handle, volume, MAX_PATH ));
168         ok( GetLastError() == ERROR_NO_MORE_FILES, "wrong error %u\n", GetLastError() );
169         pFindVolumeClose( handle );
170     }
171 }
172 
173 static void test_GetVolumeNameForVolumeMountPointA(void)
174 {
175     BOOL ret;
176     char volume[MAX_PATH], path[] = "c:\\";
177     DWORD len = sizeof(volume), reti;
178     char temp_path[MAX_PATH];
179 
180     /* not present before w2k */
181     if (!pGetVolumeNameForVolumeMountPointA) {
182         win_skip("GetVolumeNameForVolumeMountPointA not found\n");
183         return;
184     }
185 
186     reti = GetTempPathA(MAX_PATH, temp_path);
187     ok(reti != 0, "GetTempPathA error %d\n", GetLastError());
188     ok(reti < MAX_PATH, "temp path should fit into MAX_PATH\n");
189 
190     ret = pGetVolumeNameForVolumeMountPointA(path, volume, 0);
191     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointA succeeded\n");
192     ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE ||
193         GetLastError() == ERROR_INVALID_PARAMETER, /* Vista */
194         "wrong error, last=%d\n", GetLastError());
195 
196     if (0) { /* these crash on XP */
197     ret = pGetVolumeNameForVolumeMountPointA(path, NULL, len);
198     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointA succeeded\n");
199 
200     ret = pGetVolumeNameForVolumeMountPointA(NULL, volume, len);
201     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointA succeeded\n");
202     }
203 
204     ret = pGetVolumeNameForVolumeMountPointA(path, volume, len);
205     ok(ret == TRUE, "GetVolumeNameForVolumeMountPointA failed\n");
206     ok(!strncmp( volume, "\\\\?\\Volume{", 11),
207         "GetVolumeNameForVolumeMountPointA failed to return valid string <%s>\n",
208         volume);
209 
210     /* test with too small buffer */
211     ret = pGetVolumeNameForVolumeMountPointA(path, volume, 10);
212     ok(ret == FALSE && GetLastError() == ERROR_FILENAME_EXCED_RANGE,
213             "GetVolumeNameForVolumeMountPointA failed, wrong error returned, was %d, should be ERROR_FILENAME_EXCED_RANGE\n",
214              GetLastError());
215 
216     /* Try on an arbitrary directory */
217     /* On FAT filesystems it seems that GetLastError() is set to
218        ERROR_INVALID_FUNCTION. */
219     ret = pGetVolumeNameForVolumeMountPointA(temp_path, volume, len);
220     ok(ret == FALSE && (GetLastError() == ERROR_NOT_A_REPARSE_POINT ||
221         GetLastError() == ERROR_INVALID_FUNCTION),
222         "GetVolumeNameForVolumeMountPointA failed on %s, last=%d\n",
223         temp_path, GetLastError());
224 
225     /* Try on a nonexistent dos drive */
226     path[2] = 0;
227     for (;path[0] <= 'z'; path[0]++) {
228         ret = QueryDosDeviceA( path, volume, len);
229         if(!ret) break;
230     }
231     if (path[0] <= 'z')
232     {
233         path[2] = '\\';
234         ret = pGetVolumeNameForVolumeMountPointA(path, volume, len);
235         ok(ret == FALSE && GetLastError() == ERROR_FILE_NOT_FOUND,
236             "GetVolumeNameForVolumeMountPointA failed on %s, last=%d\n",
237             path, GetLastError());
238 
239         /* Try without trailing \ and on a nonexistent dos drive  */
240         path[2] = 0;
241         ret = pGetVolumeNameForVolumeMountPointA(path, volume, len);
242         ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
243             "GetVolumeNameForVolumeMountPointA failed on %s, last=%d\n",
244             path, GetLastError());
245     }
246 }
247 
248 static void test_GetVolumeNameForVolumeMountPointW(void)
249 {
250     BOOL ret;
251     WCHAR volume[MAX_PATH], path[] = {'c',':','\\',0};
252     DWORD len = sizeof(volume) / sizeof(WCHAR);
253 
254     /* not present before w2k */
255     if (!pGetVolumeNameForVolumeMountPointW) {
256         win_skip("GetVolumeNameForVolumeMountPointW not found\n");
257         return;
258     }
259 
260     ret = pGetVolumeNameForVolumeMountPointW(path, volume, 0);
261     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointA succeeded\n");
262     ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE ||
263         GetLastError() == ERROR_INVALID_PARAMETER, /* Vista */
264         "wrong error, last=%d\n", GetLastError());
265 
266     if (0) { /* these crash on XP */
267     ret = pGetVolumeNameForVolumeMountPointW(path, NULL, len);
268     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointW succeeded\n");
269 
270     ret = pGetVolumeNameForVolumeMountPointW(NULL, volume, len);
271     ok(ret == FALSE, "GetVolumeNameForVolumeMountPointW succeeded\n");
272     }
273 
274     ret = pGetVolumeNameForVolumeMountPointW(path, volume, len);
275     ok(ret == TRUE, "GetVolumeNameForVolumeMountPointW failed\n");
276 }
277 
278 static void test_GetLogicalDriveStringsA(void)
279 {
280     UINT size, size2;
281     char *buf, *ptr;
282 
283     ok( pGetLogicalDriveStringsA != NULL, "GetLogicalDriveStringsA not available\n");
284     if(!pGetLogicalDriveStringsA) {
285         return;
286     }
287 
288     size = pGetLogicalDriveStringsA(0, NULL);
289     ok(size%4 == 1, "size = %d\n", size);
290 
291     buf = HeapAlloc(GetProcessHeap(), 0, size);
292 
293     *buf = 0;
294     size2 = pGetLogicalDriveStringsA(2, buf);
295     ok(size2 == size, "size2 = %d\n", size2);
296     ok(!*buf, "buf changed\n");
297 
298     size2 = pGetLogicalDriveStringsA(size, buf);
299     ok(size2 == size-1, "size2 = %d\n", size2);
300 
301     for(ptr = buf; ptr < buf+size2; ptr += 4) {
302         ok(('A' <= *ptr && *ptr <= 'Z'), "device name '%c' is not uppercase\n", *ptr);
303         ok(ptr[1] == ':', "ptr[1] = %c, expected ':'\n", ptr[1]);
304         ok(ptr[2] == '\\', "ptr[2] = %c expected '\\'\n", ptr[2]);
305         ok(!ptr[3], "ptr[3] = %c expected nullbyte\n", ptr[3]);
306     }
307     ok(!*ptr, "buf[size2] is not nullbyte\n");
308 
309     HeapFree(GetProcessHeap(), 0, buf);
310 }
311 
312 static void test_GetLogicalDriveStringsW(void)
313 {
314     UINT size, size2;
315     WCHAR *buf, *ptr;
316 
317     ok( pGetLogicalDriveStringsW != NULL, "GetLogicalDriveStringsW not available\n");
318     if(!pGetLogicalDriveStringsW) {
319         return;
320     }
321 
322     SetLastError(0xdeadbeef);
323     size = pGetLogicalDriveStringsW(0, NULL);
324     if (size == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
325         win_skip("GetLogicalDriveStringsW not implemented\n");
326         return;
327     }
328     ok(size%4 == 1, "size = %d\n", size);
329 
330     buf = HeapAlloc(GetProcessHeap(), 0, size*sizeof(WCHAR));
331 
332     *buf = 0;
333     size2 = pGetLogicalDriveStringsW(2, buf);
334     ok(size2 == size, "size2 = %d\n", size2);
335     ok(!*buf, "buf changed\n");
336 
337     size2 = pGetLogicalDriveStringsW(size, buf);
338     ok(size2 == size-1, "size2 = %d\n", size2);
339 
340     for(ptr = buf; ptr < buf+size2; ptr += 4) {
341         ok('A' <= *ptr && *ptr <= 'Z', "device name '%c' is not uppercase\n", *ptr);
342         ok(ptr[1] == ':', "ptr[1] = %c, expected ':'\n", ptr[1]);
343         ok(ptr[2] == '\\', "ptr[2] = %c expected '\\'\n", ptr[2]);
344         ok(!ptr[3], "ptr[3] = %c expected nullbyte\n", ptr[3]);
345     }
346     ok(!*ptr, "buf[size2] is not nullbyte\n");
347 
348     HeapFree(GetProcessHeap(), 0, buf);
349 }
350 
351 static void test_GetVolumeInformationA(void)
352 {
353     BOOL ret;
354     UINT result;
355     char Root_Colon[]="C:";
356     char Root_Slash[]="C:\\";
357     char Root_UNC[]="\\\\?\\C:\\";
358     char volume[MAX_PATH+1];
359     DWORD vol_name_size=MAX_PATH+1, vol_serial_num=-1, max_comp_len=0, fs_flags=0, fs_name_len=MAX_PATH+1;
360     char vol_name_buf[MAX_PATH+1], fs_name_buf[MAX_PATH+1];
361     char windowsdir[MAX_PATH+10];
362     char currentdir[MAX_PATH+1];
363 
364     ok( pGetVolumeInformationA != NULL, "GetVolumeInformationA not found\n");
365     if(!pGetVolumeInformationA) {
366         return;
367     }
368 
369     /* get windows drive letter and update strings for testing */
370     result = GetWindowsDirectoryA(windowsdir, sizeof(windowsdir));
371     ok(result < sizeof(windowsdir), "windowsdir is abnormally long!\n");
372     ok(result != 0, "GetWindowsDirectory: error %d\n", GetLastError());
373     Root_Colon[0] = windowsdir[0];
374     Root_Slash[0] = windowsdir[0];
375     Root_UNC[4] = windowsdir[0];
376 
377     result = GetCurrentDirectoryA(MAX_PATH, currentdir);
378     ok(result, "GetCurrentDirectory: error %d\n", GetLastError());
379     /* Note that GetCurrentDir yields no trailing slash for subdirs */
380 
381     /* check for NO error on no trailing \ when current dir is root dir */
382     ret = SetCurrentDirectoryA(Root_Slash);
383     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
384     ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
385             NULL, NULL, fs_name_buf, fs_name_len);
386     ok(ret, "GetVolumeInformationA root failed, last error %u\n", GetLastError());
387 
388     /* check for error on no trailing \ when current dir is subdir (windows) of queried drive */
389     ret = SetCurrentDirectoryA(windowsdir);
390     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
391     SetLastError(0xdeadbeef);
392     ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
393             NULL, NULL, fs_name_buf, fs_name_len);
394     ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
395         "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
396 
397     /* reset current directory */
398     ret = SetCurrentDirectoryA(currentdir);
399     ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
400 
401     if (toupper(currentdir[0]) == toupper(windowsdir[0])) {
402         skip("Please re-run from another device than %c:\n", windowsdir[0]);
403         /* FIXME: Use GetLogicalDrives to find another device to avoid this skip. */
404     } else {
405         char Root_Env[]="=C:"; /* where MS maintains the per volume directory */
406         Root_Env[1] = windowsdir[0];
407 
408         /* C:\windows becomes the current directory on drive C: */
409         /* Note that paths to subdirs are stored without trailing slash, like what GetCurrentDir yields. */
410         ret = SetEnvironmentVariableA(Root_Env, windowsdir);
411         ok(ret, "SetEnvironmentVariable %s failed\n", Root_Env);
412 
413         ret = SetCurrentDirectoryA(windowsdir);
414         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
415         ret = SetCurrentDirectoryA(currentdir);
416         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
417 
418         /* windows dir is current on the root drive, call fails */
419         SetLastError(0xdeadbeef);
420         ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
421                 NULL, NULL, fs_name_buf, fs_name_len);
422         ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
423            "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
424 
425         /* Try normal drive letter with trailing \ */
426         ret = pGetVolumeInformationA(Root_Slash, vol_name_buf, vol_name_size, NULL,
427                 NULL, NULL, fs_name_buf, fs_name_len);
428         ok(ret, "GetVolumeInformationA with \\ failed, last error %u\n", GetLastError());
429 
430         ret = SetCurrentDirectoryA(Root_Slash);
431         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
432         ret = SetCurrentDirectoryA(currentdir);
433         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
434 
435         /* windows dir is STILL CURRENT on root drive; the call fails as before,   */
436         /* proving that SetCurrentDir did not remember the other drive's directory */
437         SetLastError(0xdeadbeef);
438         ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
439                 NULL, NULL, fs_name_buf, fs_name_len);
440         ok(!ret && (GetLastError() == ERROR_INVALID_NAME),
441            "GetVolumeInformationA did%s fail, last error %u\n", ret ? " not":"", GetLastError());
442 
443         /* Now C:\ becomes the current directory on drive C: */
444         ret = SetEnvironmentVariableA(Root_Env, Root_Slash); /* set =C:=C:\ */
445         ok(ret, "SetEnvironmentVariable %s failed\n", Root_Env);
446 
447         /* \ is current on root drive, call succeeds */
448         ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
449                 NULL, NULL, fs_name_buf, fs_name_len);
450         ok(ret, "GetVolumeInformationA failed, last error %u\n", GetLastError());
451 
452         /* again, SetCurrentDirectory on another drive does not matter */
453         ret = SetCurrentDirectoryA(Root_Slash);
454         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
455         ret = SetCurrentDirectoryA(currentdir);
456         ok(ret, "SetCurrentDirectory: error %d\n", GetLastError());
457 
458         /* \ is current on root drive, call succeeds */
459         ret = pGetVolumeInformationA(Root_Colon, vol_name_buf, vol_name_size, NULL,
460                 NULL, NULL, fs_name_buf, fs_name_len);
461         ok(ret, "GetVolumeInformationA failed, last error %u\n", GetLastError());
462     }
463 
464     /* try null root directory to return "root of the current directory"  */
465     ret = pGetVolumeInformationA(NULL, vol_name_buf, vol_name_size, NULL,
466             NULL, NULL, fs_name_buf, fs_name_len);
467     ok(ret, "GetVolumeInformationA failed on null root dir, last error %u\n", GetLastError());
468 
469     /* Try normal drive letter with trailing \  */
470     ret = pGetVolumeInformationA(Root_Slash, vol_name_buf, vol_name_size,
471             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
472     ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", Root_Slash, GetLastError());
473 
474     /* try again with drive letter and the "disable parsing" prefix */
475     SetLastError(0xdeadbeef);
476     ret = pGetVolumeInformationA(Root_UNC, vol_name_buf, vol_name_size,
477             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
478     ok(ret, "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", Root_UNC, GetLastError());
479 
480     /* try again with device name space  */
481     Root_UNC[2] = '.';
482     SetLastError(0xdeadbeef);
483     ret = pGetVolumeInformationA(Root_UNC, vol_name_buf, vol_name_size,
484             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
485     ok(ret, "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", Root_UNC, GetLastError());
486 
487     /* try again with a directory off the root - should generate error  */
488     if (windowsdir[strlen(windowsdir)-1] != '\\') strcat(windowsdir, "\\");
489     SetLastError(0xdeadbeef);
490     ret = pGetVolumeInformationA(windowsdir, vol_name_buf, vol_name_size,
491             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
492     ok(!ret && (GetLastError()==ERROR_DIR_NOT_ROOT),
493           "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", windowsdir, GetLastError());
494     /* A subdir with trailing \ yields DIR_NOT_ROOT instead of INVALID_NAME */
495     if (windowsdir[strlen(windowsdir)-1] == '\\') windowsdir[strlen(windowsdir)-1] = 0;
496     SetLastError(0xdeadbeef);
497     ret = pGetVolumeInformationA(windowsdir, vol_name_buf, vol_name_size,
498             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
499     ok(!ret && (GetLastError()==ERROR_INVALID_NAME),
500           "GetVolumeInformationA did%s fail, root=%s, last error=%u\n", ret ? " not":"", windowsdir, GetLastError());
501 
502     if (!pGetVolumeNameForVolumeMountPointA) {
503         win_skip("GetVolumeNameForVolumeMountPointA not found\n");
504         return;
505     }
506     /* get the unique volume name for the windows drive  */
507     ret = pGetVolumeNameForVolumeMountPointA(Root_Slash, volume, MAX_PATH);
508     ok(ret == TRUE, "GetVolumeNameForVolumeMountPointA failed\n");
509 
510     /* try again with unique volume name */
511     ret = pGetVolumeInformationA(volume, vol_name_buf, vol_name_size,
512             &vol_serial_num, &max_comp_len, &fs_flags, fs_name_buf, fs_name_len);
513     ok(ret, "GetVolumeInformationA failed, root=%s, last error=%u\n", volume, GetLastError());
514 }
515 
516 /* Test to check that unique volume name from windows dir mount point  */
517 /* matches at least one of the unique volume names returned from the   */
518 /* FindFirstVolumeA/FindNextVolumeA list.                              */
519 static void test_enum_vols(void)
520 {
521     DWORD   ret;
522     HANDLE  hFind = INVALID_HANDLE_VALUE;
523     char    Volume_1[MAX_PATH] = {0};
524     char    Volume_2[MAX_PATH] = {0};
525     char    path[] = "c:\\";
526     BOOL    found = FALSE;
527     char    windowsdir[MAX_PATH];
528 
529     if (!pGetVolumeNameForVolumeMountPointA) {
530         win_skip("GetVolumeNameForVolumeMountPointA not found\n");
531         return;
532     }
533 
534     /*get windows drive letter and update strings for testing  */
535     ret = GetWindowsDirectoryA( windowsdir, sizeof(windowsdir) );
536     ok(ret < sizeof(windowsdir), "windowsdir is abnormally long!\n");
537     ok(ret != 0, "GetWindowsDirectory: error %d\n", GetLastError());
538     path[0] = windowsdir[0];
539 
540     /* get the unique volume name for the windows drive  */
541     ret = pGetVolumeNameForVolumeMountPointA( path, Volume_1, MAX_PATH );
542     ok(ret == TRUE, "GetVolumeNameForVolumeMountPointA failed\n");
543     ok(strlen(Volume_1) == 49, "GetVolumeNameForVolumeMountPointA returned wrong length name %s\n", Volume_1);
544 
545     /* get first unique volume name of list  */
546     hFind = pFindFirstVolumeA( Volume_2, MAX_PATH );
547     ok(hFind != INVALID_HANDLE_VALUE, "FindFirstVolume failed, err=%u\n",
548                 GetLastError());
549     /* ReactOS */
550     if (hFind != INVALID_HANDLE_VALUE) {
551     do
552     {
553         /* validate correct length of unique volume name  */
554         ok(strlen(Volume_2) == 49, "Find[First/Next]Volume returned wrong length name %s\n", Volume_1);
555         if (memcmp(Volume_1, Volume_2, 49) == 0)
556         {
557             found = TRUE;
558             break;
559         }
560     } while (pFindNextVolumeA( hFind, Volume_2, MAX_PATH ));
561     ok(found, "volume name %s not found by Find[First/Next]Volume\n", Volume_1);
562     pFindVolumeClose( hFind );
563     }
564 }
565 
566 static void test_disk_extents(void)
567 {
568     BOOL ret;
569     DWORD size;
570     HANDLE handle;
571     static DWORD data[16];
572 
573     handle = CreateFileA( "\\\\.\\c:", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
574     if (handle == INVALID_HANDLE_VALUE)
575     {
576         win_skip("can't open c: drive %u\n", GetLastError());
577         return;
578     }
579     size = 0;
580     ret = DeviceIoControl( handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, &data,
581                            sizeof(data), &data, sizeof(data), &size, NULL );
582     if (!ret && GetLastError() == ERROR_INVALID_FUNCTION)
583     {
584         win_skip("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS not supported\n");
585         CloseHandle( handle );
586         return;
587     }
588     ok(ret, "DeviceIoControl failed %u\n", GetLastError());
589     ok(size == 32, "expected 32, got %u\n", size);
590     CloseHandle( handle );
591 }
592 
593 static void test_GetVolumePathNameA(void)
594 {
595     char volume_path[MAX_PATH], cwd[MAX_PATH];
596     struct {
597         const char *file_name;
598         const char *path_name;
599         DWORD       path_len;
600         DWORD       error;
601         DWORD       broken_error;
602     } test_paths[] = {
603         { /* test 0: NULL parameters, 0 output length */
604             NULL, NULL, 0,
605             ERROR_INVALID_PARAMETER, 0xdeadbeef /* winxp */
606         },
607         { /* test 1: empty input, NULL output, 0 output length */
608             "", NULL, 0,
609             ERROR_INVALID_PARAMETER, 0xdeadbeef /* winxp */
610         },
611         { /* test 2: valid input, NULL output, 0 output length */
612             "C:\\", NULL, 0,
613             ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* winxp */
614         },
615         { /* test 3: valid input, valid output, 0 output length */
616             "C:\\", "C:\\", 0,
617             ERROR_INVALID_PARAMETER, ERROR_FILENAME_EXCED_RANGE /* winxp */
618         },
619         { /* test 4: valid input, valid output, 1 output length */
620             "C:\\", "C:\\", 1,
621             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
622         },
623         { /* test 5: valid input, valid output, valid output length */
624             "C:\\", "C:\\", sizeof(volume_path),
625             NO_ERROR, NO_ERROR
626         },
627         { /* test 6: lowercase input, uppercase output, valid output length */
628             "c:\\", "C:\\", sizeof(volume_path),
629             NO_ERROR, NO_ERROR
630         },
631         { /* test 7: poor quality input, valid output, valid output length */
632             "C::", "C:\\", sizeof(volume_path),
633             NO_ERROR, NO_ERROR
634         },
635         { /* test 8: really bogus input, valid output, 1 output length */
636             "\\\\$$$", "C:\\", 1,
637             ERROR_INVALID_NAME, ERROR_FILENAME_EXCED_RANGE
638         },
639         { /* test 9: a reasonable DOS path that is guaranteed to exist */
640             "C:\\windows\\system32", "C:\\", sizeof(volume_path),
641             NO_ERROR, NO_ERROR
642         },
643         { /* test 10: a reasonable DOS path that shouldn't exist */
644             "C:\\windows\\system32\\AnInvalidFolder", "C:\\", sizeof(volume_path),
645             NO_ERROR, NO_ERROR
646         },
647         { /* test 11: a reasonable NT-converted DOS path that shouldn't exist */
648             "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:\\", sizeof(volume_path),
649             NO_ERROR, NO_ERROR
650         },
651         { /* test 12: an unreasonable NT-converted DOS path */
652             "\\\\?\\InvalidDrive:\\AnInvalidFolder", "\\\\?\\InvalidDrive:\\" /* win2k, winxp */,
653             sizeof(volume_path),
654             ERROR_INVALID_NAME, NO_ERROR
655         },
656         { /* test 13: an unreasonable NT volume path */
657             "\\\\?\\Volume{00000000-00-0000-0000-000000000000}\\AnInvalidFolder",
658             "\\\\?\\Volume{00000000-00-0000-0000-000000000000}\\" /* win2k, winxp */,
659             sizeof(volume_path),
660             ERROR_INVALID_NAME, NO_ERROR
661         },
662         { /* test 14: an unreasonable NT-ish path */
663             "\\\\ReallyBogus\\InvalidDrive:\\AnInvalidFolder",
664             "\\\\ReallyBogus\\InvalidDrive:\\" /* win2k, winxp */, sizeof(volume_path),
665             ERROR_INVALID_NAME, NO_ERROR
666         },
667         { /* test 15: poor quality input, valid output, valid (but short) output length */
668             "C::", "C:\\", 4,
669             NO_ERROR, ERROR_MORE_DATA
670         },
671         { /* test 16: unused drive letter */
672             "M::", "C:\\", 4,
673             ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA
674         },
675         { /* test 17: an unreasonable DOS path */
676             "InvalidDrive:\\AnInvalidFolder", "%CurrentDrive%\\", sizeof(volume_path),
677             NO_ERROR, NO_ERROR
678         },
679         { /* test 18: a reasonable device path */
680             "\\??\\CdRom0", "%CurrentDrive%\\", sizeof(volume_path),
681             NO_ERROR, NO_ERROR
682         },
683         { /* test 19: an unreasonable device path */
684             "\\??\\ReallyBogus", "%CurrentDrive%\\", sizeof(volume_path),
685             NO_ERROR, NO_ERROR
686         },
687         { /* test 20 */
688             "C:", "C:", 2,
689             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
690         },
691         { /* test 21 */
692             "C:", "C:", 3,
693             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
694         },
695         { /* test 22 */
696             "C:\\", "C:", 2,
697             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
698         },
699         { /* test 23 */
700             "C:\\", "C:", 3,
701             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
702         },
703         { /* test 24 */
704             "C::", "C:", 2,
705             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
706         },
707         { /* test 25 */
708             "C::", "C:", 3,
709             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
710         },
711         { /* test 26 */
712             "C::", "C:\\", 4,
713             NO_ERROR, ERROR_MORE_DATA
714         },
715         { /* test 27 */
716             "C:\\windows\\system32\\AnInvalidFolder", "C:", 3,
717             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
718         },
719         { /* test 28 */
720             "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 3,
721             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
722         },
723         { /* test 29 */
724             "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 6,
725             ERROR_FILENAME_EXCED_RANGE, NO_ERROR
726         },
727         { /* test 30 */
728             "\\\\?\\C:\\AnInvalidFolder", "\\\\?\\C:", 7,
729             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
730         },
731         { /* test 31 */
732             "\\\\?\\c:\\AnInvalidFolder", "\\\\?\\c:", 7,
733             NO_ERROR, ERROR_FILENAME_EXCED_RANGE
734         },
735         { /* test 32 */
736             "C:/", "C:\\", 4,
737             NO_ERROR, ERROR_MORE_DATA
738         },
739         { /* test 33 */
740             "M:/", "", 4,
741             ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA
742         },
743         { /* test 34 */
744             "C:ABC:DEF:\\AnInvalidFolder", "C:\\", 4,
745             NO_ERROR, ERROR_MORE_DATA
746         },
747         { /* test 35 */
748             "?:ABC:DEF:\\AnInvalidFolder", "?:\\" /* win2k, winxp */, sizeof(volume_path),
749             ERROR_FILE_NOT_FOUND, NO_ERROR
750         },
751         { /* test 36 */
752             "relative/path", "%CurrentDrive%\\", sizeof(volume_path),
753             NO_ERROR, NO_ERROR
754         },
755         { /* test 37 */
756             "/unix-style/absolute/path", "%CurrentDrive%\\", sizeof(volume_path),
757             NO_ERROR, NO_ERROR
758         },
759         { /* test 38 */
760             "\\??\\C:\\NonExistent", "%CurrentDrive%\\", sizeof(volume_path),
761             NO_ERROR, NO_ERROR
762         },
763         { /* test 39 */
764             "\\??\\M:\\NonExistent", "%CurrentDrive%\\", sizeof(volume_path),
765             NO_ERROR, NO_ERROR
766         },
767         { /* test 40 */
768             "somefile:def", "%CurrentDrive%\\", sizeof(volume_path),
769             NO_ERROR, NO_ERROR
770         },
771         { /* test 41 */
772             "s:omefile", "S:\\" /* win2k, winxp */, sizeof(volume_path),
773             ERROR_FILE_NOT_FOUND, NO_ERROR
774         },
775     };
776     BOOL ret, success;
777     DWORD error;
778     UINT i;
779 
780     /* GetVolumePathNameA is not present before w2k */
781     if (!pGetVolumePathNameA)
782     {
783         win_skip("required functions not found\n");
784         return;
785     }
786 
787     /* Obtain the drive of the working directory */
788     ret = GetCurrentDirectoryA( sizeof(cwd), cwd );
789     ok( ret, "Failed to obtain the current working directory.\n" );
790     cwd[2] = 0;
791     ret = SetEnvironmentVariableA( "CurrentDrive", cwd );
792     ok( ret, "Failed to set an environment variable for the current working drive.\n" );
793 
794     for (i=0; i<sizeof(test_paths)/sizeof(test_paths[0]); i++)
795     {
796         BOOL broken_ret = test_paths[i].broken_error == NO_ERROR;
797         char *output = (test_paths[i].path_name != NULL ? volume_path : NULL);
798         BOOL expected_ret = test_paths[i].error == NO_ERROR;
799 
800         volume_path[0] = 0;
801         if (test_paths[i].path_len < sizeof(volume_path))
802             volume_path[ test_paths[i].path_len ] = 0x11;
803 
804         SetLastError( 0xdeadbeef );
805         ret = pGetVolumePathNameA( test_paths[i].file_name, output, test_paths[i].path_len );
806         error = GetLastError();
807         ok(ret == expected_ret || broken(ret == broken_ret),
808                                 "GetVolumePathName test %d %s unexpectedly.\n",
809                                 i, test_paths[i].error == NO_ERROR ? "failed" : "succeeded");
810 
811         if (ret)
812         {
813             char path_name[MAX_PATH];
814 
815             ExpandEnvironmentStringsA( test_paths[i].path_name, path_name, MAX_PATH);
816             /* If we succeeded then make sure the path is correct */
817             success = (strcmp( volume_path, path_name ) == 0)
818                       || broken(strcasecmp( volume_path, path_name ) == 0) /* XP */;
819             ok(success, "GetVolumePathName test %d unexpectedly returned path %s (expected %s).\n",
820                         i, volume_path, path_name);
821         }
822         else
823         {
824             /* On success Windows always returns ERROR_MORE_DATA, so only worry about failure */
825             success = (error == test_paths[i].error || broken(error == test_paths[i].broken_error));
826             ok(success, "GetVolumePathName test %d unexpectedly returned error 0x%x (expected 0x%x).\n",
827                         i, error, test_paths[i].error);
828         }
829 
830         if (test_paths[i].path_len < sizeof(volume_path))
831             ok(volume_path[ test_paths[i].path_len ] == 0x11,
832                "GetVolumePathName test %d corrupted byte after end of buffer.\n", i);
833     }
834 }
835 
836 static void test_GetVolumePathNameW(void)
837 {
838     static WCHAR drive_c1[] = {'C',':',0};
839     static WCHAR drive_c2[] = {'C',':','\\',0};
840     WCHAR volume_path[MAX_PATH];
841     BOOL ret;
842 
843     if (!pGetVolumePathNameW)
844     {
845         win_skip("required functions not found\n");
846         return;
847     }
848 
849     volume_path[0] = 0;
850     volume_path[1] = 0x11;
851     ret = pGetVolumePathNameW( drive_c1, volume_path, 1 );
852     ok(!ret, "GetVolumePathNameW test succeeded unexpectedly.\n");
853     ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE, "GetVolumePathNameW unexpectedly returned error 0x%x (expected 0x%x).\n",
854         GetLastError(), ERROR_FILENAME_EXCED_RANGE);
855     ok(volume_path[1] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
856 
857     volume_path[0] = 0;
858     volume_path[2] = 0x11;
859     ret = pGetVolumePathNameW( drive_c1, volume_path, 2 );
860     ok(!ret, "GetVolumePathNameW test succeeded unexpectedly.\n");
861     ok(GetLastError() == ERROR_FILENAME_EXCED_RANGE, "GetVolumePathNameW unexpectedly returned error 0x%x (expected 0x%x).\n",
862         GetLastError(), ERROR_FILENAME_EXCED_RANGE);
863     ok(volume_path[2] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
864 
865     volume_path[0] = 0;
866     volume_path[3] = 0x11;
867     ret = pGetVolumePathNameW( drive_c1, volume_path, 3 );
868     ok(ret || broken(!ret) /* win2k */, "GetVolumePathNameW test failed unexpectedly.\n");
869     ok(memcmp(volume_path, drive_c1, sizeof(drive_c1)) == 0
870        || broken(volume_path[0] == 0) /* win2k */,
871        "GetVolumePathNameW unexpectedly returned wrong path.\n");
872     ok(volume_path[3] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
873 
874     volume_path[0] = 0;
875     volume_path[4] = 0x11;
876     ret = pGetVolumePathNameW( drive_c1, volume_path, 4 );
877     ok(ret, "GetVolumePathNameW test failed unexpectedly.\n");
878     ok(memcmp(volume_path, drive_c2, sizeof(drive_c2)) == 0, "GetVolumePathNameW unexpectedly returned wrong path.\n");
879     ok(volume_path[4] == 0x11, "GetVolumePathW corrupted byte after end of buffer.\n");
880 }
881 
882 static void test_GetVolumePathNamesForVolumeNameA(void)
883 {
884     BOOL ret;
885     char volume[MAX_PATH], buffer[MAX_PATH];
886     DWORD len, error;
887 
888     if (!pGetVolumePathNamesForVolumeNameA || !pGetVolumeNameForVolumeMountPointA)
889     {
890         win_skip("required functions not found\n");
891         return;
892     }
893 
894     ret = pGetVolumeNameForVolumeMountPointA( "c:\\", volume, sizeof(volume) );
895     ok(ret, "failed to get volume name %u\n", GetLastError());
896     trace("c:\\ -> %s\n", volume);
897 
898     SetLastError( 0xdeadbeef );
899     ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, NULL );
900     error = GetLastError();
901     ok(!ret, "expected failure\n");
902     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
903 
904     SetLastError( 0xdeadbeef );
905     ret = pGetVolumePathNamesForVolumeNameA( "", NULL, 0, NULL );
906     error = GetLastError();
907     ok(!ret, "expected failure\n");
908     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
909 
910     SetLastError( 0xdeadbeef );
911     ret = pGetVolumePathNamesForVolumeNameA( volume, NULL, 0, NULL );
912     error = GetLastError();
913     ok(!ret, "expected failure\n");
914     ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
915 
916     SetLastError( 0xdeadbeef );
917     ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, 0, NULL );
918     error = GetLastError();
919     ok(!ret, "expected failure\n");
920     ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
921 
922     memset( buffer, 0xff, sizeof(buffer) );
923     ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), NULL );
924     ok(ret, "failed to get path names %u\n", GetLastError());
925     ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer);
926     ok(!buffer[4], "expected double null-terminated buffer\n");
927 
928     len = 0;
929     SetLastError( 0xdeadbeef );
930     ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, 0, &len );
931     error = GetLastError();
932     ok(!ret, "expected failure\n");
933     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
934 
935     len = 0;
936     SetLastError( 0xdeadbeef );
937     ret = pGetVolumePathNamesForVolumeNameA( NULL, NULL, sizeof(buffer), &len );
938     error = GetLastError();
939     ok(!ret, "expected failure\n");
940     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
941 
942     len = 0;
943     SetLastError( 0xdeadbeef );
944     ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len );
945     error = GetLastError();
946     ok(!ret, "expected failure\n");
947     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
948 
949     len = 0;
950     SetLastError( 0xdeadbeef );
951     ret = pGetVolumePathNamesForVolumeNameA( NULL, buffer, sizeof(buffer), &len );
952     error = GetLastError();
953     ok(!ret, "expected failure\n");
954     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
955 
956     len = 0;
957     memset( buffer, 0xff, sizeof(buffer) );
958     ret = pGetVolumePathNamesForVolumeNameA( volume, buffer, sizeof(buffer), &len );
959     ok(ret, "failed to get path names %u\n", GetLastError());
960     ok(len == 5 || broken(len == 2), "expected 5 got %u\n", len);
961     ok(!strcmp( "C:\\", buffer ), "expected \"\\C:\" got \"%s\"\n", buffer);
962     ok(!buffer[4], "expected double null-terminated buffer\n");
963 }
964 
965 static void test_GetVolumePathNamesForVolumeNameW(void)
966 {
967     static const WCHAR empty[] = {0};
968     static const WCHAR drive_c[] = {'c',':','\\',0};
969     static const WCHAR volume_null[] = {'\\','\\','?','\\','V','o','l','u','m','e',
970         '{','0','0','0','0','0','0','0','0','-','0','0','0','0','-','0','0','0','0',
971         '-','0','0','0','0','-','0','0','0','0','0','0','0','0','0','0','0','0','}','\\',0};
972     BOOL ret;
973     WCHAR volume[MAX_PATH], buffer[MAX_PATH];
974     DWORD len, error;
975 
976 #ifdef __REACTOS__
977     /* due to failing all calls to GetVolumeNameForVolumeMountPointW, this
978      * buffer never gets initialized and could cause a buffer overflow later */
979     volume[0] = '$';
980     volume[1] = 0;
981 #endif /* __REACTOS__ */
982 
983     if (!pGetVolumePathNamesForVolumeNameW || !pGetVolumeNameForVolumeMountPointW)
984     {
985         win_skip("required functions not found\n");
986         return;
987     }
988 
989     ret = pGetVolumeNameForVolumeMountPointW( drive_c, volume, sizeof(volume)/sizeof(volume[0]) );
990     ok(ret, "failed to get volume name %u\n", GetLastError());
991 
992     SetLastError( 0xdeadbeef );
993     ret = pGetVolumePathNamesForVolumeNameW( empty, NULL, 0, NULL );
994     error = GetLastError();
995     ok(!ret, "expected failure\n");
996     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
997 
998     SetLastError( 0xdeadbeef );
999     ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, 0, NULL );
1000     error = GetLastError();
1001     ok(!ret, "expected failure\n");
1002     ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
1003 
1004     SetLastError( 0xdeadbeef );
1005     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, 0, NULL );
1006     error = GetLastError();
1007     ok(!ret, "expected failure\n");
1008     ok(error == ERROR_MORE_DATA, "expected ERROR_MORE_DATA got %u\n", error);
1009 
1010     if (0) { /* crash */
1011     ret = pGetVolumePathNamesForVolumeNameW( volume, NULL, sizeof(buffer), NULL );
1012     ok(ret, "failed to get path names %u\n", GetLastError());
1013     }
1014 
1015     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), NULL );
1016     ok(ret, "failed to get path names %u\n", GetLastError());
1017 
1018     len = 0;
1019     memset( buffer, 0xff, sizeof(buffer) );
1020     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
1021     ok(ret, "failed to get path names %u\n", GetLastError());
1022     ok(len == 5, "expected 5 got %u\n", len);
1023     ok(!buffer[4], "expected double null-terminated buffer\n");
1024 
1025     len = 0;
1026     volume[1] = '?';
1027     volume[lstrlenW( volume ) - 1] = 0;
1028     SetLastError( 0xdeadbeef );
1029     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
1030     error = GetLastError();
1031     ok(!ret, "expected failure\n");
1032     ok(error == ERROR_INVALID_NAME, "expected ERROR_INVALID_NAME got %u\n", error);
1033 
1034     len = 0;
1035     volume[0] = '\\';
1036     volume[1] = 0;
1037     SetLastError( 0xdeadbeef );
1038     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
1039     error = GetLastError();
1040     ok(!ret, "expected failure\n");
1041     todo_wine ok(error == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", error);
1042 
1043     len = 0;
1044     lstrcpyW( volume, volume_null );
1045     SetLastError( 0xdeadbeef );
1046     ret = pGetVolumePathNamesForVolumeNameW( volume, buffer, sizeof(buffer), &len );
1047     error = GetLastError();
1048     ok(!ret, "expected failure\n");
1049     ok(error == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND got %u\n", error);
1050 }
1051 
1052 static void test_dvd_read_structure(HANDLE handle)
1053 {
1054     int i;
1055     DWORD nbBytes;
1056     BOOL ret;
1057     DVD_READ_STRUCTURE dvdReadStructure;
1058     DVD_LAYER_DESCRIPTOR dvdLayerDescriptor;
1059     struct COMPLETE_DVD_LAYER_DESCRIPTOR completeDvdLayerDescriptor;
1060     DVD_COPYRIGHT_DESCRIPTOR dvdCopyrightDescriptor;
1061     struct COMPLETE_DVD_MANUFACTURER_DESCRIPTOR completeDvdManufacturerDescriptor;
1062 
1063     dvdReadStructure.BlockByteOffset.QuadPart = 0;
1064     dvdReadStructure.SessionId = 0;
1065     dvdReadStructure.LayerNumber = 0;
1066 
1067 
1068     /* DvdPhysicalDescriptor */
1069     dvdReadStructure.Format = 0;
1070 
1071     SetLastError(0xdeadbeef);
1072 
1073     /* Test whether this ioctl is supported */
1074     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1075         &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
1076 
1077     if(!ret)
1078     {
1079         skip("IOCTL_DVD_READ_STRUCTURE not supported: %u\n", GetLastError());
1080         return;
1081     }
1082 
1083     /* Confirm there is always a header before the actual data */
1084     ok( completeDvdLayerDescriptor.Header.Length == 0x0802, "Length is 0x%04x instead of 0x0802\n", completeDvdLayerDescriptor.Header.Length);
1085     ok( completeDvdLayerDescriptor.Header.Reserved[0] == 0, "Reserved[0] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[0]);
1086     ok( completeDvdLayerDescriptor.Header.Reserved[1] == 0, "Reserved[1] is %x instead of 0\n", completeDvdLayerDescriptor.Header.Reserved[1]);
1087 
1088     /* TODO: Also check completeDvdLayerDescriptor.Descriptor content (via IOCTL_SCSI_PASS_THROUGH_DIRECT ?) */
1089 
1090     /* Insufficient output buffer */
1091     for(i=0; i<sizeof(DVD_DESCRIPTOR_HEADER); i++)
1092     {
1093         SetLastError(0xdeadbeef);
1094 
1095         ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1096             &completeDvdLayerDescriptor, i, &nbBytes, NULL);
1097         ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,"IOCTL_DVD_READ_STRUCTURE should fail with small buffer\n");
1098     }
1099 
1100     SetLastError(0xdeadbeef);
1101 
1102     /* On newer version, an output buffer of sizeof(DVD_READ_STRUCTURE) size fails.
1103         I think this is to force developers to realize that there is a header before the actual content */
1104     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1105         &dvdLayerDescriptor, sizeof(DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
1106     ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER) || broken(ret) /* < Win7 */,
1107         "IOCTL_DVD_READ_STRUCTURE should have failed\n");
1108 
1109     SetLastError(0xdeadbeef);
1110 
1111     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, NULL, sizeof(DVD_READ_STRUCTURE),
1112         &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
1113     ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER),
1114         "IOCTL_DVD_READ_STRUCTURE should have failed\n");
1115 
1116     /* Test wrong input parameters */
1117     for(i=0; i<sizeof(DVD_READ_STRUCTURE); i++)
1118     {
1119         SetLastError(0xdeadbeef);
1120 
1121         ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, i,
1122         &completeDvdLayerDescriptor, sizeof(struct COMPLETE_DVD_LAYER_DESCRIPTOR), &nbBytes, NULL);
1123         ok( (!ret && GetLastError() == ERROR_INVALID_PARAMETER),
1124             "IOCTL_DVD_READ_STRUCTURE should have failed\n");
1125     }
1126 
1127 
1128     /* DvdCopyrightDescriptor */
1129     dvdReadStructure.Format = 1;
1130 
1131     SetLastError(0xdeadbeef);
1132 
1133     /* Strangely, with NULL lpOutBuffer, last error is insufficient buffer, not invalid parameter as we could expect */
1134     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1135         NULL, sizeof(DVD_COPYRIGHT_DESCRIPTOR), &nbBytes, NULL);
1136     ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
1137 
1138     for(i=0; i<sizeof(DVD_COPYRIGHT_DESCRIPTOR); i++)
1139     {
1140         SetLastError(0xdeadbeef);
1141 
1142         ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1143             &dvdCopyrightDescriptor, i, &nbBytes, NULL);
1144         ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
1145     }
1146 
1147 
1148     /* DvdManufacturerDescriptor */
1149     dvdReadStructure.Format = 4;
1150 
1151     SetLastError(0xdeadbeef);
1152 
1153     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1154         &completeDvdManufacturerDescriptor, sizeof(DVD_MANUFACTURER_DESCRIPTOR), &nbBytes, NULL);
1155     ok(ret || broken(GetLastError() == ERROR_NOT_READY),
1156         "IOCTL_DVD_READ_STRUCTURE (DvdManufacturerDescriptor) failed, last error = %u\n", GetLastError());
1157     if(!ret)
1158         return;
1159 
1160     /* Confirm there is always a header before the actual data */
1161     ok( completeDvdManufacturerDescriptor.Header.Length == 0x0802, "Length is 0x%04x instead of 0x0802\n", completeDvdManufacturerDescriptor.Header.Length);
1162     ok( completeDvdManufacturerDescriptor.Header.Reserved[0] == 0, "Reserved[0] is %x instead of 0\n", completeDvdManufacturerDescriptor.Header.Reserved[0]);
1163     ok( completeDvdManufacturerDescriptor.Header.Reserved[1] == 0, "Reserved[1] is %x instead of 0\n", completeDvdManufacturerDescriptor.Header.Reserved[1]);
1164 
1165     SetLastError(0xdeadbeef);
1166 
1167     /* Basic parameter check */
1168     ret = DeviceIoControl(handle, IOCTL_DVD_READ_STRUCTURE, &dvdReadStructure, sizeof(DVD_READ_STRUCTURE),
1169         NULL, sizeof(DVD_MANUFACTURER_DESCRIPTOR), &nbBytes, NULL);
1170     ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "IOCTL_DVD_READ_STRUCTURE should have failed %d %u\n", ret, GetLastError());
1171 }
1172 
1173 static void test_cdrom_ioctl(void)
1174 {
1175     char drive_letter, drive_path[] = "A:\\", drive_full_path[] = "\\\\.\\A:";
1176     DWORD bitmask;
1177     HANDLE handle;
1178 
1179     bitmask = GetLogicalDrives();
1180     if(!bitmask)
1181     {
1182         trace("GetLogicalDrives failed : %u\n", GetLastError());
1183         return;
1184     }
1185 
1186     for(drive_letter='A'; drive_letter<='Z'; drive_letter++)
1187     {
1188         if(!(bitmask & (1 << (drive_letter-'A') )))
1189             continue;
1190 
1191         drive_path[0] = drive_letter;
1192         if(GetDriveTypeA(drive_path) != DRIVE_CDROM)
1193         {
1194             trace("Skipping %c:, not a CDROM drive.\n", drive_letter);
1195             continue;
1196         }
1197 
1198         trace("Testing with %c:\n", drive_letter);
1199 
1200         drive_full_path[4] = drive_letter;
1201         handle = CreateFileA(drive_full_path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
1202         if(handle == INVALID_HANDLE_VALUE)
1203         {
1204             trace("Failed to open the device : %u\n", GetLastError());
1205             continue;
1206         }
1207 
1208         /* Add your tests here */
1209         test_dvd_read_structure(handle);
1210 
1211         CloseHandle(handle);
1212     }
1213 
1214 }
1215 
1216 START_TEST(volume)
1217 {
1218     hdll = GetModuleHandleA("kernel32.dll");
1219     pGetVolumeNameForVolumeMountPointA = (void *) GetProcAddress(hdll, "GetVolumeNameForVolumeMountPointA");
1220     pGetVolumeNameForVolumeMountPointW = (void *) GetProcAddress(hdll, "GetVolumeNameForVolumeMountPointW");
1221     pFindFirstVolumeA = (void *) GetProcAddress(hdll, "FindFirstVolumeA");
1222     pFindNextVolumeA = (void *) GetProcAddress(hdll, "FindNextVolumeA");
1223     pFindVolumeClose = (void *) GetProcAddress(hdll, "FindVolumeClose");
1224     pGetLogicalDriveStringsA = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsA");
1225     pGetLogicalDriveStringsW = (void *) GetProcAddress(hdll, "GetLogicalDriveStringsW");
1226     pGetVolumeInformationA = (void *) GetProcAddress(hdll, "GetVolumeInformationA");
1227     pGetVolumePathNameA = (void *) GetProcAddress(hdll, "GetVolumePathNameA");
1228     pGetVolumePathNameW = (void *) GetProcAddress(hdll, "GetVolumePathNameW");
1229     pGetVolumePathNamesForVolumeNameA = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameA");
1230     pGetVolumePathNamesForVolumeNameW = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameW");
1231 
1232     test_query_dos_deviceA();
1233     test_define_dos_deviceA();
1234     test_FindFirstVolume();
1235     test_GetVolumePathNameA();
1236     test_GetVolumePathNameW();
1237     test_GetVolumeNameForVolumeMountPointA();
1238     test_GetVolumeNameForVolumeMountPointW();
1239     test_GetLogicalDriveStringsA();
1240     test_GetLogicalDriveStringsW();
1241     test_GetVolumeInformationA();
1242     test_enum_vols();
1243     test_disk_extents();
1244     test_GetVolumePathNamesForVolumeNameA();
1245     test_GetVolumePathNamesForVolumeNameW();
1246     test_cdrom_ioctl();
1247 }
1248