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