[REACTOS] Fix MSVC printf format warnings
[reactos.git] / modules / rosapps / applications / cmdutils / vcdcli / vcdcli.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS FS utility tool
4 * FILE: modules/rosapps/applications/cmdutils/vcdcli/vcdcli.c
5 * PURPOSE: Virtual CD-ROM management application
6 * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org>
7 */
8
9 #define WIN32_NO_STATUS
10 #include <windef.h>
11 #include <winbase.h>
12 #include <winsvc.h>
13 #include <winreg.h>
14 #include <ndk/rtltypes.h>
15 #include <ndk/rtlfuncs.h>
16 #include <tchar.h>
17 #include <stdio.h>
18
19 #include <vcdioctl.h>
20
21 #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
22 #define IOCTL_CDROM_EJECT_MEDIA CTL_CODE(IOCTL_CDROM_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
23
24 void
25 PrintUsage(int type)
26 {
27 if (type == 0)
28 {
29 _ftprintf(stdout, _T("vcdcli usage:\n"));
30 _ftprintf(stdout, _T("\tlist [/a]: list all the virtual drives\n"));
31 _ftprintf(stdout, _T("\tcreate: create a virtual drive\n"));
32 _ftprintf(stdout, _T("\tmount X path: mount path image on X virtual drive\n"));
33 _ftprintf(stdout, _T("\tremount X: remount image on X virtual drive\n"));
34 _ftprintf(stdout, _T("\tremount X: remount image on X virtual drive\n"));
35 _ftprintf(stdout, _T("\teject X: eject image on X virtual drive\n"));
36 _ftprintf(stdout, _T("\tremove X: remove virtual drive X\n"));
37 }
38 else if (type == 1)
39 {
40 _ftprintf(stdout, _T("mount usage:\n"));
41 _ftprintf(stdout, _T("\tmount <drive letter> <path.iso> [/u] [/j] [/p]\n"));
42 _ftprintf(stdout, _T("\tMount the ISO image given in <path.iso> on the previously created virtual drive <drive letter>\n"));
43 _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n"));
44 _ftprintf(stdout, _T("\t\tUse /u to make UDF volumes not appear as such\n"));
45 _ftprintf(stdout, _T("\t\tUse /j to make Joliet volumes not appear as such\n"));
46 _ftprintf(stdout, _T("\t\tUse /p to make the mounting persistent\n"));
47 }
48 else if (type == 2)
49 {
50 _ftprintf(stdout, _T("remount usage:\n"));
51 _ftprintf(stdout, _T("\tremount <drive letter>\n"));
52 _ftprintf(stdout, _T("\tRemount the ISO image that was previously mounted on the virtual drive <drive letter>\n"));
53 _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n"));
54 }
55 else if (type == 3)
56 {
57 _ftprintf(stdout, _T("eject usage:\n"));
58 _ftprintf(stdout, _T("\teject <drive letter>\n"));
59 _ftprintf(stdout, _T("\tEjects the ISO image that is mounted on the virtual drive <drive letter>\n"));
60 _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n"));
61 }
62 else if (type == 4)
63 {
64 _ftprintf(stdout, _T("remove usage:\n"));
65 _ftprintf(stdout, _T("\tremove <drive letter>\n"));
66 _ftprintf(stdout, _T("\tRemoves the virtual drive <drive letter> making it no longer usable\n"));
67 _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n"));
68 }
69 }
70
71 HANDLE
72 OpenLetter(WCHAR Letter)
73 {
74 TCHAR Device[255];
75
76 /* Make name */
77 _stprintf(Device, _T("\\\\.\\%c:"), Letter);
78
79 /* And open */
80 return CreateFile(Device, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
81 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
82 }
83
84 BOOLEAN
85 StartDriver(VOID)
86 {
87 SC_HANDLE hMgr, hSvc;
88
89 /* Open the SC manager */
90 hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
91 if (hMgr == NULL)
92 {
93 _ftprintf(stderr, _T("Failed opening service manager: %x\n"), GetLastError());
94 return FALSE;
95 }
96
97 /* Open the service matching our driver */
98 hSvc = OpenService(hMgr, _T("Vcdrom"), SERVICE_START);
99 if (hSvc == NULL)
100 {
101 _ftprintf(stderr, _T("Failed opening service: %x\n"), GetLastError());
102 CloseServiceHandle(hMgr);
103 return FALSE;
104 }
105
106 /* Start it */
107 /* FIXME: improve */
108 StartService(hSvc, 0, NULL);
109
110 /* Cleanup */
111 CloseServiceHandle(hSvc);
112 CloseServiceHandle(hMgr);
113
114 /* Always return true when service exists
115 * We don't care whether it was running or not
116 * We just need it
117 */
118 return TRUE;
119 }
120
121 HANDLE
122 OpenMaster(VOID)
123 {
124 /* We'll always talk to master first, so we start it here */
125 if (!StartDriver())
126 {
127 return INVALID_HANDLE_VALUE;
128 }
129
130 /* And then, open it */
131 return CreateFile(_T("\\\\.\\\\VirtualCdRom"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
132 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
133 }
134
135 BOOLEAN
136 IsLetterOwned(WCHAR Letter)
137 {
138 HANDLE hDev;
139 BOOLEAN Res;
140 DRIVES_LIST Drives;
141 DWORD i, BytesRead;
142
143 /* We've to deal with driver */
144 hDev = OpenMaster();
145 if (hDev == INVALID_HANDLE_VALUE)
146 {
147 _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError());
148 return FALSE;
149 }
150
151 /* Get the list of the managed drives */
152 Res = DeviceIoControl(hDev, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL);
153 if (!Res)
154 {
155 _ftprintf(stderr, _T("Failed to enumerate drives: %x\n"), GetLastError());
156 CloseHandle(hDev);
157 return FALSE;
158 }
159
160 /* Don't leak ;-) */
161 CloseHandle(hDev);
162
163 /* Do we find our letter in the list? */
164 for (i = 0; i < Drives.Count; ++i)
165 {
166 if (Drives.Drives[i] == Letter)
167 {
168 break;
169 }
170 }
171
172 /* No? Fail */
173 if (i == Drives.Count)
174 {
175 _ftprintf(stderr, _T("%c is not a drive owned by VCD\n"), Letter);
176 return FALSE;
177 }
178
179 /* Otherwise, that's fine! */
180 return TRUE;
181 }
182
183 FORCEINLINE
184 DWORD
185 Min(DWORD a, DWORD b)
186 {
187 return (a > b ? b : a);
188 }
189
190 int
191 __cdecl
192 _tmain(int argc, const TCHAR *argv[])
193 {
194 HANDLE hDev;
195 BOOLEAN Res;
196 DWORD BytesRead;
197
198 /* We need a command, at least */
199 if (argc < 2)
200 {
201 PrintUsage(0);
202 return 1;
203 }
204
205 /* List will display all the managed drives */
206 if (_tcscmp(argv[1], _T("list")) == 0)
207 {
208 DWORD i;
209 BOOLEAN All;
210 DRIVES_LIST Drives;
211
212 /* Open the driver for query */
213 hDev = OpenMaster();
214 if (hDev == INVALID_HANDLE_VALUE)
215 {
216 _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError());
217 return 1;
218 }
219
220 /* Query the virtual drives */
221 Res = DeviceIoControl(hDev, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL);
222 if (!Res)
223 {
224 _ftprintf(stderr, _T("Failed to create VCD: %x\n"), GetLastError());
225 CloseHandle(hDev);
226 return 1;
227 }
228
229 /* Done with master */
230 CloseHandle(hDev);
231
232 /* No drives? Display a pretty message */
233 if (Drives.Count == 0)
234 {
235 _ftprintf(stdout, _T("No virtual drives\n"));
236 }
237 else
238 {
239 /* Do we have to display all the information? That's '/a' */
240 All = FALSE;
241 if (argc > 2)
242 {
243 if (_tcscmp(argv[2], _T("/a")) == 0)
244 {
245 All = TRUE;
246 }
247 }
248
249 if (All)
250 {
251 _ftprintf(stdout, _T("Managed drives:\n"));
252 /* For each virtual drive... */
253 for (i = 0; i < Drives.Count; ++i)
254 {
255 HANDLE hLet;
256 IMAGE_PATH Image;
257
258 /* Display its letter */
259 _ftprintf(stdout, _T("%c: "), Drives.Drives[i]);
260
261 /* And open it to query more data */
262 hLet = OpenLetter(Drives.Drives[i]);
263 if (hLet != INVALID_HANDLE_VALUE)
264 {
265 /* Get the image path along with mount status */
266 Res = DeviceIoControl(hLet, IOCTL_VCDROM_GET_IMAGE_PATH, NULL, 0, &Image, sizeof(Image), &BytesRead, NULL);
267 /* If it succeed */
268 if (Res)
269 {
270 UNICODE_STRING Path;
271
272 /* Display image if any, otherwise display "no image" */
273 if (Image.Length != 0)
274 {
275 Path.Length = Image.Length;
276 Path.MaximumLength = Image.Length;
277 Path.Buffer = Image.Path;
278 }
279 else
280 {
281 Path.Length = sizeof(L"no image") - sizeof(UNICODE_NULL);
282 Path.MaximumLength = sizeof(L"no image");
283 Path.Buffer = L"no image";
284 }
285
286 /* Print everything including mount status */
287 _ftprintf(stdout, _T("%wZ, %s"), &Path, (Image.Mounted == 0 ? _T("not mounted") : _T("mounted")));
288 }
289
290 /* Close drive and move to the next one */
291 CloseHandle(hLet);
292 }
293
294 /* EOL! */
295 _ftprintf(stdout, _T("\n"));
296 }
297 }
298 else
299 {
300 /* Basic display, just display drives on a single line */
301 _ftprintf(stdout, _T("Virtual drives:\n"));
302 for (i = 0; i < Drives.Count; ++i)
303 {
304 _ftprintf(stdout, _T("%c: "), Drives.Drives[i]);
305 }
306 _ftprintf(stdout, _T("\n"));
307 }
308 }
309 }
310 else if (_tcscmp(argv[1], _T("create")) == 0)
311 {
312 WCHAR Letter;
313
314 /* Open driver */
315 hDev = OpenMaster();
316 if (hDev == INVALID_HANDLE_VALUE)
317 {
318 _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError());
319 return 1;
320 }
321
322 /* Issue the IOCTL */
323 Res = DeviceIoControl(hDev, IOCTL_VCDROM_CREATE_DRIVE, NULL, 0, &Letter, sizeof(WCHAR), &BytesRead, NULL);
324 if (!Res)
325 {
326 _ftprintf(stderr, _T("Failed to create drive: %x\n"), GetLastError());
327 CloseHandle(hDev);
328 return 1;
329 }
330
331 /* And display the create drive letter to the user */
332 _ftprintf(stdout, _T("The virtual drive '%c' has been created\n"), Letter);
333
334 CloseHandle(hDev);
335 }
336 else if (_tcscmp(argv[1], _T("mount")) == 0)
337 {
338 DWORD i;
339 HKEY hKey;
340 HANDLE hFile;
341 WCHAR Letter;
342 BOOLEAN bPersist;
343 TCHAR szBuffer[260];
344 UNICODE_STRING NtPathName;
345 MOUNT_PARAMETERS MountParams;
346
347 /* We need two args */
348 if (argc < 4)
349 {
350 PrintUsage(1);
351 return 1;
352 }
353
354 /* First, check letter is OK */
355 if (!_istalpha(argv[2][0]) || argv[2][1] != 0)
356 {
357 PrintUsage(1);
358 return 1;
359 }
360
361 /* Now, check the ISO image is OK and reachable by the user */
362 hFile = CreateFile(argv[3], FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
363 if (hFile == INVALID_HANDLE_VALUE)
364 {
365 _ftprintf(stderr, _T("Failed to open file: %lu\n"), GetLastError());
366 return 1;
367 }
368
369 /* Validate the drive is owned by vcdrom */
370 Letter = _totupper(argv[2][0]);
371 if (!IsLetterOwned(Letter))
372 {
373 CloseHandle(hFile);
374 return 1;
375 }
376
377 /* Get NT path for the driver */
378 if (!RtlDosPathNameToNtPathName_U(argv[3], &NtPathName, NULL, NULL))
379 {
380 _ftprintf(stderr, _T("Failed to convert path\n"));
381 CloseHandle(hFile);
382 return 1;
383 }
384
385 /* Copy it in the parameter structure */
386 _tcsncpy(MountParams.Path, NtPathName.Buffer, 255);
387 MountParams.Length = Min(NtPathName.Length, 255 * sizeof(WCHAR));
388 MountParams.Flags = 0;
389
390 /* Do we have to suppress anything? */
391 bPersist = FALSE;
392 for (i = 4; i < argc; ++i)
393 {
394 /* Make UDF uneffective */
395 if (_tcscmp(argv[i], _T("/u")) == 0)
396 {
397 MountParams.Flags |= MOUNT_FLAG_SUPP_UDF;
398 }
399 /* Make Joliet uneffective */
400 else if (_tcscmp(argv[i], _T("/j")) == 0)
401 {
402 MountParams.Flags |= MOUNT_FLAG_SUPP_JOLIET;
403 }
404 /* Should it be persistent? */
405 else if (_tcscmp(argv[i], _T("/p")) == 0)
406 {
407 bPersist = TRUE;
408 }
409 }
410
411 /* No longer needed */
412 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
413
414 /* Open the drive */
415 hDev = OpenLetter(Letter);
416 if (hDev == INVALID_HANDLE_VALUE)
417 {
418 _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError());
419 CloseHandle(hFile);
420 return 1;
421 }
422
423 /* We have to release image now, the driver will attempt to open it */
424 CloseHandle(hFile);
425
426 /* Issue the mount IOCTL */
427 Res = DeviceIoControl(hDev, IOCTL_VCDROM_MOUNT_IMAGE, &MountParams, sizeof(MountParams), NULL, 0, &BytesRead, NULL);
428 if (!Res)
429 {
430 _ftprintf(stderr, _T("Failed to mount %s on %c: %x\n"), argv[3], Letter, GetLastError());
431 CloseHandle(hDev);
432 return 1;
433 }
434
435 /* Pretty print in case of a success */
436 _ftprintf(stdout, _T("%s mounted on %c\n"), argv[3], Letter);
437
438 CloseHandle(hDev);
439
440 /* Should it persistent? */
441 if (bPersist)
442 {
443 /* Create the registry key Device<Letter> */
444 _stprintf(szBuffer, _T("SYSTEM\\CurrentControlSet\\Services\\Vcdrom\\Parameters\\Device%c"), Letter);
445 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szBuffer, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY | KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS)
446 {
447 /* Set the image path */
448 _tcsncpy(szBuffer, MountParams.Path, MountParams.Length);
449 szBuffer[MountParams.Length / sizeof(TCHAR)] = 0;
450 RegSetValueExW(hKey, _T("IMAGE"), 0, REG_SZ, (BYTE *)szBuffer, MountParams.Length);
451
452 /* Set the drive letter */
453 szBuffer[0] = Letter;
454 szBuffer[1] = _T(':');
455 szBuffer[2] = 0;
456 RegSetValueExW(hKey, _T("DRIVE"), 0, REG_SZ, (BYTE *)szBuffer, 3 * sizeof(TCHAR));
457
458 RegCloseKey(hKey);
459 }
460 else
461 {
462 _ftprintf(stderr, _T("Failed to make mounting persistent: %x\n"), GetLastError());
463 }
464 }
465 }
466 else if (_tcscmp(argv[1], _T("remount")) == 0)
467 {
468 WCHAR Letter;
469
470 /* We need an arg */
471 if (argc < 3)
472 {
473 PrintUsage(2);
474 return 1;
475 }
476
477 /* First, check letter is OK */
478 if (!_istalpha(argv[2][0]) || argv[2][1] != 0)
479 {
480 PrintUsage(2);
481 return 1;
482 }
483
484 /* Validate the drive is owned by vcdrom */
485 Letter = _totupper(argv[2][0]);
486 if (!IsLetterOwned(Letter))
487 {
488 return 1;
489 }
490
491 /* Open the drive */
492 hDev = OpenLetter(Letter);
493 if (hDev == INVALID_HANDLE_VALUE)
494 {
495 _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError());
496 return 1;
497 }
498
499 /* Issue the remount IOCTL */
500 Res = DeviceIoControl(hDev, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
501 if (!Res)
502 {
503 _ftprintf(stderr, _T("Failed to remount media on %c: %x\n"), Letter, GetLastError());
504 CloseHandle(hDev);
505 return 1;
506 }
507
508 /* Pretty print in case of a success */
509 _ftprintf(stdout, _T("Media remounted on %c\n"), Letter);
510
511 CloseHandle(hDev);
512 }
513 else if (_tcscmp(argv[1], _T("eject")) == 0)
514 {
515 WCHAR Letter;
516
517 /* We need an arg */
518 if (argc < 3)
519 {
520 PrintUsage(3);
521 return 1;
522 }
523
524 /* First, check letter is OK */
525 if (!_istalpha(argv[2][0]) || argv[2][1] != 0)
526 {
527 PrintUsage(3);
528 return 1;
529 }
530
531 /* Validate the drive is owned by vcdrom */
532 Letter = _totupper(argv[2][0]);
533 if (!IsLetterOwned(Letter))
534 {
535 return 1;
536 }
537
538 /* Open the drive */
539 hDev = OpenLetter(Letter);
540 if (hDev == INVALID_HANDLE_VALUE)
541 {
542 _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError());
543 return 1;
544 }
545
546 /* Issue the eject IOCTL */
547 Res = DeviceIoControl(hDev, IOCTL_CDROM_EJECT_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL);
548 if (!Res)
549 {
550 _ftprintf(stderr, _T("Failed to eject media on %c: %x\n"), Letter, GetLastError());
551 CloseHandle(hDev);
552 return 1;
553 }
554
555 /* Pretty print in case of a success */
556 _ftprintf(stdout, _T("Media ejected on %c\n"), Letter);
557
558 CloseHandle(hDev);
559 }
560 else if (_tcscmp(argv[1], _T("remove")) == 0)
561 {
562 WCHAR Letter;
563
564 /* We need an arg */
565 if (argc < 3)
566 {
567 PrintUsage(4);
568 return 1;
569 }
570
571 /* First, check letter is OK */
572 if (!_istalpha(argv[2][0]) || argv[2][1] != 0)
573 {
574 PrintUsage(4);
575 return 1;
576 }
577
578 /* Validate the drive is owned by vcdrom */
579 Letter = _totupper(argv[2][0]);
580 if (!IsLetterOwned(Letter))
581 {
582 return 1;
583 }
584
585 /* Open the drive */
586 hDev = OpenLetter(Letter);
587 if (hDev == INVALID_HANDLE_VALUE)
588 {
589 _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError());
590 return 1;
591 }
592
593 /* Issue the remove IOCTL */
594 Res = DeviceIoControl(hDev, IOCTL_VCDROM_DELETE_DRIVE, NULL, 0, NULL, 0, &BytesRead, NULL);
595 if (!Res)
596 {
597 _ftprintf(stderr, _T("Failed to remove virtual drive %c: %x\n"), Letter, GetLastError());
598 CloseHandle(hDev);
599 return 1;
600 }
601
602 /* Pretty print in case of a success */
603 _ftprintf(stdout, _T("Virtual drive %c removed\n"), Letter);
604
605 CloseHandle(hDev);
606 }
607 else
608 {
609 PrintUsage(0);
610 }
611
612 return 0;
613 }