xref: /reactos/base/setup/lib/fsutil.c (revision d7c1d220)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Filesystem Format and ChkDsk support functions
5  * COPYRIGHT:   Copyright 2003-2019 Casper S. Hornstrup <chorns@users.sourceforge.net>
6  *              Copyright 2017-2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7  */
8 
9 //
10 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c
11 // for how to get FS providers in a dynamic way. In the (near) future we may
12 // consider merging some of this code with us into a fmifs / fsutil / fslib library...
13 //
14 
15 /* INCLUDES *****************************************************************/
16 
17 #include "precomp.h"
18 
19 #include "partlist.h"
20 #include "fsrec.h"
21 #include "bootcode.h"
22 #include "fsutil.h"
23 
24 #include <fslib/vfatlib.h>
25 #include <fslib/btrfslib.h>
26 // #include <fslib/ext2lib.h>
27 // #include <fslib/ntfslib.h>
28 
29 #define NDEBUG
30 #include <debug.h>
31 
32 
33 /* TYPEDEFS *****************************************************************/
34 
35 #include <pshpack1.h>
36 typedef struct _FAT_BOOTSECTOR
37 {
38     UCHAR       JumpBoot[3];                // Jump instruction to boot code
39     CHAR        OemName[8];                 // "MSWIN4.1" for MS formatted volumes
40     USHORT      BytesPerSector;             // Bytes per sector
41     UCHAR       SectorsPerCluster;          // Number of sectors in a cluster
42     USHORT      ReservedSectors;            // Reserved sectors, usually 1 (the bootsector)
43     UCHAR       NumberOfFats;               // Number of FAT tables
44     USHORT      RootDirEntries;             // Number of root directory entries (fat12/16)
45     USHORT      TotalSectors;               // Number of total sectors on the drive, 16-bit
46     UCHAR       MediaDescriptor;            // Media descriptor byte
47     USHORT      SectorsPerFat;              // Sectors per FAT table (fat12/16)
48     USHORT      SectorsPerTrack;            // Number of sectors in a track
49     USHORT      NumberOfHeads;              // Number of heads on the disk
50     ULONG       HiddenSectors;              // Hidden sectors (sectors before the partition start like the partition table)
51     ULONG       TotalSectorsBig;            // This field is the new 32-bit total count of sectors on the volume
52     UCHAR       DriveNumber;                // Int 0x13 drive number (e.g. 0x80)
53     UCHAR       Reserved1;                  // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
54     UCHAR       BootSignature;              // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
55     ULONG       VolumeSerialNumber;         // Volume serial number
56     CHAR        VolumeLabel[11];            // Volume label. This field matches the 11-byte volume label recorded in the root directory
57     CHAR        FileSystemType[8];          // One of the strings "FAT12   ", "FAT16   ", or "FAT     "
58 
59     UCHAR       BootCodeAndData[448];       // The remainder of the boot sector
60 
61     USHORT      BootSectorMagic;            // 0xAA55
62 
63 } FAT_BOOTSECTOR, *PFAT_BOOTSECTOR;
64 C_ASSERT(sizeof(FAT_BOOTSECTOR) == FAT_BOOTSECTOR_SIZE);
65 
66 typedef struct _FAT32_BOOTSECTOR
67 {
68     UCHAR       JumpBoot[3];                // Jump instruction to boot code
69     CHAR        OemName[8];                 // "MSWIN4.1" for MS formatted volumes
70     USHORT      BytesPerSector;             // Bytes per sector
71     UCHAR       SectorsPerCluster;          // Number of sectors in a cluster
72     USHORT      ReservedSectors;            // Reserved sectors, usually 1 (the bootsector)
73     UCHAR       NumberOfFats;               // Number of FAT tables
74     USHORT      RootDirEntries;             // Number of root directory entries (fat12/16)
75     USHORT      TotalSectors;               // Number of total sectors on the drive, 16-bit
76     UCHAR       MediaDescriptor;            // Media descriptor byte
77     USHORT      SectorsPerFat;              // Sectors per FAT table (fat12/16)
78     USHORT      SectorsPerTrack;            // Number of sectors in a track
79     USHORT      NumberOfHeads;              // Number of heads on the disk
80     ULONG       HiddenSectors;              // Hidden sectors (sectors before the partition start like the partition table)
81     ULONG       TotalSectorsBig;            // This field is the new 32-bit total count of sectors on the volume
82     ULONG       SectorsPerFatBig;           // This field is the FAT32 32-bit count of sectors occupied by ONE FAT. BPB_FATSz16 must be 0
83     USHORT      ExtendedFlags;              // Extended flags (fat32)
84     USHORT      FileSystemVersion;          // File system version (fat32)
85     ULONG       RootDirStartCluster;        // Starting cluster of the root directory (fat32)
86     USHORT      FsInfo;                     // Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1.
87     USHORT      BackupBootSector;           // If non-zero, indicates the sector number in the reserved area of the volume of a copy of the boot record. Usually 6.
88     UCHAR       Reserved[12];               // Reserved for future expansion
89     UCHAR       DriveNumber;                // Int 0x13 drive number (e.g. 0x80)
90     UCHAR       Reserved1;                  // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
91     UCHAR       BootSignature;              // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
92     ULONG       VolumeSerialNumber;         // Volume serial number
93     CHAR        VolumeLabel[11];            // Volume label. This field matches the 11-byte volume label recorded in the root directory
94     CHAR        FileSystemType[8];          // Always set to the string "FAT32   "
95 
96     UCHAR       BootCodeAndData[420];       // The remainder of the boot sector
97 
98     USHORT      BootSectorMagic;            // 0xAA55
99 
100 } FAT32_BOOTSECTOR, *PFAT32_BOOTSECTOR;
101 C_ASSERT(sizeof(FAT32_BOOTSECTOR) == FAT32_BOOTSECTOR_SIZE);
102 
103 typedef struct _BTRFS_BOOTSECTOR
104 {
105     UCHAR JumpBoot[3];
106     UCHAR ChunkMapSize;
107     UCHAR BootDrive;
108     ULONGLONG PartitionStartLBA;
109     UCHAR Fill[1521]; // 1536 - 15
110     USHORT BootSectorMagic;
111 } BTRFS_BOOTSECTOR, *PBTRFS_BOOTSECTOR;
112 C_ASSERT(sizeof(BTRFS_BOOTSECTOR) == BTRFS_BOOTSECTOR_SIZE);
113 
114 typedef struct _NTFS_BOOTSECTOR
115 {
116     UCHAR Jump[3];
117     UCHAR OEMID[8];
118     USHORT BytesPerSector;
119     UCHAR SectorsPerCluster;
120     UCHAR Unused0[7];
121     UCHAR MediaId;
122     UCHAR Unused1[2];
123     USHORT SectorsPerTrack;
124     USHORT Heads;
125     UCHAR Unused2[4];
126     UCHAR Unused3[4];
127     USHORT Unknown[2];
128     ULONGLONG SectorCount;
129     ULONGLONG MftLocation;
130     ULONGLONG MftMirrLocation;
131     CHAR ClustersPerMftRecord;
132     UCHAR Unused4[3];
133     CHAR ClustersPerIndexRecord;
134     UCHAR Unused5[3];
135     ULONGLONG SerialNumber;
136     UCHAR Checksum[4];
137     UCHAR BootStrap[426];
138     USHORT EndSector;
139     UCHAR BootCodeAndData[7680]; // The remainder of the boot sector (8192 - 512)
140 } NTFS_BOOTSECTOR, *PNTFS_BOOTSECTOR;
141 C_ASSERT(sizeof(NTFS_BOOTSECTOR) == NTFS_BOOTSECTOR_SIZE);
142 
143 // TODO: Add more bootsector structures!
144 
145 #include <poppack.h>
146 
147 
148 /* LOCALS *******************************************************************/
149 
150 /** IFS_PROVIDER **/
151 typedef struct _FILE_SYSTEM
152 {
153     PCWSTR FileSystemName;
154     PULIB_FORMAT FormatFunc;
155     PULIB_CHKDSK ChkdskFunc;
156 } FILE_SYSTEM, *PFILE_SYSTEM;
157 
158 /* The list of file systems on which we can install ReactOS */
159 static FILE_SYSTEM RegisteredFileSystems[] =
160 {
161     /* NOTE: The FAT formatter will automatically
162      * determine whether to use FAT12/16 or FAT32. */
163     { L"FAT"  , VfatFormat, VfatChkdsk },
164     { L"FAT32", VfatFormat, VfatChkdsk },
165 #if 0
166     { L"FATX" , VfatxFormat, VfatxChkdsk },
167     { L"NTFS" , NtfsFormat, NtfsChkdsk },
168 #endif
169     { L"BTRFS", BtrfsFormat, BtrfsChkdsk },
170 #if 0
171     { L"EXT2" , Ext2Format, Ext2Chkdsk },
172     { L"EXT3" , Ext2Format, Ext2Chkdsk },
173     { L"EXT4" , Ext2Format, Ext2Chkdsk },
174 #endif
175 };
176 
177 
178 /* FUNCTIONS ****************************************************************/
179 
180 /** QueryAvailableFileSystemFormat() **/
181 BOOLEAN
182 NTAPI
GetRegisteredFileSystems(IN ULONG Index,OUT PCWSTR * FileSystemName)183 GetRegisteredFileSystems(
184     IN ULONG Index,
185     OUT PCWSTR* FileSystemName)
186 {
187     if (Index >= ARRAYSIZE(RegisteredFileSystems))
188         return FALSE;
189 
190     *FileSystemName = RegisteredFileSystems[Index].FileSystemName;
191 
192     return TRUE;
193 }
194 
195 
196 /** GetProvider() **/
197 static PFILE_SYSTEM
GetFileSystemByName(IN PCWSTR FileSystemName)198 GetFileSystemByName(
199     IN PCWSTR FileSystemName)
200 {
201 #if 0 // Reenable when the list of registered FSes will again be dynamic
202 
203     PLIST_ENTRY ListEntry;
204     PFILE_SYSTEM_ITEM Item;
205 
206     ListEntry = List->ListHead.Flink;
207     while (ListEntry != &List->ListHead)
208     {
209         Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
210         if (Item->FileSystemName &&
211             (_wcsicmp(FileSystemName, Item->FileSystemName) == 0))
212         {
213             return Item;
214         }
215 
216         ListEntry = ListEntry->Flink;
217     }
218 
219 #else
220 
221     ULONG Count = ARRAYSIZE(RegisteredFileSystems);
222     PFILE_SYSTEM FileSystems = RegisteredFileSystems;
223 
224     ASSERT(FileSystems && Count != 0);
225 
226     while (Count--)
227     {
228         if (FileSystems->FileSystemName &&
229             (_wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0))
230         {
231             return FileSystems;
232         }
233 
234         ++FileSystems;
235     }
236 
237 #endif
238 
239     return NULL;
240 }
241 
242 
243 /** ChkdskEx() **/
244 NTSTATUS
245 NTAPI
ChkdskFileSystem_UStr(_In_ PUNICODE_STRING DriveRoot,_In_ PCWSTR FileSystemName,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)246 ChkdskFileSystem_UStr(
247     _In_ PUNICODE_STRING DriveRoot,
248     _In_ PCWSTR FileSystemName,
249     _In_ BOOLEAN FixErrors,
250     _In_ BOOLEAN Verbose,
251     _In_ BOOLEAN CheckOnlyIfDirty,
252     _In_ BOOLEAN ScanDrive,
253     _In_opt_ PFMIFSCALLBACK Callback)
254 {
255     PFILE_SYSTEM FileSystem;
256     NTSTATUS Status;
257     BOOLEAN Success;
258 
259     FileSystem = GetFileSystemByName(FileSystemName);
260 
261     if (!FileSystem || !FileSystem->ChkdskFunc)
262     {
263         // Success = FALSE;
264         // Callback(DONE, 0, &Success);
265         return STATUS_NOT_SUPPORTED;
266     }
267 
268     Status = STATUS_SUCCESS;
269     Success = FileSystem->ChkdskFunc(DriveRoot,
270                                      Callback,
271                                      FixErrors,
272                                      Verbose,
273                                      CheckOnlyIfDirty,
274                                      ScanDrive,
275                                      NULL,
276                                      NULL,
277                                      NULL,
278                                      NULL,
279                                      (PULONG)&Status);
280     if (!Success)
281         DPRINT1("ChkdskFunc() failed with Status 0x%lx\n", Status);
282 
283     // Callback(DONE, 0, &Success);
284 
285     return Status;
286 }
287 
288 NTSTATUS
289 NTAPI
ChkdskFileSystem(_In_ PCWSTR DriveRoot,_In_ PCWSTR FileSystemName,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)290 ChkdskFileSystem(
291     _In_ PCWSTR DriveRoot,
292     _In_ PCWSTR FileSystemName,
293     _In_ BOOLEAN FixErrors,
294     _In_ BOOLEAN Verbose,
295     _In_ BOOLEAN CheckOnlyIfDirty,
296     _In_ BOOLEAN ScanDrive,
297     _In_opt_ PFMIFSCALLBACK Callback)
298 {
299     UNICODE_STRING DriveRootU;
300 
301     RtlInitUnicodeString(&DriveRootU, DriveRoot);
302     return ChkdskFileSystem_UStr(&DriveRootU,
303                                  FileSystemName,
304                                  FixErrors,
305                                  Verbose,
306                                  CheckOnlyIfDirty,
307                                  ScanDrive,
308                                  Callback);
309 }
310 
311 
312 /** FormatEx() **/
313 NTSTATUS
314 NTAPI
FormatFileSystem_UStr(_In_ PUNICODE_STRING DriveRoot,_In_ PCWSTR FileSystemName,_In_ FMIFS_MEDIA_FLAG MediaFlag,_In_opt_ PUNICODE_STRING Label,_In_ BOOLEAN QuickFormat,_In_ ULONG ClusterSize,_In_opt_ PFMIFSCALLBACK Callback)315 FormatFileSystem_UStr(
316     _In_ PUNICODE_STRING DriveRoot,
317     _In_ PCWSTR FileSystemName,
318     _In_ FMIFS_MEDIA_FLAG MediaFlag,
319     _In_opt_ PUNICODE_STRING Label,
320     _In_ BOOLEAN QuickFormat,
321     _In_ ULONG ClusterSize,
322     _In_opt_ PFMIFSCALLBACK Callback)
323 {
324     PFILE_SYSTEM FileSystem;
325     BOOLEAN Success;
326     BOOLEAN BackwardCompatible = FALSE; // Default to latest FS versions.
327     MEDIA_TYPE MediaType;
328 
329     FileSystem = GetFileSystemByName(FileSystemName);
330 
331     if (!FileSystem || !FileSystem->FormatFunc)
332     {
333         // Success = FALSE;
334         // Callback(DONE, 0, &Success);
335         return STATUS_NOT_SUPPORTED;
336     }
337 
338     /* Set the BackwardCompatible flag in case we format with older FAT12/16 */
339     if (_wcsicmp(FileSystemName, L"FAT") == 0)
340         BackwardCompatible = TRUE;
341     // else if (_wcsicmp(FileSystemName, L"FAT32") == 0)
342         // BackwardCompatible = FALSE;
343 
344     /* Convert the FMIFS MediaFlag to a NT MediaType */
345     // FIXME: Actually covert all the possible flags.
346     switch (MediaFlag)
347     {
348     case FMIFS_FLOPPY:
349         MediaType = F5_320_1024; // FIXME: This is hardfixed!
350         break;
351     case FMIFS_REMOVABLE:
352         MediaType = RemovableMedia;
353         break;
354     case FMIFS_HARDDISK:
355         MediaType = FixedMedia;
356         break;
357     default:
358         DPRINT1("Unknown FMIFS MediaFlag %d, converting 1-to-1 to NT MediaType\n",
359                 MediaFlag);
360         MediaType = (MEDIA_TYPE)MediaFlag;
361         break;
362     }
363 
364     Success = FileSystem->FormatFunc(DriveRoot,
365                                      Callback,
366                                      QuickFormat,
367                                      BackwardCompatible,
368                                      MediaType,
369                                      Label,
370                                      ClusterSize);
371     if (!Success)
372         DPRINT1("FormatFunc() failed\n");
373 
374     // Callback(DONE, 0, &Success);
375 
376     return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
377 }
378 
379 NTSTATUS
380 NTAPI
FormatFileSystem(_In_ PCWSTR DriveRoot,_In_ PCWSTR FileSystemName,_In_ FMIFS_MEDIA_FLAG MediaFlag,_In_opt_ PCWSTR Label,_In_ BOOLEAN QuickFormat,_In_ ULONG ClusterSize,_In_opt_ PFMIFSCALLBACK Callback)381 FormatFileSystem(
382     _In_ PCWSTR DriveRoot,
383     _In_ PCWSTR FileSystemName,
384     _In_ FMIFS_MEDIA_FLAG MediaFlag,
385     _In_opt_ PCWSTR Label,
386     _In_ BOOLEAN QuickFormat,
387     _In_ ULONG ClusterSize,
388     _In_opt_ PFMIFSCALLBACK Callback)
389 {
390     UNICODE_STRING DriveRootU;
391     UNICODE_STRING LabelU;
392 
393     RtlInitUnicodeString(&DriveRootU, DriveRoot);
394     RtlInitUnicodeString(&LabelU, Label);
395 
396     return FormatFileSystem_UStr(&DriveRootU,
397                                  FileSystemName,
398                                  MediaFlag,
399                                  &LabelU,
400                                  QuickFormat,
401                                  ClusterSize,
402                                  Callback);
403 }
404 
405 
406 //
407 // Bootsector routines
408 //
409 
410 NTSTATUS
InstallFatBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)411 InstallFatBootCode(
412     IN PCWSTR SrcPath,          // FAT12/16 bootsector source file (on the installation medium)
413     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
414     IN HANDLE RootPartition)    // Partition holding the (old) FAT12/16 information
415 {
416     NTSTATUS Status;
417     UNICODE_STRING Name;
418     IO_STATUS_BLOCK IoStatusBlock;
419     LARGE_INTEGER FileOffset;
420     BOOTCODE OrigBootSector = {0};
421     BOOTCODE NewBootSector  = {0};
422 
423     /* Allocate and read the current original partition bootsector */
424     Status = ReadBootCodeByHandle(&OrigBootSector,
425                                   RootPartition,
426                                   FAT_BOOTSECTOR_SIZE);
427     if (!NT_SUCCESS(Status))
428         return Status;
429 
430     /* Allocate and read the new bootsector from SrcPath */
431     RtlInitUnicodeString(&Name, SrcPath);
432     Status = ReadBootCodeFromFile(&NewBootSector,
433                                   &Name,
434                                   FAT_BOOTSECTOR_SIZE);
435     if (!NT_SUCCESS(Status))
436     {
437         FreeBootCode(&OrigBootSector);
438         return Status;
439     }
440 
441     /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */
442     RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName,
443                   &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
444                   FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) -
445                   FIELD_OFFSET(FAT_BOOTSECTOR, OemName));
446 
447     /* Free the original bootsector */
448     FreeBootCode(&OrigBootSector);
449 
450     /* Write the new bootsector to DstPath */
451     FileOffset.QuadPart = 0ULL;
452     Status = NtWriteFile(DstPath,
453                          NULL,
454                          NULL,
455                          NULL,
456                          &IoStatusBlock,
457                          NewBootSector.BootCode,
458                          NewBootSector.Length,
459                          &FileOffset,
460                          NULL);
461 
462     /* Free the new bootsector */
463     FreeBootCode(&NewBootSector);
464 
465     return Status;
466 }
467 
468 NTSTATUS
InstallFat32BootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)469 InstallFat32BootCode(
470     IN PCWSTR SrcPath,          // FAT32 bootsector source file (on the installation medium)
471     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
472     IN HANDLE RootPartition)    // Partition holding the (old) FAT32 information
473 {
474     NTSTATUS Status;
475     UNICODE_STRING Name;
476     IO_STATUS_BLOCK IoStatusBlock;
477     LARGE_INTEGER FileOffset;
478     USHORT BackupBootSector = 0;
479     BOOTCODE OrigBootSector = {0};
480     BOOTCODE NewBootSector  = {0};
481 
482     /* Allocate and read the current original partition bootsector */
483     Status = ReadBootCodeByHandle(&OrigBootSector,
484                                   RootPartition,
485                                   FAT32_BOOTSECTOR_SIZE);
486     if (!NT_SUCCESS(Status))
487         return Status;
488 
489     /* Allocate and read the new bootsector (2 sectors) from SrcPath */
490     RtlInitUnicodeString(&Name, SrcPath);
491     Status = ReadBootCodeFromFile(&NewBootSector,
492                                   &Name,
493                                   2 * FAT32_BOOTSECTOR_SIZE);
494     if (!NT_SUCCESS(Status))
495     {
496         FreeBootCode(&OrigBootSector);
497         return Status;
498     }
499 
500     /* Adjust the bootsector (copy a part of the FAT32 BPB) */
501     RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName,
502                   &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
503                   FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) -
504                   FIELD_OFFSET(FAT32_BOOTSECTOR, OemName));
505 
506     /*
507      * We know we copy the boot code to a file only when DstPath != RootPartition,
508      * otherwise the boot code is copied to the specified root partition.
509      */
510     if (DstPath != RootPartition)
511     {
512         /* Copy to a file: Disable the backup bootsector */
513         ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0;
514     }
515     else
516     {
517         /* Copy to a disk: Get the location of the backup bootsector */
518         BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector;
519     }
520 
521     /* Free the original bootsector */
522     FreeBootCode(&OrigBootSector);
523 
524     /* Write the first sector of the new bootcode to DstPath sector 0 */
525     FileOffset.QuadPart = 0ULL;
526     Status = NtWriteFile(DstPath,
527                          NULL,
528                          NULL,
529                          NULL,
530                          &IoStatusBlock,
531                          NewBootSector.BootCode,
532                          FAT32_BOOTSECTOR_SIZE,
533                          &FileOffset,
534                          NULL);
535     if (!NT_SUCCESS(Status))
536     {
537         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
538         FreeBootCode(&NewBootSector);
539         return Status;
540     }
541 
542     if (DstPath == RootPartition)
543     {
544         /* Copy to a disk: Write the backup bootsector */
545         if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF))
546         {
547             FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE);
548             Status = NtWriteFile(DstPath,
549                                  NULL,
550                                  NULL,
551                                  NULL,
552                                  &IoStatusBlock,
553                                  NewBootSector.BootCode,
554                                  FAT32_BOOTSECTOR_SIZE,
555                                  &FileOffset,
556                                  NULL);
557             if (!NT_SUCCESS(Status))
558             {
559                 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
560                 FreeBootCode(&NewBootSector);
561                 return Status;
562             }
563         }
564     }
565 
566     /* Write the second sector of the new bootcode to boot disk sector 14 */
567     // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE);
568     FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE;
569     Status = NtWriteFile(DstPath,   // or really RootPartition ???
570                          NULL,
571                          NULL,
572                          NULL,
573                          &IoStatusBlock,
574                          ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE),
575                          FAT32_BOOTSECTOR_SIZE,
576                          &FileOffset,
577                          NULL);
578     if (!NT_SUCCESS(Status))
579     {
580         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
581     }
582 
583     /* Free the new bootsector */
584     FreeBootCode(&NewBootSector);
585 
586     return Status;
587 }
588 
589 NTSTATUS
InstallBtrfsBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)590 InstallBtrfsBootCode(
591     IN PCWSTR SrcPath,          // BTRFS bootsector source file (on the installation medium)
592     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
593     IN HANDLE RootPartition)    // Partition holding the (old) BTRFS information
594 {
595     NTSTATUS Status;
596     NTSTATUS LockStatus;
597     UNICODE_STRING Name;
598     IO_STATUS_BLOCK IoStatusBlock;
599     LARGE_INTEGER FileOffset;
600     PARTITION_INFORMATION_EX PartInfo;
601     BOOTCODE NewBootSector = {0};
602 
603     /* Allocate and read the new bootsector from SrcPath */
604     RtlInitUnicodeString(&Name, SrcPath);
605     Status = ReadBootCodeFromFile(&NewBootSector,
606                                   &Name,
607                                   BTRFS_BOOTSECTOR_SIZE);
608     if (!NT_SUCCESS(Status))
609         return Status;
610 
611     /*
612      * The BTRFS driver requires the volume to be locked in order to modify
613      * the first sectors of the partition, even though they are outside the
614      * file-system space / in the reserved area (they are situated before
615      * the super-block at 0x1000) and is in principle allowed by the NT
616      * storage stack.
617      * So we lock here in order to write the bootsector at sector 0.
618      * If locking fails, we ignore and continue nonetheless.
619      */
620     LockStatus = NtFsControlFile(DstPath,
621                                  NULL,
622                                  NULL,
623                                  NULL,
624                                  &IoStatusBlock,
625                                  FSCTL_LOCK_VOLUME,
626                                  NULL,
627                                  0,
628                                  NULL,
629                                  0);
630     if (!NT_SUCCESS(LockStatus))
631     {
632         DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus);
633     }
634 
635     /* Obtain partition info and write it to the bootsector */
636     Status = NtDeviceIoControlFile(RootPartition,
637                                    NULL,
638                                    NULL,
639                                    NULL,
640                                    &IoStatusBlock,
641                                    IOCTL_DISK_GET_PARTITION_INFO_EX,
642                                    NULL,
643                                    0,
644                                    &PartInfo,
645                                    sizeof(PartInfo));
646     if (!NT_SUCCESS(Status))
647     {
648         DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status);
649         goto Quit;
650     }
651 
652     /* Write new bootsector to RootPath */
653     ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA =
654         PartInfo.StartingOffset.QuadPart / SECTORSIZE;
655 
656     /* Write sector 0 */
657     FileOffset.QuadPart = 0ULL;
658     Status = NtWriteFile(DstPath,
659                          NULL,
660                          NULL,
661                          NULL,
662                          &IoStatusBlock,
663                          NewBootSector.BootCode,
664                          NewBootSector.Length,
665                          &FileOffset,
666                          NULL);
667     if (!NT_SUCCESS(Status))
668     {
669         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
670         goto Quit;
671     }
672 
673 Quit:
674     /* Unlock the volume */
675     LockStatus = NtFsControlFile(DstPath,
676                                  NULL,
677                                  NULL,
678                                  NULL,
679                                  &IoStatusBlock,
680                                  FSCTL_UNLOCK_VOLUME,
681                                  NULL,
682                                  0,
683                                  NULL,
684                                  0);
685     if (!NT_SUCCESS(LockStatus))
686     {
687         DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus);
688     }
689 
690     /* Free the new bootsector */
691     FreeBootCode(&NewBootSector);
692 
693     return Status;
694 }
695 
696 NTSTATUS
InstallNtfsBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)697 InstallNtfsBootCode(
698     IN PCWSTR SrcPath,          // NTFS bootsector source file (on the installation medium)
699     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
700     IN HANDLE RootPartition)    // Partition holding the (old) NTFS information
701 {
702     NTSTATUS Status;
703     UNICODE_STRING Name;
704     IO_STATUS_BLOCK IoStatusBlock;
705     LARGE_INTEGER FileOffset;
706     BOOTCODE OrigBootSector = {0};
707     BOOTCODE NewBootSector  = {0};
708 
709     /* Allocate and read the current original partition bootsector */
710     Status = ReadBootCodeByHandle(&OrigBootSector, RootPartition, NTFS_BOOTSECTOR_SIZE);
711     if (!NT_SUCCESS(Status))
712     {
713         DPRINT1("InstallNtfsBootCode: Status %lx\n", Status);
714         return Status;
715     }
716 
717     /* Allocate and read the new bootsector (16 sectors) from SrcPath */
718     RtlInitUnicodeString(&Name, SrcPath);
719     Status = ReadBootCodeFromFile(&NewBootSector, &Name, NTFS_BOOTSECTOR_SIZE);
720     if (!NT_SUCCESS(Status))
721     {
722         DPRINT1("InstallNtfsBootCode: Status %lx\n", Status);
723         FreeBootCode(&OrigBootSector);
724         return Status;
725     }
726 
727     /* Adjust the bootsector (copy a part of the NTFS BPB) */
728     RtlCopyMemory(&((PNTFS_BOOTSECTOR)NewBootSector.BootCode)->OEMID,
729                   &((PNTFS_BOOTSECTOR)OrigBootSector.BootCode)->OEMID,
730                   FIELD_OFFSET(NTFS_BOOTSECTOR, BootStrap) - FIELD_OFFSET(NTFS_BOOTSECTOR, OEMID));
731 
732     /* Write sector 0 */
733     FileOffset.QuadPart = 0ULL;
734     Status = NtWriteFile(DstPath,
735                          NULL,
736                          NULL,
737                          NULL,
738                          &IoStatusBlock,
739                          NewBootSector.BootCode,
740                          NewBootSector.Length,
741                          &FileOffset,
742                          NULL);
743     if (!NT_SUCCESS(Status))
744     {
745         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
746         goto Quit;
747     }
748 
749 Quit:
750     /* Free the new bootsector */
751     FreeBootCode(&NewBootSector);
752 
753     return Status;
754 }
755 
756 
757 //
758 // Formatting routines
759 //
760 
761 NTSTATUS
762 NTAPI
ChkdskVolume(_In_ PVOLINFO Volume,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)763 ChkdskVolume(
764     _In_ PVOLINFO Volume,
765     _In_ BOOLEAN FixErrors,
766     _In_ BOOLEAN Verbose,
767     _In_ BOOLEAN CheckOnlyIfDirty,
768     _In_ BOOLEAN ScanDrive,
769     _In_opt_ PFMIFSCALLBACK Callback)
770 {
771     /* Do not check a volume with an unknown file system */
772     if (!*Volume->FileSystem)
773         return STATUS_UNRECOGNIZED_VOLUME;
774 
775     /* Check the volume */
776     DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName);
777     return ChkdskFileSystem(Volume->DeviceName,
778                             Volume->FileSystem,
779                             FixErrors,
780                             Verbose,
781                             CheckOnlyIfDirty,
782                             ScanDrive,
783                             Callback);
784 }
785 
786 NTSTATUS
787 NTAPI
ChkdskPartition(_In_ PPARTENTRY PartEntry,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)788 ChkdskPartition(
789     _In_ PPARTENTRY PartEntry,
790     _In_ BOOLEAN FixErrors,
791     _In_ BOOLEAN Verbose,
792     _In_ BOOLEAN CheckOnlyIfDirty,
793     _In_ BOOLEAN ScanDrive,
794     _In_opt_ PFMIFSCALLBACK Callback)
795 {
796     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
797     ASSERT(PartEntry->Volume);
798 
799     // if (!PartEntry->Volume) { check_raw_sectors(); } else { check_FS(); }
800 
801     /* Check the associated volume */
802     return ChkdskVolume(&PartEntry->Volume->Info,
803                         FixErrors,
804                         Verbose,
805                         CheckOnlyIfDirty,
806                         ScanDrive,
807                         Callback);
808 }
809 
810 NTSTATUS
811 NTAPI
FormatVolume(_In_ PVOLINFO Volume,_In_ PCWSTR FileSystemName,_In_ FMIFS_MEDIA_FLAG MediaFlag,_In_opt_ PCWSTR Label,_In_ BOOLEAN QuickFormat,_In_ ULONG ClusterSize,_In_opt_ PFMIFSCALLBACK Callback)812 FormatVolume(
813     _In_ PVOLINFO Volume,
814     _In_ PCWSTR FileSystemName,
815     _In_ FMIFS_MEDIA_FLAG MediaFlag,
816     _In_opt_ PCWSTR Label,
817     _In_ BOOLEAN QuickFormat,
818     _In_ ULONG ClusterSize,
819     _In_opt_ PFMIFSCALLBACK Callback)
820 {
821     NTSTATUS Status;
822 
823     if (!FileSystemName || !*FileSystemName)
824     {
825         DPRINT1("No file system specified\n");
826         return STATUS_UNRECOGNIZED_VOLUME;
827     }
828 
829     /* Format the volume */
830     DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName);
831     Status = FormatFileSystem(Volume->DeviceName,
832                               FileSystemName,
833                               MediaFlag,
834                               Label,
835                               QuickFormat,
836                               ClusterSize,
837                               Callback);
838     if (!NT_SUCCESS(Status))
839         return Status;
840 
841     /* Set the new volume's file system and label */
842     RtlStringCbCopyW(Volume->FileSystem, sizeof(Volume->FileSystem), FileSystemName);
843     if (!Label) Label = L"";
844     RtlStringCbCopyW(Volume->VolumeLabel, sizeof(Volume->VolumeLabel), Label);
845 
846     return STATUS_SUCCESS;
847 }
848 
849 NTSTATUS
850 NTAPI
FormatPartition(_In_ PPARTENTRY PartEntry,_In_ PCWSTR FileSystemName,_In_ FMIFS_MEDIA_FLAG MediaFlag,_In_opt_ PCWSTR Label,_In_ BOOLEAN QuickFormat,_In_ ULONG ClusterSize,_In_opt_ PFMIFSCALLBACK Callback)851 FormatPartition(
852     _In_ PPARTENTRY PartEntry,
853     _In_ PCWSTR FileSystemName,
854     _In_ FMIFS_MEDIA_FLAG MediaFlag,
855     _In_opt_ PCWSTR Label,
856     _In_ BOOLEAN QuickFormat,
857     _In_ ULONG ClusterSize,
858     _In_opt_ PFMIFSCALLBACK Callback)
859 {
860     NTSTATUS Status;
861     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
862     UCHAR PartitionType;
863 
864     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
865 
866     if (!FileSystemName || !*FileSystemName)
867     {
868         DPRINT1("No file system specified\n");
869         return STATUS_UNRECOGNIZED_VOLUME;
870     }
871 
872     /*
873      * Prepare the partition for formatting (for MBR disks, reset the
874      * partition type), and adjust the file system name in case of FAT
875      * vs. FAT32, depending on the geometry of the partition.
876      */
877 
878 // FIXME: Do this only if QuickFormat == FALSE? What about FAT handling?
879 
880     /*
881      * Retrieve a partition type as a hint only. It will be used to determine
882      * whether to actually use FAT12/16 or FAT32 file system, depending on the
883      * geometry of the partition. If the partition resides on an MBR disk,
884      * the partition style will be reset to this value as well, unless the
885      * partition is OEM.
886      */
887     PartitionType = FileSystemToMBRPartitionType(FileSystemName,
888                                                  PartEntry->StartSector.QuadPart,
889                                                  PartEntry->SectorCount.QuadPart);
890     if (PartitionType == PARTITION_ENTRY_UNUSED)
891     {
892         /* Unknown file system */
893         DPRINT1("Unknown file system '%S'\n", FileSystemName);
894         return STATUS_UNRECOGNIZED_VOLUME;
895     }
896 
897     /* Reset the MBR partition type, unless this is an OEM partition */
898     if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR)
899     {
900         if (!IsOEMPartition(PartEntry->PartitionType))
901             SetMBRPartitionType(PartEntry, PartitionType);
902     }
903 
904     /*
905      * Adjust the file system name in case of FAT vs. FAT32, according to
906      * the type of partition returned by FileSystemToMBRPartitionType().
907      */
908     if (_wcsicmp(FileSystemName, L"FAT") == 0)
909     {
910         if ((PartitionType == PARTITION_FAT32) ||
911             (PartitionType == PARTITION_FAT32_XINT13))
912         {
913             FileSystemName = L"FAT32";
914         }
915     }
916 
917     /* Commit the partition changes to the disk */
918     Status = WritePartitions(DiskEntry);
919     if (!NT_SUCCESS(Status))
920     {
921         DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n",
922                 DiskEntry->DiskNumber, Status);
923         return STATUS_PARTITION_FAILURE;
924     }
925 
926     /* We must have an associated volume now */
927     ASSERT(PartEntry->Volume);
928 
929     /* Format the associated volume */
930     Status = FormatVolume(&PartEntry->Volume->Info,
931                           FileSystemName,
932                           MediaFlag,
933                           Label,
934                           QuickFormat,
935                           ClusterSize,
936                           Callback);
937     if (!NT_SUCCESS(Status))
938         return Status;
939 
940     PartEntry->Volume->FormatState = Formatted;
941     PartEntry->Volume->New = FALSE;
942     return STATUS_SUCCESS;
943 }
944 
945 
946 //
947 // FileSystem Volume Operations Queue
948 //
949 
950 static FSVOL_OP
DoFormatting(_In_ PVOLENTRY Volume,_In_opt_ PVOID Context,_In_opt_ PFSVOL_CALLBACK FsVolCallback)951 DoFormatting(
952     _In_ PVOLENTRY Volume,
953     _In_opt_ PVOID Context,
954     _In_opt_ PFSVOL_CALLBACK FsVolCallback)
955 {
956     FSVOL_OP Result;
957     NTSTATUS Status = STATUS_SUCCESS;
958     PPARTENTRY PartEntry;
959     FORMAT_VOLUME_INFO FmtInfo = {0};
960 
961     PartEntry = Volume->PartEntry;
962     ASSERT(PartEntry && (PartEntry->Volume == Volume));
963 
964     FmtInfo.Volume = Volume;
965 
966 RetryFormat:
967     Result = FsVolCallback(Context,
968                            FSVOLNOTIFY_STARTFORMAT,
969                            (ULONG_PTR)&FmtInfo,
970                            FSVOL_FORMAT);
971     if (Result != FSVOL_DOIT)
972         goto EndFormat;
973 
974     ASSERT(FmtInfo.FileSystemName && *FmtInfo.FileSystemName);
975 
976     /* Format the partition */
977     Status = FormatPartition(PartEntry,
978                              FmtInfo.FileSystemName,
979                              FmtInfo.MediaFlag,
980                              FmtInfo.Label,
981                              FmtInfo.QuickFormat,
982                              FmtInfo.ClusterSize,
983                              FmtInfo.Callback);
984     if (!NT_SUCCESS(Status))
985     {
986         // FmtInfo.NtPathPartition = PathBuffer;
987         FmtInfo.ErrorStatus = Status;
988 
989         Result = FsVolCallback(Context,
990                                FSVOLNOTIFY_FORMATERROR,
991                                (ULONG_PTR)&FmtInfo,
992                                0);
993         if (Result == FSVOL_RETRY)
994             goto RetryFormat;
995         // else if (Result == FSVOL_ABORT || Result == FSVOL_SKIP), stop.
996     }
997 
998 EndFormat:
999     /* This notification is always sent, even in case of error or abort */
1000     FmtInfo.ErrorStatus = Status;
1001     FsVolCallback(Context,
1002                   FSVOLNOTIFY_ENDFORMAT,
1003                   (ULONG_PTR)&FmtInfo,
1004                   0);
1005     return Result;
1006 }
1007 
1008 static FSVOL_OP
DoChecking(_In_ PVOLENTRY Volume,_In_opt_ PVOID Context,_In_opt_ PFSVOL_CALLBACK FsVolCallback)1009 DoChecking(
1010     _In_ PVOLENTRY Volume,
1011     _In_opt_ PVOID Context,
1012     _In_opt_ PFSVOL_CALLBACK FsVolCallback)
1013 {
1014     FSVOL_OP Result;
1015     NTSTATUS Status = STATUS_SUCCESS;
1016     CHECK_VOLUME_INFO ChkInfo = {0};
1017 
1018     ASSERT(*Volume->Info.FileSystem);
1019 
1020     ChkInfo.Volume = Volume;
1021 
1022 RetryCheck:
1023     Result = FsVolCallback(Context,
1024                            FSVOLNOTIFY_STARTCHECK,
1025                            (ULONG_PTR)&ChkInfo,
1026                            FSVOL_CHECK);
1027     if (Result != FSVOL_DOIT)
1028         goto EndCheck;
1029 
1030     /* Check the volume */
1031     Status = ChkdskVolume(&Volume->Info,
1032                           ChkInfo.FixErrors,
1033                           ChkInfo.Verbose,
1034                           ChkInfo.CheckOnlyIfDirty,
1035                           ChkInfo.ScanDrive,
1036                           ChkInfo.Callback);
1037 
1038     /* If volume checking succeeded, or if it is not supported
1039      * with the current file system, disable checks on the volume */
1040     if (NT_SUCCESS(Status) || (Status == STATUS_NOT_SUPPORTED))
1041         Volume->NeedsCheck = FALSE;
1042 
1043     if (!NT_SUCCESS(Status))
1044     {
1045         // ChkInfo.NtPathPartition = PathBuffer;
1046         ChkInfo.ErrorStatus = Status;
1047 
1048         Result = FsVolCallback(Context,
1049                                FSVOLNOTIFY_CHECKERROR,
1050                                (ULONG_PTR)&ChkInfo,
1051                                0);
1052         if (Result == FSVOL_RETRY)
1053             goto RetryCheck;
1054         // else if (Result == FSVOL_ABORT || Result == FSVOL_SKIP), stop.
1055 
1056         // Volume->NeedsCheck = FALSE;
1057     }
1058 
1059 EndCheck:
1060     /* This notification is always sent, even in case of error or abort */
1061     ChkInfo.ErrorStatus = Status;
1062     FsVolCallback(Context,
1063                   FSVOLNOTIFY_ENDCHECK,
1064                   (ULONG_PTR)&ChkInfo,
1065                   0);
1066     return Result;
1067 }
1068 
1069 static
1070 PVOLENTRY
GetNextUnformattedVolume(_In_ PPARTLIST List,_In_opt_ PVOLENTRY Volume)1071 GetNextUnformattedVolume(
1072     _In_ PPARTLIST List,
1073     _In_opt_ PVOLENTRY Volume)
1074 {
1075     PLIST_ENTRY Entry;
1076 
1077     for (;;)
1078     {
1079         /* If we have a current volume, get the next one, otherwise get the first */
1080         Entry = (Volume ? &Volume->ListEntry : &List->VolumesList);
1081         Entry = Entry->Flink;
1082 
1083         if (Entry == &List->VolumesList)
1084             return NULL;
1085 
1086         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
1087         if (Volume->New && (Volume->FormatState == Unformatted))
1088         {
1089             /* Found a candidate, return it */
1090             return Volume;
1091         }
1092     }
1093 }
1094 
1095 BOOLEAN
1096 NTAPI
FsVolCommitOpsQueue(_In_ PPARTLIST PartitionList,_In_ PVOLENTRY SystemVolume,_In_ PVOLENTRY InstallVolume,_In_opt_ PFSVOL_CALLBACK FsVolCallback,_In_opt_ PVOID Context)1097 FsVolCommitOpsQueue(
1098     _In_ PPARTLIST PartitionList,
1099     _In_ PVOLENTRY SystemVolume,
1100     _In_ PVOLENTRY InstallVolume,
1101     _In_opt_ PFSVOL_CALLBACK FsVolCallback,
1102     _In_opt_ PVOID Context)
1103 {
1104     BOOLEAN Success = TRUE; // Suppose success originally.
1105     FSVOL_OP Result;
1106     PLIST_ENTRY Entry;
1107     PVOLENTRY Volume;
1108 
1109     /* Machine state for the format step */
1110     typedef enum _FORMATMACHINESTATE
1111     {
1112         Start,
1113         FormatSystemVolume,
1114         FormatInstallVolume,
1115         FormatOtherVolume,
1116         FormatDone
1117     } FORMATMACHINESTATE;
1118     FORMATMACHINESTATE FormatState, OldFormatState;
1119     static const PCSTR FormatStateNames[] = {
1120         "Start",
1121         "FormatSystemVolume",
1122         "FormatInstallVolume",
1123         "FormatOtherVolume",
1124         "FormatDone"
1125     };
1126 
1127     ASSERT(PartitionList && SystemVolume && InstallVolume);
1128 
1129     /* Commit all partition changes to all the disks */
1130     if (!WritePartitionsToDisk(PartitionList))
1131     {
1132         DPRINT("WritePartitionsToDisk() failed\n");
1133         /* Result = */ FsVolCallback(Context,
1134                             FSVOLNOTIFY_PARTITIONERROR,
1135                             STATUS_PARTITION_FAILURE, // FIXME
1136                             0);
1137         return FALSE;
1138     }
1139 
1140 //
1141 // FIXME: Should we do the following here, or in the caller?
1142 //
1143     /*
1144      * In all cases, whether or not we are going to perform a formatting,
1145      * we must perform a file system check of both the system and the
1146      * installation volumes.
1147      */
1148     SystemVolume->NeedsCheck = TRUE;
1149     InstallVolume->NeedsCheck = TRUE;
1150 
1151     Result = FsVolCallback(Context,
1152                            FSVOLNOTIFY_STARTQUEUE,
1153                            0, 0);
1154     if (Result == FSVOL_ABORT)
1155         return FALSE;
1156 
1157     /*
1158      * Commit the Format queue
1159      */
1160 
1161     Result = FsVolCallback(Context,
1162                            FSVOLNOTIFY_STARTSUBQUEUE,
1163                            FSVOL_FORMAT,
1164                            0);
1165     if (Result == FSVOL_ABORT)
1166         return FALSE;
1167     /** HACK!! **/
1168     if (Result == FSVOL_SKIP)
1169         goto StartCheckQueue;
1170     /** END HACK!! **/
1171 
1172     /* Reset the formatter machine state */
1173     FormatState = Start;
1174     Volume = NULL;
1175 NextFormat:
1176     OldFormatState = FormatState;
1177     switch (FormatState)
1178     {
1179         case Start:
1180         {
1181             /*
1182              * We start by formatting the system volume in case it is new
1183              * (it didn't exist before) and is not the same as the installation
1184              * volume. Otherwise we just require a file system check on it,
1185              * and start by formatting the installation volume instead.
1186              */
1187             if (SystemVolume != InstallVolume)
1188             {
1189                 Volume = SystemVolume;
1190 
1191                 if (Volume->FormatState == Unformatted)
1192                 {
1193                     // TODO: Should we let the user use a custom file system,
1194                     // or should we always use FAT(32) for it?
1195                     // For "compatibility", FAT(32) would be best indeed.
1196 
1197                     FormatState = FormatSystemVolume;
1198                     DPRINT1("FormatState: %s --> %s\n",
1199                             FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1200                     break;
1201                 }
1202 
1203                 /* The system volume is separate, so it had better be formatted! */
1204                 ASSERT(Volume->FormatState == Formatted);
1205 
1206                 /* Require a file system check on the system volume too */
1207                 Volume->NeedsCheck = TRUE;
1208             }
1209             __fallthrough;
1210         }
1211 
1212         case FormatSystemVolume:
1213         {
1214             Volume = InstallVolume;
1215 
1216             FormatState = FormatInstallVolume;
1217             DPRINT1("FormatState: %s --> %s\n",
1218                     FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1219             break;
1220         }
1221 
1222         case FormatInstallVolume:
1223             /* Restart volume enumeration */
1224             Volume = NULL;
1225         case FormatOtherVolume:
1226         {
1227             Volume = GetNextUnformattedVolume(PartitionList, Volume);
1228 
1229             FormatState = (Volume ? FormatOtherVolume : FormatDone);
1230             DPRINT1("FormatState: %s --> %s\n",
1231                     FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1232             if (Volume)
1233                 break;
1234             __fallthrough;
1235         }
1236 
1237         case FormatDone:
1238         {
1239             DPRINT1("FormatState: FormatDone\n");
1240             Success = TRUE;
1241             goto EndFormat;
1242         }
1243         DEFAULT_UNREACHABLE;
1244     }
1245 
1246     Result = DoFormatting(Volume, Context, FsVolCallback);
1247     if (Result == FSVOL_ABORT)
1248     {
1249         Success = FALSE;
1250         goto Quit;
1251     }
1252     /* Schedule a check for this volume */
1253     Volume->NeedsCheck = TRUE;
1254     /* Go to the next volume to be formatted */
1255     goto NextFormat;
1256 
1257 EndFormat:
1258     FsVolCallback(Context,
1259                   FSVOLNOTIFY_ENDSUBQUEUE,
1260                   FSVOL_FORMAT,
1261                   0);
1262 
1263 
1264     /*
1265      * Commit the CheckFS queue
1266      */
1267 
1268 StartCheckQueue:
1269     Result = FsVolCallback(Context,
1270                            FSVOLNOTIFY_STARTSUBQUEUE,
1271                            FSVOL_CHECK,
1272                            0);
1273     if (Result == FSVOL_ABORT)
1274         return FALSE;
1275 
1276     /* Loop through each unchecked volume and do the check */
1277     for (Entry = PartitionList->VolumesList.Flink;
1278          Entry != &PartitionList->VolumesList;
1279          Entry = Entry->Flink)
1280     {
1281         Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
1282         if (!Volume->NeedsCheck)
1283             continue;
1284 
1285         /* Found a candidate */
1286         ASSERT(Volume->FormatState == Formatted);
1287         Result = DoChecking(Volume, Context, FsVolCallback);
1288         if (Result == FSVOL_ABORT)
1289         {
1290             Success = FALSE;
1291             goto Quit;
1292         }
1293         /* Go to the next volume to be checked */
1294     }
1295     Success = TRUE;
1296 
1297     FsVolCallback(Context,
1298                   FSVOLNOTIFY_ENDSUBQUEUE,
1299                   FSVOL_CHECK,
1300                   0);
1301 
1302 
1303 Quit:
1304     /* All the queues have been committed */
1305     FsVolCallback(Context,
1306                   FSVOLNOTIFY_ENDQUEUE,
1307                   Success,
1308                   0);
1309     return Success;
1310 }
1311 
1312 /* EOF */
1313