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 } 577