diff --git a/.vs/rufus.vcxproj b/.vs/rufus.vcxproj index 6a4fcc47..9aef5e72 100644 --- a/.vs/rufus.vcxproj +++ b/.vs/rufus.vcxproj @@ -337,6 +337,8 @@ + + diff --git a/.vs/rufus.vcxproj.filters b/.vs/rufus.vcxproj.filters index 97df1b3a..1e5e232d 100644 --- a/.vs/rufus.vcxproj.filters +++ b/.vs/rufus.vcxproj.filters @@ -81,6 +81,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/src/Makefile.am b/src/Makefile.am index 38dba118..5d9fcfc1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,7 @@ AM_V_WINDRES = $(AM_V_WINDRES_$(V)) %_rc.o: %.rc ../res/loc/embedded.loc $(AM_V_WINDRES) $(AM_RCFLAGS) -i $< -o $@ -rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c \ +rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.c icon.c iso.c localization.c \ net.c parser.c pki.c process.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c ui.c vhd.c rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \ -DEXT2_FLAT_INCLUDES=0 diff --git a/src/Makefile.in b/src/Makefile.in index 1db35975..bf95bce1 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -90,7 +90,8 @@ PROGRAMS = $(noinst_PROGRAMS) am_rufus_OBJECTS = rufus-badblocks.$(OBJEXT) rufus-checksum.$(OBJEXT) \ rufus-dev.$(OBJEXT) rufus-dos.$(OBJEXT) \ rufus-dos_locale.$(OBJEXT) rufus-drive.$(OBJEXT) \ - rufus-format.$(OBJEXT) rufus-icon.$(OBJEXT) \ + rufus-format.$(OBJEXT) rufus-format_ext.$(OBJEXT) \ + rufus-format_fat32.$(OBJEXT) rufus-icon.$(OBJEXT) \ rufus-iso.$(OBJEXT) rufus-localization.$(OBJEXT) \ rufus-net.$(OBJEXT) rufus-parser.$(OBJEXT) rufus-pki.$(OBJEXT) \ rufus-process.$(OBJEXT) rufus-rufus.$(OBJEXT) \ @@ -272,7 +273,7 @@ AM_V_WINDRES_0 = @echo " RC $@";$(WINDRES) AM_V_WINDRES_1 = $(WINDRES) AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) AM_V_WINDRES = $(AM_V_WINDRES_$(V)) -rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c icon.c iso.c localization.c \ +rufus_SOURCES = badblocks.c checksum.c dev.c dos.c dos_locale.c drive.c format.c format_ext.c format_fat32.c icon.c iso.c localization.c \ net.c parser.c pki.c process.c rufus.c smart.c stdfn.c stdio.c stdlg.c syslinux.c ui.c vhd.c rufus_CFLAGS = -I$(srcdir)/ms-sys/inc -I$(srcdir)/syslinux/libfat -I$(srcdir)/syslinux/libinstaller -I$(srcdir)/syslinux/win -I$(srcdir)/libcdio $(AM_CFLAGS) \ @@ -378,6 +379,18 @@ rufus-format.o: format.c rufus-format.obj: format.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format.obj `if test -f 'format.c'; then $(CYGPATH_W) 'format.c'; else $(CYGPATH_W) '$(srcdir)/format.c'; fi` +rufus-format_ext.o: format_ext.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format_ext.o `test -f 'format_ext.c' || echo '$(srcdir)/'`format_ext.c + +rufus-format_ext.obj: format_ext.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format_ext.obj `if test -f 'format_ext.c'; then $(CYGPATH_W) 'format_ext.c'; else $(CYGPATH_W) '$(srcdir)/format_ext.c'; fi` + +rufus-format_fat32.o: format_fat32.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format_fat32.o `test -f 'format_fat32.c' || echo '$(srcdir)/'`format_fat32.c + +rufus-format_fat32.obj: format_fat32.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-format_fat32.obj `if test -f 'format_fat32.c'; then $(CYGPATH_W) 'format_fat32.c'; else $(CYGPATH_W) '$(srcdir)/format_fat32.c'; fi` + rufus-icon.o: icon.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(rufus_CFLAGS) $(CFLAGS) -c -o rufus-icon.o `test -f 'icon.c' || echo '$(srcdir)/'`icon.c diff --git a/src/format.c b/src/format.c index 950ba564..78ce2577 100644 --- a/src/format.c +++ b/src/format.c @@ -1,7 +1,6 @@ /* * Rufus: The Reliable USB Formatting Utility * Formatting function calls - * Copyright © 2007-2009 Tom Thornhill/Ridgecrop * Copyright © 2011-2020 Pete Batard * * This program is free software: you can redistribute it and/or modify @@ -70,7 +69,6 @@ extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, extern BOOL use_vds; uint8_t *grub2_buf = NULL, *sec_buf = NULL; long grub2_len; -static BOOL WritePBR(HANDLE hLogicalDrive); /* * Convert the fmifs outputs messages (that use an OEM code page) to UTF-8 @@ -313,799 +311,6 @@ static void ToValidLabel(char* Label, BOOL bFAT) free(wLabel); } -/* - * 28.2 CALCULATING THE VOLUME SERIAL NUMBER - * - * For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94 - * seconds. DOS takes the date and time just before it writes it to the - * disk. - * - * Low order word is calculated: Volume Serial Number is: - * Month & Day 12/26 0c1ah - * Sec & Hundredths 41:94 295eh 3578:1d02 - * ----- - * 3578h - * - * High order word is calculated: - * Hours & Minutes 21:55 1537h - * Year 1995 07cbh - * ----- - * 1d02h - */ -static DWORD GetVolumeID(void) -{ - SYSTEMTIME s; - DWORD d; - WORD lo,hi,tmp; - - GetLocalTime(&s); - - lo = s.wDay + (s.wMonth << 8); - tmp = (s.wMilliseconds/10) + (s.wSecond << 8); - lo += tmp; - - hi = s.wMinute + (s.wHour << 8); - hi += s.wYear; - - d = lo + (hi << 16); - return d; -} - -/* - * Proper computation of FAT size - * See: http://www.syslinux.org/archives/2016-February/024850.html - * and subsequent replies. - */ -static DWORD GetFATSizeSectors(DWORD DskSize, DWORD ReservedSecCnt, DWORD SecPerClus, DWORD NumFATs, DWORD BytesPerSect) -{ - ULONGLONG Numerator, Denominator; - ULONGLONG FatElementSize = 4; - ULONGLONG ReservedClusCnt = 2; - ULONGLONG FatSz; - - Numerator = DskSize - ReservedSecCnt + ReservedClusCnt * SecPerClus; - Denominator = SecPerClus * BytesPerSect / FatElementSize + NumFATs; - FatSz = Numerator / Denominator + 1; // +1 to ensure we are rounded up - - return (DWORD)FatSz; -} - -/* - * Large FAT32 volume formatting from fat32format by Tom Thornhill - * http://www.ridgecrop.demon.co.uk/index.htm?fat32format.htm - */ -static BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) -{ - BOOL r = FALSE; - DWORD i; - HANDLE hLogicalVolume = NULL; - DWORD cbRet; - DISK_GEOMETRY dgDrive; - BYTE geometry_ex[256]; // DISK_GEOMETRY_EX is variable size - PDISK_GEOMETRY_EX xdgDrive = (PDISK_GEOMETRY_EX)(void*)geometry_ex; - PARTITION_INFORMATION piDrive; - PARTITION_INFORMATION_EX xpiDrive; - // Recommended values - DWORD ReservedSectCount = 32; - DWORD NumFATs = 2; - DWORD BackupBootSect = 6; - DWORD VolumeId = 0; // calculated before format - char* VolumeName = NULL; - DWORD BurstSize = 128; // Zero in blocks of 64K typically - - // Calculated later - DWORD FatSize = 0; - DWORD BytesPerSect = 0; - DWORD SectorsPerCluster = 0; - DWORD TotalSectors = 0; - DWORD SystemAreaSize = 0; - DWORD UserAreaSize = 0; - ULONGLONG qTotalSectors = 0; - - // Structures to be written to the disk - FAT_BOOTSECTOR32 *pFAT32BootSect = NULL; - FAT_FSINFO *pFAT32FsInfo = NULL; - DWORD *pFirstSectOfFat = NULL; - BYTE* pZeroSect = NULL; - char VolId[12] = "NO NAME "; - - // Debug temp vars - ULONGLONG FatNeeded, ClusterCount; - - if (safe_strncmp(FSName, "FAT", 3) != 0) { - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; - goto out; - } - PrintInfoDebug(0, MSG_222, "Large FAT32"); - UpdateProgressWithInfoInit(NULL, TRUE); - VolumeId = GetVolumeID(); - - // Open the drive and lock it - hLogicalVolume = GetLogicalHandle(DriveIndex, PartitionOffset, TRUE, TRUE, FALSE); - if (IS_ERROR(FormatStatus)) - goto out; - if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) - die("Invalid logical volume handle", ERROR_INVALID_HANDLE); - - // Try to disappear the volume while we're formatting it - UnmountVolume(hLogicalVolume); - - // Work out drive params - if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dgDrive, - sizeof(dgDrive), &cbRet, NULL)) { - if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, xdgDrive, - sizeof(geometry_ex), &cbRet, NULL)) { - uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY error: %s", WindowsErrorString()); - die("Failed to get device geometry (both regular and _ex)", ERROR_NOT_SUPPORTED); - } - memcpy(&dgDrive, &xdgDrive->Geometry, sizeof(dgDrive)); - } - if (dgDrive.BytesPerSector < 512) - dgDrive.BytesPerSector = 512; - if (IS_ERROR(FormatStatus)) goto out; - if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &piDrive, - sizeof(piDrive), &cbRet, NULL)) { - if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &xpiDrive, - sizeof(xpiDrive), &cbRet, NULL)) { - uprintf("IOCTL_DISK_GET_PARTITION_INFO error: %s", WindowsErrorString()); - die("Failed to get partition info (both regular and _ex)", ERROR_NOT_SUPPORTED); - } - - memset(&piDrive, 0, sizeof(piDrive)); - piDrive.StartingOffset.QuadPart = xpiDrive.StartingOffset.QuadPart; - piDrive.PartitionLength.QuadPart = xpiDrive.PartitionLength.QuadPart; - piDrive.HiddenSectors = (DWORD) (xpiDrive.StartingOffset.QuadPart / dgDrive.BytesPerSector); - } - if (IS_ERROR(FormatStatus)) goto out; - - BytesPerSect = dgDrive.BytesPerSector; - - // Checks on Disk Size - qTotalSectors = piDrive.PartitionLength.QuadPart/dgDrive.BytesPerSector; - // Low end limit - 65536 sectors - if (qTotalSectors < 65536) { - // Most FAT32 implementations would probably mount this volume just fine, - // but the spec says that we shouldn't do this, so we won't - die("This drive is too small for FAT32 - there must be at least 64K clusters", APPERR(ERROR_INVALID_CLUSTER_SIZE)); - } - - if (qTotalSectors >= 0xffffffff) { - // This is a more fundamental limitation on FAT32 - the total sector count in the root dir - // is 32bit. With a bit of creativity, FAT32 could be extended to handle at least 2^28 clusters - // There would need to be an extra field in the FSInfo sector, and the old sector count could - // be set to 0xffffffff. This is non standard though, the Windows FAT driver FASTFAT.SYS won't - // understand this. Perhaps a future version of FAT32 and FASTFAT will handle this. - die("This drive is too big for FAT32 - max 2TB supported", APPERR(ERROR_INVALID_VOLUME_SIZE)); - } - - // coverity[tainted_data] - pFAT32BootSect = (FAT_BOOTSECTOR32*) calloc(BytesPerSect, 1); - pFAT32FsInfo = (FAT_FSINFO*) calloc(BytesPerSect, 1); - pFirstSectOfFat = (DWORD*) calloc(BytesPerSect, 1); - if (!pFAT32BootSect || !pFAT32FsInfo || !pFirstSectOfFat) { - die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); - } - - // fill out the boot sector and fs info - pFAT32BootSect->sJmpBoot[0]=0xEB; - pFAT32BootSect->sJmpBoot[1]=0x58; // jmp.s $+0x5a is 0xeb 0x58, not 0xeb 0x5a. Thanks Marco! - pFAT32BootSect->sJmpBoot[2]=0x90; - memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8); - pFAT32BootSect->wBytsPerSec = (WORD) BytesPerSect; - SectorsPerCluster = ClusterSize / BytesPerSect; - - pFAT32BootSect->bSecPerClus = (BYTE) SectorsPerCluster ; - pFAT32BootSect->wRsvdSecCnt = (WORD) ReservedSectCount; - pFAT32BootSect->bNumFATs = (BYTE) NumFATs; - pFAT32BootSect->wRootEntCnt = 0; - pFAT32BootSect->wTotSec16 = 0; - pFAT32BootSect->bMedia = 0xF8; - pFAT32BootSect->wFATSz16 = 0; - pFAT32BootSect->wSecPerTrk = (WORD) dgDrive.SectorsPerTrack; - pFAT32BootSect->wNumHeads = (WORD) dgDrive.TracksPerCylinder; - pFAT32BootSect->dHiddSec = (DWORD) piDrive.HiddenSectors; - TotalSectors = (DWORD) (piDrive.PartitionLength.QuadPart/dgDrive.BytesPerSector); - pFAT32BootSect->dTotSec32 = TotalSectors; - - FatSize = GetFATSizeSectors(pFAT32BootSect->dTotSec32, pFAT32BootSect->wRsvdSecCnt, - pFAT32BootSect->bSecPerClus, pFAT32BootSect->bNumFATs, BytesPerSect); - - pFAT32BootSect->dFATSz32 = FatSize; - pFAT32BootSect->wExtFlags = 0; - pFAT32BootSect->wFSVer = 0; - pFAT32BootSect->dRootClus = 2; - pFAT32BootSect->wFSInfo = 1; - pFAT32BootSect->wBkBootSec = (WORD) BackupBootSect; - pFAT32BootSect->bDrvNum = 0x80; - pFAT32BootSect->Reserved1 = 0; - pFAT32BootSect->bBootSig = 0x29; - - pFAT32BootSect->dBS_VolID = VolumeId; - memcpy(pFAT32BootSect->sVolLab, VolId, 11); - memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8); - ((BYTE*)pFAT32BootSect)[510] = 0x55; - ((BYTE*)pFAT32BootSect)[511] = 0xaa; - - // FATGEN103.DOC says "NOTE: Many FAT documents mistakenly say that this 0xAA55 signature occupies the "last 2 bytes of - // the boot sector". This statement is correct if - and only if - BPB_BytsPerSec is 512. If BPB_BytsPerSec is greater than - // 512, the offsets of these signature bytes do not change (although it is perfectly OK for the last two bytes at the end - // of the boot sector to also contain this signature)." - // - // Windows seems to only check the bytes at offsets 510 and 511. Other OSs might check the ones at the end of the sector, - // so we'll put them there too. - if (BytesPerSect != 512) { - ((BYTE*)pFAT32BootSect)[BytesPerSect-2] = 0x55; - ((BYTE*)pFAT32BootSect)[BytesPerSect-1] = 0xaa; - } - - // FSInfo sect - pFAT32FsInfo->dLeadSig = 0x41615252; - pFAT32FsInfo->dStrucSig = 0x61417272; - pFAT32FsInfo->dFree_Count = (DWORD) -1; - pFAT32FsInfo->dNxt_Free = (DWORD) -1; - pFAT32FsInfo->dTrailSig = 0xaa550000; - - // First FAT Sector - pFirstSectOfFat[0] = 0x0ffffff8; // Reserved cluster 1 media id in low byte - pFirstSectOfFat[1] = 0x0fffffff; // Reserved cluster 2 EOC - pFirstSectOfFat[2] = 0x0fffffff; // end of cluster chain for root dir - - // Write boot sector, fats - // Sector 0 Boot Sector - // Sector 1 FSInfo - // Sector 2 More boot code - we write zeros here - // Sector 3 unused - // Sector 4 unused - // Sector 5 unused - // Sector 6 Backup boot sector - // Sector 7 Backup FSInfo sector - // Sector 8 Backup 'more boot code' - // zeroed sectors upto ReservedSectCount - // FAT1 ReservedSectCount to ReservedSectCount + FatSize - // ... - // FATn ReservedSectCount to ReservedSectCount + FatSize - // RootDir - allocated to cluster2 - - UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize); - ClusterCount = UserAreaSize / SectorsPerCluster; - - // Sanity check for a cluster count of >2^28, since the upper 4 bits of the cluster values in - // the FAT are reserved. - if (ClusterCount > 0x0FFFFFFF) { - die("This drive has more than 2^28 clusters, try to specify a larger cluster size or use the default", - ERROR_INVALID_CLUSTER_SIZE); - } - - // Sanity check - < 64K clusters means that the volume will be misdetected as FAT16 - if (ClusterCount < 65536) { - die("FAT32 must have at least 65536 clusters, try to specify a smaller cluster size or use the default", - ERROR_INVALID_CLUSTER_SIZE); - } - - // Sanity check, make sure the fat is big enough - // Convert the cluster count into a Fat sector count, and check the fat size value we calculated - // earlier is OK. - FatNeeded = ClusterCount * 4; - FatNeeded += (BytesPerSect-1); - FatNeeded /= BytesPerSect; - if (FatNeeded > FatSize) { - die("This drive is too big for large FAT32 format", APPERR(ERROR_INVALID_VOLUME_SIZE)); - } - - // Now we're committed - print some info first - uprintf("Size : %s %u sectors", SizeToHumanReadable(piDrive.PartitionLength.QuadPart, TRUE, FALSE), TotalSectors); - uprintf("Cluster size %d bytes, %d Bytes Per Sector", SectorsPerCluster*BytesPerSect, BytesPerSect); - uprintf("Volume ID is %x:%x", VolumeId>>16, VolumeId&0xffff); - uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs", ReservedSectCount, FatSize, NumFATs); - uprintf("%d Total clusters", ClusterCount); - - // Fix up the FSInfo sector - pFAT32FsInfo->dFree_Count = (UserAreaSize/SectorsPerCluster) - 1; - pFAT32FsInfo->dNxt_Free = 3; // clusters 0-1 reserved, we used cluster 2 for the root dir - - uprintf("%d Free Clusters", pFAT32FsInfo->dFree_Count); - // Work out the Cluster count - - // First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster - SystemAreaSize = ReservedSectCount + (NumFATs*FatSize) + SectorsPerCluster; - uprintf("Clearing out %d sectors for reserved sectors, FATs and root cluster...", SystemAreaSize); - - // Not the most effective, but easy on RAM - pZeroSect = (BYTE*)calloc(BytesPerSect, BurstSize); - if (!pZeroSect) { - die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); - } - - for (i=0; i<(SystemAreaSize+BurstSize-1); i+=BurstSize) { - UpdateProgressWithInfo(OP_FORMAT, MSG_217, (uint64_t)i, (uint64_t)(SystemAreaSize + BurstSize)); - CHECK_FOR_USER_CANCEL; - if (write_sectors(hLogicalVolume, BytesPerSect, i, BurstSize, pZeroSect) != (BytesPerSect*BurstSize)) { - die("Error clearing reserved sectors", ERROR_WRITE_FAULT); - } - } - - uprintf ("Initializing reserved sectors and FATs..."); - // Now we should write the boot sector and fsinfo twice, once at 0 and once at the backup boot sect position - for (i=0; i<2; i++) { - int SectorStart = (i==0) ? 0 : BackupBootSect; - write_sectors(hLogicalVolume, BytesPerSect, SectorStart, 1, pFAT32BootSect); - write_sectors(hLogicalVolume, BytesPerSect, SectorStart+1, 1, pFAT32FsInfo); - } - - // Write the first fat sector in the right places - for ( i=0; i EXT2_ET_BASE) && error_code < (EXT2_ET_BASE + 1000)) { - static_sprintf(error_string, "Unknown ext2fs error %ld (EXT2_ET_BASE + %ld)", error_code, error_code - EXT2_ET_BASE); - } else { - SetLastError((FormatStatus == 0) ? (ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | (error_code & 0xFFFF)) : FormatStatus); - static_sprintf(error_string, WindowsErrorString()); - } - return error_string; - } -} - -static float ext2_percent_start = 0.0f, ext2_percent_share = 0.5f; -const float ext2_max_marker = 80.0f; -errcode_t ext2fs_print_progress(int64_t cur_value, int64_t max_value) -{ - static int64_t last_value = -1; - if (max_value == 0) - return 0; - UpdateProgressWithInfo(OP_FORMAT, MSG_217, (uint64_t)((ext2_percent_start * max_value) + (ext2_percent_share * cur_value)), max_value); - cur_value = (int64_t)(((float)cur_value / (float)max_value) * min(ext2_max_marker, (float)max_value)); - if ((cur_value < last_value) || (cur_value > last_value)) { - last_value = cur_value; - uprintfs("+"); - } - return IS_ERROR(FormatStatus) ? EXT2_ET_CANCEL_REQUESTED : 0; -} - -const char* GetExtFsLabel(DWORD DriveIndex, uint64_t PartitionOffset) -{ - static char label[EXT2_LABEL_LEN + 1]; - errcode_t r; - ext2_filsys ext2fs = NULL; - io_manager manager = nt_io_manager(); - char* volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE); - - if (volume_name == NULL) - return NULL; - r = ext2fs_open(volume_name, EXT2_FLAG_SKIP_MMP, 0, 0, manager, &ext2fs); - free(volume_name); - if (r == 0) { - strncpy(label, ext2fs->super->s_volume_name, EXT2_LABEL_LEN); - label[EXT2_LABEL_LEN] = 0; - } - if (ext2fs != NULL) - ext2fs_close(ext2fs); - return (r == 0) ? label : NULL; -} - -BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) -{ - // Mostly taken from mke2fs.conf - const float reserve_ratio = 0.05f; - const ext2fs_default_t ext2fs_default[5] = { - { 3*MB, 1024, 128, 3}, // "floppy" - { 512*MB, 1024, 128, 2}, // "small" - { 4*GB, 4096, 256, 2}, // "default" - { 16*GB, 4096, 256, 3}, // "big" - { 1024*TB, 4096, 256, 4} // "huge" - }; - - BOOL ret = FALSE; - char *volume_name = NULL; - int i, count; - struct ext2_super_block features = { 0 }; - io_manager manager = nt_io_manager(); - blk_t journal_size; - blk64_t size = 0, cur; - ext2_filsys ext2fs = NULL; - errcode_t r; - uint8_t* buf = NULL; - -#if defined(RUFUS_TEST) - // Create a 32 MB disk image file to test - uint8_t zb[1024]; - HANDLE h; - DWORD dwSize; - volume_name = strdup("\\??\\C:\\tmp\\disk.img"); - memset(zb, 0xFF, sizeof(zb)); // Set to nonzero so we can detect init issues - h = CreateFileU(volume_name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - for (i = 0; i < 32 * 1024; i++) { - if (!WriteFile(h, zb, sizeof(zb), &dwSize, NULL) || (dwSize != sizeof(zb))) { - uprintf("Write error: %s", WindowsErrorString()); - break; - } - } - CloseHandle(h); -#else - volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE); -#endif - if ((volume_name == NULL) | (strlen(FSName) != 4) || (strncmp(FSName, "ext", 3) != 0)) { - FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; - goto out; - } - if (strchr(volume_name, ' ') != NULL) - uprintf("Notice: Using physical device to access partition data"); - - if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) { - if (strcmp(FSName, FileSystemLabel[FS_EXT4]) == 0) - uprintf("ext4 file system is not supported, defaulting to ext3"); - else - uprintf("Invalid ext file system version requested, defaulting to ext3"); - } - - if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) - FSName = FileSystemLabel[FS_EXT3]; - - PrintInfoDebug(0, MSG_222, FSName); - UpdateProgressWithInfoInit(NULL, TRUE); - - // Figure out the volume size and block size - r = ext2fs_get_device_size2(volume_name, KB, &size); - if ((r != 0) || (size == 0)) { - FormatStatus = ext2_last_winerror(ERROR_READ_FAULT); - uprintf("Could not read device size: %s", error_message(r)); - goto out; - } - size *= KB; - for (i = 0; i < ARRAYSIZE(ext2fs_default); i++) { - if (size < ext2fs_default[i].max_size) - break; - } - assert(i < ARRAYSIZE(ext2fs_default)); - // NB: We validated that BlockSize is a power of two in FormatPartition() - if (BlockSize == 0) - BlockSize = ext2fs_default[i].block_size; - size /= BlockSize; - for (features.s_log_block_size = 0; EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE; features.s_log_block_size++) { - if (EXT2_BLOCK_SIZE(&features) == BlockSize) - break; - } - assert(EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE); - - // Set the blocks, reserved blocks and inodes - ext2fs_blocks_count_set(&features, size); - ext2fs_r_blocks_count_set(&features, (blk64_t)(reserve_ratio * size)); - features.s_rev_level = 1; - features.s_inode_size = ext2fs_default[i].inode_size; - features.s_inodes_count = ((ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio) > UINT32_MAX) ? - UINT32_MAX : (uint32_t)(ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio); - uprintf("%d possible inodes out of %lld blocks (block size = %d)", features.s_inodes_count, size, EXT2_BLOCK_SIZE(&features)); - uprintf("%lld blocks (%0.1f%%) reserved for the super user", ext2fs_r_blocks_count(&features), reserve_ratio * 100.0f); - - // Set features - ext2fs_set_feature_xattr(&features); - ext2fs_set_feature_resize_inode(&features); - ext2fs_set_feature_dir_index(&features); - ext2fs_set_feature_filetype(&features); - ext2fs_set_feature_sparse_super(&features); - ext2fs_set_feature_large_file(&features); - if (FSName[3] != '2') - ext2fs_set_feature_journal(&features); - features.s_backup_bgs[0] = ~0; - features.s_default_mount_opts = EXT2_DEFM_XATTR_USER | EXT2_DEFM_ACL; - - // Now that we have set our base features, initialize a virtual superblock - r = ext2fs_initialize(volume_name, EXT2_FLAG_EXCLUSIVE | EXT2_FLAG_64BITS, &features, manager, &ext2fs); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_INVALID_DATA); - uprintf("Could not initialize %s features: %s", FSName, error_message(r)); - goto out; - } - - // Zero 16 blocks of data from the start of our volume - buf = calloc(16, ext2fs->io->block_size); - assert(buf != NULL); - r = io_channel_write_blk64(ext2fs->io, 0, 16, buf); - safe_free(buf); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("Could not zero %s superblock area: %s", FSName, error_message(r)); - goto out; - } - - // Finish setting up the file system - IGNORE_RETVAL(CoCreateGuid((GUID*)ext2fs->super->s_uuid)); - ext2fs_init_csum_seed(ext2fs); - ext2fs->super->s_def_hash_version = EXT2_HASH_HALF_MD4; - IGNORE_RETVAL(CoCreateGuid((GUID*)ext2fs->super->s_hash_seed)); - ext2fs->super->s_max_mnt_count = -1; - ext2fs->super->s_creator_os = EXT2_OS_WINDOWS; - ext2fs->super->s_errors = EXT2_ERRORS_CONTINUE; - if (Label != NULL) - static_strcpy(ext2fs->super->s_volume_name, Label); - - r = ext2fs_allocate_tables(ext2fs); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_INVALID_DATA); - uprintf("Could not allocate %s tables: %s", FSName, error_message(r)); - goto out; - } - r = ext2fs_convert_subcluster_bitmap(ext2fs, &ext2fs->block_map); - if (r != 0) { - uprintf("Could not set %s cluster bitmap: %s", FSName, error_message(r)); - goto out; - } - - ext2_percent_start = 0.0f; - ext2_percent_share = (FSName[3] == '2') ? 1.0f : 0.5f; - uprintf("Creating %d inode sets: [1 marker = %0.1f set(s)]", ext2fs->group_desc_count, - max((float)ext2fs->group_desc_count / ext2_max_marker, 1.0f)); - for (i = 0; i < (int)ext2fs->group_desc_count; i++) { - if (ext2fs_print_progress((int64_t)i, (int64_t)ext2fs->group_desc_count)) - goto out; - cur = ext2fs_inode_table_loc(ext2fs, i); - count = ext2fs_div_ceil((ext2fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(ext2fs, i)) - * EXT2_BLOCK_SIZE(ext2fs->super), EXT2_BLOCK_SIZE(ext2fs->super)); - r = ext2fs_zero_blocks2(ext2fs, cur, count, &cur, &count); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("\r\nCould not zero inode set at position %llu (%d blocks): %s", cur, count, error_message(r)); - goto out; - } - } - uprintfs("\r\n"); - - // Create root and lost+found dirs - r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_DIR_NOT_ROOT); - uprintf("Failed to create %s root dir: %s", FSName, error_message(r)); - goto out; - } - ext2fs->umask = 077; - r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, 0, "lost+found"); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_DIR_NOT_ROOT); - uprintf("Failed to create %s 'lost+found' dir: %s", FSName, error_message(r)); - goto out; - } - - // Create bitmaps - for (i = EXT2_ROOT_INO + 1; i < (int)EXT2_FIRST_INODE(ext2fs->super); i++) - ext2fs_inode_alloc_stats(ext2fs, i, 1); - ext2fs_mark_ib_dirty(ext2fs); - - r = ext2fs_mark_inode_bitmap2(ext2fs->inode_map, EXT2_BAD_INO); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("Could not set inode bitmaps: %s", error_message(r)); - goto out; - } - ext2fs_inode_alloc_stats(ext2fs, EXT2_BAD_INO, 1); - r = ext2fs_update_bb_inode(ext2fs, NULL); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("Could not set inode stats: %s", error_message(r)); - goto out; - } - - if (FSName[3] != '2') { - // Create the journal - ext2_percent_start = 0.5f; - journal_size = ext2fs_default_journal_size(ext2fs_blocks_count(ext2fs->super)); - journal_size /= 2; // That journal init is really killing us! - uprintf("Creating %d journal blocks: [1 marker = %0.1f block(s)]", journal_size, - max((float)journal_size / ext2_max_marker, 1.0f)); - // Even with EXT2_MKJOURNAL_LAZYINIT, this call is absolutely dreadful in terms of speed... - r = ext2fs_add_journal_inode(ext2fs, journal_size, EXT2_MKJOURNAL_NO_MNT_CHECK | ((Flags & FP_QUICK) ? EXT2_MKJOURNAL_LAZYINIT : 0)); - uprintfs("\r\n"); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("Could not create %s journal: %s", FSName, error_message(r)); - goto out; - } - } - - // Create a 'persistence.conf' file if required - if (Flags & FP_CREATE_PERSISTENCE_CONF) { - // You *do* want the LF at the end of the "/ union" line, else Debian Live bails out... - const char *name = "persistence.conf", data[] = "/ union\n"; - int written = 0, fsize = sizeof(data) - 1; - ext2_file_t ext2fd; - ext2_ino_t inode_id; - uint32_t ctime = (uint32_t)time(0); - struct ext2_inode inode = { 0 }; - inode.i_mode = 0100644; - inode.i_links_count = 1; - inode.i_atime = ctime; - inode.i_ctime = ctime; - inode.i_mtime = ctime; - inode.i_size = fsize; - - ext2fs_namei(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &inode_id); - ext2fs_new_inode(ext2fs, EXT2_ROOT_INO, 010755, 0, &inode_id); - ext2fs_link(ext2fs, EXT2_ROOT_INO, name, inode_id, EXT2_FT_REG_FILE); - ext2fs_inode_alloc_stats(ext2fs, inode_id, 1); - ext2fs_write_new_inode(ext2fs, inode_id, &inode); - ext2fs_file_open(ext2fs, inode_id, EXT2_FILE_WRITE, &ext2fd); - if ((ext2fs_file_write(ext2fd, data, fsize, &written) != 0) || (written != fsize)) - uprintf("Error: Could not create '%s' file", name); - else - uprintf("Created '%s' file", name); - ext2fs_file_close(ext2fd); - } - - // Finally we can call close() to get the file system gets created - r = ext2fs_close(ext2fs); - if (r != 0) { - FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); - uprintf("Could not create %s volume: %s", FSName, error_message(r)); - goto out; - } - UpdateProgressWithInfo(OP_FORMAT, MSG_217, 100, 100); - uprintf("Done"); - ret = TRUE; - -out: - free(volume_name); - ext2fs_free(ext2fs); - free(buf); - return ret; -} - /* * Call on VDS to format a partition */ @@ -1772,7 +977,8 @@ static __inline const char* bt_to_name(void) { return ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) ? "KolibriOS" : "Standard"; } } -static BOOL WritePBR(HANDLE hLogicalVolume) + +BOOL WritePBR(HANDLE hLogicalVolume) { int i; FAKE_FD fake_fd = { 0 }; diff --git a/src/format.h b/src/format.h index d150d498..48aeb081 100644 --- a/src/format.h +++ b/src/format.h @@ -1,8 +1,7 @@ /* * Rufus: The Reliable USB Formatting Utility * Formatting function calls - * Copyright © 2007-2009 Tom Thornhill/Ridgecrop - * Copyright © 2011-2019 Pete Batard + * Copyright © 2011-2020 Pete Batard * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,8 +19,6 @@ #include #include // for MEDIA_TYPE -#include "ext2fs/ext2fs.h" - #pragma once /* Callback command types (some errorcode were filled from HPUSBFW V2.2.3 and their @@ -107,63 +104,6 @@ typedef BOOLEAN (WINAPI* EnableVolumeCompression_t)( ULONG CompressionFlags // FILE_SYSTEM_PROP_FLAG ); -/* Large FAT32 */ -#pragma pack(push, 1) -typedef struct tagFAT_BOOTSECTOR32 -{ - // Common fields. - BYTE sJmpBoot[3]; - BYTE sOEMName[8]; - WORD wBytsPerSec; - BYTE bSecPerClus; - WORD wRsvdSecCnt; - BYTE bNumFATs; - WORD wRootEntCnt; - WORD wTotSec16; // if zero, use dTotSec32 instead - BYTE bMedia; - WORD wFATSz16; - WORD wSecPerTrk; - WORD wNumHeads; - DWORD dHiddSec; - DWORD dTotSec32; - // Fat 32/16 only - DWORD dFATSz32; - WORD wExtFlags; - WORD wFSVer; - DWORD dRootClus; - WORD wFSInfo; - WORD wBkBootSec; - BYTE Reserved[12]; - BYTE bDrvNum; - BYTE Reserved1; - BYTE bBootSig; // == 0x29 if next three fields are ok - DWORD dBS_VolID; - BYTE sVolLab[11]; - BYTE sBS_FilSysType[8]; -} FAT_BOOTSECTOR32; - -typedef struct { - DWORD dLeadSig; // 0x41615252 - BYTE sReserved1[480]; // zeros - DWORD dStrucSig; // 0x61417272 - DWORD dFree_Count; // 0xFFFFFFFF - DWORD dNxt_Free; // 0xFFFFFFFF - BYTE sReserved2[12]; // zeros - DWORD dTrailSig; // 0xAA550000 -} FAT_FSINFO; -#pragma pack(pop) - -#define die(msg, err) do { uprintf(msg); \ - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|err; \ - goto out; } while(0) - -// For ext2/ext3/ext4 formatting -typedef struct { - uint64_t max_size; - uint32_t block_size; - uint32_t inode_size; - uint32_t inode_ratio; // inode to data ration (bitshift) -} ext2fs_default_t; - -extern io_manager nt_io_manager(void); -extern DWORD ext2_last_winerror(DWORD default_error); +BOOL WritePBR(HANDLE hLogicalDrive); +BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); +BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); diff --git a/src/format_ext.c b/src/format_ext.c new file mode 100644 index 00000000..4085f89c --- /dev/null +++ b/src/format_ext.c @@ -0,0 +1,491 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * extfs formatting + * Copyright © 2019-2020 Pete Batard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include "rufus.h" +#include "file.h" +#include "drive.h" +#include "format.h" +#include "missing.h" +#include "resource.h" +#include "msapi_utf8.h" +#include "localization.h" +#include "ext2fs/ext2fs.h" + +extern const char* FileSystemLabel[FS_MAX]; +extern io_manager nt_io_manager(void); +extern DWORD ext2_last_winerror(DWORD default_error); +static float ext2_percent_start = 0.0f, ext2_percent_share = 0.5f; +const float ext2_max_marker = 80.0f; + +typedef struct { + uint64_t max_size; + uint32_t block_size; + uint32_t inode_size; + uint32_t inode_ratio; +} ext2fs_default_t; + +const char* error_message(errcode_t error_code) +{ + static char error_string[256]; + + switch (error_code) { + case EXT2_ET_MAGIC_EXT2FS_FILSYS: + case EXT2_ET_MAGIC_BADBLOCKS_LIST: + case EXT2_ET_MAGIC_BADBLOCKS_ITERATE: + case EXT2_ET_MAGIC_INODE_SCAN: + case EXT2_ET_MAGIC_IO_CHANNEL: + case EXT2_ET_MAGIC_IO_MANAGER: + case EXT2_ET_MAGIC_BLOCK_BITMAP: + case EXT2_ET_MAGIC_INODE_BITMAP: + case EXT2_ET_MAGIC_GENERIC_BITMAP: + case EXT2_ET_MAGIC_ICOUNT: + case EXT2_ET_MAGIC_EXTENT_HANDLE: + case EXT2_ET_BAD_MAGIC: + return "Bad magic"; + case EXT2_ET_RO_FILSYS: + return "Read-only file system"; + case EXT2_ET_GDESC_BAD_BLOCK_MAP: + case EXT2_ET_GDESC_BAD_INODE_MAP: + case EXT2_ET_GDESC_BAD_INODE_TABLE: + return "Bad map or table"; + case EXT2_ET_UNEXPECTED_BLOCK_SIZE: + return "Unexpected block size"; + case EXT2_ET_DIR_CORRUPTED: + return "Corrupted entry"; + case EXT2_ET_GDESC_READ: + case EXT2_ET_GDESC_WRITE: + case EXT2_ET_INODE_BITMAP_WRITE: + case EXT2_ET_INODE_BITMAP_READ: + case EXT2_ET_BLOCK_BITMAP_WRITE: + case EXT2_ET_BLOCK_BITMAP_READ: + case EXT2_ET_INODE_TABLE_WRITE: + case EXT2_ET_INODE_TABLE_READ: + case EXT2_ET_NEXT_INODE_READ: + case EXT2_ET_SHORT_READ: + case EXT2_ET_SHORT_WRITE: + return "read/write error"; + case EXT2_ET_DIR_NO_SPACE: + return "no space left"; + case EXT2_ET_TOOSMALL: + return "Too small"; + case EXT2_ET_BAD_DEVICE_NAME: + return "Bad device name"; + case EXT2_ET_MISSING_INODE_TABLE: + return "Missing inode table"; + case EXT2_ET_CORRUPT_SUPERBLOCK: + return "Superblock is corrupted"; + case EXT2_ET_CALLBACK_NOTHANDLED: + return "Unhandled callback"; + case EXT2_ET_BAD_BLOCK_IN_INODE_TABLE: + return "Bad block in inode table"; + case EXT2_ET_UNSUPP_FEATURE: + case EXT2_ET_RO_UNSUPP_FEATURE: + case EXT2_ET_UNIMPLEMENTED: + return "Unsupported feature"; + case EXT2_ET_LLSEEK_FAILED: + return "Seek failed"; + case EXT2_ET_NO_MEMORY: + case EXT2_ET_BLOCK_ALLOC_FAIL: + case EXT2_ET_INODE_ALLOC_FAIL: + return "Out of memory"; + case EXT2_ET_INVALID_ARGUMENT: + return "Invalid argument"; + case EXT2_ET_NO_DIRECTORY: + return "No directory"; + case EXT2_ET_FILE_NOT_FOUND: + return "File not found"; + case EXT2_ET_FILE_RO: + return "File is read-only"; + case EXT2_ET_DIR_EXISTS: + return "Directory already exists"; + case EXT2_ET_CANCEL_REQUESTED: + return "Cancel requested"; + case EXT2_ET_FILE_TOO_BIG: + return "File too big"; + case EXT2_ET_JOURNAL_NOT_BLOCK: + case EXT2_ET_NO_JOURNAL_SB: + return "No journal superblock"; + case EXT2_ET_JOURNAL_TOO_SMALL: + return "Journal too small"; + case EXT2_ET_NO_JOURNAL: + return "No journal"; + case EXT2_ET_TOO_MANY_INODES: + return "Too many inodes"; + case EXT2_ET_NO_CURRENT_NODE: + return "No current node"; + case EXT2_ET_OP_NOT_SUPPORTED: + return "Operation not supported"; + case EXT2_ET_IO_CHANNEL_NO_SUPPORT_64: + return "I/O Channel does not support 64-bit operation"; + case EXT2_ET_BAD_DESC_SIZE: + return "Bad descriptor size"; + case EXT2_ET_INODE_CSUM_INVALID: + case EXT2_ET_INODE_BITMAP_CSUM_INVALID: + case EXT2_ET_EXTENT_CSUM_INVALID: + case EXT2_ET_DIR_CSUM_INVALID: + case EXT2_ET_EXT_ATTR_CSUM_INVALID: + case EXT2_ET_SB_CSUM_INVALID: + case EXT2_ET_BLOCK_BITMAP_CSUM_INVALID: + case EXT2_ET_MMP_CSUM_INVALID: + return "Invalid checksum"; + case EXT2_ET_UNKNOWN_CSUM: + return "Unknown checksum"; + case EXT2_ET_FILE_EXISTS: + return "File exists"; + case EXT2_ET_INODE_IS_GARBAGE: + return "Inode is garbage"; + case EXT2_ET_JOURNAL_FLAGS_WRONG: + return "Wrong journal flags"; + case EXT2_ET_FILESYSTEM_CORRUPTED: + return "File system is corrupted"; + case EXT2_ET_BAD_CRC: + return "Bad CRC"; + case EXT2_ET_CORRUPT_JOURNAL_SB: + return "Journal Superblock is corrupted"; + case EXT2_ET_INODE_CORRUPTED: + case EXT2_ET_EA_INODE_CORRUPTED: + return "Inode is corrupted"; + default: + if ((error_code > EXT2_ET_BASE) && error_code < (EXT2_ET_BASE + 1000)) { + static_sprintf(error_string, "Unknown ext2fs error %ld (EXT2_ET_BASE + %ld)", error_code, error_code - EXT2_ET_BASE); + } else { + SetLastError((FormatStatus == 0) ? (ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | (error_code & 0xFFFF)) : FormatStatus); + static_sprintf(error_string, WindowsErrorString()); + } + return error_string; + } +} + +errcode_t ext2fs_print_progress(int64_t cur_value, int64_t max_value) +{ + static int64_t last_value = -1; + if (max_value == 0) + return 0; + UpdateProgressWithInfo(OP_FORMAT, MSG_217, (uint64_t)((ext2_percent_start * max_value) + (ext2_percent_share * cur_value)), max_value); + cur_value = (int64_t)(((float)cur_value / (float)max_value) * min(ext2_max_marker, (float)max_value)); + if ((cur_value < last_value) || (cur_value > last_value)) { + last_value = cur_value; + uprintfs("+"); + } + return IS_ERROR(FormatStatus) ? EXT2_ET_CANCEL_REQUESTED : 0; +} + +const char* GetExtFsLabel(DWORD DriveIndex, uint64_t PartitionOffset) +{ + static char label[EXT2_LABEL_LEN + 1]; + errcode_t r; + ext2_filsys ext2fs = NULL; + io_manager manager = nt_io_manager(); + char* volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE); + + if (volume_name == NULL) + return NULL; + r = ext2fs_open(volume_name, EXT2_FLAG_SKIP_MMP, 0, 0, manager, &ext2fs); + free(volume_name); + if (r == 0) { + strncpy(label, ext2fs->super->s_volume_name, EXT2_LABEL_LEN); + label[EXT2_LABEL_LEN] = 0; + } + if (ext2fs != NULL) + ext2fs_close(ext2fs); + return (r == 0) ? label : NULL; +} + +BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) +{ + // Mostly taken from mke2fs.conf + const float reserve_ratio = 0.05f; + const ext2fs_default_t ext2fs_default[5] = { + { 3 * MB, 1024, 128, 3}, // "floppy" + { 512 * MB, 1024, 128, 2}, // "small" + { 4 * GB, 4096, 256, 2}, // "default" + { 16 * GB, 4096, 256, 3}, // "big" + { 1024 * TB, 4096, 256, 4} // "huge" + }; + + BOOL ret = FALSE; + char* volume_name = NULL; + int i, count; + struct ext2_super_block features = { 0 }; + io_manager manager = nt_io_manager(); + blk_t journal_size; + blk64_t size = 0, cur; + ext2_filsys ext2fs = NULL; + errcode_t r; + uint8_t* buf = NULL; + +#if defined(RUFUS_TEST) + // Create a disk image file to test + uint8_t zb[1024]; + HANDLE h; + DWORD dwSize; + HCRYPTPROV hCryptProv = 0; + volume_name = strdup(TEST_IMG_PATH); + uprintf("Creating '%s'...", volume_name); + if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) || !CryptGenRandom(hCryptProv, sizeof(zb), zb)) { + uprintf("Failed to randomize buffer - filling with constant value"); + memset(zb, rand(), sizeof(zb)); + } + CryptReleaseContext(hCryptProv, 0); + h = CreateFileU(volume_name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + for (i = 0; i < TEST_IMG_SIZE * sizeof(zb); i++) { + if (!WriteFile(h, zb, sizeof(zb), &dwSize, NULL) || (dwSize != sizeof(zb))) { + uprintf("Write error: %s", WindowsErrorString()); + break; + } + } + CloseHandle(h); +#else + volume_name = AltGetLogicalName(DriveIndex, PartitionOffset, FALSE, TRUE); +#endif + if ((volume_name == NULL) | (strlen(FSName) != 4) || (strncmp(FSName, "ext", 3) != 0)) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; + goto out; + } + if (strchr(volume_name, ' ') != NULL) + uprintf("Notice: Using physical device to access partition data"); + + if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) { + if (strcmp(FSName, FileSystemLabel[FS_EXT4]) == 0) + uprintf("ext4 file system is not supported, defaulting to ext3"); + else + uprintf("Invalid ext file system version requested, defaulting to ext3"); + } + + if ((strcmp(FSName, FileSystemLabel[FS_EXT2]) != 0) && (strcmp(FSName, FileSystemLabel[FS_EXT3]) != 0)) + FSName = FileSystemLabel[FS_EXT3]; + + PrintInfoDebug(0, MSG_222, FSName); + UpdateProgressWithInfoInit(NULL, TRUE); + + // Figure out the volume size and block size + r = ext2fs_get_device_size2(volume_name, KB, &size); + if ((r != 0) || (size == 0)) { + FormatStatus = ext2_last_winerror(ERROR_READ_FAULT); + uprintf("Could not read device size: %s", error_message(r)); + goto out; + } + size *= KB; + for (i = 0; i < ARRAYSIZE(ext2fs_default); i++) { + if (size < ext2fs_default[i].max_size) + break; + } + assert(i < ARRAYSIZE(ext2fs_default)); + // NB: We validated that BlockSize is a power of two in FormatPartition() + if (BlockSize == 0) + BlockSize = ext2fs_default[i].block_size; + size /= BlockSize; + for (features.s_log_block_size = 0; EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE; features.s_log_block_size++) { + if (EXT2_BLOCK_SIZE(&features) == BlockSize) + break; + } + assert(EXT2_BLOCK_SIZE_BITS(&features) <= EXT2_MAX_BLOCK_LOG_SIZE); + + // Set the blocks, reserved blocks and inodes + ext2fs_blocks_count_set(&features, size); + ext2fs_r_blocks_count_set(&features, (blk64_t)(reserve_ratio * size)); + features.s_rev_level = 1; + features.s_inode_size = ext2fs_default[i].inode_size; + features.s_inodes_count = ((ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio) > UINT32_MAX) ? + UINT32_MAX : (uint32_t)(ext2fs_blocks_count(&features) >> ext2fs_default[i].inode_ratio); + uprintf("%d possible inodes out of %lld blocks (block size = %d)", features.s_inodes_count, size, EXT2_BLOCK_SIZE(&features)); + uprintf("%lld blocks (%0.1f%%) reserved for the super user", ext2fs_r_blocks_count(&features), reserve_ratio * 100.0f); + + // Set features + ext2fs_set_feature_xattr(&features); + // ext2fs_set_feature_resize_inode(&features); + ext2fs_set_feature_dir_index(&features); + ext2fs_set_feature_filetype(&features); + ext2fs_set_feature_sparse_super(&features); + ext2fs_set_feature_large_file(&features); + if (FSName[3] != '2') + ext2fs_set_feature_journal(&features); + features.s_backup_bgs[0] = ~0; + features.s_default_mount_opts = EXT2_DEFM_XATTR_USER | EXT2_DEFM_ACL; + + // Now that we have set our base features, initialize a virtual superblock + r = ext2fs_initialize(volume_name, EXT2_FLAG_EXCLUSIVE | EXT2_FLAG_64BITS, &features, manager, &ext2fs); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_INVALID_DATA); + uprintf("Could not initialize %s features: %s", FSName, error_message(r)); + goto out; + } + + // Zero 16 blocks of data from the start of our volume + buf = calloc(16, ext2fs->io->block_size); + assert(buf != NULL); + r = io_channel_write_blk64(ext2fs->io, 0, 16, buf); + safe_free(buf); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("Could not zero %s superblock area: %s", FSName, error_message(r)); + goto out; + } + + // Finish setting up the file system + IGNORE_RETVAL(CoCreateGuid((GUID*)ext2fs->super->s_uuid)); + ext2fs_init_csum_seed(ext2fs); + ext2fs->super->s_def_hash_version = EXT2_HASH_HALF_MD4; + IGNORE_RETVAL(CoCreateGuid((GUID*)ext2fs->super->s_hash_seed)); + ext2fs->super->s_max_mnt_count = -1; + ext2fs->super->s_creator_os = EXT2_OS_WINDOWS; + ext2fs->super->s_errors = EXT2_ERRORS_CONTINUE; + if (Label != NULL) + static_strcpy(ext2fs->super->s_volume_name, Label); + + r = ext2fs_allocate_tables(ext2fs); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_INVALID_DATA); + uprintf("Could not allocate %s tables: %s", FSName, error_message(r)); + goto out; + } + r = ext2fs_convert_subcluster_bitmap(ext2fs, &ext2fs->block_map); + if (r != 0) { + uprintf("Could not set %s cluster bitmap: %s", FSName, error_message(r)); + goto out; + } + + ext2_percent_start = 0.0f; + ext2_percent_share = (FSName[3] == '2') ? 1.0f : 0.5f; + uprintf("Creating %d inode sets: [1 marker = %0.1f set(s)]", ext2fs->group_desc_count, + max((float)ext2fs->group_desc_count / ext2_max_marker, 1.0f)); + for (i = 0; i < (int)ext2fs->group_desc_count; i++) { + if (ext2fs_print_progress((int64_t)i, (int64_t)ext2fs->group_desc_count)) + goto out; + cur = ext2fs_inode_table_loc(ext2fs, i); + count = ext2fs_div_ceil((ext2fs->super->s_inodes_per_group - ext2fs_bg_itable_unused(ext2fs, i)) + * EXT2_BLOCK_SIZE(ext2fs->super), EXT2_BLOCK_SIZE(ext2fs->super)); + r = ext2fs_zero_blocks2(ext2fs, cur, count, &cur, &count); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("\r\nCould not zero inode set at position %llu (%d blocks): %s", cur, count, error_message(r)); + goto out; + } + } + uprintfs("\r\n"); + + // Create root and lost+found dirs + r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_DIR_NOT_ROOT); + uprintf("Failed to create %s root dir: %s", FSName, error_message(r)); + goto out; + } + ext2fs->umask = 077; + r = ext2fs_mkdir(ext2fs, EXT2_ROOT_INO, 0, "lost+found"); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_DIR_NOT_ROOT); + uprintf("Failed to create %s 'lost+found' dir: %s", FSName, error_message(r)); + goto out; + } + + // Create bitmaps + for (i = EXT2_ROOT_INO + 1; i < (int)EXT2_FIRST_INODE(ext2fs->super); i++) + ext2fs_inode_alloc_stats(ext2fs, i, 1); + ext2fs_mark_ib_dirty(ext2fs); + + r = ext2fs_mark_inode_bitmap2(ext2fs->inode_map, EXT2_BAD_INO); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("Could not set inode bitmaps: %s", error_message(r)); + goto out; + } + ext2fs_inode_alloc_stats(ext2fs, EXT2_BAD_INO, 1); + r = ext2fs_update_bb_inode(ext2fs, NULL); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("Could not set inode stats: %s", error_message(r)); + goto out; + } + + if (FSName[3] != '2') { + // Create the journal + ext2_percent_start = 0.5f; + journal_size = ext2fs_default_journal_size(ext2fs_blocks_count(ext2fs->super)); + journal_size /= 2; // That journal init is really killing us! + uprintf("Creating %d journal blocks: [1 marker = %0.1f block(s)]", journal_size, + max((float)journal_size / ext2_max_marker, 1.0f)); + // Even with EXT2_MKJOURNAL_LAZYINIT, this call is absolutely dreadful in terms of speed... + r = ext2fs_add_journal_inode(ext2fs, journal_size, EXT2_MKJOURNAL_NO_MNT_CHECK | ((Flags & FP_QUICK) ? EXT2_MKJOURNAL_LAZYINIT : 0)); + uprintfs("\r\n"); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("Could not create %s journal: %s", FSName, error_message(r)); + goto out; + } + } + + // Create a 'persistence.conf' file if required + if (Flags & FP_CREATE_PERSISTENCE_CONF) { + // You *do* want the LF at the end of the "/ union" line, else Debian Live bails out... + const char* name = "persistence.conf", data[] = "/ union\n"; + int written = 0, fsize = sizeof(data) - 1; + ext2_file_t ext2fd; + ext2_ino_t inode_id; + uint32_t ctime = (uint32_t)time(0); + struct ext2_inode inode = { 0 }; + inode.i_mode = 0100644; + inode.i_links_count = 1; + inode.i_atime = ctime; + inode.i_ctime = ctime; + inode.i_mtime = ctime; + inode.i_size = fsize; + + ext2fs_namei(ext2fs, EXT2_ROOT_INO, EXT2_ROOT_INO, name, &inode_id); + ext2fs_new_inode(ext2fs, EXT2_ROOT_INO, 010755, 0, &inode_id); + ext2fs_link(ext2fs, EXT2_ROOT_INO, name, inode_id, EXT2_FT_REG_FILE); + ext2fs_inode_alloc_stats(ext2fs, inode_id, 1); + ext2fs_write_new_inode(ext2fs, inode_id, &inode); + ext2fs_file_open(ext2fs, inode_id, EXT2_FILE_WRITE, &ext2fd); + if ((ext2fs_file_write(ext2fd, data, fsize, &written) != 0) || (written != fsize)) + uprintf("Error: Could not create '%s' file", name); + else + uprintf("Created '%s' file", name); + ext2fs_file_close(ext2fd); + } + + // Finally we can call close() to get the file system gets created + r = ext2fs_close(ext2fs); + if (r != 0) { + FormatStatus = ext2_last_winerror(ERROR_WRITE_FAULT); + uprintf("Could not create %s volume: %s", FSName, error_message(r)); + goto out; + } + UpdateProgressWithInfo(OP_FORMAT, MSG_217, 100, 100); + uprintf("Done"); + ret = TRUE; + +out: + free(volume_name); + ext2fs_free(ext2fs); + free(buf); + return ret; +} + diff --git a/src/format_fat32.c b/src/format_fat32.c new file mode 100644 index 00000000..5bd289f8 --- /dev/null +++ b/src/format_fat32.c @@ -0,0 +1,445 @@ +/* + * Rufus: The Reliable USB Formatting Utility + * Large FAT32 formatting + * Copyright © 2007-2009 Tom Thornhill/Ridgecrop + * Copyright © 2011-2020 Pete Batard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef _CRTDBG_MAP_ALLOC +#include +#include +#endif + +#include +#include +#include +#include + +#include "rufus.h" +#include "file.h" +#include "drive.h" +#include "format.h" +#include "resource.h" +#include "msapi_utf8.h" +#include "localization.h" + +#define die(msg, err) do { uprintf(msg); \ + FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|err; \ + goto out; } while(0) + +/* Large FAT32 */ +#pragma pack(push, 1) +typedef struct tagFAT_BOOTSECTOR32 +{ + // Common fields. + BYTE sJmpBoot[3]; + BYTE sOEMName[8]; + WORD wBytsPerSec; + BYTE bSecPerClus; + WORD wRsvdSecCnt; + BYTE bNumFATs; + WORD wRootEntCnt; + WORD wTotSec16; // if zero, use dTotSec32 instead + BYTE bMedia; + WORD wFATSz16; + WORD wSecPerTrk; + WORD wNumHeads; + DWORD dHiddSec; + DWORD dTotSec32; + // Fat 32/16 only + DWORD dFATSz32; + WORD wExtFlags; + WORD wFSVer; + DWORD dRootClus; + WORD wFSInfo; + WORD wBkBootSec; + BYTE Reserved[12]; + BYTE bDrvNum; + BYTE Reserved1; + BYTE bBootSig; // == 0x29 if next three fields are ok + DWORD dBS_VolID; + BYTE sVolLab[11]; + BYTE sBS_FilSysType[8]; +} FAT_BOOTSECTOR32; + +typedef struct { + DWORD dLeadSig; // 0x41615252 + BYTE sReserved1[480]; // zeros + DWORD dStrucSig; // 0x61417272 + DWORD dFree_Count; // 0xFFFFFFFF + DWORD dNxt_Free; // 0xFFFFFFFF + BYTE sReserved2[12]; // zeros + DWORD dTrailSig; // 0xAA550000 +} FAT_FSINFO; +#pragma pack(pop) + +/* + * 28.2 CALCULATING THE VOLUME SERIAL NUMBER + * + * For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94 + * seconds. DOS takes the date and time just before it writes it to the + * disk. + * + * Low order word is calculated: Volume Serial Number is: + * Month & Day 12/26 0c1ah + * Sec & Hundredths 41:94 295eh 3578:1d02 + * ----- + * 3578h + * + * High order word is calculated: + * Hours & Minutes 21:55 1537h + * Year 1995 07cbh + * ----- + * 1d02h + */ +static DWORD GetVolumeID(void) +{ + SYSTEMTIME s; + DWORD d; + WORD lo, hi, tmp; + + GetLocalTime(&s); + + lo = s.wDay + (s.wMonth << 8); + tmp = (s.wMilliseconds / 10) + (s.wSecond << 8); + lo += tmp; + + hi = s.wMinute + (s.wHour << 8); + hi += s.wYear; + + d = lo + (hi << 16); + return d; +} + +/* + * Proper computation of FAT size + * See: http://www.syslinux.org/archives/2016-February/024850.html + * and subsequent replies. + */ +static DWORD GetFATSizeSectors(DWORD DskSize, DWORD ReservedSecCnt, DWORD SecPerClus, DWORD NumFATs, DWORD BytesPerSect) +{ + ULONGLONG Numerator, Denominator; + ULONGLONG FatElementSize = 4; + ULONGLONG ReservedClusCnt = 2; + ULONGLONG FatSz; + + Numerator = DskSize - ReservedSecCnt + ReservedClusCnt * SecPerClus; + Denominator = SecPerClus * BytesPerSect / FatElementSize + NumFATs; + FatSz = Numerator / Denominator + 1; // +1 to ensure we are rounded up + + return (DWORD)FatSz; +} + +/* + * Large FAT32 volume formatting from fat32format by Tom Thornhill + * http://www.ridgecrop.demon.co.uk/index.htm?fat32format.htm + */ +BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags) +{ + BOOL r = FALSE; + DWORD i; + HANDLE hLogicalVolume = NULL; + DWORD cbRet; + DISK_GEOMETRY dgDrive; + BYTE geometry_ex[256]; // DISK_GEOMETRY_EX is variable size + PDISK_GEOMETRY_EX xdgDrive = (PDISK_GEOMETRY_EX)(void*)geometry_ex; + PARTITION_INFORMATION piDrive; + PARTITION_INFORMATION_EX xpiDrive; + // Recommended values + DWORD ReservedSectCount = 32; + DWORD NumFATs = 2; + DWORD BackupBootSect = 6; + DWORD VolumeId = 0; // calculated before format + char* VolumeName = NULL; + DWORD BurstSize = 128; // Zero in blocks of 64K typically + + // Calculated later + DWORD FatSize = 0; + DWORD BytesPerSect = 0; + DWORD SectorsPerCluster = 0; + DWORD TotalSectors = 0; + DWORD SystemAreaSize = 0; + DWORD UserAreaSize = 0; + ULONGLONG qTotalSectors = 0; + + // Structures to be written to the disk + FAT_BOOTSECTOR32* pFAT32BootSect = NULL; + FAT_FSINFO* pFAT32FsInfo = NULL; + DWORD* pFirstSectOfFat = NULL; + BYTE* pZeroSect = NULL; + char VolId[12] = "NO NAME "; + + // Debug temp vars + ULONGLONG FatNeeded, ClusterCount; + + if (safe_strncmp(FSName, "FAT", 3) != 0) { + FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_PARAMETER; + goto out; + } + PrintInfoDebug(0, MSG_222, "Large FAT32"); + UpdateProgressWithInfoInit(NULL, TRUE); + VolumeId = GetVolumeID(); + + // Open the drive and lock it + hLogicalVolume = GetLogicalHandle(DriveIndex, PartitionOffset, TRUE, TRUE, FALSE); + if (IS_ERROR(FormatStatus)) + goto out; + if ((hLogicalVolume == INVALID_HANDLE_VALUE) || (hLogicalVolume == NULL)) + die("Invalid logical volume handle", ERROR_INVALID_HANDLE); + + // Try to disappear the volume while we're formatting it + UnmountVolume(hLogicalVolume); + + // Work out drive params + if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dgDrive, + sizeof(dgDrive), &cbRet, NULL)) { + if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, xdgDrive, + sizeof(geometry_ex), &cbRet, NULL)) { + uprintf("IOCTL_DISK_GET_DRIVE_GEOMETRY error: %s", WindowsErrorString()); + die("Failed to get device geometry (both regular and _ex)", ERROR_NOT_SUPPORTED); + } + memcpy(&dgDrive, &xdgDrive->Geometry, sizeof(dgDrive)); + } + if (dgDrive.BytesPerSector < 512) + dgDrive.BytesPerSector = 512; + if (IS_ERROR(FormatStatus)) goto out; + if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &piDrive, + sizeof(piDrive), &cbRet, NULL)) { + if (!DeviceIoControl (hLogicalVolume, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &xpiDrive, + sizeof(xpiDrive), &cbRet, NULL)) { + uprintf("IOCTL_DISK_GET_PARTITION_INFO error: %s", WindowsErrorString()); + die("Failed to get partition info (both regular and _ex)", ERROR_NOT_SUPPORTED); + } + + memset(&piDrive, 0, sizeof(piDrive)); + piDrive.StartingOffset.QuadPart = xpiDrive.StartingOffset.QuadPart; + piDrive.PartitionLength.QuadPart = xpiDrive.PartitionLength.QuadPart; + piDrive.HiddenSectors = (DWORD)(xpiDrive.StartingOffset.QuadPart / dgDrive.BytesPerSector); + } + if (IS_ERROR(FormatStatus)) goto out; + + BytesPerSect = dgDrive.BytesPerSector; + + // Checks on Disk Size + qTotalSectors = piDrive.PartitionLength.QuadPart / dgDrive.BytesPerSector; + // Low end limit - 65536 sectors + if (qTotalSectors < 65536) { + // Most FAT32 implementations would probably mount this volume just fine, + // but the spec says that we shouldn't do this, so we won't + die("This drive is too small for FAT32 - there must be at least 64K clusters", APPERR(ERROR_INVALID_CLUSTER_SIZE)); + } + + if (qTotalSectors >= 0xffffffff) { + // This is a more fundamental limitation on FAT32 - the total sector count in the root dir + // is 32bit. With a bit of creativity, FAT32 could be extended to handle at least 2^28 clusters + // There would need to be an extra field in the FSInfo sector, and the old sector count could + // be set to 0xffffffff. This is non standard though, the Windows FAT driver FASTFAT.SYS won't + // understand this. Perhaps a future version of FAT32 and FASTFAT will handle this. + die("This drive is too big for FAT32 - max 2TB supported", APPERR(ERROR_INVALID_VOLUME_SIZE)); + } + + // coverity[tainted_data] + pFAT32BootSect = (FAT_BOOTSECTOR32*)calloc(BytesPerSect, 1); + pFAT32FsInfo = (FAT_FSINFO*)calloc(BytesPerSect, 1); + pFirstSectOfFat = (DWORD*)calloc(BytesPerSect, 1); + if (!pFAT32BootSect || !pFAT32FsInfo || !pFirstSectOfFat) { + die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); + } + + // fill out the boot sector and fs info + pFAT32BootSect->sJmpBoot[0] = 0xEB; + pFAT32BootSect->sJmpBoot[1] = 0x58; // jmp.s $+0x5a is 0xeb 0x58, not 0xeb 0x5a. Thanks Marco! + pFAT32BootSect->sJmpBoot[2] = 0x90; + memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8); + pFAT32BootSect->wBytsPerSec = (WORD)BytesPerSect; + SectorsPerCluster = ClusterSize / BytesPerSect; + + pFAT32BootSect->bSecPerClus = (BYTE)SectorsPerCluster; + pFAT32BootSect->wRsvdSecCnt = (WORD)ReservedSectCount; + pFAT32BootSect->bNumFATs = (BYTE)NumFATs; + pFAT32BootSect->wRootEntCnt = 0; + pFAT32BootSect->wTotSec16 = 0; + pFAT32BootSect->bMedia = 0xF8; + pFAT32BootSect->wFATSz16 = 0; + pFAT32BootSect->wSecPerTrk = (WORD)dgDrive.SectorsPerTrack; + pFAT32BootSect->wNumHeads = (WORD)dgDrive.TracksPerCylinder; + pFAT32BootSect->dHiddSec = (DWORD)piDrive.HiddenSectors; + TotalSectors = (DWORD)(piDrive.PartitionLength.QuadPart / dgDrive.BytesPerSector); + pFAT32BootSect->dTotSec32 = TotalSectors; + + FatSize = GetFATSizeSectors(pFAT32BootSect->dTotSec32, pFAT32BootSect->wRsvdSecCnt, + pFAT32BootSect->bSecPerClus, pFAT32BootSect->bNumFATs, BytesPerSect); + + pFAT32BootSect->dFATSz32 = FatSize; + pFAT32BootSect->wExtFlags = 0; + pFAT32BootSect->wFSVer = 0; + pFAT32BootSect->dRootClus = 2; + pFAT32BootSect->wFSInfo = 1; + pFAT32BootSect->wBkBootSec = (WORD)BackupBootSect; + pFAT32BootSect->bDrvNum = 0x80; + pFAT32BootSect->Reserved1 = 0; + pFAT32BootSect->bBootSig = 0x29; + + pFAT32BootSect->dBS_VolID = VolumeId; + memcpy(pFAT32BootSect->sVolLab, VolId, 11); + memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8); + ((BYTE*)pFAT32BootSect)[510] = 0x55; + ((BYTE*)pFAT32BootSect)[511] = 0xaa; + + // FATGEN103.DOC says "NOTE: Many FAT documents mistakenly say that this 0xAA55 signature occupies the "last 2 bytes of + // the boot sector". This statement is correct if - and only if - BPB_BytsPerSec is 512. If BPB_BytsPerSec is greater than + // 512, the offsets of these signature bytes do not change (although it is perfectly OK for the last two bytes at the end + // of the boot sector to also contain this signature)." + // + // Windows seems to only check the bytes at offsets 510 and 511. Other OSs might check the ones at the end of the sector, + // so we'll put them there too. + if (BytesPerSect != 512) { + ((BYTE*)pFAT32BootSect)[BytesPerSect - 2] = 0x55; + ((BYTE*)pFAT32BootSect)[BytesPerSect - 1] = 0xaa; + } + + // FSInfo sect + pFAT32FsInfo->dLeadSig = 0x41615252; + pFAT32FsInfo->dStrucSig = 0x61417272; + pFAT32FsInfo->dFree_Count = (DWORD)-1; + pFAT32FsInfo->dNxt_Free = (DWORD)-1; + pFAT32FsInfo->dTrailSig = 0xaa550000; + + // First FAT Sector + pFirstSectOfFat[0] = 0x0ffffff8; // Reserved cluster 1 media id in low byte + pFirstSectOfFat[1] = 0x0fffffff; // Reserved cluster 2 EOC + pFirstSectOfFat[2] = 0x0fffffff; // end of cluster chain for root dir + + // Write boot sector, fats + // Sector 0 Boot Sector + // Sector 1 FSInfo + // Sector 2 More boot code - we write zeros here + // Sector 3 unused + // Sector 4 unused + // Sector 5 unused + // Sector 6 Backup boot sector + // Sector 7 Backup FSInfo sector + // Sector 8 Backup 'more boot code' + // zeroed sectors upto ReservedSectCount + // FAT1 ReservedSectCount to ReservedSectCount + FatSize + // ... + // FATn ReservedSectCount to ReservedSectCount + FatSize + // RootDir - allocated to cluster2 + + UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs * FatSize); + ClusterCount = UserAreaSize / SectorsPerCluster; + + // Sanity check for a cluster count of >2^28, since the upper 4 bits of the cluster values in + // the FAT are reserved. + if (ClusterCount > 0x0FFFFFFF) { + die("This drive has more than 2^28 clusters, try to specify a larger cluster size or use the default", + ERROR_INVALID_CLUSTER_SIZE); + } + + // Sanity check - < 64K clusters means that the volume will be misdetected as FAT16 + if (ClusterCount < 65536) { + die("FAT32 must have at least 65536 clusters, try to specify a smaller cluster size or use the default", + ERROR_INVALID_CLUSTER_SIZE); + } + + // Sanity check, make sure the fat is big enough + // Convert the cluster count into a Fat sector count, and check the fat size value we calculated + // earlier is OK. + FatNeeded = ClusterCount * 4; + FatNeeded += (BytesPerSect - 1); + FatNeeded /= BytesPerSect; + if (FatNeeded > FatSize) { + die("This drive is too big for large FAT32 format", APPERR(ERROR_INVALID_VOLUME_SIZE)); + } + + // Now we're committed - print some info first + uprintf("Size : %s %u sectors", SizeToHumanReadable(piDrive.PartitionLength.QuadPart, TRUE, FALSE), TotalSectors); + uprintf("Cluster size %d bytes, %d Bytes Per Sector", SectorsPerCluster * BytesPerSect, BytesPerSect); + uprintf("Volume ID is %x:%x", VolumeId >> 16, VolumeId & 0xffff); + uprintf("%d Reserved Sectors, %d Sectors per FAT, %d FATs", ReservedSectCount, FatSize, NumFATs); + uprintf("%d Total clusters", ClusterCount); + + // Fix up the FSInfo sector + pFAT32FsInfo->dFree_Count = (UserAreaSize / SectorsPerCluster) - 1; + pFAT32FsInfo->dNxt_Free = 3; // clusters 0-1 reserved, we used cluster 2 for the root dir + + uprintf("%d Free Clusters", pFAT32FsInfo->dFree_Count); + // Work out the Cluster count + + // First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster + SystemAreaSize = ReservedSectCount + (NumFATs * FatSize) + SectorsPerCluster; + uprintf("Clearing out %d sectors for reserved sectors, FATs and root cluster...", SystemAreaSize); + + // Not the most effective, but easy on RAM + pZeroSect = (BYTE*)calloc(BytesPerSect, BurstSize); + if (!pZeroSect) { + die("Failed to allocate memory", ERROR_NOT_ENOUGH_MEMORY); + } + + for (i = 0; i < (SystemAreaSize + BurstSize - 1); i += BurstSize) { + UpdateProgressWithInfo(OP_FORMAT, MSG_217, (uint64_t)i, (uint64_t)(SystemAreaSize + BurstSize)); + CHECK_FOR_USER_CANCEL; + if (write_sectors(hLogicalVolume, BytesPerSect, i, BurstSize, pZeroSect) != (BytesPerSect * BurstSize)) { + die("Error clearing reserved sectors", ERROR_WRITE_FAULT); + } + } + + uprintf ("Initializing reserved sectors and FATs..."); + // Now we should write the boot sector and fsinfo twice, once at 0 and once at the backup boot sect position + for (i = 0; i < 2; i++) { + int SectorStart = (i == 0) ? 0 : BackupBootSect; + write_sectors(hLogicalVolume, BytesPerSect, SectorStart, 1, pFAT32BootSect); + write_sectors(hLogicalVolume, BytesPerSect, SectorStart + 1, 1, pFAT32FsInfo); + } + + // Write the first fat sector in the right places + for (i = 0; i < NumFATs; i++) { + int SectorStart = ReservedSectCount + (i * FatSize); + uprintf("FAT #%d sector at address: %d", i, SectorStart); + write_sectors(hLogicalVolume, BytesPerSect, SectorStart, 1, pFirstSectOfFat); + } + + if (!(Flags & FP_NO_BOOT)) { + // Must do it here, as have issues when trying to write the PBR after a remount + PrintInfoDebug(0, MSG_229); + if (!WritePBR(hLogicalVolume)) { + // Non fatal error, but the drive probably won't boot + uprintf("Could not write partition boot record - drive may not boot..."); + } + } + + // Set the FAT32 volume label + PrintInfoDebug(0, MSG_221); + // Handle must be closed for SetVolumeLabel to work + safe_closehandle(hLogicalVolume); + VolumeName = GetLogicalName(DriveIndex, PartitionOffset, TRUE, TRUE); + if ((VolumeName == NULL) || (!SetVolumeLabelA(VolumeName, Label))) { + uprintf("Could not set label: %s", WindowsErrorString()); + // Non fatal error + } + + uprintf("Format completed."); + r = TRUE; + +out: + safe_free(VolumeName); + safe_closehandle(hLogicalVolume); + safe_free(pFAT32BootSect); + safe_free(pFAT32FsInfo); + safe_free(pFirstSectOfFat); + safe_free(pZeroSect); + return r; +} diff --git a/src/rufus.h b/src/rufus.h index 767c0d8e..5571c423 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -124,6 +124,8 @@ // Bit masks used for the display of additional image options in the UI #define IMOP_WINTOGO 0x01 #define IMOP_PERSISTENCE 0x02 +#define TEST_IMG_PATH "\\??\\C:\\tmp\\disk.img" +#define TEST_IMG_SIZE 4500 // Size in MB #define safe_free(p) do {free((void*)p); p = NULL;} while(0) #define safe_mm_free(p) do {_mm_free((void*)p); p = NULL;} while(0) diff --git a/src/rufus.rc b/src/rufus.rc index cb3f3606..e925d017 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.9.1603" +CAPTION "Rufus 3.9.1604" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,9,1603,0 - PRODUCTVERSION 3,9,1603,0 + FILEVERSION 3,9,1604,0 + PRODUCTVERSION 3,9,1604,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://rufus.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.9.1603" + VALUE "FileVersion", "3.9.1604" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" VALUE "OriginalFilename", "rufus-3.9.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.9.1603" + VALUE "ProductVersion", "3.9.1604" END END BLOCK "VarFileInfo"