/*++ Copyright (c) 1993 Microsoft Corporation Module Name: sputil.c Abstract: Miscellaneous functions for text setup. Author: Ted Miller (tedm) 17-Sep-1993 Revision History: --*/ #include "spprecmp.h" #pragma hdrstop #include "bootvar.h" #include "bootstatus.h" #if !defined(SETUP_CAB_TEST_USERMODE) // // On the x86 and amd64, we want to clear the previous OS entry in boot.ini // if we reformat C: // #if defined(_AMD64_) || defined(_X86_) UCHAR OldSystemLine[MAX_PATH]; BOOLEAN DiscardOldSystemLine = FALSE; #endif // defined(_AMD64_) || defined(_X86_) BOOLEAN Nec98RestoreBootFiles = TRUE; //NEC98 extern PDISK_REGION TargetRegion_Nec98; #define REGKEY_SERVICES L"\\Registry\\Machine\\System\\CurrentControlSet\\Services" LIST_ENTRY SpServiceList; typedef struct _SERVICE_ENTRY { LIST_ENTRY Next; PWCHAR ServiceName; } SERVICE_ENTRY, *PSERVICE_ENTRY; // // Setup progress callback data // #define MAX_SETUP_PROGRESS_SUBSCRIBERS 8 ULONG ProgressSubscribersCount = 0; TM_PROGRESS_SUBSCRIBER ProgressSubscribers[MAX_SETUP_PROGRESS_SUBSCRIBERS] = {0}; // // NEC98 // NTSTATUS SpDeleteAndBackupBootFiles( IN BOOLEAN RestoreBackupFiles, IN BOOLEAN DeleteBackupFiles, IN BOOLEAN DeleteRootFiles, IN BOOLEAN RestorePreviousOs, IN BOOLEAN ClearBootFlag ); // // NEC98 // VOID SpSetAutoBootFlag( IN PDISK_REGION TargetRegion, IN BOOLEAN SetBootPosision ); // // NEC98 // NTSTATUS SppRestoreBootCode( VOID ); // // These symbols are the Chkdsk return codes given by autochk // when invoked with the '/s' switch. They were duplicated from // utils\ifsutil\inc\supera.hxx, and should be kept in sync with // the codes listed there. // #define CHKDSK_EXIT_SUCCESS 0 #define CHKDSK_EXIT_ERRS_FIXED 1 #define CHKDSK_EXIT_MINOR_ERRS 2 // whether or not "/f" #define CHKDSK_EXIT_COULD_NOT_CHK 3 #define CHKDSK_EXIT_ERRS_NOT_FIXED 3 #define CHKDSK_EXIT_COULD_NOT_FIX 3 #define AUTOFMT_EXIT_SUCCESS 0 #define AUTOFMT_EXIT_COULD_NOT_FORMAT 1 // // Gauge used to display progress of autochk and autofmt // PVOID UserModeGauge = NULL; // // This variable is used when displaying the progress bar // during autochk and autofmt. It indicates the disk that // is being autochecked or formatted. // ULONG CurrentDiskIndex = 0; // // Seed used for generating random number for disk signature // and pseudo GUIDs // ULONG RandomSeed = 17; BOOLEAN SppPromptOptionalAutochk( IN PVOID SifHandle, IN PWSTR MediaShortname, IN PWSTR DiskDevicePath ); extern BOOLEAN SpGenerateNTPathName( IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR TargetPath ); VOID SpDone( IN DWORD MsgId, IN BOOLEAN Successful, IN BOOLEAN Wait ) /*++ Routine Description: Display a message indicating that we are done with setup, and text setup completed successfully, or that windows nt is not installed. Then reboot the machine. Arguments: Successful - if TRUE, then tell the user that pressing enter will restart the machine and continue setup. Otherwise, tell the user that Windows NT is not installed. Wait - if FALSE, do not display a screen, just reboot immediately. Otherwise, wait for the user to press enter before rebooting. Return Value: DOES NOT RETURN --*/ { #define SECS_FOR_REBOOT 15 ULONG MessageId; PWSTR p; LARGE_INTEGER DelayInterval; ULONG InputChar; ULONG Seconds; PVOID DelayGauge; if(Wait) { if (MsgId) { MessageId = MsgId; } else if(RepairWinnt) { MessageId = Successful ? SP_SCRN_REPAIR_SUCCESS : SP_SCRN_REPAIR_FAILURE; } else { MessageId = Successful ? SP_SCRN_TEXTSETUP_SUCCESS : SP_SCRN_TEXTSETUP_FAILURE; } SpStartScreen(MessageId,3,4,FALSE,FALSE,DEFAULT_ATTRIBUTE); #if defined(_AMD64_) || defined(_X86_) SpContinueScreen(SP_SCRN_REMOVE_FLOPPY,3,1,FALSE,DEFAULT_ATTRIBUTE); // // For machines with El-Torito boot we need to tell the user // to remove the CD-ROM also. There are a whole bunch of different // possibilities: user booted from floppy but is using the CD, etc. // We'll only tell the user to remove the CD if he actually booted // from it, since otherwise we assume the machine is set up to *not* // boot from CD-ROM and the presence of the CD is irrelevent. // // tedm: the above logic is nice but there are plenty of machines // out there with broken eltorito. Thus well always tell people to // remove the CD if they have a CD-ROM drive. // #if 0 SpStringToLower(ArcBootDevicePath); if(wcsstr(ArcBootDevicePath,L")cdrom(")) { SpContinueScreen(SP_SCRN_ALSO_REMOVE_CD,3,0,FALSE,DEFAULT_ATTRIBUTE); } // #else if(IoGetConfigurationInformation()->CdRomCount) { SpContinueScreen(SP_SCRN_ALSO_REMOVE_CD,3,0,FALSE,DEFAULT_ATTRIBUTE); } #endif #endif // defined(_AMD64_) || defined(_X86_) SpContinueScreen(SP_SCRN_ENTER_TO_RESTART,3,1,FALSE,DEFAULT_ATTRIBUTE); if(!RepairWinnt && Successful) { SpContinueScreen(SP_SCRN_RESTART_EXPLAIN,3,0,FALSE,DEFAULT_ATTRIBUTE); } SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_ENTER_EQUALS_RESTART,0); DelayInterval.LowPart = -10000000; DelayInterval.HighPart = -1; Seconds = 0; SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_SETUP_REBOOT); DelayGauge = SpCreateAndDisplayGauge( SECS_FOR_REBOOT, 0, 15, L"", TemporaryBuffer, GF_ITEMS_REMAINING, ATT_BG_RED | ATT_BG_INTENSE ); ASSERT( DelayGauge ); SpInputDrain(); while (Seconds < SECS_FOR_REBOOT) { KeDelayExecutionThread( ExGetPreviousMode(), FALSE, &DelayInterval ); if (SpInputIsKeyWaiting()) { InputChar = SpInputGetKeypress(); if (InputChar == ASCI_CR) { break; } else { SpInputDrain(); break; } } SpTickGauge( DelayGauge ); Seconds += 1; } SpDestroyGauge( DelayGauge ); } #ifdef _X86_ // // restore backed up boot files for other OS on NEC98. // if (IsNEC_98) { //NEC98 if(Nec98RestoreBootFiles && (IsFloppylessBoot || UnattendedOperation)) { WCHAR DevicePath[MAX_PATH]; WCHAR PartitionPath[MAX_PATH]; BOOLEAN RestoreBackupFiles, DeleteBackupFiles, DeleteRootDirFiles, RestorePreviousOs, ClearBootFlag; if(TargetRegion_Nec98) { wcscpy(DevicePath, PartitionedDisks[TargetRegion_Nec98->DiskNumber].HardDisk->DevicePath ); swprintf(PartitionPath, L"partition%lu", SpPtGetOrdinal(TargetRegion_Nec98,PartitionOrdinalCurrent) ); SpConcatenatePaths(DevicePath,PartitionPath); } if(Successful){ if(!_wcsicmp(NtBootDevicePath, DevicePath)) { // // case normal exit and same bootpath and targetpath. // RestoreBackupFiles = FALSE; DeleteBackupFiles = TRUE; DeleteRootDirFiles = FALSE; RestorePreviousOs = FALSE; ClearBootFlag = FALSE; //SpDeleteAndBackupBootFiles(FALSE,TRUE,FALSE,FALSE,FALSE); } else { // // case normal exit and different bootpath and targetpath. // RestoreBackupFiles = TRUE; DeleteBackupFiles = TRUE; DeleteRootDirFiles = TRUE; RestorePreviousOs = TRUE; ClearBootFlag = FALSE; //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,FALSE); } } else { // // case abnormal exit // if(TargetRegion_Nec98) { // // after selecting target partition // if(!_wcsicmp(NtBootDevicePath, DevicePath)) { RestoreBackupFiles = FALSE; DeleteBackupFiles = TRUE; DeleteRootDirFiles = TRUE; RestorePreviousOs = FALSE; ClearBootFlag = TRUE; //SpDeleteAndBackupBootFiles(FALSE,TRUE,TRUE,FALSE,TRUE); }else{ RestoreBackupFiles = TRUE; DeleteBackupFiles = TRUE; DeleteRootDirFiles = TRUE; RestorePreviousOs = TRUE; ClearBootFlag = TRUE; //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,TRUE); } } else { RestoreBackupFiles = TRUE; DeleteBackupFiles = TRUE; DeleteRootDirFiles = TRUE; RestorePreviousOs = TRUE; ClearBootFlag = FALSE; //SpDeleteAndBackupBootFiles(TRUE,TRUE,TRUE,TRUE,FALSE); } // // In the case of, winnt32 from Win95 that have separated // system partition or winnt from DOS, Auto boot flag will // set system partition not booted partition.. // if(IsFloppylessBoot){ ClearBootFlag = TRUE; } } SpDeleteAndBackupBootFiles(RestoreBackupFiles, DeleteBackupFiles, DeleteRootDirFiles, RestorePreviousOs, ClearBootFlag); } } //NEC98 #endif CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_SHUTTING_DOWN,DEFAULT_STATUS_ATTRIBUTE); SpShutdownSystem(); // // Shouldn't get here. // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: shutdown returned!\n")); HalReturnToFirmware(HalRebootRoutine); } VOID SpFatalSifError( IN PVOID SifHandle, IN PWSTR Section, IN PWSTR Key, OPTIONAL IN ULONG Line, IN ULONG ValueNumber ) /*++ Routine Description: Inform the user that a required value is missing or corrupt in a sif file. Display the section, line number or key, and value number. Then reboot the machine. Arguments: SifHandle - specifies the information file which is corrupt. Section - supplies the name of the section that is corrupt. Key - if specified, specifies the line in the section that is missing or corrupt. Line - if Key is not specified, then this is the line number within the section that is corrupt. ValueNumber - supplies the value number on the line that is missing or corrupt. Return Value: DOES NOT RETURN --*/ { ULONG ValidKeys[2] = { KEY_F3,0 }; // // Display a message indicating that there is a fatal // error in the sif file. // if(Key) { SpStartScreen( SP_SCRN_FATAL_SIF_ERROR_KEY, 3, HEADER_HEIGHT+3, FALSE, FALSE, DEFAULT_ATTRIBUTE, ValueNumber, Section, Key ); } else { SpStartScreen( SP_SCRN_FATAL_SIF_ERROR_LINE, 3, HEADER_HEIGHT+3, FALSE, FALSE, DEFAULT_ATTRIBUTE, ValueNumber, Line, Section ); } SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_F3_EQUALS_EXIT,0); SpWaitValidKey(ValidKeys,NULL,NULL); SpDone(0,FALSE,TRUE); } VOID SpNonFatalSifError( IN PVOID SifHandle, IN PWSTR Section, IN PWSTR Key, OPTIONAL IN ULONG Line, IN ULONG ValueNumber, IN PWSTR FileName ) /*++ Routine Description: Inform the user that a required value is missing or corrupt in a sif file. Display the section, line number or key, and value number, along with the file name that cannot be copied. Then ask the user if they want to skip the file or exit Setup. Arguments: SifHandle - specifies the information file which is corrupt. Section - supplies the name of the section that is corrupt. Key - if specified, specifies the line in the section that is missing or corrupt. Line - if Key is not specified, then this is the line number within the section that is corrupt. ValueNumber - supplies the value number on the line that is missing or corrupt. FileName - supplies the name of the file that cannot be copied. Return Value: none (may not return if user chooses to exit Setup) --*/ { ULONG ValidKeys[3] = { ASCI_ESC, KEY_F3, 0 }; // // Display a message indicating that there is a fatal // error in the sif file. // if(Key) { SpStartScreen( SP_SCRN_NONFATAL_SIF_ERROR_KEY, 3, HEADER_HEIGHT+3, FALSE, FALSE, DEFAULT_ATTRIBUTE, ValueNumber, Section, Key, FileName ); } else { SpStartScreen( SP_SCRN_NONFATAL_SIF_ERROR_LINE, 3, HEADER_HEIGHT+3, FALSE, FALSE, DEFAULT_ATTRIBUTE, ValueNumber, Line, Section, FileName ); } SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ESC_EQUALS_SKIP_FILE, SP_STAT_F3_EQUALS_EXIT, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_ESC: // skip file break; case KEY_F3: // exit setup SpConfirmExit(); } } VOID SpConfirmExit( VOID ) /*++ Routine Description: Confirm with the user that he really wants to exit. If he does, then exit, otherwise return. When this routine returns, the caller must repaint the entire client area and status area of the screen. Arguments: None. Return Value: MAY NOT RETURN --*/ { ULONG ValidKeys[3] = { ASCI_CR, KEY_F3, 0 }; WCHAR *p = (WCHAR *)TemporaryBuffer; BOOLEAN FirstLine,FirstCharOnLine; // // Don't erase the screen. // // We have to do something very funky here because the resources // are originally in ANSI, which doesn't have the line-draw chars. // vSpFormatMessage( TemporaryBuffer, sizeof(TemporaryBuffer), SP_SCRN_EXIT_CONFIRMATION, NULL, NULL ); for(FirstCharOnLine=TRUE,FirstLine=TRUE; *p; p++) { switch(*p) { case L'+': if(FirstCharOnLine) { *p = SplangGetLineDrawChar( FirstLine ? LineCharDoubleUpperLeft : LineCharDoubleLowerLeft ); FirstCharOnLine = FALSE; } else { *p = SplangGetLineDrawChar( FirstLine ? LineCharDoubleUpperRight : LineCharDoubleLowerRight ); } break; case L'=': FirstCharOnLine = FALSE; *p = SplangGetLineDrawChar(LineCharDoubleHorizontal); break; case L'-': FirstCharOnLine = FALSE; *p = SplangGetLineDrawChar(LineCharSingleHorizontal); break; case L'|': FirstCharOnLine = FALSE; *p = SplangGetLineDrawChar(LineCharDoubleVertical); break; case L'*': *p = SplangGetLineDrawChar( FirstCharOnLine ? LineCharDoubleVerticalToSingleHorizontalRight : LineCharDoubleVerticalToSingleHorizontalLeft ); FirstCharOnLine = FALSE; break; case L'\n': FirstCharOnLine = TRUE; FirstLine = FALSE; break; default: FirstCharOnLine = FALSE; break; } } SpDisplayText( TemporaryBuffer, wcslen(TemporaryBuffer)+1, TRUE, TRUE, ATT_FG_RED | ATT_BG_WHITE, 0, 0 ); SpvidClearScreenRegion( 0, VideoVars.ScreenHeight-STATUS_HEIGHT, VideoVars.ScreenWidth, STATUS_HEIGHT, DEFAULT_STATUS_BACKGROUND ); if(SpWaitValidKey(ValidKeys,NULL,NULL) == KEY_F3) { SpDone(0,FALSE,TRUE); } // // User backed out of bailing, just return to caller. // } #endif PWSTR SpDupStringW( IN PCWSTR String ) { PWSTR p; p = SpMemAlloc((wcslen(String)+1) * sizeof(WCHAR)); ASSERT(p); wcscpy(p,String); return(p); } PSTR SpDupString( IN PCSTR String ) { PUCHAR p; p = SpMemAlloc(strlen(String)+1); ASSERT(p); strcpy(p,String); return(p); } PWSTR SpToUnicode( IN PUCHAR OemString ) { ULONG OemStringSize; ULONG MaxUnicodeStringSize; ULONG ActualUnicodeStringSize; PWSTR UnicodeString; // // Determine the maximum number of bytes in the oem string // and allocate a buffer to hold a string of that size. // The maximum length of the equivalent unicode string // is twice that number (this occurs when all oem chars // in the string are single-byte). // OemStringSize = strlen(OemString) + 1; MaxUnicodeStringSize = OemStringSize * sizeof(WCHAR); UnicodeString = SpMemAlloc(MaxUnicodeStringSize); ASSERT(UnicodeString); // // Call the conversion routine. // RtlOemToUnicodeN( UnicodeString, MaxUnicodeStringSize, &ActualUnicodeStringSize, OemString, OemStringSize ); // // Reallocate the unicode string to its real size, // which depends on the number of doublebyte characters // OemString contained. // if(ActualUnicodeStringSize != MaxUnicodeStringSize) { UnicodeString = SpMemRealloc(UnicodeString,ActualUnicodeStringSize); ASSERT(UnicodeString); } return(UnicodeString); } PUCHAR SpToOem( IN PWSTR UnicodeString ) { ULONG UnicodeStringSize; ULONG MaxOemStringSize; ULONG ActualOemStringSize; PUCHAR OemString; // // Allocate a buffer of maximum size to hold the oem string. // The maximum size would occur if all characters in the // unicode string being converted have doublebyte OEM equivalents. // UnicodeStringSize = (wcslen(UnicodeString)+1) * sizeof(WCHAR); MaxOemStringSize = UnicodeStringSize; OemString = SpMemAlloc(MaxOemStringSize); ASSERT(OemString); // // Call the conversion routine. // RtlUnicodeToOemN( OemString, MaxOemStringSize, &ActualOemStringSize, UnicodeString, UnicodeStringSize ); // // Reallocate the oem string to reflect its true size, // which depends on the number of doublebyte characters it contains. // if(ActualOemStringSize != MaxOemStringSize) { OemString = SpMemRealloc(OemString,ActualOemStringSize); ASSERT(OemString); } return(OemString); } VOID SpConcatenatePaths( IN OUT PWSTR Path1, OPTIONAL IN PCWSTR Path2 OPTIONAL ) { UNICODE_STRING Path1_Ustr; UNICODE_STRING Path2_Ustr; if (!Path1) { return; } RtlInitUnicodeString(&Path1_Ustr, Path1); Path1_Ustr.MaximumLength = 10000; // arbitrarily large RtlInitUnicodeString(&Path2_Ustr, Path2); SpConcatenatePaths_Ustr(&Path1_Ustr, &Path2_Ustr); RTL_STRING_NUL_TERMINATE(&Path1_Ustr); } NTSTATUS SpConcatenatePaths_Ustr( IN OUT PUNICODE_STRING Path1_Ustr, IN PCUNICODE_STRING Path2_Ustr ) { NTSTATUS Status = STATUS_INTERNAL_ERROR; UNICODE_STRING Path2_Ustr_mutable; const static UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L""); BOOLEAN AppendPath2 = FALSE; SIZE_T NewLength = 0; if (Path1_Ustr == NULL) { Status = STATUS_SUCCESS; goto Exit; } if (Path1_Ustr->Buffer == NULL) { Status = STATUS_SUCCESS; goto Exit; } // // remove one trailing backslash from path1, remove one leading backslash from path2, // append one trailing backlash to path1, and append path2 to path1. // if (Path1_Ustr->Length != 0 && RTL_STRING_GET_LAST_CHAR(Path1_Ustr) == L'\\') { Path1_Ustr->Length -= sizeof(Path1_Ustr->Buffer[0]); Path1_Ustr->MaximumLength -= sizeof(Path1_Ustr->Buffer[0]); } if (Path2_Ustr != NULL && Path2_Ustr->Buffer != NULL && Path2_Ustr->Length != 0) { Path2_Ustr_mutable = *Path2_Ustr; if (Path2_Ustr_mutable.Buffer[0] == L'\\') { Path2_Ustr_mutable.Buffer += 1; Path2_Ustr_mutable.Length -= sizeof(Path2_Ustr_mutable.Buffer[0]); Path2_Ustr_mutable.MaximumLength -= sizeof(Path2_Ustr_mutable.Buffer[0]); } AppendPath2 = TRUE; } else { AppendPath2 = FALSE; } // // Append a backslash, then Path2 if it was specified // NewLength = Path1_Ustr->Length + sizeof(WCHAR); if (NewLength > Path1_Ustr->MaximumLength) { Status = STATUS_NAME_TOO_LONG; goto Exit; } Path1_Ustr->Buffer[RTL_STRING_GET_LENGTH_CHARS(Path1_Ustr)] = L'\\'; Path1_Ustr->Length = (RTL_STRING_LENGTH_TYPE)NewLength; if (AppendPath2) { NewLength = (Path1_Ustr->Length + Path2_Ustr_mutable.Length); if (NewLength > Path1_Ustr->MaximumLength) { Status = STATUS_NAME_TOO_LONG; goto Exit; } RtlMoveMemory( Path1_Ustr->Buffer + RTL_STRING_GET_LENGTH_CHARS(Path1_Ustr), Path2_Ustr_mutable.Buffer, Path2_Ustr_mutable.Length ); Path1_Ustr->Length = (RTL_STRING_LENGTH_TYPE)NewLength; } Status = STATUS_SUCCESS; Exit: return Status; } #if !defined(SETUP_CAB_TEST_USERMODE) VOID SpFetchDiskSpaceRequirements( IN PVOID SifHandle, IN ULONG BytesPerCluster, OUT PULONG FreeKBRequired, OPTIONAL OUT PULONG FreeKBRequiredSysPart OPTIONAL ) { PWSTR p; if(FreeKBRequired) { WCHAR ClusterSizeString[64]; if( BytesPerCluster <= 512 ) { // // We got some miniscule cluster size. Assume 512 byte. // wcscpy( ClusterSizeString, L"WinDirSpace512" ); } else if( BytesPerCluster > (256 * 1024) ) { // // We got some huge cluster size. Must be garbage, assume 32K byte. // wcscpy( ClusterSizeString, L"WinDirSpace32K" ); } else { swprintf( ClusterSizeString, L"WinDirSpace%uK", BytesPerCluster/1024 ); } p = SpGetSectionKeyIndex( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 0 ); if(!p) { SpFatalSifError( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 0, 0 ); } *FreeKBRequired = (ULONG)SpStringToLong(p,NULL,10); } if(FreeKBRequiredSysPart) { p = SpGetSectionKeyIndex( SifHandle, SIF_DISKSPACEREQUIREMENTS, SIF_FREESYSPARTDISKSPACE, 0 ); if(!p) { SpFatalSifError( SifHandle, SIF_DISKSPACEREQUIREMENTS, SIF_FREESYSPARTDISKSPACE, 0, 0 ); } *FreeKBRequiredSysPart = (ULONG)SpStringToLong(p,NULL,10); } } VOID SpFetchTempDiskSpaceRequirements( IN PVOID SifHandle, IN ULONG BytesPerCluster, OUT PULONG LocalSourceKBRequired, OPTIONAL OUT PULONG BootKBRequired OPTIONAL ) { PWSTR p; WCHAR ClusterSizeString[64]; if( BytesPerCluster <= 512 ) { // // We got some miniscule cluster size. Assume 512 byte. // wcscpy( ClusterSizeString, L"TempDirSpace512" ); } else if( BytesPerCluster > (256 * 1024) ) { // // We got some huge cluster size. Must be garbage, assume 32K byte. // wcscpy( ClusterSizeString, L"TempDirSpace32K" ); } else { swprintf( ClusterSizeString, L"TempDirSpace%uK", BytesPerCluster/1024 ); } if(LocalSourceKBRequired) { p = SpGetSectionKeyIndex( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 0 ); if(!p) { SpFatalSifError( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 0, 0 ); } *LocalSourceKBRequired = ((ULONG)SpStringToLong(p,NULL,10) + 1023) / 1024; // round up } if(BootKBRequired) { p = SpGetSectionKeyIndex( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 1 ); if(!p) { SpFatalSifError( SifHandle, SIF_DISKSPACEREQUIREMENTS, ClusterSizeString, 0, 1 ); } *BootKBRequired = ((ULONG)SpStringToLong(p,NULL,10) + 1023) / 1024; // round up } } PDISK_REGION SpRegionFromArcName( IN PWSTR ArcName, IN PartitionOrdinalType OrdinalType, IN PDISK_REGION PreviousMatch ) /*++ Routine Description: Given an ARC name find the region descriptor which describes the drive this ARC name is on. Arguments: ArcName - supplies the arc name. OrdinalType - primary (multi) or secondary (scsi) type. PreviousMatch - specifies where we should begin looking. Return Value: Region descriptor if one found, otherwise NULL. --*/ { PDISK_REGION Region = NULL; PWSTR NormalizedArcPath = NULL; ULONG disk; PWSTR ArcPath1,ArcPath2; BOOLEAN StartLooking = FALSE; #define BufferSize 2048 ArcPath1 = SpMemAlloc(BufferSize); ArcPath2 = SpMemAlloc(BufferSize); if( ArcName && *ArcName ) { NormalizedArcPath = SpNormalizeArcPath( ArcName ); if( NormalizedArcPath ) { if(!PreviousMatch) { // then we start from the beginning StartLooking = TRUE; } for( disk=0; diskPartitionedSpace && StartLooking) { SpArcNameFromRegion(Region,ArcPath1,BufferSize,OrdinalType,PrimaryArcPath); SpArcNameFromRegion(Region,ArcPath2,BufferSize,OrdinalType,SecondaryArcPath); if(!_wcsicmp(ArcPath1, NormalizedArcPath) || !_wcsicmp(ArcPath2, NormalizedArcPath)) { break; } } Region = Region->Next; } if ( Region ) { break; } Region = PartitionedDisks[disk].ExtendedDiskRegions; while( Region ) { if((!StartLooking) && (Region == PreviousMatch)) { StartLooking = TRUE; } else if(Region->PartitionedSpace && StartLooking) { SpArcNameFromRegion(Region,ArcPath1,BufferSize,OrdinalType,PrimaryArcPath); SpArcNameFromRegion(Region,ArcPath2,BufferSize,OrdinalType,SecondaryArcPath); if(!_wcsicmp(ArcPath1, NormalizedArcPath) || !_wcsicmp(ArcPath2, NormalizedArcPath)) { break; } } Region = Region->Next; } if ( Region ) { break; } } #if defined(REMOTE_BOOT) if ( (Region == NULL) && RemoteBootSetup && !RemoteInstallSetup && (PreviousMatch == NULL) ) { if (_wcsicmp(L"net(0)", NormalizedArcPath) == 0) { Region = RemoteBootTargetRegion; } } #endif // defined(REMOTE_BOOT) } if( NormalizedArcPath ) { SpMemFree( NormalizedArcPath ); } } SpMemFree(ArcPath1); SpMemFree(ArcPath2); return( Region ); } PDISK_REGION SpRegionFromNtName( IN PWSTR NtName, IN PartitionOrdinalType OrdinalType ) /*++ Routine Description: Given an Nt name find the region descriptor which describes the drive this NT name is on. Arguments: NtName - supplies the Nt name of the desired region. PartitionOrdinalType - Specifies the ordinal type of the partition. Return Value: Region descriptor if one found, otherwise NULL. --*/ { PDISK_REGION Region = NULL; PWSTR p; // // Convert to arc path. // if (p = SpNtToArc(NtName, PrimaryArcPath)) { Region = SpRegionFromArcName(p, PartitionOrdinalCurrent, NULL); SpMemFree(p); } return(Region); } PDISK_REGION SpRegionFromDosName( IN PCWSTR DosName ) /*++ Routine Description: Given a DOS name find the region descriptor which describes the drive this ARC name is on. Arguments: ArcName - supplies the arc name. Return Value: Region descriptor if one found, otherwise NULL. --*/ { PDISK_REGION Region = NULL; ULONG disk; WCHAR DriveLetter; if( DosName && *DosName && *(DosName + 1) == L':' ) { DriveLetter = SpToUpper(*DosName); #if defined(REMOTE_BOOT) if ( RemoteBootSetup && !RemoteInstallSetup && (DriveLetter == L'C') ) { return RemoteBootTargetRegion; } #endif // defined(REMOTE_BOOT) for( disk=0; diskPartitionedSpace && (Region->DriveLetter == DriveLetter)) { break; } Region = Region->Next; } if ( Region ) { break; } Region = PartitionedDisks[disk].ExtendedDiskRegions; while( Region ) { if(Region->PartitionedSpace && (Region->DriveLetter == DriveLetter)) { break; } Region = Region->Next; } if ( Region ) { break; } } } return( Region ); } PDISK_REGION SpRegionFromArcOrDosName( IN PWSTR Name, IN PartitionOrdinalType OrdinalType, IN PDISK_REGION PreviousMatch ) { PDISK_REGION Region; // // Determine if Name represents an ARC name or a DOS name and use // the appropriate routine to extract the region for this name. Check // for the ":" character at position 2 to see if it is a DOS name. // If not a DOS name then assume it is an ARC name. // if(Name) { if(Name[0] && (Name[1] == ':')) { if(PreviousMatch) { Region = NULL; } else { Region = SpRegionFromDosName(Name); } } else { Region = SpRegionFromArcName(Name, OrdinalType, PreviousMatch); } } else { Region = NULL; } return(Region); } VOID SpNtNameFromRegion( IN PDISK_REGION Region, OUT PWSTR NtPath, IN ULONG BufferSizeBytes, IN PartitionOrdinalType OrdinalType ) /*++ Routine Description: Generate a name in the NT name space for a region. This name can be in one of three forms. For partitions, the name is always of the form \device\harddisk\partition. If the region is actually a DoubleSpace drive, then the name is of the form \device\harddisk\partition. where is the filename of the CVF (ie, something like dblspace.001). If the region is on a redirected drive, the name is of the form \device\lanmanredirector\\ Arguments: Region - supplies a pointer to the region descriptor for the region whose path is desired. NtPath - receives the path. BufferSizeBytes - specifies the size of the buffer pointed to by NtPath. The name will be truncated to fit in the buffer if necessary. OrdinalType - indicates which partition ordinal (original, on disk, current) to use when generating the name. Return Value: None. --*/ { ULONG MaxNameChars; ULONG NeededChars; WCHAR PartitionComponent[50]; INT iResult = 0; #if defined(REMOTE_BOOT) // // Handle remote boot case where target is over the network. // if (Region->DiskNumber == 0xffffffff) { wcscpy(NtPath,Region->TypeName); return; } #endif // defined(REMOTE_BOOT) // // Calculate the maximum size of the name if unicode characters. // Leave room for a terminating nul. // MaxNameChars = (BufferSizeBytes / sizeof(WCHAR)) - 1; // // Generate the partition component of the name. // Note that the first letter of PartitionComponent must be upper case. // if(_snwprintf(PartitionComponent, (sizeof(PartitionComponent)/sizeof(WCHAR)) - 1, L"\\Partition%u", SpPtGetOrdinal(Region,OrdinalType)) < 0){ ASSERT(FALSE); PartitionComponent[(sizeof(PartitionComponent)/sizeof(WCHAR)) - 1] = '\0'; } // // Calculate the amount of buffer space needed for the path. // NeededChars = wcslen(HardDisks[Region->DiskNumber].DevicePath) + wcslen(PartitionComponent); if(Region->Filesystem == FilesystemDoubleSpace) { // // Add the size taken up by the double space cvf name. // This is the length of the name, plus one character // for the dot. // NeededChars += 8+1+3+1; // Maximum size of a CVF file name } // // Even though we do something reasonable in this case, // really it should never happen. If the name is truncated, // it won't be of any use anyway. // ASSERT(NeededChars <= MaxNameChars); // // Generate the name. // if(Region->Filesystem == FilesystemDoubleSpace) { iResult = _snwprintf(NtPath, MaxNameChars, L"%ws%ws.%ws.%03d", HardDisks[Region->DiskNumber].DevicePath, PartitionComponent, L"DBLSPACE", Region->SeqNumber); } else{ iResult = _snwprintf(NtPath, MaxNameChars, L"%ws%ws", HardDisks[Region->DiskNumber].DevicePath, PartitionComponent); } if(iResult < 0){ ASSERT(FALSE); NtPath[MaxNameChars] = '\0'; } } VOID SpArcNameFromRegion( IN PDISK_REGION Region, OUT PWSTR ArcPath, IN ULONG BufferSizeBytes, IN PartitionOrdinalType OrdinalType, IN ENUMARCPATHTYPE ArcPathType ) /*++ Routine Description: Generate a name in the ARC name space for a region. Arguments: Region - supplies a pointer to the region descriptor for the region whose path is desired. ArcPath - receives the path. BufferSizeBytes - specifies the size of the buffer pointed to by ArcPath. The name will be truncated to fit in the buffer if necessary. OrdinalType - indicates which partition ordinal (original, on disk, current) to use when generating the name. ArcPathType - Look for the primary or secondary arc path depending on this value. This is meaningful for disks on amd64/x86 that are scsi but visible through the bios. The multi() style name is the 'primary' arc path; the scsi() style name is the 'secondary' one. Return Value: None. --*/ { PWSTR p; // // Get the nt name. // SpNtNameFromRegion(Region,ArcPath,BufferSizeBytes,OrdinalType); // // Convert to arc path. // if(p = SpNtToArc(ArcPath,ArcPathType)) { wcsncpy(ArcPath,p,(BufferSizeBytes/sizeof(WCHAR))-1); SpMemFree(p); ArcPath[(BufferSizeBytes/sizeof(WCHAR))-1] = 0; } else { *ArcPath = 0; } } BOOLEAN SpNtNameFromDosPath ( IN PCWSTR DosPath, OUT PWSTR NtPath, IN UINT NtPathSizeInBytes, IN PartitionOrdinalType OrdinalType ) /*++ Routine Description: SpNtNameFromDosPath converts a DOS path (in x:\foo\bar format) into an NT name (such as \devices\harddisk0\parition1\foo\bar). Arguments: DosPath - Specifies the DOS path to convert NtPath - Receives the NT object NtPathSizeInBytes - Specifies the size of NtPath OrdinalType - indicates which partition ordinal (original, on disk, current) to use when generating the name. Return Value: TRUE if the path was converted, FALSE otherwise. --*/ { PDISK_REGION region; // // Get region on disk for the DOS path // region = SpRegionFromDosName (DosPath); if (!region) { KdPrintEx (( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpNtPathFromDosPath failed to get region for %ws\n", DosPath )); return FALSE; } // // Convert region struct into an NT path. // SpNtNameFromRegion( region, NtPath, NtPathSizeInBytes - (wcslen (&DosPath[2]) * sizeof (WCHAR)), OrdinalType ); SpConcatenatePaths (NtPath, &DosPath[2]); return TRUE; } BOOLEAN SpPromptForDisk( IN PWSTR DiskDescription, IN OUT PWSTR DiskDevicePath, IN PWSTR DiskTagFile, IN BOOLEAN IgnoreDiskInDrive, IN BOOLEAN AllowEscape, IN BOOLEAN WarnMultiplePrompts, OUT PBOOLEAN pRedrawFlag ) /*++ Routine Description: Prompt the user to insert a floppy disk or CD-ROM. Arguments: DiskDescription - supplies a descriptive name for the disk. DiskDevicePath - supplies the device path for the device on which we want the user to insert the disk. This should be a real nt device object, as opposed to a symbolic link (ie, use \device\floppy0, not \dosdevices\a:). NOTE: This path will be modified only in case of prompting for a CD-ROM 0 and the required disk existed on another CD-ROM like CD-ROM 2. DiskTagFile - supplies the full path (relative to the root) of a file whose presence on the disk indicates the presence of the disk we are prompting for. IgnoreDiskInDrive - if TRUE, the Setup will always issue at least one prompt. If FALSE, Setup checks the disk in the drive and thus may issue 0 prompts. AllowEscape - if TRUE, the user can press escape to indicate that he wishes to cancel the operation. (This is meaningful only to the caller). WarnMultiplePrompts - if TRUE and DiskDevicePath desribes a floppy disk drive, then put up a little note when displaying the disk prompt, that we may prompt for some disks more than once. Users get confused when we ask them to insert disks that they already inserted once before. pRedrawFlag - if non-NULL, receives a flag indicating whether the screen was messed up with a disk prompt, requiring a redraw. Return Value: TRUE if the requested disk is in the drive. FALSE otherwise. FALSE can only be returned if AllowEscape is TRUE. --*/ { WCHAR OpenPath[MAX_PATH]; NTSTATUS Status; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; BOOLEAN Done = FALSE; BOOLEAN rc; WCHAR DriveLetter; ULONG PromptId; ULONG ValidKeys[4] = { KEY_F3, ASCI_CR, 0, 0 }; BOOLEAN TryOpen; // // Initially, assume no redraw required // if(pRedrawFlag) { *pRedrawFlag = FALSE; } // // Need to get device characteristics to see whether // the device is a cd, fixed disk or removable disk/floppy. // SpStringToLower(DiskDevicePath); if( !_wcsnicmp(DiskDevicePath,L"\\device\\cdrom",13)) { PromptId = SP_SCRN_CDROM_PROMPT; WarnMultiplePrompts = FALSE; } else if( !_wcsnicmp(DiskDevicePath,L"\\device\\floppy",14)) { PromptId = SP_SCRN_FLOPPY_PROMPT; DriveLetter = (WCHAR)SpStringToLong(wcsstr(DiskDevicePath,L"floppy")+6,NULL,10) + L'A'; } else { // // Assume hard disk // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpPromptforDisk assuming %ws is hard disk, returning TRUE\n",DiskDevicePath)); return(TRUE); } // // Form the complete NT pathname of the tagfile. // wcscpy(OpenPath,DiskDevicePath); SpConcatenatePaths(OpenPath,DiskTagFile); // // Initialize object attributes. // INIT_OBJA(&ObjectAttributes,&UnicodeString,OpenPath); // // If we're looking for a cdrom0, and there are multiple CDROM // drives in the machine, skip prompting the user the first time // and look for our tag on all the CD drives first. // if( (PromptId == SP_SCRN_CDROM_PROMPT) && (IoGetConfigurationInformation()->CdRomCount > 1) && (wcsstr( OpenPath, L"cdrom0" ))) { IgnoreDiskInDrive = FALSE; } do { // // Put up the prompt. // TryOpen = TRUE; if(IgnoreDiskInDrive) { // // We going to put up a prompt screen, so a redraw will be required // if(pRedrawFlag) { *pRedrawFlag = TRUE; } SpStartScreen(PromptId,0,0,TRUE,TRUE,DEFAULT_ATTRIBUTE,DiskDescription,DriveLetter); // // Display status options: exit, enter, and escape if specified. // SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, SP_STAT_ENTER_EQUALS_CONTINUE, AllowEscape ? SP_STAT_ESC_EQUALS_CANCEL : 0, 0 ); if(AllowEscape) { ValidKeys[2] = ASCI_ESC; } switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_ESC: rc = FALSE; Done = TRUE; TryOpen = FALSE; break; case KEY_F3: TryOpen = FALSE; SpConfirmExit(); break; case ASCI_CR: break; } } // // Attempt to open the tagfile. // if(TryOpen) { // // If this function was called during repair, do not clear the scree. // This condition is necessary so that the screen will not // blink when setup is repairing multiple files without asking the // user to confirm each file. // if( !RepairWinnt ) { CLEAR_CLIENT_SCREEN(); } SpDisplayStatusText(SP_STAT_PLEASE_WAIT,DEFAULT_STATUS_ATTRIBUTE); // // If we're looking for a cdrom0, and there are multiple CDROM // drives in the machine, check all of them. // if( (PromptId == SP_SCRN_CDROM_PROMPT) && (IoGetConfigurationInformation()->CdRomCount > 1) && (wcsstr( OpenPath, L"cdrom0" ))) { WCHAR CdRomDevicePath[MAX_PATH]; ULONG i; // // We're looking for a CD. We've assumed we're looking for // Cdrom0, but there are more than one on the system. // for( i = 0; i < IoGetConfigurationInformation()->CdRomCount; i++ ) { // // Modify our path, taking into account our new device. Let's // leave OpenPath alone. Just in case we fail, we won't have to // re-initialize him. // swprintf(CdRomDevicePath, L"\\device\\cdrom%u", i); if(DiskTagFile) SpConcatenatePaths(CdRomDevicePath, DiskTagFile); // // Initialize object attributes. // INIT_OBJA(&ObjectAttributes,&UnicodeString,CdRomDevicePath); Status = ZwCreateFile( &Handle, FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0 ); if(NT_SUCCESS(Status)) { if( i > 0 ) { // // We found the tagfile on a different device than // than where we were supposed to look. Modify the // DiskDevicePath. // swprintf(DiskDevicePath, L"\\device\\cdrom%u", i); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP:SpPromptForDisk: %ws has the requested %ws file.\n", DiskDevicePath, DiskTagFile)); } ZwClose(Handle); return( TRUE ); } } // // If we missed, we can fall through without any harm and use // the prompt/error code below. But first, cover our tracks. // INIT_OBJA(&ObjectAttributes, &UnicodeString, OpenPath); } Status = ZwCreateFile( &Handle, FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0 ); // // If we got back success, then we're done. // if(NT_SUCCESS(Status)) { ZwClose(Handle); Done = TRUE; rc = TRUE; } else { // // Handle CD-ROM error code indicating that there is no media // in the drive. // if((Status == STATUS_DEVICE_NOT_READY) && (PromptId == SP_SCRN_CDROM_PROMPT)) { Status = STATUS_NO_MEDIA_IN_DEVICE; } // // If we got back something other than file not found, path not found, // or no media in drive, tell the user that the disk may be damaged. // if((Status != STATUS_NO_MEDIA_IN_DEVICE) && (Status != STATUS_OBJECT_NAME_NOT_FOUND) && (Status != STATUS_OBJECT_PATH_NOT_FOUND) && (Status != STATUS_NO_SUCH_FILE)) { SpDisplayScreen(SP_SCRN_DISK_DAMAGED,3,HEADER_HEIGHT+1); SpDisplayStatusText(SP_STAT_ENTER_EQUALS_CONTINUE,DEFAULT_STATUS_ATTRIBUTE); SpInputDrain(); while(SpInputGetKeypress() != ASCI_CR) ; } } } // // Set this value to true to force us to put up the prompt. // IgnoreDiskInDrive = TRUE; } while(!Done); return(rc); } VOID SpGetSourceMediaInfo( IN PVOID SifHandle, IN PWSTR MediaShortName, OUT PWSTR *Description, OPTIONAL OUT PWSTR *Tagfile, OPTIONAL OUT PWSTR *Directory OPTIONAL ) { PWSTR description,tagfile,directory; PWSTR SectionName; // // Look in the platform-specific section first. // SectionName = SpMakePlatformSpecificSectionName(SIF_SETUPMEDIA); if(SectionName && !SpGetSectionKeyExists(SifHandle,SectionName,MediaShortName)) { SpMemFree(SectionName); SectionName = SIF_SETUPMEDIA; } if(Description) { description = SpGetSectionKeyIndex( SifHandle, SectionName, MediaShortName, 0 ); if(description) { *Description = description; } else { SpFatalSifError(SifHandle,SectionName,MediaShortName,0,0); } } if(Tagfile) { tagfile = SpGetSectionKeyIndex( SifHandle, SectionName, MediaShortName, 1 ); if(tagfile) { *Tagfile = tagfile; } else { SpFatalSifError(SifHandle,SectionName,MediaShortName,0,1); } } if(Directory) { if (NoLs && !_wcsicmp (MediaShortName, L"1")) { directory = L""; } else { directory = SpGetSectionKeyIndex( SifHandle, SectionName, MediaShortName, 3 ); } if(directory) { *Directory = directory; } else { SpFatalSifError(SifHandle,SectionName,MediaShortName,0,3); } } if(SectionName != SIF_SETUPMEDIA) { SpMemFree(SectionName); } } BOOLEAN SpPromptForSetupMedia( IN PVOID SifHandle, IN PWSTR MediaShortname, IN PWSTR DiskDevicePath ) { PWSTR Tagfile,Description; BOOLEAN RedrawNeeded; SpGetSourceMediaInfo(SifHandle,MediaShortname,&Description,&Tagfile,NULL); // // Prompt for the disk, based on the setup media type. // SpPromptForDisk( Description, DiskDevicePath, Tagfile, FALSE, // don't ignore disk in drive FALSE, // don't allow escape TRUE, // warn about multiple prompts for same disk &RedrawNeeded ); return(RedrawNeeded); } ULONG SpFindStringInTable( IN PWSTR *StringTable, IN PWSTR StringToFind ) { ULONG i; for(i=0; StringTable[i]; i++) { if(!_wcsicmp(StringTable[i],StringToFind)) { break; } } return(i); } PWSTR SpGenerateCompressedName( IN PWSTR Filename ) /*++ Routine Description: Given a filename, generate the compressed form of the name. The compressed form is generated as follows: Look backwards for a dot. If there is no dot, append "._" to the name. If there is a dot followed by 0, 1, or 2 charcaters, append "_". Otherwise assume there is a 3-character extension and replace the third character after the dot with "_". Arguments: Filename - supplies filename whose compressed form is desired. Return Value: Pointer to buffer containing nul-terminated compressed-form filename. The caller must free this buffer via SpFree(). --*/ { PWSTR CompressedName,p,q; // // The maximum length of the compressed filename is the length of the // original name plus 2 (for ._). // CompressedName = SpMemAlloc((wcslen(Filename)+3)*sizeof(WCHAR)); wcscpy(CompressedName,Filename); p = wcsrchr(CompressedName,L'.'); q = wcsrchr(CompressedName,L'\\'); if(q < p) { // // If there are 0, 1, or 2 characters after the dot, just append // the underscore. p points to the dot so include that in the length. // if(wcslen(p) < 4) { wcscat(CompressedName,L"_"); } else { // // Assume there are 3 characters in the extension. So replace // the final one with an underscore. // p[3] = L'_'; } } else { // // No dot, just add ._. // wcscat(CompressedName,L"._"); } return(CompressedName); } BOOLEAN SpNonCriticalError( IN PVOID SifHandle, IN ULONG MsgId, IN PWSTR p1, OPTIONAL IN PWSTR p2 OPTIONAL ) /*++ Routine Description: This routine lets Setup display a non critical error to the user and ask the user whether he wants to retry the operation, skip the operation or exit Setup. Arguments: SifHandle - supplies handle to loaded setup information file. MsgId - message to display p1 - optional replacement string p2 - optional replacement string Return Value: TRUE if user wants to retry the operation, FALSE otherwise. Exit Setup won't return from this routine --*/ { ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 }; CLEAR_CLIENT_SCREEN(); while(1) { if(p1!=NULL && p2!=NULL ) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1, p2 ); } else if (p1!=NULL) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1 ); } else{ SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE ); } SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_RETRY, SP_STAT_ESC_EQUALS_SKIP_OPERATION, SP_STAT_F3_EQUALS_EXIT, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: // retry return(TRUE); case ASCI_ESC: // skip operation return(FALSE); case KEY_F3: // exit setup SpConfirmExit(); break; } } } BOOLEAN SpNonCriticalErrorWithContinue ( IN ULONG MsgId, IN PWSTR p1, OPTIONAL IN PWSTR p2 OPTIONAL ) /*++ Routine Description: This routine lets Setup display a non critical error to the user and ask the user whether he wants to ignore the failure, skip the operation or exit Setup. Arguments: MsgId - message to display p1 - optional replacement string p2 - optional replacement string Return Value: TRUE if user wants to ignore the failure, FALSE otherwise. Exit Setup won't return from this routine --*/ { ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 }; CLEAR_CLIENT_SCREEN(); while(1) { if(p1!=NULL && p2!=NULL ) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1, p2 ); } else if (p1!=NULL) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1 ); } else{ SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE ); } SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_ESC_EQUALS_SKIP_OPERATION, SP_STAT_F3_EQUALS_EXIT, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: // ignore failure return(TRUE); case ASCI_ESC: // skip operation return(FALSE); case KEY_F3: // exit setup SpConfirmExit(); break; } } } VOID SpNonCriticalErrorNoRetry ( IN ULONG MsgId, IN PWSTR p1, OPTIONAL IN PWSTR p2 OPTIONAL ) /*++ Routine Description: This routine lets Setup display a non critical error to the user and ask the user whether he wants to continue exit Setup. Arguments: MsgId - message to display p1 - optional replacement string p2 - optional replacement string Return Value: None. --*/ { ULONG ValidKeys[3] = { ASCI_CR, KEY_F3, 0 }; CLEAR_CLIENT_SCREEN(); while(1) { if(p1!=NULL && p2!=NULL ) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1, p2 ); } else if (p1!=NULL) { SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, p1 ); } else{ SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE ); } SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_F3_EQUALS_EXIT, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: // continue return; case KEY_F3: // exit setup SpConfirmExit(); break; } } } PWSTR SpDetermineSystemPartitionDirectory( IN PDISK_REGION SystemPartitionRegion, IN PWSTR OriginalSystemPartitionDirectory OPTIONAL ) /*++ Routine Description: This routine figures out what directory to use for the hal and osloader on the system partition. In the past we just used \os\nt but consider the case where there is a Windows NT 3.1 installation and a Windows NT 3.5 system sharing a system partition. The 3.5 installation overwrites the 3.1 hal with a 3.5 one, which won't work with 3.1, and the 3.1 system is now hosed. For now, we will use the existing directory (in the case of an upgrade), or \os\winnt50.n (where 'n' is a unique digit from 0 to 999) for a fresh install. Arguments: SystemPartitionRegion - supplies the disk region for the system partition to be used for the windows nt we are installing. OriginalSystemPartitionDirectory - if we are upgrading nt, then this will be the directory on the system partition that is used by the system we are upgrading. Return Value: Directory to be used on the system partition. --*/ { WCHAR ReturnPath[512]; #if defined(EFI_NVRAM_ENABLED) #define OS_DIRECTORY_PREFIX L"\\EFI\\Microsoft\\WINNT50" #else #define OS_DIRECTORY_PREFIX L"\\OS\\WINNT50" #endif if(ARGUMENT_PRESENT(OriginalSystemPartitionDirectory)) { // // Note that we're about to break an install under // certain conditions. For example, say the user has // two NT4 installs, both sharing the same \os\winnt40 // directory. Now the user has decided to upgrade one // of those. We're about to upgrade the hal, osloader, ... // in that winnt40 directory, which will break the // users secondary install that's sharing this directory. // This should be a rare case though, and this is // exactly how we behaved in NT40 and NT3.51. // wcscpy( ReturnPath, OriginalSystemPartitionDirectory ); } else { // // We want to return os\winnt50, but we also want // to make sure that whatever directory we select, it's // unique (since this is a clean install). Note that // this allows the user to have multiple NT installs, // with no shared files (which fixes the upgrade problem // described above. // if( !SpGenerateNTPathName( SystemPartitionRegion, #if DBG OS_DIRECTORY_PREFIX L"C", // C - for Checked #else OS_DIRECTORY_PREFIX, #endif ReturnPath ) ) { // // Odd... Just default to using // the base directory name. // wcscpy( ReturnPath, #if DBG OS_DIRECTORY_PREFIX L"C" // C - for Checked #else OS_DIRECTORY_PREFIX #endif ); } } KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpDetermineSystemPartitionDirectory - Generated directory name: %ws\n", ReturnPath )); return SpDupStringW( ReturnPath ); } VOID SpFindSizeOfFilesInOsWinnt( IN PVOID MasterSifHandle, IN PDISK_REGION SystemPartition, IN PULONG TotalSize ) /*++ Routine Description: This routine computes the size of of the files present on os\winnt. Currently these files are osloader.exe and hal.dll. The size computed by this function can be used to adjust the total required free space on the system partition. Arguments: Region - supplies the disk region for the system partition. TotalSize - Variable that will contain the total size of the files in os\winnt, in number of bytes. Return Value: None. --*/ { ULONG FileSize; ULONG i, Count; PWSTR FileName; NTSTATUS Status; PWSTR SystemPartitionDirectory; PWSTR SystemPartitionDevice; *TotalSize = 0; SystemPartitionDirectory = SpDetermineSystemPartitionDirectory( SystemPartition, NULL ); if( SystemPartitionDirectory == NULL ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to determine system partition directory \n")); return; } // // Get the device path of the system partition. // SpNtNameFromRegion( SystemPartition, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); SystemPartitionDevice = SpDupStringW(TemporaryBuffer); // // Compute the size of the files that are always copied to the system // partition directory. These files are listed on SIF_SYSPARTCOPYALWAYS // Count = SpCountLinesInSection(MasterSifHandle, SIF_SYSPARTCOPYALWAYS); for (i = 0; i < Count; i++) { FileName = SpGetSectionLineIndex(MasterSifHandle,SIF_SYSPARTCOPYALWAYS,i,0); if( FileName == NULL ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to get file name from txtsetup.sif, Section = %ls \n", SIF_SYSPARTCOPYALWAYS )); continue; } Status = SpGetFileSizeByName( SystemPartitionDevice, SystemPartitionDirectory, FileName, &FileSize ); if( !NT_SUCCESS( Status ) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpGetFileSizeByName() failed. File = %ls, Status = %x\n",FileName, Status ) ); continue; } *TotalSize += FileSize; } // // Now compute the size of hal.dll // FileName = L"hal.dll"; Status = SpGetFileSizeByName( SystemPartitionDevice, SystemPartitionDirectory, FileName, &FileSize ); if( !NT_SUCCESS( Status ) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpGetFileSizeByName() failed. File = %ls, Status = %x\n",FileName, Status ) ); return; } *TotalSize += FileSize; } ENUMFILESRESULT SpEnumFiles( IN PCWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) /*++ Routine Description: This routine processes every file (and subdirectory) in the directory specified by 'DirName'. Each entry is sent to the callback function 'EnumFilesProc' for processing. If the callback returns TRUE, processing continues, otherwise processing terminates. Arguments: DirName - Supplies the directory name containing the files/subdirectories to be processed. EnumFilesProc - Callback function to be called for each file/subdirectory. The function must have the following prototype: BOOLEAN EnumFilesProc( IN PWSTR, IN PFILE_BOTH_DIR_INFORMATION, OUT PULONG ); ReturnData - Pointer to the returned data. The contents stored here depend on the reason for termination (See below). p1 - Optional pointer, to be passed to the callback function. Return Value: This function can return one of three values. The data stored in 'ReturnData' depends upon which value is returned: NormalReturn - if the whole process completes uninterrupted (ReturnData is not used) EnumFileError - if an error occurs while enumerating files (ReturnData contains the error code) CallbackReturn - if the callback returns FALSE, causing termination (ReturnData contains data defined by the callback) --*/ { HANDLE hFindFile; NTSTATUS Status; UNICODE_STRING PathName; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; BOOLEAN bStartScan; ENUMFILESRESULT ret; // // Prepare to open the directory // INIT_OBJA(&Obja, &PathName, DirName); // // Open the specified directory for list access // Status = ZwOpenFile( &hFindFile, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if(!NT_SUCCESS(Status)) { if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open directory %ws for list (%lx)\n", DirName, Status)); } *ReturnData = Status; return EnumFileError; } DirectoryInfo = SpMemAlloc(ACTUAL_MAX_PATH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION)); if(!DirectoryInfo) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Unable to allocate memory for SpEnumFiles()\n")); *ReturnData = STATUS_NO_MEMORY; return EnumFileError; } bStartScan = TRUE; while(TRUE) { Status = ZwQueryDirectoryFile( hFindFile, NULL, NULL, NULL, &IoStatusBlock, DirectoryInfo, (ACTUAL_MAX_PATH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION)), FileBothDirectoryInformation, TRUE, NULL, bStartScan ); if(Status == STATUS_NO_MORE_FILES) { ret = NormalReturn; break; } else if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Unable to query directory %ws (%lx)\n", DirName, Status)); *ReturnData = Status; ret = EnumFileError; break; } if(bStartScan) { bStartScan = FALSE; } // // Now pass this entry off to our callback function for processing // if(!EnumFilesProc(DirName, DirectoryInfo, ReturnData, p1)) { ret = CallbackReturn; break; } } SpMemFree(DirectoryInfo); ZwClose(hFindFile); return ret; } /*typedef struct { PVOID OptionalPtr; ENUMFILESPROC EnumProc; } RECURSION_DATA, *PRECURSION_DATA; BOOLEAN SppRecursiveEnumProc ( IN PCWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Param ) { PWSTR FullPath; PWSTR temp; ULONG Len; NTSTATUS Status; ULONG ReturnData; ENUMFILESRESULT EnumResult; BOOLEAN b = FALSE; PRECURSION_DATA RecursionData; RecursionData = (PRECURSION_DATA) Param; // // Build the full file or dir path // temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); Len = FileInfo->FileNameLength/sizeof(WCHAR); wcsncpy(temp,FileInfo->FileName,Len); temp[Len] = 0; wcscpy(TemporaryBuffer,DirName); SpConcatenatePaths(TemporaryBuffer,temp); FullPath = SpDupStringW(TemporaryBuffer); // // For directories, recurse // if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if( (wcscmp( temp, L"." ) == 0) || (wcscmp( temp, L".." ) == 0) ) { // // Skip past . and .. directories // b = TRUE; } else { // // Recurse through subdirectory // EnumResult = SpEnumFilesRecursive ( FullPath, RecursionData->EnumProc, &ReturnData, RecursionData->OptionalPtr ); if (EnumResult != NormalReturn) { *ret = EnumResult; return FALSE; } } } // // Call normal enum proc for file or dir (except . or .. dirs) // if (!b) { b = RecursionData->EnumProc ( DirName, FileInfo, ret, RecursionData->OptionalPtr ); } SpMemFree (FullPath); return b; }*/ /*BOOLEAN SppRecursiveEnumProcDel ( IN PCWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Param ) {*/ /* This function is the same as above except that it checks for reparse points. The reason we have 2 seperate functions rather than one function and an extra parameter is so that we don't have the Reparse point check overhead for other recursive processing like copying file. Given the no. of files this could be overhead. Also this way we don't hack the recursive directory search algo as well as reduce stack overhead in a recursive operation. */ /* HANDLE hFixed; NTSTATUS Status; UNICODE_STRING PathName; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; FILE_FS_DEVICE_INFORMATION DeviceInfo; PWSTR FullPath; PWSTR temp; ULONG Len; ULONG ReturnData; ENUMFILESRESULT EnumResult; BOOLEAN b = FALSE; BOOLEAN IsLink = FALSE; PRECURSION_DATA RecursionData; RecursionData = (PRECURSION_DATA) Param; // // Build the full file or dir path // temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); Len = FileInfo->FileNameLength/sizeof(WCHAR); wcsncpy(temp,FileInfo->FileName,Len); temp[Len] = 0; wcscpy(TemporaryBuffer,DirName); SpConcatenatePaths(TemporaryBuffer,temp); FullPath = SpDupStringW(TemporaryBuffer); // // For directories, recurse // if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if( (wcscmp( temp, L"." ) == 0) || (wcscmp( temp, L".." ) == 0) ) { // // Skip past . and .. directories // b = TRUE; } else { // // Recurse through subdirectory // // // Look for mount point and delete right away to avoid cycle complications // if( FileInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) IsLink = TRUE; if( !IsLink ){ EnumResult = SpEnumFilesRecursiveDel ( FullPath, RecursionData->EnumProc, &ReturnData, RecursionData->OptionalPtr ); if (EnumResult != NormalReturn) { *ret = EnumResult; return FALSE; } } } } // // Call normal enum proc for file or dir (except . or .. dirs) // if (!b) { b = RecursionData->EnumProc ( DirName, FileInfo, ret, RecursionData->OptionalPtr ); } SpMemFree (FullPath); return b; }*/ #define LONGEST_NT_PATH_LENGTH 512 // RtlGetLongestNtPathLength always return just 277(MAX_PATH+UNC_PREFIX_LENGTH) // longest NT path is 32000 character. #define MAX_DEPTH -1 typedef struct { HANDLE hHandle; int Index; PFILE_BOTH_DIR_INFORMATION FileInfo; }ENUM_LEVEL, *PENUM_LEVEL; BOOLEAN SpEnumFilesInline( IN PCWSTR pPath, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL, IN BOOLEAN bExcludeRepasePointDirs OPTIONAL, IN LONG DirectoriesMaxDepth, IN BOOLEAN bEnumerateDirFirst OPTIONAL ) { PENUM_LEVEL level = NULL; int MaxLevelNumber = 0; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION FileInfo = NULL; int SizeOfFileInfo; NTSTATUS Status; PWSTR Path = NULL; PWSTR SubDir = NULL; int index; int i; BOOLEAN FirstQuery; ENUMFILESRESULT enumResult = NormalReturn; if(!pPath || wcslen(pPath) >= LONGEST_NT_PATH_LENGTH){ return EnumFileError; } __try{ Path = (PWSTR)SpMemAlloc(LONGEST_NT_PATH_LENGTH * sizeof(WCHAR)); if(!Path){ if(ReturnData){ *ReturnData = STATUS_NO_MEMORY; } enumResult = EnumFileError; __leave; } SubDir = (PWSTR)SpMemAlloc(LONGEST_NT_PATH_LENGTH * sizeof(WCHAR)); if(!SubDir){ if(ReturnData){ *ReturnData = STATUS_NO_MEMORY; } enumResult = EnumFileError; __leave; } SizeOfFileInfo = LONGEST_NT_PATH_LENGTH * sizeof(WCHAR) + sizeof(FILE_BOTH_DIR_INFORMATION); FileInfo = (PFILE_BOTH_DIR_INFORMATION)SpMemAlloc(SizeOfFileInfo); if(!FileInfo){ if(ReturnData){ *ReturnData = STATUS_NO_MEMORY; } enumResult = EnumFileError; __leave; } MaxLevelNumber = LONGEST_NT_PATH_LENGTH / 2; level = (PENUM_LEVEL)SpMemAlloc(sizeof(level[0]) * MaxLevelNumber); if(!level){ if(ReturnData){ *ReturnData = STATUS_NO_MEMORY; } enumResult = EnumFileError; __leave; } memset(level, 0, sizeof(level[0]) * MaxLevelNumber); wcscpy(Path, pPath); index = wcslen(Path) - 1; if('\\' != Path[index] && '//' != Path[index]){ Path[index + 1] = '\\'; Path[index + 2] = '\0'; } for(index = 0; index >= 0;){ INIT_OBJA(&ObjectAttributes, &UnicodeString, Path); level[index].Index = wcslen(Path); if(!bEnumerateDirFirst){ level[index].FileInfo = (PFILE_BOTH_DIR_INFORMATION)SpMemAlloc(SizeOfFileInfo); if(!level[index].FileInfo){ if(ReturnData){ *ReturnData = STATUS_NO_MEMORY; } enumResult = EnumFileError; __leave; } } Status = ZwOpenFile(&level[index].hHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if(!NT_SUCCESS(Status)){ level[index].hHandle = NULL; if(ReturnData){ *ReturnData = Status; } enumResult = EnumFileError; if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpEnumFilesInline, Failed to open %ws folder for list access - status 0x%08X.\n", Path, Status)); } __leave;//index--; } else{ FirstQuery = TRUE; } for(;;) { for(; index >= 0; index--){ Status = ZwQueryDirectoryFile(level[index].hHandle, NULL, // no event to signal NULL, // no apc routine NULL, // no apc context &IoStatusBlock, FileInfo, SizeOfFileInfo - sizeof(WCHAR), // leave room for terminating nul FileBothDirectoryInformation, TRUE, // want single entry NULL, // get 'em all FirstQuery); FirstQuery = FALSE; if(NT_SUCCESS(Status)){ break; } else{ if(STATUS_NO_MORE_FILES != Status){ if(ReturnData){ *ReturnData = Status; } KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpEnumFilesInline, Failed to query %d level - status 0x%08X.\n", index, Status)); enumResult = EnumFileError; __leave; } else{ if(!bEnumerateDirFirst){ if(index > 0){ wcsncpy(SubDir, Path, level[index - 1].Index); SubDir[level[index - 1].Index] = '\0'; if(!EnumFilesProc(SubDir, level[index - 1].FileInfo, ReturnData, p1)){ enumResult = CallbackReturn; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n", SubDir, level[index - 1].FileInfo->FileName)); __leave; } } } } } ZwClose(level[index].hHandle); level[index].hHandle = NULL; } if(index < 0){ break; } FileInfo->FileName[FileInfo->FileNameLength / sizeof(WCHAR)] = '\0'; wcscpy(&Path[level[index].Index], FileInfo->FileName); wcsncpy(SubDir, Path, level[index].Index); SubDir[level[index].Index] = '\0'; if(!(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)){ if(!EnumFilesProc(SubDir, FileInfo, ReturnData, p1)){ enumResult = CallbackReturn; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n", SubDir, FileInfo->FileName)); __leave; } } else{ if(wcscmp(FileInfo->FileName, L".") && wcscmp(FileInfo->FileName, L"..")){ wcscat(Path, L"\\"); if(bEnumerateDirFirst){ if(!EnumFilesProc(SubDir, FileInfo, ReturnData, p1)){ enumResult = CallbackReturn; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP:SpEnumFilesInline, Callback returned FALSE on %ws\\%ws\n", SubDir, FileInfo->FileName)); __leave; } } else{ ASSERT(level[index].FileInfo); memcpy(level[index].FileInfo, FileInfo, SizeOfFileInfo); } if(DirectoriesMaxDepth >= 0 && index >= DirectoriesMaxDepth){ continue; } if(bExcludeRepasePointDirs && FileInfo->FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT){ continue; } index++; break; } } } } enumResult = NormalReturn; } __finally{ if(level){ for(i = 0; i < MaxLevelNumber; i++){ if(level[i].hHandle){ ZwClose(level[i].hHandle); } if(level[i].FileInfo){ SpMemFree(level[i].FileInfo); } } SpMemFree(level); } if(SubDir){ SpMemFree(SubDir); } if(Path){ SpMemFree(Path); } if(FileInfo){ SpMemFree(FileInfo); } } return enumResult; } ENUMFILESRESULT SpEnumFilesRecursive ( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) { return SpEnumFilesInline(DirName, EnumFilesProc, ReturnData, p1, FALSE, MAX_DEPTH, FALSE); /* RECURSION_DATA RecursionData; RecursionData.OptionalPtr = p1; RecursionData.EnumProc = EnumFilesProc; return SpEnumFiles ( DirName, SppRecursiveEnumProc, ReturnData, &RecursionData ); */ } /*typedef struct { ULONG MaxDepth; ULONG CurrentDepth; PVOID OptionalPtr; ENUMFILESPROC EnumProc; } RECURSION_LIMITED_DATA, *PRECURSION_LIMITED_DATA; BOOLEAN SppRecursiveLimitedEnumProc ( IN PCWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Param )*/ /*++ Routine Description: This routine is the same as SppRecursiveEnumProc with the added feature that it supports recursion depth limiting. The recursion context is passed in via the Param argument and is of type RECURSION_LIMITED_DATA. Arguments: DirName - Supplies the directory name containing the current directory of the File/Dir currently being enumerated. FileInfo - File/Dir info about the current file being enumerated ret - Pointer to the returned data. The contents stored here depend on the reason for termination: NormalReturn - if the whole process completes uninterrupted (ReturnData is not used) EnumFileError - if an error occurs while enumerating files (ReturnData contains the error code) CallbackReturn - if the callback returns FALSE, causing termination (ReturnData contains data defined by the callback) Param - Recursion context Return Value: TRUE - continue processing otherwise, FALSE --*/ /*{ PWSTR FullPath; PWSTR temp; ULONG Len; NTSTATUS Status; ULONG ReturnData; ENUMFILESRESULT EnumResult; BOOLEAN b = FALSE; PRECURSION_LIMITED_DATA RecursionData; RecursionData = (PRECURSION_LIMITED_DATA) Param; // // If we are at our max recursion depth, bail out // // Note: using >= allows us to look at files at the MaxDepth, // but not recurse into directories beyond MaxDepth. // if (RecursionData->CurrentDepth >= RecursionData->MaxDepth) { *ret = NormalReturn; return TRUE; } // // Build the full file or dir path // temp = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); Len = FileInfo->FileNameLength/sizeof(WCHAR); wcsncpy(temp,FileInfo->FileName,Len); temp[Len] = 0; wcscpy(TemporaryBuffer,DirName); SpConcatenatePaths(TemporaryBuffer,temp); FullPath = SpDupStringW(TemporaryBuffer); // // if the length of FullPath >= MAX_PATH, then we might // have encountered a corrupt region of the file system. // Hence, ensure that the length of FullPath is < MAX_PATH-1. // (allow for null termination when comparing to MAX_PATH) // if (wcslen(FullPath) >= MAX_PATH) { SpMemFree(FullPath); // // skip this entry and continue scanning // // (Since this routine is used by Bootcfg in the recover console, // this behavior is helpful because it allows us to continue scanning // and perhaps find a valid Windows install - which would then allow // us to possibly do more recovery work...) // *ret = NormalReturn; return TRUE; } // // For directories, recurse // if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if( (wcscmp( temp, L"." ) == 0) || (wcscmp( temp, L".." ) == 0) ) { // // Skip past . and .. directories // b = TRUE; } else { // // Recurse through subdirectory // RecursionData->CurrentDepth++; EnumResult = SpEnumFilesRecursiveLimited ( FullPath, RecursionData->EnumProc, RecursionData->MaxDepth, RecursionData->CurrentDepth, &ReturnData, RecursionData->OptionalPtr ); RecursionData->CurrentDepth--; if (EnumResult != NormalReturn) { *ret = EnumResult; return FALSE; } } } // // Call normal enum proc for file or dir (except . or .. dirs) // if (!b) { b = RecursionData->EnumProc ( DirName, FileInfo, ret, RecursionData->OptionalPtr ); } SpMemFree (FullPath); return b; }*/ ENUMFILESRESULT SpEnumFilesRecursiveLimited ( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, IN ULONG MaxDepth, IN ULONG CurrentDepth, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) /*++ Routine Description: This routine processes every file (and subdirectory) in the directory specified by 'DirName'. Each entry is sent to the callback function 'EnumFilesProc' for processing. If the callback returns TRUE, processing continues, otherwise processing terminates. This routine employs recursion depth limiting. Arguments: DirName - Supplies the directory name containing the files/subdirectories to be processed. EnumFilesProc - Callback function to be called for each file/subdirectory. The function must have the following prototype: BOOLEAN EnumFilesProc( IN PWSTR, IN PFILE_BOTH_DIR_INFORMATION, OUT PULONG ); MaxDepth - The maximum depth the recursion will be allowed to go. Note: During the recursion process, the directories will be recursed until CurrentDepth == MaxDepth. Files at MaxDepth + 1 will be processed via EnumProc, but any directories below MaxDepth will not be visited. CurrentDepth - The depth the recursion is currently at. Note: When first calling this routine, CurrentDepth should be 0. This argument exists because this routine is the core of the recursion and is called by SppRecursiveLimitedEnumProc. Each time SppRecursiveLimitedEnumProc calls this function, it passes the current recursion depth. ReturnData - Pointer to the returned data. The contents stored here depend on the reason for termination (See below). p1 - Optional pointer, to be passed to the callback function. Return Value: This function can return one of three values. The data stored in 'ReturnData' depends upon which value is returned: NormalReturn - if the whole process completes uninterrupted (ReturnData is not used) EnumFileError - if an error occurs while enumerating files (ReturnData contains the error code) CallbackReturn - if the callback returns FALSE, causing termination (ReturnData contains data defined by the callback) --*/ { /* RECURSION_LIMITED_DATA RecursionData; RecursionData.OptionalPtr = p1; RecursionData.EnumProc = EnumFilesProc; RecursionData.MaxDepth = MaxDepth; RecursionData.CurrentDepth = CurrentDepth; return SpEnumFiles ( DirName, SppRecursiveLimitedEnumProc, ReturnData, &RecursionData );*/ return SpEnumFilesInline(DirName, EnumFilesProc, ReturnData, p1, FALSE, MaxDepth, FALSE); } ENUMFILESRESULT SpEnumFilesRecursiveDel ( IN PWSTR DirName, IN ENUMFILESPROC EnumFilesProc, OUT PULONG ReturnData, IN PVOID p1 OPTIONAL ) // // This function is the same as SpEnumFilesRecursive except that // it handles reparse points too and avoids name cycles and calls // SppRecursiveEnumProcDel instead // { return SpEnumFilesInline(DirName, EnumFilesProc, ReturnData, p1, TRUE, MAX_DEPTH, FALSE); /* RECURSION_DATA RecursionData; RecursionData.OptionalPtr = p1; RecursionData.EnumProc = EnumFilesProc; return SpEnumFiles ( DirName, SppRecursiveEnumProcDel, ReturnData, &RecursionData );*/ } VOID SpFatalKbdError( IN ULONG MessageId, ... ) /*++ Routine Description: Inform the user that a keyboard problem (specified by MessageId) prevents setup from continuing. Since we can't prompt the user to press a key to reboot, we just go into an infinite loop until they power-cycle the computer. In the headless case, we will restart after a key is pressed remotely. Arguments: MessageId - Message ID for keyboard error message to display ... - Supply arguments for insertion/substitution into the message text. Return Value: DOES NOT RETURN --*/ { va_list arglist; // // Display a message indicating that a keyboard // error prevents Setup from continuing. // CLEAR_CLIENT_SCREEN(); va_start(arglist, MessageId); vSpDisplayFormattedMessage( MessageId, FALSE, FALSE, DEFAULT_ATTRIBUTE, 3, HEADER_HEIGHT+3, arglist ); va_end(arglist); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_KBD_HARD_REBOOT, 0); // // attempt to read from terminal. // if terminal is not connected, // loop forever // SpTermDrain(); while(!SpTermGetKeypress()); SpDone( 0, FALSE, FALSE ); } VOID SpFatalError( IN ULONG MessageId, ... ) /*++ Routine Description: Inform the user of a blocking problem. Then reboot. Arguments: MessageId - Message ID for keyboard error message to display ... - Supply arguments for insertion/substitution into the message text. Return Value: DOES NOT RETURN --*/ { va_list arglist; CLEAR_CLIENT_SCREEN(); va_start(arglist, MessageId); vSpDisplayFormattedMessage( MessageId, FALSE, FALSE, DEFAULT_ATTRIBUTE, 3, HEADER_HEIGHT+3, arglist ); va_end(arglist); SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_REBOOT, 0); SpInputDrain(); while( SpInputGetKeypress() != KEY_F3 ); SpDone( 0, FALSE, TRUE ); } VOID SpRunAutochkOnNtAndSystemPartitions( IN HANDLE MasterSifHandle, IN PDISK_REGION WinntPartitionRegion, IN PDISK_REGION SystemPartitionRegion, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR TargetPath ) /*++ Routine Description: Run autochk on the NT and System partitions. We always invoke autochk.exe for both the winnt and system partitions. However under some conditions we pass flags that cause it to run only if the dirty bit is set. Running only when the dirty bit is set is referred to below as a "light check" wheras running regardless of the state of the dirty bit is the "heavy check." If this is repair, run the heavy check in all cases on both partitions. If this is express setup or unattended operation, run light check on ntfs partitions and heavy check on fat ones. Otherwise (attended custom setup), ask the user. Arguments: MasterSifHandle - Handle to txtsetup.sif. WinntPartitionRegion - Pointer to the structure that describes the NT partition. SystemPartitionRegion - Pointer to the structure that describes the system partition. SetupSourceDevicePath - NT device path where autochk.exe is located DirectoryOnSourceDevice - Directory on that device where autochk.exe is located Return Value: None. --*/ { PWSTR MediaShortName; PWSTR MediaDirectory; PWSTR AutochkPath; ULONG AutochkStatus; WCHAR DriveLetterString[3] = L"?:"; NTSTATUS Status; ULONG ValidKeys[3] = { ASCI_CR, ASCI_ESC, 0 }; PWSTR WinntPartition, SystemPartition; ULONG WinntPartIndex, SystemPartIndex, i; PWSTR AutochkPartition[2]; PWSTR AutochkType[2]; LARGE_INTEGER DelayTime; PWSTR HeavyCheck = L"-t -p"; // -t causes autochk to send messages (like % complete) PWSTR LightCheck = L"-t"; // to the setup driver BOOLEAN RunAutochkForRepair; BOOLEAN MultiplePartitions = TRUE, RebootRequired = FALSE; ULONG InputChar; // // We first need to determine if either the system partition // or winnt partition also contains the directory from which // autochk is being run. If so, then we want to run autochk on that // partition last. This is done so that no further access to // that partition will be necessary should a reboot be required. // // First, get the device path of the nt partition and system partition. // #if defined(REMOTE_BOOT) // Note that during a remote boot setup, there will be no winnt partition, // and if the machine is diskless there will be no system partition. // #endif // defined(REMOTE_BOOT) if (WinntPartitionRegion != NULL) { SpNtNameFromRegion( WinntPartitionRegion, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); WinntPartition = SpDupStringW(TemporaryBuffer); } else { WinntPartition = NULL; } if (SystemPartitionRegion != NULL) { SpNtNameFromRegion( SystemPartitionRegion, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); SystemPartition = SpDupStringW(TemporaryBuffer); } else { SystemPartition = NULL; } // // Skip autocheck if not partitions names could // be formed // if (!WinntPartition && !SystemPartition) { return; } #if defined(REMOTE_BOOT) if (!RemoteBootSetup) { #endif // defined(REMOTE_BOOT) if (WinntPartition) { if (SystemPartition && !_wcsicmp(WinntPartition, SystemPartition)) { SystemPartIndex = WinntPartIndex = 0; MultiplePartitions = FALSE; } else if(!_wcsicmp(WinntPartition, SetupSourceDevicePath)) { WinntPartIndex = 1; SystemPartIndex = 0; } else { WinntPartIndex = 0; SystemPartIndex = 1; } } else { WinntPartIndex = 1; SystemPartIndex = 0; } AutochkPartition[WinntPartIndex] = WinntPartition; if(MultiplePartitions) { AutochkPartition[SystemPartIndex] = SystemPartition; } #if defined(REMOTE_BOOT) } else { // // Remote boot system - only check the system partition. // SystemPartIndex = WinntPartIndex = 0; AutochkPartition[SystemPartIndex] = SystemPartition; MultiplePartitions = FALSE; } #endif // defined(REMOTE_BOOT) // // For repair or Disaster Recovery, we run the heavy check in all cases. // @@ mtp // if( RepairWinnt || SpDrEnabled() ) { AutochkType[WinntPartIndex] = HeavyCheck; if(MultiplePartitions) { AutochkType[SystemPartIndex] = HeavyCheck; } } else { #if defined(REMOTE_BOOT) // // On a diskless remote boot system, there will be no system partition. // if (SystemPartitionRegion != NULL) #endif // defined(REMOTE_BOOT) { AutochkType[SystemPartIndex] = (SystemPartitionRegion->Filesystem == FilesystemNtfs) ? LightCheck : HeavyCheck; } // // If MultiplePartitions is FALSE, then the WinntPartition is the same // as the SystemPartition, so we are not going to autochk the WinntPartition. // #if defined(REMOTE_BOOT) // MultiplePartitions will also be FALSE if this is a remote boot system, // in which case the WinntPartition is remote. Again, we are not going // to autochk the WinntPartition. // #endif // defined(REMOTE_BOOT) if (MultiplePartitions) { ASSERT(WinntPartitionRegion != NULL); ASSERT(WinntPartition != NULL); AutochkType[WinntPartIndex] = (WinntPartitionRegion->Filesystem == FilesystemNtfs) ? LightCheck : HeavyCheck; } } CLEAR_CLIENT_SCREEN(); // // Prepare to run autochk // MediaShortName = SpLookUpValueForFile( MasterSifHandle, L"autochk.exe", INDEX_WHICHMEDIA, TRUE ); // // Prompt the user to insert the setup media. If we're repairing, // then we don't want to force the user to have the setup media // (there's certain things they can do without it), so we give them // a slightly different prompt, that allows them to press ESC and // not run autochk. // if (!Win9xRollback) { if(RepairWinnt) { RunAutochkForRepair = SppPromptOptionalAutochk( MasterSifHandle, MediaShortName, SetupSourceDevicePath ); if(!RunAutochkForRepair) { SpMemFree( WinntPartition ); SpMemFree( SystemPartition ); CLEAR_CLIENT_SCREEN(); return; } } else { SpPromptForSetupMedia( MasterSifHandle, MediaShortName, SetupSourceDevicePath ); } SpGetSourceMediaInfo(MasterSifHandle,MediaShortName,NULL,NULL,&MediaDirectory); wcscpy( TemporaryBuffer, SetupSourceDevicePath ); SpConcatenatePaths( TemporaryBuffer, DirectoryOnSourceDevice ); SpConcatenatePaths( TemporaryBuffer, MediaDirectory ); SpConcatenatePaths( TemporaryBuffer, L"autochk.exe" ); AutochkPath = SpDupStringW( TemporaryBuffer ); } else { // // Win9x rollback -- autochk.exe is in $win_nt$.~bt\i386 // wcscpy (TemporaryBuffer, NtBootDevicePath); SpConcatenatePaths (TemporaryBuffer, DirectoryOnBootDevice); SpConcatenatePaths (TemporaryBuffer, L"i386\\autochk.exe"); AutochkPath = SpDupStringW (TemporaryBuffer); } // // Run autochk on the partition(s) // CLEAR_CLIENT_SCREEN(); SpDisplayScreen( SP_SCRN_RUNNING_AUTOCHK, 3, 4 ); // // Create the gauge. // Since we want only one progress bar displayed to the user // while autochk is running, we initialize the range of the // gauge based on the number of partitions to be examined. // If the system and NT partitions are the same, the we set // the range as 100. Otherwise, we set the range at 200. // Note that on the multiple partitions case, 50% of the gauge // will be used to display the progress for each disk. // The IOCTL that calls SpFillGauge(), will have to adjust the // amount of the gauge to be filled, based on the partition that // is currently being examined. // UserModeGauge = SpCreateAndDisplayGauge( (MultiplePartitions)? 200 : 100, 0, 15, L"", NULL, GF_PERCENTAGE, 0 ); // Setup is checking disk(s)... // for(i = 0; i < (ULONG)(MultiplePartitions ? 2 : 1); i++) { // // Display message informing that autocheck is being run // if (AutochkPartition[i] != NULL) { DriveLetterString[0] = (i == WinntPartIndex) ? WinntPartitionRegion->DriveLetter : SystemPartitionRegion->DriveLetter; SpDisplayStatusText( SP_STAT_CHECKING_DRIVE, DEFAULT_STATUS_ATTRIBUTE, DriveLetterString ); if(!i) { // // Cheesy kludge below to wait 4 seconds before invoking autochk.exe // the first time. This was necessary because the cache manager delays // in closing the handle to system.log (opened by NT registry APIs when // we find NT's to upgrade) // DelayTime.HighPart = -1; DelayTime.LowPart = (ULONG)-40000000; KeDelayExecutionThread (KernelMode, FALSE, &DelayTime); } // // Tell the IOCTL which disk is being examined. // CurrentDiskIndex = i; AutochkStatus = 0; Status = SpExecuteImage( AutochkPath, &AutochkStatus, 2, AutochkType[i], AutochkPartition[i] ); if( NT_SUCCESS( Status ) ) { switch(AutochkStatus) { case CHKDSK_EXIT_COULD_NOT_FIX : // // Inform that the partition has an unrecoverable error // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: autochk.exe failed on %ls. ReturnCode = %x \n", AutochkPartition[i], AutochkStatus )); SpStartScreen( SP_SCRN_FATAL_ERROR_AUTOCHK_FAILED, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DriveLetterString ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, 0 ); SpInputDrain(); while( SpInputGetKeypress() != KEY_F3 ); // // The third arg of SpDone is TRUE to provide 15 // seconds before reboot. We don't want this during // an uninstall. // SpDone( 0, FALSE, !Win9xRollback ); case CHKDSK_EXIT_ERRS_FIXED : // // Autochk was able to repair the partition, but will require a reboot. // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: autochk requires a reboot for %ls.\n", AutochkPartition[i])); RebootRequired = TRUE; default : KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Ran autochk.exe on %ls. \n", AutochkPartition[i] )); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to run autochk.exe on %ls. Status = %x \n", AutochkPartition[i], Status )); SpStartScreen( Win9xRollback ? SP_SCRN_CANT_RUN_AUTOCHK_UNINSTALL : SP_SCRN_CANT_RUN_AUTOCHK, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DriveLetterString ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 ); SpInputDrain(); do { InputChar = SpInputGetKeypress(); } while (InputChar != ASCI_CR && (!Win9xRollback || InputChar != KEY_F3)); if (InputChar == KEY_F3) { SpDone (0, FALSE, FALSE); } // // Put the screen back the way it was // CLEAR_CLIENT_SCREEN(); SpDisplayScreen( SP_SCRN_RUNNING_AUTOCHK, 3, 4 ); if( UserModeGauge != NULL ) { SpDrawGauge( UserModeGauge ); } } } } // // The gauge is no longer needed. // SpDestroyGauge( UserModeGauge ); UserModeGauge = NULL; if (WinntPartition != NULL) { SpMemFree( WinntPartition ); } if (SystemPartition != NULL) { SpMemFree( SystemPartition ); } SpMemFree( AutochkPath ); CLEAR_CLIENT_SCREEN(); if (RebootRequired) { #ifdef _X86_ // // If we are trying to cancel a setup that is in-progress, make sure // that the textmode option is removed from boot.ini, but the textmode // option that has /rollback is left in-place. // if (Win9xRollback) { SpRemoveExtraBootIniEntry(); SpAddRollbackBootOption (TRUE); SpFlushBootVars(); } #endif if (TargetPath && TargetPath[0] && NTUpgrade == UpgradeFull) { SpSetUpgradeStatus( WinntPartitionRegion, TargetPath, UpgradeNotInProgress ); } // // If this is not an unattended case let the user see the // error message and confirm it. // if (!UnattendedOperation) { SpStartScreen( SP_SCRN_AUTOCHK_REQUIRES_REBOOT, 3, HEADER_HEIGHT+1, TRUE, TRUE, DEFAULT_ATTRIBUTE ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_REBOOT, 0 ); SpInputDrain(); while( SpInputGetKeypress() != KEY_F3 ); } if (IsNEC_98) { //NEC98 Nec98RestoreBootFiles = FALSE; } //NEC98 SpDone(SP_SCRN_AUTOCHK_REQUIRES_REBOOT, FALSE, TRUE ); } } BOOLEAN SppPromptOptionalAutochk( IN PVOID SifHandle, IN PWSTR MediaShortname, IN PWSTR DiskDevicePath ) { PWSTR Tagfile,Description,Directory; NTSTATUS Status; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; ULONG ValidKeys[4] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 }; BOOLEAN AutochkChosen; SpGetSourceMediaInfo(SifHandle,MediaShortname,&Description,&Tagfile,&Directory); // // We initially see if the media is in the drive, and if not, we give // the user a message with the option of skipping autochk. We // do this now, so that the user doesn't simply get a disk prompt with // a Cancel option (Cancel what? Autochk? The whole repair process?) // wcscpy(TemporaryBuffer, DiskDevicePath); SpConcatenatePaths(TemporaryBuffer, Tagfile); INIT_OBJA(&ObjectAttributes, &UnicodeString, TemporaryBuffer); Status = ZwCreateFile( &Handle, FILE_GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0 ); // // If we got back success, then we're done. // if(NT_SUCCESS(Status)) { ZwClose(Handle); return TRUE; } // // The media isn't currently in the drive, so give the // user the option of whether to run autochk or not. // AutochkChosen = FALSE; do { SpDisplayScreen(SP_SCRN_AUTOCHK_OPTION, 3, HEADER_HEIGHT+1); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_ESC_EQUALS_CANCEL, 0 ); switch(SpWaitValidKey(ValidKeys, NULL, NULL)) { case ASCI_ESC: return FALSE; case KEY_F3: SpConfirmExit(); break; case ASCI_CR: AutochkChosen = TRUE; } } while(!AutochkChosen); // // Prompt for the disk, based on the setup media type. // return(SpPromptForDisk(Description, DiskDevicePath, Tagfile, FALSE, TRUE, TRUE, NULL)); } PWSTR SpMakePlatformSpecificSectionName( IN PWSTR SectionName ) { PWSTR p; p = SpMemAlloc((wcslen(SectionName) + wcslen(PlatformExtension) + 1) * sizeof(WCHAR)); wcscpy(p,SectionName); wcscat(p,PlatformExtension); return(p); } NTSTATUS SpRunAutoFormat( IN HANDLE MasterSifHandle, IN PWSTR RegionDescription, IN PDISK_REGION PartitionRegion, IN ULONG FilesystemType, IN BOOLEAN QuickFormat, IN DWORD ClusterSize, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSourceDevice ) /*++ Routine Description: Run autofmt to format a partition. Arguments: MasterSifHandle - Handle to txtsetup.sif. RegionDescription - The region description, as displayed to the user, in the screen with the various partitions for the user to choose. PartitionRegion - Pointer to the structure that describes the partition to be formatted. FilesystemType - Indicates the file system to use. ClusterSize - File system cluster-size to use. (0=>Use default) SetupSourceDevicePath - NT device path where autochk.exe is located DirectoryOnSourceDevice - Directory on that device where autochk.exe is located Return Value: None. --*/ { PWSTR MediaShortName; PWSTR MediaDirectory; PWSTR AutofmtPath; ULONG AutofmtStatus; NTSTATUS Status; WCHAR AutofmtArgument[32]; PWSTR PartitionPath; LARGE_INTEGER DelayTime; ULONG PartitionOrdinal; ASSERT( ( FilesystemType == FilesystemNtfs ) || ( FilesystemType == FilesystemFat32) || ( FilesystemType == FilesystemFat ) ); // // Make SURE it's not partition0! The results of formatting partition0 // are so disasterous that this warrants a special check. // PartitionOrdinal = SpPtGetOrdinal(PartitionRegion,PartitionOrdinalCurrent); if(!PartitionOrdinal) { SpBugCheck( SETUP_BUGCHECK_PARTITION, PARTITIONBUG_B, PartitionRegion->DiskNumber, 0 ); } // // Get the device path of the partition to format // SpNtNameFromRegion( PartitionRegion, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); PartitionPath = SpDupStringW(TemporaryBuffer); CLEAR_CLIENT_SCREEN(); // // Prepair to run autofmt // MediaShortName = SpLookUpValueForFile( MasterSifHandle, L"autofmt.exe", INDEX_WHICHMEDIA, TRUE ); // // Prompt the user to insert the setup media. // SpPromptForSetupMedia( MasterSifHandle, MediaShortName, SetupSourceDevicePath ); SpGetSourceMediaInfo(MasterSifHandle,MediaShortName,NULL,NULL,&MediaDirectory); wcscpy( TemporaryBuffer, SetupSourceDevicePath ); SpConcatenatePaths( TemporaryBuffer, DirectoryOnSourceDevice ); SpConcatenatePaths( TemporaryBuffer, MediaDirectory ); SpConcatenatePaths( TemporaryBuffer, L"autofmt.exe" ); AutofmtPath = SpDupStringW( TemporaryBuffer ); // // Run autofmt on the partition // CLEAR_CLIENT_SCREEN(); // // Put up a screen indicating what we are doing. // SpStartScreen( SP_SCRN_SETUP_IS_FORMATTING, 0, HEADER_HEIGHT + 3, TRUE, FALSE, DEFAULT_ATTRIBUTE, RegionDescription, HardDisks[PartitionRegion->DiskNumber].Description ); SpvidClearScreenRegion( 0, VideoVars.ScreenHeight-STATUS_HEIGHT, VideoVars.ScreenWidth, STATUS_HEIGHT, DEFAULT_STATUS_BACKGROUND ); // // Create and display the (global) gauge. // SpFormatMessage( TemporaryBuffer, sizeof(TemporaryBuffer), SP_TEXT_SETUP_IS_FORMATTING ); UserModeGauge = SpCreateAndDisplayGauge( 100, 0, VideoVars.ScreenHeight - STATUS_HEIGHT - (3*GAUGE_HEIGHT/2), TemporaryBuffer, NULL, GF_PERCENTAGE, 0 ); // // Cheesy kludge below to wait 4 seconds before invoking autochk.exe // the first time. This was necessary because the cache manager delays // in closing the handle to system.log (opened by NT registry APIs when // we find NT's to upgrade) // DelayTime.HighPart = -1; DelayTime.LowPart = (ULONG)-40000000; KeDelayExecutionThread (KernelMode, FALSE, &DelayTime); AutofmtStatus = AUTOFMT_EXIT_SUCCESS; if (ClusterSize > 0) { swprintf(AutofmtArgument, L"/a:%lu /t ", ClusterSize); } else { wcscpy(AutofmtArgument, L"/t "); } if (QuickFormat) { wcscat(AutofmtArgument, L"/Q "); } switch(FilesystemType) { case FilesystemNtfs: wcscat(AutofmtArgument, L"/fs:ntfs"); break; case FilesystemFat32: wcscat(AutofmtArgument, L"/fs:fat32"); break; case FilesystemFat: default: wcscat(AutofmtArgument, L"/fs:fat"); break; } // // Tell the IOCTL which disk is being examined. // CurrentDiskIndex = 0; // // For quick format, emulate as though progress is // being made // if (UserModeGauge && QuickFormat) { SpFillGauge(UserModeGauge, 20); } // // Note that autofmt requires that the partition path comes // before the autofmt switches // Status = SpExecuteImage( AutofmtPath, &AutofmtStatus, 2, PartitionPath, AutofmtArgument ); // // For quick format, emulate as though progress is // being made // if (UserModeGauge && QuickFormat) { SpFillGauge(UserModeGauge, 100); // // wait for a second so that user can // see it filled // DelayTime.HighPart = -1; DelayTime.LowPart = (ULONG)-10000000; KeDelayExecutionThread (KernelMode, FALSE, &DelayTime); } // // Destroy the gauge // SpDestroyGauge( UserModeGauge ); UserModeGauge = NULL; if( NT_SUCCESS( Status ) ) { // // autofmt.exe was run. // Find out if the partition was formatted. // KdPrint(("SETUP:AutoFormat Status : %lx\n", AutofmtStatus)); switch(AutofmtStatus) { case AUTOFMT_EXIT_SUCCESS: KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Ran autofmt.exe on %ls. \n", PartitionPath )); #if defined(_AMD64_) || defined(_X86_) if (!IsNEC_98) { //NEC98 // // If we formatted C:, then clear the previous OS entry // in boot.ini. // if(PartitionRegion == SpPtValidSystemPartition()) { *OldSystemLine = '\0'; } } //NEC98 #endif // defined(_AMD64_) || defined(_X86_) break; // case AUTOFMT_EXIT_COULD_NOT_FORMAT : default: // // autofmt was unable to format the partition // Status = STATUS_UNSUCCESSFUL; break; } } else { // // autofmt.exe didn't get executed. // Display a fatal error message. // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to run autofmt.exe on %ls. Status = %x \n", PartitionPath, Status )); SpStartScreen( SP_SCRN_CANT_RUN_AUTOFMT, 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 ); } // // Do the cleanup and return // SpMemFree( PartitionPath ); SpMemFree( AutofmtPath ); CLEAR_CLIENT_SCREEN(); return( Status ); } // // NEC98 // // // On floppyless setup if user have canceled setup or setup be stoped by error // occured,previous OS cann't boot to be written boot code and boot loader. // NTSTATUS SpDeleteAndBackupBootFiles( BOOLEAN RestoreBackupFiles, BOOLEAN DeleteBackupFiles, BOOLEAN DeleteRootDirFiles, BOOLEAN RestorePreviousOs, BOOLEAN ClearBootFlag ) { #define WINNT_BAK L"$WIN_NT$.~BU" #define FILE_ATTRIBUTES_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE) PWSTR DeleteRootFiles[] = {L"ntdetect.com",L"$ldr$",L"boot.ini",L"txtsetup.sif",L"ntldr",L"bootfont.bin",L"bootsect.dos"}; PWSTR RestoreFiles[] = {L"boot.ini",L"ntdetect.com",L"ntldr"}; WCHAR DevicePath[256],SourceFileName[256],TargetFileName[256],TmpFileName[256]; UCHAR i; NTSTATUS status=0; PWSTR SetupSourceDevicePath,DirectoryOnSetupSource; SpdInitialize(); #if defined(_AMD64_) || defined(_X86_) if(RestorePreviousOs){ // // IF bootsect.dos exist in boot path, setup restore previous OS bootcode. // // NOTE:When you modefied boot.ini for multi boot function if it is same NT boot partition // and partition where is exiting bootsect.dos , setup restore DOS bootcode. // Therefore NT on this partition is not boot forever. // SppRestoreBootCode(); } #endif // defined(_AMD64_) || defined(_X86_) if(DeleteRootDirFiles){ // // Delete floppy less boot files in root. // for(i=0 ; i < ELEMENT_COUNT(DeleteRootFiles); i++) { wcscpy(TargetFileName,NtBootDevicePath); SpDeleteFile(TargetFileName, DeleteRootFiles[i], NULL); } #if defined(_X86_) // // If we're on an x86, but it's *NOT* an ARC machine, // then there's no need for the arc loaders to be // present. // if( !SpIsArc() ) { wcscpy(TargetFileName,NtBootDevicePath); SpDeleteFile(TargetFileName, L"arcsetup.exe", NULL); wcscpy(TargetFileName,NtBootDevicePath); SpDeleteFile(TargetFileName, L"arcldr.exe", NULL); } #endif // defined(_X86_) } // // If \BOOTSECT.NEC exists, restore it to \BOOTSECT.DOS. // BTY, winnt32 makes \BOOTSECT.DOS even if boot sector is for NT.(NEC98 only) // wcscpy(SourceFileName,NtBootDevicePath); SpConcatenatePaths(SourceFileName,L"\\"); SpConcatenatePaths(SourceFileName,L"bootsect.nec"); wcscpy(TargetFileName,NtBootDevicePath); SpConcatenatePaths(TargetFileName,L"\\"); SpConcatenatePaths(TargetFileName,L"bootsect.dos"); if(SpFileExists(SourceFileName,FALSE)) { if(SpFileExists(TargetFileName,FALSE)) { SpDeleteFile( TargetFileName, NULL, NULL); } SpRenameFile( SourceFileName, TargetFileName, FALSE ); } if(RestoreBackupFiles){ // // Restore previous NT files to root form $WIN_NT$.~BU. // for(i=0 ; i < ELEMENT_COUNT(RestoreFiles) ;i++) { wcscpy(SourceFileName,NtBootDevicePath); SpConcatenatePaths(SourceFileName,WINNT_BAK); SpConcatenatePaths(SourceFileName,RestoreFiles[i]); wcscpy(TargetFileName,NtBootDevicePath); SpConcatenatePaths(TargetFileName,L"\\"); SpConcatenatePaths(TargetFileName,RestoreFiles[i]); if( SpFileExists( SourceFileName, FALSE ) ) { SpCopyFileUsingNames(SourceFileName,TargetFileName,FILE_ATTRIBUTES_RHS,0L); } } // // Force uncompressd to "\ntldr". // wcscpy(TargetFileName,NtBootDevicePath); SpConcatenatePaths(TargetFileName,L"\\"); SpConcatenatePaths(TargetFileName,L"ntldr"); if( SpFileExists( TargetFileName, FALSE ) ) { SpVerifyNoCompression(TargetFileName); } } if(DeleteBackupFiles){ // // Delete files in $WIN_NT$.~BU. // for(i=0 ; i < ELEMENT_COUNT(RestoreFiles); i++) { wcscpy(TargetFileName,NtBootDevicePath); SpConcatenatePaths(TargetFileName,WINNT_BAK); SpDeleteFile(TargetFileName, RestoreFiles[i], NULL); } // // Delete $WIN_NT$.~BU // wcscpy(TargetFileName,NtBootDevicePath); SpConcatenatePaths(TargetFileName,WINNT_BAK); if( SpFileExists( TargetFileName, FALSE ) ) { SpDeleteFile(TargetFileName, NULL, NULL); } #if NEC_TEST //0 // // It's not available to delete $WIN_NT.~BT, but we will try // to delete $WIN_NT$.~LS, Because Nec98 will boot back after F.3 // if (WinntSetup && !WinntFromCd && !RemoteBootSetup && LocalSourceRegion) { SpGetWinntParams(&SetupSourceDevicePath,&DirectoryOnSetupSource); wcscpy(TargetFileName,SetupSourceDevicePath); SpConcatenatePaths(TargetFileName,DirectoryOnSetupSource); if( SpFileExists( TargetFileName, FALSE ) ) { SpDeleteFile(TargetFileName, NULL, NULL); } } #endif //NEC_TEST } //if(ClearBootFlag && TmpTargetRegion){ if(ClearBootFlag){ SpSetAutoBootFlag(NULL,FALSE); } SpdTerminate(); return(status); } BOOLEAN SpFindServiceInList( IN PWSTR ServiceName ) { LIST_ENTRY *Next; PSERVICE_ENTRY ServiceEntry; Next = SpServiceList.Flink; while ((ULONG_PTR)Next != (ULONG_PTR)&SpServiceList) { ServiceEntry = CONTAINING_RECORD( Next, SERVICE_ENTRY, Next ); Next = ServiceEntry->Next.Flink; if (_wcsicmp( ServiceEntry->ServiceName, ServiceName ) == 0) { return TRUE; } } return FALSE; } BOOLEAN AddServiceToList( IN PWSTR ServiceName ) { PSERVICE_ENTRY ServiceEntry; if (SpFindServiceInList(ServiceName)) { return TRUE; } ServiceEntry = (PSERVICE_ENTRY) SpMemAlloc( sizeof(SERVICE_ENTRY) ); if (ServiceEntry == NULL) { return FALSE; } ServiceEntry->ServiceName = SpDupStringW( ServiceName ); InsertTailList( &SpServiceList, &ServiceEntry->Next ); return TRUE; } BOOLEAN SpFindServiceDependencies( IN HANDLE ServicesHandle, IN PWSTR ServiceName, IN PWSTR ServiceDependName ) { NTSTATUS Status; HANDLE KeyHandle; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; PKEY_VALUE_PARTIAL_INFORMATION ValInfo; ULONG ResultLength; PWSTR SubkeyName; PWSTR s; BOOLEAN rVal = FALSE; INIT_OBJA( &Obja, &UnicodeString, ServiceName ); Obja.RootDirectory = ServicesHandle; Status = ZwOpenKey( &KeyHandle, KEY_READ, &Obja ); if (!NT_SUCCESS(Status)) { return rVal; } ValInfo = (PKEY_VALUE_PARTIAL_INFORMATION) TemporaryBuffer; RtlInitUnicodeString( &UnicodeString, L"DependOnService"); Status = ZwQueryValueKey( KeyHandle, &UnicodeString, KeyValuePartialInformation, TemporaryBuffer, sizeof(TemporaryBuffer), &ResultLength ); if (!NT_SUCCESS(Status)) { ZwClose( KeyHandle ); return rVal; } if (ValInfo->Type == REG_MULTI_SZ) { s = (PWSTR)ValInfo->Data; while (s && *s) { SubkeyName = SpDupStringW( s ); if (SubkeyName) { if (_wcsicmp( ServiceDependName, SubkeyName ) == 0) { if (AddServiceToList( ServiceName )) { rVal = TRUE; } } else if (SpFindServiceDependencies( ServicesHandle, SubkeyName, ServiceDependName )) { if (AddServiceToList( ServiceName )) { rVal = TRUE; } } SpMemFree( SubkeyName ); } s = s + ((wcslen(s)+1)*sizeof(WCHAR)); } } else if (ValInfo->Type == REG_SZ) { SubkeyName = SpDupStringW( (PWSTR)ValInfo->Data ); if (_wcsicmp( ServiceDependName, SubkeyName ) == 0) { if (AddServiceToList( ServiceName )) { rVal = TRUE; } } else if (SpFindServiceDependencies( ServicesHandle, SubkeyName, ServiceDependName )) { if (AddServiceToList( ServiceName )) { rVal = TRUE; } } SpMemFree( SubkeyName ); } ZwClose( KeyHandle ); return rVal; } NTSTATUS SpGetServiceTree( IN PWSTR ServiceName ) { NTSTATUS Status; HANDLE KeyHandle = NULL; HANDLE ServicesHandle = NULL; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; ULONG ResultLength; ULONG SubKeyIndex; PKEY_BASIC_INFORMATION KeyInfo; PWSTR SubkeyName; InitializeListHead( &SpServiceList ); RtlInitUnicodeString( &UnicodeString, REGKEY_SERVICES ); InitializeObjectAttributes( &Obja, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = ZwOpenKey( &ServicesHandle, KEY_READ, &Obja ); if (!NT_SUCCESS(Status)) { return(Status); } for (SubKeyIndex=0,KeyInfo=(PKEY_BASIC_INFORMATION)TemporaryBuffer; NT_SUCCESS( ZwEnumerateKey( ServicesHandle, SubKeyIndex, KeyBasicInformation, TemporaryBuffer, sizeof(TemporaryBuffer), &ResultLength ) ); SubKeyIndex++ ) { KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0; SubkeyName = SpDupStringW(KeyInfo->Name); if (SubkeyName) { SpFindServiceDependencies( ServicesHandle, SubkeyName, ServiceName ); SpMemFree( SubkeyName ); } } ZwClose( ServicesHandle ); return Status; } VOID SpCreateNewGuid( IN GUID *Guid ) /*++ Routine Description: Creates a new pseudo GUID Arguments: Guid - Place holder for the new pseudo Return Value: None. --*/ { if (Guid) { LARGE_INTEGER Time; ULONG Random1 = RtlRandom(&RandomSeed); ULONG Random2 = RtlRandom(&RandomSeed); // // Get system time // KeQuerySystemTime(&Time); RtlZeroMemory(Guid, sizeof(GUID)); // // First 8 bytes is system time // RtlCopyMemory(Guid, &(Time.QuadPart), sizeof(Time.QuadPart)); // // Next 8 bytes are two random numbers // RtlCopyMemory(Guid->Data4, &Random1, sizeof(ULONG)); RtlCopyMemory(((PCHAR)Guid->Data4) + sizeof(ULONG), &Random2, sizeof(ULONG)); #if 0 { WCHAR GuidStr[256]; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCreateNewGuid : %ws\n", SpPtGuidToString(Guid, GuidStr))); } #endif } } NTSTATUS RegisterSetupProgressCallback( IN TM_SETUP_PROGRESS_CALLBACK Callback, IN PVOID Context ) /*++ Routine Description: Registers the given callback function to post setup progress events Arguments: Callback - The callback function Context - Caller specified, context for the callback function that needs to sent with each event Return Value: STATUS_SUCCESS if successful, otherwise appropriate error code. --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (Callback) { if (ProgressSubscribersCount < MAX_SETUP_PROGRESS_SUBSCRIBERS) { ProgressSubscribers[ProgressSubscribersCount].Callback = Callback; ProgressSubscribers[ProgressSubscribersCount].Context = Context; ProgressSubscribersCount++; Callback(CallbackEvent, CallbackInitialize, Context, NULL); Status = STATUS_SUCCESS; } else { Status = STATUS_NO_MEMORY; } } return Status; } NTSTATUS DeregisterSetupProgressCallback( IN TM_SETUP_PROGRESS_CALLBACK Callback, IN PVOID Context ) /*++ Routine Description: Deregisters the given callback function to quit posting setup progress events Arguments: Callback - The callback function Context - Caller specified, context for the callback function that needs to sent with each event Return Value: STATUS_SUCCESS if successful, otherwise appropriate error code. --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (Callback) { ULONG Index; for (Index = 0; Index < MAX_SETUP_PROGRESS_SUBSCRIBERS; Index++) { if (ProgressSubscribers[Index].Callback == Callback) { ProgressSubscribers[Index].Callback = NULL; ProgressSubscribers[Index].Context = NULL; ProgressSubscribersCount--; Index++; // // Compact the array // while ((Index < MAX_SETUP_PROGRESS_SUBSCRIBERS) && (ProgressSubscribers[Index].Callback)) { ProgressSubscribers[Index - 1] = ProgressSubscribers[Index]; Index++; } // // Indicate the callback is going away // Callback(CallbackEvent, CallbackDeInitialize, Context, NULL); Status = STATUS_SUCCESS; break; } } } return Status; } VOID SendSetupProgressEvent( IN TM_SETUP_MAJOR_EVENT MajorEvent, IN TM_SETUP_MINOR_EVENT MinorEvent, IN PVOID EventData ) /*++ Routine Description: Post the specified events and the associated data to all the registered parties interested in setup progress events. Arguments: MajorEvent - Setup progress major event MinorEvent - Setup progress minor event, w.r.t to the major event type EventData - The associated event data with the specified Major and Minor event pair Return Value: None. --*/ { ULONG Index; for (Index = 0; Index < ProgressSubscribersCount; Index++) { ASSERT(ProgressSubscribers[Index].Callback != NULL); ProgressSubscribers[Index].Callback(MajorEvent, MinorEvent, ProgressSubscribers[Index].Context, EventData); } } ULONG SpGetHeaderTextId( VOID ) /*++ Routine Description: Retreives the appropriate product type title id based on the system. Arguments: None. Return Value: Text ID for the product. This ID may be found in usetup.exe --*/ { ULONG HeaderTextId; if (AdvancedServer) { HeaderTextId = SP_HEAD_SRV_SETUP; if (SpIsProductSuite(VER_SUITE_BLADE)) { HeaderTextId = SP_HEAD_BLA_SETUP; } if (SpIsProductSuite(VER_SUITE_SMALLBUSINESS_RESTRICTED)) { HeaderTextId = SP_HEAD_SBS_SETUP; } if (SpIsProductSuite(VER_SUITE_ENTERPRISE)) { HeaderTextId = SP_HEAD_ADS_SETUP; } if (SpIsProductSuite(VER_SUITE_DATACENTER)) { HeaderTextId = SP_HEAD_DTC_SETUP; } } else { HeaderTextId = SP_HEAD_PRO_SETUP; if (SpIsProductSuite(VER_SUITE_PERSONAL)) { HeaderTextId = SP_HEAD_PER_SETUP; } } return(HeaderTextId); } NTSTATUS SpGetVersionFromStr( IN PWSTR VersionStr, OUT PDWORD Version, // major * 100 + minor OUT PDWORD BuildNumber ) /*++ Routine Description: Converts the given version string major.minor.build#.sp# (e.g. 5.0.2195.1) to the two dwords Arguments: VersionStr : The version string Version : Place holder for receiving major & minor version (major * 100 + minor) BuildNumber : Place holder for receiving build number Return Value: STATUS_SUCCESS if successful otherwise appropriate error code --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; if (VersionStr && (Version || BuildNumber)) { DWORD MajorVer = 0, MinorVer = 0, BuildNum = 0; WCHAR *EndPtr = NULL; WCHAR *EndChar = NULL; WCHAR TempBuff[64] = {0}; EndPtr = wcschr(VersionStr, TEXT('.')); if (EndPtr) { wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr)); MajorVer = SpStringToLong(TempBuff, &EndChar, 10); VersionStr = EndPtr + 1; if (VersionStr) { EndPtr = wcschr(VersionStr, TEXT('.')); if (EndPtr) { memset(TempBuff, 0, sizeof(TempBuff)); wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr)); MinorVer = SpStringToLong(TempBuff, &EndChar, 10); VersionStr = EndPtr + 1; if (VersionStr) { EndPtr = wcschr(VersionStr, TEXT('.')); if (EndPtr) { memset(TempBuff, 0, sizeof(TempBuff)); wcsncpy(TempBuff, VersionStr, (EndPtr - VersionStr)); BuildNum = SpStringToLong(TempBuff, &EndChar, 10); } } } } } if ((MajorVer > 0) || (MinorVer > 0) || (BuildNum > 0)) Status = STATUS_SUCCESS; if (NT_SUCCESS(Status)) { if (Version) *Version = (MajorVer * 100) + MinorVer; if (BuildNumber) *BuildNumber = BuildNum; } } return Status; } NTSTATUS SpQueryCanonicalName( IN PWSTR Name, IN ULONG MaxDepth, OUT PWSTR CanonicalName, IN ULONG SizeOfBufferInBytes ) /*++ Routine Description: Resolves the symbolic name to the specified depth. To resolve a symbolic name completely specify the MaxDepth as -1 Arguments: Name - Symbolic name to be resolved MaxDepth - The depth till which the resolution needs to be carried out CanonicalName - The fully resolved name SizeOfBufferInBytes - The size of the CanonicalName buffer in bytes Return Value: Appropriate NT status code --*/ { UNICODE_STRING name, canonName; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE handle; ULONG CurrentDepth; RtlInitUnicodeString(&name, Name); canonName.MaximumLength = (USHORT) (SizeOfBufferInBytes - sizeof(WCHAR)); canonName.Length = 0; canonName.Buffer = CanonicalName; if (name.Length >= canonName.MaximumLength) { return STATUS_BUFFER_TOO_SMALL; } RtlCopyMemory(canonName.Buffer, name.Buffer, name.Length); canonName.Length = name.Length; canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0; for (CurrentDepth = 0; CurrentDepth < MaxDepth; CurrentDepth++) { InitializeObjectAttributes(&oa, &canonName, OBJ_CASE_INSENSITIVE, 0, 0); status = ZwOpenSymbolicLinkObject(&handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &oa); if (!NT_SUCCESS(status)) { break; } status = ZwQuerySymbolicLinkObject(handle, &canonName, NULL); ZwClose(handle); if (!NT_SUCCESS(status)) { return status; } canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0; } return STATUS_SUCCESS; } NTSTATUS SpIterateMountMgrMountPoints( IN PVOID Context, IN SPMOUNTMGR_ITERATION_CALLBACK Callback ) /*++ Routine Description: Iterates through all the mount points acquired from mountmgr and calls the call back function for each mount point. Arguments: Context : Context that needs to be passed on to the caller across iterations Callback : The function that needs to be called back for each mount point. Return Value: Appropriate NT status code --*/ { NTSTATUS Status = STATUS_INVALID_PARAMETER; OBJECT_ATTRIBUTES ObjAttrs; UNICODE_STRING UnicodeString; HANDLE MountMgrHandle; IO_STATUS_BLOCK IoStatusBlock; if (Callback) { INIT_OBJA(&ObjAttrs, &UnicodeString, MOUNTMGR_DEVICE_NAME); // // Open the mountmgr // Status = ZwOpenFile(&MountMgrHandle, (ACCESS_MASK)(FILE_GENERIC_READ), &ObjAttrs, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE , FILE_NON_DIRECTORY_FILE); if (NT_SUCCESS(Status)) { MOUNTMGR_MOUNT_POINT MountPoint; ULONG BufferLength = 0; PVOID Buffer = NULL; Status = STATUS_BUFFER_OVERFLOW; RtlZeroMemory(&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT)); while (Status == STATUS_BUFFER_OVERFLOW) { if (Buffer) { BufferLength = ((PMOUNTMGR_MOUNT_POINTS)Buffer)->Size; SpMemFree(Buffer); } else { BufferLength += (8 * 1024); // start with 8K } // // Allocate the output buffer // Buffer = SpMemAlloc(BufferLength); if (!Buffer) { Status = STATUS_NO_MEMORY; break; // ran out of memory } RtlZeroMemory(Buffer, BufferLength); // // Get the mount points // Status = ZwDeviceIoControlFile(MountMgrHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_MOUNTMGR_QUERY_POINTS, &MountPoint, sizeof(MOUNTMGR_MOUNT_POINT), Buffer, BufferLength); } if (NT_SUCCESS(Status)) { ULONG Index; BOOLEAN Done = FALSE; PMOUNTMGR_MOUNT_POINTS MountPoints = (PMOUNTMGR_MOUNT_POINTS)Buffer; // // Call the callback function for each mountpoint until the requester // doesn't want to continue on. // for (Index=0; !Done && (Index < MountPoints->NumberOfMountPoints); Index++) { Done = Callback(Context, MountPoints, MountPoints->MountPoints + Index); } } // // Free the allocated buffer // if (Buffer) { SpMemFree(Buffer); } // // Done with mountmgr handle // ZwClose(MountMgrHandle); } } return Status; } NTSTATUS SppLockBootStatusData( OUT PHANDLE BootStatusDataHandle, IN PDISK_REGION TargetRegion, IN PWSTR SystemRoot ) /* This function has the same functionality as the RtlLockBootStatusData API except that it doesn't point to SystemRoot. This is needed for textmode setup to open the correct boot status data file on the installation we are upgrading. We can still call the RtlUnlock routine as it operates on the handle. */ { OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING fileName; HANDLE dataFileHandle; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; PWSTR NtPartition; // // Get the name of the target patition. // SpNtNameFromRegion( TargetRegion, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); SpConcatenatePaths(TemporaryBuffer,SystemRoot); SpConcatenatePaths(TemporaryBuffer,L"bootstat.dat"); RtlInitUnicodeString(&fileName, TemporaryBuffer); InitializeObjectAttributes(&objectAttributes, &fileName, OBJ_CASE_INSENSITIVE | OBJ_OPENIF, NULL, NULL); status = ZwOpenFile(&dataFileHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &objectAttributes, &ioStatusBlock, 0, FILE_SYNCHRONOUS_IO_NONALERT); ASSERT(status != STATUS_PENDING); if(NT_SUCCESS(status)) { *BootStatusDataHandle = dataFileHandle; } else { *BootStatusDataHandle = NULL; } return status; } void SpDisableCrashRecoveryForGuiMode( IN PDISK_REGION TargetRegion, IN PWSTR SystemRoot ) /* This function processes the Crash Recovery settings. Crash Recovery functions are implemented as RTL functions. We try to call RtlLockBootStatusData to see if there are settings already in place. If we get STATUS_OBJECT_NAME_NOT_FOUND we know there weren't any settings before and we move on. If we succeed we save away the settings and then disable the feature for GUI mode. At the end of GUI mode we migrate the settings and re-enable crash recovery. */ { NTSTATUS Status; HANDLE BootStatusData; BOOLEAN Enabled = TRUE; PWSTR szYes = L"Yes"; PWSTR szNo = L"No"; //We make this special call to lock the file as the RTL API looks at SystemRoot //that points to ~bt in textmode setup. Status = SppLockBootStatusData( &BootStatusData, TargetRegion, SystemRoot ); if(!NT_SUCCESS(Status)){ if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { //Some other error occured KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlLockBootStatusData failed - Status = %lx \n", Status)); } return; } // If we made it here we need to migrate the current settings. Status = RtlGetSetBootStatusData( BootStatusData, TRUE, RtlBsdItemAabEnabled, &Enabled, sizeof(BOOLEAN), NULL ); if(!NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlGetSetBootStatusData failed to get AabEnabled - Status = %lx \n", Status)); } SpAddLineToSection( WinntSifHandle, SIF_DATA, WINNT_D_CRASHRECOVERYENABLED_W, Enabled ? &szYes : &szNo, 1 ); // Finally disable Crash Recovery for Guimode setup Enabled = FALSE; Status = RtlGetSetBootStatusData( BootStatusData, FALSE, RtlBsdItemAabEnabled, &Enabled, sizeof(BOOLEAN), NULL ); if(!NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpDisableCrashRecoveryForGuiMode() - RtlGetSetBootStatusData failed to set AabEnabled - Status = %lx \n", Status)); } RtlUnlockBootStatusData( BootStatusData ); return; } #endif NTSTATUS SpGetFileVersionFromPath( IN PCWSTR FilePath, OUT PULONGLONG Version ) /*++ Routine Description: Gets the version of the specified file. The function maps the file and calls SpGetFileVersion. Arguments: FilePath - path to the file Version - pointer to a location where to store the version Return value: The error status. --*/ { NTSTATUS Status; PVOID Base = NULL; HANDLE FileHandle = NULL; HANDLE SectionHandle = NULL; ULONG Size; if(NULL == FilePath || 0 == FilePath[0] || NULL == Version) { Status = STATUS_INVALID_PARAMETER; goto exit; } Status = SpOpenAndMapFile((PWSTR) FilePath, &FileHandle, &SectionHandle, &Base, &Size, FALSE); if(!NT_SUCCESS(Status)) { FileHandle = SectionHandle = NULL; Base = NULL; goto exit; } SpGetFileVersion(Base, Version); exit: if(Base != NULL) { SpUnmapFile(SectionHandle, Base); ZwClose(FileHandle); } return Status; }