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