windows-nt/Source/XPSP1/NT/base/ntsetup/mpk/enduser/restore.c

1505 lines
42 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "enduser.h"
//
// Partition image structure for the partition being restored.
//
PARTITION_IMAGE PartitionImage;
//
// Number of sectors to zap to wipe out an image
// 0.98 MB of 512-byte sectors
//
#define ZAP_MAX 2000
//
// Maximum size of FAT32 fats
#define MAX_FAT32_TABLE_SIZE (16*1024*1024-65536)
#define MAX_FAT32_ENTRIES ((16*1024*1024-65536)/4)
VOID
RelocateClusterBitmap(
IN HDISK DiskHandle,
IN ULONG ImageStart
);
VOID
RelocateBootPartition(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN ULONG ExtendedCount
);
VOID
FixUpBpb(
IN HDISK DiskHandle,
IN BYTE SectorsPerTrack,
IN USHORT Heads,
IN ULONG StartSector
);
VOID
RemoveNonSelectedOsData(
IN HDISK DiskHandle
);
VOID
CreatePartitionTableEntry(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN ULONG DiskSectorCount,
IN ULONG StartSector,
OUT ULONG *LastSector,
IN BOOL Relocating
);
VOID
TellUserToWait(
VOID
);
VOID
StartGauge(
VOID
);
BOOL
TestImage(
IN HDISK DiskHandle,
IN ULONG StartSector
);
BOOL
MungeParametersForFat32Extend(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN BYTE SectorsPerTrack,
IN ULONG SourceStart,
IN ULONG *TargetStart,
IN FPMASTER_DISK pMasterDisk,
IN FPPARTITION_IMAGE pPartitionImage,
IN VOID *TemporaryBuffer
);
VOID
AdjustTargetStart(
IN HDISK DiskHandle,
IN BYTE SectorsPerTrack,
IN ULONG *TargetStart,
IN VOID *TemporaryBuffer
);
ULONG
ComputeClusters(
IN ULONG ClusterSize,
IN ULONG Sectors,
IN ULONG SectorSize,
IN ULONG ReservedSectors,
IN ULONG Fats
);
BOOL
IsUnknownPartition(
IN BYTE SysId
);
VOID
RestoreUsersDisk(
IN HDISK DiskHandle
)
/*++
Routine Description:
This is the top-level routine concerned with restoring the user's
disk so it seems as if only a single os was ever preinstalled on it.
Arguments:
DiskHandle - supplies open disk handle to master/target hard disk.
Return Value:
None. Does not return if error.
--*/
{
ULONG SourceStart;
ULONG TargetStart;
BYTE Int13Unit;
BYTE SectorsPerTrack;
USHORT Heads;
USHORT Cylinders;
ULONG ExtendedCount;
UINT DiskId;
ULONG LastSector;
FPFAT32_BOOT_SECTOR pFat32BootSect;
FPPARTITION_IMAGE pFat32ImgHdr;
TellUserToWait();
GetDiskInfoByHandle(
DiskHandle,
&Int13Unit,
&SectorsPerTrack,
&Heads,
&Cylinders,
&ExtendedCount,
&DiskId
);
SourceStart = MasterDiskInfo.ImageStartSector[MasterDiskInfo.SelectionOrdinal];
TargetStart = SectorsPerTrack;
_Log("Starting restore: SourceStart = 0x%lx, TargetStart = 0x%lx\n",SourceStart,TargetStart);
//
// Read the partition image information structure off the disk
// to determine how large the image is. Cache it away somewhere safe.
//
if(MasterDiskInfo.State >= MDS_CACHED_IMAGE_HEADER) {
_Log("Have cached partition image header, fetching from disk\n");
if(!ReadDisk(DiskHandle,2,1,(FPBYTE)IoBuffer+512)) {
FatalError(textReadFailedAtSector,1,2L);
}
} else {
//
// Ok, figure out where we can start laying down the partition.
//
_Log("TargetStart is %d, adjusting...\n",TargetStart);
AdjustTargetStart( DiskHandle,
SectorsPerTrack,
&TargetStart,
(BYTE*)IoBuffer+4096);
_Log("New TargetStart is %d.\n",TargetStart);
//
// pull the first two sectors of the image in
//
if(!ReadDisk(DiskHandle,SourceStart,2,(FPBYTE)IoBuffer+512)) {
FatalError(textReadFailedAtSector,2,SourceStart);
}
//
// the first will be the image header, the second could be a fat32 boot sector
//
pFat32BootSect = (FPFAT32_BOOT_SECTOR)((BYTE*)IoBuffer+1024);
//
// We need to adjust the target start to compensate for a preserved
// eisa/hiber partition
//
if( IsFat32(pFat32BootSect) ) {
//
// if it is a fat32 boot sector, then we store away some useful things we will
// use when we call MungeParametersForFat32Extend.
//
pFat32ImgHdr = (FPPARTITION_IMAGE)((BYTE*)IoBuffer+512);
pFat32ImgHdr->Fat32ReservedSectors = pFat32BootSect->PackedBpb.ReservedSectors;
#if 0
//
// this is not necessarily 0x20
//
if(pFat32ImgHdr->Fat32ReservedSectors != 0x20) {
FatalError(textReadFailedAtSector,2,SourceStart);
}
#endif
//
// since this is a Fat32 image, we determine how big we can expand it,
// where we can start the expansion, and how big the new FATs need to be.
//
// We use IoBuffer+4096 as a convenient temporary work bufer.
//
// This will fill in the Fat32 fields of the partition image (pFat32ImageHdr)
// header which we will store away safely in the next step.
//
MungeParametersForFat32Extend(DiskHandle,
Cylinders,
SectorsPerTrack,
SourceStart,
&TargetStart,
&MasterDiskInfo,
(FPPARTITION_IMAGE)pFat32ImgHdr,
(BYTE*)IoBuffer+4096);
}
//
// Save it.
//
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,2,1,(FPBYTE)IoBuffer+512)) {
FatalError(textWriteFailedAtSector,1,2L);
}
}
_Log("Successfully cached partition image header\n");
//
// Clobbers first sector of IoBuffer
//
if(!CmdLineArgs.Test) {
UpdateMasterDiskState(DiskHandle,MDS_CACHED_IMAGE_HEADER);
_Log("Master disk state updated to indicate cached partition image header\n");
}
}
SourceStart++;
memmove(&PartitionImage,(FPBYTE)IoBuffer+512,sizeof(PARTITION_IMAGE));
_Log("Image header for image to be restored --\n");
_Log(" Signature: 0x%lx\n",PartitionImage.Signature);
_Log(" Size: %u\n",PartitionImage.Size);
_Log(" NonClusterSectors: 0x%lx\n",PartitionImage.NonClusterSectors);
_Log(" ClusterCount: 0x%lx\n",PartitionImage.ClusterCount);
_Log(" TotalSectorCount: 0x%lx\n",PartitionImage.TotalSectorCount);
_Log(" LastUsedCluster: 0x%lx\n",PartitionImage.LastUsedCluster);
_Log(" UsedClusterCount: 0x%lx\n",PartitionImage.UsedClusterCount);
_Log(" SectorsPerCluster: %u\n",PartitionImage.SectorsPerCluster);
_Log(" SystemId: %u\n",PartitionImage.SystemId);
_Log("\n");
if(CmdLineArgs.Test) {
// validate the disk image first.
TestImage(DiskHandle,SourceStart-1);
TellUserToWait();
}
StartGauge();
//
// Fix up things so that no one can get back the data for the OSes
// they didn't install.
//
RemoveNonSelectedOsData(DiskHandle);
XmsInit();
//
// Relocate the cluster bitmap if necessary
//
RelocateClusterBitmap(DiskHandle,SourceStart);
//
// Relocate the boot partition if necessary.
//
RelocateBootPartition(DiskHandle,Cylinders,ExtendedCount);
//
// Transfer the partition data from the image to the start of
// the hard drive.
//
ExpandImage(DiskHandle,SectorsPerTrack,SourceStart,TargetStart);
XmsTerminate();
TellUserToWait();
//
// Next, fix up the BPB's geometry-related fields.
//
FixUpBpb(DiskHandle,SectorsPerTrack,Heads,TargetStart);
//
// The next step is to create the partition table entry that describes
// the partition we are restoring and remove the one for the bootable
// partition.
//
CreatePartitionTableEntry(
DiskHandle,
Cylinders,
ExtendedCount,
TargetStart,
&LastSector,
FALSE
);
//
// Now we're done. As a single final step, we recreate the
// mirror boot sector for NTFS. Note that there is a window for failure
// in this operation, but there's no good way around this. If we write
// this sector before now we risk wiping out the bootstrap program.
//
if(PartitionImage.SystemId == 7) {
if(ReadDisk(DiskHandle,TargetStart,1,IoBuffer)) {
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,LastSector,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,LastSector);
}
}
} else {
FatalError(textReadFailedAtSector,1,TargetStart);
}
}
}
VOID
RemoveNonSelectedOsData(
IN HDISK DiskHandle
)
{
UINT i;
ULONG SectorCount;
ULONG OriginalCount;
ULONG CurrentSector;
PPARTITION_IMAGE p;
//
// See if we've already done this step.
//
if(!CmdLineArgs.Test && (MasterDiskInfo.State >= MDS_REMOVED_OTHERS)) {
_Log("Already removed non-selected OS data\n");
GaugeDelta(ZAP_MAX * (MasterDiskInfo.ImageCount-1));
return;
}
//
// Scribble over the first 1 MB or so of each image. This takes out
// some significant file system data structures and some data.
// We also take out the partition image header itself, but we do that
// last so if we restart we'll be able to complete operating on the
// image we were working on.
//
p = IoBuffer;
for(i=0; i<MasterDiskInfo.ImageCount; i++) {
if(i == MasterDiskInfo.SelectionOrdinal) {
continue;
}
CurrentSector = MasterDiskInfo.ImageStartSector[i];
if(!ReadDisk(DiskHandle,CurrentSector++,1,IoBuffer)) {
FatalError(textReadFailedAtSector,1,CurrentSector-1);
}
//
// If the sector is not a valid partition image header,
// then we assume we've already taken it out in a previous pass
// and we don't worry about it.
//
if(p->Signature == PARTITION_IMAGE_SIGNATURE) {
_Log("Removing non-selected OS data for image %u\n",i);
SectorCount = p->NonClusterSectors + (p->UsedClusterCount * p->SectorsPerCluster);
if(SectorCount > ZAP_MAX) {
SectorCount = ZAP_MAX;
}
OriginalCount = SectorCount;
memset(IoBuffer,0,63*512);
while(SectorCount >= 63) {
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,CurrentSector,63,IoBuffer)) {
FatalError(textWriteFailedAtSector,63,CurrentSector);
}
}
CurrentSector += 63;
SectorCount -= 63;
GaugeDelta(63);
}
if(SectorCount) {
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,CurrentSector,(BYTE)SectorCount,IoBuffer)) {
FatalError(textWriteFailedAtSector,(unsigned)SectorCount,CurrentSector);
}
}
GaugeDelta(SectorCount);
}
//
// Now take out the image header.
//
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,MasterDiskInfo.ImageStartSector[i],1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,MasterDiskInfo.ImageStartSector[i]);
}
}
//
// We allowed for ZAP_MAX sectors in the gauge but there might
// have been less than that in the image (strange case, but possible).
//
if(OriginalCount < (ULONG)ZAP_MAX) {
GaugeDelta((ULONG)ZAP_MAX - OriginalCount);
}
} else {
_Log("Non-selected OS data for image %u previously removed\n",i);
GaugeDelta(ZAP_MAX);
}
}
//
// Update state to indicate that we've done this step.
//
if(!CmdLineArgs.Test) {
_Log("Updating master disk state to indicate non-selected os data removed...\n");
UpdateMasterDiskState(DiskHandle,MDS_REMOVED_OTHERS);
_Log("Master disk state updated to indicate non-selected os data removed\n");
}
}
VOID
RelocateClusterBitmap(
IN HDISK DiskHandle,
IN ULONG ImageStart
)
{
ULONG BitmapSize;
ULONG Read;
ULONG Target;
BOOL Xms;
//
// Figure out how large the cluster bitmap is in sectors.
//
BitmapSize = (PartitionImage.LastUsedCluster/CLUSTER_BITS_PER_SECTOR) + 1;
_Log("Cluster bitmap is 0x%lx sectors\n",BitmapSize);
//
// If we've already done this step, nothing to do.
//
if(!CmdLineArgs.Test && (MasterDiskInfo.State >= MDS_RELOCATED_BITMAP)) {
_Log("Already relocated cluster bitmap\n");
GaugeDelta(2*BitmapSize);
return;
}
ImageStart += PartitionImage.NonClusterSectors
+ (PartitionImage.SectorsPerCluster * PartitionImage.UsedClusterCount);
if(PartitionImage.Flags & PARTIMAGE_RELOCATE_BITMAP) {
//
// Figure out where the bitmap will be relocated to.
//
Target = PartitionImage.BitmapRelocationStart;
MasterDiskInfo.ClusterBitmapStart = CmdLineArgs.Test ? ImageStart : Target;
_Log("Cluster bitmap to be relocated from sector 0x%lx to sector 0x%lx\n",ImageStart,Target);
//
// Note that there's no overlap problem so we just flat out
// transfer the bitmap from beginning to end.
//
while(BitmapSize) {
XmsIoDiskRead(DiskHandle,ImageStart,BitmapSize,&Read,&Xms);
XmsIoDiskWrite(DiskHandle,Target,0,Read,Xms);
BitmapSize -= Read;
ImageStart += Read;
Target += Read;
}
} else {
_Log("No need to relocate cluster bitmap\n");
MasterDiskInfo.ClusterBitmapStart = ImageStart;
}
//
// Note that we update state to indicate that this is done even if we
// don't actually relocate the cluster bitmap, for completeness and
// tracking of what's going on.
//
if(!CmdLineArgs.Test) {
_Log("Updating master disk state to indicate cluster bitmap relocated...\n");
UpdateMasterDiskState(DiskHandle,MDS_RELOCATED_BITMAP);
_Log("Master disk state updated to indicate cluster bitmap relocated\n");
}
}
VOID
RelocateBootPartition(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN ULONG ExtendedCount
)
{
ULONG Read;
ULONG Target;
ULONG ImageStart;
ULONG Count;
BOOL Xms;
if(PartitionImage.Flags & PARTIMAGE_RELOCATE_BOOT) {
//
// If we've already done this step, nothing to do.
//
if(!CmdLineArgs.Test && (MasterDiskInfo.State >= MDS_RELOCATED_BOOT)) {
_Log("Already relocated boot partition\n");
GaugeDelta(2*MasterDiskInfo.StartupPartitionSectorCount);
} else {
//
// Figure out where the boot partition will be relocated to.
//
Target = PartitionImage.BootRelocationStart;
ImageStart = MasterDiskInfo.StartupPartitionStartSector;
Count = MasterDiskInfo.StartupPartitionSectorCount;
if(!CmdLineArgs.Test) {
MasterDiskInfo.StartupPartitionStartSector = Target;
}
_Log(
"Cluster bitmap to be relocated from sector 0x%lx to sector 0x%lx\n",
ImageStart,
Target
);
//
// Note that there's no overlap problem so we just flat out
// transfer the boot partition from end to end.
//
while(Count) {
XmsIoDiskRead(DiskHandle,ImageStart,Count,&Read,&Xms);
XmsIoDiskWrite(DiskHandle,Target,0,Read,Xms);
Count -= Read;
ImageStart += Read;
Target += Read;
}
if(!CmdLineArgs.Test) {
_Log("Updating master disk state to indicate boot part relocated...\n");
UpdateMasterDiskState(DiskHandle,MDS_RELOCATED_BOOT);
_Log("Master disk state updated to indicate boot part relocated\n");
}
}
//
// Ok, it's been transferred. Fix up the mbr to point at it.
// Note that CreatePartitionTableEntry updates master disk state but is protected by
// checking aginst CmdLineArgs.Test.
//
if(CmdLineArgs.Test || (MasterDiskInfo.State < MDS_RELOCATED_BOOT_MBR)) {
CreatePartitionTableEntry(
DiskHandle,
Cylinders,
ExtendedCount,
MasterDiskInfo.StartupPartitionStartSector,
&Target,
TRUE
);
}
} else {
_Log("No need to relocate boot partition\n");
}
}
VOID
FixUpBpb(
IN HDISK DiskHandle,
IN BYTE SectorsPerTrack,
IN USHORT Heads,
IN ULONG StartSector
)
{
USHORT BackupBootSectorOfs;
//
// See if we've already done this step.
//
if(!CmdLineArgs.Test && (MasterDiskInfo.State >= MDS_UPDATED_BPB)) {
_Log("Already fixed up BPB\n");
return;
}
if(!ReadDisk(DiskHandle,StartSector,1,IoBuffer)) {
FatalError(textReadFailedAtSector,1,StartSector);
}
if(PartitionImage.Fat32ReservedSectors) {
//
// this is a FAT32 partition, we munge some extra stuff in the BPB
// when we do the partition expansion
//
FPFAT32_BOOT_SECTOR pBootSect = IoBuffer;
ULONG FatSectorCount;
pBootSect->PackedBpb.LargeSectors = PartitionImage.Fat32AdjustedSectorCount;
FatSectorCount = PartitionImage.Fat32AdjustedFatTableEntryCount / (512/4);
FatSectorCount = (PartitionImage.Fat32AdjustedFatTableEntryCount % (512/4)) ?
FatSectorCount++ : FatSectorCount;
pBootSect->PackedBpb.LargeSectorsPerFat = FatSectorCount;
BackupBootSectorOfs = pBootSect->PackedBpb.BackupBootSector;
}
//
// Slam the relevent fields.
//
*(FPUSHORT)&((FPBYTE)IoBuffer)[24] = SectorsPerTrack;
*(FPUSHORT)&((FPBYTE)IoBuffer)[26] = Heads;
*(FPULONG)&((FPBYTE)IoBuffer)[28] = StartSector;
//
// Want to do this but for fat32 it's in a different place
// so it's dangerous
//
//*(FPBYTE)&((FPBYTE)IoBuffer)[36] = Int13Unit;
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,StartSector,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,StartSector);
}
if( PartitionImage.Fat32ReservedSectors ) {
//
// Fat32 has a backup boot sector
//
if(!WriteDisk(DiskHandle,StartSector+BackupBootSectorOfs,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,StartSector+BackupBootSectorOfs);
}
_Log("Successfully fixed up FAT32 mirror BPB\n");
}
}
_Log("Successfully fixed up BPB\n");
if(PartitionImage.Fat32ReservedSectors) {
//
// FAT32 case.
//
ULONG NewFreeClusterCount;
ULONG OldFreeClusterCount;
USHORT FsInfoSectorOfs;
FPFSINFO_SECTOR pFsInfoSector;
//
// need to fixup the FsInfoSector to reflect the new free space count.
//
OldFreeClusterCount = PartitionImage.ClusterCount - PartitionImage.UsedClusterCount;
NewFreeClusterCount = PartitionImage.Fat32AdjustedFatTableEntryCount - PartitionImage.UsedClusterCount;
FsInfoSectorOfs = ((FPFAT32_BOOT_SECTOR)IoBuffer)->PackedBpb.FsInfoSector;
//
// ok, get FSINFO in.
//
if(!ReadDisk(DiskHandle,StartSector+FsInfoSectorOfs,1,IoBuffer)) {
FatalError(textReadFailedAtSector,1,StartSector+FsInfoSectorOfs);
}
//
// now verify it is, in fact, an FSINFO sector.
//
pFsInfoSector = (FPFSINFO_SECTOR)IoBuffer;
if(pFsInfoSector->FsInfoSignature != FSINFO_SIGNATURE ||
pFsInfoSector->SectorBeginSignature != FSINFO_SECTOR_BEGIN_SIGNATURE ||
pFsInfoSector->SectorEndSignature != FSINFO_SECTOR_END_SIGNATURE
) {
FatalError(textReadFailedAtSector,1,StartSector+FsInfoSectorOfs);
}
//
// validate our old free cluster count
//
if( OldFreeClusterCount != pFsInfoSector->FreeClusterCount ) {
FatalError(textReadFailedAtSector,1,StartSector+FsInfoSectorOfs);
}
//
// now drop in our new freespace count.
//
pFsInfoSector->FreeClusterCount = NewFreeClusterCount;
//
// Write FSINFO sect back out. Twice. (the two copies are identical.)
//
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,StartSector+FsInfoSectorOfs,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,StartSector+FsInfoSectorOfs);
}
if(!WriteDisk(DiskHandle,StartSector+BackupBootSectorOfs+FsInfoSectorOfs,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,StartSector+BackupBootSectorOfs+FsInfoSectorOfs);
}
}
_Log("Successfully fixed up FsInfoSector(s)\n");
}
if(!CmdLineArgs.Test) {
_Log("Updating master disk state to indicate BPB fixed up...\n");
UpdateMasterDiskState(DiskHandle,MDS_UPDATED_BPB);
_Log("Master disk state updated to indicate BPB fixed up\n");
}
}
VOID
CreatePartitionTableEntry(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN ULONG DiskSectorCount,
IN ULONG StartSector,
OUT ULONG *LastSector,
IN BOOL Relocating
)
{
unsigned i;
USHORT SectorsPerCylinder;
USHORT r;
ULONG EndSector;
BOOL Overflow;
ULONG C;
BYTE H;
BYTE S;
struct {
BYTE Active;
BYTE StartH;
BYTE StartS;
BYTE StartC;
BYTE SysId;
BYTE EndH;
BYTE EndS;
BYTE EndC;
ULONG Start;
ULONG Count;
} *PartTabEnt,*TheEntry;
if(!DiskSectorCount) {
DiskSectorCount = (ULONG)MasterDiskInfo.OriginalSectorsPerTrack
* (ULONG)MasterDiskInfo.OriginalHeads
* (ULONG)Cylinders;
}
SectorsPerCylinder = MasterDiskInfo.OriginalHeads * MasterDiskInfo.OriginalSectorsPerTrack;
//
// Read the MBR
//
if(!ReadDisk(DiskHandle,0,1,IoBuffer)) {
FatalError(textReadFailedAtSector,1,0L);
}
//
// Traverse the MBR, trying to find the MPK boot partition.
// Also make sure all entries are inactive.
//
TheEntry = NULL;
for(i=0; i<4; i++) {
PartTabEnt = (FPVOID)((FPBYTE)IoBuffer + 0x1be + (i*16));
if(PartTabEnt->SysId
&& (PartTabEnt->Start == MasterDiskInfo.StartupPartitionStartSector)
&& !TheEntry) {
TheEntry = PartTabEnt;
}
PartTabEnt->Active = 0;
}
if(!TheEntry) {
//
// Couldn't find it, something is seriously corrupt.
//
FatalError(textCantFindMPKBoot);
}
if(Relocating) {
EndSector = StartSector + MasterDiskInfo.StartupPartitionSectorCount;
} else {
if( PartitionImage.Fat32ReservedSectors ) {
//
// FAT32 resize case
//
EndSector = PartitionImage.Fat32AdjustedSectorCount + StartSector;
} else {
EndSector = PartitionImage.TotalSectorCount + StartSector;
}
//
// Refigure the end sector so it's aligned to a cylinder boundary.
//
if(r = (USHORT)(EndSector % SectorsPerCylinder)) {
EndSector += SectorsPerCylinder - r;
}
//
// In some cases NT reports the disk is 1 or 2 cylinders
// larger than int13 reports. Thus we may have a volume that
// spans beyond the end of the disk as reported by int13.
// If we cap the end of the partition to the size reported
// by int13 in this case, we will end up with a partition
// whose size in the partition table is smaller than the
// size recorded in the BPB. NTFS is particular will then
// refuse to mount the drive and you get inaccassible boot device.
// BIOSes will typically allow I/O to these "extra" cylinders
// even though they're not reported via int13 function 8, so this
// shouldn't be a problem.
//
//if(EndSector > DiskSectorCount) {
// EndSector = DiskSectorCount;
//}
}
TheEntry->Active = 0x80;
TheEntry->Start = StartSector;
TheEntry->Count = EndSector - StartSector;
//
// Calculate start CHS values.
//
C = StartSector / SectorsPerCylinder;
if(C >= (ULONG)Cylinders) {
C = Cylinders - 1;
H = (BYTE)(MasterDiskInfo.OriginalHeads - 1);
S = (BYTE)(MasterDiskInfo.OriginalSectorsPerTrack - 1);
} else {
H = (BYTE)((StartSector % SectorsPerCylinder) / MasterDiskInfo.OriginalSectorsPerTrack);
S = (BYTE)((StartSector % SectorsPerCylinder) % MasterDiskInfo.OriginalSectorsPerTrack);
}
TheEntry->StartC = (BYTE)C;
TheEntry->StartH = H;
TheEntry->StartS = (BYTE)((S + 1) | (((USHORT)C & 0x300) >> 2));
//
// Similarly for the end.
//
EndSector--;
*LastSector = EndSector;
C = EndSector / SectorsPerCylinder;
if(C >= (ULONG)Cylinders) {
C = Cylinders - 1;
H = (BYTE)(MasterDiskInfo.OriginalHeads - 1);
S = (BYTE)(MasterDiskInfo.OriginalSectorsPerTrack - 1);
Overflow = TRUE;
} else {
H = (BYTE)((EndSector % SectorsPerCylinder) / MasterDiskInfo.OriginalSectorsPerTrack);
S = (BYTE)((EndSector % SectorsPerCylinder) % MasterDiskInfo.OriginalSectorsPerTrack);
Overflow = FALSE;
}
TheEntry->EndC = (BYTE)C;
TheEntry->EndH = H;
TheEntry->EndS = (BYTE)((S + 1) | (((USHORT)C & 0x300) >> 2));
if(!Relocating) {
TheEntry->SysId = PartitionImage.SystemId;
}
if(Overflow) {
switch(TheEntry->SysId) {
case 1:
case 4:
case 6:
//
// Regular FAT12/FAT12/BIGFAT --> XINT13 FAT
//
TheEntry->SysId = 0xe;
break;
case 0xb:
//
// FAT32 --> XINT13 FAT32
//
TheEntry->SysId = 0xc;
break;
}
} else {
switch(TheEntry->SysId) {
case 0xc:
//
// XINT13 FAT32 --> FAT32
//
TheEntry->SysId = 0xb;
break;
case 0xe:
//
// XINT13 FAT --> regular FAT
//
if(Relocating) {
if(MasterDiskInfo.StartupPartitionSectorCount >= 65536L) {
TheEntry->SysId = 6;
} else {
if(MasterDiskInfo.StartupPartitionSectorCount >= 32680L) {
TheEntry->SysId = 4;
} else {
TheEntry->SysId = 1;
}
}
} else {
if(PartitionImage.TotalSectorCount >= 65536L) {
TheEntry->SysId = 6;
} else {
if(PartitionImage.TotalSectorCount >= 32680L) {
TheEntry->SysId = 4;
} else {
TheEntry->SysId = 1;
}
}
}
break;
}
}
//
// Write the MBR.
//
// Note that after the MBR has been updated, the system will no longer
// boot into this program. Thus the master disk state is now irrelevent.
// But just for completeness, we track that we completed this operation.
//
if(!CmdLineArgs.Test) {
if(!WriteDisk(DiskHandle,0,1,IoBuffer)) {
FatalError(textWriteFailedAtSector,1,0L);
}
}
if(!CmdLineArgs.Test) {
UpdateMasterDiskState(DiskHandle,Relocating ? MDS_RELOCATED_BOOT_MBR : MDS_UPDATED_MBR);
}
}
VOID
TellUserToWait(
VOID
)
{
FPCHAR p,q;
char c;
UINT line;
INT maxlen;
DispClearClientArea(NULL);
//
// This could be more than one line. We want the message centered
// and left-aligned. Determine the longest line length and center
// the entire message based on that length.
//
maxlen = 0;
p = textPleaseWaitRestoring;
do {
//
// Locate the next newline or terminator
//
for(q=p; (*q != '\n') && *q; q++);
//
// See if this line is maximum length seen so far.
//
if((q-p) > maxlen) {
maxlen = q-p;
}
p = q+1;
} while(*q);
//
// Second pass actually prints it out.
//
p = textPleaseWaitRestoring;
line = 1;
do {
for(q=p; (*q != '\n') && *q; q++);
//
// Nul-terminate the line in preparation for printing it out.
//
c = *q;
*q = 0;
DispPositionCursor((BYTE)((80-maxlen)/2),(BYTE)(TEXT_TOP_LINE+line));
DispWriteString(p);
line++;
*q = c;
p = q+1;
} while(*q);
}
VOID
StartGauge(
VOID
)
{
ULONG SectorCount;
//
// Figure out how many sectors we will transfer in total.
// This includes
//
// a) relocating the cluster bitmap
// b) relocating the boot partition
// c) transferring the actual data
// d) zapping unselected OS images
//
SectorCount = 0;
if(PartitionImage.Flags & PARTIMAGE_RELOCATE_BITMAP) {
SectorCount += (PartitionImage.LastUsedCluster/CLUSTER_BITS_PER_SECTOR) + 1;
}
if(PartitionImage.Flags & PARTIMAGE_RELOCATE_BOOT) {
SectorCount += MasterDiskInfo.StartupPartitionSectorCount;
}
SectorCount += PartitionImage.NonClusterSectors;
SectorCount += PartitionImage.SectorsPerCluster * PartitionImage.UsedClusterCount;
SectorCount *= 2;
SectorCount += ZAP_MAX * (MasterDiskInfo.ImageCount-1);
GaugeInit(SectorCount);
}
BOOL
TestImage(
IN HDISK DiskHandle,
IN ULONG StartSector
)
{
FPVOID Buffer,OriginalBuffer;
ULONG CurrentSector;
ULONG ImageCRC;
ULONG CalcCRC;
ULONG BytesCRC;
ULONG SectorsRemaining;
ULONG TotalSectors;
ULONG BitmapSize;
BYTE Count;
DispClearClientArea(NULL);
DispPositionCursor(TEXT_LEFT_MARGIN,TEXT_TOP_LINE);
DispWriteString(textValidatingImage);
_Log("Validating image...\n");
DispWriteString("\n\n");
// need to allocate aligned buffer
if(!AllocTrackBuffer(63,&Buffer,&OriginalBuffer)) {
FatalError(textOOM);
return FALSE;
}
CalcCRC = CRC32_INITIAL_VALUE;
BytesCRC = 0;
// read inital sector to get info
CurrentSector = StartSector;
if( ReadDisk( DiskHandle, CurrentSector, 1, Buffer ) ) {
ImageCRC = ((PPARTITION_IMAGE)Buffer)->CRC;
BitmapSize = ((PPARTITION_IMAGE)Buffer)->LastUsedCluster;
BitmapSize = (BitmapSize % (8*512)) ? BitmapSize/(8*512)+1 : BitmapSize/(8*512);
SectorsRemaining = ((PPARTITION_IMAGE)Buffer)->NonClusterSectors // fs structs
+ ((PPARTITION_IMAGE)Buffer)->SectorsPerCluster
* ((PPARTITION_IMAGE)Buffer)->UsedClusterCount // data area
+ BitmapSize; // image cluster bitmap
((PPARTITION_IMAGE)Buffer)->CRC = 0;
((PPARTITION_IMAGE)Buffer)->BitmapRelocationStart = 0;
((PPARTITION_IMAGE)Buffer)->BootRelocationStart = 0;
((PPARTITION_IMAGE)Buffer)->Flags = 0;
TotalSectors = SectorsRemaining;
} else {
FatalError(textReadFailedAtSector,1,CurrentSector);
return FALSE;
}
CurrentSector++;
// update the computed CRC
CalcCRC = CRC32Compute( Buffer, 512, CalcCRC);
BytesCRC += 512;
GaugeInit(SectorsRemaining);
// loop reading the entire file, updating the CRC
while (SectorsRemaining) {
Count = (BYTE) ((SectorsRemaining > 63L) ? 63L : SectorsRemaining);
if( ReadDisk( DiskHandle, CurrentSector, Count , Buffer ) ) {
CalcCRC = CRC32Compute( Buffer, Count*512, CalcCRC);
BytesCRC += Count*512;
} else {
FatalError(textReadFailedAtSector,Count,CurrentSector);
return FALSE;
}
SectorsRemaining -= Count;
CurrentSector += Count;
// print progress
GaugeDelta(Count);
}
// compare with stored CRC
DispClearClientArea(NULL);
DispPositionCursor(TEXT_LEFT_MARGIN,TEXT_TOP_LINE);
_Log("Image file checksum = 0x%08lx\n",CalcCRC);
if( CalcCRC != ImageCRC ) {
FatalError(textChecksumFail);
_Log("** WARNING ** checksum does not match original checksum 0x%08lx\n",ImageCRC);
return FALSE;
} else {
DispWriteString(textChecksumOk);
}
_Log("Image checksum ok.\n");
free(OriginalBuffer);
return TRUE;
}
BOOL
MungeParametersForFat32Extend(
IN HDISK DiskHandle,
IN USHORT Cylinders,
IN BYTE SectorsPerTrack,
IN ULONG SourceStart,
IN ULONG *TargetStart,
IN FPMASTER_DISK pMasterDisk,
IN FPPARTITION_IMAGE pPartitionImage,
IN VOID *TemporaryBuffer
)
{
ULONG SectorsPerCylinder;
USHORT ReservedSectorCount;
ULONG NewSectorCount;
ULONG NewFatSize;
ULONG ClusterSize;
//
// check for FAT32
//
if( pPartitionImage->SystemId == 0x0b || pPartitionImage->SystemId == 0x0c ) {
SectorsPerCylinder = pMasterDisk->OriginalHeads * pMasterDisk->OriginalSectorsPerTrack;
//
// get the reserved sector count and the cluster size from the header
//
ReservedSectorCount = pPartitionImage->Fat32ReservedSectors;
ClusterSize = pPartitionImage->SectorsPerCluster;
//
// Read the boot sector of the image
//
if(!ReadDisk(DiskHandle,SourceStart+1,1,TemporaryBuffer)) {
FatalError(textReadFailedAtSector,1,0L);
}
//
// Need to save away the original FAT32 fat size.
//
if( IsFat32( TemporaryBuffer ) ) {
pPartitionImage->Fat32OriginalFatTableSectCount =
((FPFAT32_BOOT_SECTOR)TemporaryBuffer)->PackedBpb.LargeSectorsPerFat;
} else {
//
// If we're here, and this isn't FAT32, we're in trouble.
//
FatalError(textCantFindMasterDisk);
}
//
// calculate the max size of the new partition
// = (total cylinder count - the new start) * (sectors/cyl)
//
// we assume the new partition will extend to the end of the disk.
//
NewSectorCount = Cylinders * SectorsPerCylinder - *TargetStart;
//
// given this number of sectors, calculate how many clusters we can make
// and how many FAT entries we need to track them
//
NewFatSize = ComputeClusters(
ClusterSize*512, // # of bytes in a cluster
NewSectorCount, // # of sectors in partition
512, // sector size in bytes
ReservedSectorCount, // reserved sector count
2 // # of Fats
);
if( NewFatSize < MAX_FAT32_ENTRIES ) {
//
// if this will fit in a 16MB-64K FAT, then leave it alone.
//
} else {
//
// otherwise we clip the partition size to the max allowed.
//
NewFatSize = MAX_FAT32_ENTRIES;
NewSectorCount = ReservedSectorCount + MAX_FAT32_ENTRIES/256 + MAX_FAT32_ENTRIES * ClusterSize;
}
pPartitionImage->Fat32AdjustedFatTableEntryCount = NewFatSize;
pPartitionImage->Fat32AdjustedSectorCount = NewSectorCount;
}
return TRUE;
}
VOID
AdjustTargetStart(
IN HDISK DiskHandle,
IN BYTE SectorsPerTrack,
IN ULONG *TargetStart,
IN VOID *TemporaryBuffer
)
{
unsigned i;
BOOL foundUnknown;
FPPARTITION_TABLE_ENTRY pPartitionTab;
//
// Read the MBR
//
if(!ReadDisk(DiskHandle,0,1,TemporaryBuffer)) {
FatalError(textReadFailedAtSector,1,0L);
}
//
// Validate that it is a good MBR.
//
if( ((FPMBR)TemporaryBuffer)->AA55Signature != BOOT_RECORD_SIGNATURE ) {
FatalError(textReadFailedAtSector,1,0L);
_Log(" WARNING: The MBR was invalid!");
}
//
// now inspect the MBR, and check for an unrecognized partition.
//
pPartitionTab = ((FPMBR)TemporaryBuffer)->PartitionTable;
foundUnknown = FALSE;
for(i=0; i<4; i++) {
if( IsUnknownPartition(pPartitionTab[i].SysId) ) {
//
// we assume the EISA config/hiber partition is at the start of the disk
// adjust the start of the image restore to the cylinder past the end of it
//
foundUnknown = TRUE;
}
}
_Log(" The master disk claims %ld sectors were reserved for EISA and hiber partitions.\n",
MasterDiskInfo.FreeSpaceStart);
if(foundUnknown == FALSE && MasterDiskInfo.FreeSpaceStart ) {
//
// eh? How did FreeSpaceStart get set without having a partition there?
//
_Log(" WARNING: The master disk claims disk space was reserved for \n");
_Log(" EISA/hiber partition when one does not appear to exist!\n");
FatalError(textCantOpenMasterDisk);
}
if(MasterDiskInfo.FreeSpaceStart) {
//
// MasterDiskInfo.FreeSpaceStart will be non-zero if we discovered a non-recognized
// partition when we built the master disk. The target start sector should be adjusted
// to this number.
//
*TargetStart = MasterDiskInfo.FreeSpaceStart;
}
//
// otherwise we should just leave the target start sector alone.
//
return;
}
//
// modified from \nt\private\utils\ufat\src\rfatsa.cxx
//
ULONG
ComputeClusters(
IN ULONG ClusterSize,
IN ULONG Sectors,
IN ULONG SectorSize,
IN ULONG ReservedSectors,
IN ULONG Fats
)
/*++
Routine Description:
This routine computes the number of clusters on a volume given
the cluster size, volume size and the fat type.
Arguments:
ClusterSize - Supplies the size of a cluster in number of bytes.
Sectors - Supplies the total number of sectors in the volume.
SectorSize - Supplies the size of a sector in number of bytes.
ReservedSectors - Supplies the number of reserved sectors.
Fats - Supplies the number of copies of fat for this volume.
Return Value:
ULONG - The total number of clusters for the given configuration.
++*/
{
ULONG entries_per_sec; // Number of FAT entries per sector.
ULONG fat_entry_size; // Size of each FAT entry in number of BITS.
ULONG sectors_left; // Number of sectors left for consideration.
ULONG sec_per_clus; // Sectors per cluster.
ULONG increment = 1; // Increment step size in number of FAT sectors.
ULONG clusters = 0; // Number of clusters in total.
ULONG temp; // Temporary place-holder for optimizing certain
// computations.
sectors_left = Sectors - ReservedSectors;
sec_per_clus = ClusterSize / SectorSize;
//
// The Fat entry size is 32 bits, since we only support FAT32
//
fat_entry_size = 32;
//
// Compute the number of FAT entries a sector can hold.
// NOTE that fat_entry_size is the size in BITS,
// this is the reason for the "* 8" (bits per byte).
//
entries_per_sec = (SectorSize * 8) / fat_entry_size;
//
// Compute a sensible increment step size to begin with.
//
while (Sectors / (increment * entries_per_sec * sec_per_clus) > 1) {
increment *= 2;
}
//
// We have to handle the first sector of FAT entries
// separately because the first two entries are reserved.
// Kind of yucky, isn't it?
//
temp = Fats + ((entries_per_sec - 2) * sec_per_clus);
if (sectors_left < temp) {
return (sectors_left - Fats) / sec_per_clus;
} else {
sectors_left -= temp;
clusters += entries_per_sec - 2;
while (increment && sectors_left) {
temp = (Fats + entries_per_sec * sec_per_clus) * increment;
if (sectors_left < temp) {
//
// If the increment step is only one, try to utilize the remaining sectors
// as much as possible.
//
if (increment == 1) {
//
// Additional clusters may be possible after allocating
// one more sector of fat.
//
if ( sectors_left > Fats) {
temp = (sectors_left - Fats) / sec_per_clus;
if (temp > 0) {
clusters += temp;
}
}
}
//
// Cut the increment step by half if it is too big.
//
increment /= 2;
} else {
sectors_left -= temp;
clusters += increment * entries_per_sec;
}
}
return clusters;
}
FatalError("This line should never be executed.");
return 0;
}
BOOL
IsUnknownPartition(
IN BYTE SysId
)
{
if( SysId != 0x00 && // not unused
SysId != 0x01 &&
SysId != 0x04 &&
SysId != 0x06 &&
SysId != 0x07 &&
SysId != 0x0b &&
SysId != 0x0c &&
SysId != 0x0e ) {
return TRUE;
}
return FALSE;
}