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