1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for DeviceIoControl
5  * PROGRAMMER:      Pierre Schweitzer <pierre@reactos.org>
6  */
7 
8 #include "precomp.h"
9 
10 #include <winioctl.h>
11 #include <mountdev.h>
12 
13 WCHAR Letter;
14 HANDLE Device;
15 UINT DriveType;
16 
17 #define ok_type(condition, format, ...) ok(condition, "(%d): " format, DriveType,  ##__VA_ARGS__)
18 #define skip_type(format, ...) skip("(%d): " format, DriveType, ##__VA_ARGS__)
19 
20 static
21 BOOL
22 GetDiskGeometry(VOID)
23 {
24     UINT Ret;
25     DISK_GEOMETRY DG;
26     DWORD Size, Error;
27     DISK_GEOMETRY_EX DGE;
28 
29     Size = 0;
30     Ret = DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DG, sizeof(DG) - 1, &Size, NULL);
31     ok_type(Ret == 0, "DeviceIoControl succeed\n");
32     Error = GetLastError();
33     ok_type(Error == ERROR_INSUFFICIENT_BUFFER, "Expecting ERROR_INSUFFICIENT_BUFFER, got %ld\n", Error);
34     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
35 
36     Size = 0;
37     Ret = DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DG, sizeof(DG), &Size, NULL);
38     /* Specific for CDROM, no disk present */
39     if (Ret == 0 && GetLastError() == ERROR_NOT_READY)
40     {
41         skip_type("No CDROM present\n");
42         return FALSE;
43     }
44     ok_type(Ret != 0, "DeviceIoControl failed: %ld\n", GetLastError());
45     ok_type(Size == sizeof(DG), "Invalid output size: %ld\n", Size);
46 
47     Size = 0;
48     Ret = DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &DGE, FIELD_OFFSET(DISK_GEOMETRY_EX, Data) - 1, &Size, NULL);
49     ok_type(Ret == 0, "DeviceIoControl succeed\n");
50     Error = GetLastError();
51     ok_type(Error == ERROR_INSUFFICIENT_BUFFER, "Expecting ERROR_INSUFFICIENT_BUFFER, got %ld\n", Error);
52     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
53 
54     Size = 0;
55     Ret = DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &DGE, FIELD_OFFSET(DISK_GEOMETRY_EX, Data), &Size, NULL);
56     ok_type(Ret != 0, "DeviceIoControl failed: %ld\n", GetLastError());
57     ok_type(Size == FIELD_OFFSET(DISK_GEOMETRY_EX, Data), "Invalid output size: %ld\n", Size);
58 
59     Size = 0;
60     Ret = DeviceIoControl(Device, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &DGE, sizeof(DGE), &Size, NULL);
61     ok_type(Ret != 0, "DeviceIoControl failed: %ld\n", GetLastError());
62     if (DriveType == DRIVE_FIXED)
63     {
64         ok_type(Size == sizeof(DGE), "Invalid output size: %ld\n", Size);
65     }
66     else
67     {
68         ok_type(Size == FIELD_OFFSET(DISK_GEOMETRY_EX, Data), "Invalid output size: %ld\n", Size);
69     }
70 
71     return TRUE;
72 }
73 
74 static
75 VOID
76 QueryDeviceName(VOID)
77 {
78     UINT Ret;
79     BOOL IsValid;
80     DWORD Size, Error;
81     MOUNTDEV_NAME MDN, *AllocatedMDN;
82 
83     Size = 0;
84     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &MDN, sizeof(MDN) - 1, &Size, NULL);
85     ok_type(Ret == 0, "DeviceIoControl succeed\n");
86     Error = GetLastError();
87     if (DriveType == DRIVE_FIXED)
88     {
89         ok_type(Error == ERROR_INVALID_PARAMETER, "Expecting ERROR_INVALID_PARAMETER, got %ld\n", Error);
90     }
91     else
92     {
93         ok_type(Error == ERROR_INSUFFICIENT_BUFFER, "Expecting ERROR_INSUFFICIENT_BUFFER, got %ld\n", Error);
94     }
95     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
96 
97     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &MDN, sizeof(MDN), &Size, NULL);
98     ok_type(Ret == 0, "DeviceIoControl succeed\n");
99     Error = GetLastError();
100     ok_type(Error == ERROR_MORE_DATA, "Expecting ERROR_MORE_DATA, got %ld\n", Error);
101     ok_type(Size == sizeof(MOUNTDEV_NAME), "Invalid output size: %ld\n", Size);
102 
103     AllocatedMDN = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(MOUNTDEV_NAME, Name) + MDN.NameLength + sizeof(UNICODE_NULL));
104     if (AllocatedMDN == NULL)
105     {
106         skip_type("Memory allocation failure\n");
107         return;
108     }
109 
110     Size = 0;
111     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, AllocatedMDN, FIELD_OFFSET(MOUNTDEV_NAME, Name) + MDN.NameLength, &Size, NULL);
112     ok_type(Ret != 0, "DeviceIoControl failed: %ld\n", GetLastError());
113     ok_type(Size == FIELD_OFFSET(MOUNTDEV_NAME, Name) + MDN.NameLength, "Invalid output size: %ld\n", Size);
114     ok_type(AllocatedMDN->NameLength == MDN.NameLength, "Mismatching sizes: %d %d\n", AllocatedMDN->NameLength, MDN.NameLength);
115 
116     if (Ret != 0)
117     {
118         IsValid = FALSE;
119         AllocatedMDN->Name[AllocatedMDN->NameLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
120 
121         if ((DriveType == DRIVE_FIXED && wcsstr(AllocatedMDN->Name, L"\\Device\\HarddiskVolume") != NULL) ||
122             (DriveType == DRIVE_CDROM && wcsstr(AllocatedMDN->Name, L"\\Device\\CdRom") != NULL))
123         {
124             IsValid = TRUE;
125         }
126         else if (wcsstr(AllocatedMDN->Name, L"\\DosDevices\\") != NULL)
127         {
128             IsValid = (AllocatedMDN->Name[12] == Letter && AllocatedMDN->Name[13] == L':');
129         }
130 
131         ok_type(IsValid, "Invalid name: %.*S\n", AllocatedMDN->NameLength, AllocatedMDN->Name);
132     }
133     else
134     {
135         skip_type("Failed to query device name\n");
136     }
137 
138     HeapFree(GetProcessHeap(), 0, AllocatedMDN);
139 }
140 
141 static
142 VOID
143 QueryUniqueId(VOID)
144 {
145     UINT Ret;
146     DWORD Size, Error;
147     MOUNTDEV_UNIQUE_ID MUI, *AllocatedMUI;
148 
149     Size = 0;
150     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, NULL, 0, &MUI, sizeof(MUI) - 1, &Size, NULL);
151     ok_type(Ret == 0, "DeviceIoControl succeed\n");
152     Error = GetLastError();
153     if (DriveType == DRIVE_FIXED)
154     {
155         ok_type(Error == ERROR_INVALID_PARAMETER, "Expecting ERROR_INVALID_PARAMETER, got %ld\n", Error);
156     }
157     else
158     {
159         ok_type(Error == ERROR_INSUFFICIENT_BUFFER, "Expecting ERROR_INSUFFICIENT_BUFFER, got %ld\n", Error);
160     }
161     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
162 
163     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, NULL, 0, &MUI, sizeof(MUI), &Size, NULL);
164     ok_type(Ret == 0, "DeviceIoControl succeed\n");
165     Error = GetLastError();
166     ok_type(Error == ERROR_MORE_DATA, "Expecting ERROR_MORE_DATA, got %ld\n", Error);
167     ok_type(Size == sizeof(MOUNTDEV_UNIQUE_ID), "Invalid output size: %ld\n", Size);
168 
169     AllocatedMUI = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + MUI.UniqueIdLength + sizeof(UNICODE_NULL));
170     if (AllocatedMUI == NULL)
171     {
172         skip_type("Memory allocation failure\n");
173         return;
174     }
175 
176     Size = 0;
177     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_UNIQUE_ID, NULL, 0, AllocatedMUI, FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + MUI.UniqueIdLength, &Size, NULL);
178     ok_type(Ret != 0, "DeviceIoControl failed: %ld\n", GetLastError());
179     ok_type(Size == FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + MUI.UniqueIdLength, "Invalid output size: %ld\n", Size);
180     ok_type(AllocatedMUI->UniqueIdLength == MUI.UniqueIdLength, "Mismatching sizes: %d %d\n", AllocatedMUI->UniqueIdLength, MUI.UniqueIdLength);
181 
182     HeapFree(GetProcessHeap(), 0, AllocatedMUI);
183 }
184 
185 static
186 VOID
187 QuerySuggestedLinkName(VOID)
188 {
189     UINT Ret;
190     DWORD Size, Error;
191     MOUNTDEV_SUGGESTED_LINK_NAME MSLN;
192 
193     Size = 0;
194     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, NULL, 0, &MSLN, sizeof(MSLN) - 1, &Size, NULL);
195     ok_type(Ret == 0, "DeviceIoControl succeed\n");
196     Error = GetLastError();
197     if (DriveType == DRIVE_FIXED)
198     {
199         ok_type(Error == ERROR_INVALID_PARAMETER, "Expecting ERROR_INVALID_PARAMETER, got %ld\n", Error);
200     }
201     else
202     {
203         ok_type(Error == ERROR_INSUFFICIENT_BUFFER, "Expecting ERROR_INSUFFICIENT_BUFFER, got %ld\n", Error);
204     }
205     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
206 
207     Ret = DeviceIoControl(Device, IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME, NULL, 0, &MSLN, sizeof(MSLN), &Size, NULL);
208     ok_type(Ret == 0, "DeviceIoControl succeed\n");
209     Error = GetLastError();
210     if (DriveType == DRIVE_FIXED)
211     {
212         ok_type(Error == ERROR_NOT_FOUND, "Expecting ERROR_NOT_FOUND, got %ld\n", Error);
213     }
214     else
215     {
216         ok_type(Error == ERROR_FILE_NOT_FOUND, "Expecting ERROR_FILE_NOT_FOUND, got %ld\n", Error);
217     }
218     ok_type(Size == 0, "Invalid output size: %ld\n", Size);
219 }
220 
221 START_TEST(DeviceIoControl)
222 {
223     UINT Ret;
224     WCHAR Path[MAX_PATH];
225     DWORD DriveMap, Current;
226     BOOL DiskDone, CdRomDone;
227 
228     DiskDone = FALSE;
229     CdRomDone = FALSE;
230     DriveMap = GetLogicalDrives();
231     for (Current = 0; Current < 26; ++Current)
232     {
233         if (DriveMap & (1 << Current))
234         {
235             Ret = StringCchPrintfW(Path, MAX_PATH, L"%c:\\", Current + L'A');
236             ok(Ret == S_OK, "StringCchPrintfW failed: %d\n", Ret);
237 
238             DriveType = GetDriveTypeW(Path);
239             if ((DriveType == DRIVE_FIXED && !DiskDone) ||
240                 (DriveType == DRIVE_CDROM && !CdRomDone))
241             {
242                 Ret = StringCchPrintfW(Path, MAX_PATH, L"\\\\?\\%c:", Current + L'A');
243                 ok(Ret == S_OK, "StringCchPrintfW failed: %d\n", Ret);
244 
245                 Device = CreateFileW(Path, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
246                 if (Device == INVALID_HANDLE_VALUE)
247                 {
248                     skip_type("CreateFileW for %S failed: %ld\n", Path, GetLastError());
249                     continue;
250                 }
251 
252                 DiskDone = (DiskDone || (DriveType == DRIVE_FIXED));
253                 CdRomDone = (CdRomDone || (DriveType == DRIVE_CDROM));
254 
255                 if (GetDiskGeometry())
256                 {
257                     QueryDeviceName();
258                     QueryUniqueId();
259                     QuerySuggestedLinkName();
260                 }
261 
262                 CloseHandle(Device);
263             }
264 
265             if (CdRomDone && DiskDone)
266             {
267                 break;
268             }
269         }
270     }
271 
272     if (!DiskDone)
273     {
274         skip("No disk drive found\n");
275     }
276 
277     if (!CdRomDone)
278     {
279         skip("No CDROM drive found\n");
280     }
281 }
282