xref: /reactos/base/setup/lib/fsutil.c (revision 682f85ad)
1 /*
2  * PROJECT:     ReactOS Setup Library
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Filesystem Format and ChkDsk support functions.
5  * COPYRIGHT:   Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net)
6  *              Copyright 2017-2020 Hermes Belusca-Maito
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 // TODO: Add more bootsector structures!
115 
116 #include <poppack.h>
117 
118 
119 /* LOCALS *******************************************************************/
120 
121 /** IFS_PROVIDER **/
122 typedef struct _FILE_SYSTEM
123 {
124     PCWSTR FileSystemName;
125     PULIB_FORMAT FormatFunc;
126     PULIB_CHKDSK ChkdskFunc;
127 } FILE_SYSTEM, *PFILE_SYSTEM;
128 
129 /* The list of file systems on which we can install ReactOS */
130 static FILE_SYSTEM RegisteredFileSystems[] =
131 {
132     /* NOTE: The FAT formatter will automatically
133      * determine whether to use FAT12/16 or FAT32. */
134     { L"FAT"  , VfatFormat, VfatChkdsk },
135     { L"FAT32", VfatFormat, VfatChkdsk },
136 #if 0
137     { L"FATX" , VfatxFormat, VfatxChkdsk },
138     { L"NTFS" , NtfsFormat, NtfsChkdsk },
139 #endif
140     { L"BTRFS", BtrfsFormat, BtrfsChkdsk },
141 #if 0
142     { L"EXT2" , Ext2Format, Ext2Chkdsk },
143     { L"EXT3" , Ext2Format, Ext2Chkdsk },
144     { L"EXT4" , Ext2Format, Ext2Chkdsk },
145     { L"FFS"  , FfsFormat , FfsChkdsk  },
146     { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk },
147 #endif
148 };
149 
150 
151 /* FUNCTIONS ****************************************************************/
152 
153 /** QueryAvailableFileSystemFormat() **/
154 BOOLEAN
155 GetRegisteredFileSystems(
156     IN ULONG Index,
157     OUT PCWSTR* FileSystemName)
158 {
159     if (Index >= ARRAYSIZE(RegisteredFileSystems))
160         return FALSE;
161 
162     *FileSystemName = RegisteredFileSystems[Index].FileSystemName;
163 
164     return TRUE;
165 }
166 
167 
168 /** GetProvider() **/
169 static PFILE_SYSTEM
170 GetFileSystemByName(
171     IN PCWSTR FileSystemName)
172 {
173 #if 0 // Reenable when the list of registered FSes will again be dynamic
174 
175     PLIST_ENTRY ListEntry;
176     PFILE_SYSTEM_ITEM Item;
177 
178     ListEntry = List->ListHead.Flink;
179     while (ListEntry != &List->ListHead)
180     {
181         Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
182         if (Item->FileSystemName &&
183             (wcsicmp(FileSystemName, Item->FileSystemName) == 0))
184         {
185             return Item;
186         }
187 
188         ListEntry = ListEntry->Flink;
189     }
190 
191 #else
192 
193     ULONG Count = ARRAYSIZE(RegisteredFileSystems);
194     PFILE_SYSTEM FileSystems = RegisteredFileSystems;
195 
196     ASSERT(FileSystems && Count != 0);
197 
198     while (Count--)
199     {
200         if (FileSystems->FileSystemName &&
201             (wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0))
202         {
203             return FileSystems;
204         }
205 
206         ++FileSystems;
207     }
208 
209 #endif
210 
211     return NULL;
212 }
213 
214 
215 /** ChkdskEx() **/
216 NTSTATUS
217 ChkdskFileSystem_UStr(
218     IN PUNICODE_STRING DriveRoot,
219     IN PCWSTR FileSystemName,
220     IN BOOLEAN FixErrors,
221     IN BOOLEAN Verbose,
222     IN BOOLEAN CheckOnlyIfDirty,
223     IN BOOLEAN ScanDrive,
224     IN PFMIFSCALLBACK Callback)
225 {
226     PFILE_SYSTEM FileSystem;
227     NTSTATUS Status;
228     BOOLEAN Success;
229 
230     FileSystem = GetFileSystemByName(FileSystemName);
231 
232     if (!FileSystem || !FileSystem->ChkdskFunc)
233     {
234         // Success = FALSE;
235         // Callback(DONE, 0, &Success);
236         return STATUS_NOT_SUPPORTED;
237     }
238 
239     Status = STATUS_SUCCESS;
240     Success = FileSystem->ChkdskFunc(DriveRoot,
241                                      Callback,
242                                      FixErrors,
243                                      Verbose,
244                                      CheckOnlyIfDirty,
245                                      ScanDrive,
246                                      NULL,
247                                      NULL,
248                                      NULL,
249                                      NULL,
250                                      (PULONG)&Status);
251     if (!Success)
252         DPRINT1("ChkdskFunc() failed with Status 0x%lx\n", Status);
253 
254     // Callback(DONE, 0, &Success);
255 
256     return Status;
257 }
258 
259 NTSTATUS
260 ChkdskFileSystem(
261     IN PCWSTR DriveRoot,
262     IN PCWSTR FileSystemName,
263     IN BOOLEAN FixErrors,
264     IN BOOLEAN Verbose,
265     IN BOOLEAN CheckOnlyIfDirty,
266     IN BOOLEAN ScanDrive,
267     IN PFMIFSCALLBACK Callback)
268 {
269     UNICODE_STRING DriveRootU;
270 
271     RtlInitUnicodeString(&DriveRootU, DriveRoot);
272     return ChkdskFileSystem_UStr(&DriveRootU,
273                                  FileSystemName,
274                                  FixErrors,
275                                  Verbose,
276                                  CheckOnlyIfDirty,
277                                  ScanDrive,
278                                  Callback);
279 }
280 
281 
282 /** FormatEx() **/
283 NTSTATUS
284 FormatFileSystem_UStr(
285     IN PUNICODE_STRING DriveRoot,
286     IN PCWSTR FileSystemName,
287     IN FMIFS_MEDIA_FLAG MediaFlag,
288     IN PUNICODE_STRING Label,
289     IN BOOLEAN QuickFormat,
290     IN ULONG ClusterSize,
291     IN PFMIFSCALLBACK Callback)
292 {
293     PFILE_SYSTEM FileSystem;
294     BOOLEAN Success;
295     BOOLEAN BackwardCompatible = FALSE; // Default to latest FS versions.
296     MEDIA_TYPE MediaType;
297 
298     FileSystem = GetFileSystemByName(FileSystemName);
299 
300     if (!FileSystem || !FileSystem->FormatFunc)
301     {
302         // Success = FALSE;
303         // Callback(DONE, 0, &Success);
304         return STATUS_NOT_SUPPORTED;
305     }
306 
307     /* Set the BackwardCompatible flag in case we format with older FAT12/16 */
308     if (wcsicmp(FileSystemName, L"FAT") == 0)
309         BackwardCompatible = TRUE;
310     // else if (wcsicmp(FileSystemName, L"FAT32") == 0)
311         // BackwardCompatible = FALSE;
312 
313     /* Convert the FMIFS MediaFlag to a NT MediaType */
314     // FIXME: Actually covert all the possible flags.
315     switch (MediaFlag)
316     {
317     case FMIFS_FLOPPY:
318         MediaType = F5_320_1024; // FIXME: This is hardfixed!
319         break;
320     case FMIFS_REMOVABLE:
321         MediaType = RemovableMedia;
322         break;
323     case FMIFS_HARDDISK:
324         MediaType = FixedMedia;
325         break;
326     default:
327         DPRINT1("Unknown FMIFS MediaFlag %d, converting 1-to-1 to NT MediaType\n",
328                 MediaFlag);
329         MediaType = (MEDIA_TYPE)MediaFlag;
330         break;
331     }
332 
333     Success = FileSystem->FormatFunc(DriveRoot,
334                                      Callback,
335                                      QuickFormat,
336                                      BackwardCompatible,
337                                      MediaType,
338                                      Label,
339                                      ClusterSize);
340     if (!Success)
341         DPRINT1("FormatFunc() failed\n");
342 
343     // Callback(DONE, 0, &Success);
344 
345     return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
346 }
347 
348 NTSTATUS
349 FormatFileSystem(
350     IN PCWSTR DriveRoot,
351     IN PCWSTR FileSystemName,
352     IN FMIFS_MEDIA_FLAG MediaFlag,
353     IN PCWSTR Label,
354     IN BOOLEAN QuickFormat,
355     IN ULONG ClusterSize,
356     IN PFMIFSCALLBACK Callback)
357 {
358     UNICODE_STRING DriveRootU;
359     UNICODE_STRING LabelU;
360 
361     RtlInitUnicodeString(&DriveRootU, DriveRoot);
362     RtlInitUnicodeString(&LabelU, Label);
363 
364     return FormatFileSystem_UStr(&DriveRootU,
365                                  FileSystemName,
366                                  MediaFlag,
367                                  &LabelU,
368                                  QuickFormat,
369                                  ClusterSize,
370                                  Callback);
371 }
372 
373 
374 //
375 // Bootsector routines
376 //
377 
378 NTSTATUS
379 InstallFatBootCode(
380     IN PCWSTR SrcPath,          // FAT12/16 bootsector source file (on the installation medium)
381     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
382     IN HANDLE RootPartition)    // Partition holding the (old) FAT12/16 information
383 {
384     NTSTATUS Status;
385     UNICODE_STRING Name;
386     IO_STATUS_BLOCK IoStatusBlock;
387     LARGE_INTEGER FileOffset;
388     BOOTCODE OrigBootSector = {0};
389     BOOTCODE NewBootSector  = {0};
390 
391     /* Allocate and read the current original partition bootsector */
392     Status = ReadBootCodeByHandle(&OrigBootSector,
393                                   RootPartition,
394                                   FAT_BOOTSECTOR_SIZE);
395     if (!NT_SUCCESS(Status))
396         return Status;
397 
398     /* Allocate and read the new bootsector from SrcPath */
399     RtlInitUnicodeString(&Name, SrcPath);
400     Status = ReadBootCodeFromFile(&NewBootSector,
401                                   &Name,
402                                   FAT_BOOTSECTOR_SIZE);
403     if (!NT_SUCCESS(Status))
404     {
405         FreeBootCode(&OrigBootSector);
406         return Status;
407     }
408 
409     /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */
410     RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName,
411                   &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
412                   FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) -
413                   FIELD_OFFSET(FAT_BOOTSECTOR, OemName));
414 
415     /* Free the original bootsector */
416     FreeBootCode(&OrigBootSector);
417 
418     /* Write the new bootsector to DstPath */
419     FileOffset.QuadPart = 0ULL;
420     Status = NtWriteFile(DstPath,
421                          NULL,
422                          NULL,
423                          NULL,
424                          &IoStatusBlock,
425                          NewBootSector.BootCode,
426                          NewBootSector.Length,
427                          &FileOffset,
428                          NULL);
429 
430     /* Free the new bootsector */
431     FreeBootCode(&NewBootSector);
432 
433     return Status;
434 }
435 
436 NTSTATUS
437 InstallFat32BootCode(
438     IN PCWSTR SrcPath,          // FAT32 bootsector source file (on the installation medium)
439     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
440     IN HANDLE RootPartition)    // Partition holding the (old) FAT32 information
441 {
442     NTSTATUS Status;
443     UNICODE_STRING Name;
444     IO_STATUS_BLOCK IoStatusBlock;
445     LARGE_INTEGER FileOffset;
446     USHORT BackupBootSector = 0;
447     BOOTCODE OrigBootSector = {0};
448     BOOTCODE NewBootSector  = {0};
449 
450     /* Allocate and read the current original partition bootsector */
451     Status = ReadBootCodeByHandle(&OrigBootSector,
452                                   RootPartition,
453                                   FAT32_BOOTSECTOR_SIZE);
454     if (!NT_SUCCESS(Status))
455         return Status;
456 
457     /* Allocate and read the new bootsector (2 sectors) from SrcPath */
458     RtlInitUnicodeString(&Name, SrcPath);
459     Status = ReadBootCodeFromFile(&NewBootSector,
460                                   &Name,
461                                   2 * FAT32_BOOTSECTOR_SIZE);
462     if (!NT_SUCCESS(Status))
463     {
464         FreeBootCode(&OrigBootSector);
465         return Status;
466     }
467 
468     /* Adjust the bootsector (copy a part of the FAT32 BPB) */
469     RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName,
470                   &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
471                   FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) -
472                   FIELD_OFFSET(FAT32_BOOTSECTOR, OemName));
473 
474     /*
475      * We know we copy the boot code to a file only when DstPath != RootPartition,
476      * otherwise the boot code is copied to the specified root partition.
477      */
478     if (DstPath != RootPartition)
479     {
480         /* Copy to a file: Disable the backup bootsector */
481         ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0;
482     }
483     else
484     {
485         /* Copy to a disk: Get the location of the backup bootsector */
486         BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector;
487     }
488 
489     /* Free the original bootsector */
490     FreeBootCode(&OrigBootSector);
491 
492     /* Write the first sector of the new bootcode to DstPath sector 0 */
493     FileOffset.QuadPart = 0ULL;
494     Status = NtWriteFile(DstPath,
495                          NULL,
496                          NULL,
497                          NULL,
498                          &IoStatusBlock,
499                          NewBootSector.BootCode,
500                          FAT32_BOOTSECTOR_SIZE,
501                          &FileOffset,
502                          NULL);
503     if (!NT_SUCCESS(Status))
504     {
505         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
506         FreeBootCode(&NewBootSector);
507         return Status;
508     }
509 
510     if (DstPath == RootPartition)
511     {
512         /* Copy to a disk: Write the backup bootsector */
513         if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF))
514         {
515             FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE);
516             Status = NtWriteFile(DstPath,
517                                  NULL,
518                                  NULL,
519                                  NULL,
520                                  &IoStatusBlock,
521                                  NewBootSector.BootCode,
522                                  FAT32_BOOTSECTOR_SIZE,
523                                  &FileOffset,
524                                  NULL);
525             if (!NT_SUCCESS(Status))
526             {
527                 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
528                 FreeBootCode(&NewBootSector);
529                 return Status;
530             }
531         }
532     }
533 
534     /* Write the second sector of the new bootcode to boot disk sector 14 */
535     // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE);
536     FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE;
537     Status = NtWriteFile(DstPath,   // or really RootPartition ???
538                          NULL,
539                          NULL,
540                          NULL,
541                          &IoStatusBlock,
542                          ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE),
543                          FAT32_BOOTSECTOR_SIZE,
544                          &FileOffset,
545                          NULL);
546     if (!NT_SUCCESS(Status))
547     {
548         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
549     }
550 
551     /* Free the new bootsector */
552     FreeBootCode(&NewBootSector);
553 
554     return Status;
555 }
556 
557 NTSTATUS
558 InstallBtrfsBootCode(
559     IN PCWSTR SrcPath,          // BTRFS bootsector source file (on the installation medium)
560     IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
561     IN HANDLE RootPartition)    // Partition holding the (old) BTRFS information
562 {
563     NTSTATUS Status;
564     NTSTATUS LockStatus;
565     UNICODE_STRING Name;
566     IO_STATUS_BLOCK IoStatusBlock;
567     LARGE_INTEGER FileOffset;
568     PARTITION_INFORMATION_EX PartInfo;
569     BOOTCODE NewBootSector = {0};
570 
571     /* Allocate and read the new bootsector from SrcPath */
572     RtlInitUnicodeString(&Name, SrcPath);
573     Status = ReadBootCodeFromFile(&NewBootSector,
574                                   &Name,
575                                   BTRFS_BOOTSECTOR_SIZE);
576     if (!NT_SUCCESS(Status))
577         return Status;
578 
579     /*
580      * The BTRFS driver requires the volume to be locked in order to modify
581      * the first sectors of the partition, even though they are outside the
582      * file-system space / in the reserved area (they are situated before
583      * the super-block at 0x1000) and is in principle allowed by the NT
584      * storage stack.
585      * So we lock here in order to write the bootsector at sector 0.
586      * If locking fails, we ignore and continue nonetheless.
587      */
588     LockStatus = NtFsControlFile(DstPath,
589                                  NULL,
590                                  NULL,
591                                  NULL,
592                                  &IoStatusBlock,
593                                  FSCTL_LOCK_VOLUME,
594                                  NULL,
595                                  0,
596                                  NULL,
597                                  0);
598     if (!NT_SUCCESS(LockStatus))
599     {
600         DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus);
601     }
602 
603     /* Obtain partition info and write it to the bootsector */
604     Status = NtDeviceIoControlFile(RootPartition,
605                                    NULL,
606                                    NULL,
607                                    NULL,
608                                    &IoStatusBlock,
609                                    IOCTL_DISK_GET_PARTITION_INFO_EX,
610                                    NULL,
611                                    0,
612                                    &PartInfo,
613                                    sizeof(PartInfo));
614     if (!NT_SUCCESS(Status))
615     {
616         DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status);
617         goto Quit;
618     }
619 
620     /* Write new bootsector to RootPath */
621     ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA =
622         PartInfo.StartingOffset.QuadPart / SECTORSIZE;
623 
624     /* Write sector 0 */
625     FileOffset.QuadPart = 0ULL;
626     Status = NtWriteFile(DstPath,
627                          NULL,
628                          NULL,
629                          NULL,
630                          &IoStatusBlock,
631                          NewBootSector.BootCode,
632                          NewBootSector.Length,
633                          &FileOffset,
634                          NULL);
635     if (!NT_SUCCESS(Status))
636     {
637         DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
638         goto Quit;
639     }
640 
641 Quit:
642     /* Unlock the volume */
643     LockStatus = NtFsControlFile(DstPath,
644                                  NULL,
645                                  NULL,
646                                  NULL,
647                                  &IoStatusBlock,
648                                  FSCTL_UNLOCK_VOLUME,
649                                  NULL,
650                                  0,
651                                  NULL,
652                                  0);
653     if (!NT_SUCCESS(LockStatus))
654     {
655         DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus);
656     }
657 
658     /* Free the new bootsector */
659     FreeBootCode(&NewBootSector);
660 
661     return Status;
662 }
663 
664 
665 //
666 // Formatting routines
667 //
668 
669 NTSTATUS
670 ChkdskPartition(
671     IN PPARTENTRY PartEntry,
672     IN BOOLEAN FixErrors,
673     IN BOOLEAN Verbose,
674     IN BOOLEAN CheckOnlyIfDirty,
675     IN BOOLEAN ScanDrive,
676     IN PFMIFSCALLBACK Callback)
677 {
678     NTSTATUS Status;
679     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
680     // UNICODE_STRING PartitionRootPath;
681     WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer
682 
683     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
684 
685     /* HACK: Do not try to check a partition with an unknown filesystem */
686     if (!*PartEntry->FileSystem)
687     {
688         PartEntry->NeedsCheck = FALSE;
689         return STATUS_SUCCESS;
690     }
691 
692     /* Set PartitionRootPath */
693     RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath),
694                         L"\\Device\\Harddisk%lu\\Partition%lu",
695                         DiskEntry->DiskNumber,
696                         PartEntry->PartitionNumber);
697     DPRINT("PartitionRootPath: %S\n", PartitionRootPath);
698 
699     /* Check the partition */
700     Status = ChkdskFileSystem(PartitionRootPath,
701                               PartEntry->FileSystem,
702                               FixErrors,
703                               Verbose,
704                               CheckOnlyIfDirty,
705                               ScanDrive,
706                               Callback);
707     if (!NT_SUCCESS(Status))
708         return Status;
709 
710     PartEntry->NeedsCheck = FALSE;
711     return STATUS_SUCCESS;
712 }
713 
714 NTSTATUS
715 FormatPartition(
716     IN PPARTENTRY PartEntry,
717     IN PCWSTR FileSystemName,
718     IN FMIFS_MEDIA_FLAG MediaFlag,
719     IN PCWSTR Label,
720     IN BOOLEAN QuickFormat,
721     IN ULONG ClusterSize,
722     IN PFMIFSCALLBACK Callback)
723 {
724     NTSTATUS Status;
725     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
726     UCHAR PartitionType;
727     // UNICODE_STRING PartitionRootPath;
728     WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer
729 
730     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
731 
732     if (!FileSystemName || !*FileSystemName)
733     {
734         DPRINT1("No file system specified?\n");
735         return STATUS_UNRECOGNIZED_VOLUME;
736     }
737 
738     /*
739      * Prepare the partition for formatting (for MBR disks, reset the
740      * partition type), and adjust the filesystem name in case of FAT
741      * vs. FAT32, depending on the geometry of the partition.
742      */
743 
744 // FIXME: Do this only if QuickFormat == FALSE? What about FAT handling?
745 
746     /*
747      * Retrieve a partition type as a hint only. It will be used to determine
748      * whether to actually use FAT12/16 or FAT32 filesystem, depending on the
749      * geometry of the partition. If the partition resides on an MBR disk,
750      * the partition style will be reset to this value as well, unless the
751      * partition is OEM.
752      */
753     PartitionType = FileSystemToMBRPartitionType(FileSystemName,
754                                                  PartEntry->StartSector.QuadPart,
755                                                  PartEntry->SectorCount.QuadPart);
756     if (PartitionType == PARTITION_ENTRY_UNUSED)
757     {
758         /* Unknown file system */
759         DPRINT1("Unknown file system '%S'\n", FileSystemName);
760         return STATUS_UNRECOGNIZED_VOLUME;
761     }
762 
763     /* Reset the MBR partition type, unless this is an OEM partition */
764     if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR)
765     {
766         if (!IsOEMPartition(PartEntry->PartitionType))
767             SetMBRPartitionType(PartEntry, PartitionType);
768     }
769 
770     /*
771      * Adjust the filesystem name in case of FAT vs. FAT32, according to
772      * the type of partition returned by FileSystemToMBRPartitionType().
773      */
774     if (wcsicmp(FileSystemName, L"FAT") == 0)
775     {
776         if ((PartitionType == PARTITION_FAT32) ||
777             (PartitionType == PARTITION_FAT32_XINT13))
778         {
779             FileSystemName = L"FAT32";
780         }
781     }
782 
783     /* Commit the partition changes to the disk */
784     Status = WritePartitions(DiskEntry);
785     if (!NT_SUCCESS(Status))
786     {
787         DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n",
788                 DiskEntry->DiskNumber, Status);
789         return STATUS_PARTITION_FAILURE;
790     }
791 
792     /* Set PartitionRootPath */
793     RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath),
794                         L"\\Device\\Harddisk%lu\\Partition%lu",
795                         DiskEntry->DiskNumber,
796                         PartEntry->PartitionNumber);
797     DPRINT("PartitionRootPath: %S\n", PartitionRootPath);
798 
799     /* Format the partition */
800     Status = FormatFileSystem(PartitionRootPath,
801                               FileSystemName,
802                               MediaFlag,
803                               Label,
804                               QuickFormat,
805                               ClusterSize,
806                               Callback);
807     if (!NT_SUCCESS(Status))
808         return Status;
809 
810 //
811 // TODO: Here, call a partlist.c function that update the actual
812 // FS name and the label fields of the volume.
813 //
814     PartEntry->FormatState = Formatted;
815 
816     /* Set the new partition's file system proper */
817     RtlStringCbCopyW(PartEntry->FileSystem,
818                      sizeof(PartEntry->FileSystem),
819                      FileSystemName);
820 
821     PartEntry->New = FALSE;
822 
823     return STATUS_SUCCESS;
824 }
825 
826 /* EOF */
827