windows-nt/Source/XPSP1/NT/base/ntsetup/opktools/setupmgr/common/save.c
2020-09-26 16:20:57 +08:00

786 lines
21 KiB
C

//----------------------------------------------------------------------------
//
// Copyright (c) 1997-1999 Microsoft Corporation
// All rights reserved.
//
// File Name:
// save.c
//
// Description:
// This file has the code that dumps the user's answers to the
// answer file.
//
// The entry point SaveAllSettings() is called by the wizard when
// it is time to save the answer file (and possibly the .udf and
// sample batch script).
//
// The global vars GenSettings, NetSettings, etc. are examined
// and we decide what [Section] key=value settings need to be
// written.
//
// If you're adding a page to this wizard, see the function
// QueueSettingsToAnswerFile().
//
//----------------------------------------------------------------------------
#include "pch.h"
#include "allres.h"
#define FILE_DOT_UDB _T(".udb")
//
// local prototypes
//
static BOOL IsOkToOverwriteFiles(HWND hwnd);
static BOOL BuildAltFileNames(HWND hwnd);
static BOOL WriteSampleBatchScript(HWND hwnd);
static VOID QuoteStringIfNecessary( OUT TCHAR *szOutputString,
IN const TCHAR* const szInputString,
IN DWORD cbSize);
//
// Localized "Usage" string
//
static TCHAR *StrUsage = NULL;
//
// External function in savefile.c
//
extern BOOL QueueSettingsToAnswerFile(HWND hwnd);
BOOL SettingQueueHalScsi_Flush(LPTSTR lpFileName,
QUEUENUM dwWhichQueue);
static TCHAR *StrSampleBatchScriptLine1 = NULL;
static TCHAR *StrSampleBatchScriptLine2 = NULL;
static TCHAR *StrBatchScriptSysprepWarning = NULL;
//----------------------------------------------------------------------------
//
// Function: SetCdRomPath
//
// Purpose: To find the CD-ROM on this local machine and set
// WizGlobals.CdSourcePath to it.
//
// Arguments: VOID
//
// Returns: BOOL TRUE - if the CD path was set
// FALSE - if no CD path was found
//
//----------------------------------------------------------------------------
static BOOL
SetCdRomPath( VOID )
{
TCHAR *p;
TCHAR DriveLetters[MAX_PATH];
TCHAR PathBuffer[MAX_PATH];
//
// Find the CD-ROM
//
// GetLogicalDriveStrings() fills in the DriveLetters buffer, and it
// looks like:
//
// c:\(null)d:\(null)x:\(null)(null)
//
// (i.e. double-null at the end)
//
// ISSUE-2002/02/27-stelo,swamip - Replace with existing code that searches drives
//
if ( ! GetLogicalDriveStrings(MAX_PATH, DriveLetters) )
DriveLetters[0] = _T('\0');
p = DriveLetters;
while ( *p ) {
if ( GetDriveType(p) == DRIVE_CDROM ) {
SYSTEM_INFO SystemInfo;
HRESULT hrCat;
lstrcpyn( PathBuffer, p , AS(PathBuffer));
GetSystemInfo( &SystemInfo );
switch( SystemInfo.wProcessorArchitecture )
{
case PROCESSOR_ARCHITECTURE_INTEL:
hrCat=StringCchCat( PathBuffer, AS(PathBuffer), _T("i386") );
break;
case PROCESSOR_ARCHITECTURE_AMD64:
hrCat=StringCchCat( PathBuffer, AS(PathBuffer), _T("amd64") );
break;
default:
hrCat=StringCchCat( PathBuffer, AS(PathBuffer), _T("i386") );
AssertMsg( FALSE,
"Unknown Processor. Can't set setup files path." );
}
break;
}
while ( *p++ );
}
//
// If no CD Found, leave the setup files path blank
//
if( *p == _T('\0') )
{
lstrcpyn( WizGlobals.CdSourcePath, _T(""), AS(WizGlobals.CdSourcePath) );
return( FALSE );
}
else
{
lstrcpyn( WizGlobals.CdSourcePath, PathBuffer, AS(WizGlobals.CdSourcePath) );
return( TRUE );
}
}
//----------------------------------------------------------------------------
//
// Function: DidSetupmgrWriteThisFile
//
// Purpose: To check to see if a specific file was written by Setup Manager
//
// Arguments: IN LPTSTR lpFile - the full path and name of the file to check
//
// Returns: BOOL TRUE - if setup manager wrote the file
// FALSE - if it didn't
//
//----------------------------------------------------------------------------
static BOOL
DidSetupmgrWriteThisFile( IN LPTSTR lpFile )
{
INT iRet;
TCHAR Buffer[MAX_INILINE_LEN];
FILE *fp = My_fopen(lpFile, _T("r") );
if ( fp == NULL )
return( FALSE );
if ( My_fgets(Buffer, MAX_INILINE_LEN - 1, fp) == NULL )
return( FALSE );
My_fclose(fp);
if ( lstrcmp(Buffer, _T(";SetupMgrTag\n")) == 0 ||
lstrcmp(Buffer, _T("@rem SetupMgrTag\n")) == 0 )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
//----------------------------------------------------------------------------
//
// Function: SaveAllSettings
//
// Purpose: This is the entry point for saving the answer file. It is
// called by the SaveScript page.
//
// If multiple computers were specified, it also writes a .udf.
//
// It always writes a batch file that makes it easy to use the
// answer file just created.
//
// Arguments: HWND hwnd
//
// Returns: BOOL
//
//----------------------------------------------------------------------------
BOOL
SaveAllSettings(HWND hwnd)
{
//
// Build the file names for the .udf and sample batch script. The
// results are stored in FixedGlobals.
//
// After calling BuildAltFileNames(), FixedGlobals.UdfFileName and
// FixedGlobals.BatchFileName will be null strings if we're not
// supposed to write those files out
//
if ( ! BuildAltFileNames(hwnd) )
return FALSE;
//
// Before overwriting anything do some checks.
//
if ( ! IsOkToOverwriteFiles(hwnd) )
return FALSE;
//
// Empty any intermediatte stuff we have on the queues because
// user is going back & next alot.
//
// Then initialize the queues with the original settings.
//
SettingQueue_Empty(SETTING_QUEUE_ANSWERS);
SettingQueue_Empty(SETTING_QUEUE_UDF);
SettingQueue_Copy(SETTING_QUEUE_ORIG_ANSWERS,
SETTING_QUEUE_ANSWERS);
SettingQueue_Copy(SETTING_QUEUE_ORIG_UDF,
SETTING_QUEUE_UDF);
//
// Call the function that everybody plugs into in savefile.c to
// queue up all the answers from the UI.
//
if (!QueueSettingsToAnswerFile(hwnd))
return FALSE;
//
// Flush the answer file queue.
//
if ( ! SettingQueue_Flush(FixedGlobals.ScriptName,
SETTING_QUEUE_ANSWERS) ) {
ReportErrorId(hwnd,
MSGTYPE_ERR | MSGTYPE_WIN32,
IDS_ERRORS_WRITING_ANSWER_FILE,
FixedGlobals.ScriptName);
return FALSE;
}
//
// If multiple computernames, flush the .udf queue
//
if ( FixedGlobals.UdfFileName[0] ) {
if ( ! SettingQueue_Flush(FixedGlobals.UdfFileName,
SETTING_QUEUE_UDF) ) {
ReportErrorId(hwnd,
MSGTYPE_ERR | MSGTYPE_WIN32,
IDS_ERRORS_WRITING_UDF,
FixedGlobals.UdfFileName);
return FALSE;
}
}
//
// If they are using SCSI or HAL files, then flush the txtsetup.oem queue
//
// NTRAID#NTBUG9-551746-2002/02/27-stelo,swamip - Unused code, should be removed
//
if ( GetNameListSize( &GenSettings.OemScsiFiles ) > 0 ||
GetNameListSize( &GenSettings.OemHalFiles ) > 0 ) {
TCHAR szTextmodePath[MAX_PATH + 1] = _T("");
// Note-ConcatenatePaths truncates to prevent overflow
ConcatenatePaths( szTextmodePath,
WizGlobals.OemFilesPath,
_T("Textmode\\txtsetup.oem"),
NULL );
if ( ! SettingQueueHalScsi_Flush(szTextmodePath,
SETTING_QUEUE_TXTSETUP_OEM) ) {
ReportErrorId(hwnd,
MSGTYPE_ERR | MSGTYPE_WIN32,
IDS_ERRORS_WRITING_ANSWER_FILE,
szTextmodePath);
return FALSE;
}
}
//
// Write the sample batch script if BuildAltFileNames hasn't already
// determined that we should not (i.e. If a remote boot answer file,
// don't write a sample batch script)
//
if ( FixedGlobals.BatchFileName[0] ) {
if ( ! WriteSampleBatchScript(hwnd) )
return FALSE;
}
return TRUE;
}
//----------------------------------------------------------------------------
//
// Function: IsOkToOverwriteFiles
//
// Purpose: This is called prior to writing the answer file, .udf and sample
// batch script.
//
// Before overwriting any file, we make sure that it was created
// by setupmgr. If not, we prompt the user.
//
// Returns:
// TRUE - to go ahead and overwrite any of the files that might exist
// FALSE - user canceled the overwrite
//
//----------------------------------------------------------------------------
static BOOL
IsOkToOverwriteFiles(HWND hwnd)
{
INT i;
INT iRet;
//
// If we are editing a script just write out the files
//
if( ! WizGlobals.bNewScript )
{
return( TRUE );
}
//
// Check foo.txt foo.udf and foo.bat that we're about to write out.
// If any of them already exists, then check to see if they were
// created by setupmgr. We will prompt the user before overwriting
// something we didn't write before.
//
for ( i=0; i<3; i++ ) {
LPTSTR lpFile = NULL;
//
// The answer file, .udf or the batch script?
//
if ( i == 0 )
lpFile = FixedGlobals.ScriptName;
else if ( i == 1 )
lpFile = FixedGlobals.UdfFileName;
else
lpFile = FixedGlobals.BatchFileName;
//
// If the file already exists, prompt the user if it doesn't
// have our tag.
//
// Look for ;SetupMgrTag in the answer file and .udf
// Look for rem SetupMgrTag in the batch script
//
if ( lpFile[0] && DoesFileExist(lpFile) ) {
if( DidSetupmgrWriteThisFile( lpFile ) )
{
iRet = ReportErrorId(hwnd,
MSGTYPE_YESNO,
IDS_ERR_FILE_ALREADY_EXISTS,
lpFile);
if ( iRet == IDNO )
return( FALSE );
}
else {
iRet = ReportErrorId(hwnd,
MSGTYPE_YESNO,
IDS_ERR_SAVEFILE_NOT_SETUPMGR,
lpFile);
if ( iRet == IDNO )
return( FALSE );
}
}
}
return( TRUE );
}
//----------------------------------------------------------------------------
//
// Function: BuildAltFileNames
//
// Purpose: This function derives the name for the .udf and the .bat
// associatted with a given answer filename.
//
// Note: This function has a couple of side-effects. If there is no
// extension on FixedGlobals.ScriptName, it adds one.
//
// Also, after this function runs, FixedGlobals.UdfFileName will
// be a null string if there <= 1 computer names (i.e. no udf)
//
// If we're writing a .sif (remote boot), FixGlobals.BatchFileName
// will be a null-string.
//
// The finish page relies on this.
//
// Returns: BOOL
//
//----------------------------------------------------------------------------
static BOOL
BuildAltFileNames(HWND hwnd)
{
TCHAR PathBuffer[MAX_PATH],
*lpFilePart = NULL,
*pExtension;
INT nEntries = GetNameListSize(&GenSettings.ComputerNames);
BOOL bMultipleComputers = ( nEntries > 1 );
HRESULT hrCat;
//
// Find out the filename part of the answer file pathname and copy
// it to PathBuffer[]
//
GetFullPathName(FixedGlobals.ScriptName,
MAX_PATH,
PathBuffer,
&lpFilePart);
if (lpFilePart == NULL)
return FALSE;
//
// Point at the extension in the PathBuffer[].
//
// e.g. foo.txt, point at the dot.
// foo, point at the null byte.
//
// If there is no extension, put one on it
//
if ( (pExtension = wcsrchr(lpFilePart, _T('.'))) == NULL ) {
pExtension = &lpFilePart[lstrlen(lpFilePart)];
if ( WizGlobals.iProductInstall == PRODUCT_REMOTEINSTALL )
hrCat=StringCchCat(FixedGlobals.ScriptName, AS(FixedGlobals.ScriptName), _T(".sif"));
else
hrCat=StringCchCat(FixedGlobals.ScriptName, AS(FixedGlobals.ScriptName), _T(".txt"));
}
//
// Cannot allow foo.bat or foo.udf as answer file names because we
// will probably be writing other stuff to foo.bat and/or foo.udf
//
if ( (LSTRCMPI(pExtension, _T(".bat")) == 0) ||
(LSTRCMPI(pExtension, FILE_DOT_UDB) == 0) )
{
ReportErrorId(hwnd,
MSGTYPE_ERR,
IDS_ERR_BAD_SCRIPT_EXTENSION);
return FALSE;
}
//
// Build the .udf name if there was > 1 computer names specified
//
if ( bMultipleComputers ) {
lstrcpyn(pExtension, FILE_DOT_UDB, MAX_PATH- (int)(pExtension - PathBuffer) );
lstrcpyn(FixedGlobals.UdfFileName, PathBuffer,AS(FixedGlobals.UdfFileName));
} else {
FixedGlobals.UdfFileName[0] = _T('\0');
}
//
// Build the .bat file name. We won't be creating a sample batch
// script in the case of remote boot, so null it out, else the Finish
// page will be broken.
//
if ( (WizGlobals.iProductInstall == PRODUCT_REMOTEINSTALL) ||
(WizGlobals.iProductInstall == PRODUCT_SYSPREP) ) {
FixedGlobals.BatchFileName[0] = _T('\0');
} else {
lstrcpyn(pExtension, _T(".bat"), MAX_PATH - (int)(pExtension - PathBuffer) );
lstrcpyn(FixedGlobals.BatchFileName, PathBuffer,AS(FixedGlobals.BatchFileName));
}
return TRUE;
}
//----------------------------------------------------------------------------
//
// Function: AddLanguageSwitch
//
// Purpose: Add the /copysource language switch to copy over the right
// language files
//
// Returns: VOID
//
//----------------------------------------------------------------------------
static VOID
AddLanguageSwitch( TCHAR *Buffer, DWORD cbSize )
{
INT iNumLanguages;
HRESULT hrCat;
iNumLanguages = GetNameListSize( &GenSettings.LanguageFilePaths );
if ( iNumLanguages > 0 )
{
hrCat=StringCchCat( Buffer, cbSize, _T(" /copysource:lang") );
}
hrCat=StringCchCat( Buffer, cbSize, _T("\n") ); // make sure it has a line-feed at the end
}
//----------------------------------------------------------------------------
//
// Function: WriteSampleBatchScript
//
// Purpose: writes the sample batch script
//
// Returns: FALSE if errors writing the file. Any errors are reported
// to the user.
//
//----------------------------------------------------------------------------
static BOOL
WriteSampleBatchScript(HWND hwnd)
{
FILE *fp;
TCHAR Buffer[MAX_INILINE_LEN];
TCHAR *pszScriptName = NULL;
TCHAR szComputerName[MAX_PATH];
TCHAR SetupFilesBuffer[MAX_INILINE_LEN];
TCHAR AnswerFileBuffer[MAX_INILINE_LEN];
TCHAR SetupFilesQuotedBuffer[MAX_INILINE_LEN];
TCHAR Winnt32Buffer[MAX_INILINE_LEN];
DWORD dwSize;
INT nEntries = GetNameListSize(&GenSettings.ComputerNames);
BOOL bMultipleComputers = ( nEntries > 1 );
HRESULT hrPrintf;
if ( (fp = My_fopen(FixedGlobals.BatchFileName, _T("w")) ) == NULL ) {
ReportErrorId(hwnd,
MSGTYPE_ERR | MSGTYPE_WIN32,
IDS_ERR_OPEN_SAMPLE_BAT,
FixedGlobals.BatchFileName);
return FALSE;
}
My_fputs(_T("@rem SetupMgrTag\n@echo off\n\n"), fp);
if( StrSampleBatchScriptLine1 == NULL )
{
StrSampleBatchScriptLine1 = MyLoadString( IDS_BATCH_SCRIPT_LINE1 );
StrSampleBatchScriptLine2 = MyLoadString( IDS_BATCH_SCRIPT_LINE2 );
}
My_fputs( _T("rem\nrem "), fp );
My_fputs( StrSampleBatchScriptLine1, fp );
My_fputs( _T("\nrem "), fp );
My_fputs( StrSampleBatchScriptLine2, fp );
My_fputs( _T("\nrem\n\n"), fp );
if ( !(pszScriptName = MyGetFullPath( FixedGlobals.ScriptName )) )
{
My_fclose( fp );
return FALSE;
}
//
// Quote the Script name if it contains spaces
//
QuoteStringIfNecessary( AnswerFileBuffer, pszScriptName, AS(AnswerFileBuffer) );
// Note: MAX_INILINE_LEN=1K AnswerFileBuffer at this time is MAX_PATH+2 MAX
// buffer overrun should not be a problem.
hrPrintf=StringCchPrintf( Buffer, AS(Buffer), _T("set AnswerFile=.\\%s\n"), AnswerFileBuffer );
My_fputs( Buffer, fp );
if( bMultipleComputers )
{
TCHAR UdfFileBuffer[1024];
pszScriptName = MyGetFullPath( FixedGlobals.UdfFileName );
//
// Quote the UDF name if it contains spaces
//
QuoteStringIfNecessary( UdfFileBuffer, pszScriptName, AS(UdfFileBuffer) );
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("set UdfFile=.\\%s\nset ComputerName=%%1\n"),
UdfFileBuffer );
My_fputs( Buffer, fp );
}
if( WizGlobals.bStandAloneScript )
{
SetCdRomPath();
lstrcpyn( SetupFilesBuffer, WizGlobals.CdSourcePath, AS(SetupFilesBuffer) );
}
else
{
GetComputerNameFromUnc( WizGlobals.UncDistFolder, szComputerName, AS(szComputerName) );
hrPrintf=StringCchPrintf( SetupFilesBuffer, AS(SetupFilesBuffer),
_T("%s%s%s%s%s"),
szComputerName,
_T("\\"),
WizGlobals.DistShareName,
_T("\\"),
WizGlobals.Architecture );
}
QuoteStringIfNecessary( SetupFilesQuotedBuffer, SetupFilesBuffer, AS(SetupFilesQuotedBuffer) );
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("set SetupFiles=%s\n\n"),
SetupFilesQuotedBuffer );
My_fputs( Buffer, fp );
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("%s\\winnt32"),
SetupFilesBuffer );
QuoteStringIfNecessary( Winnt32Buffer, Buffer, AS(Winnt32Buffer) );
if( bMultipleComputers )
{
if( StrUsage == NULL )
{
StrUsage = MyLoadString( IDS_USAGE );
}
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("if \"%%ComputerName%%\" == \"\" goto USAGE\n\n") );
My_fputs( Buffer, fp );
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("%s%s"),
Winnt32Buffer,
_T(" /s:%SetupFiles% ")
_T("/unattend:%AnswerFile% ")
_T("/udf:%ComputerName%,%UdfFile% ")
_T("/makelocalsource") );
AddLanguageSwitch( Buffer, AS(Buffer) );
My_fputs( Buffer, fp );
My_fputs( _T("goto DONE\n\n"), fp );
My_fputs( _T(":USAGE\n"), fp );
My_fputs( _T("echo.\n"), fp );
hrPrintf=StringCchPrintf( Buffer, AS(Buffer),
_T("echo %s: unattend ^<computername^>\n"),
StrUsage );
My_fputs( Buffer, fp );
My_fputs( _T("echo.\n\n"), fp );
My_fputs( _T(":DONE\n"), fp );
}
else
{
hrPrintf=StringCchPrintf(Buffer, AS(Buffer),
_T("%s%s"),
Winnt32Buffer,
_T(" /s:%SetupFiles% ")
_T("/unattend:%AnswerFile%"));
AddLanguageSwitch( Buffer, AS(Buffer) );
My_fputs( Buffer, fp );
}
My_fclose( fp );
return( TRUE );
}
//----------------------------------------------------------------------------
//
// Function: QuoteStringIfNecessary
//
// Purpose: If the given input string has white space then the whole string
// is quoted and returned in the output string. Else just the string
// is returned in the output string.
//
// szOutputString is assumed to be of size MAX_INILINE_LEN
//
// Arguments: OUT TCHAR *szOutputString
//
// Returns: VOID
//
//----------------------------------------------------------------------------
static VOID
QuoteStringIfNecessary( OUT TCHAR *szOutputString,
IN const TCHAR* const szInputString,
IN DWORD cbSize)
{
HRESULT hrPrintf;
if( DoesContainWhiteSpace( szInputString ) )
{
hrPrintf=StringCchPrintf( szOutputString, cbSize,
_T("\"%s\""),
szInputString );
}
else
{
lstrcpyn( szOutputString, szInputString ,cbSize);
}
}