* 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
*/
//
#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 **/
}
+//
+// 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
//