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.

863 lines
20 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <windows.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <ntddvol.h>
  8. #include <ntdddisk.h>
  9. #include "identify.h"
  10. #define SECTOR_SIZE 512
  11. #define SECTOR_MASK (SECTOR_SIZE - 1)
  12. #define MAX_NUM_CHS_ADDRESSABLE_SECTORS 16514064
  13. //
  14. // Perform the copy using 8 outstanding I/Os of 128k each
  15. //
  16. #define COPYBUF_SIZE (128 * 1024)
  17. #define COPYBUF_COUNT 8
  18. //
  19. // A failed assert will abort the process
  20. //
  21. #define assert(x) if (!(x)) { printf("Assert failed: %s\n", #x); exit(-1); }
  22. typedef struct _COPYBUF {
  23. OVERLAPPED Overlapped;
  24. ULONG State;
  25. ULONG Bytes;
  26. PVOID Buffer;
  27. } COPYBUF, *PCOPYBUF;
  28. //
  29. // Three possible states for a copybuf
  30. //
  31. #define CB_FREE 0x0
  32. #define CB_READ 0x1
  33. #define CB_WRITE 0x2
  34. //
  35. // CUSTOM_IDENTIFY_DATA consists of an IDENTIFY_DATA structure,
  36. // along with three fields in which to pass along the "BIOS" disk
  37. // geometry to the SIMICS simulator.
  38. //
  39. #pragma pack(push,1)
  40. typedef union _CUSTOM_IDENTIFY_DATA {
  41. IDENTIFY_DATA IdentifyData;
  42. struct {
  43. USHORT Reserved[128];
  44. ULONG Cylinders;
  45. USHORT Heads;
  46. USHORT SectorsPerTrack;
  47. } BiosData;
  48. } CUSTOM_IDENTIFY_DATA, *PCUSTOM_IDENTIFY_DATA;
  49. #pragma pack(pop)
  50. BOOLEAN
  51. DisplayDiskGeometry(
  52. IN HANDLE handle
  53. );
  54. VOID
  55. DoWrite (
  56. IN PVOID Buffer,
  57. IN ULONG Length,
  58. IN ULONG64 Offset,
  59. IN PCOPYBUF CopyBuf
  60. );
  61. BOOLEAN
  62. GetIdentifyData(
  63. IN HANDLE Handle,
  64. OUT PIDENTIFY_DATA IdentifyData
  65. );
  66. BOOLEAN
  67. GetVolumeInfo (
  68. IN PCHAR DrivePath,
  69. OUT PULONG DriveNumber,
  70. OUT PULONG PartitionNumber,
  71. OUT PULONG64 StartingOffset,
  72. OUT PULONG64 ExtentLength
  73. );
  74. VOID
  75. InitializeCopyBuffers (
  76. VOID
  77. );
  78. VOID
  79. MassageIdentifyData(
  80. VOID
  81. );
  82. VOID
  83. ProcessCompletedCopy (
  84. PCOPYBUF CopyBuf
  85. );
  86. BOOL
  87. ScanCopyBuffers (
  88. VOID
  89. );
  90. VOID
  91. StartRead (
  92. IN OUT PCOPYBUF CopyBuf
  93. );
  94. VOID
  95. StartWrite (
  96. IN OUT PCOPYBUF CopyBuf
  97. );
  98. //
  99. // Global data declarations follow
  100. //
  101. COPYBUF CopyBufArray[COPYBUF_COUNT];
  102. //
  103. // Identifies the PhysicalDrive.
  104. //
  105. INT gDeviceNumber;
  106. //
  107. // Identifies the position of the drive on the controller
  108. // ie. master == 0, slave == 1.
  109. //
  110. UCHAR gDriveNumber = 1;
  111. HANDLE DriveHandle;
  112. HANDLE FileHandle;
  113. //
  114. // CopyOffset is the byte offset between data on the source disk image and
  115. // corresponding data in the output file. This is used to account for the
  116. // sector-sized prefix in the output file.
  117. //
  118. ULONG CopyOffset;
  119. ULONG64 CurrentOffset;
  120. ULONG64 DriveSize;
  121. ULONG64 MaxSize;
  122. UCHAR PercentComplete;
  123. ULONG OutstandingIo;
  124. IDENTIFY_DATA IdentifyData;
  125. DISK_GEOMETRY DiskGeometry;
  126. //
  127. // Array of event handles, one per copy buffer
  128. //
  129. HANDLE IoEvents[COPYBUF_COUNT];
  130. int
  131. _cdecl main (
  132. int argc,
  133. char *argv[]
  134. )
  135. {
  136. char deviceBuffer[20];
  137. PCHAR outputFileName;
  138. PCHAR drive;
  139. BOOLEAN result;
  140. ULONG64 volumeOffset;
  141. ULONG64 volumeSize;
  142. ULONG partitionNumber;
  143. DWORD waitResult;
  144. PCOPYBUF copyBuf;
  145. ULONG i;
  146. //
  147. // Must be invoked with two arguments
  148. //
  149. if (argc != 3) {
  150. fprintf(stderr,
  151. "Usage: %s <drive:> <OutputFile>\n",
  152. argv[0]);
  153. exit(1);
  154. }
  155. InitializeCopyBuffers();
  156. //
  157. // Extract both arguments
  158. //
  159. drive = argv[1];
  160. outputFileName = argv[2];
  161. result = GetVolumeInfo(drive,
  162. &gDeviceNumber,
  163. &partitionNumber,
  164. &volumeOffset,
  165. &volumeSize);
  166. if (result == FALSE) {
  167. exit(1);
  168. }
  169. //
  170. // Calculate how many sectors need to be in the image
  171. //
  172. MaxSize = (volumeOffset + volumeSize + SECTOR_MASK) / SECTOR_SIZE;
  173. sprintf(deviceBuffer,"\\\\.\\PhysicalDrive%d",
  174. gDeviceNumber);
  175. //
  176. // Open the physical source drive.
  177. //
  178. DriveHandle = CreateFile(deviceBuffer,
  179. GENERIC_READ | GENERIC_WRITE,
  180. FILE_SHARE_READ | FILE_SHARE_WRITE,
  181. NULL,
  182. OPEN_EXISTING,
  183. FILE_ATTRIBUTE_NORMAL |
  184. FILE_FLAG_NO_BUFFERING |
  185. FILE_FLAG_OVERLAPPED,
  186. NULL);
  187. if (INVALID_HANDLE_VALUE == DriveHandle){
  188. printf("Couldn't open: %s. Drive may not exist. ",
  189. deviceBuffer);
  190. return -1;
  191. }
  192. //
  193. // Retrieve and display the BIOS disk geometry
  194. //
  195. result = DisplayDiskGeometry( DriveHandle );
  196. if (result == FALSE) {
  197. printf("Could not retrieve disk geometry\n");
  198. exit(1);
  199. }
  200. //
  201. // Reteive the identify data, if possible. If the data could not be
  202. // retrieved, MassageIdentifyData() will attempt to fabricate the relevant
  203. // portions based on the BIOS disk geometry retrieved previously.
  204. //
  205. GetIdentifyData( DriveHandle,
  206. &IdentifyData );
  207. MassageIdentifyData();
  208. DriveSize = IdentifyData.UserAddressableSectors * (ULONGLONG)512;
  209. if (MaxSize == 0) {
  210. MaxSize = DriveSize;
  211. } else {
  212. MaxSize *= 512;
  213. }
  214. printf("Drive size %dMB\n",(ULONG)(DriveSize / (1024 * 1024)));
  215. printf("Image size %dMB\n",(ULONG)(MaxSize / (1024 * 1024)));
  216. //
  217. // Open the output file
  218. //
  219. FileHandle = CreateFile(outputFileName,
  220. GENERIC_READ | GENERIC_WRITE,
  221. 0,
  222. NULL,
  223. CREATE_ALWAYS,
  224. FILE_ATTRIBUTE_NORMAL |
  225. FILE_FLAG_NO_BUFFERING |
  226. FILE_FLAG_OVERLAPPED,
  227. NULL);
  228. if (FileHandle == INVALID_HANDLE_VALUE) {
  229. printf("Could not create %s\n", outputFileName);
  230. return -1;
  231. }
  232. //
  233. // Write the identify data
  234. //
  235. CopyOffset = 0;
  236. CurrentOffset = 0;
  237. DoWrite(&IdentifyData,
  238. sizeof(IDENTIFY_DATA),
  239. 0,
  240. &CopyBufArray[0]);
  241. //
  242. // Kick off reads on all of the remaining copy buffers
  243. //
  244. CopyOffset = sizeof(IDENTIFY_DATA);
  245. for (i = 1; i < COPYBUF_COUNT; i++) {
  246. StartRead(&CopyBufArray[i]);
  247. }
  248. //
  249. // Loop, processing completed I/O as appropriate. When all
  250. // outstanding io has completed, the copy is complete.
  251. //
  252. do {
  253. waitResult = WaitForMultipleObjects( COPYBUF_COUNT,
  254. IoEvents,
  255. FALSE,
  256. INFINITE );
  257. waitResult -= WAIT_OBJECT_0;
  258. assert(waitResult < COPYBUF_COUNT);
  259. copyBuf = &CopyBufArray[waitResult];
  260. ProcessCompletedCopy(copyBuf);
  261. } while (OutstandingIo > 0);
  262. //
  263. // The copy is finished.
  264. //
  265. printf("%s created\n", outputFileName);
  266. CloseHandle(DriveHandle);
  267. CloseHandle(FileHandle);
  268. return 0;
  269. }
  270. VOID
  271. InitializeCopyBuffers (
  272. VOID
  273. )
  274. {
  275. ULONG bytes;
  276. PCOPYBUF copyBuf;
  277. PCOPYBUF copyBufEnd;
  278. ULONG i;
  279. HANDLE event;
  280. PCHAR copyBuffer;
  281. //
  282. // Make a single, sector-aligned allocation to contain all of the copy
  283. // buffers
  284. //
  285. bytes = COPYBUF_SIZE * COPYBUF_COUNT + SECTOR_MASK;
  286. copyBuffer = malloc(bytes);
  287. if (copyBuffer == NULL) {
  288. printf("Out of memory\n");
  289. exit(-1);
  290. }
  291. copyBuffer =
  292. (PCHAR)(((ULONG_PTR)copyBuffer + SECTOR_MASK) & ~SECTOR_MASK);
  293. //
  294. // Walk the copyBuf array, initializing each to point to it's portion of
  295. // the copy buffer
  296. //
  297. copyBuf = CopyBufArray;
  298. for (i = 0; i < COPYBUF_COUNT; i++) {
  299. copyBuf->State = CB_FREE;
  300. copyBuf->Buffer = copyBuffer;
  301. event = CreateEvent( NULL,
  302. FALSE,
  303. FALSE,
  304. NULL );
  305. assert(event != NULL);
  306. copyBuf->Overlapped.hEvent = event;
  307. IoEvents[i] = event;
  308. copyBuffer += COPYBUF_SIZE;
  309. copyBuf++;
  310. }
  311. }
  312. BOOLEAN
  313. GetVolumeInfo (
  314. IN PCHAR DrivePath,
  315. OUT PULONG DriveNumber,
  316. OUT PULONG PartitionNumber,
  317. OUT PULONG64 StartingOffset,
  318. OUT PULONG64 ExtentLength
  319. )
  320. {
  321. char deviceBuffer[20];
  322. HANDLE volumeHandle;
  323. BOOL result;
  324. STORAGE_DEVICE_NUMBER deviceNumber;
  325. PARTITION_INFORMATION partitionInformation;
  326. ULONG bytesReturned;
  327. //
  328. // Determine which physical drive contains the specified partition by
  329. //
  330. // - Opening the volume
  331. //
  332. // - Sending IOCTL_STORAGE_GET_DEVICE_NUMBER to retrieve the device and
  333. // partition number
  334. //
  335. // - Sending IOCTL_DISK_GET_PARTITION_INFO to retrieve the starting
  336. // offset and length of the volume
  337. //
  338. // - Closing the volume
  339. //
  340. sprintf(deviceBuffer, "\\\\.\\%s", DrivePath);
  341. volumeHandle = CreateFile(deviceBuffer,
  342. GENERIC_READ,
  343. FILE_SHARE_READ | FILE_SHARE_WRITE,
  344. NULL,
  345. OPEN_EXISTING,
  346. FILE_ATTRIBUTE_NORMAL |
  347. FILE_FLAG_NO_BUFFERING |
  348. FILE_FLAG_OVERLAPPED,
  349. NULL);
  350. if (volumeHandle == INVALID_HANDLE_VALUE) {
  351. printf("Error %d opening %s\n", GetLastError(), deviceBuffer);
  352. return FALSE;
  353. }
  354. result = DeviceIoControl(volumeHandle,
  355. IOCTL_STORAGE_GET_DEVICE_NUMBER,
  356. NULL,
  357. 0,
  358. &deviceNumber,
  359. sizeof(deviceNumber),
  360. &bytesReturned,
  361. NULL);
  362. if (result == FALSE) {
  363. printf("Could not get device number for %s\n", deviceBuffer);
  364. CloseHandle(volumeHandle);
  365. return FALSE;
  366. }
  367. if (deviceNumber.DeviceType != FILE_DEVICE_DISK) {
  368. printf("%s is not a disk\n",deviceBuffer);
  369. CloseHandle(volumeHandle);
  370. return FALSE;
  371. }
  372. bytesReturned = 0;
  373. result = DeviceIoControl(volumeHandle,
  374. IOCTL_DISK_GET_PARTITION_INFO,
  375. NULL,
  376. 0,
  377. &partitionInformation,
  378. sizeof(partitionInformation),
  379. &bytesReturned,
  380. NULL);
  381. CloseHandle(volumeHandle);
  382. if (result == FALSE) {
  383. printf("Error %d retrieving partition information for %s\n",
  384. GetLastError(),
  385. deviceBuffer);
  386. return FALSE;
  387. }
  388. //
  389. // All of the information was successfully retrieved. Fill in the
  390. // output parameters and return.
  391. //
  392. *DriveNumber = deviceNumber.DeviceNumber;
  393. *PartitionNumber = deviceNumber.PartitionNumber;
  394. *StartingOffset = partitionInformation.StartingOffset.QuadPart;
  395. *ExtentLength = partitionInformation.PartitionLength.QuadPart;
  396. return TRUE;
  397. }
  398. BOOLEAN
  399. GetIdentifyData(
  400. IN HANDLE Handle,
  401. OUT PIDENTIFY_DATA IdentifyData
  402. )
  403. {
  404. SENDCMDINPARAMS inputParams;
  405. PSENDCMDOUTPARAMS outputParams;
  406. PIDENTIFY_DATA identifyData;
  407. ULONG bytesReturned;
  408. ULONG bufSize;
  409. ZeroMemory(&inputParams, sizeof(SENDCMDINPARAMS));
  410. bufSize = sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE;
  411. bufSize *= 2;
  412. outputParams = (PSENDCMDOUTPARAMS) malloc(bufSize);
  413. if (!outputParams) {
  414. printf("Out of memory\n");
  415. return FALSE;
  416. }
  417. ZeroMemory(outputParams, bufSize);
  418. //
  419. // Build register structure to send SMART command.
  420. //
  421. inputParams.irDriveRegs.bFeaturesReg = 0;
  422. inputParams.irDriveRegs.bSectorCountReg = 1;
  423. inputParams.irDriveRegs.bSectorNumberReg = 1;
  424. inputParams.irDriveRegs.bCylLowReg = 0;
  425. inputParams.irDriveRegs.bCylHighReg = 0;
  426. inputParams.irDriveRegs.bDriveHeadReg = 0xA0 | ((gDriveNumber & 1) << 4);
  427. inputParams.irDriveRegs.bCommandReg = ID_CMD;
  428. bytesReturned = 0;
  429. if (!DeviceIoControl (Handle,
  430. SMART_RCV_DRIVE_DATA,
  431. &inputParams,
  432. sizeof(SENDCMDINPARAMS) - 1,
  433. outputParams,
  434. bufSize,
  435. &bytesReturned,
  436. NULL)) {
  437. printf("IDE_IDENTIFY failed with 0x%x, %d bytes returned\n",
  438. GetLastError(),
  439. bytesReturned);
  440. printf("WARNING: This image file will work with the SIMICS simulator\n"
  441. " but not simnow.\n");
  442. memset(IdentifyData, 0, sizeof(IDENTIFY_DATA));
  443. free(outputParams);
  444. return FALSE;
  445. }
  446. identifyData = (PIDENTIFY_DATA)outputParams->bBuffer;
  447. *IdentifyData = *identifyData;
  448. free(outputParams);
  449. return TRUE;
  450. }
  451. VOID
  452. MassageIdentifyData(
  453. VOID
  454. )
  455. /*++
  456. Routine Description:
  457. This routine sets the bios CHS geometry in the IdentifyData structure
  458. in a place previously agreed upon with Simics.
  459. Arguments:
  460. None.
  461. Return Value:
  462. None.
  463. --*/
  464. {
  465. PCUSTOM_IDENTIFY_DATA custom;
  466. ULONG sectorCount;
  467. USHORT ideCylinders;
  468. USHORT ideHeads;
  469. USHORT ideSectorsPerTrack;
  470. C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumCylinders)/2 == 1);
  471. C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumHeads)/2 == 3);
  472. C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,NumSectorsPerTrack)/2 == 6);
  473. C_ASSERT(FIELD_OFFSET(IDENTIFY_DATA,CurrentSectorCapacity)/2 == 57);
  474. //
  475. // Set the BIOS disk geometry in the new fields that are passed
  476. // along to the SIMICS simulator.
  477. //
  478. custom = (PCUSTOM_IDENTIFY_DATA)&IdentifyData;
  479. custom->BiosData.Cylinders = DiskGeometry.Cylinders.LowPart;
  480. custom->BiosData.Heads = (USHORT)DiskGeometry.TracksPerCylinder;
  481. custom->BiosData.SectorsPerTrack = (USHORT)DiskGeometry.SectorsPerTrack;
  482. if (IdentifyData.NumCylinders == 0) {
  483. //
  484. // The IDENTIFY_DATA ioctl failed (SMART isn't supported), so parts
  485. // of the IDE geometry must be fabricated, including:
  486. //
  487. // - NumCylinders
  488. // - NumHeads
  489. // - NumSectorsPerTrack
  490. // - CurrentSectorCapacity
  491. // - UserAddressableSectors
  492. //
  493. sectorCount = DiskGeometry.Cylinders.LowPart *
  494. DiskGeometry.TracksPerCylinder *
  495. DiskGeometry.SectorsPerTrack;
  496. if (sectorCount > MAX_NUM_CHS_ADDRESSABLE_SECTORS) {
  497. IdentifyData.NumCylinders = 16383;
  498. IdentifyData.NumHeads = 16;
  499. IdentifyData.NumSectorsPerTrack = 63;
  500. } else {
  501. IdentifyData.NumSectorsPerTrack =
  502. (USHORT)DiskGeometry.SectorsPerTrack;
  503. IdentifyData.NumHeads = 16;
  504. IdentifyData.NumCylinders = (USHORT)
  505. (sectorCount / (IdentifyData.NumSectorsPerTrack *
  506. IdentifyData.NumHeads));
  507. }
  508. IdentifyData.CurrentSectorCapacity = sectorCount;
  509. IdentifyData.UserAddressableSectors = sectorCount;
  510. }
  511. printf("IDE disk geometry:\n"
  512. " Cyls %d\n"
  513. " Heads %d\n"
  514. " Sct/Trk %d\n\n"
  515. "BIOS disk geometry:\n"
  516. " Cyls %d\n"
  517. " Heads %d\n"
  518. " Sct/Trk %d\n",
  519. IdentifyData.NumCylinders,
  520. IdentifyData.NumHeads,
  521. IdentifyData.NumSectorsPerTrack,
  522. custom->BiosData.Cylinders,
  523. custom->BiosData.Heads,
  524. custom->BiosData.SectorsPerTrack);
  525. }
  526. VOID
  527. DoWrite (
  528. IN PVOID Buffer,
  529. IN ULONG Length,
  530. IN ULONG64 Offset,
  531. IN PCOPYBUF CopyBuf
  532. )
  533. {
  534. LARGE_INTEGER offset;
  535. BOOL result;
  536. offset.QuadPart = Offset;
  537. CopyBuf->Overlapped.Offset = offset.HighPart;
  538. CopyBuf->Overlapped.OffsetHigh = offset.LowPart;
  539. CopyBuf->State = CB_READ;
  540. memcpy(CopyBuf->Buffer,Buffer,Length);
  541. CopyBuf->Bytes = Length;
  542. StartWrite(CopyBuf);
  543. }
  544. VOID
  545. StartWrite (
  546. IN OUT PCOPYBUF CopyBuf
  547. )
  548. {
  549. LARGE_INTEGER offset;
  550. BOOL result;
  551. ULONG error;
  552. CopyBuf->State = CB_WRITE;
  553. //
  554. // Adjust the offset
  555. //
  556. offset.LowPart = CopyBuf->Overlapped.Offset;
  557. offset.HighPart = CopyBuf->Overlapped.OffsetHigh;
  558. offset.QuadPart += CopyOffset;
  559. CopyBuf->Overlapped.Offset = offset.LowPart;
  560. CopyBuf->Overlapped.OffsetHigh = offset.HighPart;
  561. result = WriteFile( FileHandle,
  562. CopyBuf->Buffer,
  563. CopyBuf->Bytes,
  564. NULL,
  565. &CopyBuf->Overlapped );
  566. if (result == FALSE) {
  567. error = GetLastError();
  568. if (error != ERROR_IO_PENDING &&
  569. error != ERROR_IO_INCOMPLETE) {
  570. printf("Error %d returned from write\n",error);
  571. exit(-1);
  572. }
  573. }
  574. OutstandingIo += 1;
  575. }
  576. VOID
  577. StartRead (
  578. IN OUT PCOPYBUF CopyBuf
  579. )
  580. {
  581. LARGE_INTEGER offset;
  582. BOOL result;
  583. ULONG64 length;
  584. ULONG error;
  585. if (CurrentOffset == MaxSize) {
  586. return;
  587. }
  588. length = MaxSize - CurrentOffset;
  589. if (length > COPYBUF_SIZE) {
  590. length = COPYBUF_SIZE;
  591. }
  592. CopyBuf->State = CB_READ;
  593. offset.QuadPart = CurrentOffset;
  594. CurrentOffset += length;
  595. CopyBuf->Overlapped.Offset = offset.LowPart;
  596. CopyBuf->Overlapped.OffsetHigh = offset.HighPart;
  597. result = ReadFile( DriveHandle,
  598. CopyBuf->Buffer,
  599. (ULONG)length,
  600. NULL,
  601. &CopyBuf->Overlapped );
  602. if (result == FALSE) {
  603. error = GetLastError();
  604. if (error != ERROR_IO_PENDING &&
  605. error != ERROR_IO_INCOMPLETE) {
  606. printf("Error %d returned from read\n",error);
  607. exit(-1);
  608. }
  609. }
  610. OutstandingIo += 1;
  611. }
  612. BOOLEAN
  613. DisplayDiskGeometry(
  614. IN HANDLE handle
  615. )
  616. {
  617. BOOL result;
  618. ULONG bytesReturned;
  619. result = DeviceIoControl(handle,
  620. IOCTL_DISK_GET_DRIVE_GEOMETRY,
  621. NULL,
  622. 0,
  623. &DiskGeometry,
  624. sizeof(DiskGeometry),
  625. &bytesReturned,
  626. NULL);
  627. if (result == FALSE) {
  628. return FALSE;
  629. }
  630. printf("%I64d Cylinders %d Heads %d Sectors/Track\n",
  631. DiskGeometry.Cylinders.QuadPart,
  632. DiskGeometry.TracksPerCylinder,
  633. DiskGeometry.SectorsPerTrack);
  634. return TRUE;
  635. }
  636. VOID
  637. ProcessCompletedCopy (
  638. PCOPYBUF CopyBuf
  639. )
  640. {
  641. UCHAR percent;
  642. HANDLE handle;
  643. BOOL result;
  644. //
  645. // Decrement the outstanding Io count. Successfully starting another
  646. // read or write will increment it again.
  647. //
  648. OutstandingIo -= 1;
  649. //
  650. // We have found a buffer with either a read or a write in progress.
  651. // Retrieve the number of bytes transferred.
  652. //
  653. if (CopyBuf->State == CB_READ) {
  654. handle = DriveHandle;
  655. } else {
  656. handle = FileHandle;
  657. }
  658. result = GetOverlappedResult( handle,
  659. &CopyBuf->Overlapped,
  660. &CopyBuf->Bytes,
  661. FALSE );
  662. assert(result != FALSE);
  663. if (CopyBuf->State == CB_READ) {
  664. //
  665. // This buffer contains data read from the drive, kick off a write
  666. // to the output file.
  667. //
  668. StartWrite(CopyBuf);
  669. } else {
  670. //
  671. // This buffer represents data that has been written to the drive.
  672. // Use it to start another read.
  673. //
  674. percent = (UCHAR)((CurrentOffset * 100) / MaxSize);
  675. if (percent != PercentComplete) {
  676. printf("%d%%\r",percent);
  677. PercentComplete = percent;
  678. }
  679. StartRead(CopyBuf);
  680. }
  681. }