#include #include #include #include #include #include #include #include #include #include #include #include #include #include "makemast.h" #define xERROR 0 #define xPROCEED 1 #define xBAIL 2 FPVOID IoBuffer,UnalignedBuffer; CMD_LINE_ARGS CmdLineArgs; ULONG CheckSum; ULONG ImageCheckSum; ULONG BytesCRC; ULONG FreeSpaceStart; ULONG FreeSpaceEnd; // // Extra space needed for worst case disk expansion. // // 16MB - 64K is the largest FAT that Win98 scandisk can handle, thus // we only support a FAT table up to that size. // // So at worst we'll need to reserve twice that amount of space (32MB) in front to prevent // the cluster restores from trashing our bitmap and MPK startup partitions. // // Fat32ResizeMaxOffset will be set to 0 if the partition isn't FAT32 #define FAT32_SPACE_TO_LEAVE 65535 ULONG Fat32ResizeMaxOffset; // // Text for the program // char *textNoDisks; char *textSelectDiskPrompt; char *textCantOpenDisk; char *textOOM; char *textDiskReadError; char *textDiskWriteError; char *textSureWipeMaster; char *textSureWipeDisk; char *textMbrIoFailed; char *textRebootPrompt; char *textDone; char *textDisk; char *textPaddedMbCount; char *textInvalidSelection; char *textMaster; char *textYesNo; char *textMasterDiskCorrupt; char *textUsage; char *textMasterDiskFull; char *textRoomForNImages; char *textEnterImageFilename; char *textInvalidImageFile; char *textImageFileTooBig; char *textCantAccessImageFile; char *textTransferringFile; char *textCantOpenFile; char *textReadingListFromFile; char *textGeometryMismatch; char *textTooFragmented; char *textChecksum; char *textChecksumFail; char *textBytesProcessed; char *textChecksumOK; char *textTooManyPartitions; char *textFoundExistingPartitions; char *textExpandedImageFileTooBig; char *textFreeSpaceNeedsToBeHere; MESSAGE_STRING TextMessages[] = { { &textNoDisks, 1 }, { &textSelectDiskPrompt, 2 }, { &textCantOpenDisk, 3 }, { &textOOM, 4 }, { &textDiskReadError, 5 }, { &textDiskWriteError, 6 }, { &textSureWipeMaster, 7 }, { &textSureWipeDisk, 8 }, { &textMbrIoFailed, 9 }, { &textRebootPrompt, 10 }, { &textDone, 11 }, { &textDisk, 12 }, { &textPaddedMbCount, 13 }, { &textInvalidSelection, 14 }, { &textMaster, 15 }, { &textYesNo, 16 }, { &textMasterDiskCorrupt, 17 }, { &textUsage, 18 }, { &textMasterDiskFull, 19 }, { &textRoomForNImages, 20 }, { &textEnterImageFilename, 21 }, { &textInvalidImageFile, 22 }, { &textImageFileTooBig, 23 }, { &textCantAccessImageFile, 24 }, { &textTransferringFile, 25 }, { &textCantOpenFile, 26 }, { &textReadingListFromFile, 27 }, { &textGeometryMismatch, 28 }, { &textTooFragmented, 29 }, { &textChecksum, 30 }, { &textChecksumFail, 31 }, { &textBytesProcessed, 32 }, { &textChecksumOK, 33 }, { &textTooManyPartitions, 34 }, { &textFoundExistingPartitions, 35 }, { &textExpandedImageFileTooBig,36 }, { &textFreeSpaceNeedsToBeHere,37 } }; BOOL DoIt( IN UINT DiskId ); BOOL SetUpMasterDisk( IN HDISK DiskHandle, IN USHORT SectorCount, IN BOOL Init ); BOOL TransferImages( IN HDISK DiskHandle ); BOOL DetermineRelocations( IN UINT FileHandle, IN HDISK DiskHandle, IN ULONG ImageStartSector, IN ULONG ImageSectorCount, IN FPMASTER_DISK MasterDisk ); BOOL NeedToRelocClusterBitmap( IN FPMASTER_DISK MasterDisk, IN OUT FPPARTITION_IMAGE PartitionImage, IN ULONG ImageStart, IN UINT FileHandle, IN OUT FPBYTE ClusterBuffer ); BOOL NeedToRelocBootPart( IN FPMASTER_DISK MasterDisk, IN OUT FPPARTITION_IMAGE PartitionImage, IN UINT FileHandle, IN OUT FPBYTE ClusterBuffer ); BOOL IsClusterRunFree( IN UINT FileHandle, IN FPPARTITION_IMAGE PartitionImage, IN ULONG StartCluster, IN ULONG ClusterCount, IN OUT FPBYTE ClusterBuffer, OUT BOOL *Free ); BOOL IsRunGoodForRelocations( IN ULONG ImageStart, IN ULONG ImageLength, IN FPMASTER_DISK MasterDisk, IN OUT ULONG *RunStart, IN ULONG RunLength, IN ULONG SectorsNeeded ); BOOL DetermineIfEisaPartitionExists( IN HDISK DiskHandle, IN VOID* IoBuffer, IN FPPARTITION_TABLE_ENTRY pPartitionTable ); ULONG DetermineHighestSectOccupied( IN FPPARTITION_TABLE_ENTRY pPartitionTable, IN BYTE SectorsPerTrack, IN USHORT Heads ); BOOL RestorePartitionTable( IN HDISK DiskHandle, IN VOID* IoBuffer, IN FPPARTITION_TABLE_ENTRY pPartitionTable ); BYTE FindFirstUnusedEntry( IN FPPARTITION_TABLE_ENTRY pPartitionTable ); BOOL IsUnknownPartition( BYTE SysId ); int main( IN int argc, IN char *argv[] ) { UINT DiskCount; UINT Selection; BOOL b; UINT u; BYTE Int13Unit; ULONG dontcare; if(!GetTextForProgram(argv[0],TextMessages,sizeof(TextMessages)/sizeof(TextMessages[0]))) { fprintf(stderr,"Unable to find messages for program\n"); return(FAILURE); } if(!ParseArgs(argc,argv,TRUE,"DFIMQRXY",&CmdLineArgs)) { fprintf(stderr,textUsage); fprintf(stderr,"\n"); return(FAILURE); } // // Initialize. // DiskCount = InitializeDiskList(); if(!DiskCount) { fprintf(stderr,"%s\n",textNoDisks); return(FAILURE); } _Log("%u disks\n",DiskCount); // // We don't check return code because zero partitions // is a perfectly valid case. // InitializePartitionList(); // // Allocate a maximally-sized i/o buffer // if(!AllocTrackBuffer(63,&IoBuffer,&UnalignedBuffer)) { fprintf(stderr,"%s\n",textOOM); return(FAILURE); } // // Select disk. // Selection = (UINT)(-1); if(CmdLineArgs.MasterDiskInt13Unit) { for(u=0; u FreeSpaceEnd ) { // // The logic that determines FreeSpaceStart assumes that all the existing partitions // on the disk are at the beginning of the disk and the free space is contiguous after // the last pre-existing unknown partition. // fprintf(stderr,textFreeSpaceNeedsToBeHere); fprintf(stderr,"\n"); _Log(textFreeSpaceNeedsToBeHere); _Log("\n"); return(FALSE); } // // Clear out the partition table and then create a new partition // of the requested size at the end of the disk. // We also slam in our boot code. // // Once we've done that, slam in our special data structure // on sector 1. // if(Init) { _Log("Initializing the disk... \n"); if(ReinitializePartitionTable(DiskHandle,IoBuffer) == -1) { fprintf(stderr,"%s\n",textMbrIoFailed); return(FALSE); } PartId = MakePartitionAtEndOfEmptyDisk(DiskHandle,IoBuffer,SectorCount,TRUE); if(PartId != (UINT)(-1)) { GetPartitionInfoById( PartId, 0, &DiskId, &SystemId, &StartSector, &Count ); } else { fprintf(stderr,"%s\n",textMbrIoFailed); return(FALSE); } if( bPreserveEisaPartition ) { // // We put the partitions we want to save back into the partition table. // if(!RestorePartitionTable(DiskHandle,IoBuffer,PartitionTable)) { return FALSE; } } memset(p,0,512); p->StartupPartitionStartSector = StartSector; p->StartupPartitionSectorCount = Count; p->Signature = MASTER_DISK_SIGNATURE; p->Size = sizeof(MASTER_DISK); p->FreeSpaceStart = FreeSpaceStart; p->FreeSpaceEnd = FreeSpaceEnd; } else { // // Reinitialize only selected fields // if(!ReadDisk(DiskHandle,1,1,p)) { fprintf(stderr,textDiskReadError,1L); fprintf(stderr,"\n"); return(FALSE); } p->State = MDS_NONE; p->SelectedLanguage = 0; p->SelectionOrdinal = 0; p->ClusterBitmapStart = 0; p->NonClusterSectorsDone = 0; p->ForwardXferSectorCount = 0; p->ReverseXferSectorCount = 0; } // // Store away or validate the disk geometry. // if(!Init) { _Log("Checking disk geometry...\n"); // // Validate that this matches the original geometry values // if((SectorsPerTrack != p->OriginalSectorsPerTrack) || (Heads != p->OriginalHeads)) { fprintf(stderr,"\n%s\n",textGeometryMismatch); } _Log("%d sectors per track, %d heads.\n", SectorsPerTrack, Heads); _Log("Master disk geometry matches values recorded in master disk.\n"); } else { _Log("Setting disk geometry...\n"); p->OriginalSectorsPerTrack = SectorsPerTrack; p->OriginalHeads = Heads; _Log("%d sectors per track, %d heads.\n", SectorsPerTrack, Heads); } // // Save it out to disk. // if(!WriteDisk(DiskHandle,1,1,p)) { fprintf(stderr,textDiskWriteError,1L); fprintf(stderr,"\n"); return(FALSE); } // // Apply the boot image if specified. // if(CmdLineArgs.ImageFile) { if(!ApplyImage(DiskHandle,p->StartupPartitionStartSector,p->StartupPartitionSectorCount,SectorsPerTrack,Heads)) { return(FALSE); } printf("\n\n"); } return(TRUE); } BOOL TransferImages( IN HDISK DiskHandle ) { PPARTITION_IMAGE Images; FPMASTER_DISK MasterDisk; ULONG NextImageEnd; UINT u,Limit; UINT FileHandle; UINT Read; ULONG FileSize; ULONG Sector; UINT NewImages; FPCHAR Filenames; FPCHAR p; ULONG OriginalSize; FPBYTE New; FILE *FileList; BOOL b; // // Retrieve the master disk structure. Leave it in the last sector // of the i/o buffer. // if(!ReadDisk(DiskHandle,1,1,(FPBYTE)IoBuffer+(62*512))) { fprintf(stderr,textDiskReadError,1L); fprintf(stderr,"\n"); return(FALSE); } MasterDisk = (FPVOID)((FPBYTE)IoBuffer+(62*512)); // // Allocate a buffer for the image headers. // Images = malloc(MasterDisk->ImageCount * sizeof(PARTITION_IMAGE)); if(!Images) { fprintf(stderr,"%s\n",textOOM); } // // Read each existing image's header. // NextImageEnd = MasterDisk->StartupPartitionStartSector; for(u=0; uImageCount; u++) { if(!ReadDisk(DiskHandle,MasterDisk->ImageStartSector[u],1,IoBuffer)) { fprintf(stderr,textDiskReadError,MasterDisk->ImageStartSector[u]); fprintf(stderr,"\n"); return(FALSE); } memmove(&Images[u],IoBuffer,sizeof(PARTITION_IMAGE)); // // Sanity check // if((Images[u].Signature != PARTITION_IMAGE_SIGNATURE) || (Images[u].Size != sizeof(PARTITION_IMAGE))) { fprintf(stderr,textMasterDiskCorrupt,MasterDisk->ImageStartSector[u]); fprintf(stderr,"\n"); return(FALSE); } NextImageEnd = MasterDisk->ImageStartSector[u]; } // // Make sure there's room for new images. // if(MasterDisk->ImageCount >= MAX_PARTITION_IMAGES) { fprintf(stderr,textMasterDiskFull,MasterDisk->ImageCount); fprintf(stderr,"\n"); return(FALSE); } else { // // Prompt the user for partition image filenames. // // Open and validate each file, and make sure there's room on the disk // for the image. We don't leave the files open since under DOS // we might run out of file handles, and we don't want to transfer // the data now (it could take a long time and we want to let the user // go get a cup of coffee or something). Actual data transfer // takes place in a separate loop. // // We leave 1 track + 128 sectors free at the start of the disk. // The track is for the MBR and the other is used for transfers where // the max transfer size is for one 64K cluster (we ensure that // no transfer's write range overlaps its read). Because we're not // totally confident that the geometry we've got now is the one that // will be in effect on the end-user's machine, we maximize // the track size. // Filenames = malloc(0); if(!Filenames) { fprintf(stderr,"%s\n",textOOM); return(FALSE); } p = Filenames; Limit = MAX_PARTITION_IMAGES - MasterDisk->ImageCount; NewImages = 0; printf(textRoomForNImages,Limit); printf("\n"); if(CmdLineArgs.FileListFile) { FileList = fopen(CmdLineArgs.FileListFile,"rt"); if(!FileList) { fprintf(stderr,textCantOpenFile,CmdLineArgs.FileListFile); fprintf(stderr,"\n"); return(FALSE); } printf(textReadingListFromFile,CmdLineArgs.FileListFile); printf("\n"); } for(u=0; uSignature != PARTITION_IMAGE_SIGNATURE) || (((FPPARTITION_IMAGE)IoBuffer)->Size != sizeof(PARTITION_IMAGE))) { _dos_close(FileHandle); printf("%s\n",textInvalidImageFile); goto prompt; } FileSize /= 512; // // BUGBUG we should reserve at least 32MB worth of sectors to prevent // overlapping with the expanded FAT table in the Fat32 case. // // As well, we need to have the capability to preserve a hiber partition. // FreeSpaceStart describes how many sectors we need to leave at the // beginning of the disk to avoid stomping on such a partition. // // FreeSpaceStart was determined in SetupMasterDisk() // #define RESERVE (63+128+FAT32_SPACE_TO_LEAVE) if((NextImageEnd - RESERVE - FreeSpaceStart) < FileSize) { _dos_close(FileHandle); fprintf(stderr,textImageFileTooBig,FileSize,NextImageEnd-RESERVE); fprintf(stderr,"\n"); goto prompt; } // // Now check that the disk is at least big enough to contain the real size // of the image. // if( FreeSpaceEnd - FreeSpaceStart < ((FPPARTITION_IMAGE)IoBuffer)->TotalSectorCount ) { _dos_close(FileHandle); fprintf(stderr,textExpandedImageFileTooBig, ((FPPARTITION_IMAGE)IoBuffer)->TotalSectorCount, FreeSpaceEnd - FreeSpaceStart); fprintf(stderr,"\n"); _Log(textExpandedImageFileTooBig, ((FPPARTITION_IMAGE)IoBuffer)->TotalSectorCount, FreeSpaceEnd - FreeSpaceStart); _Log("\n"); goto prompt; } // // It's OK. Remember the filename. // New = realloc(Filenames,(p - Filenames) + strlen((FPBYTE)IoBuffer+512) + 1); if(!New) { fprintf(stderr,"%s\n",textOOM); return(FALSE); } p += New - Filenames; Filenames = New; strcpy(p,(FPBYTE)IoBuffer+512); p += strlen(p)+1; MasterDisk->ImageStartSector[MasterDisk->ImageCount+u] = NextImageEnd - FileSize; NextImageEnd -= FileSize; NewImages++; } else { break; } } } // // Now transfer the data for new images. // printf("\n"); for(p=Filenames,u=0; uImageStartSector[MasterDisk->ImageCount+u]; b = DetermineRelocations( FileHandle, DiskHandle, Sector, FileSize, MasterDisk ); if(!b) { _dos_close(FileHandle); return(FALSE); } FileSize--; Sector++; while(FileSize) { Limit = (FileSize > 62L) ? 62 : (UINT)FileSize; if(_dos_read(FileHandle,IoBuffer,Limit*512,&Read) || (Read != (Limit*512))) { _dos_close(FileHandle); fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } CheckSum = CRC32Compute( IoBuffer, Limit*512, CheckSum); BytesCRC += Limit*512; if(!WriteDisk(DiskHandle,Sector,(BYTE)Limit,IoBuffer)) { _dos_close(FileHandle); fprintf(stderr,"\n"); fprintf(stderr,textDiskWriteError,Sector); fprintf(stderr,"\n"); return(FALSE); } FileSize -= Limit; Sector += Limit; printf(textTransferringFile,p,100*(OriginalSize-FileSize)/OriginalSize); printf("\r"); } _dos_close(FileHandle); printf(textChecksum, p, CheckSum ); _Log(textChecksum, p, CheckSum ); _Log("\n"); printf("\n"); printf( textBytesProcessed,BytesCRC ); _Log( textBytesProcessed,BytesCRC ); _Log("\n"); printf("\n"); if( CheckSum != ImageCheckSum ) { printf(textChecksumFail,ImageCheckSum); _Log(textChecksumFail,ImageCheckSum); } else { printf(textChecksumOK); _Log(textChecksumOK); } printf("\n"); _Log("\n"); } MasterDisk->ImageCount += NewImages; if(!WriteDisk(DiskHandle,1,1,(FPBYTE)IoBuffer+(62*512))) { fprintf(stderr,textDiskWriteError,1L); fprintf(stderr,"\n"); return(FALSE); } free(Filenames); free(Images); return(TRUE); } BYTE BitValue[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; BOOL DetermineRelocations( IN UINT FileHandle, IN HDISK DiskHandle, IN ULONG ImageStartSector, IN ULONG ImageSectorCount, IN FPMASTER_DISK MasterDisk ) /*++ Routine Description: This routine determines where there is unused space in the volume being imaged that will allow relocation of the cluster bitmap and the mpk boot partition somewhere on the disk. Arguments: Return Value: --*/ { UINT Read; FPPARTITION_IMAGE p; FPBYTE ClusterBuffer; ULONG SectorsNeeded; ULONG Count; ULONG Cluster; ULONG Base; BOOL On; BOOL InRun; ULONG Start; BOOL b; // // Set up pointers. // p = IoBuffer; ClusterBuffer = (FPBYTE)IoBuffer + 512; // // Read the partition image header from the file. // if(DosSeek(FileHandle,0,DOSSEEK_START)) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } if(_dos_read(FileHandle,p,512,&Read) || (Read != 512)) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } // // Calculate the checksum of the header. // ImageCheckSum = ((PPARTITION_IMAGE)p)->CRC; ((PPARTITION_IMAGE)p)->CRC = 0; CheckSum = CRC32Compute( (FPBYTE)p, 512, CheckSum); BytesCRC += 512; ((PPARTITION_IMAGE)p)->CRC = ImageCheckSum; // // determine whether this is a FAT32 image // if(DosSeek(FileHandle,512,DOSSEEK_START) == (ULONG)(-1)) { _dos_close(FileHandle); printf("%s\n",textInvalidImageFile); return FALSE; } if( _dos_read(FileHandle,ClusterBuffer,512,&Read) || (Read != 512) ) { _dos_close(FileHandle); printf("%s\n",textInvalidImageFile); return FALSE; } if( IsFat32(ClusterBuffer) ) { Fat32ResizeMaxOffset = FAT32_SPACE_TO_LEAVE; _Log("The image is a FAT32 volume, setting up resizing...\n\n"); } else { // we don't support resizing Fat32ResizeMaxOffset = 0; _Log("The image is not a FAT32 volume, resizing disabled...\n\n"); } // // Determine whether the cluster bitmap will need to be relocated // and whether the boot partition will need to be relocated. // Track how many contiguous sectors are needed to relocate // the stuff we need to relocate. If we don't need to relocate // anything, we're done. // p->Flags &= ~(PARTIMAGE_RELOCATE_BITMAP | PARTIMAGE_RELOCATE_BOOT); p->BitmapRelocationStart = 0; p->BootRelocationStart = 0; SectorsNeeded = 0; if(!NeedToRelocClusterBitmap(MasterDisk,p,ImageStartSector,FileHandle,ClusterBuffer)) { return(FALSE); } if(p->Flags & PARTIMAGE_RELOCATE_BITMAP) { SectorsNeeded += (p->LastUsedCluster/(8*512)) + 1; _Log("Need to relocate bitmap\n"); } if(!NeedToRelocBootPart(MasterDisk,p,FileHandle,ClusterBuffer)) { return(FALSE); } if(p->Flags & PARTIMAGE_RELOCATE_BOOT) { SectorsNeeded += MasterDisk->StartupPartitionSectorCount; _Log("Need to relocate boot\n"); } if( p->Flags & PARTIMAGE_RELOCATE_BOOT || p->Flags & PARTIMAGE_RELOCATE_BITMAP ) { SectorsNeeded += Fat32ResizeMaxOffset; _Log("Need to add maximum possible %ld sectors to compensate for FAT32 fat growth.\n", Fat32ResizeMaxOffset); } if(SectorsNeeded) { _Log("0x%lx sectors needed for relocation\n",SectorsNeeded); b = FALSE; // // Seek the input file to the beginning of the cluster bitmap. // Start = (1 + p->NonClusterSectors + (p->SectorsPerCluster * p->UsedClusterCount)) * 512; if(DosSeek(FileHandle,Start,DOSSEEK_START) != Start) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } Count = 0; for(Cluster=0; Cluster <= p->LastUsedCluster; Cluster++,Base++) { // // Reload the cluster buffer if necessary. // if(!(Cluster % (512*8))) { if(_dos_read(FileHandle,ClusterBuffer,512,&Read) || (Read != 512)) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } Base = 0; } On = (ClusterBuffer[Base/8] & BitValue[Base%8]) ? TRUE : FALSE; // // Do something special before we examine the first cluster's bit, // to force a state transition. // if(!Cluster) { InRun = !On; } if(On == InRun) { // // Used/unused state didn't change. If we're not in a used run, // keep track of the size. // if(!On) { Count += p->SectorsPerCluster; } } else { if(On) { // // First 1 bit in a used run. The current free run is now // exhausted, so examine it. // if(Count) { b = IsRunGoodForRelocations( ImageStartSector, ImageSectorCount, MasterDisk, &Start, Count, SectorsNeeded ); if(b) { // // for FAT32 Fat32ResizeMaxOffset will be != 0 // this lets us offset the relocation to account // for possible FAT expansion. // // If there is an EISA/hiber partition, we need to // offset the start of the relocation by FreeSpaceStart, // otherwise we might get stomped when we're restoring // the partition image. // Start = Start + Fat32ResizeMaxOffset + FreeSpaceStart; if(p->Flags & PARTIMAGE_RELOCATE_BOOT) { _Log("Boot will be relocated to 0x%lx\n",Start); p->BootRelocationStart = Start; Start += MasterDisk->StartupPartitionSectorCount; } if(p->Flags & PARTIMAGE_RELOCATE_BITMAP) { _Log("Bitmap will be relocated to 0x%lx\n",Start); p->BitmapRelocationStart = Start; } break; } } } else { // // First 0 bit in an unused run. // Count = p->SectorsPerCluster; Start = (ULONG)(MasterDisk->OriginalSectorsPerTrack) + p->NonClusterSectors + (Cluster * p->SectorsPerCluster); } InRun = On; } } } else { b = TRUE; } if(b) { // // Restore the file pointer to just after the partition image // header sector. // if(DosSeek(FileHandle,512,DOSSEEK_START) != 512) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } // // Write out the partition image header. // if(!WriteDisk(DiskHandle,ImageStartSector,1,p)) { fprintf(stderr,"\n"); fprintf(stderr,textDiskWriteError,ImageStartSector); fprintf(stderr,"\n"); return(FALSE); } } else { fprintf(stderr,"\n%s\n",textTooFragmented); } return(b); } BOOL NeedToRelocClusterBitmap( IN FPMASTER_DISK MasterDisk, IN OUT FPPARTITION_IMAGE PartitionImage, IN ULONG ImageStart, IN UINT FileHandle, IN OUT FPBYTE ClusterBuffer ) { ULONG ClusterBitmapStart; ULONG ClusterBitmapSize; ULONG Cluster0Sector; ULONG FirstUnused; ULONG FirstCluster; ULONG LastCluster; ULONG NeededClusters; BOOL b; // // Figure out where the cluster bitmap starts on-disk and // how many sectors it occupies. We offset the start by // Fat32ResizeMaxOffset sectors at the front of the allocation // in order to anticipate FAT expansion. // // Fat32ResizeMaxOffset will be zero for a non-FAT32 image and // will have no effect in that case. // ClusterBitmapStart = ImageStart + 1 + PartitionImage->NonClusterSectors + (PartitionImage->UsedClusterCount * PartitionImage->SectorsPerCluster) - Fat32ResizeMaxOffset; ClusterBitmapSize = (PartitionImage->LastUsedCluster/(8*512)) + 1 + Fat32ResizeMaxOffset; _Log("On-disk cluster bitmap start/size = 0x%lx/0x%lx\n",ClusterBitmapStart,ClusterBitmapSize); // // Calculate the first physical sector corresponding to cluster 0 // in the restored volume, and the first sector past the last // used cluster in the restored volume. // Cluster0Sector = MasterDisk->OriginalSectorsPerTrack + PartitionImage->NonClusterSectors + FreeSpaceStart; FirstUnused = Cluster0Sector + ((PartitionImage->LastUsedCluster+1) * PartitionImage->SectorsPerCluster); _Log("Volume cluster 0 sector = 0x%lx\n",Cluster0Sector); _Log("Volume first unused sector = 0x%lx\n",FirstUnused); // // If the cluster bitmap overlaps or could potentially overlap // with the area on the disk where the non-cluster data will get // restored, then it must be relocated. // if(ClusterBitmapStart < Cluster0Sector ) { _Log("Cluster bitmap is in volume non-cluster data\n"); PartitionImage->Flags |= PARTIMAGE_RELOCATE_BITMAP; return(TRUE); } // // If the cluster bitmap is in space we know for sure is free, then // it does not need to be relocated. // // If we are preserving an EISA/hiber partition, we need to adjust // by FreeSpaceStart sectors. // if(ClusterBitmapStart >= FirstUnused) { _Log("Cluster bitmap is past end of used space in volume\n"); return(TRUE); } // // Figure out which clusters in the restored volume are occupied by the // on-disk cluster bitmap. // FirstCluster = (ClusterBitmapStart - Cluster0Sector) / PartitionImage->SectorsPerCluster; LastCluster = (((ClusterBitmapStart + ClusterBitmapSize) - 1) - Cluster0Sector) / PartitionImage->SectorsPerCluster; _Log("First/last clusters used by cluster bitmap: 0x%lx/0x%lx\n",FirstCluster,LastCluster); if(LastCluster > PartitionImage->LastUsedCluster) { LastCluster = PartitionImage->LastUsedCluster; _Log("(last cluster adjusted to 0x%lx)\n",LastCluster); } NeededClusters = (LastCluster - FirstCluster) + 1; _Log("Need 0x%lx clusters free in volume to encompass cluster bitmap\n",NeededClusters); // // Determine whether these clusters are free in the volume // if(!IsClusterRunFree(FileHandle,PartitionImage,FirstCluster,NeededClusters,ClusterBuffer,&b)) { return(FALSE); } if(b) { _Log("Needed clusters for bitmap are free\n"); } else { _Log("Needed clusters for bitmap are not free\n"); PartitionImage->Flags |= PARTIMAGE_RELOCATE_BITMAP; } return(TRUE); } BOOL NeedToRelocBootPart( IN FPMASTER_DISK MasterDisk, IN OUT FPPARTITION_IMAGE PartitionImage, IN UINT FileHandle, IN OUT FPBYTE ClusterBuffer ) { ULONG Cluster0Sector; ULONG FirstUnused; ULONG FirstCluster; ULONG LastCluster; ULONG NeededClusters; ULONG StartupPartitionStartSector; ULONG StartupPartitionSectorCount; BOOL b; // // Calculate the first physical sector corresponding to cluster 0 // in the restored volume, and the first physical sector past the last // used cluster in the restored volume. // Cluster0Sector = MasterDisk->OriginalSectorsPerTrack + PartitionImage->NonClusterSectors + FreeSpaceStart; FirstUnused = Cluster0Sector + ((PartitionImage->LastUsedCluster+1) * PartitionImage->SectorsPerCluster); _Log("Volume cluster 0 sector = 0x%lx\n",Cluster0Sector); _Log("Volume first unused sector = 0x%lx\n",FirstUnused); // // If the boot partition is in space we know for sure is free, then // it does not need to be relocated. // // We offset the start of the MPK boot partition by Fat32ResizeMaxOffset // sectors at the front of the allocation in order to anticipate FAT expansion. // // Fat32ResizeMaxOffset will be zero for a non-FAT32 image and // will have no effect in that case. // StartupPartitionStartSector = MasterDisk->StartupPartitionStartSector - Fat32ResizeMaxOffset; StartupPartitionSectorCount = MasterDisk->StartupPartitionSectorCount + Fat32ResizeMaxOffset; if(StartupPartitionStartSector >= FirstUnused) { _Log("Boot partition is past end of used space in volume\n"); return(TRUE); } // // Figure out which clusters in the restored volume are occupied by the // boot partition. // FirstCluster = (StartupPartitionStartSector - Cluster0Sector) / PartitionImage->SectorsPerCluster; LastCluster = (((StartupPartitionStartSector + StartupPartitionSectorCount) - 1) - Cluster0Sector) / PartitionImage->SectorsPerCluster; _Log("First/last clusters used by boot partition: 0x%lx/0x%lx\n",FirstCluster,LastCluster); if(LastCluster > PartitionImage->LastUsedCluster) { LastCluster = PartitionImage->LastUsedCluster; _Log("(last cluster adjusted to 0x%lx)\n",LastCluster); } NeededClusters = (LastCluster - FirstCluster) + 1; _Log("Need 0x%lx clusters free in volume to encompass boot partition.\n",NeededClusters); // // Determine whether these clusters are free in the volume // if(!IsClusterRunFree(FileHandle,PartitionImage,FirstCluster,NeededClusters,ClusterBuffer,&b)) { return(FALSE); } if(b) { _Log("Needed clusters for boot partition are free\n"); } else { _Log("Needed clusters for boot partition are not free\n"); PartitionImage->Flags |= PARTIMAGE_RELOCATE_BOOT; } return(TRUE); } BOOL IsClusterRunFree( IN UINT FileHandle, IN FPPARTITION_IMAGE PartitionImage, IN ULONG StartCluster, IN ULONG ClusterCount, IN OUT FPBYTE ClusterBuffer, OUT BOOL *Free ) { ULONG Count; UINT Bit; ULONG Offset; UINT Read; *Free = FALSE; _Log(" Checking if cluster run is free: start 0x%lx, length 0x%lx\n",StartCluster,ClusterCount); // // Calculate the offset in the file to the sector of the // cluster bitmap continaing the entry for the start cluster. // Offset = 1 + PartitionImage->NonClusterSectors + (PartitionImage->UsedClusterCount * PartitionImage->SectorsPerCluster); Offset += StartCluster / (8*512); _Log(" Offset in image file to cluster bitmap = 0x%lx sectors\n",Offset); Offset *= 512; if(DosSeek(FileHandle,Offset,DOSSEEK_START) != Offset) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } if(_dos_read(FileHandle,ClusterBuffer,512,&Read) || (Read != 512)) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } Offset = (StartCluster % (8*512)) / 8; Bit = (UINT)(StartCluster % 8); Count = 0; _Log(" Start byte/bit in cluster buffer: %u/%u\n",Offset,Bit); while((StartCluster < PartitionImage->LastUsedCluster) && !(ClusterBuffer[Offset] & BitValue[Bit])) { if(++Count == ClusterCount) { _Log(" Range is free\n"); *Free = TRUE; return(TRUE); } StartCluster++; Bit++; if(Bit == 8) { Bit = 0; Offset++; if((Offset == 512) && (StartCluster < PartitionImage->LastUsedCluster)) { if(_dos_read(FileHandle,ClusterBuffer,512,&Read) || (Read != 512)) { fprintf(stderr,"\n%s\n",textCantAccessImageFile); return(FALSE); } Offset = 0; } } } _Log(" Range is not free\n"); return(TRUE); } BOOL IsRunGoodForRelocations( IN ULONG ImageStart, IN ULONG ImageLength, IN FPMASTER_DISK MasterDisk, IN OUT ULONG *RunStart, IN ULONG RunLength, IN ULONG SectorsNeeded ) { ULONG RunEnd; ULONG ImageEnd; _Log(" Check if run is ok for reloc, run start = 0x%lx, len = 0x%lx\n",*RunStart,RunLength); // // Firstly, the run has to be large enough. // if(RunLength < SectorsNeeded) { _Log(" Run is too small\n"); return(FALSE); } _Log(" Image start = 0x%lx, length = 0x%lx\n",ImageStart,ImageLength); _Log(" Boot start = 0x%lx, length = 0x%lx\n",MasterDisk->StartupPartitionStartSector,MasterDisk->StartupPartitionSectorCount); // // Secondly, the run must not overlap the on-disk image. // if((*RunStart + SectorsNeeded) > ImageStart) { // // There's not enough space at the start of the run. // We need to see whether there's enough space at the end. // _Log(" Not enough space at head of run\n"); RunEnd = *RunStart + RunLength; ImageEnd = ImageStart + ImageLength; if((RunEnd - SectorsNeeded) < ImageEnd) { // // Not enough space at the end either. // _Log(" Not enough space at end either\n"); return(FALSE); } // // There could be enough space at the end of the run. // Adjust the run parameters in preparation for checking for // overlap with the boot partition. // if(*RunStart < ImageEnd) { _Log(" Run starts inside image, adjusting run start\n"); *RunStart = ImageEnd; } else { _Log(" Run is entirely past end of image\n"); } // // Note: we don't check for the case where the run is somehow // further out on the disk than the bootup partition. We assume // the start of the boot partition is a magic boundary. // if((*RunStart + SectorsNeeded) <= MasterDisk->StartupPartitionStartSector) { _Log(" Run is acceptable\n"); return(TRUE); } else { _Log(" Run overlaps boot partition\n"); return(FALSE); } } else { // // There's enough space at the start of the run. // We assume that the image is nearer to the start of the disk // than than the boot partition is, so in this case we don't // need to check anything else, we're done. // _Log(" There's enough space at head of run\n"); return(TRUE); } } BOOL DetermineIfEisaPartitionExists( IN HDISK DiskHandle, IN VOID* IoBuffer, IN FPPARTITION_TABLE_ENTRY pPartitionTable ) { UCHAR i; BOOL found; // // Suck in the MBR // if(!ReadDisk(DiskHandle,0,1,IoBuffer)) { fprintf(stderr,textDiskReadError,0); fprintf(stderr,"\n"); return(FALSE); } // // Validate that it is a good MBR. // if( ((FPMBR)IoBuffer)->AA55Signature != BOOT_RECORD_SIGNATURE ) { fprintf(stderr,textDiskReadError,0); _Log(" WARNING: The MBR was invalid!"); return FALSE; } // // Save away the partition table into the buffer we were passed. // memcpy(pPartitionTable, ((FPMBR)IoBuffer)->PartitionTable, sizeof(PARTITION_TABLE_ENTRY) * NUM_PARTITION_TABLE_ENTRIES); // // Now walk the entries, looking for non-recognized partition types. // We assume these are EISA or hiber partitions. // found = FALSE; for( i=0; iAA55Signature != BOOT_RECORD_SIGNATURE ) { fprintf(stderr,textDiskReadError,0); _Log(" WARNING: The MBR was invalid!"); return FALSE; } // // Restore the original partitions. // for(i=0;iPartitionTable); if( j != -1 ) { memcpy(&(theMBR->PartitionTable[j]), &(pPartitionTable[i]), sizeof(PARTITION_TABLE_ENTRY)); } else { fprintf(stderr,textTooManyPartitions); fprintf(stderr,"\n"); return(FALSE); } } } if(!WriteDisk(DiskHandle,0,1,IoBuffer)) { fprintf(stderr,textDiskWriteError,0); fprintf(stderr,"\n"); return(FALSE); } return TRUE; } 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; } BYTE FindFirstUnusedEntry( IN FPPARTITION_TABLE_ENTRY pPartitionTable ) { BYTE i; for(i=0;i