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