8e59205989286bd392ba01c0d12be61f15227c19
[reactos.git] / rosapps / applications / cmdutils / rosvboxmgmt / rosvboxmgmt.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS VBox Shared Folders Management
4 * FILE: cmdutils/hackssign/client.c
5 * PURPOSE: Communicate with VBox mini redirector to deal with shared folders
6 * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org>
7 */
8
9 #include <stdio.h>
10 #include <wchar.h>
11 #include <windows.h>
12 #include <shlobj.h>
13 #include <shobjidl.h>
14 #include <shlwapi.h>
15
16 /* DON'T CHANGE ORDER!!!! */
17 PCWSTR devices[3] = { L"\\\\.\\VBoxMiniRdrDN", L"\\??\\VBoxMiniRdrDN", L"\\Device\\VBoxMiniRdr" };
18
19 #define MAX_LEN 255
20
21 /* Taken from VBox header */
22 #define _MRX_MAX_DRIVE_LETTERS 26
23 #define IOCTL_MRX_VBOX_BASE FILE_DEVICE_NETWORK_FILE_SYSTEM
24 #define _MRX_VBOX_CONTROL_CODE(request, method, access) \
25 CTL_CODE(IOCTL_MRX_VBOX_BASE, request, method, access)
26 #define IOCTL_MRX_VBOX_ADDCONN _MRX_VBOX_CONTROL_CODE(100, METHOD_BUFFERED, FILE_ANY_ACCESS)
27 #define IOCTL_MRX_VBOX_GETLIST _MRX_VBOX_CONTROL_CODE(103, METHOD_BUFFERED, FILE_ANY_ACCESS)
28 #define IOCTL_MRX_VBOX_GETGLOBALLIST _MRX_VBOX_CONTROL_CODE(104, METHOD_BUFFERED, FILE_ANY_ACCESS)
29 #define IOCTL_MRX_VBOX_GETGLOBALCONN _MRX_VBOX_CONTROL_CODE(105, METHOD_BUFFERED, FILE_ANY_ACCESS)
30 #define IOCTL_MRX_VBOX_START _MRX_VBOX_CONTROL_CODE(106, METHOD_BUFFERED, FILE_ANY_ACCESS)
31
32 BOOL performDevIoCtl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize)
33 {
34 short i;
35 BOOL ret;
36 DWORD lpBytesReturned;
37 HANDLE dev = INVALID_HANDLE_VALUE;
38
39 wprintf(L"Trying to open a VBoxSRV device\n");
40 for (i = 0; i < 3; ++i)
41 {
42 dev = CreateFile(devices[i],
43 GENERIC_READ | GENERIC_WRITE,
44 FILE_SHARE_READ | FILE_SHARE_WRITE,
45 NULL, OPEN_EXISTING, 0, NULL);
46 if (dev != INVALID_HANDLE_VALUE)
47 {
48 break;
49 }
50 }
51
52 if (dev == INVALID_HANDLE_VALUE)
53 {
54 return FALSE;
55 }
56
57 wprintf(L"%s opened.\n", devices[i]);
58
59 ret = DeviceIoControl(dev, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, &lpBytesReturned, NULL);
60 wprintf(L"Done: it %s with error: %lx\n", (ret != 0 ? L"succeed" : L"failed"), (ret != 0 ? ERROR_SUCCESS : GetLastError()));
61
62 CloseHandle(dev);
63
64 return ret;
65 }
66
67 int startVBoxSrv(void)
68 {
69 return (performDevIoCtl(IOCTL_MRX_VBOX_START, NULL, 0, NULL, 0) == FALSE);
70 }
71
72 int addConn(PCWSTR letter, PCWSTR path)
73 {
74 BOOL ret;
75 PWSTR inputBuffer;
76 DWORD inputBufferSize;
77
78 if (iswalpha(letter[0]) == 0)
79 {
80 wprintf(L"Invalid letter provided\n");
81 return 1;
82 }
83
84 if (wcschr(path, L'\\') != NULL)
85 {
86 wprintf(L"Only give the name of a share\n");
87 return 1;
88 }
89
90 inputBufferSize = (wcslen(path) + wcslen(devices[2]) + wcslen(L"\\;Z:\\vboxsvr\\")) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
91 inputBuffer = malloc(inputBufferSize);
92 if (inputBuffer == NULL)
93 {
94 wprintf(L"Memory failure\n");
95 return 1;
96 }
97
98 swprintf(inputBuffer, L"%s\\;%c:\\vboxsvr\\%s", devices[2], towupper(letter[0]), path);
99 wprintf(L"Will create the following connection: %s\n", inputBuffer);
100 ret = performDevIoCtl(IOCTL_MRX_VBOX_ADDCONN, inputBuffer, inputBufferSize, NULL, 0);
101 free(inputBuffer);
102
103 return (ret == FALSE);
104 }
105
106 int getList(void)
107 {
108 short i;
109 BOOL ret;
110 DWORD outputBufferSize;
111 char outputBuffer[_MRX_MAX_DRIVE_LETTERS];
112
113 outputBufferSize = sizeof(outputBuffer);
114 ret = performDevIoCtl(IOCTL_MRX_VBOX_GETLIST, NULL, 0, &outputBuffer, outputBufferSize);
115 if (ret == FALSE)
116 {
117 return 1;
118 }
119
120 for (i = 0; i < _MRX_MAX_DRIVE_LETTERS; i += 2)
121 {
122 wprintf(L"%c: %s\t%c: %s\n", 'A' + i, (outputBuffer[i] == 0 ? L"FALSE" : L"TRUE"),
123 'A' + (i + 1), (outputBuffer[i + 1] == 0 ? L"FALSE" : L"TRUE"));
124 }
125
126 return 0;
127 }
128
129 PCWSTR getGlobalConn(CHAR id)
130 {
131 BOOL ret;
132 static WCHAR name[MAX_LEN];
133
134 ret = performDevIoCtl(IOCTL_MRX_VBOX_GETGLOBALCONN, &id, sizeof(id), name, sizeof(name));
135 if (ret == FALSE)
136 {
137 return NULL;
138 }
139
140 name[MAX_LEN - 1] = 0;
141 return name;
142 }
143
144 int getGlobalList(void)
145 {
146 short i;
147 BOOL ret;
148 DWORD outputBufferSize;
149 char outputBuffer[_MRX_MAX_DRIVE_LETTERS];
150
151 outputBufferSize = sizeof(outputBuffer);
152 memset(outputBuffer, 0, outputBufferSize);
153 ret = performDevIoCtl(IOCTL_MRX_VBOX_GETGLOBALLIST, NULL, 0, &outputBuffer, outputBufferSize);
154 if (ret == FALSE)
155 {
156 return 1;
157 }
158
159 for (i = 0; i < _MRX_MAX_DRIVE_LETTERS; ++i)
160 {
161 CHAR id = outputBuffer[i];
162 BOOL active = ((id & 0x80) == 0x80);
163 PCWSTR name = NULL;
164
165 if (active)
166 {
167 name = getGlobalConn(id);
168 }
169 if (name == NULL)
170 {
171 name = L"None";
172 }
173
174 wprintf(L"%c: %s (%s)%c", 'A' + i, (active ? L"Active" : L"Inactive"), name, (i & 1 ? '\n' : '\t'));
175 }
176
177 return 0;
178 }
179
180 BOOL createUNCShortcut(PCWSTR share)
181 {
182 HRESULT res;
183 IShellLink *link;
184 IPersistFile *persist;
185 WCHAR path[MAX_PATH];
186 WCHAR linkPath[MAX_PATH];
187
188 res = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void**)&link);
189 if (FAILED(res))
190 {
191 return FALSE;
192 }
193
194 res = link->lpVtbl->QueryInterface(link, &IID_IPersistFile, (void **)&persist);
195 if (FAILED(res))
196 {
197 link->lpVtbl->Release(link);
198 return FALSE;
199 }
200
201 wcscpy(path, L"\\\\vboxsvr\\");
202 wcscat(path, share);
203 link->lpVtbl->SetPath(link, path);
204
205 res = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, path);
206 if (FAILED(res))
207 {
208 persist->lpVtbl->Release(persist);
209 link->lpVtbl->Release(link);
210 return FALSE;
211 }
212
213 wsprintf(linkPath, L"%s\\Browse %s (VBox).lnk", path, share);
214 res = persist->lpVtbl->Save(persist, linkPath, TRUE);
215
216 persist->lpVtbl->Release(persist);
217 link->lpVtbl->Release(link);
218
219 return SUCCEEDED(res);
220 }
221
222 BOOL assignDriveLetter(PCWSTR share)
223 {
224 DWORD driveMap;
225 char drive;
226 wchar_t cmdLine[MAX_PATH];
227 STARTUPINFOW startupInfo;
228 PROCESS_INFORMATION processInformation;
229 BOOL ok = FALSE;
230
231 driveMap = GetLogicalDrives();
232
233 for (drive = _MRX_MAX_DRIVE_LETTERS - 1; drive >= 0; ++drive)
234 {
235 if (!(driveMap & (1 << drive)))
236 {
237 break;
238 }
239 }
240
241 if (drive < 0)
242 {
243 wprintf(L"Failed finding an appropriate drive for shared folder\n");
244 return 1;
245 }
246
247 drive += 'A';
248
249 wsprintf(cmdLine, L"hackssign_client.exe assign %c %s", drive, share);
250 RtlZeroMemory(&startupInfo, sizeof(startupInfo));
251 startupInfo.cb = sizeof(STARTUPINFOW);
252
253 if (!CreateProcessW(NULL,
254 (PVOID)cmdLine,
255 NULL,
256 NULL,
257 FALSE,
258 0,
259 NULL,
260 NULL,
261 &startupInfo,
262 &processInformation))
263 {
264 wprintf(L"Failed starting hackssign_client.exe\n");
265 return 1;
266 }
267
268 ok = (WaitForSingleObject(processInformation.hProcess, -1) == WAIT_OBJECT_0);
269 CloseHandle(processInformation.hProcess);
270 CloseHandle(processInformation.hThread);
271
272 wprintf(L"%c assigned to %s\n", drive, share);
273
274 return ok;
275 }
276
277 int autoStart(BOOL assign)
278 {
279 short i;
280 BOOL ret;
281 DWORD outputBufferSize;
282 char outputBuffer[_MRX_MAX_DRIVE_LETTERS];
283 OFSTRUCT ofs;
284
285 if (startVBoxSrv() != 0)
286 {
287 return 1;
288 }
289
290 outputBufferSize = sizeof(outputBuffer);
291 memset(outputBuffer, 0, outputBufferSize);
292 ret = performDevIoCtl(IOCTL_MRX_VBOX_GETGLOBALLIST, NULL, 0, &outputBuffer, outputBufferSize);
293 if (ret == FALSE)
294 {
295 return 1;
296 }
297
298 if (assign)
299 {
300 wchar_t path[MAX_PATH];
301 if (GetModuleFileName(NULL, path, ARRAYSIZE(path)) != 0)
302 {
303 *(wcsrchr(path, '\\')) = 0;
304 wprintf(L"Switching to %s\n", path);
305 SetCurrentDirectory(path);
306 }
307
308 if (OpenFile("hackssign_client.exe", &ofs, OF_EXIST) != 1)
309 {
310 wprintf(L"hackssign_client.exe not found, falling back to links\n");
311 assign = FALSE;
312 }
313 }
314
315 if (!assign)
316 {
317 CoInitialize(NULL);
318 }
319
320 for (i = 0; i < _MRX_MAX_DRIVE_LETTERS; ++i)
321 {
322 CHAR id = outputBuffer[i];
323 BOOL active = ((id & 0x80) == 0x80);
324 PCWSTR name = NULL;
325
326 if (active)
327 {
328 name = getGlobalConn(id);
329 }
330 if (name == NULL)
331 {
332 continue;
333 }
334
335 if (!assign)
336 {
337 createUNCShortcut(name);
338 }
339 else
340 {
341 assignDriveLetter(name);
342 }
343 }
344
345 return 0;
346 }
347
348 void printUsage(void)
349 {
350 wprintf(L"ReactOS VBox Shared Folders Management\n");
351 wprintf(L"\tstart: start the VBox Shared folders (mandatory prior any operation!)\n");
352 wprintf(L"\taddconn <letter> <share name>: add a connection\n");
353 wprintf(L"\tgetlist: list connections\n");
354 wprintf(L"\tgetgloballist: list available shares\n");
355 wprintf(L"\tautolink: automagically configure the VBox Shared folders and creates desktop folders\n");
356 wprintf(L"\tautoassign: automagically configure the VBox Shared folders and assigns drive letters\n");
357 }
358
359 int wmain(int argc, wchar_t *argv[])
360 {
361 PCWSTR cmd;
362
363 if (argc == 1)
364 {
365 printUsage();
366 return 0;
367 }
368
369 cmd = argv[1];
370
371 if (_wcsicmp(cmd, L"start") == 0)
372 {
373 return startVBoxSrv();
374 }
375 else if (_wcsicmp(cmd, L"addconn") == 0)
376 {
377 if (argc < 4)
378 {
379 printUsage();
380 return 0;
381 }
382
383 return addConn(argv[2], argv[3]);
384 }
385 else if (_wcsicmp(cmd, L"getlist") == 0)
386 {
387 return getList();
388 }
389 else if (_wcsicmp(cmd, L"getgloballist") == 0)
390 {
391 return getGlobalList();
392 }
393 else if (_wcsicmp(cmd, L"autolink") == 0)
394 {
395 return autoStart(FALSE);
396 }
397 else if (_wcsicmp(cmd, L"autoassign") == 0)
398 {
399 return autoStart(TRUE);
400 }
401 else
402 {
403 printUsage();
404 return 0;
405 }
406 }