/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Spntfix.c Abstract: This module contains code to repair winnt installations. Author: Shie-Lin Tzong (shielint) 27-Jan-1994 Revision History: --*/ #include "spprecmp.h" #pragma hdrstop // // Path to the ntuser.dat hive // #define DEFAULT_USER_PATH L"Users\\Default User" // // Global variables control which repair options should be performed. // Initialized to ALL options. We explicitly use 1 and 0 for true and false. // #ifdef _X86_ ULONG RepairItems[RepairItemMax] = { 0, 0, 0}; // BCL - Seagate - removed one. #else ULONG RepairItems[RepairItemMax] = { 0, 0}; // BCL #endif PVOID RepairGauge = NULL; // // global variables for delayed driver CAB opening during // repair // extern PWSTR gszDrvInfDeviceName; extern PWSTR gszDrvInfDirName; extern HANDLE ghSif; #define ATTR_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM) //************************************************************** // S E L E C T I N G N T T O REPAIR S T U F F //************************************************************** #define MENU_LEFT_X 3 #define MENU_WIDTH (VideoVars.ScreenWidth-(2*MENU_LEFT_X)) #define LIST_BOX_WIDTH 50 #define LIST_BOX_HEIGHT RepairItemMax+1 #define HIVE_LIST_BOX_WIDTH 45 #define HIVE_LIST_BOX_HEIGHT RepairHiveMax+1 #define MENU_INDENT 4 VOID SppGetRepairPathInformation( IN PVOID LogFileHandle, OUT PWSTR *SystemPartition, OUT PWSTR *SystemPartitionDirectory, OUT PWSTR *WinntPartition, OUT PWSTR *WinntPartitionDirectory ) /*++ Routine Description: This goes through the list of NTs on the system and finds out which are repairable. Presents the information to the user. Arguments: SifHandle - Handle the txtsetup.sif SystemPartition - Supplies a variable to receive the name of System partition. SystemPartitionDirectory - Supplies a variable to receive the name of the osloader directory on the system partition. WinntPartition - Supplies a variable to receive the name of winnt partition. WinntPartitionDirectory - Supplies a variable to receive the winnt directory. Return Value: None. --*/ { PWSTR KeyName = NULL; *SystemPartition = SpGetSectionKeyIndex (LogFileHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DEVICE, 0); if (*SystemPartition == NULL) { KeyName = SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DEVICE; goto ReportError; } *SystemPartitionDirectory = SpGetSectionKeyIndex (LogFileHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DIRECTORY, 0); if (*SystemPartitionDirectory == NULL) { KeyName = SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DIRECTORY; goto ReportError; } *WinntPartition = SpGetSectionKeyIndex ( LogFileHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_TARGET_DEVICE, 0); if (*WinntPartition == NULL) { KeyName = SIF_NEW_REPAIR_PATHS_TARGET_DEVICE; goto ReportError; } *WinntPartitionDirectory = SpGetSectionKeyIndex (LogFileHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_TARGET_DIRECTORY, 0); if (*WinntPartitionDirectory == NULL) { KeyName = SIF_NEW_REPAIR_PATHS_TARGET_DIRECTORY; goto ReportError; } ReportError: if (KeyName) { // // Unable to find path information. This indicates the setup.log // is bad. Inform user and exit. // SpFatalSifError(LogFileHandle,SIF_NEW_REPAIR_PATHS,KeyName,0,0); } } BOOLEAN SpFindNtToRepair( IN PVOID SifHandle, OUT PDISK_REGION *TargetRegion, OUT PWSTR *TargetPath, OUT PDISK_REGION *SystemPartitionRegion, OUT PWSTR *SystemPartitionDirectory, OUT PBOOLEAN RepairableBootSetsFound ) /*++ Routine Description: This goes through the list of NTs on the system and finds out which are repairable. Presents the information to the user. Arguments: SifHandle: Handle the txtsetup.sif TargetRegion: Variable to receive the partition of the Windows NT to install NULL if not chosen. TargetPath: Variable to receive the target path of Windows NT. NULL if not decided. SystemPartitionRegion: Variable to receive the system partition of the Windows NT SystemPartitionDirectory: Variable to receive the osloader directory of the system partition. RepairableBootSetsFound: Indicates whether a repairable boot set was found. This information can be used by the caller when the function returns FALSE, so that the caller can determine if no repairable disk was found, or if the user didn't select any of the repairable systems found. Return Value: A boolean value to indicate if selection has been made. --*/ { NT_PRODUCT_TYPE ProductType; BOOLEAN GoRepair = FALSE; NTSTATUS NtStatus; ULONG j, RepairBootSets = 0, MajorVersion, MinorVersion, BuildNumber, ProductSuiteMask; PSP_BOOT_ENTRY BootEntry; PSP_BOOT_ENTRY ChosenBootEntry; LCID LangId; UPG_PROGRESS_TYPE UpgradeProgressValue; // // Find all upgradeable boot entries. These are entries that are unique in // the boot entry list and are present on disk. // SpDetermineUniqueAndPresentBootEntries(); for ( BootEntry = SpBootEntries; BootEntry != NULL ; BootEntry = BootEntry->Next ) { if (!BootEntry->Processable) { continue; } // // Reinitialize // BootEntry->Processable = FALSE; LangId = -1; // // try loading the registry and getting the following information // out of it: // // 1) Product type: WINNT | LANMANNT // 2) Major and Minor Version Number // // Based on the information, we will update the RepairableList. // NtStatus = SpDetermineProduct( BootEntry->OsPartitionDiskRegion, BootEntry->OsDirectory, &ProductType, &MajorVersion, &MinorVersion, &BuildNumber, &ProductSuiteMask, &UpgradeProgressValue, NULL, NULL, // Pid is not needed NULL, // ignore eval variation flag &LangId, // Language Id NULL // service pack not needed? ); if(NT_SUCCESS(NtStatus)) { // // make sure we only try to repair a build that matches the CD we have inserted // BootEntry->Processable = SpDoBuildsMatch( SifHandle, BuildNumber, ProductType, ProductSuiteMask, AdvancedServer, SuiteType, LangId ); if( BootEntry->Processable ) { RepairBootSets++; ChosenBootEntry = BootEntry; } } } // // Find out how many valid boot sets there are which we can repair // *RepairableBootSetsFound = (RepairBootSets != 0); if ( RepairBootSets == 1 ) { // // If it is a fresh attempt at upgrade ask the user if he // wants to upgrade or not // GoRepair = SppSelectNTSingleRepair( ChosenBootEntry->OsPartitionDiskRegion, ChosenBootEntry->OsDirectory, ChosenBootEntry->FriendlyName ); } else if (RepairBootSets > 1) { // // Find out if the user wants to upgrade one of the Windows // NT found // GoRepair = SppSelectNTMultiRepair( &ChosenBootEntry ); } // // Depending on upgrade selection made do the setup needed before // we do the upgrade // if (GoRepair) { PWSTR p1,p2,p3; ULONG u; // // Return the region we are goint to repair // *TargetRegion = ChosenBootEntry->OsPartitionDiskRegion; *TargetPath = SpDupStringW(ChosenBootEntry->OsDirectory); *SystemPartitionRegion = ChosenBootEntry->LoaderPartitionDiskRegion; // // Process the osloader variable to extract the system partition path. // The var vould be of the form ...partition(1)\os\nt\... or // ...partition(1)os\nt\... // So we search forward for the first \ and then backwards for // the closest ) to find the start of the directory. We then // search backwards in the resulting string for the last \ to find // the end of the directory. // p1 = ChosenBootEntry->LoaderFile; p2 = wcsrchr(p1, L'\\'); if (p2 == NULL) { p2 = p1; } u = (ULONG)(p2 - p1); if(u == 0) { *SystemPartitionDirectory = SpDupStringW(L""); } else { p2 = p3 = SpMemAlloc((u+2)*sizeof(WCHAR)); ASSERT(p3); if(*p1 != L'\\') { *p3++ = L'\\'; } wcsncpy(p3, p1, u); p3[u] = 0; *SystemPartitionDirectory = p2; } } // // Do cleanup // CLEAR_CLIENT_SCREEN(); return (GoRepair); } BOOLEAN SppSelectNTSingleRepair( IN PDISK_REGION Region, IN PWSTR OsLoadFileName, IN PWSTR LoadIdentifier ) /*++ Routine Description: Inform a user that Setup has found a previous Windows NT installation. The user has the option to repair this or cancel. Arguments: Region - Region descriptor for the NT found OsLoadFileName - Directory for the NT found LoadIdentifier - Multi boot load identifier used for this NT. Return Value: --*/ { ULONG ValidKeys[] = { KEY_F3,ASCI_CR, ASCI_ESC, 0 }; ULONG c; PWSTR TmpString = NULL; ASSERT(Region->PartitionedSpace); ASSERT(wcslen(OsLoadFileName) >= 2); if( Region->DriveLetter ) { swprintf( TemporaryBuffer, L"%wc:%ws", Region->DriveLetter, OsLoadFileName ); TmpString = SpDupStringW( TemporaryBuffer ); } while(1) { SpStartScreen( SP_SCRN_WINNT_REPAIR, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, (Region->DriveLetter)? TmpString : OsLoadFileName, LoadIdentifier ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_ENTER_EQUALS_REPAIR, 0 ); if( TmpString != NULL ) { SpMemFree( TmpString ); } switch(c=SpWaitValidKey(ValidKeys,NULL,NULL)) { case KEY_F3: SpConfirmExit(); break; case ASCI_CR: return(TRUE); default: // // must have entered ESC // return(FALSE); } } } BOOLEAN SppSelectNTMultiRepair( OUT PSP_BOOT_ENTRY *BootEntryChosen ) { PVOID Menu; ULONG MenuTopY; ULONG ValidKeys[] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 }; ULONG Keypress; PSP_BOOT_ENTRY BootEntry,FirstRepairSet; while(1) { // // Display the text that goes above the menu on the partitioning screen. // SpDisplayScreen(SP_SCRN_WINNT_REPAIR_LIST,3,CLIENT_TOP+1); // // Calculate menu placement. Leave one blank line // and one line for a frame. // MenuTopY = NextMessageTopLine + (SplangQueryMinimizeExtraSpacing() ? 2 : 5); // // Create a menu. // Menu = SpMnCreate( MENU_LEFT_X, MenuTopY, MENU_WIDTH, VideoVars.ScreenHeight-MenuTopY-2-STATUS_HEIGHT ); ASSERT(Menu); // // Build up a menu of partitions and free spaces. // FirstRepairSet = NULL; for(BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next ) { if( BootEntry->Processable ) { if( BootEntry->OsPartitionDiskRegion->DriveLetter ) { swprintf( TemporaryBuffer, L"%wc:%ws %ws", BootEntry->OsPartitionDiskRegion->DriveLetter, BootEntry->OsDirectory, BootEntry->FriendlyName ); } else { swprintf( TemporaryBuffer, L"%ws %ws", BootEntry->OsDirectory, BootEntry->FriendlyName ); } SpMnAddItem(Menu, TemporaryBuffer, MENU_LEFT_X+MENU_INDENT, MENU_WIDTH-(2*MENU_INDENT), TRUE, (ULONG_PTR)BootEntry ); if(FirstRepairSet == NULL) { FirstRepairSet = BootEntry; } } } // // Initialize the status line. // SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_ENTER_EQUALS_REPAIR, 0 ); // // Display the menu // SpMnDisplay( Menu, (ULONG_PTR)FirstRepairSet, TRUE, ValidKeys, NULL, NULL, &Keypress, (PULONG_PTR)BootEntryChosen ); // // Now act on the user's selection. // switch(Keypress) { case KEY_F3: SpConfirmExit(); break; case ASCI_CR: SpMnDestroy(Menu); return( TRUE ); default: SpMnDestroy(Menu); return(FALSE); } SpMnDestroy(Menu); } } VOID SppRepairScreenRepaint( IN PWSTR FullSourcename, OPTIONAL IN PWSTR FullTargetname, OPTIONAL IN BOOLEAN RepaintEntireScreen ) { UNREFERENCED_PARAMETER(FullTargetname); UNREFERENCED_PARAMETER(FullSourcename); // // Repaint the entire screen if necessary. // if(RepaintEntireScreen) { if( SpDrEnabled() ) { SpStartScreen( SP_SCRN_ASR_IS_EXAMINING, 0, 6, TRUE, FALSE, DEFAULT_ATTRIBUTE ); } else { SpStartScreen( SP_SCRN_SETUP_IS_EXAMINING,0, 6, TRUE, FALSE, DEFAULT_ATTRIBUTE ); } if(RepairGauge) { SpDrawGauge(RepairGauge); } } } BOOLEAN SpErDiskScreen( BOOLEAN *HasErDisk ) /*++ Routine Description: Ask user if user has emergency repair disk. Arguments: *HasErDisk - Indicates whether diskette will be used. Return Value: TRUE - User chose disk or diskless. FALSE - User wants to return to previous screen. --*/ { ULONG ValidKeys[] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 }; ULONG MnemonicKeys[] = { MnemonicLocate, 0 }; BOOLEAN Choosing; ULONG c; for (Choosing = TRUE; Choosing; ) { SpDisplayScreen(SP_SCRN_REPAIR_ASK_REPAIR_DISK,3,4); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_L_EQUALS_LOCATE, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_F3_EQUALS_EXIT, 0 ); // // Wait for keypress. Valid keys: // // L = Do not use ER diskette, locate on hard disk // F3 = exit // ENTER = Use ER diskette // ESC = cancel, return to previous screen // SpInputDrain(); switch(c=SpWaitValidKey(ValidKeys,NULL,MnemonicKeys)) { case ASCI_CR: // // User wants express setup. // *HasErDisk = TRUE; Choosing = FALSE; break; case (MnemonicLocate | KEY_MNEMONIC): // // User wants repair without diskette. // *HasErDisk = FALSE; Choosing = FALSE; break; case KEY_F3: // // User wants to exit. // SpConfirmExit(); break; default: // // must be ESC // *HasErDisk = FALSE; Choosing = FALSE; return( FALSE ); } } return( TRUE ); } BOOLEAN SppRepairReportError( IN BOOLEAN AllowEsc, IN ULONG ErrorScreenId, IN ULONG SubErrorId, IN PWSTR SectionName, IN ULONG LineNumber, IN PBOOLEAN DoNotPromptAgain ) /*++ Routine Description: Inform a user that repair has encountered some kind of error. The user has the option to continue or exit. Arguments: AllowEsc - Supplies a BOOLEAN to indicate if ESC is allowed. ErrorScreenId - The SCREEN error message number. SubErrorId - the sub error number SectionName - the name of the section which error occured. LineNumber - the error line number within the specified section. Return Value: FALSE if ESC was pressed. --*/ { ULONG ValidKeys0[] = { KEY_F3, ASCI_CR, 0 }; ULONG ValidKeys1[] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 }; ULONG MnemonicKeys[] = { MnemonicRepairAll, 0 }; PULONG ValidKeys; PULONG Mnemonics; ULONG c; PWSTR SubError; BOOLEAN rc; SubError = SpMemAlloc(512); // // Line numbers are 0-based. Want to display to user as 1-based. // LineNumber++; // // Fetch/format the suberror. // SpFormatMessage(SubError, 512, SubErrorId, SectionName, LineNumber); // // Display the error screen. // SpStartScreen( ErrorScreenId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, SubError ); SpMemFree(SubError); // // Display status options: enter to continue. // if (AllowEsc) { SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_A_EQUALS_REPAIR_ALL, SP_STAT_ESC_EQUALS_SKIP_FILE, SP_STAT_F3_EQUALS_EXIT, 0); ValidKeys = ValidKeys1; Mnemonics = MnemonicKeys; if( DoNotPromptAgain != NULL ) { *DoNotPromptAgain = FALSE; } } else { SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_F3_EQUALS_EXIT, 0); ValidKeys = ValidKeys0; Mnemonics = NULL; } // // Wait for the user to press enter. // switch(c=SpWaitValidKey(ValidKeys,NULL,Mnemonics)) { case KEY_F3: SpConfirmExit(); break; case ASCI_CR: rc = TRUE; break; case ASCI_ESC: rc = FALSE; break; default: // // must be repair all mnemonic // ASSERT(c == (MnemonicRepairAll | KEY_MNEMONIC)); if( DoNotPromptAgain != NULL ) { *DoNotPromptAgain = TRUE; } rc = TRUE; break; } CLEAR_CLIENT_SCREEN(); return(rc); } BOOLEAN SpLoadRepairLogFile( IN PWCHAR Filename, OUT PVOID *Handle ) /*++ Routine Description: Load repair text file (setup.log) into memory. Arguments: Filename - Supplies full filename (in NT namespace) of the file to be loaded. Handle - receives handle to loaded file, which can be used in subsequent calls to other text file services. Return Value: BOOLEAN value to indicate if the setup.log is processed. --*/ { NTSTATUS Status; PWSTR Version; ULONG ErrorSubId; ULONG ErrorLine; // // Load setup.log // Status = SpLoadSetupTextFile( Filename, NULL, // No image already in memory 0, // Image size is empty Handle, &ErrorLine, TRUE, FALSE ); if(!NT_SUCCESS(Status)) { if(Status == STATUS_UNSUCCESSFUL) { // // Syntax error in setup.log file // ErrorSubId = SP_TEXT_REPAIR_INF_ERROR_1; } else { // // Unable to load setup.log file // ErrorLine = 0; ErrorSubId = SP_TEXT_REPAIR_INF_ERROR_0; } SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR, ErrorSubId, NULL, ErrorLine, NULL ); *Handle = NULL; return (FALSE); } // // Check if this setup.log file is for Winnt 3.5 // Version = SpGetSectionKeyIndex(*Handle, SIF_NEW_REPAIR_SIGNATURE, SIF_NEW_REPAIR_VERSION_KEY, 0); // should be moved to spsif.c if(Version == NULL) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR, SP_TEXT_REPAIR_INF_ERROR_2, NULL, 0, NULL); } else { if(!_wcsicmp(Version,SIF_NEW_REPAIR_NT_VERSION)) { return(TRUE); } else { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR, SP_TEXT_REPAIR_INF_ERROR_5, NULL, 0, NULL); } } // // Control comes here only when error occurs ... // SpFreeTextFile(*Handle); *Handle = NULL; return(FALSE); } VOID SpRepairDiskette( OUT PVOID *SifHandle, OUT PDISK_REGION *TargetRegion, OUT PWSTR *TargetPath, OUT PDISK_REGION *SystemPartitionRegion, OUT PWSTR *SystemPartitionDirectory ) /*++ Routine Description: This routine checks if there is a floppy drive. If no, it returns silently. Otherwise, it prompts user for Emergency Repair Disk. Arguments: SifHandle - Supplies a variable to receive the setup.log file handle. TargetRegion - Supplies a variable to receive the pointer to the target installation region. TargetPath - Supplies a variable to receive the nt name of target path. SystemPartitionRegion - Supplies a variable to receive the pointer of the system partition region. SystemPartitionDirectory - Supplies a variable to receive the osloader directory name on the system partition. Return Value: None. --*/ { PWSTR szDiskName; BOOLEAN b, rc = FALSE; PWSTR FullLogFilename, p, FloppyDevicePath = L"\\device\\floppy0"; PWSTR SystemPartition, WinntPartition; // // Assume failure. // *SifHandle = NULL; // // Always want to prompt for the disk in A:. // First, check if there is an A:. If no floppy drive, // simply skip the request for ER diskette. // if(SpGetFloppyDriveType(0) == FloppyTypeNone) { return; } // // Fetch the generic repair disk name. // SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_REPAIR_DISK_NAME); szDiskName = SpDupStringW(TemporaryBuffer); p = TemporaryBuffer; *p = 0; SpConcatenatePaths(p, FloppyDevicePath); SpConcatenatePaths(p, SETUP_LOG_FILENAME); FullLogFilename = SpDupStringW(p); while (rc == FALSE) { // // Prompt for the disk -- ignore what may be in the drive already, // and allow escape. // b = SpPromptForDisk( szDiskName, FloppyDevicePath, SETUP_LOG_FILENAME, TRUE, // Always prompt for at least once TRUE, // Allow user to cancel FALSE, // No multiple prompts NULL // don't care about redraw flag ); // // If the user pressed escape at the disk prompt, bail out now. // if(!b) { rc = TRUE; // User canceled. Skip repair floppy } else { rc = SpLoadRepairLogFile(FullLogFilename, SifHandle); if (rc) { // // Now we need to figure out the partition, path information // to update boot.ini. // SppGetRepairPathInformation(*SifHandle, &SystemPartition, SystemPartitionDirectory, &WinntPartition, TargetPath ); *SystemPartitionRegion = SpRegionFromNtName( SystemPartition, PartitionOrdinalCurrent); if (*SystemPartitionRegion == NULL) { SpFatalSifError(*SifHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DEVICE,0,0); } *TargetRegion = SpRegionFromNtName(WinntPartition, PartitionOrdinalCurrent); if (*TargetRegion == NULL) { SpFatalSifError(*SifHandle, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_TARGET_DEVICE,0,0); } } } } SpMemFree(szDiskName); SpMemFree(FullLogFilename); return; } VOID SppRepairWinntFiles( IN PVOID LogFileHandle, IN PVOID MasterSifHandle, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR SystemPartition, IN PWSTR SystemPartitionDirectory, IN PWSTR WinntPartition, IN PWSTR WinntPartitionDirectory ) /*++ Routine Description: This routine goes through the system partition files and winnt files listed in the setup.log file and checks their validity. Arguments: LogFileHandle - Handle of the setup.log MasterSifHandle - Handle of the txtsetup.sif SourceDevicePath - supplies the NT name of the source device DirectoryOnSourceDevice - supplies the directory on the source device which contains source file. Return Value: None. --*/ { PWSTR SystemPartitionFiles = L"system partition files"; PWSTR WinntFiles = L"WinNt files"; ULONG TotalFileCount; BOOLEAN RepairWithoutConfirming; // // Create file repair gauge // TotalFileCount = SpCountLinesInSection(LogFileHandle,SIF_NEW_REPAIR_SYSPARTFILES); TotalFileCount += SpCountLinesInSection(LogFileHandle,SIF_NEW_REPAIR_WINNTFILES); TotalFileCount += SpCountLinesInSection(LogFileHandle,SIF_NEW_REPAIR_FILES_IN_REPAIR_DIR); CLEAR_CLIENT_SCREEN(); SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_SETUP_IS_EXAMINING); RepairGauge = SpCreateAndDisplayGauge(TotalFileCount,0,15,TemporaryBuffer,NULL,GF_PERCENTAGE,0); ASSERT(RepairGauge); // // delay opening of driver inf and cab file till required // ghSif = MasterSifHandle; gszDrvInfDeviceName = SourceDevicePath; gszDrvInfDirName = DirectoryOnSourceDevice; SpDisplayStatusText(SP_STAT_PLEASE_WAIT,DEFAULT_STATUS_ATTRIBUTE); SppRepairScreenRepaint(NULL, NULL, TRUE); // // first recreate all of the directories we copy into // if (SystemPartition != NULL) { SpCreateDirectory(SystemPartition,NULL,SystemPartitionDirectory,0,0); } // // Create the nt tree. // SpCreateDirectoryStructureFromSif(MasterSifHandle, SIF_NTDIRECTORIES, WinntPartition, WinntPartitionDirectory); // // Verify and repair the files in [Files.InRepairDirectory]. If textmode // setup is executing a disaster recovery, do not prompt the user for the // files to repair. Just go ahead and repair 'em. // RepairWithoutConfirming = SpDrEnabled() && SpDrIsRepairFast(); SppVerifyAndRepairVdmFiles(LogFileHandle, WinntPartition, NULL, &RepairWithoutConfirming); // // Verify and repair the files in [FIles.SystemPartition] // SppVerifyAndRepairFiles(LogFileHandle, MasterSifHandle, SIF_NEW_REPAIR_SYSPARTFILES, SourceDevicePath, DirectoryOnSourceDevice, SystemPartition, SystemPartitionDirectory, TRUE, &RepairWithoutConfirming); // // Verify and repair the files in [Files.WinNt] // SppVerifyAndRepairFiles(LogFileHandle, MasterSifHandle, SIF_NEW_REPAIR_WINNTFILES, SourceDevicePath, DirectoryOnSourceDevice, WinntPartition, NULL, FALSE, &RepairWithoutConfirming); SpDestroyGauge(RepairGauge); RepairGauge = NULL; } VOID SppVerifyAndRepairFiles( IN PVOID LogFileHandle, IN PVOID MasterSifHandle, IN PWSTR SectionName, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR TargetDevicePath, IN PWSTR DirectoryOnTargetDevice, IN BOOLEAN SystemPartitionFiles, IN OUT PBOOLEAN RepairWithoutConfirming ) /*++ Routine Description: This routine goes through the files listed in the specified section of setup.log file and checks their validity. If a file's checksum does not match the checksum listed in the setup.log file, we will prompt the user and recopy the file from original installation sources. Arguments: LogFileHandle - Handle of the setup.log MasterSifHandle - Handle of the txtsetup.sif SectionName - Section in setup.log to be examined SourceDevicePath - supplies the NT name of the source device DirectoryOnSourceDevice - supplies the directory on the source device which contains source file. TargetDevicePath - supplies the nt name of the target device DirectoryOnTargetDevice - the name of the winnt directory on target device SystemPartitionFile - supplies a boolean value to indicate if the target file is on system partition RepairWithoutConfirming - Pointer to a flag that indicates whether or not setup should repair a damaged file without asking the user to confirm. Return Value: None. --*/ { PWSTR FullTargetName, ChecksumString; PWSTR TargetDirectory, TargetFileName; PWSTR SourceFileName; ULONG Checksum, FileChecksum, PrefixLength, Length, Count, i; BOOLEAN IsNtImage, IsValid, RepairFile, SysPartNTFS = FALSE; BOOLEAN RedrawGauge = TRUE, ForceNoComp; FILE_TO_COPY FileToCopy; PWSTR OemDiskDescription, OemDiskTag, OemSourceDirectory; PWSTR DevicePath, Directory, q; PWSTR MediaShortName, PreviousMediaName = L""; PWSTR MediaDir; NTSTATUS Status; // // Allocate a SMALL buffer for local use and init FileToCopy struct // TargetDirectory = NULL; FullTargetName = SpMemAlloc(1024); *FullTargetName = 0; FileToCopy.Next = NULL; FileToCopy.AbsoluteTargetDirectory = TRUE; FileToCopy.TargetDevicePath = TargetDevicePath; SpConcatenatePaths(FullTargetName,TargetDevicePath); if(SystemPartitionFiles) { PDISK_REGION SystemPartitionRegion; // // We must find out whether the system partition is NTFS, because // if it is, then we might want to make sure it's not compressed. // if(SystemPartitionRegion = SpRegionFromNtName(TargetDevicePath, PartitionOrdinalCurrent)) { SysPartNTFS = (SystemPartitionRegion->Filesystem == FilesystemNtfs); } // // For system partition files, we need to concatenate target // directory to FullTargetName. Because the target filename // of system partition files do not have target directory. // FileToCopy.TargetDirectory = DirectoryOnTargetDevice; SpConcatenatePaths(FullTargetName,FileToCopy.TargetDirectory); } PrefixLength = wcslen(FullTargetName); Count = SpCountLinesInSection(LogFileHandle,SectionName); for (i = 0; i < Count; i++) { if (RedrawGauge) { SppRepairScreenRepaint(NULL, NULL, TRUE); RedrawGauge = FALSE; } SpTickGauge(RepairGauge); // // Initialize the 'ForceNoComp' flag to FALSE, thus allowing the // file to use NTFS compression. // ForceNoComp = FALSE; // // Initialize target fullname to be DevicePath+Directory for // system partition file or DevicePath for Winnt files // FullTargetName[PrefixLength] = (WCHAR)NULL; // // If we allocate space for TargetDirectory we must free it. // if (TargetDirectory) { SpMemFree(TargetDirectory); TargetDirectory = NULL; } TargetFileName = SpGetKeyName(LogFileHandle,SectionName,i); if(!TargetFileName) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } // // If the target file name contains \system32\config\, it is // hive related file. We simply ignore it. // q = SpDupStringW(TargetFileName); SpStringToUpper(q); if (wcsstr(q,L"\\SYSTEM32\\CONFIG\\")) { SpMemFree(q); continue; } SpMemFree(q); SpConcatenatePaths(FullTargetName,TargetFileName); SpDisplayStatusText(SP_STAT_EXAMINING_WINNT, DEFAULT_STATUS_ATTRIBUTE, TargetFileName); ChecksumString = SpGetSectionLineIndex(LogFileHandle,SectionName,i,1); if(!ChecksumString) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } Checksum = (ULONG)SpStringToLong(ChecksumString, NULL, 16); // // Validate the security set on the file. // Note that we do not check the files in the system partition // on non-x86 systems since it is always FAT // #ifndef _X86_ if(!SystemPartitionFiles) { #endif Status = SpVerifyFileAccess( FullTargetName, STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | SYNCHRONIZE ); if( !NT_SUCCESS( Status ) && ((Status == STATUS_ACCESS_DENIED)||(Status == STATUS_PRIVILEGE_NOT_HELD)) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Security of %ls, must be fixed. Status = %x\n", FullTargetName, Status )); Status = SpSetDefaultFileSecurity( FullTargetName ); if( !NT_SUCCESS( Status ) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to change security of %ls. Status = %x\n", FullTargetName, Status )); } } #ifndef _X86_ // end of block started by "if(!SystemPartitionFiles) {" above } #endif // // If this is a system partition file and the system partition is NTFS, // then check to see whether this file can use NTFS compression, and // if not, force it to be uncompressed. // if((SysPartNTFS) && IsFileFlagSet(MasterSifHandle,TargetFileName,FILEFLG_FORCENOCOMP)) { ForceNoComp = TRUE; SpVerifyNoCompression(FullTargetName); } SpValidateAndChecksumFile(NULL,FullTargetName,&IsNtImage,&FileChecksum,&IsValid); // // If the image is invalid or the file on the target is not the // original file copied by setup, we will recopy it. // if (!IsValid || FileChecksum != Checksum) { // // Ask user if he wants to repair the file // if(*RepairWithoutConfirming) { RepairFile = TRUE; } else { RepairFile = SppRepairReportError( TRUE, SP_SCRN_REPAIR_FILE_MISMATCH, SP_TEXT_REPAIR_INF_ERROR_4, TargetFileName, i, RepairWithoutConfirming); RedrawGauge = TRUE; } if (!RepairFile) { continue; } SpDisplayStatusText(SP_STAT_REPAIR_WINNT, DEFAULT_STATUS_ATTRIBUTE, TargetFileName); if (SystemPartitionFiles) { FileToCopy.TargetFilename = TargetFileName; } else { // // For Winnt files, the TargetName contains path and filename. // We need to seperate them. // TargetDirectory = SpDupStringW(TargetFileName); Length = wcslen(TargetDirectory); while (Length) { if (TargetDirectory[Length] == L'\\') { TargetDirectory[Length] = 0; TargetFileName = &TargetDirectory[Length + 1]; break; } else { Length--; } } if (Length == 0) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } FileToCopy.TargetFilename = TargetFileName; FileToCopy.TargetDirectory = TargetDirectory; } SourceFileName = SpGetSectionLineIndex(LogFileHandle,SectionName,i,0); if (!SourceFileName) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } FileToCopy.SourceFilename = NULL; q = SpDupStringW(SourceFileName); SpStringToUpper(q); if (wcsstr(q,L"DRIVER.CAB")) { SpMemFree(q); q = SpDupStringW(TargetFileName); SpStringToUpper(q); if (!wcsstr(q,L"DRIVER.CAB")) { FileToCopy.SourceFilename = TargetFileName; } } SpMemFree(q); FileToCopy.SourceFilename = FileToCopy.SourceFilename ? FileToCopy.SourceFilename : SourceFileName; FileToCopy.Flags = COPY_ALWAYS | COPY_NOVERSIONCHECK | (ForceNoComp ? COPY_FORCENOCOMP : 0); // // The file may come from OEM diskette. We need to check if the // sources device is listed in log file. If not, it must be // from MS setup sources. // OemSourceDirectory = SpGetSectionLineIndex(LogFileHandle,SectionName,i,2); OemDiskTag = NULL; if (OemSourceDirectory) { OemDiskDescription = SpGetSectionLineIndex(LogFileHandle,SectionName,i,3); if (OemDiskDescription) { OemDiskTag = SpGetSectionLineIndex(LogFileHandle,SectionName,i,4); if((OemDiskTag != NULL) && (wcslen(OemDiskTag) == 0)){ OemDiskTag = SourceFileName; } } } if (OemDiskTag) { BOOLEAN rs; PWSTR szDevicePath = SpDupStringW(L"\\device\\floppy0"); // // Prompt for the disk, based on the setup media type. // rs = SpPromptForDisk( OemDiskDescription, szDevicePath, OemDiskTag, FALSE, // don't ignore disk in drive TRUE, // allow escape TRUE, // warn about multiple prompts for same disk NULL // don't care redraw flag ); SpMemFree(szDevicePath); RedrawGauge = TRUE; if (rs == FALSE) { continue; } DevicePath = L"\\device\\floppy0"; Directory = OemSourceDirectory; MediaDir = NULL; } else { PWSTR szDescription = 0, szTagFileName = 0; BOOLEAN bDiskFound = FALSE; // // Search SourceFileName against txtsetup.sif to figure out its // media name. // MediaShortName = SpLookUpValueForFile( MasterSifHandle, SourceFileName, INDEX_WHICHMEDIA, FALSE ); if(MediaShortName) { SpGetSourceMediaInfo(MasterSifHandle,MediaShortName,NULL,NULL,&MediaDir); } else { SpNonFatalSifError( MasterSifHandle, SIF_FILESONSETUPMEDIA, SourceFileName, 0, INDEX_WHICHMEDIA, SourceFileName ); // // If we returned from SpNonFatalSifError, then the user wants to // skip the file. // RedrawGauge = TRUE; continue; } // // Prompt user to insert the source media, if changed. // SpGetSourceMediaInfo(MasterSifHandle, MediaShortName, &szDescription, &szTagFileName, NULL); // // Prompt for the disk, based on the setup media type. // bDiskFound = SpPromptForDisk( szDescription, SourceDevicePath, szTagFileName, FALSE, // don't ignore disk in drive TRUE, // don't allow escape TRUE, // warn about multiple prompts for same disk NULL // don't care redraw flag ); RedrawGauge = TRUE; // // user might have wanted to skip the file // if (!bDiskFound) continue; DevicePath = SourceDevicePath; Directory = DirectoryOnSourceDevice; } // // Copy the file. // // If the file is listed for lock smashing then we need to smash it // if installing UP on x86 (we don't bother with the latter // qualifications here). // SpCopyFileWithRetry( &FileToCopy, DevicePath, Directory, MediaDir, NULL, // TargetRoot -> NULL SystemPartitionFiles ? ATTR_RHS : 0, SppRepairScreenRepaint, NULL, // Do not want checksum NULL, // Do not want to know if file was skipped IsFileFlagSet( MasterSifHandle, FileToCopy.TargetFilename, FILEFLG_SMASHLOCKS) ? COPY_SMASHLOCKS : 0 ); } } SpMemFree(FullTargetName); if (RedrawGauge) { SppRepairScreenRepaint(NULL, NULL, TRUE); } } BOOLEAN SpDisplayRepairMenu( VOID ) /*++ Routine Description: This routine presents a list of repairable items to user and let user choose the items to be fixed among the list. Arguments: None. Return Value: None. Some global repare variables are set or cleared. --*/ { PVOID Menu; ULONG MenuTopY; ULONG ValidKeys[] = { KEY_F3, ASCI_CR, ASCI_ESC, 0 }; ULONG Keypress, MessageIds[RepairItemMax]; ULONG i; ULONG_PTR OptionChosen, InitialHighlight; PWSTR MenuItem; ULONG ListBoxWidth, curLBEntryWidth; // // Initialize repair options to repair ALL. // Initialize repair menu item message id. // for (i = 0; i < RepairItemMax; i++) { RepairItems[i] = 1; if (i == 0) { MessageIds[i] = SP_REPAIR_MENU_ITEM_1; } else { MessageIds[i] = MessageIds[i - 1] + 1; } } while(1) { // // Display the text that goes above the menu on the partitioning screen. // SpDisplayScreen(SP_SCRN_REPAIR_MENU,3,CLIENT_TOP+1); // // Calculate menu placement. Leave one blank line // and one line for a frame. // MenuTopY = NextMessageTopLine + (SplangQueryMinimizeExtraSpacing() ? 2 : 5); // // Create a menu. // First, find the longest string, so we can size the listbox accordingly // ListBoxWidth = LIST_BOX_WIDTH; // It will be at least this wide for (i = 0; i <= RepairItemMax; i++ ) { if (i == RepairItemMax) { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), SP_REPAIR_MENU_ITEM_CONTINUE); } else { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), MessageIds[i]); } if((curLBEntryWidth = SplangGetColumnCount(TemporaryBuffer)+(2*MENU_INDENT)) > ListBoxWidth) { ListBoxWidth = min(curLBEntryWidth, MENU_WIDTH); } } Menu = SpMnCreate( MENU_LEFT_X, MenuTopY, ListBoxWidth, LIST_BOX_HEIGHT ); if( !Menu ) return FALSE; ASSERT(Menu); for (i = 0; i <= RepairItemMax; i++ ) { if (i == RepairItemMax) { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), SP_REPAIR_MENU_ITEM_CONTINUE); } else { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), MessageIds[i]); (TemporaryBuffer)[1] = RepairItems[i] ? L'X' : L' '; } SpMnAddItem(Menu, TemporaryBuffer, MENU_LEFT_X+MENU_INDENT, ListBoxWidth-(2*MENU_INDENT), TRUE, i ); } InitialHighlight = RepairItemMax; // // Initialize the status line. // SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_F3_EQUALS_EXIT, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_ENTER_EQUALS_CHANGE, 0 ); DisplayMenu: // // Display the menu // SpMnDisplay( Menu, InitialHighlight, TRUE, ValidKeys, NULL, NULL, &Keypress, &OptionChosen ); // // Now act on the user's selection. // switch(Keypress) { case KEY_F3: SpConfirmExit(); break; case ASCI_CR: if (OptionChosen == RepairItemMax) { SpMnDestroy(Menu); return( TRUE ); } else { MenuItem = SpMnGetText(Menu, OptionChosen); if( !MenuItem ) goto DisplayMenu; RepairItems[OptionChosen] ^= 1; if (RepairItems[OptionChosen]) { MenuItem[1] = L'X'; } else { MenuItem[1] = L' '; } InitialHighlight = OptionChosen; goto DisplayMenu; } break; default: SpMnDestroy(Menu); return(FALSE); } SpMnDestroy(Menu); } } NTSTATUS SppRepairFile( IN PVOID MasterSifHandle, IN PWSTR TargetPath, IN PWSTR TargetFilename, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR SourceFilename, IN BOOLEAN SystemPartitionFile ) /*++ Routine Description: This routine repairs ONE file and the source of the file MUST be on emergency repair diskette or on the repair directory of the winnt being repaired. Arguments: MasterSifHandle - Hanle of the txtsetup.sif TargetPath - Supplies the target file path TargetFilename - supplies the name of the target file SourceDevicePath - supplies the NT name of the source device DirectoryOnSourceDevice - supplies the directory on the source device which contains source file. SourceFilename - supplies the name of the source file SystemPartitionFile - supplies a boolean value to indicate if the target file is on system partition Return Value: NTSTATUS of the file copy. --*/ { PWSTR szDiskName; PWSTR FullSourceFilename, FullTargetFilename; NTSTATUS Status; if (RepairFromErDisk) { // // Fetch the generic repair disk name. // SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer), SP_TEXT_REPAIR_DISK_NAME); szDiskName = SpDupStringW(TemporaryBuffer); // // Prompt for the disk -- do not ignore what may be in the drive // already, and dont allow escape. // SpPromptForDisk( szDiskName, SourceDevicePath, SETUP_LOG_FILENAME, FALSE, // if disk is in already dont prompt FALSE, // Do not allow user to cancel TRUE, // warn for multiple prompts NULL // don't care about redraw flag ); SpMemFree(szDiskName); } // // Form the name of the source and target fullname. // wcscpy(TemporaryBuffer, TargetPath); SpConcatenatePaths(TemporaryBuffer, TargetFilename); FullTargetFilename = SpDupStringW(TemporaryBuffer); wcscpy(TemporaryBuffer, SourceDevicePath); SpConcatenatePaths(TemporaryBuffer, DirectoryOnSourceDevice); SpConcatenatePaths(TemporaryBuffer, SourceFilename); FullSourceFilename = SpDupStringW(TemporaryBuffer); // // Copy the file. // // If the file is listed for lock smashing then we need to smash it // if installing UP on x86 (we don't bother with the latter // qualifications here). // Status = SpCopyFileUsingNames( FullSourceFilename, FullTargetFilename, SystemPartitionFile ? ATTR_RHS : 0, IsFileFlagSet(MasterSifHandle,TargetFilename,FILEFLG_SMASHLOCKS) ? COPY_SMASHLOCKS : 0 ); SpMemFree(FullSourceFilename); SpMemFree(FullTargetFilename); return(Status); } VOID SppRepairStartMenuGroupsAndItems( IN PWSTR WinntPartition, IN PWSTR WinntDirectory ) /*++ Routine Description: This routine loads the software hive, and set a value on Winlogon key to indicate to Winlogon that it should recreate the Start Menu groups and items for the Default User. Arguments: WinntPartition - supplies the NT name of the Winnt partition. WinntDirectory - Supplies the name of the Winnt directory. Return Value: None. --*/ { NTSTATUS Status; PWSTR p,q; PWSTR LOCAL_MACHINE_KEY_NAME = L"\\registry\\machine"; ULONG Repair = 1; PWSTR WINLOGON_KEY_NAME = L"Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; PWSTR REPAIR_VALUE_NAME = L"Repair"; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; HANDLE SoftwareKey; // // Put up a screen telling the user what we are doing. // // SpStartScreen(SP_SCRN_REPAIR_CHECK_HIVES, // 0, // 8, // TRUE, // FALSE, // DEFAULT_ATTRIBUTE // ); // // SpDisplayStatusText(SP_STAT_REG_LOADING_HIVES,DEFAULT_STATUS_ATTRIBUTE); // // Load the software hive // // // Form the name of the hive file. // This is WinntPartition + WinntDirectory + system32\config + the hive name. // p = NULL; q = NULL; wcscpy(TemporaryBuffer,WinntPartition); SpConcatenatePaths(TemporaryBuffer,WinntDirectory); SpConcatenatePaths(TemporaryBuffer,L"system32\\config\\software"); p = SpDupStringW( TemporaryBuffer ); // // Form the path of the key into which we will // load the hive. We'll use the convention that // a hive will be loaded into \registry\machine\x. // wcscpy(TemporaryBuffer,LOCAL_MACHINE_KEY_NAME); SpConcatenatePaths(TemporaryBuffer,L"x"); wcscat(TemporaryBuffer,L"software"); q = SpDupStringW( TemporaryBuffer ); if( (p == NULL) || (q == NULL) ) { goto fix_strtmenu_cleanup_1; } // // Attempt to load the hive. // Status = SpLoadUnloadKey(NULL,NULL,q,p); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to load hive %ws to key %ws (%lx)\n",p,q,Status)); goto fix_strtmenu_cleanup_1; } INIT_OBJA(&Obja,&UnicodeString,q); Status = ZwOpenKey(&SoftwareKey,KEY_ALL_ACCESS,&Obja); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to open %ws (%lx)\n",q,Status)); goto fix_strtmenu_cleanup_2; } Status = SpOpenSetValueAndClose( SoftwareKey, WINLOGON_KEY_NAME, REPAIR_VALUE_NAME, REG_DWORD, &Repair, sizeof(ULONG) ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to set value %ws on key %ws. Status = %lx\n",REPAIR_VALUE_NAME,REPAIR_VALUE_NAME,Status)); goto fix_strtmenu_cleanup_3; } Status = ZwFlushKey(SoftwareKey ); fix_strtmenu_cleanup_3: Status = ZwClose( SoftwareKey ); fix_strtmenu_cleanup_2: Status = SpLoadUnloadKey(NULL,NULL,q,NULL); fix_strtmenu_cleanup_1: if( p != NULL ) { SpMemFree( p ); } if( q != NULL ) { SpMemFree( q ); } } VOID SppInspectHives( IN PWSTR PartitionPath, IN PWSTR SystemRoot, OUT PULONG HiveLoaded, IN PWSTR *HiveNames ) /*++ Routine Description: This routine inspects setup hives by loading and unloading them and returns the loadable information in HiveLoaded[]. Arguments: PartitionPath - supplies the NT name of the Winnt partition. SystemRoot - Supplies the name of the Winnt System root. HiveLoaded - Supplies a pointer to a ULONG array to receive the loadable information for each hive inspected. HIveNames - Supplies a pointer to a PWSTR array to receive the name of hives to inspect. Return Value: None. HiveLoaded array initialized. --*/ { NTSTATUS Status; PWSTR pwstrTemp1,pwstrTemp2; int h; PWSTR LOCAL_MACHINE_KEY_NAME = L"\\registry\\machine"; // // Put up a screen telling the user what we are doing. // SpStartScreen(SP_SCRN_REPAIR_CHECK_HIVES, 0, 8, TRUE, FALSE, DEFAULT_ATTRIBUTE ); SpDisplayStatusText(SP_STAT_REG_LOADING_HIVES,DEFAULT_STATUS_ATTRIBUTE); // // Load each template hive we care about from the target tree. // for (h = 0; h < RepairHiveMax; h++) { pwstrTemp1 = TemporaryBuffer; pwstrTemp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); if( h != RepairHiveUser ) { // // Form the name of the hive file. // This is partitionpath + sysroot + system32\config + the hive name. // wcscpy(pwstrTemp1,PartitionPath); SpConcatenatePaths(pwstrTemp1,SystemRoot); SpConcatenatePaths(pwstrTemp1,L"system32\\config"); SpConcatenatePaths(pwstrTemp1,HiveNames[h]); } else { wcscpy(pwstrTemp1,PartitionPath); SpConcatenatePaths(pwstrTemp1,DEFAULT_USER_PATH); SpConcatenatePaths(pwstrTemp1,HiveNames[h]); } // // First we must verify that the hive file exists. We have to do // this because loading a hive will create one if it didn't already // exist! // if(!SpFileExists(pwstrTemp1, FALSE)) { HiveLoaded[h] = 0; continue; } // // Form the path of the key into which we will // load the hive. We'll use the convention that // a hive will be loaded into \registry\machine\x. // wcscpy(pwstrTemp2,LOCAL_MACHINE_KEY_NAME); SpConcatenatePaths(pwstrTemp2,L"x"); wcscat(pwstrTemp2,HiveNames[h]); // // Attempt to load the hive. // HiveLoaded[h] = 0; Status = SpLoadUnloadKey(NULL,NULL,pwstrTemp2,pwstrTemp1); if (NT_SUCCESS(Status) || Status == STATUS_NO_MEMORY) { // // If the reason the hive did not load is because of not // enough memory. We assume the hive is OK. // HiveLoaded[h] = 1; // // Unload the hive. // SpLoadUnloadKey(NULL,NULL,pwstrTemp2,NULL); } } // // Sam and security hives must be updated together. If any one of // them failed to load, we must update both. // if ((HiveLoaded[RepairHiveSecurity] == 0) || (HiveLoaded[RepairHiveSam] == 0)) { HiveLoaded[RepairHiveSam] = 0; HiveLoaded[RepairHiveSecurity] = 0; } } VOID SppRepairHives( PVOID MasterSifHandle, PWSTR WinntPartition, PWSTR WinntPartitionDirectory, PWSTR SourceDevicePath, PWSTR DirectoryOnSourceDevice ) /*++ Routine Description: This routine inspects hives and let user choose the hives which he wants to repair. Arguments: MasterSifHandle - The handle of textsetup.sif WinntPartition - The nt name of Winnt partition WinntPartitionDirectory - The directory name of winnt installation SourceDevicePath - The NT name of source device which contains hives DirectoryOnSourceDevice - The directory name of source device Return Value: None. --*/ { // // Do not change the order of the files in 'HiveNames' array. // If you do that, you also need to change the order of the // enum 'RepairHive' in spntfix.h // PWSTR HiveNames[RepairHiveMax] = { L"system",L"software",L"default",L"ntuser.dat",L"security",L"sam"}; ULONG HiveLoaded[RepairHiveMax]; PVOID Menu; ULONG MenuTopY; ULONG ValidKeys[] = { KEY_F3, ASCI_CR, 0 }; ULONG ValidKeys1[] = { KEY_F3, ASCI_CR, 0 }; ULONG i; ULONG_PTR InitialHighlight, OptionChosen; PWSTR MenuItem, TargetPath, p; ULONG Keypress, MessageIds[RepairHiveMax]; BOOLEAN Selectable; NTSTATUS Status; ULONG ListBoxWidth, curLBEntryWidth; BOOLEAN DetermineHivesToRepair; // // Inspect hives by loading hives to determine which hives need to be // fixed. // SppInspectHives(WinntPartition, WinntPartitionDirectory, HiveLoaded, HiveNames); // BCL - Seagate: If doing ASR, don't do the menu. if ( SpDrEnabled() ) { goto UpdateTheHives; } // // Initialize hive menu item message id. // for (i = 0; i < RepairHiveMax; i++) { if (i == 0) { MessageIds[i] = SP_REPAIR_HIVE_ITEM_1; } else { MessageIds[i] = MessageIds[i - 1] + 1; } } DetermineHivesToRepair = TRUE; while(DetermineHivesToRepair) { // // Display the text that goes above the menu on the partitioning screen. // SpDisplayScreen(SP_SCRN_REPAIR_HIVE_MENU,3,CLIENT_TOP+1); // // Calculate menu placement. Leave one blank line // and one line for a frame. // MenuTopY = NextMessageTopLine + (SplangQueryMinimizeExtraSpacing() ? 2 : 5); // // Create a menu. // First, find the longest string, so we can size the listbox accordingly // ListBoxWidth = HIVE_LIST_BOX_WIDTH; // It will be at least this wide for (i = 0; i <= RepairHiveMax; i++ ) { if (i == RepairHiveMax) { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), SP_REPAIR_MENU_ITEM_CONTINUE); } else { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), MessageIds[i]); } if((curLBEntryWidth = SplangGetColumnCount(TemporaryBuffer)+(2*MENU_INDENT)) > ListBoxWidth) { ListBoxWidth = min(curLBEntryWidth, MENU_WIDTH); } } Menu = SpMnCreate( MENU_LEFT_X, MenuTopY, ListBoxWidth, HIVE_LIST_BOX_HEIGHT ); ASSERT(Menu); // // Build up a menu of hives // for (i = 0; i <= RepairHiveMax; i++ ) { if (i == RepairHiveSam) { Selectable = FALSE; } else { Selectable = TRUE; } if (i == RepairHiveMax) { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), SP_REPAIR_MENU_ITEM_CONTINUE); } else { SpFormatMessage(TemporaryBuffer, sizeof(TemporaryBuffer), MessageIds[i]); p = TemporaryBuffer; if (HiveLoaded[i] || ( i == RepairHiveSam )) { p[1] = L' '; } else { p[1] = L'X'; } } SpMnAddItem(Menu, TemporaryBuffer, MENU_LEFT_X+MENU_INDENT, ListBoxWidth-(2*MENU_INDENT), Selectable, i ); } InitialHighlight = RepairHiveMax; // // Initialize the status line. // SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CHANGE, SP_STAT_F3_EQUALS_EXIT, 0 ); DisplayMenu: // // Display the menu // SpMnDisplay( Menu, InitialHighlight, TRUE, ValidKeys, NULL, NULL, &Keypress, &OptionChosen ); // // Now act on the user's selection. // switch(Keypress) { case KEY_F3: SpConfirmExit(); break; case ASCI_CR: if (OptionChosen == RepairHiveMax) { SpMnDestroy(Menu); DetermineHivesToRepair = FALSE; } else { HiveLoaded[OptionChosen] ^= 1; MenuItem = SpMnGetText(Menu, OptionChosen); if ((HiveLoaded[OptionChosen] != 0) || (OptionChosen == RepairHiveSam)){ MenuItem[1] = L' '; } else { MenuItem[1] = L'X'; } // // Security and sam must go together. // HiveLoaded[RepairHiveSam] = HiveLoaded[RepairHiveSecurity]; InitialHighlight = OptionChosen; goto DisplayMenu; } break; } } UpdateTheHives: // // At this point user has decided which hives to repair. // We will copy the hives from repair disk to // Winnt\system32\config directory. // for (i = 0; i < RepairHiveMax; i++ ) { // BCL - Seagate: Don't do ntuser.dat. As of 4/17/98, there is no // copy of this file to copy from. if ( SpDrEnabled() && i == RepairHiveUser ) { continue; } if (HiveLoaded[i] == 0) { // // Form Target path // if( i != RepairHiveUser ) { wcscpy(TemporaryBuffer, WinntPartition); SpConcatenatePaths(TemporaryBuffer, WinntPartitionDirectory); SpConcatenatePaths(TemporaryBuffer, L"\\SYSTEM32\\CONFIG"); TargetPath = SpDupStringW(TemporaryBuffer); } else { wcscpy(TemporaryBuffer, WinntPartition); SpConcatenatePaths(TemporaryBuffer, WinntPartitionDirectory); SpConcatenatePaths(TemporaryBuffer, DEFAULT_USER_PATH); TargetPath = SpDupStringW(TemporaryBuffer); } Status = SppRepairFile(MasterSifHandle, TargetPath, HiveNames[i], SourceDevicePath, DirectoryOnSourceDevice, HiveNames[i], FALSE ); if (!NT_SUCCESS(Status)) { // // Tell user we couldn't do it. Options are to continue or exit. // SpStartScreen( SP_SCRN_REPAIR_HIVE_FAIL, 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(ValidKeys1,NULL,NULL)) { case ASCI_CR: return; break; case KEY_F3: SpConfirmExit(); break; } } SpMemFree(TargetPath); } } } VOID SpRepairWinnt( IN PVOID LogFileHandle, IN PVOID MasterSifHandle, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice ) /*++ Routine Description: This is a the top level repair rutine. It calls worker routines for each repair options that user selected. Arguments: LogFileHandle - Handle of the setup.log MasterSifHandle - Handle of the txtsetup.sif SourceDevicePath - The NT name for the repair source device. DirectoryOnSourceDevice - The directory name on the repair source device which contains the source files. Return Value: None. --*/ { PWSTR SystemPartition, SystemPartitionDirectory; PWSTR WinntPartition, WinntPartitionDirectory; PWSTR HiveRepairSourceDevice, DirectoryOnHiveRepairSource; // // Initialize the diamond decompression engine. // SpdInitialize(); // // Determine SystemPartition, SystemPartitionDirectory. // WinntParition and WinntPartitionDirectory of the WINNT // installation to be repaired. // SppGetRepairPathInformation(LogFileHandle, &SystemPartition, &SystemPartitionDirectory, &WinntPartition, &WinntPartitionDirectory ); // // If repair involves disk access, then run autochk on Nt and system // partitions. // if( RepairItems[RepairFiles] #ifdef _X86_ || RepairItems[RepairNvram] #endif ) { PDISK_REGION SystemPartitionRegion; PDISK_REGION WinntPartitionRegion; WinntPartitionRegion = SpRegionFromNtName( WinntPartition, PartitionOrdinalCurrent); SystemPartitionRegion = SpRegionFromNtName( SystemPartition, PartitionOrdinalCurrent); if( !RepairNoCDROMDrive ) { // // If we know that the system doesn't have a CD-ROM drive, // then don't even attempt to run autochk. // SpRunAutochkOnNtAndSystemPartitions( MasterSifHandle, WinntPartitionRegion, SystemPartitionRegion, SourceDevicePath, DirectoryOnSourceDevice, NULL ); } } // // Verify and repair security of the directories that form the NT tree // This needs to be done before repairing the hives because the // system32\config directory might not be there anymore! // SppVerifyAndRepairNtTreeAccess(MasterSifHandle, WinntPartition, WinntPartitionDirectory, SystemPartition, SystemPartitionDirectory ); #if 0 // BCL - Seagate - the RepairHives member has been removed from the // struct if (RepairItems[RepairHives]) { // // User has selected to repair hives. If user has provided the // ER disk, we will copy the hive from ER disk to repair damaged // hives. Otherwise we copy the hive from the directory where // setup.log was loaded. // if (RepairFromErDisk) { HiveRepairSourceDevice = L"\\device\\floppy0"; DirectoryOnHiveRepairSource = L""; } else { HiveRepairSourceDevice = WinntPartition; wcscpy(TemporaryBuffer, WinntPartitionDirectory); SpConcatenatePaths(TemporaryBuffer, SETUP_REPAIR_DIRECTORY); DirectoryOnHiveRepairSource = SpDupStringW(TemporaryBuffer); } SppRepairHives(MasterSifHandle, WinntPartition, WinntPartitionDirectory, HiveRepairSourceDevice, DirectoryOnHiveRepairSource ); if (!RepairFromErDisk) { SpMemFree(DirectoryOnHiveRepairSource); } } if (RepairItems[RepairFiles]) { SppRepairWinntFiles(LogFileHandle, MasterSifHandle, SourceDevicePath, DirectoryOnSourceDevice, SystemPartition, SystemPartitionDirectory, WinntPartition, WinntPartitionDirectory ); } #endif // // The code to repair nvram variables and boot sector is // incorporated into SpStartSetup. // // // Load the software hive, and and set the repair flag under Winlogon, // so that winlogon can recreate the start menu groups and items for // the default user. // SppRepairStartMenuGroupsAndItems( WinntPartition, WinntPartitionDirectory ); // // Terminate diamond. // SpdTerminate(); } VOID SppVerifyAndRepairNtTreeAccess( IN PVOID MasterSifHandle, IN PWSTR TargetDevicePath, IN PWSTR DirectoryOnTargetDevice, IN PWSTR SystemPartition, IN PWSTR SystemPartitionDirectory ) /*++ Routine Description: This routine examines whether or not the directories that form the NT tree are accessible, and set the appropriate security descriptor in each directory, when necessary. Arguments: MasterSifHandle - Hanle of the txtsetup.sif TargetDevicePath - supplies the nt name of the target device DirectoryOnTargetDevice - the name of the winnt directory on target device SystemPartition - supplies the nt name of the target device (non-x86 platforms) SystemPartitionDirectory - the name of the winnt directory on target device (non-x86 platforms) Return Value: None. --*/ { ULONG Count, i; PWSTR SectionName = L"WinntDirectories"; PWSTR DirectoryName; PWSTR TargetPath; PWSTR WinNtDirectory; NTSTATUS Status; SpDisplayStatusText(SP_STAT_SETUP_IS_EXAMINING_DIRS, DEFAULT_STATUS_ATTRIBUTE); if(SpIsArc()){ // // Make sure that on ARC platforms, the system partition directory // exists (re-create it if it doesn't exist) // SpCreateDirectory(SystemPartition,NULL,SystemPartitionDirectory,0,0); } WinNtDirectory = ( PWSTR )SpMemAlloc( ( wcslen( TargetDevicePath ) + 1 + wcslen( DirectoryOnTargetDevice ) + 1 + 1 )*sizeof( WCHAR ) ); TargetPath = ( PWSTR )SpMemAlloc( 1024 ); if( ( WinNtDirectory == NULL ) || ( TargetPath == NULL ) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to allocate memory for WinNtDirectory \n")); if( WinNtDirectory != NULL ) { SpMemFree( WinNtDirectory ); } if( TargetPath != NULL ) { SpMemFree( TargetPath ); } return; } wcscpy( WinNtDirectory, TargetDevicePath ); SpConcatenatePaths( WinNtDirectory, DirectoryOnTargetDevice ); Count = SpCountLinesInSection(MasterSifHandle, SectionName); // // Note that in the loop below, the maximum value for 'i' is 'Count' // instead of 'Count-1'. This is because we need to create the directory // 'Profiles\\Default User' which cannot be listed in txtsetup.sif. // This is due to pre-install requirements, and DOS limitation regarding // long file names. // for (i = 0; i <= Count; i++) { if( i != Count ) { DirectoryName = SpGetSectionLineIndex(MasterSifHandle,SectionName,i,0); } else { // // Due to pre-installation requirements, and DOS limitation // regarding long file names, the "Default User" directory // is not specified on txtsetup.sif, as the other directories. // This directory is treated as a special case in the // repair process. // DirectoryName = DEFAULT_USER_PATH; } if(!DirectoryName) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); continue; } wcscpy( TargetPath, WinNtDirectory ); // // Make sure that TargetPath doesn't contain '\' as the last character // if(!((DirectoryName[0] == L'\\') && (DirectoryName[1] == 0))) { SpConcatenatePaths( TargetPath, DirectoryName ); } Status = SpVerifyFileAccess( TargetPath, STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | FILE_TRAVERSE | WRITE_DAC | SYNCHRONIZE ); // // If unable to access the directory, try to determine why. // If it is because of access denied, change the directory security. // If it is because the directory doesn't exist, then create it. // if( !NT_SUCCESS( Status ) ) { if ((Status == STATUS_ACCESS_DENIED)||(Status == STATUS_PRIVILEGE_NOT_HELD) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Security of %ls, must be fixed. Status = %x\n", TargetPath, Status )); Status = SpSetDefaultFileSecurity( TargetPath ); if( !NT_SUCCESS( Status ) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to change security of %ls. Status = %x\n", TargetPath, Status )); } } else if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) { if(((DirectoryName[0] == L'\\') && (DirectoryName[1] == 0))) { // // Create the target directory // SpCreateDirectory( TargetDevicePath, NULL, DirectoryOnTargetDevice, 0, 0); } else { SpCreateDirectory( TargetDevicePath, DirectoryOnTargetDevice, DirectoryName, 0, 0); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to access directory %ls. Status = %x\n", TargetPath, Status )); } } } if( WinNtDirectory != NULL ) { SpMemFree( WinNtDirectory ); } if( TargetPath != NULL ) { SpMemFree( TargetPath ); } return; } VOID SppVerifyAndRepairVdmFiles( IN PVOID LogFileHandle, IN PWSTR TargetDevicePath, IN PWSTR DirectoryOnTargetDevice, IN PBOOLEAN RepairWithoutConfirming ) /*++ Routine Description: This routine repairs the Vdm configuration files listed on 'Files.InRepairDirectory' of setup.log. Currently, such files are: autoexec.nt and config.nt. It is assumed that files in this section will be copied from the emergency repair disk, or from the repair directory. Arguments: LogFileHandle - Handle of the setup.log TargetDevicePath - supplies the nt name of the target device DirectoryOnTargetDevice - the name of the winnt directory on target device RepairWithoutConfirming - Pointer to a flag that indicates whether or not setup should repair files without confirming with the user. Return Value: None. --*/ { PWSTR FullTargetName, ChecksumString; PWSTR TargetDirectory, TargetFileName; PWSTR SourceFileName; ULONG Checksum, FileChecksum, PrefixLength, Length, Count, i; BOOLEAN IsNtImage, IsValid, RepairFile; BOOLEAN RedrawGauge = TRUE; FILE_TO_COPY FileToCopy; PWSTR DevicePath, Directory; PWSTR SectionName = SIF_NEW_REPAIR_FILES_IN_REPAIR_DIR; // // Allocate a SMALL buffer for local use and init FileToCopy struct // TargetDirectory = NULL; FullTargetName = SpMemAlloc(1024); *FullTargetName = 0; FileToCopy.Next = NULL; FileToCopy.Flags = COPY_ALWAYS; FileToCopy.AbsoluteTargetDirectory = TRUE; FileToCopy.TargetDevicePath = TargetDevicePath; SpConcatenatePaths(FullTargetName,TargetDevicePath); PrefixLength = wcslen(FullTargetName); Count = SpCountLinesInSection(LogFileHandle,SectionName); for (i = 0; i < Count; i++) { if (RedrawGauge) { SppRepairScreenRepaint(NULL, NULL, TRUE); RedrawGauge = FALSE; } SpTickGauge(RepairGauge); // // Initialize target fullname to be DevicePath+Directory for // system partition file or DevicePath for Winnt files // FullTargetName[PrefixLength] = (WCHAR)NULL; // // If we allocate space for TargetDirectory we must free it. // if (TargetDirectory) { SpMemFree(TargetDirectory); TargetDirectory = NULL; } TargetFileName = SpGetKeyName(LogFileHandle,SectionName,i); if(!TargetFileName) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } SpConcatenatePaths(FullTargetName,TargetFileName); SpDisplayStatusText(SP_STAT_EXAMINING_WINNT, DEFAULT_STATUS_ATTRIBUTE, TargetFileName); ChecksumString = SpGetSectionLineIndex(LogFileHandle,SectionName,i,1); if(!ChecksumString) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } Checksum = (ULONG)SpStringToLong(ChecksumString, NULL, 16); SpValidateAndChecksumFile(NULL,FullTargetName,&IsNtImage,&FileChecksum,&IsValid); // // If the image is invalid or the file on the target is not the // original file copied by setup, we will recopy it. // if (!IsValid || FileChecksum != Checksum) { // // Ask user if he wants to repair the file // RepairFile = ( *RepairWithoutConfirming )? TRUE : SppRepairReportError( TRUE, SP_SCRN_REPAIR_FILE_MISMATCH, SP_TEXT_REPAIR_INF_ERROR_4, TargetFileName, i, RepairWithoutConfirming); RedrawGauge = TRUE; if (!RepairFile) { continue; } SpDisplayStatusText(SP_STAT_REPAIR_WINNT, DEFAULT_STATUS_ATTRIBUTE, TargetFileName); // // TargetName contains path and filename. // We need to seperate them. // TargetDirectory = SpDupStringW(TargetFileName); Length = wcslen(TargetDirectory); while (Length) { if (TargetDirectory[Length] == L'\\') { TargetDirectory[Length] = 0; TargetFileName = &TargetDirectory[Length + 1]; break; } else { Length--; } } if (Length == 0) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } FileToCopy.TargetFilename = TargetFileName; FileToCopy.TargetDirectory = TargetDirectory; SourceFileName = SpGetSectionLineIndex(LogFileHandle,SectionName,i,0); if (!SourceFileName) { SppRepairReportError(FALSE, SP_SCRN_REPAIR_INF_ERROR_0, SP_TEXT_REPAIR_INF_ERROR_1, SectionName, i, NULL); RedrawGauge = TRUE; continue; } FileToCopy.SourceFilename = SourceFileName; // // Find out whether the source file should come from the // Emergency Repair Disk or the Repair directory // if (RepairFromErDisk) { BOOLEAN rs; PWSTR szDiskName; PWSTR szDevicePath = SpDupStringW(L"\\device\\floppy0"); // // Fetch the generic repair disk name. // SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer), SP_TEXT_REPAIR_DISK_NAME); szDiskName = SpDupStringW(TemporaryBuffer); // // Prompt for the disk, based on the setup media type. // rs = SpPromptForDisk( szDiskName, szDevicePath, SETUP_LOG_FILENAME, FALSE, // if disk is in already dont prompt FALSE, // allow escape TRUE, // warn for multiple prompts NULL // don't care about redraw flag ); SpMemFree(szDiskName); SpMemFree(szDevicePath); RedrawGauge = TRUE; if (rs == FALSE) { continue; } DevicePath = L"\\device\\floppy0"; wcscpy( TemporaryBuffer, L"\\" ); Directory = SpDupStringW(TemporaryBuffer); // OemSourceDirectory; } else { RedrawGauge = TRUE; DevicePath = TargetDevicePath; wcscpy( TemporaryBuffer, DirectoryOnTargetDevice ); SpConcatenatePaths( TemporaryBuffer, SETUP_REPAIR_DIRECTORY ); Directory = SpDupStringW(TemporaryBuffer); } // // Copy the file. // SpCopyFileWithRetry( &FileToCopy, DevicePath, Directory, NULL, NULL, // TargetRoot -> NULL 0, // SystemPartitionFiles ? ATTR_RHS : 0, SppRepairScreenRepaint, NULL, // Do not want checksum NULL, // Do not want to know if file was skipped 0 ); SpMemFree( Directory ); } } SpMemFree(FullTargetName); if (RedrawGauge) { SppRepairScreenRepaint(NULL, NULL, TRUE); } }