From 80eca5739dadfe28e7ae982fe909aec1d3c8cd0e Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Wed, 21 Apr 2021 22:58:41 +0100 Subject: [PATCH] [core] split zeroing and dd write operations * This is in preparation for async reads * Also move open/close image operations to WriteDrive() * Also increase DD buffer size to 32 MB to improve performance --- res/appstore/Package.appxmanifest | 2 +- src/drive.c | 4 +- src/format.c | 216 +++++++++++++++++++----------- src/rufus.h | 4 +- src/rufus.rc | 10 +- 5 files changed, 148 insertions(+), 88 deletions(-) diff --git a/res/appstore/Package.appxmanifest b/res/appstore/Package.appxmanifest index f7320324..779b35fe 100644 --- a/res/appstore/Package.appxmanifest +++ b/res/appstore/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="3.14.1775.0" /> Rufus diff --git a/src/drive.c b/src/drive.c index 9ef693c8..4bf2e0da 100644 --- a/src/drive.c +++ b/src/drive.c @@ -748,9 +748,9 @@ BOOL VdsRescan(DWORD dwRescanType, DWORD dwSleepTime, BOOL bSilent) BOOL DeletePartition(DWORD DriveIndex, ULONGLONG PartitionOffset, BOOL bSilent) { HRESULT hr = S_FALSE; - VDS_PARTITION_PROP* prop_array; + VDS_PARTITION_PROP* prop_array = NULL; LONG i, prop_array_size; - IVdsAdvancedDisk *pAdvancedDisk; + IVdsAdvancedDisk *pAdvancedDisk = NULL; if (!GetVdsDiskInterface(DriveIndex, &IID_IVdsAdvancedDisk, (void**)&pAdvancedDisk, bSilent)) return FALSE; diff --git a/src/format.c b/src/format.c index bee0f6b5..e72a2fac 100644 --- a/src/format.c +++ b/src/format.c @@ -1484,12 +1484,13 @@ static int sector_write(int fd, const void* _buf, unsigned int count) } /* Write an image file or zero a drive */ -static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) +static BOOL WriteDrive(HANDLE hPhysicalDrive, BOOL bZeroDrive) { BOOL s, ret = FALSE; LARGE_INTEGER li; - DWORD i, rSize, wSize, xSize, BufSize; - uint64_t wb, target_size = hSourceImage?img_report.image_size:SelectedDrive.DiskSize; + HANDLE hSourceImage = INVALID_HANDLE_VALUE; + DWORD i, read_size, write_size, comp_size, buf_size; + uint64_t wb, target_size = bZeroDrive ? SelectedDrive.DiskSize : img_report.image_size; uint64_t cur_value, last_value = UINT64_MAX; int64_t bled_ret; uint8_t* buffer = NULL; @@ -1507,51 +1508,23 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) uprintf("Warning: Unable to rewind image position - wrong data might be copied!"); UpdateProgressWithInfoInit(NULL, FALSE); - if (img_report.compression_type != BLED_COMPRESSION_NONE) { - uprintf("Writing compressed image:"); - sec_buf = (uint8_t*)_mm_malloc(SelectedDrive.SectorSize, SelectedDrive.SectorSize); - if (sec_buf == NULL) { - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; - uprintf("Could not allocate disk write buffer"); - goto out; - } - assert((uintptr_t)sec_buf % SelectedDrive.SectorSize == 0); - sec_buf_pos = 0; - bled_init(_uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus); - bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type); - bled_exit(); - if ((bled_ret >= 0) && (sec_buf_pos != 0)) { - // A disk image that doesn't end up on disk boundary should be a rare - // enough case, so we dont bother checking the write operation and - // just issue a notice about it in the log. - uprintf("Notice: Compressed image data didn't end on block boundary."); - // Gonna assert that WriteFile() and _write() share the same file offset - WriteFile(hPhysicalDrive, sec_buf, SelectedDrive.SectorSize, &wSize, NULL); - } - safe_mm_free(sec_buf); - if ((bled_ret < 0) && (SCODE_CODE(FormatStatus) != ERROR_CANCELLED)) { - // Unfortunately, different compression backends return different negative error codes - uprintf("Could not write compressed image: %lld", bled_ret); - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; - goto out; - } - } else { - uprintf(hSourceImage?"Writing Image:":fast_zeroing?"Fast-zeroing drive:":"Zeroing drive:"); + if (bZeroDrive) { + uprintf(fast_zeroing ? "Fast-zeroing drive:" : "Zeroing drive:"); // Our buffer size must be a multiple of the sector size and *ALIGNED* to the sector size - BufSize = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; - buffer = (uint8_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize); + buf_size = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; + buffer = (uint8_t*)_mm_malloc(buf_size, SelectedDrive.SectorSize); if (buffer == NULL) { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; - uprintf("Could not allocate disk write buffer"); + uprintf("Could not allocate disk zeroing buffer"); goto out; } assert((uintptr_t)buffer % SelectedDrive.SectorSize == 0); // Clear buffer - memset(buffer, fast_zeroing ? 0xff : 0x00, BufSize); + memset(buffer, fast_zeroing ? 0xff : 0x00, buf_size); if (fast_zeroing) { - cmp_buffer = (uint32_t*)_mm_malloc(BufSize, SelectedDrive.SectorSize); + cmp_buffer = (uint32_t*)_mm_malloc(buf_size, SelectedDrive.SectorSize); if (cmp_buffer == NULL) { FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; uprintf("Could not allocate disk comparison buffer"); @@ -1560,35 +1533,22 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) assert((uintptr_t)cmp_buffer % SelectedDrive.SectorSize == 0); } - // Don't bother trying for something clever, using double buffering overlapped and whatnot: - // With Windows' default optimizations, sync read + sync write for sequential operations - // will be as fast, if not faster, than whatever async scheme you can come up with. - rSize = BufSize; - for (wb = 0, wSize = 0; wb < target_size; wb += wSize) { - UpdateProgressWithInfo(OP_FORMAT, hSourceImage ? MSG_261 : fast_zeroing ? MSG_306 : MSG_286, wb, target_size); + read_size = buf_size; + for (wb = 0, write_size = 0; wb < target_size; wb += write_size) { + UpdateProgressWithInfo(OP_FORMAT, fast_zeroing ? MSG_306 : MSG_286, wb, target_size); cur_value = (wb * min(80, target_size)) / target_size; if (cur_value != last_value) { last_value = cur_value; uprintfs("+"); } - if (hSourceImage != NULL) { - s = ReadFile(hSourceImage, buffer, BufSize, &rSize, NULL); - if (!s) { - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; - uprintf("Read error: %s", WindowsErrorString()); - goto out; - } - if (rSize == 0) - break; - } // Don't overflow our projected size (mostly for VHDs) - if (wb + rSize > target_size) { - rSize = (DWORD)(target_size - wb); + if (wb + read_size > target_size) { + read_size = (DWORD)(target_size - wb); } // WriteFile fails unless the size is a multiple of sector size - if (rSize % SelectedDrive.SectorSize != 0) - rSize = ((rSize + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; + if (read_size % SelectedDrive.SectorSize != 0) + read_size = ((read_size + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; // Fast-zeroing: Depending on your hardware, reading from flash may be much faster than writing, so // we might speed things up by skipping empty blocks, or skipping the write if the data is the same. @@ -1601,8 +1561,8 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) CHECK_FOR_USER_CANCEL; // Read block and compare against the block that needs to be written - s = ReadFile(hPhysicalDrive, cmp_buffer, rSize, &xSize, NULL); - if ((!s) || (xSize != rSize) ) { + s = ReadFile(hPhysicalDrive, cmp_buffer, read_size, &comp_size, NULL); + if ((!s) || (comp_size != read_size)) { uprintf("Read error: Could not read data for fast zeroing comparison - %s", WindowsErrorString()); goto out; } @@ -1612,10 +1572,10 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) // Check all bits are the same if ((zero_data == 0) || (zero_data == 0xffffffff)) { // Compare the rest of the block against the first element - for (i = 1; (i < rSize / sizeof(uint32_t)) && (cmp_buffer[i] == zero_data); i++); - if (i >= rSize / sizeof(uint32_t)) { + for (i = 1; (i < read_size / sizeof(uint32_t)) && (cmp_buffer[i] == zero_data); i++); + if (i >= read_size / sizeof(uint32_t)) { // Block is empty, skip write - wSize = rSize; + write_size = read_size; continue; } } @@ -1632,11 +1592,11 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) for (i = 1; i <= WRITE_RETRIES; i++) { CHECK_FOR_USER_CANCEL; - s = WriteFile(hPhysicalDrive, buffer, rSize, &wSize, NULL); - if ((s) && (wSize == rSize)) + s = WriteFile(hPhysicalDrive, buffer, read_size, &write_size, NULL); + if ((s) && (write_size == read_size)) break; if (s) - uprintf("Write error: Wrote %d bytes, expected %d bytes", wSize, rSize); + uprintf("Write error: Wrote %d bytes, expected %d bytes", write_size, read_size); else uprintf("Write error at sector %lld: %s", wb / SelectedDrive.SectorSize, WindowsErrorString()); if (i < WRITE_RETRIES) { @@ -1656,10 +1616,120 @@ static BOOL WriteDrive(HANDLE hPhysicalDrive, HANDLE hSourceImage) if (i > WRITE_RETRIES) goto out; } + } else if (img_report.compression_type != BLED_COMPRESSION_NONE) { + uprintf("Writing compressed image:"); + hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hSourceImage == INVALID_HANDLE_VALUE) { + uprintf("Could not open image '%s': %s", image_path, WindowsErrorString()); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; + goto out; + } + sec_buf = (uint8_t*)_mm_malloc(SelectedDrive.SectorSize, SelectedDrive.SectorSize); + if (sec_buf == NULL) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; + uprintf("Could not allocate disk write buffer"); + goto out; + } + assert((uintptr_t)sec_buf % SelectedDrive.SectorSize == 0); + sec_buf_pos = 0; + bled_init(_uprintf, NULL, sector_write, update_progress, NULL, &FormatStatus); + bled_ret = bled_uncompress_with_handles(hSourceImage, hPhysicalDrive, img_report.compression_type); + bled_exit(); + if ((bled_ret >= 0) && (sec_buf_pos != 0)) { + // A disk image that doesn't end up on disk boundary should be a rare + // enough case, so we dont bother checking the write operation and + // just issue a notice about it in the log. + uprintf("Notice: Compressed image data didn't end on block boundary."); + // Gonna assert that WriteFile() and _write() share the same file offset + WriteFile(hPhysicalDrive, sec_buf, SelectedDrive.SectorSize, &write_size, NULL); + } + safe_mm_free(sec_buf); + if ((bled_ret < 0) && (SCODE_CODE(FormatStatus) != ERROR_CANCELLED)) { + // Unfortunately, different compression backends return different negative error codes + uprintf("Could not write compressed image: %lld", bled_ret); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + goto out; + } + } else { + hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hSourceImage == INVALID_HANDLE_VALUE) { + uprintf("Could not open image '%s': %s", image_path, WindowsErrorString()); + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; + goto out; + } + + // Our buffer size must be a multiple of the sector size and *ALIGNED* to the sector size + buf_size = ((DD_BUFFER_SIZE + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; + buffer = (uint8_t*)_mm_malloc(buf_size, SelectedDrive.SectorSize); + if (buffer == NULL) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_NOT_ENOUGH_MEMORY; + uprintf("Could not allocate disk write buffer"); + goto out; + } + assert((uintptr_t)buffer % SelectedDrive.SectorSize == 0); + + // Don't bother trying for something clever, using double buffering overlapped and whatnot: + // With Windows' default optimizations, sync read + sync write for sequential operations + // will be as fast, if not faster, than whatever async scheme you can come up with. + read_size = buf_size; + for (wb = 0, write_size = 0; wb < target_size; wb += write_size) { + UpdateProgressWithInfo(OP_FORMAT, MSG_261, wb, target_size); + cur_value = (wb * min(80, target_size)) / target_size; + if (cur_value != last_value) { + last_value = cur_value; + uprintfs("+"); + } + s = ReadFile(hSourceImage, buffer, buf_size, &read_size, NULL); + if (!s) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; + uprintf("Read error: %s", WindowsErrorString()); + goto out; + } + if (read_size == 0) + break; + // Don't overflow our projected size (mostly for VHDs) + if (wb + read_size > target_size) { + read_size = (DWORD)(target_size - wb); + } + + // WriteFile fails unless the size is a multiple of sector size + if (read_size % SelectedDrive.SectorSize != 0) + read_size = ((read_size + SelectedDrive.SectorSize - 1) / SelectedDrive.SectorSize) * SelectedDrive.SectorSize; + + for (i = 1; i <= WRITE_RETRIES; i++) { + CHECK_FOR_USER_CANCEL; + s = WriteFile(hPhysicalDrive, buffer, read_size, &write_size, NULL); + if ((s) && (write_size == read_size)) + break; + if (s) + uprintf("Write error: Wrote %d bytes, expected %d bytes", write_size, read_size); + else + uprintf("Write error at sector %lld: %s", wb / SelectedDrive.SectorSize, WindowsErrorString()); + if (i < WRITE_RETRIES) { + li.QuadPart = wb; + uprintf("Retrying in %d seconds...", WRITE_TIMEOUT / 1000); + Sleep(WRITE_TIMEOUT); + if (!SetFilePointerEx(hPhysicalDrive, li, NULL, FILE_BEGIN)) { + uprintf("Write error: Could not reset position - %s", WindowsErrorString()); + goto out; + } + } else { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_WRITE_FAULT; + goto out; + } + Sleep(200); + } + if (i > WRITE_RETRIES) + goto out; + } + } RefreshDriveLayout(hPhysicalDrive); ret = TRUE; out: + safe_closehandle(hSourceImage); safe_mm_free(buffer); safe_mm_free(cmp_buffer); return ret; @@ -1684,7 +1754,6 @@ DWORD WINAPI FormatThread(void* param) DWORD cr, DriveIndex = (DWORD)(uintptr_t)param, ClusterSize, Flags; HANDLE hPhysicalDrive = INVALID_HANDLE_VALUE; HANDLE hLogicalVolume = INVALID_HANDLE_VALUE; - HANDLE hSourceImage = INVALID_HANDLE_VALUE; SYSTEMTIME lt; FILE* log_fd; uint8_t *buffer = NULL, extra_partitions = 0; @@ -1791,7 +1860,7 @@ DWORD WINAPI FormatThread(void* param) } if (zero_drive) { - WriteDrive(hPhysicalDrive, NULL); + WriteDrive(hPhysicalDrive, TRUE); goto out; } @@ -1875,15 +1944,7 @@ DWORD WINAPI FormatThread(void* param) // Write an image file if ((boot_type == BT_IMAGE) && write_as_image) { - hSourceImage = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); - if (hSourceImage == INVALID_HANDLE_VALUE) { - uprintf("Could not open image '%s': %s", image_path, WindowsErrorString()); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_OPEN_FAILED; - goto out; - } - - WriteDrive(hPhysicalDrive, hSourceImage); + WriteDrive(hPhysicalDrive, FALSE); // If the image contains a partition we might be able to access, try to re-mount it safe_unlockclose(hPhysicalDrive); @@ -2188,7 +2249,6 @@ out: } safe_free(volume_name); safe_free(buffer); - safe_closehandle(hSourceImage); safe_unlockclose(hLogicalVolume); safe_unlockclose(hPhysicalDrive); // This can take a while if (IS_ERROR(FormatStatus)) { diff --git a/src/rufus.h b/src/rufus.h index becf8c96..1a09cf23 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -99,12 +99,12 @@ #define BADCLOCK_PATTERN_MLC {0x00, 0xff, 0x33, 0xcc} #define BADBLOCK_PATTERN_TLC {0x00, 0xff, 0x1c71c7, 0xe38e38} #define BADBLOCK_BLOCK_SIZE (128 * 1024) -#define LARGE_FAT32_SIZE (32*1073741824LL) // Size at which we need to use fat32format +#define LARGE_FAT32_SIZE (32 * 1073741824LL) // Size at which we need to use fat32format #define UDF_FORMAT_SPEED 3.1f // Speed estimate at which we expect UDF drives to be formatted (GB/s) #define UDF_FORMAT_WARN 20 // Duration (in seconds) above which we warn about long UDF formatting times #define MAX_FAT32_SIZE 2.0f // Threshold above which we disable FAT32 formatting (in TB) #define FAT32_CLUSTER_THRESHOLD 1.011f // For FAT32, cluster size changes don't occur at power of 2 boundaries but sligthly above -#define DD_BUFFER_SIZE 65536 // Minimum size of the buffer we use for DD operations +#define DD_BUFFER_SIZE (32 * 1024 * 1024) // Minimum size of buffer to use for DD operations #define UBUFFER_SIZE 4096 #define RSA_SIGNATURE_SIZE 256 #define CBN_SELCHANGE_INTERNAL (CBN_SELCHANGE + 256) diff --git a/src/rufus.rc b/src/rufus.rc index bc049958..eeffddb6 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.14.1774" +CAPTION "Rufus 3.14.1775" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -395,8 +395,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,14,1774,0 - PRODUCTVERSION 3,14,1774,0 + FILEVERSION 3,14,1775,0 + PRODUCTVERSION 3,14,1775,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -414,13 +414,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.14.1774" + VALUE "FileVersion", "3.14.1775" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.14.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.14.1774" + VALUE "ProductVersion", "3.14.1775" END END BLOCK "VarFileInfo"