|
|
#include "spprecmp.h"
#pragma hdrstop
/*++
Revision History Michael Peterson (Seagate Software) + Modified SpIsNtInDirectory() so that it always returns FALSE if DR is in effect. --*/ PWSTR *NtDirectoryList; ULONG NtDirectoryCount;
BOOLEAN SpNFilesExist( IN OUT PWSTR PathName, IN PWSTR *Files, IN ULONG FileCount, IN BOOLEAN Directories ) { UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; HANDLE Handle; ULONG i; PWSTR FilenamePart; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status;
//
// No reason to call this routine to check for 0 files.
//
ASSERT(FileCount);
//
// Stick a backslash on the end of the path part if necessary.
//
SpConcatenatePaths(PathName,L""); FilenamePart = PathName + wcslen(PathName);
//
// Check each file. If any one of then doesn't exist,
// then return FALSE.
//
for(i=0; i<FileCount; i++) {
//
// Restore PathName and concatenate the new filename
//
*FilenamePart = L'\0'; SpConcatenatePaths(PathName, Files[i]);
INIT_OBJA(&Obja,&UnicodeString,PathName);
Status = ZwCreateFile( &Handle, FILE_READ_ATTRIBUTES, &Obja, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_OPEN_REPARSE_POINT | (Directories ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE), NULL, 0 );
if(NT_SUCCESS(Status)) { ZwClose(Handle); } else { *FilenamePart = 0; return(FALSE); } }
//
// All exist. Return TRUE.
//
*FilenamePart = 0; return(TRUE); }
BOOLEAN SpIsNtInDirectory( IN PDISK_REGION Region, IN PWSTR Directory )
/*++
Routine Description:
Determine whether Windows NT is present on a partition in one of a set of given directories. This determination is based on the presence of certain windows nt system files and directories.
Arguments:
Region - supplies the region descriptor for the partition to check.
Directory - supplies the path to check for a windows nt installation.
Return Value:
TRUE if we think we've found Windows NT in the given directory on the given partition.
--*/
{ PWSTR NTDirectories[3] = { L"system32", L"system32\\drivers", L"system32\\config" }; PWSTR NTFiles[2] = { L"system32\\ntoskrnl.exe", L"system32\\ntdll.dll" }; PWSTR PaeNTFiles[2] = { L"system32\\ntkrnlpa.exe", L"system32\\ntdll.dll" }; PWSTR OpenPath; BOOLEAN rc;
if( SpDrEnabled() && ! RepairWinnt ) { return( FALSE ); }
OpenPath = SpMemAlloc(1024);
//
// Place the fixed part of the name into the buffer.
//
SpNtNameFromRegion( Region, OpenPath, 1024, PartitionOrdinalCurrent );
SpConcatenatePaths(OpenPath,Directory);
if(SpNFilesExist(OpenPath, NTDirectories, ELEMENT_COUNT(NTDirectories), TRUE) && (SpNFilesExist(OpenPath, NTFiles, ELEMENT_COUNT(NTFiles), FALSE) || SpNFilesExist(OpenPath, PaeNTFiles, ELEMENT_COUNT(PaeNTFiles), FALSE))) { rc = TRUE; } else { rc = FALSE; }
SpMemFree(OpenPath); return(rc); }
ULONG SpRemoveInstallation( IN PDISK_REGION Region, IN PWSTR PartitionPath, IN PWSTR Directory ) { HANDLE Handle; NTSTATUS Status; PWSTR FileName; ULONG Space = 0; ULONG ClusterSize; ULONG bps; PVOID Gauge; PWSTR Filename; ULONG FileCount; ULONG FileSize; ULONG i; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; IO_STATUS_BLOCK IoStatusBlock; ULONG ErrLine; PVOID Inf; BOOLEAN OldFormatSetupLogFile; PWSTR SectionName; HANDLE TempHandle; ULONG RootDirLength; PUCHAR UBuffer; PUCHAR Buffer;
FileName = SpMemAlloc(1024);
//
// Fetch the number of bytes in a sector.
//
bps = HardDisks[Region->DiskNumber].Geometry.BytesPerSector;
//
// Get cluster size from the BPB.
//
ASSERT(Region->Filesystem >= FilesystemFirstKnown);
Status = SpOpenPartition( HardDisks[Region->DiskNumber].DevicePath, SpPtGetOrdinal(Region,PartitionOrdinalCurrent), &Handle, FALSE );
if(!NT_SUCCESS(Status)) { goto xx0; }
UBuffer = SpMemAlloc(2*bps); Buffer = ALIGN(UBuffer,bps); Status = SpReadWriteDiskSectors( Handle, 0, 1, bps, Buffer, FALSE );
if(!NT_SUCCESS(Status)) { ZwClose(Handle); SpMemFree(UBuffer); goto xx0; }
//
// Make sure this sector appears to hold a valid boot sector
// for a hard disk.
//
// "55AA" was not presented by DOS 5.0 for NEC98,
// so must not to check "55aa" in BPB,
//
if(((!IsNEC_98) && ((Buffer[510] == 0x55) && (Buffer[511] == 0xaa) && (Buffer[21] == 0xf8))) || ((IsNEC_98) && (Buffer[21] == 0xf8))) { //NEC98
//
// bps * spc.
//
ClusterSize = (ULONG)U_USHORT(Buffer+11) * (ULONG)Buffer[13];
} else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRemoveInstallation: sector 0 on %ws is invalid\n",PartitionPath)); Status = STATUS_UNSUCCESSFUL; }
ZwClose(Handle); SpMemFree(UBuffer);
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRemoveInstallation: can't get cluster size on %ws\n",PartitionPath)); goto xx0; }
//
// Find out if the repair directory exists, if it does exist load
// setup.log from the repair directory. Otherwise, load setup.log
// from the WinNt directory
//
wcscpy(FileName,PartitionPath); SpConcatenatePaths(FileName,Directory); RootDirLength = wcslen(FileName);
SpConcatenatePaths(FileName,SETUP_REPAIR_DIRECTORY); INIT_OBJA( &Obja, &UnicodeString, FileName ); Status = ZwOpenFile( &TempHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
if( !NT_SUCCESS( Status ) ) { FileName[ RootDirLength ] = L'\0'; } else { ZwClose( TempHandle ); }
SpConcatenatePaths(FileName,SETUP_LOG_FILENAME);
//
// Load setup.log from the given path.
//
Status = SpLoadSetupTextFile(FileName,NULL,0,&Inf,&ErrLine,TRUE,FALSE); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRemoveInstallation: can't load inf file %ws (%lx)\n",FileName,Status));
while(1) { ULONG ks[2] = { ASCI_CR, 0 };
SpStartScreen( SP_SCRN_CANT_LOAD_SETUP_LOG, 3, HEADER_HEIGHT+2, FALSE, FALSE, DEFAULT_ATTRIBUTE, FileName + wcslen(PartitionPath) // skip \device\harddiskx\partitiony
);
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 );
switch(SpWaitValidKey(ks,NULL,NULL)) { case ASCI_CR: goto xx0; } } }
//
// Go through all files in the [Repair.WinntFiles] section
//
SpStartScreen( SP_SCRN_WAIT_REMOVING_NT_FILES, 0, 8, TRUE, FALSE, DEFAULT_ATTRIBUTE );
//
// Determine whether setup.log has the new or old style
//
if( OldFormatSetupLogFile = !IsSetupLogFormatNew( Inf ) ) { SectionName = SIF_REPAIRWINNTFILES; } else { SectionName = SIF_NEW_REPAIR_WINNTFILES; }
FileCount = SpCountLinesInSection(Inf,SectionName);
SpFormatMessage( TemporaryBuffer, sizeof(TemporaryBuffer), SP_TEXT_SETUP_IS_REMOVING_FILES );
Gauge = SpCreateAndDisplayGauge( FileCount, 0, VideoVars.ScreenHeight - STATUS_HEIGHT - (3*GAUGE_HEIGHT/2), TemporaryBuffer, NULL, GF_PERCENTAGE, 0 );
//
// Clear the status area.
//
SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,0);
//
// Set the status text in the lower right portion of the screen
// to "Removing:" in preparation for displaying filenames as
// files are deleted. The 12 is for an 8.3 name.
//
SpDisplayStatusActionLabel(SP_STAT_REMOVING,12);
for(i=0; i<FileCount; i++) {
if( OldFormatSetupLogFile ) { Filename = SpGetSectionLineIndex(Inf,SectionName,i,1); } else { Filename = SpGetKeyName(Inf,SectionName,i); } if(Filename) {
PWCHAR p = wcsrchr(Filename,L'\\');
if(p) { p++; } else { p = Filename; }
#ifdef _X86_
{ //
// Don't remove files in the system directory.
// We might have installed into the windows directory
// so removing files in the system directory would
// wipe out the user's fonts (which are shared between
// 3.1 and nt).
//
PWSTR dup = SpDupStringW(Filename); SpStringToLower(dup); if(wcsstr(dup,L"\\system\\")) { SpMemFree(dup); SpTickGauge(Gauge); continue; } SpMemFree(dup); } #endif
SpDisplayStatusActionObject(p);
//
// Form the full pathname of the file being deleted.
//
wcscpy(FileName,PartitionPath); SpConcatenatePaths(FileName,Filename);
//
// Open the file.
//
INIT_OBJA(&Obja,&UnicodeString,FileName);
Status = ZwCreateFile( &Handle, FILE_READ_ATTRIBUTES, &Obja, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, // open if exists
FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRemoveInstallation: unable to open %ws (%lx)\n",FileName,Status)); } else {
//
// Get the file size.
//
Status = SpGetFileSize(Handle,&FileSize); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpRemoveInstallation: unable to get %ws file size (%lx)\n",FileName,Status)); FileSize = 0; } else {
//
// Add the size of this file to the running total.
//
if(FileSize % ClusterSize) {
FileSize += ClusterSize - (FileSize % ClusterSize); }
Space += FileSize; }
ZwClose(Handle);
//
// Delete the file
//
Status = SpDeleteFile(FileName,NULL,NULL); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to delete %ws (%lx)\n",FileName,Status)); Space -= FileSize; } } }
SpTickGauge(Gauge); }
SpFreeTextFile(Inf);
SpDestroyGauge(Gauge);
SpDisplayStatusActionLabel(0,0);
xx0:
SpMemFree(FileName); return(Space); }
BOOLEAN SpIsNtOnPartition( IN PDISK_REGION Region )
/*++
Routine Description:
Determine whether there is any Windows NT installed on a given partition.
Arguments:
PartitionPath - supplies NT path to partition on which we should look for NT installations.
Return Value:
TRUE if any NT installations were found. FALSE if not.
--*/
{ ULONG i;
SpGetNtDirectoryList(&NtDirectoryList,&NtDirectoryCount);
for(i=0; i<NtDirectoryCount; i++) { if(SpIsNtInDirectory(Region,NtDirectoryList[i])) { return(TRUE); } }
return(FALSE); }
BOOLEAN SpAllowRemoveNt( IN PDISK_REGION Region, IN PWSTR DriveSpec, OPTIONAL IN BOOLEAN RescanForNTs, IN ULONG ScreenMsgId, OUT PULONG SpaceFreed )
/*++
Routine Description:
Arguments:
ScreenMsgId - supplies the message id of the text that will be printed above the menu of located nt directories, to supply instructions, etc.
SpaceFreed - receives amount of disk space created by removing a Windows NT tree, if this function returns TRUE.
Return Value:
TRUE if any files were actually removed. FALSE otherwise.
If an error occured, the user will have already been told about it.
--*/
{ ULONG i; ULONG NtCount; PULONG MenuOrdinals; PWSTR *MenuItems; PWSTR *MenuTemp; BOOLEAN rc,b; BOOLEAN Add; ULONG MenuWidth,MenuLeftX; PVOID Menu; PWSTR PartitionPath;
CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_EXAMINING_DISK_CONFIG,DEFAULT_STATUS_ATTRIBUTE);
PartitionPath = SpMemAlloc(512);
//
// Form the nt pathname for this partition.
//
SpNtNameFromRegion( Region, PartitionPath, 512, PartitionOrdinalCurrent );
//
// Assume nothing deleted.
//
rc = FALSE;
//
// Go look for Windows NT installations.
//
if(RescanForNTs) { SpGetNtDirectoryList(&NtDirectoryList,&NtDirectoryCount); }
if(!NtDirectoryCount) { goto xx0; }
//
// Determine whether any of the NT trees we found are
// on the given partition, and build an association between
// NT trees and their ordinal positions in the menu we will
// present to the user, and the menu itself.
//
NtCount = 0; MenuOrdinals = SpMemAlloc((NtDirectoryCount+1)*sizeof(ULONG)); MenuItems = SpMemAlloc((NtDirectoryCount+1)*sizeof(PWSTR));
//
// Eliminate potential duplicate entries in the menu
// to be presented to the user.
//
MenuTemp = SpMemAlloc(NtDirectoryCount*sizeof(PWSTR)); for(i=0; i<NtDirectoryCount; i++) {
WCHAR FullName[128]; ULONG j;
_snwprintf( FullName, (sizeof(FullName)/sizeof(WCHAR))-1, L"%s%s", DriveSpec ? DriveSpec : L"", NtDirectoryList[i] );
FullName[(sizeof(FullName)/sizeof(WCHAR))-1] = 0;
//
// If the name is not already in the list, then add it.
//
for(Add=TRUE,j=0; Add && (j<i); j++) { if(MenuTemp[j] && !_wcsicmp(FullName,MenuTemp[j])) { Add = FALSE; } }
MenuTemp[i] = Add ? SpDupStringW(FullName) : NULL; }
//
// Construct the menu to be presented to the user by looking in the
// list of directories constructed above.
//
for(i=0; i<NtDirectoryCount; i++) {
if(MenuTemp[i] && SpIsNtInDirectory(Region,NtDirectoryList[i])) {
MenuOrdinals[NtCount] = i; MenuItems[NtCount] = SpDupStringW(MenuTemp[i]); NtCount++; } }
for(i=0; i<NtDirectoryCount; i++) { if(MenuTemp[i]) { SpMemFree(MenuTemp[i]); } } SpMemFree(MenuTemp);
//
// If we found any nt directories on this partition,
// make a menu to present to the user. Otherwise we
// are done here.
//
if(!NtCount) { goto xx1; }
MenuOrdinals = SpMemRealloc(MenuOrdinals,(NtCount+1) * sizeof(ULONG)); MenuItems = SpMemRealloc(MenuItems,(NtCount+1) * sizeof(PWSTR));
SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_REMOVE_NO_FILES); MenuItems[NtCount] = SpDupStringW(TemporaryBuffer);
//
// Determine the width of the widest item.
//
MenuWidth = 0; for(i=0; i<=NtCount; i++) { if(SplangGetColumnCount(MenuItems[i]) > MenuWidth) { MenuWidth = SplangGetColumnCount(MenuItems[i]); } } //
// Use 80-column screen here because that's how the screen text
// above the menu will be formatted.
//
MenuLeftX = 40 - (MenuWidth/2);
//
// Create menu and populate it.
//
SpDisplayScreen(ScreenMsgId,3,HEADER_HEIGHT+1);
Menu = SpMnCreate( MenuLeftX, NextMessageTopLine+(SplangQueryMinimizeExtraSpacing() ? 1 : 2), MenuWidth, VideoVars.ScreenHeight-STATUS_HEIGHT-NextMessageTopLine-(SplangQueryMinimizeExtraSpacing() ? 2 : 3) );
for(i=0; i<=NtCount; i++) { SpMnAddItem(Menu,MenuItems[i],MenuLeftX,MenuWidth,TRUE,i); }
//
// Present the menu of installations available for removal
// on this partition and await a choice.
//
b = TRUE; do {
ULONG Keys[4] = { ASCI_CR,KEY_F3,ASCI_ESC,0 }; ULONG Mnemonics[2] = { MnemonicRemoveFiles,0 }; ULONG key; ULONG_PTR Choice;
SpDisplayScreen(ScreenMsgId,3,HEADER_HEIGHT+1); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_ENTER_EQUALS_SELECT, SP_STAT_F3_EQUALS_EXIT, 0 );
nextkey:
SpMnDisplay(Menu,NtCount,FALSE,Keys,NULL,NULL,&key,&Choice);
if(key == KEY_F3) { SpConfirmExit(); } else if(key == ASCI_ESC) { break; } else if(key == ASCI_CR) {
if(Choice == NtCount) { b = FALSE; } else {
BOOLEAN keys; ULONG ValidKeys2[3] = { KEY_F3,ASCI_ESC,0 };
//
// User wants to actually remove an installation.
// Confirm and then do that here.
//
redraw2:
SpStartScreen( SP_SCRN_REMOVE_EXISTING_NT, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, MenuItems[Choice] );
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_R_EQUALS_REMOVE_FILES, SP_STAT_ESC_EQUALS_CANCEL, SP_STAT_F3_EQUALS_EXIT, 0 );
keys = TRUE; while(keys) { switch(SpWaitValidKey(ValidKeys2,NULL,Mnemonics)) { case KEY_F3: SpConfirmExit(); goto redraw2; case ASCI_ESC: keys = FALSE; break; default:
//
// Must be r=remove files.
//
*SpaceFreed = SpRemoveInstallation( Region, PartitionPath, NtDirectoryList[MenuOrdinals[Choice]] );
rc = TRUE;
SpStartScreen( SP_SCRN_DONE_REMOVING_EXISTING_NT, 4, HEADER_HEIGHT+3, FALSE, FALSE, DEFAULT_ATTRIBUTE, *SpaceFreed );
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 );
while(SpInputGetKeypress() != ASCI_CR) ; keys = FALSE; b = FALSE; break; } } }
} else { goto nextkey; } } while(b);
SpMnDestroy(Menu);
xx1:
for(i=0; i<=NtCount; i++) { SpMemFree(MenuItems[i]); }
SpMemFree(MenuItems); SpMemFree(MenuOrdinals);
xx0:
SpMemFree(PartitionPath); return(rc); }
#if 0
typedef VOID (*PINSTALLATION_CALLBACK_ROUTINE)( IN PWSTR DirectoryPath, IN PFILE_DIRECTORY_INFORMATION FoundFileInfo );
//
// Stuff to reduce stack usage.
//
PINSTALLATION_CALLBACK_ROUTINE FileIterationCallback; POBJECT_ATTRIBUTES FileIterationObja; PIO_STATUS_BLOCK FileIterationIoStatusBlock; PUNICODE_STRING FileIterationUnicodeString;
VOID SpIterateInstallationFilesWorker( IN PWSTR FilenamePart1, IN PWSTR FilenamePart2 ) { PVOID InfoBuffer; PWSTR FullPath; NTSTATUS Status; HANDLE hFile; BOOLEAN restart; #define DIRINFO(x) ((PFILE_DIRECTORY_INFORMATION)InfoBuffer)
InfoBuffer = SpMemAlloc(1024);
//
// Form the full path name of the current directory.
//
FullPath = SpMemAlloc( ( wcslen(FilenamePart1) + (FilenamePart2 ? wcslen(FilenamePart2) : 0), + 2) * sizeof(WCHAR) );
wcscpy(FullPath,FilenamePart1); if(FilenamePart2) { SpConcatenatePaths(FullPath,FilenamePart2); }
//
// Open the directory for list access.
//
INIT_OBJA(FileIterationObja,FileIterationUnicodeString,FullPath);
Status = ZwOpenFile( &hFile, FILE_LIST_DIRECTORY | SYNCHRONIZE, FileIterationObja, FileIterationIoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
if(NT_SUCCESS(Status)) {
restart = TRUE;
do {
Status = ZwQueryDirectoryFile( hFile, NULL, NULL, NULL, FileIterationIoStatusBlock, InfoBuffer, 1024 - sizeof(WCHAR), // leave room for nul
FileDirectoryInformation, TRUE, // return single entry
NULL, // no file name (match all files)
restart );
restart = FALSE;
if(NT_SUCCESS(Status)) {
//
// nul-terminate the filename just in case
//
DIRINFO->FileName[DIRINFO->FileNameLength/sizeof(WCHAR)] = 0;
if(DIRINFO->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if(DIRINFO->FileName[0] != L'.') {
SpIterateInstallationFiles( FullPath, DIRINFO->FileName );
FileIterationCallback(FullPath,InfoBuffer); } } else { FileIterationCallback(FullPath,InfoBuffer); } }
} while(NT_SUCCESS(Status));
ZwClose(hFile);
} else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: unable to open directory %ws for list access (%lx)\n",FullPath,Status)); }
SpMemFree(FullPath); SpMemFree(InfoBuffer); }
VOID SpIterateInstallationFiles( IN PWSTR FilenamePart1, IN PWSTR FilenamePart2, IN PINSTALLATION_CALLBACK_ROUTINE CallbackFunction ) { //
// Set up stack-saving globals
//
FileIterationIoStatusBlock = SpMemAlloc(sizeof(IO_STATUS_BLOCK); FileIterationUnicodeString = SpMemAlloc(sizeof(UNICODE_STRING)); FileIterationObja = SpMemAlloc(sizeof(OBJECT_ATTRIBUTES);
FileIterationCallback = CallbackFunction;
//
// Do the iteration.
//
SpIterateInstallationFilesWorker(FileNamePart1,FilenamePart2);
//
// Clean up.
//
SpMemFree(FileIterationObja); SpMemFree(FileIterationUnicodeString); SpMemFree(FileIterationIoStatusBlock); } #endif
BOOLEAN IsSetupLogFormatNew( IN PVOID Inf )
/*++
Routine Description:
Informs the caller whether or not the information on setup.log is in the new format.
Arguments:
Inf -
Return Value:
TRUE if the information is in the new format. FALSE otherwise.
--*/
{ return( SpGetSectionKeyExists ( Inf, SIF_NEW_REPAIR_SIGNATURE, SIF_NEW_REPAIR_VERSION_KEY ) ); }
|