diff --git a/modules/rosapps/applications/cmdutils/CMakeLists.txt b/modules/rosapps/applications/cmdutils/CMakeLists.txt index 8a47be4e68f..843b2c5be54 100644 --- a/modules/rosapps/applications/cmdutils/CMakeLists.txt +++ b/modules/rosapps/applications/cmdutils/CMakeLists.txt @@ -5,5 +5,6 @@ add_subdirectory(gflags) add_subdirectory(tee) add_subdirectory(touch) add_subdirectory(uptime) +add_subdirectory(vcdcli) add_subdirectory(winspool_print) add_subdirectory(y) diff --git a/modules/rosapps/applications/cmdutils/vcdcli/CMakeLists.txt b/modules/rosapps/applications/cmdutils/vcdcli/CMakeLists.txt new file mode 100644 index 00000000000..528bfb6d47a --- /dev/null +++ b/modules/rosapps/applications/cmdutils/vcdcli/CMakeLists.txt @@ -0,0 +1,6 @@ + +include_directories(${REACTOS_SOURCE_DIR}/modules/rosapps/drivers/vcdrom) +add_executable(vcdcli vcdcli vcdcli.rc) +set_module_type(vcdcli win32cui UNICODE) +add_importlibs(vcdcli advapi32 msvcrt kernel32 ntdll) +add_cd_file(TARGET vcdcli DESTINATION reactos/system32 FOR all) diff --git a/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.c b/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.c new file mode 100644 index 00000000000..315dc958bc8 --- /dev/null +++ b/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.c @@ -0,0 +1,558 @@ +/* + * 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 + +#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 \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")); + } + 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) + { + HANDLE hFile; + WCHAR Letter; + 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: %s\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; /* FIXME */ + + /* 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); + } + 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; +} diff --git a/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.rc b/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.rc new file mode 100644 index 00000000000..f6a932310f8 --- /dev/null +++ b/modules/rosapps/applications/cmdutils/vcdcli/vcdcli.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Virtual CD-ROM Controler" +#define REACTOS_STR_INTERNAL_NAME "vcdcli" +#define REACTOS_STR_ORIGINAL_FILENAME "vcdcli.exe" +#include