1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[misc] move Large FAT32 and ext formatting to their own source

* Also add randomization to test images
This commit is contained in:
Pete Batard 2020-02-10 13:04:50 +00:00
parent 3c9acb0d3c
commit 35af4cbacb
No known key found for this signature in database
GPG key ID: 38E0CF5E69EDD671
10 changed files with 973 additions and 868 deletions

View file

@ -337,6 +337,8 @@
<ClCompile Include="..\src\drive.c" />
<ClCompile Include="..\src\format.c" />
<ClCompile Include="..\src\dos.c" />
<ClCompile Include="..\src\format_ext.c" />
<ClCompile Include="..\src\format_fat32.c" />
<ClCompile Include="..\src\icon.c" />
<ClCompile Include="..\src\iso.c" />
<ClCompile Include="..\src\localization.c" />

View file

@ -81,6 +81,12 @@
<ClCompile Include="..\src\ui.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\format_ext.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\format_fat32.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\rufus.h">

View file

@ -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

View file

@ -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

View file

@ -1,7 +1,6 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Formatting function calls
* Copyright © 2007-2009 Tom Thornhill/Ridgecrop
* Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
*
* 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<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;
}
// Error messages for ext2fs
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;
}
}
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 };

View file

@ -1,8 +1,7 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Formatting function calls
* Copyright © 2007-2009 Tom Thornhill/Ridgecrop
* Copyright © 2011-2019 Pete Batard <pete@akeo.ie>
* Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
*
* 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 <windows.h>
#include <winioctl.h> // 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);

491
src/format_ext.c Normal file
View file

@ -0,0 +1,491 @@
/*
* Rufus: The Reliable USB Formatting Utility
* extfs formatting
* Copyright © 2019-2020 Pete Batard <pete@akeo.ie>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#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;
}

445
src/format_fat32.c Normal file
View file

@ -0,0 +1,445 @@
/*
* Rufus: The Reliable USB Formatting Utility
* Large FAT32 formatting
* Copyright © 2007-2009 Tom Thornhill/Ridgecrop
* Copyright © 2011-2020 Pete Batard <pete@akeo.ie>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#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;
}

View file

@ -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)

View file

@ -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"