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

838 lines
22 KiB
C

#include "imagpart.h"
#include <malloc.h>
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);
}