/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS FS utility tool * FILE: modules/rosapps/applications/cmdutils/vcdcli/vcdcli.c * PURPOSE: Virtual CD-ROM management application * PROGRAMMERS: Pierre Schweitzer */ #define WIN32_NO_STATUS #include #include #include #include #include #include #include #include #include #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM #define IOCTL_CDROM_EJECT_MEDIA CTL_CODE(IOCTL_CDROM_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) void PrintUsage(int type) { if (type == 0) { _ftprintf(stdout, _T("vcdcli usage:\n")); _ftprintf(stdout, _T("\tlist [/a]: list all the virtual drives\n")); _ftprintf(stdout, _T("\tcreate: create a virtual drive\n")); _ftprintf(stdout, _T("\tmount X path: mount path image on X virtual drive\n")); _ftprintf(stdout, _T("\tremount X: remount image on X virtual drive\n")); _ftprintf(stdout, _T("\tremount X: remount image on X virtual drive\n")); _ftprintf(stdout, _T("\teject X: eject image on X virtual drive\n")); _ftprintf(stdout, _T("\tremove X: remove virtual drive X\n")); } else if (type == 1) { _ftprintf(stdout, _T("mount usage:\n")); _ftprintf(stdout, _T("\tmount [/u] [/j] [/p]\n")); _ftprintf(stdout, _T("\tMount the ISO image given in on the previously created virtual drive \n")); _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n")); _ftprintf(stdout, _T("\t\tUse /u to make UDF volumes not appear as such\n")); _ftprintf(stdout, _T("\t\tUse /j to make Joliet volumes not appear as such\n")); _ftprintf(stdout, _T("\t\tUse /p to make the mounting persistent\n")); } else if (type == 2) { _ftprintf(stdout, _T("remount usage:\n")); _ftprintf(stdout, _T("\tremount \n")); _ftprintf(stdout, _T("\tRemount the ISO image that was previously mounted on the virtual drive \n")); _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n")); } else if (type == 3) { _ftprintf(stdout, _T("eject usage:\n")); _ftprintf(stdout, _T("\teject \n")); _ftprintf(stdout, _T("\tEjects the ISO image that is mounted on the virtual drive \n")); _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n")); } else if (type == 4) { _ftprintf(stdout, _T("remove usage:\n")); _ftprintf(stdout, _T("\tremove \n")); _ftprintf(stdout, _T("\tRemoves the virtual drive making it no longer usable\n")); _ftprintf(stdout, _T("\t\tDo not use colon for drive letter\n")); } } HANDLE OpenLetter(WCHAR Letter) { TCHAR Device[255]; /* Make name */ _stprintf(Device, _T("\\\\.\\%c:"), Letter); /* And open */ return CreateFile(Device, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } BOOLEAN StartDriver(VOID) { SC_HANDLE hMgr, hSvc; /* Open the SC manager */ hMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (hMgr == NULL) { _ftprintf(stderr, _T("Failed opening service manager: %x\n"), GetLastError()); return FALSE; } /* Open the service matching our driver */ hSvc = OpenService(hMgr, _T("Vcdrom"), SERVICE_START); if (hSvc == NULL) { _ftprintf(stderr, _T("Failed opening service: %x\n"), GetLastError()); CloseServiceHandle(hMgr); return FALSE; } /* Start it */ /* FIXME: improve */ StartService(hSvc, 0, NULL); /* Cleanup */ CloseServiceHandle(hSvc); CloseServiceHandle(hMgr); /* Always return true when service exists * We don't care whether it was running or not * We just need it */ return TRUE; } HANDLE OpenMaster(VOID) { /* We'll always talk to master first, so we start it here */ if (!StartDriver()) { return INVALID_HANDLE_VALUE; } /* And then, open it */ return CreateFile(_T("\\\\.\\\\VirtualCdRom"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } BOOLEAN IsLetterOwned(WCHAR Letter) { HANDLE hDev; BOOLEAN Res; DRIVES_LIST Drives; DWORD i, BytesRead; /* We've to deal with driver */ hDev = OpenMaster(); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError()); return FALSE; } /* Get the list of the managed drives */ Res = DeviceIoControl(hDev, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to enumerate drives: %x\n"), GetLastError()); CloseHandle(hDev); return FALSE; } /* Don't leak ;-) */ CloseHandle(hDev); /* Do we find our letter in the list? */ for (i = 0; i < Drives.Count; ++i) { if (Drives.Drives[i] == Letter) { break; } } /* No? Fail */ if (i == Drives.Count) { _ftprintf(stderr, _T("%c is not a drive owned by VCD\n"), Letter); return FALSE; } /* Otherwise, that's fine! */ return TRUE; } FORCEINLINE DWORD Min(DWORD a, DWORD b) { return (a > b ? b : a); } int __cdecl _tmain(int argc, const TCHAR *argv[]) { HANDLE hDev; BOOLEAN Res; DWORD BytesRead; /* We need a command, at least */ if (argc < 2) { PrintUsage(0); return 1; } /* List will display all the managed drives */ if (_tcscmp(argv[1], _T("list")) == 0) { DWORD i; BOOLEAN All; DRIVES_LIST Drives; /* Open the driver for query */ hDev = OpenMaster(); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError()); return 1; } /* Query the virtual drives */ Res = DeviceIoControl(hDev, IOCTL_VCDROM_ENUMERATE_DRIVES, NULL, 0, &Drives, sizeof(Drives), &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to create VCD: %x\n"), GetLastError()); CloseHandle(hDev); return 1; } /* Done with master */ CloseHandle(hDev); /* No drives? Display a pretty message */ if (Drives.Count == 0) { _ftprintf(stdout, _T("No virtual drives\n")); } else { /* Do we have to display all the information? That's '/a' */ All = FALSE; if (argc > 2) { if (_tcscmp(argv[2], _T("/a")) == 0) { All = TRUE; } } if (All) { _ftprintf(stdout, _T("Managed drives:\n")); /* For each virtual drive... */ for (i = 0; i < Drives.Count; ++i) { HANDLE hLet; IMAGE_PATH Image; /* Display its letter */ _ftprintf(stdout, _T("%c: "), Drives.Drives[i]); /* And open it to query more data */ hLet = OpenLetter(Drives.Drives[i]); if (hLet != INVALID_HANDLE_VALUE) { /* Get the image path along with mount status */ Res = DeviceIoControl(hLet, IOCTL_VCDROM_GET_IMAGE_PATH, NULL, 0, &Image, sizeof(Image), &BytesRead, NULL); /* If it succeed */ if (Res) { UNICODE_STRING Path; /* Display image if any, otherwise display "no image" */ if (Image.Length != 0) { Path.Length = Image.Length; Path.MaximumLength = Image.Length; Path.Buffer = Image.Path; } else { Path.Length = sizeof(L"no image") - sizeof(UNICODE_NULL); Path.MaximumLength = sizeof(L"no image"); Path.Buffer = L"no image"; } /* Print everything including mount status */ _ftprintf(stdout, _T("%wZ, %s"), &Path, (Image.Mounted == 0 ? _T("not mounted") : _T("mounted"))); } /* Close drive and move to the next one */ CloseHandle(hLet); } /* EOL! */ _ftprintf(stdout, _T("\n")); } } else { /* Basic display, just display drives on a single line */ _ftprintf(stdout, _T("Virtual drives:\n")); for (i = 0; i < Drives.Count; ++i) { _ftprintf(stdout, _T("%c: "), Drives.Drives[i]); } _ftprintf(stdout, _T("\n")); } } } else if (_tcscmp(argv[1], _T("create")) == 0) { WCHAR Letter; /* Open driver */ hDev = OpenMaster(); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD: %x\n"), GetLastError()); return 1; } /* Issue the IOCTL */ Res = DeviceIoControl(hDev, IOCTL_VCDROM_CREATE_DRIVE, NULL, 0, &Letter, sizeof(WCHAR), &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to create drive: %x\n"), GetLastError()); CloseHandle(hDev); return 1; } /* And display the create drive letter to the user */ _ftprintf(stdout, _T("The virtual drive '%c' has been created\n"), Letter); CloseHandle(hDev); } else if (_tcscmp(argv[1], _T("mount")) == 0) { DWORD i; HKEY hKey; HANDLE hFile; WCHAR Letter; BOOLEAN bPersist; TCHAR szBuffer[260]; UNICODE_STRING NtPathName; MOUNT_PARAMETERS MountParams; /* We need two args */ if (argc < 4) { PrintUsage(1); return 1; } /* First, check letter is OK */ if (!_istalpha(argv[2][0]) || argv[2][1] != 0) { PrintUsage(1); return 1; } /* Now, check the ISO image is OK and reachable by the user */ hFile = CreateFile(argv[3], FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open file: %lu\n"), GetLastError()); return 1; } /* Validate the drive is owned by vcdrom */ Letter = _totupper(argv[2][0]); if (!IsLetterOwned(Letter)) { CloseHandle(hFile); return 1; } /* Get NT path for the driver */ if (!RtlDosPathNameToNtPathName_U(argv[3], &NtPathName, NULL, NULL)) { _ftprintf(stderr, _T("Failed to convert path\n")); CloseHandle(hFile); return 1; } /* Copy it in the parameter structure */ _tcsncpy(MountParams.Path, NtPathName.Buffer, 255); MountParams.Length = Min(NtPathName.Length, 255 * sizeof(WCHAR)); MountParams.Flags = 0; /* Do we have to suppress anything? */ bPersist = FALSE; for (i = 4; i < argc; ++i) { /* Make UDF uneffective */ if (_tcscmp(argv[i], _T("/u")) == 0) { MountParams.Flags |= MOUNT_FLAG_SUPP_UDF; } /* Make Joliet uneffective */ else if (_tcscmp(argv[i], _T("/j")) == 0) { MountParams.Flags |= MOUNT_FLAG_SUPP_JOLIET; } /* Should it be persistent? */ else if (_tcscmp(argv[i], _T("/p")) == 0) { bPersist = TRUE; } } /* No longer needed */ RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); /* Open the drive */ hDev = OpenLetter(Letter); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError()); CloseHandle(hFile); return 1; } /* We have to release image now, the driver will attempt to open it */ CloseHandle(hFile); /* Issue the mount IOCTL */ Res = DeviceIoControl(hDev, IOCTL_VCDROM_MOUNT_IMAGE, &MountParams, sizeof(MountParams), NULL, 0, &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to mount %s on %c: %x\n"), argv[3], Letter, GetLastError()); CloseHandle(hDev); return 1; } /* Pretty print in case of a success */ _ftprintf(stdout, _T("%s mounted on %c\n"), argv[3], Letter); CloseHandle(hDev); /* Should it persistent? */ if (bPersist) { /* Create the registry key Device */ _stprintf(szBuffer, _T("SYSTEM\\CurrentControlSet\\Services\\Vcdrom\\Parameters\\Device%c"), Letter); if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szBuffer, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY | KEY_SET_VALUE, NULL, &hKey, NULL) == ERROR_SUCCESS) { /* Set the image path */ _tcsncpy(szBuffer, MountParams.Path, MountParams.Length); szBuffer[MountParams.Length / sizeof(TCHAR)] = 0; RegSetValueExW(hKey, _T("IMAGE"), 0, REG_SZ, (BYTE *)szBuffer, MountParams.Length); /* Set the drive letter */ szBuffer[0] = Letter; szBuffer[1] = _T(':'); szBuffer[2] = 0; RegSetValueExW(hKey, _T("DRIVE"), 0, REG_SZ, (BYTE *)szBuffer, 3 * sizeof(TCHAR)); RegCloseKey(hKey); } else { _ftprintf(stderr, _T("Failed to make mounting persistent: %x\n"), GetLastError()); } } } else if (_tcscmp(argv[1], _T("remount")) == 0) { WCHAR Letter; /* We need an arg */ if (argc < 3) { PrintUsage(2); return 1; } /* First, check letter is OK */ if (!_istalpha(argv[2][0]) || argv[2][1] != 0) { PrintUsage(2); return 1; } /* Validate the drive is owned by vcdrom */ Letter = _totupper(argv[2][0]); if (!IsLetterOwned(Letter)) { return 1; } /* Open the drive */ hDev = OpenLetter(Letter); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError()); return 1; } /* Issue the remount IOCTL */ Res = DeviceIoControl(hDev, IOCTL_STORAGE_LOAD_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to remount media on %c: %x\n"), Letter, GetLastError()); CloseHandle(hDev); return 1; } /* Pretty print in case of a success */ _ftprintf(stdout, _T("Media remounted on %c\n"), Letter); CloseHandle(hDev); } else if (_tcscmp(argv[1], _T("eject")) == 0) { WCHAR Letter; /* We need an arg */ if (argc < 3) { PrintUsage(3); return 1; } /* First, check letter is OK */ if (!_istalpha(argv[2][0]) || argv[2][1] != 0) { PrintUsage(3); return 1; } /* Validate the drive is owned by vcdrom */ Letter = _totupper(argv[2][0]); if (!IsLetterOwned(Letter)) { return 1; } /* Open the drive */ hDev = OpenLetter(Letter); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError()); return 1; } /* Issue the eject IOCTL */ Res = DeviceIoControl(hDev, IOCTL_CDROM_EJECT_MEDIA, NULL, 0, NULL, 0, &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to eject media on %c: %x\n"), Letter, GetLastError()); CloseHandle(hDev); return 1; } /* Pretty print in case of a success */ _ftprintf(stdout, _T("Media ejected on %c\n"), Letter); CloseHandle(hDev); } else if (_tcscmp(argv[1], _T("remove")) == 0) { WCHAR Letter; /* We need an arg */ if (argc < 3) { PrintUsage(4); return 1; } /* First, check letter is OK */ if (!_istalpha(argv[2][0]) || argv[2][1] != 0) { PrintUsage(4); return 1; } /* Validate the drive is owned by vcdrom */ Letter = _totupper(argv[2][0]); if (!IsLetterOwned(Letter)) { return 1; } /* Open the drive */ hDev = OpenLetter(Letter); if (hDev == INVALID_HANDLE_VALUE) { _ftprintf(stderr, _T("Failed to open VCD %c: %x\n"), Letter, GetLastError()); return 1; } /* Issue the remove IOCTL */ Res = DeviceIoControl(hDev, IOCTL_VCDROM_DELETE_DRIVE, NULL, 0, NULL, 0, &BytesRead, NULL); if (!Res) { _ftprintf(stderr, _T("Failed to remove virtual drive %c: %x\n"), Letter, GetLastError()); CloseHandle(hDev); return 1; } /* Pretty print in case of a success */ _ftprintf(stdout, _T("Virtual drive %c removed\n"), Letter); CloseHandle(hDev); } else { PrintUsage(0); } return 0; }