Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1751 lines
41 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
winnt.c
Abstract:
Top level file for DOS based NT installation program.
Author:
Ted Miller (tedm) 30-March-1992
Revision History:
--*/
/*
NOTES:
The function of this program is to pull down a complete Windows NT
installation source onto a local partition, and create a setup boot
floppy. The machine is then rebooted, starting a Windows NT Setup
just as if the user had used the real setup floppies or CD-ROM.
The following assumptions are made:
- The floppy must be provided by the user and already formatted.
- The files on the network source are in the same directory layout
structure that will be created in the temp directory on the local
source (ie, as far as winnt is concerned, the source and target
directory layout is the same).
The inf file is expected to be formatted as follows:
[SpaceRequirements]
# BootDrive is the # bytes required free on C:.
# NtDrive is the # bytes required free on the drive chosen by
# the user to contain Windows NT.
BootDrive =
NtDrive =
[Miscellaneous]
# misc junk that goes nowhere else.
[Directories]
# Specification of the source directory structure. All directories
# are relative to the directory where dos2nt.inf was found on the
# remote source or the temp directory on the local source.
# Loading and trailing backslashes are ignored -- to specify the root,
# leave the dirctory field blank or use \.
d1 =
d2 = os2
.
.
.
[Files]
# List of files to be copied to the local source directory.
# Format is <srcdir>,<filename> where <srcdir> matches an entry in the
# Directories section, and <filename> should not contain any path
# characters.
d1,ntoskrnl.exe
d1,ntdll.dll
.
.
.
[FloppyFiles]
# List of files that are to be placed on the floppy that Setup creates.
# Format is same as for lines in the [Files] sections except the directory
# is only used for the source -- the target path is always a:\.
d1,aha154x.sys
.
.
.
*/
#include "winnt.h"
#include <errno.h>
#include <string.h>
#include <dos.h>
#include <stdlib.h>
#include <direct.h>
#include <fcntl.h>
#include <ctype.h>
#include <process.h>
//
// define name of default inf file and default source path
//
#define DEFAULT_INF_NAME "dosnet.inf"
//
// Command line arguments
//
PCHAR CmdLineSource,CmdLineTarget,CmdLineInf,CmdLineDelete;
BOOLEAN SourceGiven,TargetGiven,InfGiven,DeleteGiven;
//
// If the user gives a script file on the command line,
// if will be appended to winnt.sif.
//
PCHAR DngScriptFile = NULL;
//
// DngSourceRootPath is the drivespec and path to the root of the source,
// and never ends in \ (will be length 2 if source is the root).
//
// Examples: D:\foo\bar D:\foo D:
//
PCHAR DngSourceRootPath;
CHAR DngTargetDriveLetter;
int InfFileHandle;
PVOID DngInfHandle;
PCHAR LocalSourceDirName = LOCAL_SOURCE_DIRECTORY;
PCHAR x86DirName = "\\I386";
//
// If this flag is TRUE, then verify the files that are copied to
// the floppy. If it is FALSE, don't. The /f switch overrides.
//
BOOLEAN DngFloppyVerify = TRUE;
//
// If this is FALSE, suppress creation of the boot floppies.
//
BOOLEAN DngCreateFloppies = TRUE;
BOOLEAN DngFloppiesOnly = FALSE;
//
// If TRUE, create winnt floppies.
// If FALSE, create cd/floppy floppies (no winnt.sif)
//
BOOLEAN DngWinntFloppies = TRUE;
//
// If this flag is TRUE, then check the free space on the floppy disk
// before accepting it. Otherwise don't check free space.
//
BOOLEAN DngCheckFloppySpace = TRUE;
//
// Current drive when program invoked, saved so we can restore it
// if the user exits early.
//
unsigned DngOriginalCurrentDrive;
//
// If this is true, we do floppyless operation,
// installing an nt boot on the system partition (C:)
// and starting setup from there.
//
BOOLEAN DngFloppyless = FALSE;
//
// Unattended mode, ie, skip final reboot screen.
//
BOOLEAN DngUnattended = FALSE;
BOOLEAN DngServer = FALSE;
//
// Flag that indicates that we are running on Windows
// (ie, not bare DOS).
//
BOOLEAN DngWindows = FALSE;
//
// Flag that indicates that we are running OEM preinstall
//
BOOLEAN DngOemPreInstall = FALSE;
PCHAR OemSystemDirectory = WINNT_OEM_DIR;
PCHAR OemOptionalDirectory = WINNT_OEM_OPTIONAL_DIR;
PCHAR UniquenessDatabaseFile;
PCHAR UniquenessId;
//
// Command to execute at the end of GUI setup
//
PCHAR CmdToExecuteAtEndOfGui = NULL;
//
// Keep track of any optional dirs that the user wants
// to copy
//
unsigned OptionalDirCount;
CHAR *OptionalDirs[MAX_OPTIONALDIRS];
unsigned OptionalDirFlags[MAX_OPTIONALDIRS];
unsigned OptionalDirFileCount;
//
// Keep track of any OEM boot file specified on [OemBootFiles]
// in the script file
//
unsigned OemBootFilesCount;
CHAR *OemBootFiles[MAX_OEMBOOTFILES];
#define TEDM
#ifdef TEDM
BOOLEAN DngAllowNt = FALSE;
#endif
VOID
DnpFetchArguments(
VOID
);
BOOLEAN
DnpParseArguments(
IN int argc,
IN char *argv[]
);
VOID
DnpValidateAndConnectToShare(
VOID
);
VOID
DnpValidateAndInspectTarget(
VOID
);
VOID
DnpCheckMemory(
VOID
);
BOOLEAN
DnpIsValidLocalSource(
IN CHAR Drive
);
VOID
DnpDetermineLocalSourceDrive(
VOID
);
BOOLEAN
DnpConstructLocalSourceList(
OUT PCHAR DriveList
);
VOID
DnpReadInf(
VOID
);
VOID
DnpCheckEnvironment(
VOID
);
BOOLEAN
RememberOptionalDir(
IN PCHAR Dir,
IN unsigned Flags
);
void
_far
DnInt24(
unsigned deverror,
unsigned errcode,
unsigned _far *devhdr
);
VOID
StartLog(
VOID
);
// in cpu.asm
USHORT
HwGetProcessorType(
VOID
);
VOID
main(
IN int argc,
IN char *argv[]
)
{
#ifdef DOS_V
//
// If CodePage isn't Japan start us Winnt.exe (winntus.exe)
//
if (DnGetCodepage() != 932) {
argv[0] = "winntus";
execv("winntus", argv);
return;
}
#endif // def DOS_V
//
// Parse arguments
//
if(!DnpParseArguments(argc,argv)) {
PCHAR *p;
//
// Bad args. Print usage message and exit.
//
// If user specified /D, display message informing that the
// switch is no longer supported
//
for( (p = DeleteGiven ? DntUsageNoSlashD : DntUsage);
*p;
p++) {
puts(*p);
}
return;
}
//
// establish int 24 handler
//
_harderr(DnInt24);
//
// determine current drive
//
_dos_getdrive(&DngOriginalCurrentDrive);
//
// Initialize screen
//
DnInitializeDisplay();
DnWriteString(DntStandardHeader);
if(DngUnattended) {
//
// Check to see if we should process the contents of
// the script file.
// Note that we need to process the contents of the script file
// only after the video is initialized. Otherwise, we won't be able
// to report fatal errors.
//
if (DngScriptFile) {
DnpFetchArguments();
}
}
#if 0
//
// /D is no longer supported
//
if(DeleteGiven) {
DnDeleteNtTree(CmdLineDelete);
}
#endif
DnpCheckEnvironment();
DnpValidateAndConnectToShare();
DnpReadInf();
DnpCheckMemory();
if(!DngFloppiesOnly) {
DnpDetermineLocalSourceDrive();
}
if(DngCreateFloppies) {
DnCreateBootFloppies();
}
if(!DngFloppiesOnly) {
DnCopyFiles();
DnToNtSetup();
}
DnExit(0);
}
BOOLEAN
RememberOptionalDir(
IN PCHAR Dir,
IN unsigned Flags
)
{
unsigned u;
for (u = 0; u < OptionalDirCount; u++) {
if(!stricmp(OptionalDirs[u],Dir)) {
OptionalDirFlags[u] = Flags;
return (TRUE);
}
}
//
// Not already in there
//
if (OptionalDirCount < MAX_OPTIONALDIRS) {
OptionalDirs[OptionalDirCount] = Dir;
OptionalDirFlags[OptionalDirCount] = Flags;
OptionalDirCount++;
return (TRUE);
}
return (FALSE);
}
BOOLEAN
RememberOemBootFile(
IN PCHAR File
)
{
unsigned u;
for (u = 0; u < OemBootFilesCount; u++) {
if(!stricmp(OemBootFiles[u],File)) {
return (TRUE);
}
}
//
// Not already in there
//
if (OemBootFilesCount < MAX_OEMBOOTFILES) {
OemBootFiles[OemBootFilesCount] = File;
OemBootFilesCount++;
return (TRUE);
}
return (FALSE);
}
VOID
DnpFetchArguments(
VOID
)
{
PCHAR WinntSetupP = WINNT_SETUPPARAMS;
PCHAR WinntYes = WINNT_A_YES;
PCHAR WinntNo = WINNT_A_NO;
int FileHandle;
int Status;
PVOID ScriptHandle;
PCHAR WinntUnattended = WINNT_UNATTENDED;
PCHAR WinntOemPreinstall = WINNT_OEMPREINSTALL;
unsigned LineNumber;
//
// First open the script file as a dos file
//
if(_dos_open(DngScriptFile,O_RDONLY,&FileHandle)) {
//
// fatal error.
//
DnFatalError(&DnsOpenReadScript);
}
//
// Now open it as a INF file
//
LineNumber = 0;
Status = DnInitINFBuffer(FileHandle,&ScriptHandle, &LineNumber);
if(Status == ENOMEM) {
DnFatalError(&DnsOutOfMemory);
} else if(Status) {
DnFatalError(&DnsParseScriptFile, DngScriptFile, LineNumber);
}
//
// We no longer need the file handle
//
_dos_close(FileHandle);
//
// Find out if this is an OEM preinstall
//
if (DnSearchINFSection(ScriptHandle,WinntUnattended)) {
if (DnGetSectionKeyExists(ScriptHandle,WinntUnattended,WinntOemPreinstall)) {
PCHAR Ptr;
//
// OEM preinstall key exists
//
Ptr = DnGetSectionKeyIndex(ScriptHandle,WinntUnattended,WinntOemPreinstall,0);
if (Ptr != NULL) {
if (stricmp(Ptr,WinntYes) == 0) {
//
// This is an OEM pre-install
//
DngOemPreInstall = TRUE;
} else {
//
// Assume this is not an OEM pre-install
//
DngOemPreInstall = FALSE;
}
}
}
if( DngOemPreInstall ) {
//
// Always add to the list of optional directories the directory
// $OEM$
//
RememberOptionalDir(OemSystemDirectory, OPTDIR_OEMSYS);
//
// If this an OEM pre-install, build a list with the name of all
// OEM optional directories.
//
if (DnSearchINFSection(ScriptHandle, WINNT_OEMOPTIONAL)) {
unsigned KeyIndex;
PCHAR DirName;
//
// Add the temporary OEM directories to the array of
// temporary directories.
//
for( KeyIndex = 0;
((DirName = DnGetKeyName(ScriptHandle,WINNT_OEMOPTIONAL,KeyIndex)) != NULL );
KeyIndex++ ) {
//
// We have a valid directory name
//
PCHAR p;
if((p = DnDupString(DirName)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
RememberOptionalDir(p, OPTDIR_OEMOPT);
}
}
//
// If this an OEM pre-install, build a list with the name of all
// OEM boot files.
//
if (DnSearchINFSection(ScriptHandle, WINNT_OEMBOOTFILES)) {
unsigned LineIndex;
PCHAR FileName;
//
// Add the OEM boot files to the array of
// OEM boot files.
//
for( LineIndex = 0;
((FileName = DnGetSectionLineIndex(ScriptHandle,WINNT_OEMBOOTFILES,LineIndex,0)) != NULL );
LineIndex++ ) {
PCHAR q;
if((q = DnDupString(FileName)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
RememberOemBootFile(q);
}
}
}
}
//
// We are done with the ScriptHandle for now
//
DnFreeINFBuffer(ScriptHandle);
}
BOOLEAN
DnpParseArguments(
IN int argc,
IN char *argv[]
)
/*++
Routine Description:
Parse arguments passed to the program. Perform syntactic validation
and fill in defaults where necessary.
Valid arguments:
/d:path - specify installation to remove
(not supported anymore)
/s:sharepoint[path] - specify source sharepoint and path on it
/t:drive[:] - specify temporary local source drive
/i:filename - specify name of inf file
/o - create boot floppies only
/f - turn floppy verification off
/c - suppress free-space check on the floppy
/x - suppress creation of the floppy altogether
/b - floppyless operation
/u - unattended (skip final reboot screen)
/w - [undoc'ed] must be specifed when running
under windows, chicago, etc.
Arguments:
argc - # arguments
argv - array of pointers to arguments
Return Value:
None.
--*/
{
PCHAR arg;
CHAR swit;
PCHAR ArgSwitches[] = { "E", "D", "T", "I", "RX", "R", "S", NULL };
PCHAR RestOfSwitch;
int i;
int l;
//
// Skip program name
//
argv++;
DeleteGiven = SourceGiven = TargetGiven = FALSE;
OptionalDirCount = 0;
CmdLineTarget = CmdLineInf = NULL;
while(--argc) {
if((**argv == '-') || (**argv == '/')) {
swit = argv[0][1];
//
// Process switches that take no arguments here.
//
switch(swit) {
case '?':
return(FALSE); // force usage
case 'f':
case 'F':
argv++;
DngFloppyVerify = FALSE;
continue;
case 'c':
case 'C':
argv++;
DngCheckFloppySpace = FALSE;
continue;
case 'x':
case 'X':
argv++;
DngCreateFloppies = FALSE;
continue;
#ifdef LOGGING
case 'l':
case 'L':
argv++;
StartLog();
continue;
#endif
case 'o':
case 'O':
//
// check for /Ox. /O* is a secret switch that replaces the old /o.
//
switch(argv[0][2]) {
case 'x':
case 'X':
DngWinntFloppies = FALSE;
case '*':
break;
default:
return(FALSE);
}
argv++;
DngFloppiesOnly = TRUE;
continue;
case 'b':
case 'B':
argv++;
DngFloppyless = TRUE;
continue;
case 'u':
case 'U':
if(((argv[0][2] == 'd') || (argv[0][2] == 'D'))
&& ((argv[0][3] == 'f') || (argv[0][3] == 'F'))) {
if((argv[0][4] == ':') && argv[0][5]) {
if((arg = strchr(&argv[0][5],',')) == NULL) {
arg = strchr(&argv[0][5],0);
}
l = arg - &argv[0][5];
UniquenessId = MALLOC(l+2,TRUE);
memcpy(UniquenessId,&argv[0][5],l);
UniquenessId[l] = 0;
if(*arg++) {
if(*arg) {
//
// Now the rest of the param is the filename of
// the uniqueness database
//
UniquenessDatabaseFile = DnDupString(arg);
UniquenessId[l] = '*';
UniquenessId[l+1] = 0;
} else {
return(FALSE);
}
}
} else {
return(FALSE);
}
} else {
DngUnattended = TRUE;
//
// User can say -u:<file> also
//
if(argv[0][2] == ':') {
if(argv[0][3] == 0) {
return(FALSE);
}
if((DngScriptFile = DnDupString(&argv[0][3])) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
}
}
argv++;
continue;
case 'w':
case 'W':
//
// Accept for compatibility but do nothing.
// This flag used to force us to run under Windows,
// when doing a 386 stepping check could crash the system.
// Now we don;t support 386, so this check is never done.
//
argv++;
continue;
#ifdef TEDM
case 'i':
case 'I':
if(!stricmp(argv[0]+1,"I_am_TedM")) {
argv++;
DngAllowNt = TRUE;
continue;
}
#endif
}
//
// Process switches that take arguments here.
//
//
// This code taken from winnt32.c. It has the
// purpose of validating the switch and determining
// where the next argument lines
//
for (i=0; ArgSwitches[i]; i++) {
l = strlen(ArgSwitches[i]);
if (!strnicmp(ArgSwitches[i],&argv[0][1],l)) {
//
// we have a match. Next char of arg must either
// be : or nul. If it's : then arg immediately
// follows. Otherwise, if it's null, then arg must
// be next argument
//
if (argv[0][1+l] == ':') {
arg = &argv[0][2+l];
if (*arg == '\0') {
return (FALSE);
}
RestOfSwitch = &argv[0][2];
break;
} else {
if (argv[0][1+l] == '\0') {
if (argc <= 1) {
//
// no arguments left
//
return (FALSE);
}
RestOfSwitch = &argv[0][2];
argc--;
arg = argv[1];
argv++;
break;
} else {
//
// Do nothing here
//
NULL;
} // if ... else
} // if ... else
} // if ...
} // for
//
// Check termination condition
//
if (!ArgSwitches[i]) {
return (FALSE);
}
switch(swit) {
case 'r':
case 'R':
RememberOptionalDir(
DnDupString(arg),
( (RestOfSwitch[0] == 'X' || RestOfSwitch[0] == 'x') ?
OPTDIR_TEMPONLY : 0 ) );
break;
case 'd':
case 'D':
//
// /D is no longer supported
//
DeleteGiven = TRUE;
return(FALSE);
#if 0
case 'd':
case 'D':
if(DeleteGiven) {
return(FALSE);
} else {
if((CmdLineDelete = DnDupString(arg)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
DeleteGiven = TRUE;
}
break;
#endif
case 's':
case 'S':
if(SourceGiven) {
return(FALSE);
} else {
if((CmdLineSource = DnDupString(arg)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
SourceGiven = TRUE;
}
break;
case 't':
case 'T':
if(TargetGiven) {
return(FALSE);
} else {
if((CmdLineTarget = DnDupString(arg)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
TargetGiven = TRUE;
}
break;
case 'i':
case 'I':
if(InfGiven) {
return(FALSE);
} else {
if((CmdLineInf = DnDupString(arg)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
InfGiven = TRUE;
}
break;
case 'E':
case 'e':
if(CmdToExecuteAtEndOfGui) {
return(FALSE);
} else {
if((CmdToExecuteAtEndOfGui = DnDupString(arg)) == NULL) {
DnFatalError(&DnsOutOfMemory);
}
}
break;
default:
return(FALSE);
}
} else {
return(FALSE);
}
argv++;
}
//
// If /u was specified, make sure /s was also given
// and force /b.
//
if(DngUnattended) {
if(!SourceGiven) {
return(FALSE);
}
DngFloppyless = TRUE;
}
if(DngFloppyless) {
//
// Force us into the floppy creation code.
//
DngCreateFloppies = TRUE;
DngWinntFloppies = TRUE;
}
return(TRUE);
}
VOID
DnpValidateAndConnectToShare(
VOID
)
/*++
Routine Description:
Split the source given by the user into drive and path
components. If the user did not specify a source, prompt him
for one. Look for dos2nt.inf on the source (ie, validate the
source) and keep prompting the user for a share until he enters
one which appears to be valid.
Arguments:
None.
Return Value:
None.
--*/
{
CHAR UserString[256];
PCHAR InfFullName;
PCHAR q;
BOOLEAN ValidSourcePath;
unsigned len;
DnClearClientArea();
DnWriteStatusText(NULL);
//
// Use default inf file if none specified.
//
if(!InfGiven) {
CmdLineInf = DEFAULT_INF_NAME;
}
//
// If the user did not enter a source, prompt him for one.
//
if(SourceGiven) {
strcpy(UserString,CmdLineSource);
} else {
DnDisplayScreen(&DnsNoShareGiven);
DnWriteStatusText("%s %s",DntEnterEqualsContinue,DntF3EqualsExit);
if(getcwd(UserString,sizeof(UserString)-1) == NULL) {
UserString[0] = '\0';
}
DnGetString(UserString,NO_SHARE_X,NO_SHARE_Y,NO_SHARE_W);
}
ValidSourcePath = FALSE;
do {
DnWriteStatusText(DntOpeningInfFile);
//
// Make a copy of the path the user typed leaving extra room.
//
DngSourceRootPath = MALLOC(256,TRUE);
if(len = strlen(UserString)) {
strcpy(DngSourceRootPath,UserString);
//
// If the user typed something like x:, then we want to
// change that to x:. so this does what he expects.
// Doing so also lets the canonicalize routine work.
//
if((DngSourceRootPath[1] == ':') && !DngSourceRootPath[2]) {
DngSourceRootPath[2] = '.';
DngSourceRootPath[3] = 0;
}
//
// Now attempt to canonicalize the name. If this doesn't work,
// then it's definitely not a valid path.
//
if(DnCanonicalizePath(DngSourceRootPath,UserString)) {
strcpy(DngSourceRootPath,UserString);
//
// If the path doesn't end with a backslash,
// append a backslash before appending the inf filename.
//
len = strlen(DngSourceRootPath);
if(DngSourceRootPath[len-1] != '\\') {
DngSourceRootPath[len] = '\\';
DngSourceRootPath[len+1] = 0;
len++;
}
InfFullName = MALLOC(len + strlen(CmdLineInf) + 1,TRUE);
strcpy(InfFullName,DngSourceRootPath);
strcat(InfFullName,CmdLineInf);
//
// Attempt to open the inf file on the source.
// If that fails look for it in the i386 subdirectory.
//
_LOG(("Validate source path: trying %s\n",InfFullName));
ValidSourcePath = (BOOLEAN)(_dos_open(InfFullName,O_RDONLY,&InfFileHandle) == 0);
FREE(InfFullName);
if(!ValidSourcePath) {
InfFullName = MALLOC(len+strlen(CmdLineInf)+strlen(x86DirName)+1,TRUE);
strcpy(InfFullName,DngSourceRootPath);
strcat(InfFullName,x86DirName+1);
strcat(InfFullName,"\\");
strcat(InfFullName,CmdLineInf);
_LOG(("Validate source path: trying %s\n",InfFullName));
ValidSourcePath = (BOOLEAN)(_dos_open(InfFullName,O_RDONLY,&InfFileHandle) == 0);
FREE(InfFullName);
if(ValidSourcePath) {
//
// Change the source to the i386 subdirectory.
//
q = DngSourceRootPath;
DngSourceRootPath = MALLOC(strlen(q)+strlen(x86DirName),TRUE);
strcpy(DngSourceRootPath,q);
strcat(DngSourceRootPath,x86DirName+1);
FREE(q);
}
}
}
}
if(!ValidSourcePath) {
FREE(DngSourceRootPath);
DnClearClientArea();
DnDisplayScreen(&DnsBadSource);
DnWriteStatusText("%s %s",DntEnterEqualsContinue,DntF3EqualsExit);
DnGetString(UserString,NO_SHARE_X,BAD_SHARE_Y,NO_SHARE_W);
}
} while(!ValidSourcePath);
//
// Make sure DngSourceRootPath does not end with a backslash.
// and trim the buffer down to size.
//
len = strlen(DngSourceRootPath);
if(DngSourceRootPath[len-1] == '\\') {
DngSourceRootPath[len-1] = 0;
}
if(q = DnDupString(DngSourceRootPath)) {
FREE(DngSourceRootPath);
DngSourceRootPath = q;
}
_LOG(("Source root path is %s\n",DngSourceRootPath));
}
VOID
DnpReadInf(
VOID
)
/*++
Routine Description:
Read the INF file. Does not return if error.
Arguments:
None.
Return Value:
None.
--*/
{
int Status;
PCHAR p;
unsigned LineNumber;
DnWriteStatusText(DntReadingInf,CmdLineInf);
DnClearClientArea();
LineNumber = 0;
Status = DnInitINFBuffer(InfFileHandle,&DngInfHandle,&LineNumber);
if(Status == ENOMEM) {
DnFatalError(&DnsOutOfMemory);
} else if(Status) {
DnFatalError(&DnsBadInf);
}
_dos_close(InfFileHandle);
//
// Determine product type (workstation/server)
//
p = DnGetSectionKeyIndex(DngInfHandle,DnfMiscellaneous,"ProductType",0);
if(p && atoi(p)) {
DngServer = TRUE;
}
DnPositionCursor(0,0);
DnWriteString(DngServer ? DntServerHeader : DntWorkstationHeader);
//
// Get mandatory optional components
//
LineNumber = 0;
while(p = DnGetSectionLineIndex(DngInfHandle,"OptionalSrcDirs",LineNumber++,0)) {
RememberOptionalDir(p,OPTDIR_TEMPONLY);
}
}
VOID
DnpCheckEnvironment(
VOID
)
/*++
Routine Description:
Verify that the following are true:
- DOS major version 3 or greater
- there is a floppy drive at a: that is 1.2 meg or greater
If any of the above are not true, abort with a fatal error.
Arguments:
None.
Return Value:
None.
--*/
{
UCHAR DeviceParams[256];
unsigned char _near * pDeviceParams = DeviceParams;
DnWriteStatusText(DntInspectingComputer);
DeviceParams[0] = 0; // get default device params
_asm {
//
// Really there is no reason we can't run on NT-x86 --
// because currently we emulate a 286 on non-x86 machines,
// just executing the standard checks below have the effect
// of preventing us from running on non-x86 machines and
// allowing us to run on nt-x86.
//
// However we explicitly check for NT here and refuse
// to run on it so it looks better to the user.
//
//
// Check if we're on NT.
// The true version on NT is 5.50.
//
mov ax,3306h
sub bx,bx
int 21h
cmp bx,3205h // check for v. 5.50
jne checkwin
#ifdef TEDM
cmp DngAllowNt,1
je checkflop
#endif
push seg DnsCantRunOnNt
push offset DnsCantRunOnNt
call DnFatalError // doesn't return
checkwin:
//
// The /w switch used to be necessary since we could crash Windows
// checking the CPU stepping on a 386. However since we don't support
// 386 any more, we never do that check and we can simply detect
// whether we're on Windows. The /w switch is not necessary.
//
mov ax,1600h
int 2fh
test al,7fh
jz checkcpu
mov DngWindows,1
checkcpu:
//
// Check CPU type. Fail if not greater than 386.
//
call HwGetProcessorType
cmp ax,3
ja checkflop
push seg DnsRequires486
push offset DnsRequires486
call DnFatalError // doesn't return
checkflop:
//
// If this is not a floppyless installation, check for 1.2MB
// or greater A:. Get the default device params for drive A:
// and check the device type field.
//
cmp DngFloppyless,1 // floppyless installation?
je checkdosver // yes, no floppy drive required
mov ax,440dh // ioctl
mov bl,1 // drive a:
mov cx,860h // category disk, func get params
mov dx,pDeviceParams // ds is already correct
int 21h
jnc gotdevparams
flopperr:
push seg DnsRequiresFloppy
push offset DnsRequiresFloppy
call DnFatalError // doesn't return
gotdevparams:
//
// Check to make sure that the device is removable and perform
// checks on the media type
//
mov si,pDeviceParams
test [si+2],1 // bit 0 clear if removable
jnz flopperr
#ifdef ALLOW_525
cmp [si+1],1 // media type = 1.2meg floppy?
jz checkdosver
#endif
cmp [si+1],7 // media type = 1.4meg floppy
jb flopperr // or greater?
checkdosver:
//
// Check DOS version >= 3.2.
//
mov ax, 3000h // function 30h -- get DOS version
int 21h
cmp al,3
ja checkdone // >= 4.0
jne baddosver // < 3.0
cmp ah,20 // 3.2 or above?
jae checkdone // yes
//
// version < 3.2
//
baddosver:
push seg DnsBadDosVersion
push offset DnsBadDosVersion
call DnFatalError
checkdone:
}
}
VOID
DnpCheckMemory(
VOID
)
/*++
Routine Description:
Verify that enough memory is installed in the machine.
Arguments:
None.
Return Value:
None. Does not return in there's not enough memory.
--*/
{
USHORT MemoryK;
ULONG TotalMemory,RequiredMemory;
PCHAR RequiredMemoryStr;
DnWriteStatusText(DntInspectingComputer);
//
// I cannot figure out a reliable way to determine the amount of
// memory in the machine. Int 15 func 88 is likely to be hooked by
// himem.sys or some other memory manager to return 0. DOS maintains
// the original amount of extended memory but to get to this value
// you have to execute the sysvars undocumented int21 ah=52 call, and
// even then what about versions previous to dos 4? Calling himem to
// ask for the total amount of xms memory does not give you the total
// amount of extended memory, just the amount of xms memory.
// So we'll short-circuit the memory check code by always deciding that
// there's 50MB of extended memory. This should always be big enough,
// and this way the rest of the code stays intact, ready to work if
// we figure out a way to make the memory determination. Just replace
// the following line with the check, and make sure MemoryK is set to
// the amount of extended memory in K.
//
// Update: one might be able to get the amount of extended memory by
// looking in CMOS. See the code below.
//
MemoryK = 50*1024;
#if 0
_asm {
mov al,0x17 // get extended memory low
cli
out 0x70,al
jmp short x1
x1: in al,0x71
sti
mov bl,al
mov al,0x18 // get extended memory high
cli
out 0x70,al
jmp short x2
x2: in al,0x71
sti
mov bh,al
mov MemoryK,bx
}
#endif
//
// Account for conventional memory. Simplistic, but good enough.
//
MemoryK += 1024;
TotalMemory = (ULONG)MemoryK * 1024L;
RequiredMemoryStr = DnGetSectionKeyIndex( DngInfHandle,
DnfMiscellaneous,
DnkMinimumMemory,
0
);
//
// If the required memory is not specified in the inf, force an error
// to get someone's attention so we can fix dosnet.inf.
//
RequiredMemory = RequiredMemoryStr ? (ULONG)atol(RequiredMemoryStr) : 0xffffffff;
if(TotalMemory < RequiredMemory) {
CHAR Decimal[10];
ULONG r;
CHAR Line1[100],Line2[100];
r = ((RequiredMemory % (1024L*1024L)) * 100L) / (1024L*1024L);
if(r) {
sprintf(Decimal,".%lu",r);
} else {
Decimal[0] = 0;
}
sprintf(Line1,DnsNotEnoughMemory.Strings[NOMEM_LINE1],RequiredMemory/(1024L*1024L),Decimal);
DnsNotEnoughMemory.Strings[NOMEM_LINE1] = Line1;
r = ((TotalMemory % (1024L*1024L)) * 100L) / (1024L*1024L);
if(r) {
sprintf(Decimal,".%lu",r);
} else {
Decimal[0] = 0;
}
sprintf(Line2,DnsNotEnoughMemory.Strings[NOMEM_LINE2],TotalMemory/(1024L*1024L),Decimal);
DnsNotEnoughMemory.Strings[NOMEM_LINE2] = Line2;
DnFatalError(&DnsNotEnoughMemory);
}
}
void
_far
DnInt24(
unsigned deverror,
unsigned errcode,
unsigned _far *devhdr
)
/*++
Routine Description:
Int24 handler. We do not perform any special actions on a hard error;
rather we just return FAIL so the caller of the failing api will get
back an error code and take appropriate action itself.
This function should never be invoked directly.
Arguments:
deverror - supplies the device error code.
errcode - the DI register passed by MS-DOS to int 24 handlers.
devhdr - supplies pointer to the device header for the device on which
the hard error occured.
Return Value:
None.
--*/
{
_hardresume(_HARDERR_FAIL);
}
VOID
DnpDetermineLocalSourceDrive(
VOID
)
/*++
Routine Description:
Determine the local source drive, ie, the drive that will contain the
local copy of the windows nt setup source tree. The local source could
have been passed on the command line, in which case we will validate it.
If there was no drive specified, examine each drive in the system looking
for a local, fixed drive with enough free space on it (as specified in
the inf file).
Arguments:
None.
Return Value:
None. Sets the global variable DngTargetDriveLetter.
--*/
{
CHAR DriveList[25];
ULONG RequiredSpace;
DnRemoveLocalSourceTrees();
DnRemovePagingFiles();
DnDetermineSpaceRequirements(&RequiredSpace);
if(TargetGiven) {
//
// Drive was specified on the command line.
// If it's valid, use it.
//
if(DnpIsValidLocalSource(*CmdLineTarget)) {
DngTargetDriveLetter = *CmdLineTarget;
return;
}
//
// Drive specified on the command line is not valid.
//
DnFatalError(
&DnsBadLocalSrcDrive,
(unsigned)(RequiredSpace/(1024L*1024L)),
RequiredSpace
);
}
//
// No drive was specified on the command line or the one that was
// specified is not valid. Construct a list of valid drives.
//
if(!DnpConstructLocalSourceList(DriveList)) {
DnFatalError(
&DnsNoLocalSrcDrives,
(unsigned)(RequiredSpace/(1024L*1024L)),
RequiredSpace
);
}
//
// Use the first drive on the list. BUGBUG add interface here for
// user confirmation and selection from DriveList.
//
DngTargetDriveLetter = DriveList[0];
return;
}
BOOLEAN
DnpIsValidLocalSource(
IN CHAR Drive
)
/*++
Routine Description:
Determine if a drive is valid as a local source.
To be valid a drive must be extant, non-removable, local, and have
enough free space on it.
Arguments:
Drive - drive letter of drive to check.
Return Value:
TRUE if Drive is valid as a local source. FALSE otherwise.
--*/
{
unsigned d = (unsigned)toupper(Drive) - (unsigned)'A' + 1;
struct diskfree_t DiskSpace;
ULONG RequiredSpace,Space;
unsigned DontCare;
DnDetermineSpaceRequirements(&RequiredSpace);
if( DnIsDriveValid(d)
&& !DnIsDriveRemote(d,NULL)
&& !DnIsDriveRemovable(d)
&& !DnIsDriveCompressedVolume(d,&DontCare))
{
//
// Check free space on the drive.
//
if(!_dos_getdiskfree(d,&DiskSpace)) {
Space = (ULONG)DiskSpace.avail_clusters
* (ULONG)DiskSpace.sectors_per_cluster
* (ULONG)DiskSpace.bytes_per_sector;
if(Space >= RequiredSpace) {
return(TRUE);
}
}
}
return(FALSE);
}
BOOLEAN
DnpConstructLocalSourceList(
OUT PCHAR DriveList
)
/*++
Routine Description:
Construct a list of drives that are valid for use as a local source.
To be valid a drive must be extant, non-removable, local, and have
enough free space on it.
The 'list' is a string with a character for each valid drive, terminated
by a nul character, ie,
CDE0
Arguments:
DriveList - receives the string in the above format.
Return Value:
FALSE if no valid drives were found. TRUE if at least one was.
--*/
{
PCHAR p = DriveList;
BOOLEAN b = FALSE;
CHAR Drive;
for(Drive='C'; Drive<='Z'; Drive++) {
if(DnpIsValidLocalSource(Drive)) {
*p++ = Drive;
b = TRUE;
}
}
*p = 0;
return(b);
}
#ifdef LOGGING
FILE *_LogFile;
VOID
StartLog(
VOID
)
{
if(!_LogFile) {
_LogFile = fopen("c:\\$winnt.log","wt");
}
}
#if 0
VOID
EndLog(
VOID
)
{
if(_LogFile) {
fclose(_LogFile);
_LogFile = NULL;
}
}
#endif
VOID
__LOG(
IN PCHAR FormatString,
...
)
{
va_list arglist;
if(_LogFile) {
va_start(arglist,FormatString);
vfprintf(_LogFile,FormatString,arglist);
va_end(arglist);
fflush(_LogFile);
}
}
#endif // def LOGGING