|
|
#include "spprecmp.h"
#pragma hdrstop
#define MAX_NT_DIR_LEN 50
/*++
Revision History:
--*/
VOID SpCheckDirectoryForNt( IN PDISK_REGION Region, IN PWSTR Directory, OUT PBOOLEAN ReselectDirectory, OUT PBOOLEAN NtInDirectory );
VOID pSpDrawGetNtPathScreen( OUT PULONG EditFieldY );
ValidationValue SpGetPathKeyCallback( IN ULONG Key );
BOOLEAN SpIsValid8Dot3( IN PWSTR Path );
BOOLEAN pSpConsecutiveBackslashes( IN PWSTR Path );
VOID SpNtfsNameFilter( IN OUT PWSTR Path );
BOOLEAN SpGetUnattendedPath( IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR TargetPath );
BOOLEAN SpGenerateNTPathName( IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR TargetPath );
//
// From spcopy.c.
//
BOOLEAN SpDelEnumFileAndDirectory( IN PCWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Pointer );
extern PVOID FileDeleteGauge;
BOOLEAN SpGetTargetPath( IN PVOID SifHandle, IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR *TargetPath ) //
// Return value - True - indicates that the path has to be wiped out
// False - the path doesn't already exist
//
{ ULONG EditFieldY; WCHAR NtDir[MAX_NT_DIR_LEN+2]; BOOLEAN BadDirectory = FALSE; BOOLEAN NtAlreadyPresent; BOOLEAN GotUnattendedPath = FALSE; BOOLEAN WipeDir = FALSE;
NtDir[0] = 0;
//
// If this is an ASR recovery session, just get the target path from
// the dr_state.sif file and return.
//
if( SpDrEnabled() && ! RepairWinnt ) { PWSTR TargetPathFromDrState;
TargetPathFromDrState = SpDrGetNtDirectory(); *TargetPath = SpDupStringW(TargetPathFromDrState); ASSERT(*TargetPath);
NTUpgrade = DontUpgrade; return(FALSE); }
//
// If this is unattended operation, fetch the path from the
// unattended script. The path we get there might have
// indicate that we should generate a pathname. This allows
// installation into a path that is guaranteed to be unique.
// (in case the user already has nt on the machine, etc).
//
if(UnattendedOperation) { GotUnattendedPath = SpGetUnattendedPath(Region,DefaultPath,NtDir); } else { if (PreferredInstallDir) { GotUnattendedPath = TRUE; wcscpy( NtDir, PreferredInstallDir ); } else { GotUnattendedPath = TRUE; wcscpy( NtDir, DefaultPath ); } }
if (!GotUnattendedPath) { BadDirectory = TRUE; }
do { if (BadDirectory) { //
// we do not have a good path so ask the user
//
ASSERT(wcslen(DefaultPath) < MAX_NT_DIR_LEN); ASSERT(*DefaultPath == L'\\');
wcsncpy(NtDir,DefaultPath,MAX_NT_DIR_LEN);
NtDir[MAX_NT_DIR_LEN] = 0;
pSpDrawGetNtPathScreen(&EditFieldY);
SpGetInput( SpGetPathKeyCallback, 6, // left edge of the edit field
EditFieldY, MAX_NT_DIR_LEN, NtDir, FALSE // escape clears edit field
); }
//
// If the user didn't start with a backslash, put one in there
// for him.
//
if(NtDir[0] != L'\\') { RtlMoveMemory(NtDir+1,NtDir,MAX_NT_DIR_LEN+1); NtDir[0] = L'\\'; }
//
// Assume the directory is OK and not already present.
//
BadDirectory = FALSE; NtAlreadyPresent = FALSE;
//
// Force 8.3 because otherwise WOW won't run.
// This checks also nabs "" and "\" and disallows them.
//
if(!SpIsValid8Dot3(NtDir)) { BadDirectory = TRUE; } else {
//
// Perform a filtering operation that coalesces
// consecutive dots, etc.
//
SpNtfsNameFilter(NtDir);
//
// If the name has consecutive backslashes, disallow it.
//
if(pSpConsecutiveBackslashes(NtDir)) { BadDirectory = TRUE; } }
//
// If we have a bad directory, tell the user.
//
if(BadDirectory) {
SpDisplayScreen(SP_SCRN_INVALID_NTPATH,3,HEADER_HEIGHT+1);
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 );
SpInputDrain(); while(SpInputGetKeypress() != ASCI_CR) ; } else { //
// The directory is good. Check to see if Windows NT is
// already in there. If it is, then the user will have
// the option of reselecting a path or overwriting the
// existing installation. This is brute force. Next
// time look for an opportunity to be more elegant.
//
if(!SpDrEnabled()) { SpCheckDirectoryForNt(Region,NtDir,&BadDirectory,&NtAlreadyPresent); } else { BadDirectory = FALSE; }
//
// If the directory is OK and we didn't find Windows NT in it,
// then see if the directory is the Windows directory and whether
// the user wants to install into this. If we found Windows NT
// in this directory, no user input is needed. We just need to
// find out if this also contains a Windows installation.
//
if(!BadDirectory && NtAlreadyPresent) WipeDir = TRUE;
}
} while(BadDirectory);
//
// Remove trailing backslash. Only have to worry about one
// because if there were two, pSpConsecutiveBackslashes() would
// have caught this earlier and we'd never have gotten here.
//
if(NtDir[wcslen(NtDir)-1] == L'\\') { NtDir[wcslen(NtDir)-1] = 0; }
//
// Make a duplicate of the directory name.
//
*TargetPath = SpDupStringW(NtDir);
ASSERT(*TargetPath);
return( WipeDir ); }
BOOLEAN SpGenerateNTPathName( IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR TargetPath )
/*++
Routine Description:
Using the default path as a starting point, this routine generates a unique path name to install nt into.
Arguments:
Region - supplies region to which nt is being installed.
DefaultPath - supplies the default path for the installation. The path to install to will be based on this name.
TargetPath - receives the path to install to if the return value is TRUE. This buffer must be large enough to hold MAX_NT_DIR_LEN+2 wchars.
Return Value:
TRUE if the path we return is valid and should be used as the target path. FALSE otherwise.
--*/
{ PWCHAR p; unsigned i; WCHAR num[5];
//
// Init TargetPath and remember where he ends.
//
wcscpy( TargetPath, DefaultPath ); num[0] = L'.';
p = TargetPath + wcslen( TargetPath );
//
// Form the region's nt pathname.
//
SpNtNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent );
//
// Using extensions with numerical values 0-999, attempt to locate
// a nonexistent directory name.
//
for(i=0; i<999; i++) {
//
// See whether a directory or file exists. If not, we found our path.
//
if( (!SpNFilesExist(TemporaryBuffer,&TargetPath,1,TRUE )) && (!SpNFilesExist(TemporaryBuffer,&TargetPath,1,FALSE)) ) { return(TRUE); }
swprintf(&num[1],L"%u",i); wcscpy(p,num); }
//
// Couldn't find a pathname that doesn't exist.
//
return FALSE; }
BOOLEAN SpGetUnattendedPath( IN PDISK_REGION Region, IN PWSTR DefaultPath, OUT PWSTR TargetPath )
/*++
Routine Description:
In an unattended installation, look in the unattended script to determine the target path. The target path can either be fully specified or can be * which will cause is to generate a unique pathname. This is useful to ensure that nt gets installed into a unique directory when other installations may be present on the same machine.
Call this routine only if this is unattended mode setup.
Arguments:
Region - supplies region to which nt is being installed.
DefaultPath - supplies the default path for the installation. The path to install to will be based on this name.
TargetPath - receives the path to install to if the return value is TRUE. This buffer must be large enough to hold MAX_NT_DIR_LEN+2 wchars.
Return Value:
TRUE if the path we return is valid and should be used as the target path. FALSE otherwise.
--*/
{ PWSTR PathSpec;
ASSERT(UnattendedOperation); if(!UnattendedOperation) { return(FALSE); }
PathSpec = SpGetSectionKeyIndex(UnattendedSifHandle,SIF_UNATTENDED,L"TargetPath",0); if(!PathSpec) { //
// Default to *.
//
PathSpec = L"*"; }
//
// if it's not "*" then it's an absolute path -- just return it.
//
if(wcscmp(PathSpec,L"*")) { wcsncpy(TargetPath,PathSpec,MAX_NT_DIR_LEN); TargetPath[MAX_NT_DIR_LEN] = 0; return(TRUE); }
return SpGenerateNTPathName( Region, DefaultPath, TargetPath ); }
VOID SpCheckDirectoryForNt( IN PDISK_REGION Region, IN PWSTR Directory, OUT PBOOLEAN ReselectDirectory, OUT PBOOLEAN NtInDirectory )
/*++
Routine Description:
Check a directory for the presence of Windows NT. If Windows NT is in there, then inform the user that if he continues, his existing configuration will be overwritten.
Arguments:
Region - supplies region descriptor for partition to check for nt.
Directory - supplies name of directory on the partition ro check for nt.
ReselectDirectory - receives boolean value indicating whether the caller should ask the user to select a different directory.
NtInDirectory - receives a boolean value indicating whether we found windows nt in the given directory.
Return Value:
None.
--*/
{ ULONG ValidKeys[3] = { KEY_F3,ASCI_ESC,0 }; ULONG Mnemonics[2] = { MnemonicDeletePartition2, 0 };
//
// Assume the directory is ok as-is and so the user does not have to
// select a different one.
//
*ReselectDirectory = FALSE; *NtInDirectory = FALSE;
//
// Check for Windows NT in the directory.
// If it's in there, then ask the user whether he wants to
// overwrite it.
//
SpNtNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent );
if( (!SpNFilesExist(TemporaryBuffer,&Directory,1,TRUE )) && (!SpNFilesExist(TemporaryBuffer,&Directory,1,FALSE)) ) { return; } else{
*NtInDirectory = TRUE;
while(1) { SpStartScreen( SP_SCRN_NTPATH_EXISTS, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, Directory );
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_L_EQUALS_DELETE, SP_STAT_ESC_EQUALS_NEW_PATH, SP_STAT_F3_EQUALS_EXIT, 0 );
switch(SpWaitValidKey(ValidKeys,NULL,Mnemonics)) {
case KEY_F3: SpConfirmExit(); break;
case ASCI_ESC: //
// Reselect path.
//
*ReselectDirectory = TRUE; // fall through
default: //
// Path is ok, just return.
//
return; } } } }
ValidationValue SpGetPathKeyCallback( IN ULONG Key ) { ULONG u;
switch(Key) {
case KEY_F3: SpConfirmExit(); pSpDrawGetNtPathScreen(&u); return(ValidateRepaint);
default:
//
// Ignore special keys and illegal characters.
// Use the set of illegal FAT characters.
// Disallow 127 because there is no ANSI equivalent
// and so the name can't be displayed by Windows.
// Disallow space because DOS can't handle it.
// Disallow oem characters because we have problems
// booting if they are used.
//
if((Key & KEY_NON_CHARACTER) || wcschr(L" \"*+,/:;<=>?[]|!#$&@^'`{}()%~",(WCHAR)Key) || (Key >= 127) || (Key < 32)) { return(ValidateReject); } break; }
return(ValidateAccept); }
VOID pSpDrawGetNtPathScreen( OUT PULONG EditFieldY ) { SpDisplayScreen(SP_SCRN_GETPATH_1,3,HEADER_HEIGHT+1); *EditFieldY = NextMessageTopLine + 1; SpContinueScreen(SP_SCRN_GETPATH_2,3,4,FALSE,DEFAULT_ATTRIBUTE);
SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, SP_STAT_F3_EQUALS_EXIT, 0 ); }
BOOLEAN SpIsValid8Dot3( IN PWSTR Path )
/*++
Routine Description:
Check whether a path is valid 8.3. The path may or may not start with a backslash. Only backslashes are recognized as path separators. Individual characters are not checked for validity (ie, * would not invalidate the path). The path may or may not terminate with a backslash. A component may have a dot without characters in the extension (ie, a\b.\c is valid).
\ and "" are explicitly disallowed even though they fit the rules.
Arguments:
Path - pointer to path to check.
Return Value:
TRUE if valid 8.3, FALSE otherwise.
--*/
{ unsigned Count; BOOLEAN DotSeen,FirstChar;
if((*Path == 0) || ((Path[0] == L'\\') && (Path[1] == 0))) { return(FALSE); }
DotSeen = FALSE; FirstChar = TRUE; Count = 0;
while(*Path) {
//
// Path points to start of current component (1 past the slash)
//
switch(*Path) {
case L'.': if(FirstChar) { return(FALSE); } if(DotSeen) { return(FALSE); }
Count = 0; DotSeen = TRUE; break;
case L'\\':
DotSeen = FALSE; FirstChar = TRUE; Count = 0;
if(*(++Path) == '\\') {
// 2 slashes in a row
return(FALSE); }
continue;
default:
Count++; FirstChar = FALSE;
if((Count == 4) && DotSeen) { return(FALSE); }
if(Count == 9) { return(FALSE); }
}
Path++; }
return(TRUE); }
BOOLEAN pSpConsecutiveBackslashes( IN PWSTR Path ) { int x = wcslen(Path); int i;
for(i=0; i<x-1; i++) {
if((Path[i] == L'\\') && (Path[i+1] == L'\\')) {
return(TRUE); } }
return(FALSE); }
VOID SpNtfsNameFilter( IN OUT PWSTR Path )
/*++
Routine Description:
Strip trailing .' within a path component. This also strips tailing .'s from the entire path itself. Also condense other consecutive .'s into a single ..
Example: \...\..a...b. ==> \\.a.b
Arguments:
Path - On input, supplies the path to be filtered. On output, contains the filtered pathname.
Return Value:
None.
--*/
{ PWSTR TempPath = SpDupStringW(Path); PWSTR p,q; BOOLEAN Dot;
if (TempPath) { //
// Coalesce adjacent dots and strip trailing dots within a component.
// xfers Path ==> TempPath
//
for(Dot=FALSE,p=Path,q=TempPath; *p; p++) {
if(*p == L'.') {
Dot = TRUE;
} else {
if(Dot && (*p != L'\\')) { *q++ = L'.'; } Dot = FALSE; *q++ = *p; } } *q = 0;
wcscpy(Path,TempPath); } }
ULONG SpGetMaxNtDirLen( VOID ) { return( MAX_NT_DIR_LEN ); }
VOID SpDeleteExistingTargetDir( IN PDISK_REGION Region, IN PWSTR NtDir, IN BOOLEAN GaugeNeeded, IN DWORD MsgId ) /*
Parameters : Region - Pointer to Region structure associated with the partition that contains the OS NtDir - The directory to recursively delete GaugeNeeded - Should we display a gauge while deleting the dir ? MsgId - Use this if you want to display a title message
*/
{ ENUMFILESRESULT Result; PWSTR FullNtPath; NTSTATUS Status, Stat; PULONG RecursiveOperation;
if( MsgId ) SpDisplayScreen(MsgId, 3, 4 );
SpNtNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent );
SpConcatenatePaths( TemporaryBuffer, NtDir );
FullNtPath = SpDupStringW(TemporaryBuffer);
// First try and delete the install directory
// This is to see if itself is a reparse point. Also if it is just an empty dir
// then we save time.
Stat = SpDeleteFileEx( FullNtPath, NULL, NULL, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT );
if(NT_SUCCESS(Stat)){ SpMemFree( FullNtPath); return; // We are done
}
RecursiveOperation = SpMemAlloc(sizeof(ULONG)); ASSERT( RecursiveOperation );
//
// Do the counting phase for the clean-up
//
*RecursiveOperation = SP_COUNT_FILESTODELETE;
SpDisplayStatusText(SP_STAT_SETUP_IS_EXAMINING_DIRS,DEFAULT_STATUS_ATTRIBUTE);
Result = SpEnumFilesRecursiveDel( FullNtPath, SpDelEnumFileAndDirectory, &Status, RecursiveOperation);
//
// Now do the cleanup (actual deleting)
//
FileDeleteGauge = NULL; if( GaugeNeeded ){ SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_SETUP_IS_DELETING); FileDeleteGauge = SpCreateAndDisplayGauge(*RecursiveOperation,0,15,TemporaryBuffer,NULL,GF_PERCENTAGE,0); ASSERT(FileDeleteGauge); }
*RecursiveOperation = SP_DELETE_FILESTODELETE;
Result = SpEnumFilesRecursiveDel( FullNtPath, SpDelEnumFileAndDirectory, &Status, RecursiveOperation);
//Delete the main parent as the recursive call only cleans out everythin below it
Stat = SpDeleteFileEx( FullNtPath, NULL, NULL, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT );
if(!NT_SUCCESS(Stat) && (Stat != STATUS_OBJECT_NAME_NOT_FOUND)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Dir Not Deleted - Status - %ws (%lx)\n", (PWSTR)FullNtPath, Stat)); }
if (GaugeNeeded) { SpDestroyGauge(FileDeleteGauge); FileDeleteGauge = NULL; }
SpMemFree( FullNtPath ); SpMemFree( RecursiveOperation );
return;
}
|