[SETUPLIB] Code re-organization in bootsup.c and fsutil.c.
[reactos.git] / base / setup / lib / fsutil.c
index 2abe8e4..9bd0aee 100644 (file)
@@ -3,7 +3,7 @@
  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
  * PURPOSE:     Filesystem support functions
  * COPYRIGHT:   Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net)
- *              Copyright 2017-2019 Hermes Belusca-Maito
+ *              Copyright 2017-2020 Hermes Belusca-Maito
  */
 
 //
@@ -18,6 +18,7 @@
 
 #include "partlist.h"
 #include "fsrec.h"
+#include "bootcode.h"
 #include "fsutil.h"
 
 #include <fslib/vfatlib.h>
 #include <debug.h>
 
 
+/* TYPEDEFS *****************************************************************/
+
+#include <pshpack1.h>
+typedef struct _FAT_BOOTSECTOR
+{
+    UCHAR       JumpBoot[3];                // Jump instruction to boot code
+    CHAR        OemName[8];                 // "MSWIN4.1" for MS formatted volumes
+    USHORT      BytesPerSector;             // Bytes per sector
+    UCHAR       SectorsPerCluster;          // Number of sectors in a cluster
+    USHORT      ReservedSectors;            // Reserved sectors, usually 1 (the bootsector)
+    UCHAR       NumberOfFats;               // Number of FAT tables
+    USHORT      RootDirEntries;             // Number of root directory entries (fat12/16)
+    USHORT      TotalSectors;               // Number of total sectors on the drive, 16-bit
+    UCHAR       MediaDescriptor;            // Media descriptor byte
+    USHORT      SectorsPerFat;              // Sectors per FAT table (fat12/16)
+    USHORT      SectorsPerTrack;            // Number of sectors in a track
+    USHORT      NumberOfHeads;              // Number of heads on the disk
+    ULONG       HiddenSectors;              // Hidden sectors (sectors before the partition start like the partition table)
+    ULONG       TotalSectorsBig;            // This field is the new 32-bit total count of sectors on the volume
+    UCHAR       DriveNumber;                // Int 0x13 drive number (e.g. 0x80)
+    UCHAR       Reserved1;                  // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
+    UCHAR       BootSignature;              // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
+    ULONG       VolumeSerialNumber;         // Volume serial number
+    CHAR        VolumeLabel[11];            // Volume label. This field matches the 11-byte volume label recorded in the root directory
+    CHAR        FileSystemType[8];          // One of the strings "FAT12   ", "FAT16   ", or "FAT     "
+
+    UCHAR       BootCodeAndData[448];       // The remainder of the boot sector
+
+    USHORT      BootSectorMagic;            // 0xAA55
+
+} FAT_BOOTSECTOR, *PFAT_BOOTSECTOR;
+C_ASSERT(sizeof(FAT_BOOTSECTOR) == FAT_BOOTSECTOR_SIZE);
+
+typedef struct _FAT32_BOOTSECTOR
+{
+    UCHAR       JumpBoot[3];                // Jump instruction to boot code
+    CHAR        OemName[8];                 // "MSWIN4.1" for MS formatted volumes
+    USHORT      BytesPerSector;             // Bytes per sector
+    UCHAR       SectorsPerCluster;          // Number of sectors in a cluster
+    USHORT      ReservedSectors;            // Reserved sectors, usually 1 (the bootsector)
+    UCHAR       NumberOfFats;               // Number of FAT tables
+    USHORT      RootDirEntries;             // Number of root directory entries (fat12/16)
+    USHORT      TotalSectors;               // Number of total sectors on the drive, 16-bit
+    UCHAR       MediaDescriptor;            // Media descriptor byte
+    USHORT      SectorsPerFat;              // Sectors per FAT table (fat12/16)
+    USHORT      SectorsPerTrack;            // Number of sectors in a track
+    USHORT      NumberOfHeads;              // Number of heads on the disk
+    ULONG       HiddenSectors;              // Hidden sectors (sectors before the partition start like the partition table)
+    ULONG       TotalSectorsBig;            // This field is the new 32-bit total count of sectors on the volume
+    ULONG       SectorsPerFatBig;           // This field is the FAT32 32-bit count of sectors occupied by ONE FAT. BPB_FATSz16 must be 0
+    USHORT      ExtendedFlags;              // Extended flags (fat32)
+    USHORT      FileSystemVersion;          // File system version (fat32)
+    ULONG       RootDirStartCluster;        // Starting cluster of the root directory (fat32)
+    USHORT      FsInfo;                     // Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1.
+    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.
+    UCHAR       Reserved[12];               // Reserved for future expansion
+    UCHAR       DriveNumber;                // Int 0x13 drive number (e.g. 0x80)
+    UCHAR       Reserved1;                  // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0.
+    UCHAR       BootSignature;              // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present.
+    ULONG       VolumeSerialNumber;         // Volume serial number
+    CHAR        VolumeLabel[11];            // Volume label. This field matches the 11-byte volume label recorded in the root directory
+    CHAR        FileSystemType[8];          // Always set to the string "FAT32   "
+
+    UCHAR       BootCodeAndData[420];       // The remainder of the boot sector
+
+    USHORT      BootSectorMagic;            // 0xAA55
+
+} FAT32_BOOTSECTOR, *PFAT32_BOOTSECTOR;
+C_ASSERT(sizeof(FAT32_BOOTSECTOR) == FAT32_BOOTSECTOR_SIZE);
+
+typedef struct _BTRFS_BOOTSECTOR
+{
+    UCHAR JumpBoot[3];
+    UCHAR ChunkMapSize;
+    UCHAR BootDrive;
+    ULONGLONG PartitionStartLBA;
+    UCHAR Fill[1521]; // 1536 - 15
+    USHORT BootSectorMagic;
+} BTRFS_BOOTSECTOR, *PBTRFS_BOOTSECTOR;
+C_ASSERT(sizeof(BTRFS_BOOTSECTOR) == BTRFS_BOOTSECTOR_SIZE);
+
+// TODO: Add more bootsector structures!
+
+#include <poppack.h>
+
+
 /* LOCALS *******************************************************************/
 
 /** IFS_PROVIDER **/
