Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

837 lines
22 KiB

  1. #include "imagpart.h"
  2. #include <malloc.h>
  3. typedef struct _HANDLES {
  4. HPARTITION SourcePartitionHandle;
  5. UINT OutputFileHandle,BitmapFileHandle;
  6. } HANDLES, *PHANDLES;
  7. //
  8. // Global data
  9. //
  10. CMD_LINE_ARGS CmdLineArgs;
  11. FPVOID IoBuffer,OriginalIoBuffer;
  12. char OutputFilename[256];
  13. char BitmapFilename[256];
  14. ULONG CheckSum;
  15. ULONG BytesCRC;
  16. //
  17. // Text for the program
  18. //
  19. char *textNoPartitions;
  20. char *textSourcePrompt;
  21. char *textTransferringFsStructs;
  22. char *textPartOpenError;
  23. char *textFileReadFailed;
  24. char *textTransferringClusters;
  25. char *textCombiningFiles;
  26. char *textReadFailedAtSector;
  27. char *textOOM;
  28. char *textDone;
  29. char *textFileWriteError;
  30. char *textCantCreateNewFile;
  31. char *textSelectOutput;
  32. char *textDisk;
  33. char *textPaddedMbCount;
  34. char *textInvalidSelection;
  35. char *textUsage;
  36. char *textCantCreateFile;
  37. char *textScanningFat;
  38. char *textNtfsUnsupportedConfig;
  39. char *textNtfsCorrupt;
  40. char *textInitNtfsDataStruct;
  41. char *textNtfsBuildingBitmap;
  42. char *textProcessingNtfsBitmap;
  43. char *textUnsupportedFs;
  44. char *textChecksum;
  45. char *textBytesProcessed;
  46. MESSAGE_STRING TextMessages[] = { { &textNoPartitions,1 },
  47. { &textSourcePrompt,2 },
  48. { &textTransferringFsStructs,3 },
  49. { &textPartOpenError,4 },
  50. { &textFileReadFailed,5 },
  51. { &textTransferringClusters,6 },
  52. { &textCombiningFiles,7 },
  53. { &textReadFailedAtSector,8 },
  54. { &textOOM,9 },
  55. { &textDone,11 },
  56. { &textFileWriteError,12 },
  57. { &textCantCreateNewFile,17 },
  58. { &textSelectOutput,18 },
  59. { &textDisk,20 },
  60. { &textPaddedMbCount,21 },
  61. { &textInvalidSelection,22 },
  62. { &textUsage,25 },
  63. { &textCantCreateFile,26 },
  64. { &textScanningFat,27 },
  65. { &textNtfsUnsupportedConfig,29 },
  66. { &textNtfsCorrupt,30 },
  67. { &textInitNtfsDataStruct,31 },
  68. { &textNtfsBuildingBitmap,32 },
  69. { &textProcessingNtfsBitmap,33 },
  70. { &textUnsupportedFs,34 },
  71. { &textChecksum,35 },
  72. { &textBytesProcessed,36 }
  73. };
  74. BOOL
  75. DoIt(
  76. IN PHANDLES Handles
  77. );
  78. BOOL
  79. DetermineAndOpenSource(
  80. IN UINT PartitionCount,
  81. OUT HPARTITION *SourcePartitionHandle
  82. );
  83. BOOL
  84. DetermineAndCreateOutput(
  85. OUT UINT *OutputFileHandle,
  86. OUT UINT *BitmapFileHandle
  87. );
  88. BOOL
  89. DetermineFsAndInitVolumeData(
  90. IN HPARTITION PartitionHandle,
  91. OUT FilesystemType *FsType,
  92. OUT PPARTITION_IMAGE PartitionImage
  93. );
  94. BOOL
  95. TransferFsStructsToOutput(
  96. IN HPARTITION PartitionHandle,
  97. IN UINT FileHandle,
  98. IN ULONG SectorCount
  99. );
  100. BOOL
  101. TransferUsedClustersToOutput(
  102. IN HANDLES *Handles,
  103. IN PARTITION_IMAGE *PartitionImage
  104. );
  105. BOOL
  106. AppendClusterMapToOutput(
  107. IN HANDLES *Handles,
  108. IN PARTITION_IMAGE *PartitionImage
  109. );
  110. int
  111. main(
  112. IN int argc,
  113. IN char *argv[]
  114. )
  115. {
  116. UINT PartCount;
  117. BOOL b;
  118. HANDLES Handles;
  119. if(!GetTextForProgram(argv[0],TextMessages,sizeof(TextMessages)/sizeof(TextMessages[0]))) {
  120. fprintf(stderr,"Unable to find messages for program\n");
  121. return(FAILURE);
  122. }
  123. if(!ParseArgs(argc,argv,TRUE,"DIQTXY",&CmdLineArgs)) {
  124. fprintf(stderr,textUsage);
  125. fprintf(stderr,"\n");
  126. return(FAILURE);
  127. }
  128. _Log("Begin imagpart\n");
  129. //
  130. // Allocate a maximally sized i/o buffer right up front.
  131. //
  132. _Log("Allocating Track Buffer.\n");
  133. if(!AllocTrackBuffer(63,&IoBuffer,&OriginalIoBuffer)) {
  134. fprintf(stderr,"%s\n",textOOM);
  135. b = FALSE;
  136. goto c0;
  137. }
  138. _Log("Scanning partition tables...\n");
  139. PartCount = InitializePartitionList();
  140. if(!PartCount) {
  141. fprintf(stderr,"%s\n",textNoPartitions);
  142. b = FALSE;
  143. goto c1;
  144. }
  145. //
  146. // Get the source partition and target filename.
  147. // The source partition is opened and the target filename is created.
  148. //
  149. _Log("Determining target partition...\n");
  150. b = DetermineAndOpenSource(PartCount,&Handles.SourcePartitionHandle);
  151. if(!b) {
  152. goto c1;
  153. }
  154. printf("\n\n");
  155. _Log("Determining output file...\n");
  156. b = DetermineAndCreateOutput(&Handles.OutputFileHandle,&Handles.BitmapFileHandle);
  157. if(!b) {
  158. goto c2;
  159. }
  160. CheckSum = CRC32_INITIAL_VALUE;
  161. BytesCRC = 0;
  162. _Log("Starting image transfer...\n");
  163. b = DoIt(&Handles);
  164. printf(textChecksum, OutputFilename, CheckSum);
  165. printf("\n");
  166. printf(textBytesProcessed, BytesCRC);
  167. _dos_close(Handles.OutputFileHandle);
  168. _dos_close(Handles.BitmapFileHandle);
  169. FlushDisks();
  170. if(!CmdLineArgs.Test) {
  171. unlink(BitmapFilename);
  172. if(!b) {
  173. unlink(OutputFilename);
  174. }
  175. }
  176. c2:
  177. ClosePartition(Handles.SourcePartitionHandle);
  178. c1:
  179. free(OriginalIoBuffer);
  180. c0:
  181. if(b) {
  182. printf("\n%s\n",textDone);
  183. }
  184. _Log("Done.\n");
  185. return(b ? SUCCESS : FAILURE);
  186. }
  187. BOOL
  188. DetermineAndOpenSource(
  189. IN UINT PartitionCount,
  190. OUT HPARTITION *SourcePartitionHandle
  191. )
  192. /*++
  193. Routine Description:
  194. This routine prompts the user to select a partition to be imaged,
  195. from the list of all partitions that are visible on all drives.
  196. The selected partition is opened for the caller.
  197. Arguments:
  198. PartitionCount - supplies the number of partitions on all drives,
  199. as returned by InitializePartitionList().
  200. SourcePartitionHandle - in the success case, receives the handle
  201. to the selected partition.
  202. Return Value:
  203. Boolean value indicating outcome. If false, a message will have been
  204. printed explaining why.
  205. --*/
  206. {
  207. UINT Selection;
  208. if(CmdLineArgs.Quiet) {
  209. Selection = 0;
  210. } else {
  211. Selection = SelectPartition(
  212. PartitionCount,
  213. textSourcePrompt,
  214. NULL,
  215. textDisk,
  216. textPaddedMbCount,
  217. textInvalidSelection
  218. );
  219. }
  220. *SourcePartitionHandle = OpenPartition(Selection);
  221. if(!(*SourcePartitionHandle)) {
  222. fprintf(stderr,"%s\n",textPartOpenError);
  223. return(FALSE);
  224. }
  225. _Log("Partition opened sucessfully.\n");
  226. return(TRUE);
  227. }
  228. BOOL
  229. DetermineAndCreateOutput(
  230. OUT UINT *OutputFileHandle,
  231. OUT UINT *BitmapFileHandle
  232. )
  233. /*++
  234. Routine Description:
  235. This routine prompts the user to enter a filename that will contain
  236. the output partition image. The file is created (it must not already
  237. exist). In addition a temporary file named $CLUSMAP.TMP will be
  238. created in the same directory. Any file with that name is overwritten.
  239. Arguments:
  240. OutputFileHandle - in the success case, receives a handle
  241. to the created output file, which will be 0-length.
  242. BitmapFileHandle - in the success case, receives a handle to the
  243. created $CLUMAP.TMP file, which will be 0-length.
  244. Return Value:
  245. Boolean value indicating outcome. If false, a message will have been
  246. printed explaining why.
  247. --*/
  248. {
  249. char *p;
  250. if(CmdLineArgs.ImageFile) {
  251. printf("%s %s\n",textSelectOutput,CmdLineArgs.ImageFile);
  252. strcpy(OutputFilename,CmdLineArgs.ImageFile);
  253. goto gotit;
  254. }
  255. //
  256. // Prompt the user for a filename.
  257. //
  258. prompt:
  259. printf("%s ",textSelectOutput);
  260. gets(OutputFilename);
  261. //
  262. // Attempt to create the file
  263. //
  264. gotit:
  265. if(_dos_creatnew(OutputFilename,_A_NORMAL,OutputFileHandle)) {
  266. fprintf(stderr,"\n");
  267. fprintf(stderr,textCantCreateNewFile,OutputFilename);
  268. fprintf(stderr,"\n");
  269. goto prompt;
  270. }
  271. _Log("Output file name is %s\n",OutputFilename);
  272. _Log("Successfully created output file.\n");
  273. //
  274. // Form the name of the temp file for the cluster bitmap
  275. //
  276. strcpy(BitmapFilename,OutputFilename);
  277. if(p = strrchr(BitmapFilename,'\\')) {
  278. p++;
  279. } else {
  280. if(BitmapFilename[1] == ':') {
  281. p = BitmapFilename+2;
  282. } else {
  283. p = BitmapFilename;
  284. }
  285. }
  286. strcpy(p,"$CLUSMAP.TMP");
  287. if(_dos_creat(BitmapFilename,_A_NORMAL,BitmapFileHandle)) {
  288. _dos_close(*OutputFileHandle);
  289. unlink(OutputFilename);
  290. fprintf(stderr,"\n");
  291. fprintf(stderr,textCantCreateFile,BitmapFilename);
  292. fprintf(stderr,"\n");
  293. _Log("FAILED creating temporary cluster bitmap file.\n");
  294. return(FALSE);
  295. }
  296. _Log("Successfully created temporary cluster bitmap file.\n");
  297. return(TRUE);
  298. }
  299. BOOL
  300. DoIt(
  301. IN PHANDLES Handles
  302. )
  303. {
  304. FilesystemType FsType;
  305. PARTITION_IMAGE PartitionImage;
  306. BOOL b;
  307. UINT Written;
  308. UINT Read;
  309. memset(&PartitionImage,0,sizeof(PARTITION_IMAGE));
  310. PartitionImage.Signature = PARTITION_IMAGE_SIGNATURE;
  311. PartitionImage.Size = sizeof(PARTITION_IMAGE);
  312. //
  313. // Determine file system and initialize various data about the volume.
  314. //
  315. _Log("Determining Filesystem and Volume Data...\n");
  316. if(!DetermineFsAndInitVolumeData(Handles->SourcePartitionHandle,&FsType,&PartitionImage)) {
  317. return(FALSE);
  318. }
  319. //
  320. // Build the cluster bitmap. One bit per cluster, 0=unused, 1=used.
  321. // The cluster bitmap goes into a temporary file, since we'll need
  322. // to append it to the actual data later.
  323. //
  324. _Log("Building cluster bitmap...\n");
  325. b = BuildClusterBitmap(
  326. FsType,
  327. Handles->SourcePartitionHandle,
  328. Handles->BitmapFileHandle,
  329. &PartitionImage
  330. );
  331. if(!b) {
  332. return(FALSE);
  333. }
  334. //
  335. // Write the partition image header into the output file.
  336. //
  337. _Log("Initializing image header...\n");
  338. memset(IoBuffer,0,512);
  339. memmove(IoBuffer,&PartitionImage,sizeof(PARTITION_IMAGE));
  340. if(_dos_write(Handles->OutputFileHandle,IoBuffer,512,&Written) || (Written != 512)) {
  341. fprintf(stderr,"\n%s\n",textFileWriteError);
  342. _Log("Error: Failed writing image header!\n");
  343. return(FALSE);
  344. }
  345. //
  346. // the CRC checking code in the other modules assumes the following is true
  347. // when the CRC stored in the image file was computed:
  348. //
  349. // ((PPARTITION_IMAGE)IoBuffer)->BitmapRelocationStart = 0;
  350. // ((PPARTITION_IMAGE)IoBuffer)->BootRelocationStart = 0;
  351. // ((PPARTITION_IMAGE)IoBuffer)->Flags = 0;
  352. //
  353. CheckSum = CRC32Compute( IoBuffer, 512, CheckSum);
  354. BytesCRC += 512;
  355. //
  356. // Transfer the file system reserved area into the output file.
  357. //
  358. _Log("Transferring fs reserved areas to image file...\n");
  359. b = TransferFsStructsToOutput(
  360. Handles->SourcePartitionHandle,
  361. Handles->OutputFileHandle,
  362. PartitionImage.NonClusterSectors
  363. );
  364. if(!b) {
  365. _Log("Error: Failed writing fs reserved areas to image file!\n");
  366. return(FALSE);
  367. }
  368. //
  369. // Transfer the used clusters into the output file.
  370. //
  371. _Log("Transferring used clusters to image file...\n");
  372. b = TransferUsedClustersToOutput(Handles,&PartitionImage);
  373. if(!b) {
  374. _Log("Error: Failed transferring used clusters to image file...\n");
  375. return(FALSE);
  376. }
  377. //
  378. // Append the cluster map to the output file.
  379. //
  380. _Log("Transferring cluster map to image file...\n");
  381. b = AppendClusterMapToOutput(Handles,&PartitionImage);
  382. if(!b) {
  383. _Log("Error: FAILED transferring cluster map to image file...\n");
  384. return(FALSE);
  385. }
  386. //
  387. // Set the checksum.
  388. //
  389. memset(IoBuffer,0,512);
  390. // seek to the start of the file
  391. if(DosSeek(Handles->OutputFileHandle,0,DOSSEEK_START) != 0) {
  392. fprintf(stderr,"\n%s\n",textFileWriteError);
  393. return(FALSE);
  394. }
  395. // read the header in
  396. if(_dos_read(Handles->OutputFileHandle,IoBuffer,512,&Read) || (Read != 512 )) {
  397. fprintf(stderr,"\n%s\n",textFileWriteError);
  398. return(FALSE);
  399. }
  400. ((PPARTITION_IMAGE)IoBuffer)->CRC = CheckSum;
  401. _Log("Image file (%s) checksum = 0x%08lx\n", OutputFilename, CheckSum);
  402. _Log("Total bytes processed = 0x%08lx\n",BytesCRC);
  403. // seek to the start of the file
  404. if(DosSeek(Handles->OutputFileHandle,0,DOSSEEK_START) != 0) {
  405. fprintf(stderr,"\n%s\n",textFileWriteError);
  406. _Log("Error: Failed writing image CRC!\n");
  407. return(FALSE);
  408. }
  409. // write out the modified image header with the CRC
  410. if(_dos_write(Handles->OutputFileHandle,IoBuffer,512,&Written) || (Written != 512)) {
  411. fprintf(stderr,"\n%s\n",textFileWriteError);
  412. _Log("Error: Failed writing image CRC!\n");
  413. return(FALSE);
  414. }
  415. return b;
  416. }
  417. BOOL
  418. DetermineFsAndInitVolumeData(
  419. IN HPARTITION PartitionHandle,
  420. OUT FilesystemType *FsType,
  421. OUT PPARTITION_IMAGE PartitionImage
  422. )
  423. /*++
  424. Routine Description:
  425. This routine determines the file system on the source partition
  426. (fat, ntfs, or other) and initializes sector and cluster count
  427. values in a PARTITION_IMAGE structure based on the volume's
  428. characteristics.
  429. Arguments:
  430. PartitionHandle - supplies handle to the source partition
  431. FsType - receives the file system type.
  432. PartitionImage - receives volume-related sector and cluster counts.
  433. The following members are filled in:
  434. TotalSectorCount
  435. NonClusterSectors
  436. ClusterCount
  437. SectorsPerCluster
  438. SystemId
  439. Return Value:
  440. Boolean value indicating outcome. If false, a message will have been
  441. printed out explaining why.
  442. --*/
  443. {
  444. BOOL b;
  445. UINT DiskId;
  446. ULONG dontcare;
  447. if(FatIsFat(PartitionHandle)) {
  448. *FsType = FilesystemFat;
  449. _Log(" The partition is FAT...\n");
  450. b = FatInitializeVolumeData(
  451. PartitionHandle,
  452. &PartitionImage->TotalSectorCount,
  453. &PartitionImage->NonClusterSectors,
  454. &PartitionImage->ClusterCount,
  455. &PartitionImage->SectorsPerCluster
  456. );
  457. } else {
  458. if(NtfsIsNtfs(PartitionHandle)) {
  459. *FsType = FilesystemNtfs;
  460. _Log(" The partition is NTFS...\n");
  461. b = NtfsInitializeVolumeData(
  462. PartitionHandle,
  463. &PartitionImage->TotalSectorCount,
  464. &PartitionImage->NonClusterSectors,
  465. &PartitionImage->ClusterCount,
  466. &PartitionImage->SectorsPerCluster
  467. );
  468. } else {
  469. fprintf(stderr,"\n%s\n",textUnsupportedFs);
  470. _Log("Error: The partition type is unknown!\n");
  471. b = FALSE;
  472. }
  473. }
  474. if(b) {
  475. //
  476. // Get the system id byte
  477. //
  478. b = GetPartitionInfoByHandle(
  479. PartitionHandle,
  480. &DiskId,
  481. &PartitionImage->SystemId,
  482. &dontcare,
  483. &dontcare
  484. );
  485. if(!b) {
  486. fprintf(stderr,"\n%s\n",textPartOpenError);
  487. _Log("Error: Failed opening partition!");
  488. }
  489. _Log(" System Id Byte = 0x%02x\n",PartitionImage->SystemId);
  490. _Log(" Total Sector Count = 0x%08lx\n",PartitionImage->TotalSectorCount);
  491. _Log(" Non-Cluster Sectors = 0x%08lx\n",PartitionImage->NonClusterSectors);
  492. _Log(" Cluster Count = 0x%08lx\n",PartitionImage->ClusterCount);
  493. _Log(" Sectors Per Cluster = 0x%02x\n",PartitionImage->SectorsPerCluster);
  494. }
  495. return(b);
  496. }
  497. BOOL
  498. TransferFsStructsToOutput(
  499. IN HPARTITION PartitionHandle,
  500. IN UINT FileHandle,
  501. IN ULONG SectorCount
  502. )
  503. /*++
  504. Routine Description:
  505. This routine transfers each sector that is not part of a cluster
  506. to the output file. Such sectors are assumed to start at sector 0
  507. and run contiguously through a sector passed to this routine as
  508. a parameter.
  509. Arguments:
  510. PartitionHandle - supplies handle to partition being imaged.
  511. FileHandle - supplies file handle for output. It is assumed that
  512. the file position is correct; no seeking is performed prior
  513. to data being written to the file.
  514. SectorCount - supplies the number of sectors to be transferred
  515. from the partition to the output file.
  516. Return Value:
  517. Boolean value indicating outcome. If FALSE, the user will have been
  518. informed.
  519. --*/
  520. {
  521. BYTE Count;
  522. ULONG Sector;
  523. ULONG OriginalCount;
  524. unsigned Written;
  525. Sector = 0L;
  526. if(SectorCount) {
  527. printf(textTransferringFsStructs,0);
  528. printf("\r");
  529. OriginalCount = SectorCount;
  530. } else {
  531. OriginalCount = 0;
  532. }
  533. while(SectorCount) {
  534. Count = (SectorCount > 63L) ? (BYTE)63 : (BYTE)SectorCount;
  535. if(!ReadPartition(PartitionHandle,Sector,Count,IoBuffer)) {
  536. fprintf(stderr,"\n");
  537. fprintf(stderr,textReadFailedAtSector,Sector);
  538. fprintf(stderr,"\n");
  539. return(FALSE);
  540. }
  541. // update checksum
  542. CheckSum = CRC32Compute( IoBuffer, Count*512, CheckSum);
  543. BytesCRC += Count * 512;
  544. if(_dos_write(FileHandle,IoBuffer,Count*512,&Written) || (Written != (Count*512U))) {
  545. fprintf(stderr,"\n%s\n",textFileWriteError);
  546. return(FALSE);
  547. }
  548. Sector += Count;
  549. SectorCount -= Count;
  550. printf(textTransferringFsStructs,100 * (OriginalCount - SectorCount) / OriginalCount);
  551. printf("\r");
  552. }
  553. if(OriginalCount) {
  554. printf("\n");
  555. }
  556. return(TRUE);
  557. }
  558. BOOL
  559. TransferUsedClustersToOutput(
  560. IN HANDLES *Handles,
  561. IN PARTITION_IMAGE *PartitionImage
  562. )
  563. {
  564. ULONG StartCluster,ClusterCount;
  565. ULONG StartSector,SectorCount;
  566. BYTE Count;
  567. UINT Written;
  568. BOOL b;
  569. ULONG SectorsSoFar;
  570. printf(textTransferringClusters,0);
  571. printf("\r");
  572. SectorsSoFar = 0;
  573. //
  574. // Reserve the last sector of the io buffer for the cluster map
  575. //
  576. if(!InitClusterMap((FPBYTE)IoBuffer+(62*512),Handles->BitmapFileHandle,PartitionImage->LastUsedCluster)) {
  577. return(FALSE);
  578. }
  579. b = TRUE;
  580. while(b && (b=GetNextClusterRun(&StartCluster,&ClusterCount)) && ClusterCount) {
  581. StartSector = PartitionImage->NonClusterSectors
  582. + (StartCluster * PartitionImage->SectorsPerCluster);
  583. SectorCount = ClusterCount * PartitionImage->SectorsPerCluster;
  584. while(b && SectorCount) {
  585. Count = (SectorCount > 62L) ? (BYTE)62 : (BYTE)SectorCount;
  586. if(b = ReadPartition(Handles->SourcePartitionHandle,StartSector,Count,IoBuffer)) {
  587. if(!_dos_write(Handles->OutputFileHandle,IoBuffer,Count*512,&Written) && (Written == (Count*512U))) {
  588. SectorCount -= Count;
  589. StartSector += Count;
  590. SectorsSoFar += Count;
  591. printf(
  592. textTransferringClusters,
  593. (100*SectorsSoFar) / (PartitionImage->UsedClusterCount*PartitionImage->SectorsPerCluster)
  594. );
  595. printf("\r");
  596. // update checksum
  597. CheckSum = CRC32Compute( IoBuffer, Count*512, CheckSum);
  598. BytesCRC += Count * 512;
  599. } else {
  600. fprintf(stderr,"\n%s\n",textFileWriteError);
  601. b = FALSE;
  602. }
  603. } else {
  604. fprintf(stderr,"\n");
  605. fprintf(stderr,textReadFailedAtSector,StartSector);
  606. fprintf(stderr,"\n");
  607. }
  608. }
  609. }
  610. if(b) {
  611. printf("\n");
  612. }
  613. return(b);
  614. }
  615. BOOL
  616. AppendClusterMapToOutput(
  617. IN HANDLES *Handles,
  618. IN PARTITION_IMAGE *PartitionImage
  619. )
  620. /*++
  621. Routine Description:
  622. This routine appends the cluster bitmap file to the end of the
  623. partition image output file.
  624. Arguments:
  625. Handles - supplies a pointer to the structure that contains the
  626. 2 output file handles. This routine takes care of seeking, rewinding,
  627. etc, so no assumptions are made about the handles' state.
  628. PartitionImage - supplies information about the partition being imaged
  629. and partition image file being created.
  630. Return Value:
  631. Boolean value indicating outcome. If FALSE, the user will have been
  632. informed.
  633. --*/
  634. {
  635. ULONG BitmapSizeBytes;
  636. UINT Read,Written;
  637. ULONG BytesLeft;
  638. UINT Size;
  639. printf(textCombiningFiles,0);
  640. printf("\r");
  641. //
  642. // Figure out how large the bitmap actually is in bytes,
  643. // rounding up to a sector boundary.
  644. //
  645. BitmapSizeBytes = ((PartitionImage->LastUsedCluster/(8*512)) + 1) * 512;
  646. //
  647. // Seek to the end of the output file and the start of the
  648. // cluster bitmap file, just in case.
  649. //
  650. DosSeek(Handles->OutputFileHandle,0,DOSSEEK_END);
  651. DosSeek(Handles->BitmapFileHandle,0,DOSSEEK_START);
  652. //
  653. // Transfer data.
  654. //
  655. BytesLeft = BitmapSizeBytes;
  656. while(BytesLeft) {
  657. Size = (BytesLeft > (63*512)) ? 63*512 : (UINT)BytesLeft;
  658. if(_dos_read(Handles->BitmapFileHandle,IoBuffer,Size,&Read) || (Read != Size)) {
  659. fprintf(stderr,"\n%s\n",textFileReadFailed);
  660. return(FALSE);
  661. }
  662. CheckSum = CRC32Compute( IoBuffer, Size, CheckSum);
  663. BytesCRC += Size;
  664. if(_dos_write(Handles->OutputFileHandle,IoBuffer,Size,&Written) || (Written != Size)) {
  665. fprintf(stderr,"\n%s\n",textFileWriteError);
  666. return(FALSE);
  667. }
  668. BytesLeft -= Size;
  669. printf(textCombiningFiles,100 * (BitmapSizeBytes - BytesLeft) / BitmapSizeBytes);
  670. printf("\r");
  671. }
  672. printf("\n");
  673. return(TRUE);
  674. }