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:
		
							parent
							
								
									e807dfb0d3
								
							
						
					
					
						commit
						e74c9cffc9
					
				
					 10 changed files with 973 additions and 868 deletions
				
			
		|  | @ -337,6 +337,8 @@ | ||||||
|     <ClCompile Include="..\src\drive.c" /> |     <ClCompile Include="..\src\drive.c" /> | ||||||
|     <ClCompile Include="..\src\format.c" /> |     <ClCompile Include="..\src\format.c" /> | ||||||
|     <ClCompile Include="..\src\dos.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\icon.c" /> | ||||||
|     <ClCompile Include="..\src\iso.c" /> |     <ClCompile Include="..\src\iso.c" /> | ||||||
|     <ClCompile Include="..\src\localization.c" /> |     <ClCompile Include="..\src\localization.c" /> | ||||||
|  |  | ||||||
|  | @ -81,6 +81,12 @@ | ||||||
|     <ClCompile Include="..\src\ui.c"> |     <ClCompile Include="..\src\ui.c"> | ||||||
|       <Filter>Source Files</Filter> |       <Filter>Source Files</Filter> | ||||||
|     </ClCompile> |     </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> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <ClInclude Include="..\src\rufus.h"> |     <ClInclude Include="..\src\rufus.h"> | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ AM_V_WINDRES   = $(AM_V_WINDRES_$(V)) | ||||||
| %_rc.o: %.rc ../res/loc/embedded.loc | %_rc.o: %.rc ../res/loc/embedded.loc | ||||||
| 	$(AM_V_WINDRES) $(AM_RCFLAGS) -i $< -o $@ | 	$(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 | 	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) \ | 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 | 	-DEXT2_FLAT_INCLUDES=0 | ||||||
|  |  | ||||||
|  | @ -90,7 +90,8 @@ PROGRAMS = $(noinst_PROGRAMS) | ||||||
| am_rufus_OBJECTS = rufus-badblocks.$(OBJEXT) rufus-checksum.$(OBJEXT) \
 | am_rufus_OBJECTS = rufus-badblocks.$(OBJEXT) rufus-checksum.$(OBJEXT) \
 | ||||||
| 	rufus-dev.$(OBJEXT) rufus-dos.$(OBJEXT) \
 | 	rufus-dev.$(OBJEXT) rufus-dos.$(OBJEXT) \
 | ||||||
