-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
format.c
3061 lines (2812 loc) · 109 KB
/
format.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Rufus: The Reliable USB Formatting Utility
* Formatting function calls
* Copyright © 2007-2009 Tom Thornhill/Ridgecrop
* Copyright © 2011-2019 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 <windowsx.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>
#include <stddef.h>
#include <ctype.h>
#include <locale.h>
#include <assert.h>
#include <vds.h>
#include "rufus.h"
#include "missing.h"
#include "resource.h"
#include "settings.h"
#include "msapi_utf8.h"
#include "localization.h"
#include "br.h"
#include "fat16.h"
#include "fat32.h"
#include "ntfs.h"
#include "partition_info.h"
#include "file.h"
#include "drive.h"
#include "format.h"
#include "badblocks.h"
#include "bled/bled.h"
#include "../res/grub/grub_version.h"
/*
* Globals
*/
const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "UDF", "exFAT", "ReFS", "ext2", "ext3", "ext4" };
DWORD FormatStatus = 0, LastWriteError = 0;
badblocks_report report = { 0 };
static float format_percent = 0.0f;
static int task_number = 0;
extern const int nb_steps[FS_MAX];
extern uint32_t dur_mins, dur_secs;
extern uint32_t wim_nb_files, wim_proc_files, wim_extra_files;
static int actual_fs_type, wintogo_index = -1, wininst_index = 0;
extern BOOL force_large_fat32, enable_ntfs_compression, lock_drive, zero_drive, fast_zeroing, enable_file_indexing, write_as_image;
extern BOOL use_vds;
uint8_t *grub2_buf = NULL;
long grub2_len;
static BOOL WritePBR(HANDLE hLogicalDrive);
/*
* Convert the fmifs outputs messages (that use an OEM code page) to UTF-8
*/
static void OutputUTF8Message(const char* src)
{
int len;
wchar_t* wdst = NULL;
if (src == NULL)
goto out;
len = (int)safe_strlen(src);
while ((len > 0) && ((src[len-1] == 0x0A) || (src[len-1] == 0x0D) || (src[len-1] == ' ')))
len--;
if (len == 0)
goto out;
len = MultiByteToWideChar(CP_OEMCP, 0, src, len, NULL, 0);
if (len == 0)
goto out;
wdst = (wchar_t*)calloc(len+1, sizeof(wchar_t));
if ((wdst == NULL) || (MultiByteToWideChar(CP_OEMCP, 0, src, len, wdst, len+1) == 0))
goto out;
uprintf("%S", wdst);
out:
safe_free(wdst);
}
/*
* FormatEx callback. Return FALSE to halt operations
*/
static BOOLEAN __stdcall FormatExCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
{
char percent_str[8];
if (IS_ERROR(FormatStatus))
return FALSE;
assert((actual_fs_type >= 0) && (actual_fs_type < FS_MAX));
switch(Command) {
case FCC_PROGRESS:
static_sprintf(percent_str, "%lu%%", *((DWORD*)pData));
PrintInfo(0, MSG_217, percent_str);
UpdateProgress(OP_FORMAT, 1.0f * (*((DWORD*)pData)));
break;
case FCC_STRUCTURE_PROGRESS: // No progress on quick format
if (task_number < nb_steps[actual_fs_type] - 1) {
if (task_number == 0)
uprintf("Creating file system...");
PrintInfo(0, MSG_218, ++task_number, nb_steps[actual_fs_type]);
format_percent += 100.0f / (1.0f * nb_steps[actual_fs_type]);
UpdateProgress(OP_CREATE_FS, format_percent);
}
break;
case FCC_DONE:
PrintInfo(0, MSG_218, nb_steps[actual_fs_type], nb_steps[actual_fs_type]);
UpdateProgress(OP_CREATE_FS, 100.0f);
if(*(BOOLEAN*)pData == FALSE) {
uprintf("Error while formatting");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_GEN_FAILURE;
}
break;
case FCC_DONE_WITH_STRUCTURE:
break;
case FCC_INCOMPATIBLE_FILE_SYSTEM:
uprintf("Incompatible File System");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INCOMPATIBLE_FS);
break;
case FCC_ACCESS_DENIED:
uprintf("Access denied");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_ACCESS_DENIED;
break;
case FCC_MEDIA_WRITE_PROTECTED:
uprintf("Media is write protected");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_PROTECT;
break;
case FCC_VOLUME_IN_USE:
uprintf("Volume is in use");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_DEVICE_IN_USE;
break;
case FCC_DEVICE_NOT_READY:
uprintf("The device is not ready");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_READY;
break;
case FCC_CANT_QUICK_FORMAT:
uprintf("Cannot quick format this volume");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_QUICK_FORMAT);
break;
case FCC_BAD_LABEL:
uprintf("Bad label");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_LABEL_TOO_LONG;
break;
case FCC_OUTPUT:
OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
break;
case FCC_CLUSTER_SIZE_TOO_BIG:
case FCC_CLUSTER_SIZE_TOO_SMALL:
uprintf("Unsupported cluster size");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_CLUSTER_SIZE);
break;
case FCC_VOLUME_TOO_BIG:
case FCC_VOLUME_TOO_SMALL:
uprintf("Volume is too %s", (Command == FCC_VOLUME_TOO_BIG)?"big":"small");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_INVALID_VOLUME_SIZE);
break;
case FCC_NO_MEDIA_IN_DRIVE:
uprintf("No media in drive");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NO_MEDIA_IN_DRIVE;
break;
default:
uprintf("FormatExCallback: Received unhandled command 0x02%X - aborting", Command);
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_NOT_SUPPORTED;
break;
}
return (!IS_ERROR(FormatStatus));
}
/*
* Chkdsk callback. Return FALSE to halt operations
*/
static BOOLEAN __stdcall ChkdskCallback(FILE_SYSTEM_CALLBACK_COMMAND Command, DWORD Action, PVOID pData)
{
DWORD* percent;
if (IS_ERROR(FormatStatus))
return FALSE;
switch (Command) {
case FCC_PROGRESS:
case FCC_CHECKDISK_PROGRESS:
percent = (DWORD*)pData;
PrintInfo(0, MSG_219, *percent);
break;
case FCC_DONE:
if (*(BOOLEAN*)pData == FALSE) {
uprintf("Error while checking disk");
return FALSE;
}
break;
case FCC_UNKNOWN1A:
case FCC_DONE_WITH_STRUCTURE:
// Silence these specific calls
break;
case FCC_INCOMPATIBLE_FILE_SYSTEM:
uprintf("Incompatible File System");
return FALSE;
case FCC_ACCESS_DENIED:
uprintf("Access denied");
return FALSE;
case FCC_MEDIA_WRITE_PROTECTED:
uprintf("Media is write protected");
return FALSE;
case FCC_VOLUME_IN_USE:
uprintf("Volume is in use");
return FALSE;
case FCC_OUTPUT:
OutputUTF8Message(((PTEXTOUTPUT)pData)->Output);
break;
case FCC_NO_MEDIA_IN_DRIVE:
uprintf("No media in drive");
return FALSE;
case FCC_READ_ONLY_MODE:
uprintf("Media has been switched to read-only - Leaving checkdisk");
break;
default:
uprintf("ChkdskExCallback: received unhandled command %X", Command);
// Assume the command isn't an error
break;
}
return TRUE;
}
/*
* Converts an UTF-8 label to a valid FAT/NTFS one
* TODO: Use IVdsService::QueryFileSystemTypes -> VDS_FILE_SYSTEM_TYPE_PROP
* to get the list of unauthorised and max length for each FS.
*/
static void ToValidLabel(char* Label, BOOL bFAT)
{
size_t i, j, k;
BOOL found;
WCHAR unauthorized[] = L"*?,;:/\\|+=<>[]\"";
WCHAR to_underscore[] = L"\t.";
WCHAR *wLabel = utf8_to_wchar(Label);
if (wLabel == NULL)
return;
for (i = 0, k = 0; i < wcslen(wLabel); i++) {
if (bFAT) { // NTFS does allows all the FAT unauthorized above
found = FALSE;
for (j = 0; j < wcslen(unauthorized); j++) {
if (wLabel[i] == unauthorized[j]) {
found = TRUE;
break;
}
}
// A FAT label that contains extended chars will be rejected
if (wLabel[i] >= 0x80) {
wLabel[k++] = L'_';
found = TRUE;
}
if (found)
continue;
}
found = FALSE;
for (j = 0; j < wcslen(to_underscore); j++) {
if (wLabel[i] == to_underscore[j]) {
wLabel[k++] = '_';
found = TRUE;
break;
}
}
if (found)
continue;
wLabel[k++] = bFAT ? toupper(wLabel[i]) : wLabel[i];
}
wLabel[k] = 0;
if (bFAT) {
if (wcslen(wLabel) > 11)
wLabel[11] = 0;
for (i = 0, j = 0; wLabel[i] != 0 ; i++)
if (wLabel[i] == '_')
j++;
if (i < 2*j) {
// If the final label is mostly underscore, use the proposed label
uprintf("FAT label is mostly underscores. Using '%s' label instead.", SelectedDrive.proposed_label);
for(i = 0; SelectedDrive.proposed_label[i] != 0; i++)
wLabel[i] = SelectedDrive.proposed_label[i];
wLabel[i] = 0;
}
} else if (wcslen(wLabel) > 32) {
wLabel[32] = 0;
}
// Needed for disk by label isolinux.cfg workaround
wchar_to_utf8_no_alloc(wLabel, img_report.usb_label, sizeof(img_report.usb_label));
safe_strcpy(Label, strlen(Label) + 1, img_report.usb_label);
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 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));