@@ -238,6 +325,297 @@ FormatFileSystem(
 }
 
 
+//
+// Bootsector routines
+//
+
+NTSTATUS
+InstallFat1216BootCode(
+    IN PCWSTR SrcPath,          // FAT12/16 bootsector source file (on the installation medium)
+    IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
+    IN HANDLE RootPartition)    // Partition holding the (old) FAT12/16 information
+{
+    NTSTATUS Status;
+    UNICODE_STRING Name;
+    IO_STATUS_BLOCK IoStatusBlock;
+    LARGE_INTEGER FileOffset;
+    BOOTCODE OrigBootSector = {0};
+    BOOTCODE NewBootSector  = {0};
+
+    /* Allocate and read the current original partition bootsector */
+    Status = ReadBootCodeByHandle(&OrigBootSector,
+                                  RootPartition,
+                                  FAT_BOOTSECTOR_SIZE);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Allocate and read the new bootsector from SrcPath */
+    RtlInitUnicodeString(&Name, SrcPath);
+    Status = ReadBootCodeFromFile(&NewBootSector,
+                                  &Name,
+                                  FAT_BOOTSECTOR_SIZE);
+    if (!NT_SUCCESS(Status))
+    {
+        FreeBootCode(&OrigBootSector);
+        return Status;
+    }
+
+    /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */
+    RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName,
+                  &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
+                  FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) -
+                  FIELD_OFFSET(FAT_BOOTSECTOR, OemName));
+
+    /* Free the original bootsector */
+    FreeBootCode(&OrigBootSector);
+
+    /* Write the new bootsector to DstPath */
+    FileOffset.QuadPart = 0ULL;
+    Status = NtWriteFile(DstPath,
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         NewBootSector.BootCode,
+                         NewBootSector.Length,
+                         &FileOffset,
+                         NULL);
+
+    /* Free the new bootsector */
+    FreeBootCode(&NewBootSector);
+
+    return Status;
+}
+
+NTSTATUS
+InstallFat32BootCode(
+    IN PCWSTR SrcPath,          // FAT32 bootsector source file (on the installation medium)
+    IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
+    IN HANDLE RootPartition)    // Partition holding the (old) FAT32 information
+{
+    NTSTATUS Status;
+    UNICODE_STRING Name;
+    IO_STATUS_BLOCK IoStatusBlock;
+    LARGE_INTEGER FileOffset;
+    USHORT BackupBootSector = 0;
+    BOOTCODE OrigBootSector = {0};
+    BOOTCODE NewBootSector  = {0};
+
+    /* Allocate and read the current original partition bootsector */
+    Status = ReadBootCodeByHandle(&OrigBootSector,
+                                  RootPartition,
+                                  FAT32_BOOTSECTOR_SIZE);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /* Allocate and read the new bootsector (2 sectors) from SrcPath */
+    RtlInitUnicodeString(&Name, SrcPath);
+    Status = ReadBootCodeFromFile(&NewBootSector,
+                                  &Name,
+                                  2 * FAT32_BOOTSECTOR_SIZE);
+    if (!NT_SUCCESS(Status))
+    {
+        FreeBootCode(&OrigBootSector);
+        return Status;
+    }
+
+    /* Adjust the bootsector (copy a part of the FAT32 BPB) */
+    RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName,
+                  &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName,
+                  FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) -
+                  FIELD_OFFSET(FAT32_BOOTSECTOR, OemName));
+
+    /*
+     * We know we copy the boot code to a file only when DstPath != RootPartition,
+     * otherwise the boot code is copied to the specified root partition.
+     */
+    if (DstPath != RootPartition)
+    {
+        /* Copy to a file: Disable the backup bootsector */
+        ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0;
+    }
+    else
+    {
+        /* Copy to a disk: Get the location of the backup bootsector */
+        BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector;
+    }
+
+    /* Free the original bootsector */
+    FreeBootCode(&OrigBootSector);
+
+    /* Write the first sector of the new bootcode to DstPath sector 0 */
+    FileOffset.QuadPart = 0ULL;
+    Status = NtWriteFile(DstPath,
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         NewBootSector.BootCode,
+                         FAT32_BOOTSECTOR_SIZE,
+                         &FileOffset,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
+        FreeBootCode(&NewBootSector);
+        return Status;
+    }
+
+    if (DstPath == RootPartition)
+    {
+        /* Copy to a disk: Write the backup bootsector */
+        if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF))
+        {
+            FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE);
+            Status = NtWriteFile(DstPath,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &IoStatusBlock,
+                                 NewBootSector.BootCode,
+                                 FAT32_BOOTSECTOR_SIZE,
+                                 &FileOffset,
+                                 NULL);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
+                FreeBootCode(&NewBootSector);
+                return Status;
+            }
+        }
+    }
+
+    /* Write the second sector of the new bootcode to boot disk sector 14 */
+    // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE);
+    FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE;
+    Status = NtWriteFile(DstPath,   // or really RootPartition ???
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE),
+                         FAT32_BOOTSECTOR_SIZE,
+                         &FileOffset,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
+    }
+
+    /* Free the new bootsector */
+    FreeBootCode(&NewBootSector);
+
+    return Status;
+}
+
+NTSTATUS
+InstallBtrfsBootCode(
+    IN PCWSTR SrcPath,          // BTRFS bootsector source file (on the installation medium)
+    IN HANDLE DstPath,          // Where to save the bootsector built from the source + partition information
+    IN HANDLE RootPartition)    // Partition holding the (old) BTRFS information
+{
+    NTSTATUS Status;
+    NTSTATUS LockStatus;
+    UNICODE_STRING Name;
+    IO_STATUS_BLOCK IoStatusBlock;
+    LARGE_INTEGER FileOffset;
+    PARTITION_INFORMATION_EX PartInfo;
+    BOOTCODE NewBootSector = {0};
+
+    /* Allocate and read the new bootsector from SrcPath */
+    RtlInitUnicodeString(&Name, SrcPath);
+    Status = ReadBootCodeFromFile(&NewBootSector,
+                                  &Name,
+                                  BTRFS_BOOTSECTOR_SIZE);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    /*
+     * The BTRFS driver requires the volume to be locked in order to modify
+     * the first sectors of the partition, even though they are outside the
+     * file-system space / in the reserved area (they are situated before
+     * the super-block at 0x1000) and is in principle allowed by the NT
+     * storage stack.
+     * So we lock here in order to write the bootsector at sector 0.
+     * If locking fails, we ignore and continue nonetheless.
+     */
+    LockStatus = NtFsControlFile(DstPath,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &IoStatusBlock,
+                                 FSCTL_LOCK_VOLUME,
+                                 NULL,
+                                 0,
+                                 NULL,
+                                 0);
+    if (!NT_SUCCESS(LockStatus))
+    {
+        DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus);
+    }
+
+    /* Obtain partition info and write it to the bootsector */
+    Status = NtDeviceIoControlFile(RootPartition,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   &IoStatusBlock,
+                                   IOCTL_DISK_GET_PARTITION_INFO_EX,
+                                   NULL,
+                                   0,
+                                   &PartInfo,
+                                   sizeof(PartInfo));
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status);
+        goto Quit;
+    }
+
+    /* Write new bootsector to RootPath */
+    ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA =
+        PartInfo.StartingOffset.QuadPart / SECTORSIZE;
+
+    /* Write sector 0 */
+    FileOffset.QuadPart = 0ULL;
+    Status = NtWriteFile(DstPath,
+                         NULL,
+                         NULL,
+                         NULL,
+                         &IoStatusBlock,
+                         NewBootSector.BootCode,
+                         NewBootSector.Length,
+                         &FileOffset,
+                         NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NtWriteFile() failed (Status %lx)\n", Status);
+        goto Quit;
+    }
+
+Quit:
+    /* Unlock the volume */
+    LockStatus = NtFsControlFile(DstPath,
+                                 NULL,
+                                 NULL,
+                                 NULL,
+                                 &IoStatusBlock,
+                                 FSCTL_UNLOCK_VOLUME,
+                                 NULL,
+                                 0,
+                                 NULL,
+                                 0);
+    if (!NT_SUCCESS(LockStatus))
+    {
+        DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus);
+    }
+
+    /* Free the new bootsector */
+    FreeBootCode(&NewBootSector);
+
+    return Status;
+}
+
+
 //
 // Formatting routines
 //