/*++ Copyright (c) 2000 Microsoft Corporation Module Name: spdrutil.c Abstract: This module contains general utility and helper functions used by ASR in textmode Setup. Authors: Michael Peterson, Seagate Software (v-michpe) 13-May-1997 Guhan Suriyanarayanan (guhans) 21-Aug-1999 Environment: Textmode Setup, Kernel-mode. Revision History: 21-Aug-1999 guhans Code clean-up and re-write. 13-May-1997 v-michpe Initial implementation. --*/ #include "spprecmp.h" #pragma hdrstop #define THIS_MODULE L"spdrutil.c" #define THIS_MODULE_CODE L"U" static const PCWSTR ASR_MOUNTED_DEVICES_KEY = L"\\registry\\machine\\SYSTEM\\MountedDevices"; static const PCWSTR ASR_HKLM_SYSTEM_KEY = L"\\registry\\machine\\SYSTEM"; #ifndef ULONGLONG_MAX #define ULONGLONG_MAX (0xFFFFFFFFFFFFFFFF) #endif // // Caller must free the string // PWSTR SpAsrGetRegionName(IN PDISK_REGION pRegion) { SpNtNameFromRegion( pRegion, (PWSTR) TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); return SpDupStringW((PWSTR)TemporaryBuffer); } ULONG SpAsrGetActualDiskSignature(IN ULONG DiskNumber) { PHARD_DISK pDisk = &HardDisks[DiskNumber]; ULONG Signature = 0; if (PARTITION_STYLE_MBR == (PARTITION_STYLE) pDisk->DriveLayout.PartitionStyle) { Signature = pDisk->DriveLayout.Mbr.Signature; } return Signature; } ULONGLONG SpAsrConvertSectorsToMB( IN ULONGLONG SectorCount, IN ULONG BytesPerSector ) { ULONGLONG mb = 1024 * 1024; if ((ULONGLONG) (SectorCount / mb) > (ULONGLONG) (ULONGLONG_MAX / BytesPerSector)) { // // This is strange. The sizeMB of the disk is too big to fit in 64-bits, // yet the SectorCount does. This implies that this disk has more than // 1 MB Per Sector. Since this is very improbable (disks commonly have 512 // BytesPerSector today), we bail out with an internal error. // DbgFatalMesg((_asrerr, "SpAsrConvertSectorsToMB. Disk has too many sectors\n")); INTERNAL_ERROR((L"Disk has too many sectors\n")); // ok } return (ULONGLONG) ((SectorCount * BytesPerSector) / mb); } extern VOID SpDeleteStorageVolumes ( IN HANDLE SysPrepRegHandle, IN DWORD ControlSetNumber ); extern NTSTATUS SpGetCurrentControlSetNumber( IN HANDLE SystemHiveRoot, OUT PULONG Number ); VOID SpAsrDeleteMountedDevicesKey(VOID) { NTSTATUS status; OBJECT_ATTRIBUTES objAttrib; UNICODE_STRING unicodeString; HANDLE keyHandle; // // Delete HKLM\SYSTEM\MountedDevices. // INIT_OBJA(&objAttrib, &unicodeString, ASR_MOUNTED_DEVICES_KEY); objAttrib.RootDirectory = NULL; status = ZwOpenKey(&keyHandle, KEY_ALL_ACCESS, &objAttrib); if(NT_SUCCESS(status)) { status = ZwDeleteKey(keyHandle); DbgStatusMesg((_asrinfo, "SpAsrDeleteMountedDevicesKey. DeleteKey [%ls] on the setup hive returned 0x%lx. \n", ASR_MOUNTED_DEVICES_KEY, status )); ZwClose(keyHandle); } else { DbgErrorMesg((_asrwarn, "SpAsrDeleteMountedDevicesKey. No [%ls] on the setup hive.\n", ASR_MOUNTED_DEVICES_KEY)); } } PVOID SpAsrMemAlloc( ULONG Size, BOOLEAN IsErrorFatal ) { PVOID ptr = SpMemAlloc(Size); if (ptr) { RtlZeroMemory(ptr, Size); } else { // allocation failed if (IsErrorFatal) { DbgFatalMesg((_asrerr, "SpAsrMemAlloc. Memory allocation failed, SpMemAlloc(%lu) returned NULL.\n", Size )); SpAsrRaiseFatalError(SP_SCRN_OUT_OF_MEMORY, L"Out of memory."); } else { DbgErrorMesg((_asrerr, "SpAsrMemAlloc. Memory allocation failed, SpMemAlloc(%lu) returned NULL. Continuing.\n", Size )); } } return ptr; } BOOLEAN SpAsrIsValidBootDrive(IN OUT PWSTR NtDir) /* Returns TRUE if NtDir starts with ?:\, where ? is between C and Z or c and z, and wcslen(NtDir) <= SpGetMaxNtDirLen(). Converts the drive letter to uppercase. */ { if (!NtDir || wcslen(NtDir) > SpGetMaxNtDirLen() ) { return FALSE; } // convert drive-letter to upper-case if (NtDir[0] >= L'c' && NtDir[0] <= L'z') { NtDir[0] = NtDir[0] - L'a' + L'A'; } // check drive letter if (NtDir[0] < L'C' || NtDir[0] > L'Z') { return FALSE; } // check " :\" if (NtDir[1] == L':' && NtDir[2] == L'\\') { return TRUE; } return FALSE; } BOOLEAN SpAsrIsBootPartitionRecord(IN ULONG CriticalPartitionFlag) { return (BOOLEAN) (CriticalPartitionFlag & ASR_PTN_MASK_BOOT); } BOOLEAN SpAsrIsSystemPartitionRecord(IN ULONG CriticalPartitionFlag) { return (BOOLEAN) (CriticalPartitionFlag & ASR_PTN_MASK_SYS); } // Fatal Error Routines // -guhans! Lots of code-repitition here, there must be a more efficient way. VOID SpAsrRaiseFatalError( IN ULONG ErrorCode, IN PWSTR KdPrintStr ) /*++ Routine: Terminate setup Returns: None. --*/ { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } VOID SpAsrRaiseFatalErrorWs( IN ULONG ErrorCode, IN PWSTR KdPrintStr, IN PWSTR MessageStr ) { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MessageStr ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } VOID SpAsrRaiseFatalErrorWsWs( IN ULONG ErrorCode, IN PWSTR KdPrintStr, IN PWSTR MessageStr1, IN PWSTR MessageStr2 ) { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MessageStr1, MessageStr2 ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } VOID SpAsrRaiseFatalErrorWsLu( IN ULONG ErrorCode, IN PWSTR KdPrintStr, IN PWSTR MessageStr, IN ULONG MessageVal ) { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MessageStr, MessageVal ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } VOID SpAsrRaiseFatalErrorLu( IN ULONG ErrorCode, IN PWSTR KdPrintStr, IN ULONG MessageVal ) { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MessageVal ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } VOID SpAsrRaiseFatalErrorLuLu( IN ULONG ErrorCode, IN PWSTR KdPrintStr, IN ULONG MessageVal1, IN ULONG MessageVal2 ) { KdPrintEx((_asrerr, "SETUP: + %ws\n", KdPrintStr)); SpStartScreen(ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MessageVal1, MessageVal2 ); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3); SpDone(0, FALSE, TRUE); } #define ASCI_O 79 #define ASCI_o 111 // // SpAsrFileErrorRetryIgnoreAbort // Display an error screen if the file that we are trying to // copy already exists on target system. Allows user to // O = Over-write existing file // ESC = Skip this file (preserve existing file) // F3 = Exit from Setup // // Returns TRUE if the user chose overwrite // FALSE if the user chose skip // Does not return if the user hit ESC // BOOL SpAsrFileErrorDeleteSkipAbort( IN ULONG ErrorCode, IN PWSTR DestinationFile ) { ULONG optionKeys[] = {KEY_F3, ASCI_ESC}; ULONG mnemonicKeys[] = {MnemonicOverwrite, 0}; ULONG *keysPtr; BOOL done = FALSE, overwriteFile = FALSE; while (!done) { SpStartScreen( ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DestinationFile ); keysPtr = optionKeys; SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_O_EQUALS_OVERWRITE, SP_STAT_ESC_EQUALS_SKIP_FILE, SP_STAT_F3_EQUALS_EXIT, 0 ); SpInputDrain(); switch(SpWaitValidKey(keysPtr,NULL,mnemonicKeys)) { case (MnemonicOverwrite | KEY_MNEMONIC): overwriteFile = TRUE; done = TRUE; break; case ASCI_ESC: overwriteFile = FALSE; done = TRUE; break; case KEY_F3: SpDone(0, FALSE, TRUE); break; } } return overwriteFile; } // // SpAsrFileErrorRetryIgnoreAbort // Display an error screen if the file could not be copied // over to the target system. Allows user to // ENTER = Retry // ESC = Skip this file and continue // F3 = Exit from Setup // // Returns TRUE if the user chose skip // FALSE if the user chose retry // Does not return if the user hit ESC // BOOL SpAsrFileErrorRetrySkipAbort( IN ULONG ErrorCode, IN PWSTR SourceFile, IN PWSTR Label, IN PWSTR Vendor, IN BOOL AllowSkip ) { ULONG optionKeys[] = {KEY_F3, ASCI_CR, ASCI_ESC}; ULONG mnemonicKeys[] = {0}; ULONG *keysPtr; BOOL done = FALSE, skipFile = FALSE; while (!done) { SpStartScreen( ErrorCode, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, SourceFile, Label, Vendor); keysPtr = optionKeys; if (AllowSkip) { SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_RETRY, SP_STAT_ESC_EQUALS_SKIP_FILE, SP_STAT_F3_EQUALS_EXIT, 0); } else { SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_RETRY, SP_STAT_F3_EQUALS_EXIT, 0); } SpInputDrain(); switch(SpWaitValidKey(keysPtr,NULL,mnemonicKeys)) { case ASCI_CR: skipFile = FALSE; done = TRUE; break; case ASCI_ESC: if (AllowSkip) { skipFile = TRUE; done = TRUE; } break; case KEY_F3: SpDone(0, FALSE, TRUE); break; } } return skipFile; } VOID SpAsrRaiseInternalError( IN PWSTR ModuleName, IN PWSTR ModuleCode, IN ULONG LineNumber, IN PWSTR KdPrintStr) { PWSTR TmpMsgBuf = SpAsrMemAlloc(4096 * sizeof(WCHAR), TRUE); swprintf(TmpMsgBuf, L"%ws%lu", ModuleCode, LineNumber); DbgFatalMesg((_asrerr, " Internal Error (%ws:%lu %ws) %ws\n", ModuleName, LineNumber, TmpMsgBuf, KdPrintStr )); SpAsrRaiseFatalErrorWs(SP_TEXT_DR_INTERNAL_ERROR, KdPrintStr, TmpMsgBuf ); // // Never gets here // } ULONGLONG SpAsrStringToULongLong( IN PWSTR String, OUT PWCHAR *EndOfValue, IN unsigned Radix ) { PWSTR p; BOOLEAN Negative; ULONGLONG Accum,v; WCHAR HighestDigitAllowed,HighestLetterAllowed; WCHAR c; // // Validate radix, 0 or 2-36. // if((Radix == 1) || (Radix > 36)) { if(EndOfValue) { *EndOfValue = String; } return(0); } p = String; // // Skip whitespace. // while(SpIsSpace(*p)) { p++; } // // First char may be a plus or minus. // Negative = FALSE; if(*p == L'-') { Negative = TRUE; p++; } else { if(*p == L'+') { p++; } } if(!Radix) { if(*p == L'0') { // // Octal number // Radix = 8; p++; if((*p == L'x') || (*p == L'X')) { // // hex number // Radix = 16; p++; } } else { Radix = 10; } } HighestDigitAllowed = (Radix < 10) ? L'0'+(WCHAR)(Radix-1) : L'9'; HighestLetterAllowed = (Radix > 10) ? L'A'+(WCHAR)(Radix-11) : 0; Accum = 0; while(1) { c = *p; if((c >= L'0') && (c <= HighestDigitAllowed)) { v = c - L'0'; } else { c = SpToUpper(c); if((c >= L'A') && (c <= HighestLetterAllowed)) { v = c - L'A' + 10; } else { break; } } Accum *= Radix; Accum += v; p++; } if(EndOfValue) { *EndOfValue = p; } return(Negative ? (0-Accum) : Accum); } LONGLONG SpAsrStringToLongLong( IN PWSTR String, OUT PWCHAR *EndOfValue, IN unsigned Radix ) { PWSTR p; BOOLEAN Negative; LONGLONG Accum,v; WCHAR HighestDigitAllowed,HighestLetterAllowed; WCHAR c; // // Validate radix, 0 or 2-36. // if((Radix == 1) || (Radix > 36)) { if(EndOfValue) { *EndOfValue = String; } return(0); } p = String; // // Skip whitespace. // while(SpIsSpace(*p)) { p++; } // // First char may be a plus or minus. // Negative = FALSE; if(*p == L'-') { Negative = TRUE; p++; } else { if(*p == L'+') { p++; } } if(!Radix) { if(*p == L'0') { // // Octal number // Radix = 8; p++; if((*p == L'x') || (*p == L'X')) { // // hex number // Radix = 16; p++; } } else { Radix = 10; } } HighestDigitAllowed = (Radix < 10) ? L'0'+(WCHAR)(Radix-1) : L'9'; HighestLetterAllowed = (Radix > 10) ? L'A'+(WCHAR)(Radix-11) : 0; Accum = 0; while(1) { c = *p; if((c >= L'0') && (c <= HighestDigitAllowed)) { v = c - L'0'; } else { c = SpToUpper(c); if((c >= L'A') && (c <= HighestLetterAllowed)) { v = c - L'A' + 10; } else { break; } } Accum *= Radix; Accum += v; p++; } if(EndOfValue) { *EndOfValue = p; } return(Negative ? (0-Accum) : Accum); } ULONG SpAsrStringToULong( IN PWSTR String, OUT PWCHAR *EndOfValue, IN unsigned Radix ) { PWSTR p; BOOLEAN Negative; ULONG Accum,v; WCHAR HighestDigitAllowed,HighestLetterAllowed; WCHAR c; // // Validate radix, 0 or 2-36. // if((Radix == 1) || (Radix > 36)) { if(EndOfValue) { *EndOfValue = String; } return(0); } p = String; // // Skip whitespace. // while(SpIsSpace(*p)) { p++; } // // First char may be a plus or minus. // Negative = FALSE; if(*p == L'-') { Negative = TRUE; p++; } else { if(*p == L'+') { p++; } } if(!Radix) { if(*p == L'0') { // // Octal number // Radix = 8; p++; if((*p == L'x') || (*p == L'X')) { // // hex number // Radix = 16; p++; } } else { Radix = 10; } } HighestDigitAllowed = (Radix < 10) ? L'0'+(WCHAR)(Radix-1) : L'9'; HighestLetterAllowed = (Radix > 10) ? L'A'+(WCHAR)(Radix-11) : 0; Accum = 0; while(1) { c = *p; if((c >= L'0') && (c <= HighestDigitAllowed)) { v = c - L'0'; } else { c = SpToUpper(c); if((c >= L'A') && (c <= HighestLetterAllowed)) { v = c - L'A' + 10; } else { break; } } Accum *= Radix; Accum += v; p++; } if(EndOfValue) { *EndOfValue = p; } return(Negative ? (0-Accum) : Accum); } USHORT SpAsrStringToUShort( IN PWSTR String, OUT PWCHAR *EndOfValue, IN USHORT Radix ) { PWSTR p; BOOLEAN Negative; USHORT Accum,v; WCHAR HighestDigitAllowed,HighestLetterAllowed; WCHAR c; // // Validate radix, 0 or 2-36. // if((Radix == 1) || (Radix > 36)) { if(EndOfValue) { *EndOfValue = String; } return(0); } p = String; // // Skip whitespace. // while(SpIsSpace(*p)) { p++; } // // First char may be a plus or minus. // Negative = FALSE; if(*p == L'-') { Negative = TRUE; p++; } else { if(*p == L'+') { p++; } } if(!Radix) { if(*p == L'0') { // // Octal number // Radix = 8; p++; if((*p == L'x') || (*p == L'X')) { // // hex number // Radix = 16; p++; } } else { Radix = 10; } } HighestDigitAllowed = (Radix < 10) ? L'0'+(WCHAR)(Radix-1) : L'9'; HighestLetterAllowed = (Radix > 10) ? L'A'+(WCHAR)(Radix-11) : 0; Accum = 0; while(1) { c = *p; if((c >= L'0') && (c <= HighestDigitAllowed)) { v = c - L'0'; } else { c = SpToUpper(c); if((c >= L'A') && (c <= HighestLetterAllowed)) { v = c - L'A' + 10; } else { break; } } Accum *= Radix; Accum += v; p++; } if(EndOfValue) { *EndOfValue = p; } return(Negative ? (0-Accum) : Accum); } int SpAsrCharToInt( IN PWSTR String, OUT PWCHAR *EndOfValue, IN USHORT Radix ) { PWSTR p; BOOLEAN Negative; USHORT Accum,v; WCHAR HighestDigitAllowed,HighestLetterAllowed; WCHAR c; // // Validate radix, 0 or 2-36. // if((Radix == 1) || (Radix > 36)) { if(EndOfValue) { *EndOfValue = String; } return(0); } p = String; // // Skip whitespace. // while(SpIsSpace(*p)) { p++; } // // First char may be a plus or minus. // Negative = FALSE; if(*p == L'-') { Negative = TRUE; p++; } else { if(*p == L'+') { p++; } } if(!Radix) { if(*p == L'0') { // // Octal number // Radix = 8; p++; if((*p == L'x') || (*p == L'X')) { // // hex number // Radix = 16; p++; } } else { Radix = 10; } } HighestDigitAllowed = (Radix < 10) ? L'0'+(WCHAR)(Radix-1) : L'9'; HighestLetterAllowed = (Radix > 10) ? L'A'+(WCHAR)(Radix-11) : 0; Accum = 0; while(1) { c = *p; if((c >= L'0') && (c <= HighestDigitAllowed)) { v = c - L'0'; } else { c = SpToUpper(c); if((c >= L'A') && (c <= HighestLetterAllowed)) { v = c - L'A' + 10; } else { break; } } Accum *= Radix; Accum += v; p++; } if(EndOfValue) { *EndOfValue = p; } return(Negative ? (0-Accum) : Accum); } PWSTR SpAsrHexStringToUChar ( IN PWSTR String, OUT unsigned char * Number ) /*++ Routine Description: This routine converts the hex representation of a number into an unsigned char. The hex representation is assumed to be a full two characters long. Arguments: String - Supplies the hex representation of the number. Number - Returns the number converted from hex representation. Return Value: A pointer to the end of the hex representation is returned if the hex representation was successfully converted to an unsigned char. Otherwise, zero is returned, indicating that an error occured. --*/ { WCHAR Result; int Count; Result = 0; for (Count = 0; Count < 2; Count++, String++) { if ((*String >= L'0') && (*String <= L'9')) { Result = (Result << 4) + *String - L'0'; } else if ((*String >= L'A') && (*String <= L'F')) { Result = (Result << 4) + *String - L'A' + 10; } else if ((*String >= L'a') && (*String <= L'f')) { Result = (Result << 4) + *String - L'a' + 10; } } *Number = (unsigned char)Result; return String; } VOID SpAsrGuidFromString( IN OUT GUID* Guid, IN PWSTR GuidString ) /*++ Routine Description: Gets a GUID from a string Arguments: Guid - The GUID that holds string representation Buffer - The string version of the guid, in the form "%x-%x-%x-%x%x%x%x%x%x%x%x" Return Value: Returns the converted string version of the given GUID --*/ { PWSTR Buffer = GuidString; int i = 0; if (Guid) { ZeroMemory(Guid, sizeof(GUID)); } if (Guid && Buffer) { Guid->Data1 = SpAsrStringToULong(Buffer, NULL, 16); Buffer += 9; Guid->Data2 = SpAsrStringToUShort(Buffer, NULL, 16); Buffer += 5; Guid->Data3 = SpAsrStringToUShort(Buffer, NULL, 16); Buffer += 5; Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[0])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[1])); ++Buffer; Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[2])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[3])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[4])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[5])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[6])); Buffer = SpAsrHexStringToUChar(Buffer,&(Guid->Data4[7])); } } BOOLEAN SpAsrIsZeroGuid( IN GUID * Guid ) { GUID ZeroGuid; ZeroMemory(&ZeroGuid, sizeof(GUID)); if (!memcmp(&ZeroGuid, Guid, sizeof(GUID))) { return TRUE; } return FALSE; }