| 	rufus-dos_locale.$(OBJEXT) rufus-drive.$(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-iso.$(OBJEXT) rufus-localization.$(OBJEXT) \
 | ||||||
| 	rufus-net.$(OBJEXT) rufus-parser.$(OBJEXT) rufus-pki.$(OBJEXT) \
 | 	rufus-net.$(OBJEXT) rufus-parser.$(OBJEXT) rufus-pki.$(OBJEXT) \
 | ||||||
| 	rufus-process.$(OBJEXT) rufus-rufus.$(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_1 = $(WINDRES) | ||||||
| AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) | AM_V_WINDRES_ = $(AM_V_WINDRES_$(AM_DEFAULT_VERBOSITY)) | ||||||
| AM_V_WINDRES = $(AM_V_WINDRES_$(V)) | 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 | 	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) \
 | 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 | 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` | 	$(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 | 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 | 	$(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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										798
									
								
								src/format.c
									
										
									
									
									
								
							
							
						
						
									
										798
									
								
								src/format.c
									
										
									
									
									
								
							|  | @ -1,7 +1,6 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Rufus: The Reliable USB Formatting Utility |  * Rufus: The Reliable USB Formatting Utility | ||||||
|  * Formatting function calls |  * Formatting function calls | ||||||
|  * Copyright © 2007-2009 Tom Thornhill/Ridgecrop |  | ||||||
|  * Copyright © 2011-2020 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 |  * 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; | extern BOOL use_vds; | ||||||
| uint8_t *grub2_buf = NULL, *sec_buf = NULL; | uint8_t *grub2_buf = NULL, *sec_buf = NULL; | ||||||
| long grub2_len; | long grub2_len; | ||||||
| static BOOL WritePBR(HANDLE hLogicalDrive); |  | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Convert the fmifs outputs messages (that use an OEM code page) to UTF-8 |  * 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); | 	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 |  * 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"; | 		return ((boot_type == BT_IMAGE) && HAS_KOLIBRIOS(img_report)) ? "KolibriOS" : "Standard"; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| static BOOL WritePBR(HANDLE hLogicalVolume) | 
 | ||||||
|  | BOOL WritePBR(HANDLE hLogicalVolume) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
| 	FAKE_FD fake_fd = { 0 }; | 	FAKE_FD fake_fd = { 0 }; | ||||||
|  |  | ||||||
							
								
								
									
										68
									
								
								src/format.h
									
										
									
									
									
								
							
							
						
						
									
										68
									
								
								src/format.h
									
										
									
									
									
								
							|  | @ -1,8 +1,7 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Rufus: The Reliable USB Formatting Utility |  * Rufus: The Reliable USB Formatting Utility | ||||||
|  * Formatting function calls |  * Formatting function calls | ||||||
|  * Copyright © 2007-2009 Tom Thornhill/Ridgecrop |  * Copyright © 2011-2020 Pete Batard <pete@akeo.ie> | ||||||
|  * Copyright © 2011-2019 Pete Batard <pete@akeo.ie> |  | ||||||
|  * |  * | ||||||
|  * This program is free software: you can redistribute it and/or modify |  * 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 |  * it under the terms of the GNU General Public License as published by | ||||||
|  | @ -20,8 +19,6 @@ | ||||||
| #include <windows.h> | #include <windows.h> | ||||||
| #include <winioctl.h>	// for MEDIA_TYPE | #include <winioctl.h>	// for MEDIA_TYPE | ||||||
| 
 | 
 | ||||||
| #include "ext2fs/ext2fs.h" |  | ||||||
| 
 |  | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| /* Callback command types (some errorcode were filled from HPUSBFW V2.2.3 and their
 | /* 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
 | 	ULONG                CompressionFlags	// FILE_SYSTEM_PROP_FLAG
 | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| /* Large FAT32 */ | BOOL WritePBR(HANDLE hLogicalDrive); | ||||||
| #pragma pack(push, 1) | BOOL FormatLargeFAT32(DWORD DriveIndex, uint64_t PartitionOffset, DWORD ClusterSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | ||||||
| typedef struct tagFAT_BOOTSECTOR32 | BOOL FormatExtFs(DWORD DriveIndex, uint64_t PartitionOffset, DWORD BlockSize, LPCSTR FSName, LPCSTR Label, DWORD Flags); | ||||||
| { |  | ||||||
| 	// 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); |  | ||||||
|  |  | ||||||
							
								
								
									
										491
									
								
								src/format_ext.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								src/format_ext.c
									
										
									
									
									
										Normal 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
									
								
							
							
						
						
									
										445
									
								
								src/format_fat32.c
									
										
									
									
									
										Normal 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; | ||||||
|  | } | ||||||
|  | @ -124,6 +124,8 @@ | ||||||
| // Bit masks used for the display of additional image options in the UI
 | // Bit masks used for the display of additional image options in the UI
 | ||||||
| #define IMOP_WINTOGO                0x01 | #define IMOP_WINTOGO                0x01 | ||||||
| #define IMOP_PERSISTENCE            0x02 | #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_free(p) do {free((void*)p); p = NULL;} while(0) | ||||||
| #define safe_mm_free(p) do {_mm_free((void*)p); p = NULL;} while(0) | #define safe_mm_free(p) do {_mm_free((void*)p); p = NULL;} while(0) | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							|  | @ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | ||||||
| IDD_DIALOG DIALOGEX 12, 12, 232, 326 | IDD_DIALOG DIALOGEX 12, 12, 232, 326 | ||||||
| STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | ||||||
| EXSTYLE WS_EX_ACCEPTFILES | EXSTYLE WS_EX_ACCEPTFILES | ||||||
| CAPTION "Rufus 3.9.1603" | CAPTION "Rufus 3.9.1604" | ||||||
| FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | ||||||
| BEGIN | BEGIN | ||||||
|     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP |     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP | ||||||
|  | @ -394,8 +394,8 @@ END | ||||||
| // | // | ||||||
| 
 | 
 | ||||||
| VS_VERSION_INFO VERSIONINFO | VS_VERSION_INFO VERSIONINFO | ||||||
|  FILEVERSION 3,9,1603,0 |  FILEVERSION 3,9,1604,0 | ||||||
|  PRODUCTVERSION 3,9,1603,0 |  PRODUCTVERSION 3,9,1604,0 | ||||||
|  FILEFLAGSMASK 0x3fL |  FILEFLAGSMASK 0x3fL | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
|  FILEFLAGS 0x1L |  FILEFLAGS 0x1L | ||||||
|  | @ -413,13 +413,13 @@ BEGIN | ||||||
|             VALUE "Comments", "https://rufus.ie" |             VALUE "Comments", "https://rufus.ie" | ||||||
|             VALUE "CompanyName", "Akeo Consulting" |             VALUE "CompanyName", "Akeo Consulting" | ||||||
|             VALUE "FileDescription", "Rufus" |             VALUE "FileDescription", "Rufus" | ||||||
|             VALUE "FileVersion", "3.9.1603" |             VALUE "FileVersion", "3.9.1604" | ||||||
|             VALUE "InternalName", "Rufus" |             VALUE "InternalName", "Rufus" | ||||||
|             VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" |             VALUE "LegalCopyright", "© 2011-2020 Pete Batard (GPL v3)" | ||||||
|             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" |             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" | ||||||
|             VALUE "OriginalFilename", "rufus-3.9.exe" |             VALUE "OriginalFilename", "rufus-3.9.exe" | ||||||
|             VALUE "ProductName", "Rufus" |             VALUE "ProductName", "Rufus" | ||||||
|             VALUE "ProductVersion", "3.9.1603" |             VALUE "ProductVersion", "3.9.1604" | ||||||
|         END |         END | ||||||
|     END |     END | ||||||
|     BLOCK "VarFileInfo" |     BLOCK "VarFileInfo" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue