/*++ Copyright (c) 1993-1994 Microsoft Corporation Module Name: cdrom.c Abstract: This module contains the set of routines that display and control the drive letters for CdRom devices. Author: Bob Rinne (bobri) 12/9/93 Environment: User process. Notes: Revision History: --*/ #include "fdisk.h" #include "shellapi.h" #include #include #include #include PCDROM_DESCRIPTOR CdRomChainBase = NULL; PCDROM_DESCRIPTOR CdRomChainLast = NULL; PCDROM_DESCRIPTOR CdRomChanged = NULL; static BOOLEAN CdRomFirstCall = TRUE; static TCHAR SourcePathLetter = (TCHAR) '\0'; static TCHAR SourcePathKeyName[80]; static TCHAR SourcePathValueName[30]; VOID CdRomAddDevice( IN PWSTR NtName, IN WCHAR DriveLetter ) /*++ Routine Description: Build a cdrom description structure for this and fill it in. Arguments: NtName - The unicode name for the device. DriveLetter - The DosDevice name. Return Value: None --*/ { PCDROM_DESCRIPTOR cdrom; PWCHAR cp; LONG error; HKEY keyHandle; DWORD valueType; ULONG size; TCHAR *string; if (CdRomFirstCall) { CdRomFirstCall = FALSE; // Get the registry path and value name. LoadString(hModule, IDS_SOURCE_PATH, SourcePathKeyName, sizeof(SourcePathKeyName)/sizeof(TCHAR)); LoadString(hModule, IDS_SOURCE_PATH_NAME, SourcePathValueName, sizeof(SourcePathValueName)/sizeof(TCHAR)); error = RegOpenKey(HKEY_LOCAL_MACHINE, SourcePathKeyName, &keyHandle); if (error == NO_ERROR) { error = RegQueryValueEx(keyHandle, SourcePathValueName, NULL, &valueType, (PUCHAR)NULL, &size); if (error == NO_ERROR) { string = (PUCHAR) LocalAlloc(LMEM_FIXED, size); if (string) { error = RegQueryValueEx(keyHandle, SourcePathValueName, NULL, &valueType, string, &size); if (error == NO_ERROR) { SourcePathLetter = *string; } } LocalFree(string); } RegCloseKey(keyHandle); } } cdrom = (PCDROM_DESCRIPTOR) malloc(sizeof(CDROM_DESCRIPTOR)); if (cdrom) { cdrom->DeviceName = (PWSTR) malloc((wcslen(NtName)+1)*sizeof(WCHAR)); if (cdrom->DeviceName) { wcscpy(cdrom->DeviceName, NtName); cp = cdrom->DeviceName; while (*cp) { if (iswdigit(*cp)) { break; } cp++; } if (*cp) { cdrom->DeviceNumber = wcstoul(cp, (WCHAR) 0, 10); } cdrom->DriveLetter = DriveLetter; cdrom->Next = NULL; cdrom->NewDriveLetter = (WCHAR) 0; if (CdRomChainBase) { CdRomChainLast->Next = cdrom; } else { AllowCdRom = TRUE; CdRomChainBase = cdrom; } CdRomChainLast = cdrom; } else { free(cdrom); } } } BOOL CALLBACK CdRomDlgProc( IN HWND hDlg, IN UINT wMsg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Handle the dialog for CD-ROMS Arguments: Standard Windows dialog procedure Return Value: TRUE if something was deleted. FALSE otherwise. --*/ { HWND hwndCombo; DWORD selection; DWORD index; CHAR driveLetter; TCHAR string[40]; PCDROM_DESCRIPTOR cdrom; static PCDROM_DESCRIPTOR currentCdrom; static CHAR currentSelectionLetter; switch (wMsg) { case WM_INITDIALOG: // Store all device strings into the selection area. hwndCombo = GetDlgItem(hDlg, IDC_CDROM_NAMES); cdrom = currentCdrom = CdRomChainBase; currentSelectionLetter = (CHAR) cdrom->DriveLetter; while (cdrom) { sprintf(string, "CdRom%d", cdrom->DeviceNumber); SendMessage(hwndCombo, CB_ADDSTRING, 0, (LONG)string); cdrom = cdrom->Next; } SendMessage(hwndCombo, CB_SETCURSEL, 0, 0); // Update the drive letter selections. selection = index = 0; hwndCombo = GetDlgItem(hDlg, IDC_DRIVELET_COMBOBOX); string[1] = TEXT(':'); string[2] = 0; for (driveLetter = 'C'; driveLetter <= 'Z'; driveLetter++) { if ((DriveLetterIsAvailable((CHAR)driveLetter)) || (driveLetter == currentSelectionLetter)) { *string = driveLetter; SendMessage(hwndCombo, CB_ADDSTRING, 0, (LONG)string); if (driveLetter == currentSelectionLetter) { selection = index; } index++; } } // set the current selection to the appropriate index SendMessage(hwndCombo, CB_SETCURSEL, selection, 0); return TRUE; case WM_COMMAND: switch (wParam) { case FD_IDHELP: DialogHelp(HC_DM_DLG_CDROM); break; case IDCANCEL: EndDialog(hDlg, FALSE); break; case IDOK: // User has selected the drive letter and wants the mount to occur. hwndCombo = GetDlgItem(hDlg, IDC_DRIVELET_COMBOBOX); selection = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SendMessage(hwndCombo, CB_GETLBTEXT, selection, (LONG)string); currentCdrom->NewDriveLetter = (WCHAR) string[0]; CdRomChanged = currentCdrom; EndDialog(hDlg, TRUE); break; default: if (HIWORD(wParam) == LBN_SELCHANGE) { TCHAR *cp; if (LOWORD(wParam) != IDC_CDROM_NAMES) { break; } // The state of something in the dialog changed. hwndCombo = GetDlgItem(hDlg, IDC_CDROM_NAMES); selection = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SendMessage(hwndCombo, CB_GETLBTEXT, selection, (LONG)string); // The format of the string returned is "cdrom#". Parse the // value of # in order to find the selection. cp = string; while (*cp) { cp++; } cp--; while ((*cp >= (TCHAR) '0') && (*cp <= (TCHAR) '9')) { cp--; } cp++; selection = 0; while (*cp) { selection = (selection * 10) + (*cp - (TCHAR) '0'); cp++; } // Find the matching device name. for (cdrom = CdRomChainBase; cdrom; cdrom = cdrom->Next) { if (selection == cdrom->DeviceNumber) { // found the match currentSelectionLetter = (CHAR) cdrom->DriveLetter; currentCdrom = cdrom; break; } } // The only thing that is important is to track the cdrom // device name selected and update the drive letter list. selection = index = 0; hwndCombo = GetDlgItem(hDlg, IDC_DRIVELET_COMBOBOX); SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0); string[1] = TEXT(':'); string[2] = 0; for (driveLetter = 'C'; driveLetter <= 'Z'; driveLetter++) { if ((DriveLetterIsAvailable((CHAR)driveLetter)) || (driveLetter == currentSelectionLetter)) { *string = driveLetter; SendMessage(hwndCombo, CB_ADDSTRING, 0, (LONG)string); if (driveLetter == currentSelectionLetter) { selection = index; } index++; } } // set the current selection to the appropriate index SendMessage(hwndCombo, CB_SETCURSEL, selection, 0); } break; } } return FALSE; } VOID CdRom( IN HWND Dialog, IN PVOID Param ) /*++ Routine Description: Start the CdRom dialogs. Arguments: None Return Value: None --*/ { BOOLEAN result = 0; DWORD action, ec; TCHAR name[40]; TCHAR letter[10]; PWSTR linkTarget; OBJECT_ATTRIBUTES oa; WCHAR dosName[20]; HANDLE handle; NTSTATUS status; IO_STATUS_BLOCK statusBlock; ANSI_STRING ansiName; UNICODE_STRING unicodeName; UINT errorMode; result = DialogBoxParam(hModule, MAKEINTRESOURCE(IDD_CDROM), Dialog, CdRomDlgProc, (ULONG) NULL); if (result) { action = ConfirmationDialog(MSG_DRIVE_RENAME_WARNING, MB_ICONQUESTION | MB_YESNOCANCEL); if (!action) { return; } // Attempt to open and lock the cdrom. sprintf(name, "\\Device\\CdRom%d", CdRomChanged->DeviceNumber); RtlInitAnsiString(&ansiName, name); status = RtlAnsiStringToUnicodeString(&unicodeName, &ansiName, TRUE); if (!NT_SUCCESS(status)) { ErrorDialog(MSG_CDROM_LETTER_ERROR); return; } memset(&oa, 0, sizeof(OBJECT_ATTRIBUTES)); oa.Length = sizeof(OBJECT_ATTRIBUTES); oa.ObjectName = &unicodeName; oa.Attributes = OBJ_CASE_INSENSITIVE; errorMode = SetErrorMode(SEM_FAILCRITICALERRORS); status = NtOpenFile(&handle, SYNCHRONIZE | FILE_READ_DATA, &oa, &statusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT); RtlFreeUnicodeString(&unicodeName); SetErrorMode(errorMode); if (!NT_SUCCESS(status)) { ErrorDialog(MSG_CANNOT_LOCK_CDROM); return; } // Lock the drive to insure that no other access is occurring // to the volume. This is done via the "Low" routine for // convenience status = LowLockDrive(handle); if (!NT_SUCCESS(status)) { LowCloseDisk(handle); ErrorDialog(MSG_CANNOT_LOCK_CDROM); return; } // Before attempting to move the name, see if the letter // is currently in use - could be a new network connection // or a partition that is scheduled for deletion. wsprintfW(dosName, L"\\DosDevices\\%wc:", (WCHAR) CdRomChanged->NewDriveLetter); ec = GetDriveLetterLinkTarget(dosName, &linkTarget); if (ec == NO_ERROR) { // Something is using this letter. LowCloseDisk(handle); ErrorDialog(MSG_CANNOT_MOVE_CDROM); return; } // remove existing definition - if this fails don't continue. sprintf(letter, "%c:", (UCHAR) CdRomChanged->DriveLetter); if (!DefineDosDevice(DDD_REMOVE_DEFINITION, (LPCTSTR) letter, (LPCTSTR) NULL)) { LowCloseDisk(handle); ErrorDialog(MSG_CDROM_LETTER_ERROR); return; } status = DiskRegistryAssignCdRomLetter(CdRomChanged->DeviceName, CdRomChanged->NewDriveLetter); MarkDriveLetterFree((UCHAR)CdRomChanged->DriveLetter); // See if this was the device used to install NT if (SourcePathLetter) { if (SourcePathLetter == CdRomChanged->DriveLetter) { LONG error; HKEY keyHandle; DWORD valueType; ULONG size; TCHAR *string; // Update the source path error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SourcePathKeyName, 0, KEY_ALL_ACCESS, &keyHandle); if (error == NO_ERROR) { error = RegQueryValueEx(keyHandle, SourcePathValueName, NULL, &valueType, (PUCHAR)NULL, &size); if (error == NO_ERROR) { string = (PUCHAR) LocalAlloc(LMEM_FIXED, size); if (string) { error = RegQueryValueEx(keyHandle, SourcePathValueName, NULL, &valueType, string, &size); if (error == NO_ERROR) { *string = SourcePathLetter = (UCHAR) CdRomChanged->NewDriveLetter; RegSetValueEx(keyHandle, SourcePathValueName, 0, REG_SZ, string, size); } } LocalFree(string); } RegCloseKey(keyHandle); } } } // set up new device letter - name is already set up sprintf(letter, "%c:", (UCHAR) CdRomChanged->NewDriveLetter); if (DefineDosDevice(DDD_RAW_TARGET_PATH, (LPCTSTR) letter, (LPCTSTR) name)) { CdRomChanged->DriveLetter = CdRomChanged->NewDriveLetter; MarkDriveLetterUsed((UCHAR)CdRomChanged->DriveLetter); } else { RegistryChanged = TRUE; } LowCloseDisk(handle); } }