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
GetRegisteredFileSystems(IN ULONG Index,OUT PCWSTR * FileSystemName)182 GetRegisteredFileSystems(
183 IN ULONG Index,
184 OUT PCWSTR* FileSystemName)
185 {
186 if (Index >= ARRAYSIZE(RegisteredFileSystems))
187 return FALSE;
188
189 *FileSystemName = RegisteredFileSystems[Index].FileSystemName;
190
191 return TRUE;
192 }
193
194
195 /** GetProvider() **/
196 static PFILE_SYSTEM
GetFileSystemByName(IN PCWSTR FileSystemName)197 GetFileSystemByName(
198 IN PCWSTR FileSystemName)
199 {
200 #if 0 // Reenable when the list of registered FSes will again be dynamic
201
202 PLIST_ENTRY ListEntry;
203 PFILE_SYSTEM_ITEM Item;
204
205 ListEntry = List->ListHead.Flink;
206 while (ListEntry != &List->ListHead)
207 {
208 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
209 if (Item->FileSystemName &&
210 (_wcsicmp(FileSystemName, Item->FileSystemName) == 0))
211 {
212 return Item;
213 }
214
215 ListEntry = ListEntry->Flink;
216 }
217
218 #else
219
220 ULONG Count = ARRAYSIZE(RegisteredFileSystems);
221 PFILE_SYSTEM FileSystems = RegisteredFileSystems;
222
223 ASSERT(FileSystems && Count != 0);
224
225 while (Count--)
226 {
227 if (FileSystems->FileSystemName &&
228 (_wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0))
229 {
230 return FileSystems;
231 }
232
233 ++FileSystems;
234 }
235
236 #endif
237
238 return NULL;
239 }
240
241
242 /** ChkdskEx() **/
243 NTSTATUS
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)244 ChkdskFileSystem_UStr(
245 _In_ PUNICODE_STRING DriveRoot,
246 _In_ PCWSTR FileSystemName,
247 _In_ BOOLEAN FixErrors,
248 _In_ BOOLEAN Verbose,
249 _In_ BOOLEAN CheckOnlyIfDirty,
250 _In_ BOOLEAN ScanDrive,
251 _In_opt_ PFMIFSCALLBACK Callback)
252 {
253 PFILE_SYSTEM FileSystem;
254 NTSTATUS Status;
255 BOOLEAN Success;
256
257 FileSystem = GetFileSystemByName(FileSystemName);
258
259 if (!FileSystem || !FileSystem->ChkdskFunc)
260 {
261 // Success = FALSE;
262 // Callback(DONE, 0, &Success);
263 return STATUS_NOT_SUPPORTED;
264 }
265
266 Status = STATUS_SUCCESS;
267 Success = FileSystem->ChkdskFunc(DriveRoot,
268 Callback,
269 FixErrors,
270 Verbose,
271 CheckOnlyIfDirty,
272 ScanDrive,
273 NULL,
274 NULL,
275 NULL,
276 NULL,
277 (PULONG)&Status);
278 if (!Success)
279 DPRINT1("ChkdskFunc() failed with Status 0x%lx\n", Status);
280
281 // Callback(DONE, 0, &Success);
282
283 return Status;
284 }
285
286 NTSTATUS
ChkdskFileSystem(_In_ PCWSTR DriveRoot,_In_ PCWSTR FileSystemName,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)287 ChkdskFileSystem(
288 _In_ PCWSTR DriveRoot,
289 _In_ PCWSTR FileSystemName,
290 _In_ BOOLEAN FixErrors,
291 _In_ BOOLEAN Verbose,
292 _In_ BOOLEAN CheckOnlyIfDirty,
293 _In_ BOOLEAN ScanDrive,
294 _In_opt_ PFMIFSCALLBACK Callback)
295 {
296 UNICODE_STRING DriveRootU;
297
298 RtlInitUnicodeString(&DriveRootU, DriveRoot);
299 return ChkdskFileSystem_UStr(&DriveRootU,
300 FileSystemName,
301 FixErrors,
302 Verbose,
303 CheckOnlyIfDirty,
304 ScanDrive,
305 Callback);
306 }
307
308
309 /** FormatEx() **/
310 NTSTATUS
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)311 FormatFileSystem_UStr(
312 _In_ PUNICODE_STRING DriveRoot,
313 _In_ PCWSTR FileSystemName,
314 _In_ FMIFS_MEDIA_FLAG MediaFlag,
315 _In_opt_ PUNICODE_STRING Label,
316 _In_ BOOLEAN QuickFormat,
317 _In_ ULONG ClusterSize,
318 _In_opt_ PFMIFSCALLBACK Callback)
319 {
320 PFILE_SYSTEM FileSystem;
321 BOOLEAN Success;
322 BOOLEAN BackwardCompatible = FALSE; // Default to latest FS versions.
323 MEDIA_TYPE MediaType;
324
325 FileSystem = GetFileSystemByName(FileSystemName);
326
327 if (!FileSystem || !FileSystem->FormatFunc)
328 {
329 // Success = FALSE;
330 // Callback(DONE, 0, &Success);
331 return STATUS_NOT_SUPPORTED;
332 }
333
334 /* Set the BackwardCompatible flag in case we format with older FAT12/16 */
335 if (_wcsicmp(FileSystemName, L"FAT") == 0)
336 BackwardCompatible = TRUE;
337 // else if (_wcsicmp(FileSystemName, L"FAT32") == 0)
338 // BackwardCompatible = FALSE;
339
340 /* Convert the FMIFS MediaFlag to a NT MediaType */
341 // FIXME: Actually covert all the possible flags.
342 switch (MediaFlag)
343 {
344 case FMIFS_FLOPPY:
345 MediaType = F5_320_1024; // FIXME: This is hardfixed!
346 break;
347 case FMIFS_REMOVABLE:
348 MediaType = RemovableMedia;
349 break;
350 case FMIFS_HARDDISK:
351 MediaType = FixedMedia;
352 break;
353 default:
354 DPRINT1("Unknown FMIFS MediaFlag %d, converting 1-to-1 to NT MediaType\n",
355 MediaFlag);
356 MediaType = (MEDIA_TYPE)MediaFlag;
357 break;
358 }
359
360 Success = FileSystem->FormatFunc(DriveRoot,
361 Callback,
362 QuickFormat,
363 BackwardCompatible,
364 MediaType,
365 Label,
366 ClusterSize);
367 if (!Success)
368 DPRINT1("FormatFunc() failed\n");
369
370 // Callback(DONE, 0, &Success);
371
372 return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
373 }
374
375 NTSTATUS
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)376 FormatFileSystem(
377 _In_ PCWSTR DriveRoot,
378 _In_ PCWSTR FileSystemName,
379 _In_ FMIFS_MEDIA_FLAG MediaFlag,
380 _In_opt_ PCWSTR Label,
381 _In_ BOOLEAN QuickFormat,
382 _In_ ULONG ClusterSize,
383 _In_opt_ PFMIFSCALLBACK Callback)
384 {
385 UNICODE_STRING DriveRootU;
386 UNICODE_STRING LabelU;
387
388 RtlInitUnicodeString(&DriveRootU, DriveRoot);
389 RtlInitUnicodeString(&LabelU, Label);
390
391 return FormatFileSystem_UStr(&DriveRootU,
392 FileSystemName,
393 MediaFlag,
394 &LabelU,
395 QuickFormat,
396 ClusterSize,
397 Callback);
398 }
399
400
401 //
402 // Bootsector routines
403 //
404
405 NTSTATUS
InstallFatBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)406 InstallFatBootCode(
407 IN PCWSTR SrcPath, // FAT12/16 bootsector source file (on the installation medium)
408 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
409 IN HANDLE RootPartition) // Partition holding the (old) FAT12/16 information
410 {
411 NTSTATUS Status;
412 UNICODE_STRING Name;
413 IO_STATUS_BLOCK IoStatusBlock;
414 LARGE_INTEGER FileOffset;
415 BOOTCODE OrigBootSector = {0};
416 BOOTCODE NewBootSector = {0};
417
418 /* Allocate and read the current original partition bootsector */
419 Status = ReadBootCodeByHandle(&OrigBootSector,
420 RootPartition,
421 FAT_BOOTSECTOR_SIZE);
422 if (!NT_SUCCESS(Status))
423 return Status;
424
425 /* Allocate and read the new bootsector from SrcPath */
426 RtlInitUnicodeString(&Name, SrcPath);
427 Status = ReadBootCodeFromFile(&NewBootSector,
428 &Name,
429 FAT_BOOTSECTOR_SIZE);
430 if (!NT_SUCCESS(Status))
431 {
432 FreeBootCode(&OrigBootSector);
433 return Status;
434 }
435
436 /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */
437 RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName,
438 &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
439 FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) -
440 FIELD_OFFSET(FAT_BOOTSECTOR, OemName));
441
442 /* Free the original bootsector */
443 FreeBootCode(&OrigBootSector);
444
445 /* Write the new bootsector to DstPath */
446 FileOffset.QuadPart = 0ULL;
447 Status = NtWriteFile(DstPath,
448 NULL,
449 NULL,
450 NULL,
451 &IoStatusBlock,
452 NewBootSector.BootCode,
453 NewBootSector.Length,
454 &FileOffset,
455 NULL);
456
457 /* Free the new bootsector */
458 FreeBootCode(&NewBootSector);
459
460 return Status;
461 }
462
463 NTSTATUS
InstallFat32BootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)464 InstallFat32BootCode(
465 IN PCWSTR SrcPath, // FAT32 bootsector source file (on the installation medium)
466 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
467 IN HANDLE RootPartition) // Partition holding the (old) FAT32 information
468 {
469 NTSTATUS Status;
470 UNICODE_STRING Name;
471 IO_STATUS_BLOCK IoStatusBlock;
472 LARGE_INTEGER FileOffset;
473 USHORT BackupBootSector = 0;
474 BOOTCODE OrigBootSector = {0};
475 BOOTCODE NewBootSector = {0};
476
477 /* Allocate and read the current original partition bootsector */
478 Status = ReadBootCodeByHandle(&OrigBootSector,
479 RootPartition,
480 FAT32_BOOTSECTOR_SIZE);
481 if (!NT_SUCCESS(Status))
482 return Status;
483
484 /* Allocate and read the new bootsector (2 sectors) from SrcPath */
485 RtlInitUnicodeString(&Name, SrcPath);
486 Status = ReadBootCodeFromFile(&NewBootSector,
487 &Name,
488 2 * FAT32_BOOTSECTOR_SIZE);
489 if (!NT_SUCCESS(Status))
490 {
491 FreeBootCode(&OrigBootSector);
492 return Status;
493 }
494
495 /* Adjust the bootsector (copy a part of the FAT32 BPB) */
496 RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName,
497 &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
498 FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) -
499 FIELD_OFFSET(FAT32_BOOTSECTOR, OemName));
500
501 /*
502 * We know we copy the boot code to a file only when DstPath != RootPartition,
503 * otherwise the boot code is copied to the specified root partition.
504 */
505 if (DstPath != RootPartition)
506 {
507 /* Copy to a file: Disable the backup bootsector */
508 ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0;
509 }
510 else
511 {
512 /* Copy to a disk: Get the location of the backup bootsector */
513 BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector;
514 }
515
516 /* Free the original bootsector */
517 FreeBootCode(&OrigBootSector);
518
519 /* Write the first sector of the new bootcode to DstPath sector 0 */
520 FileOffset.QuadPart = 0ULL;
521 Status = NtWriteFile(DstPath,
522 NULL,
523 NULL,
524 NULL,
525 &IoStatusBlock,
526 NewBootSector.BootCode,
527 FAT32_BOOTSECTOR_SIZE,
528 &FileOffset,
529 NULL);
530 if (!NT_SUCCESS(Status))
531 {
532 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
533 FreeBootCode(&NewBootSector);
534 return Status;
535 }
536
537 if (DstPath == RootPartition)
538 {
539 /* Copy to a disk: Write the backup bootsector */
540 if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF))
541 {
542 FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE);
543 Status = NtWriteFile(DstPath,
544 NULL,
545 NULL,
546 NULL,
547 &IoStatusBlock,
548 NewBootSector.BootCode,
549 FAT32_BOOTSECTOR_SIZE,
550 &FileOffset,
551 NULL);
552 if (!NT_SUCCESS(Status))
553 {
554 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
555 FreeBootCode(&NewBootSector);
556 return Status;
557 }
558 }
559 }
560
561 /* Write the second sector of the new bootcode to boot disk sector 14 */
562 // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE);
563 FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE;
564 Status = NtWriteFile(DstPath, // or really RootPartition ???
565 NULL,
566 NULL,
567 NULL,
568 &IoStatusBlock,
569 ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE),
570 FAT32_BOOTSECTOR_SIZE,
571 &FileOffset,
572 NULL);
573 if (!NT_SUCCESS(Status))
574 {
575 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
576 }
577
578 /* Free the new bootsector */
579 FreeBootCode(&NewBootSector);
580
581 return Status;
582 }
583
584 NTSTATUS
InstallBtrfsBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)585 InstallBtrfsBootCode(
586 IN PCWSTR SrcPath, // BTRFS bootsector source file (on the installation medium)
587 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
588 IN HANDLE RootPartition) // Partition holding the (old) BTRFS information
589 {
590 NTSTATUS Status;
591 NTSTATUS LockStatus;
592 UNICODE_STRING Name;
593 IO_STATUS_BLOCK IoStatusBlock;
594 LARGE_INTEGER FileOffset;
595 PARTITION_INFORMATION_EX PartInfo;
596 BOOTCODE NewBootSector = {0};
597
598 /* Allocate and read the new bootsector from SrcPath */
599 RtlInitUnicodeString(&Name, SrcPath);
600 Status = ReadBootCodeFromFile(&NewBootSector,
601 &Name,
602 BTRFS_BOOTSECTOR_SIZE);
603 if (!NT_SUCCESS(Status))
604 return Status;
605
606 /*
607 * The BTRFS driver requires the volume to be locked in order to modify
608 * the first sectors of the partition, even though they are outside the
609 * file-system space / in the reserved area (they are situated before
610 * the super-block at 0x1000) and is in principle allowed by the NT
611 * storage stack.
612 * So we lock here in order to write the bootsector at sector 0.
613 * If locking fails, we ignore and continue nonetheless.
614 */
615 LockStatus = NtFsControlFile(DstPath,
616 NULL,
617 NULL,
618 NULL,
619 &IoStatusBlock,
620 FSCTL_LOCK_VOLUME,
621 NULL,
622 0,
623 NULL,
624 0);
625 if (!NT_SUCCESS(LockStatus))
626 {
627 DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus);
628 }
629
630 /* Obtain partition info and write it to the bootsector */
631 Status = NtDeviceIoControlFile(RootPartition,
632 NULL,
633 NULL,
634 NULL,
635 &IoStatusBlock,
636 IOCTL_DISK_GET_PARTITION_INFO_EX,
637 NULL,
638 0,
639 &PartInfo,
640 sizeof(PartInfo));
641 if (!NT_SUCCESS(Status))
642 {
643 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status);
644 goto Quit;
645 }
646
647 /* Write new bootsector to RootPath */
648 ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA =
649 PartInfo.StartingOffset.QuadPart / SECTORSIZE;
650
651 /* Write sector 0 */
652 FileOffset.QuadPart = 0ULL;
653 Status = NtWriteFile(DstPath,
654 NULL,
655 NULL,
656 NULL,
657 &IoStatusBlock,
658 NewBootSector.BootCode,
659 NewBootSector.Length,
660 &FileOffset,
661 NULL);
662 if (!NT_SUCCESS(Status))
663 {
664 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
665 goto Quit;
666 }
667
668 Quit:
669 /* Unlock the volume */
670 LockStatus = NtFsControlFile(DstPath,
671 NULL,
672 NULL,
673 NULL,
674 &IoStatusBlock,
675 FSCTL_UNLOCK_VOLUME,
676 NULL,
677 0,
678 NULL,
679 0);
680 if (!NT_SUCCESS(LockStatus))
681 {
682 DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus);
683 }
684
685 /* Free the new bootsector */
686 FreeBootCode(&NewBootSector);
687
688 return Status;
689 }
690
691 NTSTATUS
InstallNtfsBootCode(IN PCWSTR SrcPath,IN HANDLE DstPath,IN HANDLE RootPartition)692 InstallNtfsBootCode(
693 IN PCWSTR SrcPath, // NTFS bootsector source file (on the installation medium)
694 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information
695 IN HANDLE RootPartition) // Partition holding the (old) NTFS information
696 {
697 NTSTATUS Status;
698 UNICODE_STRING Name;
699 IO_STATUS_BLOCK IoStatusBlock;
700 LARGE_INTEGER FileOffset;
701 BOOTCODE OrigBootSector = {0};
702 BOOTCODE NewBootSector = {0};
703
704 /* Allocate and read the current original partition bootsector */
705 Status = ReadBootCodeByHandle(&OrigBootSector, RootPartition, NTFS_BOOTSECTOR_SIZE);
706 if (!NT_SUCCESS(Status))
707 {
708 DPRINT1("InstallNtfsBootCode: Status %lx\n", Status);
709 return Status;
710 }
711
712 /* Allocate and read the new bootsector (16 sectors) from SrcPath */
713 RtlInitUnicodeString(&Name, SrcPath);
714 Status = ReadBootCodeFromFile(&NewBootSector, &Name, NTFS_BOOTSECTOR_SIZE);
715 if (!NT_SUCCESS(Status))
716 {
717 DPRINT1("InstallNtfsBootCode: Status %lx\n", Status);
718 FreeBootCode(&OrigBootSector);
719 return Status;
720 }
721
722 /* Adjust the bootsector (copy a part of the NTFS BPB) */
723 RtlCopyMemory(&((PNTFS_BOOTSECTOR)NewBootSector.BootCode)->OEMID,
724 &((PNTFS_BOOTSECTOR)OrigBootSector.BootCode)->OEMID,
725 FIELD_OFFSET(NTFS_BOOTSECTOR, BootStrap) - FIELD_OFFSET(NTFS_BOOTSECTOR, OEMID));
726
727 /* Write sector 0 */
728 FileOffset.QuadPart = 0ULL;
729 Status = NtWriteFile(DstPath,
730 NULL,
731 NULL,
732 NULL,
733 &IoStatusBlock,
734 NewBootSector.BootCode,
735 NewBootSector.Length,
736 &FileOffset,
737 NULL);
738 if (!NT_SUCCESS(Status))
739 {
740 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
741 goto Quit;
742 }
743
744 Quit:
745 /* Free the new bootsector */
746 FreeBootCode(&NewBootSector);
747
748 return Status;
749 }
750
751
752 //
753 // Formatting routines
754 //
755
756 NTSTATUS
ChkdskVolume(_In_ PVOLINFO Volume,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)757 ChkdskVolume(
758 _In_ PVOLINFO Volume,
759 _In_ BOOLEAN FixErrors,
760 _In_ BOOLEAN Verbose,
761 _In_ BOOLEAN CheckOnlyIfDirty,
762 _In_ BOOLEAN ScanDrive,
763 _In_opt_ PFMIFSCALLBACK Callback)
764 {
765 /* Do not check a volume with an unknown file system */
766 if (!*Volume->FileSystem)
767 return STATUS_UNRECOGNIZED_VOLUME;
768
769 /* Check the volume */
770 DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName);
771 return ChkdskFileSystem(Volume->DeviceName,
772 Volume->FileSystem,
773 FixErrors,
774 Verbose,
775 CheckOnlyIfDirty,
776 ScanDrive,
777 Callback);
778 }
779
780 NTSTATUS
ChkdskPartition(_In_ PPARTENTRY PartEntry,_In_ BOOLEAN FixErrors,_In_ BOOLEAN Verbose,_In_ BOOLEAN CheckOnlyIfDirty,_In_ BOOLEAN ScanDrive,_In_opt_ PFMIFSCALLBACK Callback)781 ChkdskPartition(
782 _In_ PPARTENTRY PartEntry,
783 _In_ BOOLEAN FixErrors,
784 _In_ BOOLEAN Verbose,
785 _In_ BOOLEAN CheckOnlyIfDirty,
786 _In_ BOOLEAN ScanDrive,
787 _In_opt_ PFMIFSCALLBACK Callback)
788 {
789 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
790 ASSERT(PartEntry->Volume);
791
792 // if (!PartEntry->Volume) { check_raw_sectors(); } else { check_FS(); }
793
794 /* Check the associated volume */
795 return ChkdskVolume(&PartEntry->Volume->Info,
796 FixErrors,
797 Verbose,
798 CheckOnlyIfDirty,
799 ScanDrive,
800 Callback);
801 }
802
803 NTSTATUS
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)804 FormatVolume(
805 _In_ PVOLINFO Volume,
806 _In_ PCWSTR FileSystemName,
807 _In_ FMIFS_MEDIA_FLAG MediaFlag,
808 _In_opt_ PCWSTR Label,
809 _In_ BOOLEAN QuickFormat,
810 _In_ ULONG ClusterSize,
811 _In_opt_ PFMIFSCALLBACK Callback)
812 {
813 NTSTATUS Status;
814
815 if (!FileSystemName || !*FileSystemName)
816 {
817 DPRINT1("No file system specified\n");
818 return STATUS_UNRECOGNIZED_VOLUME;
819 }
820
821 /* Format the volume */
822 DPRINT("Volume->DeviceName: %S\n", Volume->DeviceName);
823 Status = FormatFileSystem(Volume->DeviceName,
824 FileSystemName,
825 MediaFlag,
826 Label,
827 QuickFormat,
828 ClusterSize,
829 Callback);
830 if (!NT_SUCCESS(Status))
831 return Status;
832
833 /* Set the new volume's file system and label */
834 RtlStringCbCopyW(Volume->FileSystem, sizeof(Volume->FileSystem), FileSystemName);
835 if (!Label) Label = L"";
836 RtlStringCbCopyW(Volume->VolumeLabel, sizeof(Volume->VolumeLabel), Label);
837
838 return STATUS_SUCCESS;
839 }
840
841 NTSTATUS
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)842 FormatPartition(
843 _In_ PPARTENTRY PartEntry,
844 _In_ PCWSTR FileSystemName,
845 _In_ FMIFS_MEDIA_FLAG MediaFlag,
846 _In_opt_ PCWSTR Label,
847 _In_ BOOLEAN QuickFormat,
848 _In_ ULONG ClusterSize,
849 _In_opt_ PFMIFSCALLBACK Callback)
850 {
851 NTSTATUS Status;
852 PDISKENTRY DiskEntry = PartEntry->DiskEntry;
853 UCHAR PartitionType;
854
855 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
856
857 if (!FileSystemName || !*FileSystemName)
858 {
859 DPRINT1("No file system specified\n");
860 return STATUS_UNRECOGNIZED_VOLUME;
861 }
862
863 /*
864 * Prepare the partition for formatting (for MBR disks, reset the
865 * partition type), and adjust the file system name in case of FAT
866 * vs. FAT32, depending on the geometry of the partition.
867 */
868
869 // FIXME: Do this only if QuickFormat == FALSE? What about FAT handling?
870
871 /*
872 * Retrieve a partition type as a hint only. It will be used to determine
873 * whether to actually use FAT12/16 or FAT32 file system, depending on the
874 * geometry of the partition. If the partition resides on an MBR disk,
875 * the partition style will be reset to this value as well, unless the
876 * partition is OEM.
877 */
878 PartitionType = FileSystemToMBRPartitionType(FileSystemName,
879 PartEntry->StartSector.QuadPart,
880 PartEntry->SectorCount.QuadPart);
881 if (PartitionType == PARTITION_ENTRY_UNUSED)
882 {
883 /* Unknown file system */
884 DPRINT1("Unknown file system '%S'\n", FileSystemName);
885 return STATUS_UNRECOGNIZED_VOLUME;
886 }
887
888 /* Reset the MBR partition type, unless this is an OEM partition */
889 if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR)
890 {
891 if (!IsOEMPartition(PartEntry->PartitionType))
892 SetMBRPartitionType(PartEntry, PartitionType);
893 }
894
895 /*
896 * Adjust the file system name in case of FAT vs. FAT32, according to
897 * the type of partition returned by FileSystemToMBRPartitionType().
898 */
899 if (_wcsicmp(FileSystemName, L"FAT") == 0)
900 {
901 if ((PartitionType == PARTITION_FAT32) ||
902 (PartitionType == PARTITION_FAT32_XINT13))
903 {
904 FileSystemName = L"FAT32";
905 }
906 }
907
908 /* Commit the partition changes to the disk */
909 Status = WritePartitions(DiskEntry);
910 if (!NT_SUCCESS(Status))
911 {
912 DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n",
913 DiskEntry->DiskNumber, Status);
914 return STATUS_PARTITION_FAILURE;
915 }
916
917 /* We must have an associated volume now */
918 ASSERT(PartEntry->Volume);
919
920 /* Format the associated volume */
921 Status = FormatVolume(&PartEntry->Volume->Info,
922 FileSystemName,
923 MediaFlag,
924 Label,
925 QuickFormat,
926 ClusterSize,
927 Callback);
928 if (!NT_SUCCESS(Status))
929 return Status;
930
931 PartEntry->Volume->FormatState = Formatted;
932 PartEntry->Volume->New = FALSE;
933 return STATUS_SUCCESS;
934 }
935
936
937 //
938 // FileSystem Volume Operations Queue
939 //
940
941 static FSVOL_OP
DoFormatting(_In_ PVOLENTRY Volume,_In_opt_ PVOID Context,_In_opt_ PFSVOL_CALLBACK FsVolCallback)942 DoFormatting(
943 _In_ PVOLENTRY Volume,
944 _In_opt_ PVOID Context,
945 _In_opt_ PFSVOL_CALLBACK FsVolCallback)
946 {
947 FSVOL_OP Result;
948 NTSTATUS Status = STATUS_SUCCESS;
949 PPARTENTRY PartEntry;
950 FORMAT_VOLUME_INFO FmtInfo = {0};
951
952 PartEntry = Volume->PartEntry;
953 ASSERT(PartEntry && (PartEntry->Volume == Volume));
954
955 FmtInfo.Volume = Volume;
956
957 RetryFormat:
958 Result = FsVolCallback(Context,
959 FSVOLNOTIFY_STARTFORMAT,
960 (ULONG_PTR)&FmtInfo,
961 FSVOL_FORMAT);
962 if (Result != FSVOL_DOIT)
963 goto EndFormat;
964
965 ASSERT(FmtInfo.FileSystemName && *FmtInfo.FileSystemName);
966
967 /* Format the partition */
968 Status = FormatPartition(PartEntry,
969 FmtInfo.FileSystemName,
970 FmtInfo.MediaFlag,
971 FmtInfo.Label,
972 FmtInfo.QuickFormat,
973 FmtInfo.ClusterSize,
974 FmtInfo.Callback);
975 if (!NT_SUCCESS(Status))
976 {
977 // FmtInfo.NtPathPartition = PathBuffer;
978 FmtInfo.ErrorStatus = Status;
979
980 Result = FsVolCallback(Context,
981 FSVOLNOTIFY_FORMATERROR,
982 (ULONG_PTR)&FmtInfo,
983 0);
984 if (Result == FSVOL_RETRY)
985 goto RetryFormat;
986 // else if (Result == FSVOL_ABORT || Result == FSVOL_SKIP), stop.
987 }
988
989 EndFormat:
990 /* This notification is always sent, even in case of error or abort */
991 FmtInfo.ErrorStatus = Status;
992 FsVolCallback(Context,
993 FSVOLNOTIFY_ENDFORMAT,
994 (ULONG_PTR)&FmtInfo,
995 0);
996 return Result;
997 }
998
999 static FSVOL_OP
DoChecking(_In_ PVOLENTRY Volume,_In_opt_ PVOID Context,_In_opt_ PFSVOL_CALLBACK FsVolCallback)1000 DoChecking(
1001 _In_ PVOLENTRY Volume,
1002 _In_opt_ PVOID Context,
1003 _In_opt_ PFSVOL_CALLBACK FsVolCallback)
1004 {
1005 FSVOL_OP Result;
1006 NTSTATUS Status = STATUS_SUCCESS;
1007 CHECK_VOLUME_INFO ChkInfo = {0};
1008
1009 ASSERT(*Volume->Info.FileSystem);
1010
1011 ChkInfo.Volume = Volume;
1012
1013 RetryCheck:
1014 Result = FsVolCallback(Context,
1015 FSVOLNOTIFY_STARTCHECK,
1016 (ULONG_PTR)&ChkInfo,
1017 FSVOL_CHECK);
1018 if (Result != FSVOL_DOIT)
1019 goto EndCheck;
1020
1021 /* Check the volume */
1022 Status = ChkdskVolume(&Volume->Info,
1023 ChkInfo.FixErrors,
1024 ChkInfo.Verbose,
1025 ChkInfo.CheckOnlyIfDirty,
1026 ChkInfo.ScanDrive,
1027 ChkInfo.Callback);
1028
1029 /* If volume checking succeeded, or if it is not supported
1030 * with the current file system, disable checks on the volume */
1031 if (NT_SUCCESS(Status) || (Status == STATUS_NOT_SUPPORTED))
1032 Volume->NeedsCheck = FALSE;
1033
1034 if (!NT_SUCCESS(Status))
1035 {
1036 // ChkInfo.NtPathPartition = PathBuffer;
1037 ChkInfo.ErrorStatus = Status;
1038
1039 Result = FsVolCallback(Context,
1040 FSVOLNOTIFY_CHECKERROR,
1041 (ULONG_PTR)&ChkInfo,
1042 0);
1043 if (Result == FSVOL_RETRY)
1044 goto RetryCheck;
1045 // else if (Result == FSVOL_ABORT || Result == FSVOL_SKIP), stop.
1046
1047 // Volume->NeedsCheck = FALSE;
1048 }
1049
1050 EndCheck:
1051 /* This notification is always sent, even in case of error or abort */
1052 ChkInfo.ErrorStatus = Status;
1053 FsVolCallback(Context,
1054 FSVOLNOTIFY_ENDCHECK,
1055 (ULONG_PTR)&ChkInfo,
1056 0);
1057 return Result;
1058 }
1059
1060 static
1061 PVOLENTRY
GetNextUnformattedVolume(_In_ PPARTLIST List,_In_opt_ PVOLENTRY Volume)1062 GetNextUnformattedVolume(
1063 _In_ PPARTLIST List,
1064 _In_opt_ PVOLENTRY Volume)
1065 {
1066 PLIST_ENTRY Entry;
1067
1068 for (;;)
1069 {
1070 /* If we have a current volume, get the next one, otherwise get the first */
1071 Entry = (Volume ? &Volume->ListEntry : &List->VolumesList);
1072 Entry = Entry->Flink;
1073
1074 if (Entry == &List->VolumesList)
1075 return NULL;
1076
1077 Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
1078 if (Volume->New && (Volume->FormatState == Unformatted))
1079 {
1080 /* Found a candidate, return it */
1081 return Volume;
1082 }
1083 }
1084 }
1085
1086 BOOLEAN
FsVolCommitOpsQueue(_In_ PPARTLIST PartitionList,_In_ PVOLENTRY SystemVolume,_In_ PVOLENTRY InstallVolume,_In_opt_ PFSVOL_CALLBACK FsVolCallback,_In_opt_ PVOID Context)1087 FsVolCommitOpsQueue(
1088 _In_ PPARTLIST PartitionList,
1089 _In_ PVOLENTRY SystemVolume,
1090 _In_ PVOLENTRY InstallVolume,
1091 _In_opt_ PFSVOL_CALLBACK FsVolCallback,
1092 _In_opt_ PVOID Context)
1093 {
1094 BOOLEAN Success = TRUE; // Suppose success originally.
1095 FSVOL_OP Result;
1096 PLIST_ENTRY Entry;
1097 PVOLENTRY Volume;
1098
1099 /* Machine state for the format step */
1100 typedef enum _FORMATMACHINESTATE
1101 {
1102 Start,
1103 FormatSystemVolume,
1104 FormatInstallVolume,
1105 FormatOtherVolume,
1106 FormatDone
1107 } FORMATMACHINESTATE;
1108 FORMATMACHINESTATE FormatState, OldFormatState;
1109 static const PCSTR FormatStateNames[] = {
1110 "Start",
1111 "FormatSystemVolume",
1112 "FormatInstallVolume",
1113 "FormatOtherVolume",
1114 "FormatDone"
1115 };
1116
1117 ASSERT(PartitionList && SystemVolume && InstallVolume);
1118
1119 /* Commit all partition changes to all the disks */
1120 if (!WritePartitionsToDisk(PartitionList))
1121 {
1122 DPRINT("WritePartitionsToDisk() failed\n");
1123 /* Result = */ FsVolCallback(Context,
1124 FSVOLNOTIFY_PARTITIONERROR,
1125 STATUS_PARTITION_FAILURE, // FIXME
1126 0);
1127 return FALSE;
1128 }
1129
1130 //
1131 // FIXME: Should we do the following here, or in the caller?
1132 //
1133 /*
1134 * In all cases, whether or not we are going to perform a formatting,
1135 * we must perform a file system check of both the system and the
1136 * installation volumes.
1137 */
1138 SystemVolume->NeedsCheck = TRUE;
1139 InstallVolume->NeedsCheck = TRUE;
1140
1141 Result = FsVolCallback(Context,
1142 FSVOLNOTIFY_STARTQUEUE,
1143 0, 0);
1144 if (Result == FSVOL_ABORT)
1145 return FALSE;
1146
1147 /*
1148 * Commit the Format queue
1149 */
1150
1151 Result = FsVolCallback(Context,
1152 FSVOLNOTIFY_STARTSUBQUEUE,
1153 FSVOL_FORMAT,
1154 0);
1155 if (Result == FSVOL_ABORT)
1156 return FALSE;
1157 /** HACK!! **/
1158 if (Result == FSVOL_SKIP)
1159 goto StartCheckQueue;
1160 /** END HACK!! **/
1161
1162 /* Reset the formatter machine state */
1163 FormatState = Start;
1164 Volume = NULL;
1165 NextFormat:
1166 OldFormatState = FormatState;
1167 switch (FormatState)
1168 {
1169 case Start:
1170 {
1171 /*
1172 * We start by formatting the system volume in case it is new
1173 * (it didn't exist before) and is not the same as the installation
1174 * volume. Otherwise we just require a file system check on it,
1175 * and start by formatting the installation volume instead.
1176 */
1177 if (SystemVolume != InstallVolume)
1178 {
1179 Volume = SystemVolume;
1180
1181 if (Volume->FormatState == Unformatted)
1182 {
1183 // TODO: Should we let the user use a custom file system,
1184 // or should we always use FAT(32) for it?
1185 // For "compatibility", FAT(32) would be best indeed.
1186
1187 FormatState = FormatSystemVolume;
1188 DPRINT1("FormatState: %s --> %s\n",
1189 FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1190 break;
1191 }
1192
1193 /* The system volume is separate, so it had better be formatted! */
1194 ASSERT(Volume->FormatState == Formatted);
1195
1196 /* Require a file system check on the system volume too */
1197 Volume->NeedsCheck = TRUE;
1198 }
1199 __fallthrough;
1200 }
1201
1202 case FormatSystemVolume:
1203 {
1204 Volume = InstallVolume;
1205
1206 FormatState = FormatInstallVolume;
1207 DPRINT1("FormatState: %s --> %s\n",
1208 FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1209 break;
1210 }
1211
1212 case FormatInstallVolume:
1213 /* Restart volume enumeration */
1214 Volume = NULL;
1215 case FormatOtherVolume:
1216 {
1217 Volume = GetNextUnformattedVolume(PartitionList, Volume);
1218
1219 FormatState = (Volume ? FormatOtherVolume : FormatDone);
1220 DPRINT1("FormatState: %s --> %s\n",
1221 FormatStateNames[OldFormatState], FormatStateNames[FormatState]);
1222 if (Volume)
1223 break;
1224 __fallthrough;
1225 }
1226
1227 case FormatDone:
1228 {
1229 DPRINT1("FormatState: FormatDone\n");
1230 Success = TRUE;
1231 goto EndFormat;
1232 }
1233 DEFAULT_UNREACHABLE;
1234 }
1235
1236 Result = DoFormatting(Volume, Context, FsVolCallback);
1237 if (Result == FSVOL_ABORT)
1238 {
1239 Success = FALSE;
1240 goto Quit;
1241 }
1242 /* Schedule a check for this volume */
1243 Volume->NeedsCheck = TRUE;
1244 /* Go to the next volume to be formatted */
1245 goto NextFormat;
1246
1247 EndFormat:
1248 FsVolCallback(Context,
1249 FSVOLNOTIFY_ENDSUBQUEUE,
1250 FSVOL_FORMAT,
1251 0);
1252
1253
1254 /*
1255 * Commit the CheckFS queue
1256 */
1257
1258 StartCheckQueue:
1259 Result = FsVolCallback(Context,
1260 FSVOLNOTIFY_STARTSUBQUEUE,
1261 FSVOL_CHECK,
1262 0);
1263 if (Result == FSVOL_ABORT)
1264 return FALSE;
1265
1266 /* Loop through each unchecked volume and do the check */
1267 for (Entry = PartitionList->VolumesList.Flink;
1268 Entry != &PartitionList->VolumesList;
1269 Entry = Entry->Flink)
1270 {
1271 Volume = CONTAINING_RECORD(Entry, VOLENTRY, ListEntry);
1272 if (!Volume->NeedsCheck)
1273 continue;
1274
1275 /* Found a candidate */
1276 ASSERT(Volume->FormatState == Formatted);
1277 Result = DoChecking(Volume, Context, FsVolCallback);
1278 if (Result == FSVOL_ABORT)
1279 {
1280 Success = FALSE;
1281 goto Quit;
1282 }
1283 /* Go to the next volume to be checked */
1284 }
1285 Success = TRUE;
1286
1287 FsVolCallback(Context,
1288 FSVOLNOTIFY_ENDSUBQUEUE,
1289 FSVOL_CHECK,
1290 0);
1291
1292
1293 Quit:
1294 /* All the queues have been committed */
1295 FsVolCallback(Context,
1296 FSVOLNOTIFY_ENDQUEUE,
1297 Success,
1298 0);
1299 return Success;
1300 }
1301
1302 /* EOF */
1303