windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/cmdcons/extprog.c
2020-09-26 16:20:57 +08:00

2401 lines
67 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
extprog.c
Abstract:
This module implements all commands that
execute external programs.
Author:
Wesley Witt (wesw) 21-Oct-1998
Revision History:
--*/
#include "cmdcons.h"
#pragma hdrstop
#define FLG_GOT_P 0x00000100
#define FLG_GOT_R 0x00000200
#define FLG_DRIVE_MASK 0x000000ff
#define FLG_GOT_Q 0x00000100
#define FLG_GOT_FS 0x00000200
#define FLG_GOT_FAT 0x10000000
#define FLG_GOT_FAT32 0x20000000
#define FLG_GOT_NTFS 0x40000000
LPCWSTR szAutochkExe = L"AUTOCHK.EXE";
LPCWSTR szAutofmtExe = L"AUTOFMT.EXE";
BOOLEAN SawInterimMsgs;
ULONG ChkdskMessageId;
VOID
SpPtDetermineRegionSpace(
IN PDISK_REGION pRegion
);
LPWSTR
pRcDoesFileExist(
IN LPCWSTR PathPart1,
IN LPCWSTR PathPart2, OPTIONAL
IN LPCWSTR PathPart3 OPTIONAL
);
NTSTATUS
pRcAutochkProgressHandler(
IN PSETUP_FMIFS_MESSAGE Message
);
PWSTR
RcLocateImage(
IN PWSTR ImageName
)
{
LPWSTR BinaryName;
ULONG i;
WCHAR buf[ MAX_PATH + 1 ];
LPWSTR p,s;
NTSTATUS Status;
//
// Locate the binary. First see if we can find it
// on the setup boot media (boot floppies, ~bt directory, etc).
// If not, we have to try to grab it from the setup media (CD-ROM,
// ~ls directory, etc).
//
BinaryName = pRcDoesFileExist(
_CmdConsBlock->BootDevicePath,
_CmdConsBlock->DirectoryOnBootDevice,
ImageName
);
if (BinaryName) {
return BinaryName;
}
//
// look for a local $WIN_NT$.~LS source
//
for (i=0; i<26; i++) {
swprintf( buf, L"\\??\\%c:",i+L'A');
if (RcIsFileOnRemovableMedia(buf) != STATUS_SUCCESS) {
BinaryName = pRcDoesFileExist(
buf,
((!IsNEC_98) ? L"\\$win_nt$.~ls\\i386\\" : L"\\$win_nt$.~ls\\nec98\\"),
ImageName
);
if (BinaryName) {
return BinaryName;
}
}
}
if (BinaryName == NULL) {
//
// look for the CDROM drive letter
//
for (i=0; i<26; i++) {
swprintf( buf, L"\\??\\%c:",i+L'A');
if (RcIsFileOnCDROM(buf) == STATUS_SUCCESS) {
BinaryName = pRcDoesFileExist(
buf,
((!IsNEC_98) ? L"\\i386\\" : L"\\nec98\\"),
ImageName
);
if (BinaryName) {
return BinaryName;
}
}
}
}
//
// failed to find the image on any installation media
//
if (InBatchMode) {
RcMessageOut( MSG_FAILED_COULDNT_FIND_BINARY_ANYWHERE, ImageName );
return NULL;
}
//
// ask the user to type its location
//
RcMessageOut( MSG_COULDNT_FIND_BINARY, ImageName );
//
// prepend \\??\\ to it
//
swprintf( buf, L"\\??\\");
RcLineIn( &(buf[4]), MAX_PATH-4 );
//
// append the name of the program if it exists
//
BinaryName = pRcDoesFileExist( buf, NULL, ImageName );
if (BinaryName == NULL) {
//
// assume that if it failed, the user just specified the entire file path
//
BinaryName = pRcDoesFileExist( buf, NULL, NULL );
//
// if we still can't find it, print an error, return.
//
if (BinaryName == NULL) {
RcMessageOut( MSG_FAILED_COULDNT_FIND_BINARY_ANYWHERE, ImageName );
return NULL;
}
}
return BinaryName;
}
ULONG
RcCmdChkdsk(
IN PTOKENIZED_LINE TokenizedLine
)
/*++
Routine Description:
Top-level routine supporting the chkdsk command in the setup diagnostic
command interpreter.
Chkdsk may be specified entirely without arguments, in which case the
current drive is implied with no switches. Optionally, the following
switches are accepted, and passed directly to autochk.
/p - check even if not dirty
/r - recover (implies /p)
x: - drive letter of drive to check
In addition we always pass /t which causes autochk to call setup's
IOCTL_SETUP_FMIFS_MESSAGE to communicate progress.
Arguments:
TokenizedLine - supplies structure built by the line parser describing
each string on the line as typed by the user.
Return Value:
None.
--*/
{
PLINE_TOKEN Token;
LPCWSTR Arg;
unsigned Flags;
BOOLEAN b;
BOOLEAN doHelp;
LPWSTR ArgList,p,q,s,AutochkBinary;
ULONG AutochkStatus;
ULONG i;
NTSTATUS Status = 0;
UNICODE_STRING UnicodeString;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES Obja;
ULONG n;
LARGE_INTEGER Time;
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
PFILE_FS_SIZE_INFORMATION SizeInfo;
LPWSTR Numbers[5];
WCHAR buf[ MAX_PATH + 1 ];
//
// There should be at least one token for CHKDSK itself.
// There could be additional ones for arguments.
//
ASSERT(TokenizedLine->TokenCount >= 1);
if (RcCmdParseHelp( TokenizedLine, MSG_CHKDSK_HELP )) {
return 1;
}
Flags = 0;
b = TRUE;
doHelp = FALSE;
Token = TokenizedLine->Tokens->Next;
while(b && Token) {
Arg = Token->String;
if((Arg[0] == L'-') || (Arg[0] == L'/')) {
switch(Arg[1]) {
case L'p':
case L'P':
if(Flags & FLG_GOT_P) {
b = FALSE;
} else {
Flags |= FLG_GOT_P;
}
break;
case L'r':
case L'R':
if(Flags & FLG_GOT_R) {
b = FALSE;
} else {
Flags |= FLG_GOT_R;
}
break;
default:
b = FALSE;
break;
}
} else {
//
// Not arg, could be drive spec
//
if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && !Arg[2]) {
if(Flags & FLG_DRIVE_MASK) {
b = FALSE;
} else {
Flags |= (unsigned)RcToUpper(Arg[0]);
}
} else {
b = FALSE;
}
}
Token = Token->Next;
}
if(!b) {
RcMessageOut(MSG_SYNTAX_ERROR);
return 1;
}
//
// Check drive.
//
if(!(Flags & FLG_DRIVE_MASK)) {
Flags |= (unsigned)RcGetCurrentDriveLetter();
}
if(!RcIsDriveApparentlyValid((WCHAR)(Flags & FLG_DRIVE_MASK))) {
RcMessageOut(MSG_INVALID_DRIVE);
return 1;
}
//
// find the autochk.exe image
//
AutochkBinary = RcLocateImage( (PWSTR)szAutochkExe );
if (AutochkBinary == NULL) {
return 1;
}
//
// Get volume info and print initial report.
// NOTE: we do NOT leave the handle open, even though we may need it
// later, since that could interfere with autochk's ability to
// check the disk!
//
p = SpMemAlloc(100);
swprintf(p,L"\\DosDevices\\%c:\\",(WCHAR)(Flags & FLG_DRIVE_MASK));
INIT_OBJA(&Obja,&UnicodeString,p);
Status = ZwOpenFile(
&Handle,
FILE_READ_ATTRIBUTES,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE
);
SpMemFree(p);
if(NT_SUCCESS(Status)) {
VolumeInfo = _CmdConsBlock->TemporaryBuffer;
Status = ZwQueryVolumeInformationFile(
Handle,
&IoStatusBlock,
VolumeInfo,
_CmdConsBlock->TemporaryBufferSize,
FileFsVolumeInformation
);
ZwClose(Handle);
if(NT_SUCCESS(Status)) {
//
// To mimic chkdsk running from cmd.exe, we want to print out
// a nice 2 lines like
//
// Volume VOLUME_LABEL created DATE TIME
// Volume Serial Number is xxxx-xxxx
//
// But, some volumes won't have labels and some file systems
// don't support recording the volume creation time. If there's
// no volume creation time, we don't print out the first time
// at all. If there is a volume creation time, we are careful
// to distinguish the cases where there's a label and where
// there's no label.
//
// The serial number is always printed.
//
n = VolumeInfo->VolumeSerialNumber;
if(Time.QuadPart = VolumeInfo->VolumeCreationTime.QuadPart) {
//
// Save values since we need to recycle the temporary buffer.
//
VolumeInfo->VolumeLabel[VolumeInfo->VolumeLabelLength/sizeof(WCHAR)] = 0;
p = SpDupStringW(VolumeInfo->VolumeLabel);
RcFormatDateTime(&Time,_CmdConsBlock->TemporaryBuffer);
q = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
RcMessageOut(
*p ? MSG_CHKDSK_REPORT_1a : MSG_CHKDSK_REPORT_1b,
q,
p
);
SpMemFree(q);
SpMemFree(p);
}
RcMessageOut(MSG_CHKDSK_REPORT_2,n >> 16,n & 0xffff);
}
}
//
// Build argument list.
//
ArgList = SpMemAlloc(200);
p = ArgList;
*p++ = L'-';
*p++ = L't';
if(Flags & FLG_GOT_P) {
*p++ = L' ';
*p++ = L'-';
*p++ = L'p';
}
if(Flags & FLG_GOT_R) {
*p++ = L' ';
*p++ = L'-';
*p++ = L'r';
}
*p++ = L' ';
wcscpy(p,L"\\DosDevices\\");
p += wcslen(p);
*p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
*p++ = L':';
*p = 0;
if (!InBatchMode) {
SpSetAutochkCallback(pRcAutochkProgressHandler);
SawInterimMsgs = FALSE;
ChkdskMessageId = MSG_CHKDSK_CHECKING_1;
}
Status = SpExecuteImage(AutochkBinary,&AutochkStatus,1,ArgList);
if (!InBatchMode) {
SpSetAutochkCallback(NULL);
}
if(NT_SUCCESS(Status)) {
switch(AutochkStatus) {
case 0: // success
if(SawInterimMsgs) {
//
// Success, and chkdsk actually ran.
//
RcMessageOut(MSG_CHKDSK_COMPLETE);
} else {
//
// Success, but it doesn't seem like we actually did much.
// Tell the user something meaningful.
//
RcMessageOut(MSG_VOLUME_CLEAN);
}
break;
case 3: // serious error, not fixed
RcTextOut(L"\n");
RcMessageOut(MSG_VOLUME_CHECKED_BUT_HOSED);
break;
default: // errs fixed, also happens when no disk in drive or unsupported fs
if(SawInterimMsgs) {
if(Flags & FLG_GOT_R) {
RcMessageOut(MSG_VOLUME_CHECKED_AND_FIXED);
} else {
RcMessageOut(MSG_VOLUME_CHECKED_AND_FOUND);
}
} else {
RcMessageOut(MSG_CHKDSK_UNSUPPORTED_VOLUME);
}
break;
}
//
// Get size info for additional reporting
//
p = SpMemAlloc(100);
swprintf(p,L"\\DosDevices\\%c:\\",(WCHAR)(Flags & FLG_DRIVE_MASK));
INIT_OBJA(&Obja,&UnicodeString,p);
Status = ZwOpenFile(
&Handle,
FILE_READ_ATTRIBUTES,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE
);
SpMemFree(p);
if(NT_SUCCESS(Status)) {
SizeInfo = _CmdConsBlock->TemporaryBuffer;
Status = ZwQueryVolumeInformationFile(
Handle,
&IoStatusBlock,
SizeInfo,
_CmdConsBlock->TemporaryBufferSize,
FileFsSizeInformation
);
ZwClose(Handle);
if(NT_SUCCESS(Status)) {
p = (LPWSTR)((UCHAR *)_CmdConsBlock->TemporaryBuffer + sizeof(FILE_FS_SIZE_INFORMATION));
//
// Total disk space, in K
//
RcFormat64BitIntForOutput(
((SizeInfo->TotalAllocationUnits.QuadPart * SizeInfo->SectorsPerAllocationUnit) * SizeInfo->BytesPerSector) / 1024i64,
p,
FALSE
);
Numbers[0] = SpDupStringW(p);
//
// Available disk space, in K
//
RcFormat64BitIntForOutput(
((SizeInfo->AvailableAllocationUnits.QuadPart * SizeInfo->SectorsPerAllocationUnit) * SizeInfo->BytesPerSector) / 1024i64,
p,
FALSE
);
Numbers[1] = SpDupStringW(p);
//
// Bytes per cluster
//
RcFormat64BitIntForOutput(
(LONGLONG)SizeInfo->SectorsPerAllocationUnit * (LONGLONG)SizeInfo->BytesPerSector,
p,
FALSE
);
Numbers[2] = SpDupStringW(p);
//
// Total clusters
//
RcFormat64BitIntForOutput(
SizeInfo->TotalAllocationUnits.QuadPart,
p,
FALSE
);
Numbers[3] = SpDupStringW(p);
//
// Available clusters
//
RcFormat64BitIntForOutput(
SizeInfo->AvailableAllocationUnits.QuadPart,
p,
FALSE
);
Numbers[4] = SpDupStringW(p);
RcMessageOut(
MSG_CHKDSK_REPORT_3,
Numbers[0],
Numbers[1],
Numbers[2],
Numbers[3],
Numbers[4]
);
for(n=0; n<5; n++) {
SpMemFree(Numbers[n]);
}
}
}
} else {
RcNtError(Status,MSG_VOLUME_NOT_CHECKED);
}
SpMemFree(ArgList);
SpMemFree(AutochkBinary);
return 1;
}
LPWSTR
pRcDoesFileExist(
IN LPCWSTR PathPart1,
IN LPCWSTR PathPart2, OPTIONAL
IN LPCWSTR PathPart3 OPTIONAL
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE Handle;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING UnicodeString;
LPWSTR p;
wcscpy(_CmdConsBlock->TemporaryBuffer,PathPart1);
if(PathPart2) {
SpConcatenatePaths(_CmdConsBlock->TemporaryBuffer,PathPart2);
}
if(PathPart3) {
SpConcatenatePaths(_CmdConsBlock->TemporaryBuffer,PathPart3);
}
INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer);
Status = ZwOpenFile(
&Handle,
FILE_READ_ATTRIBUTES,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_NON_DIRECTORY_FILE
);
if(NT_SUCCESS(Status)) {
ZwClose(Handle);
p = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
} else {
p = NULL;
}
return(p);
}
NTSTATUS
pRcAutochkProgressHandler(
IN PSETUP_FMIFS_MESSAGE Message
)
{
ULONG Percent;
//
// We're getting called in the context of a process other than usetup.exe,
// which means we have no access to things like the video buffer.
// In some cases we need to attach to usetup.exe so the right thing happens
// if we try to access the screen or get keyboard input, etc.
//
switch(Message->FmifsPacketType) {
case FmIfsPercentCompleted:
//
// The packet is in user-mode address space, so we need to pull out
// the percent complete value before attaching to usetup.exe.
//
// The bandwidth for communication between autochk and us is very
// limited. If the drive is clean and is thus not checked, we'll see
// only a 100% complete message. Thus we have to guess what happened
// so we can print out something meaningful to the user if the volume
// appears clean.
//
Percent = ((PFMIFS_PERCENT_COMPLETE_INFORMATION)Message->FmifsPacket)->PercentCompleted;
if(Percent == 100) {
//
// Avoid printing 100% if we didn't actually do anything.
//
if(!SawInterimMsgs) {
break;
}
} else {
SawInterimMsgs = TRUE;
}
KeAttachProcess(PEProcessToPKProcess(_CmdConsBlock->UsetupProcess));
if(!Percent) {
RcMessageOut(ChkdskMessageId);
ChkdskMessageId = MSG_CHKDSK_CHECKING_2;
}
RcMessageOut(MSG_VOLUME_PERCENT_COMPLETE,Percent);
RcTextOut(L"\r");
KeDetachProcess();
break;
default:
KdPrint(("SPCMDCON: Unhandled fmifs message type %u\r\n",Message->FmifsPacketType));
break;
}
return(STATUS_SUCCESS);
}
ULONG
RcCmdFormat(
IN PTOKENIZED_LINE TokenizedLine
)
/*++
Routine Description:
Top-level routine supporting the chkdsk command in the setup diagnostic
command interpreter.
Chkdsk may be specified entirely without arguments, in which case the
current drive is implied with no switches. Optionally, the following
switches are accepted, and passed directly to autochk.
/p - check even if not dirty
/r - recover (implies /p)
x: - drive letter of drive to check
In addition we always pass /t which causes autochk to call setup's
IOCTL_SETUP_FMIFS_MESSAGE to communicate progress.
Arguments:
TokenizedLine - supplies structure built by the line parser describing
each string on the line as typed by the user.
Return Value:
None.
--*/
{
PLINE_TOKEN Token;
LPCWSTR Arg;
unsigned Flags;
BOOLEAN b;
BOOLEAN doHelp;
LPWSTR ArgList,p,q,s,AutofmtBinary;
ULONG AutofmtStatus;
ULONG i;
NTSTATUS Status = 0;
UNICODE_STRING UnicodeString;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES Obja;
ULONG n;
LARGE_INTEGER Time;
PFILE_FS_VOLUME_INFORMATION VolumeInfo;
PFILE_FS_SIZE_INFORMATION SizeInfo;
LPWSTR Numbers[5];
WCHAR buf[ MAX_PATH + 1 ];
ULONG PartitionOrdinal = 0;
PDISK_REGION PartitionRegion;
PWSTR PartitionPath;
FilesystemType FileSystemType;
WCHAR FullPath[MAX_PATH] = {0};
//
// There should be at least one token for FORMAT itself.
// There could be additional ones for arguments.
//
ASSERT(TokenizedLine->TokenCount >= 1);
if (RcCmdParseHelp( TokenizedLine, MSG_FORMAT_HELP )) {
return 1;
}
Flags = 0;
b = TRUE;
doHelp = FALSE;
Token = TokenizedLine->Tokens->Next;
while(b && Token) {
Arg = Token->String;
if((Arg[0] == L'-') || (Arg[0] == L'/')) {
switch(Arg[1]) {
case L'q':
case L'Q':
if(Flags & FLG_GOT_Q) {
b = FALSE;
} else {
Flags |= FLG_GOT_Q;
}
break;
case L'f':
case L'F':
if (Arg[2] == L's' || Arg[2] == L'S' || Arg[3] == L':') {
if(Flags & FLG_GOT_FS) {
b = FALSE;
} else {
s = wcschr(Arg,L' ');
if (s) {
*s = 0;
}
if (_wcsicmp(&Arg[4],L"fat") == 0) {
Flags |= FLG_GOT_FS;
Flags |= FLG_GOT_FAT;
} else if (_wcsicmp(&Arg[4],L"fat32") == 0) {
Flags |= FLG_GOT_FS;
Flags |= FLG_GOT_FAT32;
} else if (_wcsicmp(&Arg[4],L"ntfs") == 0) {
Flags |= FLG_GOT_FS;
Flags |= FLG_GOT_NTFS;
} else {
b = FALSE;
}
}
} else {
b = FALSE;
}
break;
default:
b = FALSE;
break;
}
} else {
//
// Not arg, could be drive spec
//
if(RcIsAlpha(Arg[0]) && (Arg[1] == L':') && !Arg[2]) {
if(Flags & FLG_DRIVE_MASK) {
b = FALSE;
} else {
Flags |= (unsigned)RcToUpper(Arg[0]);
}
} else if (Arg[0] == L'\\') {
wcscpy(FullPath, Arg);
} else {
b = FALSE;
}
}
Token = Token->Next;
}
if(!b) {
RcMessageOut(MSG_SYNTAX_ERROR);
return 1;
}
//
// Check drive.
//
if (FullPath[0] == UNICODE_NULL) {
if(!(Flags & FLG_DRIVE_MASK)) {
RcMessageOut(MSG_INVALID_DRIVE);
return 1;
}
if(!RcIsDriveApparentlyValid((WCHAR)(Flags & FLG_DRIVE_MASK))) {
RcMessageOut(MSG_INVALID_DRIVE);
return 1;
}
}
//
// we don't allow formatting removable media
//
if (FullPath[0] == UNICODE_NULL) {
swprintf(TemporaryBuffer, L"\\??\\%c:",(WCHAR)(Flags & FLG_DRIVE_MASK));
} else {
wcscpy(TemporaryBuffer, FullPath);
}
if (RcIsFileOnRemovableMedia(TemporaryBuffer) == STATUS_SUCCESS) {
RcMessageOut(MSG_CANNOT_FORMAT_REMOVABLE);
return 1;
}
//
// Locate the autofmt.exe binary
//
AutofmtBinary = RcLocateImage( (PWSTR)szAutofmtExe );
if (AutofmtBinary == NULL) {
return 1;
}
if (!InBatchMode) {
LPWSTR YesNo;
WCHAR Text[3];
YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNO, NULL, 0 );
if( YesNo ) {
p = TemporaryBuffer;
*p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
*p++ = L':';
*p = 0;
RcMessageOut( MSG_FORMAT_HEADER, TemporaryBuffer );
if( RcLineIn( Text, 2 ) ) {
if( (Text[0] == YesNo[2]) || (Text[0] == YesNo[3]) ) {
//
// the answer was no
//
return 1;
}
}
SpMemFree( YesNo );
}
}
//
// get the partion region
//
if (FullPath[0] == UNICODE_NULL) {
p = TemporaryBuffer;
*p++ = (WCHAR)(Flags & FLG_DRIVE_MASK);
*p++ = L':';
*p = 0;
PartitionRegion = SpRegionFromDosName(TemporaryBuffer);
//
// Make SURE it's not partition0! The results of formatting partition0
// are so disasterous that this warrants a special check.
//
PartitionOrdinal = SpPtGetOrdinal(PartitionRegion,PartitionOrdinalCurrent);
//
// Get the device path of the partition to format
//
SpNtNameFromRegion(
PartitionRegion,
TemporaryBuffer,
sizeof(TemporaryBuffer),
PartitionOrdinalCurrent
);
} else {
PartitionRegion = SpRegionFromNtName(FullPath, PartitionOrdinalCurrent);
if (PartitionRegion) {
PartitionOrdinal = SpPtGetOrdinal(PartitionRegion, PartitionOrdinalCurrent);
} else {
PartitionOrdinal = 0; // will err out below
}
wcscpy(TemporaryBuffer, FullPath);
}
if(!PartitionOrdinal) {
RcMessageOut(MSG_SYNTAX_ERROR);
return 1;
}
//
// Build argument list.
//
ArgList = SpMemAlloc(4096);
p = ArgList;
wcscpy(p,TemporaryBuffer);
p += wcslen(p);
*p++ = L' ';
*p++ = L'-';
*p++ = L't';
*p++ = L' ';
if(Flags & FLG_GOT_Q) {
*p++ = L'-';
*p++ = L'Q';
*p++ = L' ';
}
if(Flags & FLG_GOT_FS) {
if (Flags & FLG_GOT_FAT) {
wcscpy(p,L"/fs:fat ");
FileSystemType = FilesystemFat;
} else if (Flags & FLG_GOT_FAT32) {
wcscpy(p,L"/fs:fat32 ");
FileSystemType = FilesystemFat32;
} else if (Flags & FLG_GOT_NTFS) {
wcscpy(p,L"/fs:ntfs ");
FileSystemType = FilesystemNtfs;
}
p += wcslen(p);
} else {
FileSystemType = FilesystemNtfs;
wcscpy(p,L"/fs:ntfs ");
p += wcslen(p);
}
*p = 0;
if (!InBatchMode) {
SpSetAutochkCallback(pRcAutochkProgressHandler);
SawInterimMsgs = FALSE;
ChkdskMessageId = MSG_FORMAT_FORMATTING_1;
}
Status = SpExecuteImage(AutofmtBinary,&AutofmtStatus,1,ArgList);
if (!InBatchMode) {
SpSetAutochkCallback(NULL);
}
if(!NT_SUCCESS(Status)) {
RcNtError(Status,MSG_VOLUME_NOT_FORMATTED);
} else {
PartitionRegion->Filesystem = FileSystemType;
SpFormatMessage( PartitionRegion->TypeName,
sizeof(PartitionRegion->TypeName),
SP_TEXT_FS_NAME_BASE + PartitionRegion->Filesystem );
//
// Reset the volume label
//
PartitionRegion->VolumeLabel[0] = L'\0';
SpPtDetermineRegionSpace( PartitionRegion );
}
SpMemFree(ArgList);
SpMemFree(AutofmtBinary);
return 1;
}
typedef struct _FDISK_REGION {
PWSTR DeviceName;
PDISK_REGION Region;
ULONGLONG MaxSize;
ULONGLONG RequiredSize;
} FDISK_REGION, *PFDISK_REGION;
BOOL
RcFdiskRegionEnum(
IN PPARTITIONED_DISK Disk,
IN PDISK_REGION Region,
IN ULONG_PTR Context
)
{
WCHAR DeviceName[256];
PWSTR s;
PFDISK_REGION FDiskRegion = (PFDISK_REGION)Context;
ULONGLONG RegionSizeMB;
//
// skip container partitions & continue on
//
if (Region && (Region->ExtendedType == EPTContainerPartition)) {
return TRUE;
}
SpNtNameFromRegion(Region,
DeviceName,
sizeof(DeviceName),
PartitionOrdinalCurrent);
s = wcsrchr(DeviceName,L'\\');
if (s == NULL) {
return TRUE;
}
*s = 0;
RegionSizeMB = SpPtSectorCountToMB(Disk->HardDisk, Region->SectorCount);
if ((RegionSizeMB > FDiskRegion->MaxSize) &&
(RegionSizeMB >= FDiskRegion->RequiredSize) &&
(Region->PartitionedSpace == FALSE) &&
(_wcsicmp(DeviceName, FDiskRegion->DeviceName) == 0)){
FDiskRegion->MaxSize = RegionSizeMB;
FDiskRegion->Region = Region;
//
// This partition meets the criteria we were searching for,
// return FALSE to stop the enumeration
//
return FALSE;
}
//
// This partition does not meet the criteria, return TRUE to continue
// the enumeration.
//
return TRUE;
}
ULONG
RcCmdFdisk(
IN PTOKENIZED_LINE TokenizedLine
)
{
NTSTATUS Status;
PDISK_REGION InstallRegion;
PDISK_REGION SystemPartitionRegion;
PWCHAR DeviceName;
PWCHAR Action;
PWCHAR Operand;
ULONG DesiredMB;
FDISK_REGION FDiskRegion;
UNICODE_STRING UnicodeString;
PWCHAR szPartitionSize = 0;
BOOLEAN bPrompt = TRUE;
if (RcCmdParseHelp( TokenizedLine, MSG_FDISK_HELP )) {
return 1;
}
if (TokenizedLine->TokenCount >= 3) {
Action = TokenizedLine->Tokens->Next->String;
DeviceName = TokenizedLine->Tokens->Next->Next->String;
if (_wcsicmp(Action,L"/delete")==0) {
if (DeviceName[1] == L':') {
InstallRegion = SpRegionFromDosName(DeviceName);
} else {
InstallRegion = SpRegionFromNtName(DeviceName,0);
}
if (InstallRegion == NULL) {
RcMessageOut(MSG_SYNTAX_ERROR);
return 1;
}
if (InBatchMode)
bPrompt = FALSE;
else
pRcCls();
SpPtDoDelete(InstallRegion, DeviceName, bPrompt);
if (bPrompt)
pRcCls();
} else if (_wcsicmp(Action,L"/add")==0) {
DesiredMB = 0;
if (TokenizedLine->TokenCount >= 4) {
szPartitionSize = TokenizedLine->Tokens->Next->Next->Next->String;
RtlInitUnicodeString(&UnicodeString, szPartitionSize);
RtlUnicodeStringToInteger(&UnicodeString, 10, &DesiredMB);
}
FDiskRegion.DeviceName = DeviceName;
FDiskRegion.Region = NULL;
FDiskRegion.MaxSize = 0;
FDiskRegion.RequiredSize = DesiredMB;
SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcFdiskRegionEnum, (ULONG_PTR)&FDiskRegion );
if (FDiskRegion.Region) {
// try to create the partition of the given size
if (!SpPtDoCreate(FDiskRegion.Region,NULL,TRUE,DesiredMB,0,FALSE)) {
pRcCls();
// ask the user to give correct (aligned) size showing him the limits
if(!SpPtDoCreate(FDiskRegion.Region,NULL,FALSE,DesiredMB,0,FALSE)) {
pRcCls();
RcMessageOut(MSG_FDISK_INVALID_PARTITION_SIZE, szPartitionSize, DeviceName);
} else {
pRcCls();
}
}
} else {
// could not find a region to create the partition of the specified size
RcMessageOut(MSG_FDISK_INVALID_PARTITION_SIZE, szPartitionSize, DeviceName);
}
}
RcInitializeCurrentDirectories();
return 1;
}
pRcCls();
Status = SpPtPrepareDisks(
_CmdConsBlock->SifHandle,
&InstallRegion,
&SystemPartitionRegion,
_CmdConsBlock->SetupSourceDevicePath,
_CmdConsBlock->DirectoryOnSetupSource,
FALSE
);
if(!NT_SUCCESS(Status)) {
KdPrint(("SPCMDCON: SpPtPrepareDisks() failes, err=%08x\r\n",Status));
pRcCls();
return 1;
}
RcInitializeCurrentDirectories();
pRcCls();
return 1;
}
ULONG
RcCmdMakeDiskRaw(
IN PTOKENIZED_LINE TokenizedLine
)
{
BOOLEAN Successfull = FALSE;
#ifndef OLD_PARTITION_ENGINE
if (TokenizedLine->TokenCount > 1) {
WCHAR Buffer[256];
UNICODE_STRING UniStr;
ULONG DriveIndex = -1;
RtlInitUnicodeString(&UniStr, TokenizedLine->Tokens->Next->String);
if (NT_SUCCESS(RtlUnicodeStringToInteger(&UniStr, 10, &DriveIndex))) {
BOOLEAN Confirmed = FALSE;
swprintf(Buffer,
L"Convert %d drive to raw [y/n] ? ",
DriveIndex);
RcTextOut(Buffer);
if( RcLineIn(Buffer,2) ) {
if((Buffer[0] == L'y') || (Buffer[0] == L'Y')) {
//
// Wants to do it.
//
Confirmed = TRUE;
}
}
if (Confirmed) {
Successfull = SpPtMakeDiskRaw(DriveIndex);
} else {
Successfull = TRUE;
}
}
}
if (!Successfull) {
RcTextOut(L"Either MakeDiskRaw [disk-number] syntax is wrong or the command failed");
}
#endif
return 1;
}
NTSTATUS
RcDisplayNtInstalls(
IN PLIST_ENTRY NtInstalls
)
/*++
Routine Description:
Do a simple display of the NT Installs described in
the NtInstalls linked list
Arguments:
NtInstalls - Linked List containing description of NT installs
Return:
STATUS_SUCCESS if nt installs were successfully displayed
otherwise, error status
--*/
{
PLIST_ENTRY Next;
PNT_INSTALLATION NtInstall;
//
// make sure we have something to display
//
ASSERT(NtInstalls);
if (!NtInstalls) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcDisplayNtInstalls: incoming NT Installs list is NULL\r\n"
));
return STATUS_INVALID_PARAMETER;
}
ASSERT(! IsListEmpty(NtInstalls));
if(IsListEmpty(NtInstalls)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcDisplayNtInstalls: incoming NT Installs list is empty\r\n"
));
return STATUS_NOT_FOUND;
}
pRcEnableMoreMode();
//
// show how many installs we have
//
RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_TITLE,
InstallCountFullScan
);
//
// iterate through the database and report
//
Next = NtInstalls->Flink;
while ((UINT_PTR)Next != (UINT_PTR)NtInstalls) {
NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
Next = NtInstall->ListEntry.Flink;
RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_ENTRY,
NtInstall->InstallNumber,
NtInstall->DriveLetter,
NtInstall->Path
);
}
pRcDisableMoreMode();
return STATUS_SUCCESS;
}
NTSTATUS
RcPerformFullNtInstallsScan(
VOID
)
/*++
Routine Description:
Convenience routine for launching a full scan for NT Installs
Arguments:
none
Return:
STATUS_SUCCESS if scan was successful
otherwise, error status
--*/
{
PRC_SCAN_RECURSION_DATA RecursionData;
//
// the list should be empty before we do this. If
// someone wants to rescan the disk, they should
// empty the list first
//
ASSERT(IsListEmpty(&NtInstallsFullScan));
if (! IsListEmpty(&NtInstallsFullScan)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcPerformFullNtInstallsScan: NTInstallsFullScan list is NOT empty\r\n"
));
return STATUS_UNSUCCESSFUL;
}
ASSERT(InstallCountFullScan == 0);
if (InstallCountFullScan != 0) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcPerformFullNtInstallsScan: NTInstallsFullScan count > 0\r\n"
));
return STATUS_UNSUCCESSFUL;
}
//
// Let the user know what we are doing and that
// this could take a while
//
RcMessageOut(MSG_BOOTCFG_SCAN_NOTIFICATION);
//
// do a depth first search through directory tree
// and store the install info
//
//
// Initialize the structure that will be maintained during
// the recursive enumeration of the directories.
//
RecursionData = SpMemAlloc(sizeof(RC_SCAN_RECURSION_DATA));
RtlZeroMemory(RecursionData, sizeof(RC_SCAN_RECURSION_DATA));
//
// Build up a menu of partitions and free spaces.
//
SpEnumerateDiskRegions(RcScanDisksForNTInstallsEnum,
(ULONG_PTR)RecursionData
);
//
// there should be at least one install, otherwise
// there is not point in fixing the boot config
//
if(InstallCountFullScan == 0) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcPerformFullNtInstallsScan: Full Scan returned 0 hits!\r\n"
));
RcMessageOut(MSG_BOOTCFG_SCAN_FAILURE);
ASSERT(InstallCountFullScan > 0);
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS
RcGetBootEntries(
IN PLIST_ENTRY BootEntries,
IN PULONG BootEntriesCnt
)
/*++
Routine Description:
Get a list of current boot entries in the boot list
Arguments:
BootEntriesCnt - the number of boot entries displayed
Return:
STATUS_SUCCESS if successful and BootEntriesCnt is valid
otherwise, error status
--*/
{
NTSTATUS status;
ASSERT(BootEntries);
ASSERT(IsListEmpty(BootEntries));
if (! IsListEmpty(BootEntries)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcGetBootEntries: BootEntries list is not empty\r\n"
));
return STATUS_INVALID_PARAMETER;
}
ASSERT(BootEntriesCnt);
if (! BootEntriesCnt) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcGetBootEntries: BootEntriesCnt is NULL\r\n"
));
return STATUS_INVALID_PARAMETER;
}
//
// get an export of the loaded boot entries
//
status = SpExportBootEntries(BootEntries,
BootEntriesCnt
);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcGetBootEntries: failed to get export list: Status = %lx\r\n",
status
));
return status;
}
if (IsListEmpty(BootEntries)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcGetBootEntries: boot entries exported list is empty\r\n"
));
status = STATUS_NOT_FOUND;
}
return status;
}
NTSTATUS
RcDisplayBootEntries(
IN PLIST_ENTRY BootEntries,
IN ULONG BootEntriesCnt
)
/*++
Routine Description:
Display the list of current boot entries in the boot list
Arguments:
BootEntriesCnt - the number of boot entries displayed
Return:
STATUS_SUCCESS if successful and BootEntriesCnt is valid
otherwise, error status
--*/
{
PSP_EXPORTED_BOOT_ENTRY BootEntry;
PLIST_ENTRY Next;
ULONG i;
ASSERT(BootEntries);
ASSERT(! IsListEmpty(BootEntries));
if (IsListEmpty(BootEntries)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcDisplayBootEntries: BootEntries list is empty\r\n"
));
return STATUS_INVALID_PARAMETER;
}
ASSERT(BootEntriesCnt > 0);
if (BootEntriesCnt == 0) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcDisplayBootEntries: BootEntriesCnt is 0\r\n"
));
return STATUS_INVALID_PARAMETER;
}
pRcEnableMoreMode();
RcMessageOut(MSG_BOOTCFG_EXPORT_HEADER,
BootEntriesCnt
);
i=0;
Next = BootEntries->Flink;
while ((UINT_PTR)Next != (UINT_PTR)BootEntries) {
BootEntry = CONTAINING_RECORD( Next, SP_EXPORTED_BOOT_ENTRY, ListEntry );
Next = BootEntry->ListEntry.Flink;
RcMessageOut(MSG_BOOTCFG_EXPORT_ENTRY,
i+1,
BootEntry->LoadIdentifier,
BootEntry->OsLoadOptions,
BootEntry->DriverLetter,
BootEntry->OsDirectory
);
i++;
}
ASSERT(i == BootEntriesCnt);
pRcDisableMoreMode();
return STATUS_SUCCESS;
}
RcGetAndDisplayBootEntries(
IN ULONG NoEntriesMessageId,
OUT PULONG BootEntriesCnt OPTIONAL
)
/*++
Routine Description:
Get and Display the boot entries currently in the boot list
Arguments:
NoEntriesMessageId - the message id of the message that should be
displayed if there are no boot entries
BootEntriesCnt - on exit and if not NULL, points to the # of
boot entries displayed
Return:
STATUS_SUCCESS if nt installs were successfully displayed
and BootEntriesCnt is valid
otherwise, error status
--*/
{
LIST_ENTRY BootEntries;
ULONG cnt;
NTSTATUS status;
if (BootEntriesCnt) {
*BootEntriesCnt = 0;
}
InitializeListHead( &BootEntries );
//
// get the boot entries export
//
status = RcGetBootEntries(&BootEntries,
&cnt
);
//
// if there are no boot entries to choose as default, return
//
if (status == STATUS_NOT_FOUND) {
RcMessageOut(NoEntriesMessageId);
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(list) no boot entries found: Status = %lx\r\n",
status
));
return status;
} else if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(list) failed to get boot entries: Status = %lx\r\n",
status
));
return status;
}
status = RcDisplayBootEntries(&BootEntries,
cnt
);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(list) failed to display boot entries: Status = %lx\r\n",
status
));
return status;
}
status = SpFreeExportedBootEntries(&BootEntries,
cnt
);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(list) failed freeing export list: Status = %lx\r\n",
status
));
}
//
// send out the boot entries count if the user wants it
//
if (BootEntriesCnt != NULL) {
*BootEntriesCnt = cnt;
}
return status;
}
ULONG
RcCmdBootCfg(
IN PTOKENIZED_LINE TokenizedLine
)
/*++
Routine Description:
Provides support for managing boot configuration
Arguments:
(command console standard args)
Return:
routine always returns 1 for cmdcons.
errors are handled through messaging
--*/
{
PWCHAR Action;
PWCHAR Operand;
BOOLEAN bPrompt;
PWCHAR DeviceName;
PDISK_REGION InstallRegion;
NTSTATUS status;
pRcEnableMoreMode();
if (RcCmdParseHelp( TokenizedLine, MSG_BOOTCFG_HELP )) {
pRcDisableMoreMode();
return 1;
}
pRcDisableMoreMode();
bPrompt = (InBatchMode ? FALSE : TRUE);
if (TokenizedLine->TokenCount >= 2) {
Action = TokenizedLine->Tokens->Next->String;
//
// turn the redirect switches off in the boot config
//
if (_wcsicmp(Action,L"/disableredirect")==0) {
status = SpSetRedirectSwitchMode(DisableRedirect,
NULL,
NULL
);
if (NT_SUCCESS(status)) {
RcMessageOut(MSG_BOOTCFG_DISABLEREDIRECT_SUCCESS);
} else {
RcMessageOut(MSG_BOOTCFG_REDIRECT_FAILURE_UPDATING);
}
return 1;
}
//
// manage the redirect switches
//
if (_wcsicmp(Action,L"/redirect")==0 && (TokenizedLine->TokenCount >= 3)) {
PWSTR portU;
PCHAR port;
PWSTR baudrateU;
PCHAR baudrate;
ULONG size;
BOOLEAN setBaudRate;
//
// setting the baudrate info is optional
//
setBaudRate = FALSE;
baudrateU = NULL;
baudrate = NULL;
//
// get the redirect port (or useBiosSettings)
//
portU = SpDupStringW(TokenizedLine->Tokens->Next->Next->String);
//
// convert the argument to a char string
//
size = wcslen(portU)+1;
port = SpMemAlloc(size);
ASSERT(port);
status = RtlUnicodeToMultiByteN(
port,
size,
NULL,
portU,
size*sizeof(WCHAR)
);
ASSERT(NT_SUCCESS(status));
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(redirect) failed unicode conversion: Status = %lx\r\n"
));
return 1;
}
//
// if there is another arg, take it as the baudrate
// otherwise don't worry about including any baudrate paramters
//
if (TokenizedLine->TokenCount >= 4) {
baudrateU = SpDupStringW(TokenizedLine->Tokens->Next->Next->Next->String);
//
// convert the argument to a char string
//
size = wcslen(baudrateU)+1;
baudrate = SpMemAlloc(size);
ASSERT(baudrate);
status = RtlUnicodeToMultiByteN(
baudrate,
size,
NULL,
baudrateU,
size*sizeof(WCHAR)
);
ASSERT(NT_SUCCESS(status));
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(redirect) failed unicode conversion: Status = %lx\r\n",
status
));
return 1;
}
setBaudRate = TRUE;
}
//
// update both the port and baudrate redirect settings
//
status = SpSetRedirectSwitchMode(UseUserDefinedRedirectAndBaudRate,
port,
(setBaudRate ? baudrate : NULL)
);
//
// display the appropriate message based on what we set
//
if (NT_SUCCESS(status)) {
if (setBaudRate) {
RcMessageOut(MSG_BOOTCFG_ENABLE_REDIRECT_SUCCESS,
portU,
baudrateU
);
} else {
RcMessageOut(MSG_BOOTCFG_ENABLE_REDIRECT_PORT_SUCCESS,
portU
);
}
} else {
RcMessageOut(MSG_BOOTCFG_REDIRECT_FAILURE_UPDATING);
}
if (baudrateU) {
SpMemFree(baudrateU);
}
if (baudrate) {
SpMemFree(baudrate);
}
SpMemFree(portU);
SpMemFree(port);
return 1;
}
//
// List the entries in the boot list
//
if (_wcsicmp(Action,L"/list")==0) {
ULONG BootEntriesCnt;
//
// display the current boot list
//
status = RcGetAndDisplayBootEntries(MSG_BOOTCFG_LIST_NO_ENTRIES,
NULL
);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(list) failed to list boot entries: Status = %lx\r\n",
status
));
}
return 1;
}
//
// set the default boot entry
//
if (_wcsicmp(Action,L"/default")==0) {
ULONG BootEntriesCnt;
ULONG InstallNumber;
WCHAR buffer[3];
UNICODE_STRING UnicodeString;
NTSTATUS Status;
//
// display the current boot list
//
status = RcGetAndDisplayBootEntries(MSG_BOOTCFG_DEFAULT_NO_ENTRIES,
&BootEntriesCnt
);
if (status == STATUS_NOT_FOUND) {
//
// no boot entries in the list to set as default
// this is not an error condition, just return
//
return 1;
} else if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(default) failed to list boot entries: Status = %lx\r\n",
status
));
return 1;
}
//
// get user's install selection
//
RcMessageOut(MSG_BOOTCFG_ADD_QUERY);
RcLineIn(buffer, sizeof(buffer) / sizeof(WCHAR));
if (wcslen(buffer) > 0) {
RtlInitUnicodeString( &UnicodeString, buffer );
Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &InstallNumber );
if (! NT_SUCCESS(Status) ||
!((InstallNumber >= 1) && (InstallNumber <= BootEntriesCnt))) {
RcMessageOut(MSG_BOOTCFG_INVALID_SELECTION, buffer);
} else {
//
// the user gave us a valid install number, so try to set the default
//
status = SpSetDefaultBootEntry(InstallNumber);
if (NT_SUCCESS(status)) {
RcMessageOut(MSG_BOOTCFG_DEFAULT_SUCCESS);
} else {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(default) failed to set default: Status = %lx\r\n",
status
));
RcMessageOut(MSG_BOOTCFG_DEFAULT_FAILURE);
}
}
}
return 1;
}
//
// Scan the disks on the machine and report NT installs
//
if (_wcsicmp(Action,L"/scan")==0) {
//
// Ensure that we have the full scan of the disks
//
if (IsListEmpty(&NtInstallsFullScan)) {
status = RcPerformFullNtInstallsScan();
//
// if there are no boot entries, then return
//
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(scan) full scan return 0 hits: Status = %lx\r\n",
status
));
return 1;
}
}
//
// display discovered installs
//
status = RcDisplayNtInstalls(&NtInstallsFullScan);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(scan) failed while displaying installs: Status = %lx\r\n",
status
));
}
return 1;
}
//
// Provide support for reconstructing the boot configuration
//
// This command iterates through all the existing Nt Installs
// and prompts the user to add the installs into the boot
// configuration
//
if (_wcsicmp(Action,L"/rebuild")==0) {
ULONG i;
PNT_INSTALLATION pInstall;
WCHAR buffer[256];
PWSTR LoadIdentifier;
PWSTR OsLoadOptions;
BOOLEAN writeInstall;
BOOLEAN writeAllInstalls;
PLIST_ENTRY Next;
PNT_INSTALLATION NtInstall;
writeAllInstalls = FALSE;
LoadIdentifier = NULL;
OsLoadOptions = NULL;
//
// Ensure that we have the full scan of the disks
//
if (IsListEmpty(&NtInstallsFullScan)) {
status = RcPerformFullNtInstallsScan();
//
// if there are no boot entries, then return
//
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(rebuild) full scan return 0 hits: Status = %lx\r\n",
status
));
return 1;
}
}
//
// show how many installs we have
//
RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_TITLE,
InstallCountFullScan
);
//
// For each of the discovered NT installs, ask the user
// if they want to include it in the boot configuration
//
Next = NtInstallsFullScan.Flink;
while ((UINT_PTR)Next != (UINT_PTR)&NtInstallsFullScan) {
NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
Next = NtInstall->ListEntry.Flink;
writeInstall = TRUE;
//
// show the install under consideration
//
RcMessageOut(MSG_BOOTCFG_SCAN_RESULTS_ENTRY,
NtInstall->InstallNumber,
NtInstall->Region->DriveLetter,
NtInstall->Path
);
//
// if we are not in batch mode and the user doesn't want
// to install all of the discoveries, then ask them
// if they want to install the current one.
//
if (bPrompt && !writeAllInstalls) {
LPWSTR YesNo;
WCHAR Text[3];
//
// prompt user for action
//
YesNo = SpRetreiveMessageText( ImageBase, MSG_YESNOALL, NULL, 0 );
if( YesNo ) {
//
// query the user for an (Yes, No, All) action
//
RcMessageOut(MSG_BOOTCFG_INSTALL_DISCOVERY_QUERY);
if( RcLineIn( Text, 2 ) ) {
if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) {
writeInstall = FALSE;
} else if ((Text[0] == YesNo[4]) || (Text[0] == YesNo[5])) {
writeAllInstalls = TRUE;
}
} else {
writeInstall = FALSE;
}
SpMemFree( YesNo );
}
}
//
// if we should write the discovery, then do it...
//
if (writeInstall) {
//
// if we are not in batch mode, then prompt them for the necessary input
//
if (bPrompt) {
ASSERT(LoadIdentifier == NULL);
ASSERT(OsLoadOptions == NULL);
//
// prompt user for load identifier
//
RcMessageOut(MSG_BOOTCFG_INSTALL_LOADIDENTIFIER_QUERY);
RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
LoadIdentifier = SpDupStringW(buffer);
//
// prompt user for load os load options
//
RcMessageOut(MSG_BOOTCFG_INSTALL_OSLOADOPTIONS_QUERY);
RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
OsLoadOptions = SpDupStringW(buffer);
} else {
LPWSTR s;
LPWSTR p;
NTSTATUS Status;
s = SpRetreiveMessageText( ImageBase,
MSG_BOOTCFG_BATCH_LOADID,
NULL,
0);
ASSERT(s);
//
// terminate the string at the %0
//
p = SpDupStringW(s);
SpMemFree(s);
s = wcsstr(p, L"%0");
// make sure we found the %0
ASSERT(s);
ASSERT(s < (p + wcslen(p)));
if (s) {
// terminate at the %
*s = L'\0';
} else {
// otherwise just use all of p
NOTHING;
}
//
// construct the default load identifier
//
swprintf(_CmdConsBlock->TemporaryBuffer, L"%s%d", p, NtInstall->InstallNumber);
LoadIdentifier = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
//
// construct the default os load options
//
swprintf(_CmdConsBlock->TemporaryBuffer, L"/fastdetect");
OsLoadOptions = SpDupStringW(_CmdConsBlock->TemporaryBuffer);
SpMemFree(p);
}
#if defined(_X86_)
//
// write the discovered install into the boot list
//
status = SpAddNTInstallToBootList(_CmdConsBlock->SifHandle,
NtInstall->Region,
L"",
NtInstall->Region,
NtInstall->Path,
OsLoadOptions,
LoadIdentifier
);
#else
//
// the non-x86 case has not been tested/implemented fully
//
status = STATUS_UNSUCCESSFUL;
#endif
if (LoadIdentifier) {
SpMemFree(LoadIdentifier);
}
if (OsLoadOptions) {
SpMemFree(OsLoadOptions);
}
LoadIdentifier = NULL;
OsLoadOptions = NULL;
//
// if adding the discovered install fails, bail out
//
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(rebuild) failed adding to boot list: Status = %lx\r\n",
status
));
RcMessageOut(MSG_BOOTCFG_BOOTLIST_ADD_FAILURE);
break;
}
}
}
return 1;
}
//
// Provide support for reconstructing the boot configuration
//
// This command displays the known NT installs and prompts
// the user to install a single entry into the boot
// configuration
//
if (_wcsicmp(Action,L"/add")==0) {
ULONG i;
PNT_INSTALLATION pInstall;
ULONG InstallNumber;
WCHAR buffer[256];
UNICODE_STRING UnicodeString;
NTSTATUS Status;
PLIST_ENTRY Next;
PNT_INSTALLATION NtInstall;
//
// Ensure that we have the full scan of the disks
//
if (IsListEmpty(&NtInstallsFullScan)) {
status = RcPerformFullNtInstallsScan();
//
// if there are no boot entries, then return
//
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(rebuild) full scan return 0 hits: Status = %lx\r\n",
status
));
return 1;
}
}
//
// display discovered installs
//
status = RcDisplayNtInstalls(&NtInstallsFullScan);
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(add) failed while displaying installs: Status = %lx\r\n",
status
));
return 1;
}
//
// get user's install selection
//
RcMessageOut(MSG_BOOTCFG_ADD_QUERY);
RcLineIn(buffer, sizeof(buffer) / sizeof(WCHAR));
if (wcslen(buffer) > 0) {
RtlInitUnicodeString( &UnicodeString, buffer );
Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &InstallNumber );
if (! NT_SUCCESS(Status) ||
!((InstallNumber >= 1) && (InstallNumber <= InstallCountFullScan))) {
RcMessageOut(MSG_BOOTCFG_INVALID_SELECTION, buffer);
} else {
PWSTR LoadIdentifier;
PWSTR OsLoadOptions;
ULONG i;
BOOLEAN saveStatus;
//
// prompt user for load identifier
//
RcMessageOut(MSG_BOOTCFG_INSTALL_LOADIDENTIFIER_QUERY);
RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
LoadIdentifier = SpDupStringW(buffer);
//
// prompt user for load os load options
//
RcMessageOut(MSG_BOOTCFG_INSTALL_OSLOADOPTIONS_QUERY);
RcLineIn(buffer, sizeof(buffer)/sizeof(WCHAR));
OsLoadOptions = SpDupStringW(buffer);
//
// iterate to the InstallNumber'th node in the discover list
//
Next = NtInstallsFullScan.Flink;
while ((UINT_PTR)Next != (UINT_PTR)&NtInstallsFullScan) {
NtInstall = CONTAINING_RECORD( Next, NT_INSTALLATION, ListEntry );
Next = NtInstall->ListEntry.Flink;
if (NtInstall->InstallNumber == InstallNumber) {
break;
}
}
ASSERT(NtInstall);
if (! NtInstall) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_INFO_LEVEL,
"SPCMDCON: RcCmdBootCfg:(add) failed to find user specified NT Install\r\n"
));
RcMessageOut(MSG_BOOTCFG_ADD_NOT_FOUND);
return 1;
}
#if defined(_X86_)
//
// write the discovered install into the boot list
//
status = SpAddNTInstallToBootList(_CmdConsBlock->SifHandle,
NtInstall->Region,
L"",
NtInstall->Region,
NtInstall->Path,
OsLoadOptions,
LoadIdentifier
);
#else
//
// the non-x86 case has not been tested/implemented fully
//
status = STATUS_UNSUCCESSFUL;
#endif
if (LoadIdentifier) {
SpMemFree(LoadIdentifier);
}
if (OsLoadOptions) {
SpMemFree(OsLoadOptions);
}
if (! NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_SETUP_ID,
DPFLTR_ERROR_LEVEL,
"SPCMDCON: RcCmdBootCfg:(add) failed adding to boot list: Status = %lx\r\n",
status
));
RcMessageOut(MSG_BOOTCFG_BOOTLIST_ADD_FAILURE);
}
}
}
return 1;
}
}
//
// either no args, or none recognized; default to help
//
pRcEnableMoreMode();
RcMessageOut(MSG_BOOTCFG_HELP);
pRcDisableMoreMode();
return 1;
}