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