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