#include "imagpart.h" #include typedef struct _HANDLES { HPARTITION SourcePartitionHandle; UINT OutputFileHandle,BitmapFileHandle; } HANDLES, *PHANDLES; // // Global data // CMD_LINE_ARGS CmdLineArgs; FPVOID IoBuffer,OriginalIoBuffer; char OutputFilename[256]; char BitmapFilename[256]; ULONG CheckSum; ULONG BytesCRC; // // Text for the program // char *textNoPartitions; char *textSourcePrompt; char *textTransferringFsStructs; char *textPartOpenError; char *textFileReadFailed; char *textTransferringClusters; char *textCombiningFiles; char *textReadFailedAtSector; char *textOOM; char *textDone; char *textFileWriteError; char *textCantCreateNewFile; char *textSelectOutput; char *textDisk; char *textPaddedMbCount; char *textInvalidSelection; char *textUsage; char *textCantCreateFile; char *textScanningFat; char *textNtfsUnsupportedConfig; char *textNtfsCorrupt; char *textInitNtfsDataStruct; char *textNtfsBuildingBitmap; char *textProcessingNtfsBitmap; char *textUnsupportedFs; char *textChecksum; char *textBytesProcessed; MESSAGE_STRING TextMessages[] = { { &textNoPartitions,1 }, { &textSourcePrompt,2 }, { &textTransferringFsStructs,3 }, { &textPartOpenError,4 }, { &textFileReadFailed,5 }, { &textTransferringClusters,6 }, { &textCombiningFiles,7 }, { &textReadFailedAtSector,8 }, { &textOOM,9 }, { &textDone,11 }, { &textFileWriteError,12 }, { &textCantCreateNewFile,17 }, { &textSelectOutput,18 }, { &textDisk,20 }, { &textPaddedMbCount,21 }, { &textInvalidSelection,22 }, { &textUsage,25 }, { &textCantCreateFile,26 }, { &textScanningFat,27 }, { &textNtfsUnsupportedConfig,29 }, { &textNtfsCorrupt,30 }, { &textInitNtfsDataStruct,31 }, { &textNtfsBuildingBitmap,32 }, { &textProcessingNtfsBitmap,33 }, { &textUnsupportedFs,34 }, { &textChecksum,35 }, { &textBytesProcessed,36 } }; BOOL DoIt( IN PHANDLES Handles ); BOOL DetermineAndOpenSource( IN UINT PartitionCount, OUT HPARTITION *SourcePartitionHandle ); BOOL DetermineAndCreateOutput( OUT UINT *OutputFileHandle, OUT UINT *BitmapFileHandle ); BOOL DetermineFsAndInitVolumeData( IN HPARTITION PartitionHandle, OUT FilesystemType *FsType, OUT PPARTITION_IMAGE PartitionImage ); BOOL TransferFsStructsToOutput( IN HPARTITION PartitionHandle, IN UINT FileHandle, IN ULONG SectorCount ); BOOL TransferUsedClustersToOutput( IN HANDLES *Handles, IN PARTITION_IMAGE *PartitionImage ); BOOL AppendClusterMapToOutput( IN HANDLES *Handles, IN PARTITION_IMAGE *PartitionImage ); int main( IN int argc, IN char *argv[] ) { UINT PartCount; BOOL b; HANDLES Handles; 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,"DIQTXY",&CmdLineArgs)) { fprintf(stderr,textUsage); fprintf(stderr,"\n"); return(FAILURE); } _Log("Begin imagpart\n"); // // Allocate a maximally sized i/o buffer right up front. // _Log("Allocating Track Buffer.\n"); if(!AllocTrackBuffer(63,&IoBuffer,&OriginalIoBuffer)) { fprintf(stderr,"%s\n",textOOM); b = FALSE; goto c0; } _Log("Scanning partition tables...\n"); PartCount = InitializePartitionList(); if(!PartCount) { fprintf(stderr,"%s\n",textNoPartitions); b = FALSE; goto c1; } // // Get the source partition and target filename. // The source partition is opened and the target filename is created. // _Log("Determining target partition...\n"); b = DetermineAndOpenSource(PartCount,&Handles.SourcePartitionHandle); if(!b) { goto c1; } printf("\n\n"); _Log("Determining output file...\n"); b = DetermineAndCreateOutput(&Handles.OutputFileHandle,&Handles.BitmapFileHandle); if(!b) { goto c2; } CheckSum = CRC32_INITIAL_VALUE; BytesCRC = 0; _Log("Starting image transfer...\n"); b = DoIt(&Handles); printf(textChecksum, OutputFilename, CheckSum); printf("\n"); printf(textBytesProcessed, BytesCRC); _dos_close(Handles.OutputFileHandle); _dos_close(Handles.BitmapFileHandle); FlushDisks(); if(!CmdLineArgs.Test) { unlink(BitmapFilename); if(!b) { unlink(OutputFilename); } } c2: ClosePartition(Handles.SourcePartitionHandle); c1: free(OriginalIoBuffer); c0: if(b) { printf("\n%s\n",textDone); } _Log("Done.\n"); return(b ? SUCCESS : FAILURE); } BOOL DetermineAndOpenSource( IN UINT PartitionCount, OUT HPARTITION *SourcePartitionHandle ) /*++ Routine Description: This routine prompts the user to select a partition to be imaged, from the list of all partitions that are visible on all drives. The selected partition is opened for the caller. Arguments: PartitionCount - supplies the number of partitions on all drives, as returned by InitializePartitionList(). SourcePartitionHandle - in the success case, receives the handle to the selected partition. Return Value: Boolean value indicating outcome. If false, a message will have been printed explaining why. --*/ { UINT Selection; if(CmdLineArgs.Quiet) { Selection = 0; } else { Selection = SelectPartition( PartitionCount, textSourcePrompt, NULL, textDisk, textPaddedMbCount, textInvalidSelection ); } *SourcePartitionHandle = OpenPartition(Selection); if(!(*SourcePartitionHandle)) { fprintf(stderr,"%s\n",textPartOpenError); return(FALSE); } _Log("Partition opened sucessfully.\n"); return(TRUE); } BOOL DetermineAndCreateOutput( OUT UINT *OutputFileHandle, OUT UINT *BitmapFileHandle ) /*++ Routine Description: This routine prompts the user to enter a filename that will contain the output partition image. The file is created (it must not already exist). In addition a temporary file named $CLUSMAP.TMP will be created in the same directory. Any file with that name is overwritten. Arguments: OutputFileHandle - in the success case, receives a handle to the created output file, which will be 0-length. BitmapFileHandle - in the success case, receives a handle to the created $CLUMAP.TMP file, which will be 0-length. Return Value: Boolean value indicating outcome. If false, a message will have been printed explaining why. --*/ { char *p; if(CmdLineArgs.ImageFile) { printf("%s %s\n",textSelectOutput,CmdLineArgs.ImageFile); strcpy(OutputFilename,CmdLineArgs.ImageFile); goto gotit; } // // Prompt the user for a filename. // prompt: printf("%s ",textSelectOutput); gets(OutputFilename); // // Attempt to create the file // gotit: if(_dos_creatnew(OutputFilename,_A_NORMAL,OutputFileHandle)) { fprintf(stderr,"\n"); fprintf(stderr,textCantCreateNewFile,OutputFilename); fprintf(stderr,"\n"); goto prompt; } _Log("Output file name is %s\n",OutputFilename); _Log("Successfully created output file.\n"); // // Form the name of the temp file for the cluster bitmap // strcpy(BitmapFilename,OutputFilename); if(p = strrchr(BitmapFilename,'\\')) { p++; } else { if(BitmapFilename[1] == ':') { p = BitmapFilename+2; } else { p = BitmapFilename; } } strcpy(p,"$CLUSMAP.TMP"); if(_dos_creat(BitmapFilename,_A_NORMAL,BitmapFileHandle)) { _dos_close(*OutputFileHandle); unlink(OutputFilename); fprintf(stderr,"\n"); fprintf(stderr,textCantCreateFile,BitmapFilename); fprintf(stderr,"\n"); _Log("FAILED creating temporary cluster bitmap file.\n"); return(FALSE); } _Log("Successfully created temporary cluster bitmap file.\n"); return(TRUE); } BOOL DoIt( IN PHANDLES Handles ) { FilesystemType FsType; PARTITION_IMAGE PartitionImage; BOOL b; UINT Written; UINT Read; memset(&PartitionImage,0,sizeof(PARTITION_IMAGE)); PartitionImage.Signature = PARTITION_IMAGE_SIGNATURE; PartitionImage.Size = sizeof(PARTITION_IMAGE); // // Determine file system and initialize various data about the volume. // _Log("Determining Filesystem and Volume Data...\n"); if(!DetermineFsAndInitVolumeData(Handles->SourcePartitionHandle,&FsType,&PartitionImage)) { return(FALSE); } // // Build the cluster bitmap. One bit per cluster, 0=unused, 1=used. // The cluster bitmap goes into a temporary file, since we'll need // to append it to the actual data later. // _Log("Building cluster bitmap...\n"); b = BuildClusterBitmap( FsType, Handles->SourcePartitionHandle, Handles->BitmapFileHandle, &PartitionImage ); if(!b) { return(FALSE); } // // Write the partition image header into the output file. // _Log("Initializing image header...\n"); memset(IoBuffer,0,512); memmove(IoBuffer,&PartitionImage,sizeof(PARTITION_IMAGE)); if(_dos_write(Handles->OutputFileHandle,IoBuffer,512,&Written) || (Written != 512)) { fprintf(stderr,"\n%s\n",textFileWriteError); _Log("Error: Failed writing image header!\n"); return(FALSE); } // // the CRC checking code in the other modules assumes the following is true // when the CRC stored in the image file was computed: // // ((PPARTITION_IMAGE)IoBuffer)->BitmapRelocationStart = 0; // ((PPARTITION_IMAGE)IoBuffer)->BootRelocationStart = 0; // ((PPARTITION_IMAGE)IoBuffer)->Flags = 0; // CheckSum = CRC32Compute( IoBuffer, 512, CheckSum); BytesCRC += 512; // // Transfer the file system reserved area into the output file. // _Log("Transferring fs reserved areas to image file...\n"); b = TransferFsStructsToOutput( Handles->SourcePartitionHandle, Handles->OutputFileHandle, PartitionImage.NonClusterSectors ); if(!b) { _Log("Error: Failed writing fs reserved areas to image file!\n"); return(FALSE); } // // Transfer the used clusters into the output file. // _Log("Transferring used clusters to image file...\n"); b = TransferUsedClustersToOutput(Handles,&PartitionImage); if(!b) { _Log("Error: Failed transferring used clusters to image file...\n"); return(FALSE); } // // Append the cluster map to the output file. // _Log("Transferring cluster map to image file...\n"); b = AppendClusterMapToOutput(Handles,&PartitionImage); if(!b) { _Log("Error: FAILED transferring cluster map to image file...\n"); return(FALSE); } // // Set the checksum. // memset(IoBuffer,0,512); // seek to the start of the file if(DosSeek(Handles->OutputFileHandle,0,DOSSEEK_START) != 0) { fprintf(stderr,"\n%s\n",textFileWriteError); return(FALSE); } // read the header in if(_dos_read(Handles->OutputFileHandle,IoBuffer,512,&Read) || (Read != 512 )) { fprintf(stderr,"\n%s\n",textFileWriteError); return(FALSE); } ((PPARTITION_IMAGE)IoBuffer)->CRC = CheckSum; _Log("Image file (%s) checksum = 0x%08lx\n", OutputFilename, CheckSum); _Log("Total bytes processed = 0x%08lx\n",BytesCRC); // seek to the start of the file if(DosSeek(Handles->OutputFileHandle,0,DOSSEEK_START) != 0) { fprintf(stderr,"\n%s\n",textFileWriteError); _Log("Error: Failed writing image CRC!\n"); return(FALSE); } // write out the modified image header with the CRC if(_dos_write(Handles->OutputFileHandle,IoBuffer,512,&Written) || (Written != 512)) { fprintf(stderr,"\n%s\n",textFileWriteError); _Log("Error: Failed writing image CRC!\n"); return(FALSE); } return b; } BOOL DetermineFsAndInitVolumeData( IN HPARTITION PartitionHandle, OUT FilesystemType *FsType, OUT PPARTITION_IMAGE PartitionImage ) /*++ Routine Description: This routine determines the file system on the source partition (fat, ntfs, or other) and initializes sector and cluster count values in a PARTITION_IMAGE structure based on the volume's characteristics. Arguments: PartitionHandle - supplies handle to the source partition FsType - receives the file system type. PartitionImage - receives volume-related sector and cluster counts. The following members are filled in: TotalSectorCount NonClusterSectors ClusterCount SectorsPerCluster SystemId Return Value: Boolean value indicating outcome. If false, a message will have been printed out explaining why. --*/ { BOOL b; UINT DiskId; ULONG dontcare; if(FatIsFat(PartitionHandle)) { *FsType = FilesystemFat; _Log(" The partition is FAT...\n"); b = FatInitializeVolumeData( PartitionHandle, &PartitionImage->TotalSectorCount, &PartitionImage->NonClusterSectors, &PartitionImage->ClusterCount, &PartitionImage->SectorsPerCluster ); } else { if(NtfsIsNtfs(PartitionHandle)) { *FsType = FilesystemNtfs; _Log(" The partition is NTFS...\n"); b = NtfsInitializeVolumeData( PartitionHandle, &PartitionImage->TotalSectorCount, &PartitionImage->NonClusterSectors, &PartitionImage->ClusterCount, &PartitionImage->SectorsPerCluster ); } else { fprintf(stderr,"\n%s\n",textUnsupportedFs); _Log("Error: The partition type is unknown!\n"); b = FALSE; } } if(b) { // // Get the system id byte // b = GetPartitionInfoByHandle( PartitionHandle, &DiskId, &PartitionImage->SystemId, &dontcare, &dontcare ); if(!b) { fprintf(stderr,"\n%s\n",textPartOpenError); _Log("Error: Failed opening partition!"); } _Log(" System Id Byte = 0x%02x\n",PartitionImage->SystemId); _Log(" Total Sector Count = 0x%08lx\n",PartitionImage->TotalSectorCount); _Log(" Non-Cluster Sectors = 0x%08lx\n",PartitionImage->NonClusterSectors); _Log(" Cluster Count = 0x%08lx\n",PartitionImage->ClusterCount); _Log(" Sectors Per Cluster = 0x%02x\n",PartitionImage->SectorsPerCluster); } return(b); } BOOL TransferFsStructsToOutput( IN HPARTITION PartitionHandle, IN UINT FileHandle, IN ULONG SectorCount ) /*++ Routine Description: This routine transfers each sector that is not part of a cluster to the output file. Such sectors are assumed to start at sector 0 and run contiguously through a sector passed to this routine as a parameter. Arguments: PartitionHandle - supplies handle to partition being imaged. FileHandle - supplies file handle for output. It is assumed that the file position is correct; no seeking is performed prior to data being written to the file. SectorCount - supplies the number of sectors to be transferred from the partition to the output file. Return Value: Boolean value indicating outcome. If FALSE, the user will have been informed. --*/ { BYTE Count; ULONG Sector; ULONG OriginalCount; unsigned Written; Sector = 0L; if(SectorCount) { printf(textTransferringFsStructs,0); printf("\r"); OriginalCount = SectorCount; } else { OriginalCount = 0; } while(SectorCount) { Count = (SectorCount > 63L) ? (BYTE)63 : (BYTE)SectorCount; if(!ReadPartition(PartitionHandle,Sector,Count,IoBuffer)) { fprintf(stderr,"\n"); fprintf(stderr,textReadFailedAtSector,Sector); fprintf(stderr,"\n"); return(FALSE); } // update checksum CheckSum = CRC32Compute( IoBuffer, Count*512, CheckSum); BytesCRC += Count * 512; if(_dos_write(FileHandle,IoBuffer,Count*512,&Written) || (Written != (Count*512U))) { fprintf(stderr,"\n%s\n",textFileWriteError); return(FALSE); } Sector += Count; SectorCount -= Count; printf(textTransferringFsStructs,100 * (OriginalCount - SectorCount) / OriginalCount); printf("\r"); } if(OriginalCount) { printf("\n"); } return(TRUE); } BOOL TransferUsedClustersToOutput( IN HANDLES *Handles, IN PARTITION_IMAGE *PartitionImage ) { ULONG StartCluster,ClusterCount; ULONG StartSector,SectorCount; BYTE Count; UINT Written; BOOL b; ULONG SectorsSoFar; printf(textTransferringClusters,0); printf("\r"); SectorsSoFar = 0; // // Reserve the last sector of the io buffer for the cluster map // if(!InitClusterMap((FPBYTE)IoBuffer+(62*512),Handles->BitmapFileHandle,PartitionImage->LastUsedCluster)) { return(FALSE); } b = TRUE; while(b && (b=GetNextClusterRun(&StartCluster,&ClusterCount)) && ClusterCount) { StartSector = PartitionImage->NonClusterSectors + (StartCluster * PartitionImage->SectorsPerCluster); SectorCount = ClusterCount * PartitionImage->SectorsPerCluster; while(b && SectorCount) { Count = (SectorCount > 62L) ? (BYTE)62 : (BYTE)SectorCount; if(b = ReadPartition(Handles->SourcePartitionHandle,StartSector,Count,IoBuffer)) { if(!_dos_write(Handles->OutputFileHandle,IoBuffer,Count*512,&Written) && (Written == (Count*512U))) { SectorCount -= Count; StartSector += Count; SectorsSoFar += Count; printf( textTransferringClusters, (100*SectorsSoFar) / (PartitionImage->UsedClusterCount*PartitionImage->SectorsPerCluster) ); printf("\r"); // update checksum CheckSum = CRC32Compute( IoBuffer, Count*512, CheckSum); BytesCRC += Count * 512; } else { fprintf(stderr,"\n%s\n",textFileWriteError); b = FALSE; } } else { fprintf(stderr,"\n"); fprintf(stderr,textReadFailedAtSector,StartSector); fprintf(stderr,"\n"); } } } if(b) { printf("\n"); } return(b); } BOOL AppendClusterMapToOutput( IN HANDLES *Handles, IN PARTITION_IMAGE *PartitionImage ) /*++ Routine Description: This routine appends the cluster bitmap file to the end of the partition image output file. Arguments: Handles - supplies a pointer to the structure that contains the 2 output file handles. This routine takes care of seeking, rewinding, etc, so no assumptions are made about the handles' state. PartitionImage - supplies information about the partition being imaged and partition image file being created. Return Value: Boolean value indicating outcome. If FALSE, the user will have been informed. --*/ { ULONG BitmapSizeBytes; UINT Read,Written; ULONG BytesLeft; UINT Size; printf(textCombiningFiles,0); printf("\r"); // // Figure out how large the bitmap actually is in bytes, // rounding up to a sector boundary. // BitmapSizeBytes = ((PartitionImage->LastUsedCluster/(8*512)) + 1) * 512; // // Seek to the end of the output file and the start of the // cluster bitmap file, just in case. // DosSeek(Handles->OutputFileHandle,0,DOSSEEK_END); DosSeek(Handles->BitmapFileHandle,0,DOSSEEK_START); // // Transfer data. // BytesLeft = BitmapSizeBytes; while(BytesLeft) { Size = (BytesLeft > (63*512)) ? 63*512 : (UINT)BytesLeft; if(_dos_read(Handles->BitmapFileHandle,IoBuffer,Size,&Read) || (Read != Size)) { fprintf(stderr,"\n%s\n",textFileReadFailed); return(FALSE); } CheckSum = CRC32Compute( IoBuffer, Size, CheckSum); BytesCRC += Size; if(_dos_write(Handles->OutputFileHandle,IoBuffer,Size,&Written) || (Written != Size)) { fprintf(stderr,"\n%s\n",textFileWriteError); return(FALSE); } BytesLeft -= Size; printf(textCombiningFiles,100 * (BitmapSizeBytes - BytesLeft) / BitmapSizeBytes); printf("\r"); } printf("\n"); return(TRUE); }