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: %s\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 } 614