1 /* 2 * PROJECT: ReactOS Setup Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Filesystem Format and ChkDsk support functions. 5 * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net) 6 * Copyright 2017-2020 Hermes Belusca-Maito 7 */ 8 9 // 10 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c 11 // for how to get FS providers in a dynamic way. In the (near) future we may 12 // consider merging some of this code with us into a fmifs / fsutil / fslib library... 13 // 14 15 /* INCLUDES *****************************************************************/ 16 17 #include "precomp.h" 18 19 #include "partlist.h" 20 #include "fsrec.h" 21 #include "bootcode.h" 22 #include "fsutil.h" 23 24 #include <fslib/vfatlib.h> 25 #include <fslib/btrfslib.h> 26 // #include <fslib/ext2lib.h> 27 // #include <fslib/ntfslib.h> 28 29 #define NDEBUG 30 #include <debug.h> 31 32 33 /* TYPEDEFS *****************************************************************/ 34 35 #include <pshpack1.h> 36 typedef struct _FAT_BOOTSECTOR 37 { 38 UCHAR JumpBoot[3]; // Jump instruction to boot code 39 CHAR OemName[8]; // "MSWIN4.1" for MS formatted volumes 40 USHORT BytesPerSector; // Bytes per sector 41 UCHAR SectorsPerCluster; // Number of sectors in a cluster 42 USHORT ReservedSectors; // Reserved sectors, usually 1 (the bootsector) 43 UCHAR NumberOfFats; // Number of FAT tables 44 USHORT RootDirEntries; // Number of root directory entries (fat12/16) 45 USHORT TotalSectors; // Number of total sectors on the drive, 16-bit 46 UCHAR MediaDescriptor; // Media descriptor byte 47 USHORT SectorsPerFat; // Sectors per FAT table (fat12/16) 48 USHORT SectorsPerTrack; // Number of sectors in a track 49 USHORT NumberOfHeads; // Number of heads on the disk 50 ULONG HiddenSectors; // Hidden sectors (sectors before the partition start like the partition table) 51 ULONG TotalSectorsBig; // This field is the new 32-bit total count of sectors on the volume 52 UCHAR DriveNumber; // Int 0x13 drive number (e.g. 0x80) 53 UCHAR Reserved1; // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0. 54 UCHAR BootSignature; // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present. 55 ULONG VolumeSerialNumber; // Volume serial number 56 CHAR VolumeLabel[11]; // Volume label. This field matches the 11-byte volume label recorded in the root directory 57 CHAR FileSystemType[8]; // One of the strings "FAT12 ", "FAT16 ", or "FAT " 58 59 UCHAR BootCodeAndData[448]; // The remainder of the boot sector 60 61 USHORT BootSectorMagic; // 0xAA55 62 63 } FAT_BOOTSECTOR, *PFAT_BOOTSECTOR; 64 C_ASSERT(sizeof(FAT_BOOTSECTOR) == FAT_BOOTSECTOR_SIZE); 65 66 typedef struct _FAT32_BOOTSECTOR 67 { 68 UCHAR JumpBoot[3]; // Jump instruction to boot code 69 CHAR OemName[8]; // "MSWIN4.1" for MS formatted volumes 70 USHORT BytesPerSector; // Bytes per sector 71 UCHAR SectorsPerCluster; // Number of sectors in a cluster 72 USHORT ReservedSectors; // Reserved sectors, usually 1 (the bootsector) 73 UCHAR NumberOfFats; // Number of FAT tables 74 USHORT RootDirEntries; // Number of root directory entries (fat12/16) 75 USHORT TotalSectors; // Number of total sectors on the drive, 16-bit 76 UCHAR MediaDescriptor; // Media descriptor byte 77 USHORT SectorsPerFat; // Sectors per FAT table (fat12/16) 78 USHORT SectorsPerTrack; // Number of sectors in a track 79 USHORT NumberOfHeads; // Number of heads on the disk 80 ULONG HiddenSectors; // Hidden sectors (sectors before the partition start like the partition table) 81 ULONG TotalSectorsBig; // This field is the new 32-bit total count of sectors on the volume 82 ULONG SectorsPerFatBig; // This field is the FAT32 32-bit count of sectors occupied by ONE FAT. BPB_FATSz16 must be 0 83 USHORT ExtendedFlags; // Extended flags (fat32) 84 USHORT FileSystemVersion; // File system version (fat32) 85 ULONG RootDirStartCluster; // Starting cluster of the root directory (fat32) 86 USHORT FsInfo; // Sector number of FSINFO structure in the reserved area of the FAT32 volume. Usually 1. 87 USHORT BackupBootSector; // If non-zero, indicates the sector number in the reserved area of the volume of a copy of the boot record. Usually 6. 88 UCHAR Reserved[12]; // Reserved for future expansion 89 UCHAR DriveNumber; // Int 0x13 drive number (e.g. 0x80) 90 UCHAR Reserved1; // Reserved (used by Windows NT). Code that formats FAT volumes should always set this byte to 0. 91 UCHAR BootSignature; // Extended boot signature (0x29). This is a signature byte that indicates that the following three fields in the boot sector are present. 92 ULONG VolumeSerialNumber; // Volume serial number 93 CHAR VolumeLabel[11]; // Volume label. This field matches the 11-byte volume label recorded in the root directory 94 CHAR FileSystemType[8]; // Always set to the string "FAT32 " 95 96 UCHAR BootCodeAndData[420]; // The remainder of the boot sector 97 98 USHORT BootSectorMagic; // 0xAA55 99 100 } FAT32_BOOTSECTOR, *PFAT32_BOOTSECTOR; 101 C_ASSERT(sizeof(FAT32_BOOTSECTOR) == FAT32_BOOTSECTOR_SIZE); 102 103 typedef struct _BTRFS_BOOTSECTOR 104 { 105 UCHAR JumpBoot[3]; 106 UCHAR ChunkMapSize; 107 UCHAR BootDrive; 108 ULONGLONG PartitionStartLBA; 109 UCHAR Fill[1521]; // 1536 - 15 110 USHORT BootSectorMagic; 111 } BTRFS_BOOTSECTOR, *PBTRFS_BOOTSECTOR; 112 C_ASSERT(sizeof(BTRFS_BOOTSECTOR) == BTRFS_BOOTSECTOR_SIZE); 113 114 // TODO: Add more bootsector structures! 115 116 #include <poppack.h> 117 118 119 /* LOCALS *******************************************************************/ 120 121 /** IFS_PROVIDER **/ 122 typedef struct _FILE_SYSTEM 123 { 124 PCWSTR FileSystemName; 125 PULIB_FORMAT FormatFunc; 126 PULIB_CHKDSK ChkdskFunc; 127 } FILE_SYSTEM, *PFILE_SYSTEM; 128 129 /* The list of file systems on which we can install ReactOS */ 130 static FILE_SYSTEM RegisteredFileSystems[] = 131 { 132 /* NOTE: The FAT formatter will automatically 133 * determine whether to use FAT12/16 or FAT32. */ 134 { L"FAT" , VfatFormat, VfatChkdsk }, 135 { L"FAT32", VfatFormat, VfatChkdsk }, 136 #if 0 137 { L"FATX" , VfatxFormat, VfatxChkdsk }, 138 { L"NTFS" , NtfsFormat, NtfsChkdsk }, 139 #endif 140 { L"BTRFS", BtrfsFormat, BtrfsChkdsk }, 141 #if 0 142 { L"EXT2" , Ext2Format, Ext2Chkdsk }, 143 { L"EXT3" , Ext2Format, Ext2Chkdsk }, 144 { L"EXT4" , Ext2Format, Ext2Chkdsk }, 145 #endif 146 }; 147 148 149 /* FUNCTIONS ****************************************************************/ 150 151 /** QueryAvailableFileSystemFormat() **/ 152 BOOLEAN 153 GetRegisteredFileSystems( 154 IN ULONG Index, 155 OUT PCWSTR* FileSystemName) 156 { 157 if (Index >= ARRAYSIZE(RegisteredFileSystems)) 158 return FALSE; 159 160 *FileSystemName = RegisteredFileSystems[Index].FileSystemName; 161 162 return TRUE; 163 } 164 165 166 /** GetProvider() **/ 167 static PFILE_SYSTEM 168 GetFileSystemByName( 169 IN PCWSTR FileSystemName) 170 { 171 #if 0 // Reenable when the list of registered FSes will again be dynamic 172 173 PLIST_ENTRY ListEntry; 174 PFILE_SYSTEM_ITEM Item; 175 176 ListEntry = List->ListHead.Flink; 177 while (ListEntry != &List->ListHead) 178 { 179 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry); 180 if (Item->FileSystemName && 181 (wcsicmp(FileSystemName, Item->FileSystemName) == 0)) 182 { 183 return Item; 184 } 185 186 ListEntry = ListEntry->Flink; 187 } 188 189 #else 190 191 ULONG Count = ARRAYSIZE(RegisteredFileSystems); 192 PFILE_SYSTEM FileSystems = RegisteredFileSystems; 193 194 ASSERT(FileSystems && Count != 0); 195 196 while (Count--) 197 { 198 if (FileSystems->FileSystemName && 199 (wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0)) 200 { 201 return FileSystems; 202 } 203 204 ++FileSystems; 205 } 206 207 #endif 208 209 return NULL; 210 } 211 212 213 /** ChkdskEx() **/ 214 NTSTATUS 215 ChkdskFileSystem_UStr( 216 IN PUNICODE_STRING DriveRoot, 217 IN PCWSTR FileSystemName, 218 IN BOOLEAN FixErrors, 219 IN BOOLEAN Verbose, 220 IN BOOLEAN CheckOnlyIfDirty, 221 IN BOOLEAN ScanDrive, 222 IN PFMIFSCALLBACK Callback) 223 { 224 PFILE_SYSTEM FileSystem; 225 NTSTATUS Status; 226 BOOLEAN Success; 227 228 FileSystem = GetFileSystemByName(FileSystemName); 229 230 if (!FileSystem || !FileSystem->ChkdskFunc) 231 { 232 // Success = FALSE; 233 // Callback(DONE, 0, &Success); 234 return STATUS_NOT_SUPPORTED; 235 } 236 237 Status = STATUS_SUCCESS; 238 Success = FileSystem->ChkdskFunc(DriveRoot, 239 Callback, 240 FixErrors, 241 Verbose, 242 CheckOnlyIfDirty, 243 ScanDrive, 244 NULL, 245 NULL, 246 NULL, 247 NULL, 248 (PULONG)&Status); 249 if (!Success) 250 DPRINT1("ChkdskFunc() failed with Status 0x%lx\n", Status); 251 252 // Callback(DONE, 0, &Success); 253 254 return Status; 255 } 256 257 NTSTATUS 258 ChkdskFileSystem( 259 IN PCWSTR DriveRoot, 260 IN PCWSTR FileSystemName, 261 IN BOOLEAN FixErrors, 262 IN BOOLEAN Verbose, 263 IN BOOLEAN CheckOnlyIfDirty, 264 IN BOOLEAN ScanDrive, 265 IN PFMIFSCALLBACK Callback) 266 { 267 UNICODE_STRING DriveRootU; 268 269 RtlInitUnicodeString(&DriveRootU, DriveRoot); 270 return ChkdskFileSystem_UStr(&DriveRootU, 271 FileSystemName, 272 FixErrors, 273 Verbose, 274 CheckOnlyIfDirty, 275 ScanDrive, 276 Callback); 277 } 278 279 280 /** FormatEx() **/ 281 NTSTATUS 282 FormatFileSystem_UStr( 283 IN PUNICODE_STRING DriveRoot, 284 IN PCWSTR FileSystemName, 285 IN FMIFS_MEDIA_FLAG MediaFlag, 286 IN PUNICODE_STRING Label, 287 IN BOOLEAN QuickFormat, 288 IN ULONG ClusterSize, 289 IN PFMIFSCALLBACK Callback) 290 { 291 PFILE_SYSTEM FileSystem; 292 BOOLEAN Success; 293 BOOLEAN BackwardCompatible = FALSE; // Default to latest FS versions. 294 MEDIA_TYPE MediaType; 295 296 FileSystem = GetFileSystemByName(FileSystemName); 297 298 if (!FileSystem || !FileSystem->FormatFunc) 299 { 300 // Success = FALSE; 301 // Callback(DONE, 0, &Success); 302 return STATUS_NOT_SUPPORTED; 303 } 304 305 /* Set the BackwardCompatible flag in case we format with older FAT12/16 */ 306 if (wcsicmp(FileSystemName, L"FAT") == 0) 307 BackwardCompatible = TRUE; 308 // else if (wcsicmp(FileSystemName, L"FAT32") == 0) 309 // BackwardCompatible = FALSE; 310 311 /* Convert the FMIFS MediaFlag to a NT MediaType */ 312 // FIXME: Actually covert all the possible flags. 313 switch (MediaFlag) 314 { 315 case FMIFS_FLOPPY: 316 MediaType = F5_320_1024; // FIXME: This is hardfixed! 317 break; 318 case FMIFS_REMOVABLE: 319 MediaType = RemovableMedia; 320 break; 321 case FMIFS_HARDDISK: 322 MediaType = FixedMedia; 323 break; 324 default: 325 DPRINT1("Unknown FMIFS MediaFlag %d, converting 1-to-1 to NT MediaType\n", 326 MediaFlag); 327 MediaType = (MEDIA_TYPE)MediaFlag; 328 break; 329 } 330 331 Success = FileSystem->FormatFunc(DriveRoot, 332 Callback, 333 QuickFormat, 334 BackwardCompatible, 335 MediaType, 336 Label, 337 ClusterSize); 338 if (!Success) 339 DPRINT1("FormatFunc() failed\n"); 340 341 // Callback(DONE, 0, &Success); 342 343 return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); 344 } 345 346 NTSTATUS 347 FormatFileSystem( 348 IN PCWSTR DriveRoot, 349 IN PCWSTR FileSystemName, 350 IN FMIFS_MEDIA_FLAG MediaFlag, 351 IN PCWSTR Label, 352 IN BOOLEAN QuickFormat, 353 IN ULONG ClusterSize, 354 IN PFMIFSCALLBACK Callback) 355 { 356 UNICODE_STRING DriveRootU; 357 UNICODE_STRING LabelU; 358 359 RtlInitUnicodeString(&DriveRootU, DriveRoot); 360 RtlInitUnicodeString(&LabelU, Label); 361 362 return FormatFileSystem_UStr(&DriveRootU, 363 FileSystemName, 364 MediaFlag, 365 &LabelU, 366 QuickFormat, 367 ClusterSize, 368 Callback); 369 } 370 371 372 // 373 // Bootsector routines 374 // 375 376 NTSTATUS 377 InstallFatBootCode( 378 IN PCWSTR SrcPath, // FAT12/16 bootsector source file (on the installation medium) 379 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information 380 IN HANDLE RootPartition) // Partition holding the (old) FAT12/16 information 381 { 382 NTSTATUS Status; 383 UNICODE_STRING Name; 384 IO_STATUS_BLOCK IoStatusBlock; 385 LARGE_INTEGER FileOffset; 386 BOOTCODE OrigBootSector = {0}; 387 BOOTCODE NewBootSector = {0}; 388 389 /* Allocate and read the current original partition bootsector */ 390 Status = ReadBootCodeByHandle(&OrigBootSector, 391 RootPartition, 392 FAT_BOOTSECTOR_SIZE); 393 if (!NT_SUCCESS(Status)) 394 return Status; 395 396 /* Allocate and read the new bootsector from SrcPath */ 397 RtlInitUnicodeString(&Name, SrcPath); 398 Status = ReadBootCodeFromFile(&NewBootSector, 399 &Name, 400 FAT_BOOTSECTOR_SIZE); 401 if (!NT_SUCCESS(Status)) 402 { 403 FreeBootCode(&OrigBootSector); 404 return Status; 405 } 406 407 /* Adjust the bootsector (copy a part of the FAT12/16 BPB) */ 408 RtlCopyMemory(&((PFAT_BOOTSECTOR)NewBootSector.BootCode)->OemName, 409 &((PFAT_BOOTSECTOR)OrigBootSector.BootCode)->OemName, 410 FIELD_OFFSET(FAT_BOOTSECTOR, BootCodeAndData) - 411 FIELD_OFFSET(FAT_BOOTSECTOR, OemName)); 412 413 /* Free the original bootsector */ 414 FreeBootCode(&OrigBootSector); 415 416 /* Write the new bootsector to DstPath */ 417 FileOffset.QuadPart = 0ULL; 418 Status = NtWriteFile(DstPath, 419 NULL, 420 NULL, 421 NULL, 422 &IoStatusBlock, 423 NewBootSector.BootCode, 424 NewBootSector.Length, 425 &FileOffset, 426 NULL); 427 428 /* Free the new bootsector */ 429 FreeBootCode(&NewBootSector); 430 431 return Status; 432 } 433 434 NTSTATUS 435 InstallFat32BootCode( 436 IN PCWSTR SrcPath, // FAT32 bootsector source file (on the installation medium) 437 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information 438 IN HANDLE RootPartition) // Partition holding the (old) FAT32 information 439 { 440 NTSTATUS Status; 441 UNICODE_STRING Name; 442 IO_STATUS_BLOCK IoStatusBlock; 443 LARGE_INTEGER FileOffset; 444 USHORT BackupBootSector = 0; 445 BOOTCODE OrigBootSector = {0}; 446 BOOTCODE NewBootSector = {0}; 447 448 /* Allocate and read the current original partition bootsector */ 449 Status = ReadBootCodeByHandle(&OrigBootSector, 450 RootPartition, 451 FAT32_BOOTSECTOR_SIZE); 452 if (!NT_SUCCESS(Status)) 453 return Status; 454 455 /* Allocate and read the new bootsector (2 sectors) from SrcPath */ 456 RtlInitUnicodeString(&Name, SrcPath); 457 Status = ReadBootCodeFromFile(&NewBootSector, 458 &Name, 459 2 * FAT32_BOOTSECTOR_SIZE); 460 if (!NT_SUCCESS(Status)) 461 { 462 FreeBootCode(&OrigBootSector); 463 return Status; 464 } 465 466 /* Adjust the bootsector (copy a part of the FAT32 BPB) */ 467 RtlCopyMemory(&((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->OemName, 468 &((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->OemName, 469 FIELD_OFFSET(FAT32_BOOTSECTOR, BootCodeAndData) - 470 FIELD_OFFSET(FAT32_BOOTSECTOR, OemName)); 471 472 /* 473 * We know we copy the boot code to a file only when DstPath != RootPartition, 474 * otherwise the boot code is copied to the specified root partition. 475 */ 476 if (DstPath != RootPartition) 477 { 478 /* Copy to a file: Disable the backup bootsector */ 479 ((PFAT32_BOOTSECTOR)NewBootSector.BootCode)->BackupBootSector = 0; 480 } 481 else 482 { 483 /* Copy to a disk: Get the location of the backup bootsector */ 484 BackupBootSector = ((PFAT32_BOOTSECTOR)OrigBootSector.BootCode)->BackupBootSector; 485 } 486 487 /* Free the original bootsector */ 488 FreeBootCode(&OrigBootSector); 489 490 /* Write the first sector of the new bootcode to DstPath sector 0 */ 491 FileOffset.QuadPart = 0ULL; 492 Status = NtWriteFile(DstPath, 493 NULL, 494 NULL, 495 NULL, 496 &IoStatusBlock, 497 NewBootSector.BootCode, 498 FAT32_BOOTSECTOR_SIZE, 499 &FileOffset, 500 NULL); 501 if (!NT_SUCCESS(Status)) 502 { 503 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status); 504 FreeBootCode(&NewBootSector); 505 return Status; 506 } 507 508 if (DstPath == RootPartition) 509 { 510 /* Copy to a disk: Write the backup bootsector */ 511 if ((BackupBootSector != 0x0000) && (BackupBootSector != 0xFFFF)) 512 { 513 FileOffset.QuadPart = (ULONGLONG)((ULONG)BackupBootSector * FAT32_BOOTSECTOR_SIZE); 514 Status = NtWriteFile(DstPath, 515 NULL, 516 NULL, 517 NULL, 518 &IoStatusBlock, 519 NewBootSector.BootCode, 520 FAT32_BOOTSECTOR_SIZE, 521 &FileOffset, 522 NULL); 523 if (!NT_SUCCESS(Status)) 524 { 525 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status); 526 FreeBootCode(&NewBootSector); 527 return Status; 528 } 529 } 530 } 531 532 /* Write the second sector of the new bootcode to boot disk sector 14 */ 533 // FileOffset.QuadPart = (ULONGLONG)(14 * FAT32_BOOTSECTOR_SIZE); 534 FileOffset.QuadPart = 14 * FAT32_BOOTSECTOR_SIZE; 535 Status = NtWriteFile(DstPath, // or really RootPartition ??? 536 NULL, 537 NULL, 538 NULL, 539 &IoStatusBlock, 540 ((PUCHAR)NewBootSector.BootCode + FAT32_BOOTSECTOR_SIZE), 541 FAT32_BOOTSECTOR_SIZE, 542 &FileOffset, 543 NULL); 544 if (!NT_SUCCESS(Status)) 545 { 546 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status); 547 } 548 549 /* Free the new bootsector */ 550 FreeBootCode(&NewBootSector); 551 552 return Status; 553 } 554 555 NTSTATUS 556 InstallBtrfsBootCode( 557 IN PCWSTR SrcPath, // BTRFS bootsector source file (on the installation medium) 558 IN HANDLE DstPath, // Where to save the bootsector built from the source + partition information 559 IN HANDLE RootPartition) // Partition holding the (old) BTRFS information 560 { 561 NTSTATUS Status; 562 NTSTATUS LockStatus; 563 UNICODE_STRING Name; 564 IO_STATUS_BLOCK IoStatusBlock; 565 LARGE_INTEGER FileOffset; 566 PARTITION_INFORMATION_EX PartInfo; 567 BOOTCODE NewBootSector = {0}; 568 569 /* Allocate and read the new bootsector from SrcPath */ 570 RtlInitUnicodeString(&Name, SrcPath); 571 Status = ReadBootCodeFromFile(&NewBootSector, 572 &Name, 573 BTRFS_BOOTSECTOR_SIZE); 574 if (!NT_SUCCESS(Status)) 575 return Status; 576 577 /* 578 * The BTRFS driver requires the volume to be locked in order to modify 579 * the first sectors of the partition, even though they are outside the 580 * file-system space / in the reserved area (they are situated before 581 * the super-block at 0x1000) and is in principle allowed by the NT 582 * storage stack. 583 * So we lock here in order to write the bootsector at sector 0. 584 * If locking fails, we ignore and continue nonetheless. 585 */ 586 LockStatus = NtFsControlFile(DstPath, 587 NULL, 588 NULL, 589 NULL, 590 &IoStatusBlock, 591 FSCTL_LOCK_VOLUME, 592 NULL, 593 0, 594 NULL, 595 0); 596 if (!NT_SUCCESS(LockStatus)) 597 { 598 DPRINT1("WARNING: Failed to lock BTRFS volume for writing bootsector! Operations may fail! (Status 0x%lx)\n", LockStatus); 599 } 600 601 /* Obtain partition info and write it to the bootsector */ 602 Status = NtDeviceIoControlFile(RootPartition, 603 NULL, 604 NULL, 605 NULL, 606 &IoStatusBlock, 607 IOCTL_DISK_GET_PARTITION_INFO_EX, 608 NULL, 609 0, 610 &PartInfo, 611 sizeof(PartInfo)); 612 if (!NT_SUCCESS(Status)) 613 { 614 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status); 615 goto Quit; 616 } 617 618 /* Write new bootsector to RootPath */ 619 ((PBTRFS_BOOTSECTOR)NewBootSector.BootCode)->PartitionStartLBA = 620 PartInfo.StartingOffset.QuadPart / SECTORSIZE; 621 622 /* Write sector 0 */ 623 FileOffset.QuadPart = 0ULL; 624 Status = NtWriteFile(DstPath, 625 NULL, 626 NULL, 627 NULL, 628 &IoStatusBlock, 629 NewBootSector.BootCode, 630 NewBootSector.Length, 631 &FileOffset, 632 NULL); 633 if (!NT_SUCCESS(Status)) 634 { 635 DPRINT1("NtWriteFile() failed (Status %lx)\n", Status); 636 goto Quit; 637 } 638 639 Quit: 640 /* Unlock the volume */ 641 LockStatus = NtFsControlFile(DstPath, 642 NULL, 643 NULL, 644 NULL, 645 &IoStatusBlock, 646 FSCTL_UNLOCK_VOLUME, 647 NULL, 648 0, 649 NULL, 650 0); 651 if (!NT_SUCCESS(LockStatus)) 652 { 653 DPRINT1("Failed to unlock BTRFS volume (Status 0x%lx)\n", LockStatus); 654 } 655 656 /* Free the new bootsector */ 657 FreeBootCode(&NewBootSector); 658 659 return Status; 660 } 661 662 663 // 664 // Formatting routines 665 // 666 667 NTSTATUS 668 ChkdskPartition( 669 IN PPARTENTRY PartEntry, 670 IN BOOLEAN FixErrors, 671 IN BOOLEAN Verbose, 672 IN BOOLEAN CheckOnlyIfDirty, 673 IN BOOLEAN ScanDrive, 674 IN PFMIFSCALLBACK Callback) 675 { 676 NTSTATUS Status; 677 PDISKENTRY DiskEntry = PartEntry->DiskEntry; 678 // UNICODE_STRING PartitionRootPath; 679 WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer 680 681 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); 682 683 /* HACK: Do not try to check a partition with an unknown filesystem */ 684 if (!*PartEntry->FileSystem) 685 { 686 PartEntry->NeedsCheck = FALSE; 687 return STATUS_SUCCESS; 688 } 689 690 /* Set PartitionRootPath */ 691 RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath), 692 L"\\Device\\Harddisk%lu\\Partition%lu", 693 DiskEntry->DiskNumber, 694 PartEntry->PartitionNumber); 695 DPRINT("PartitionRootPath: %S\n", PartitionRootPath); 696 697 /* Check the partition */ 698 Status = ChkdskFileSystem(PartitionRootPath, 699 PartEntry->FileSystem, 700 FixErrors, 701 Verbose, 702 CheckOnlyIfDirty, 703 ScanDrive, 704 Callback); 705 if (!NT_SUCCESS(Status)) 706 return Status; 707 708 PartEntry->NeedsCheck = FALSE; 709 return STATUS_SUCCESS; 710 } 711 712 NTSTATUS 713 FormatPartition( 714 IN PPARTENTRY PartEntry, 715 IN PCWSTR FileSystemName, 716 IN FMIFS_MEDIA_FLAG MediaFlag, 717 IN PCWSTR Label, 718 IN BOOLEAN QuickFormat, 719 IN ULONG ClusterSize, 720 IN PFMIFSCALLBACK Callback) 721 { 722 NTSTATUS Status; 723 PDISKENTRY DiskEntry = PartEntry->DiskEntry; 724 UCHAR PartitionType; 725 // UNICODE_STRING PartitionRootPath; 726 WCHAR PartitionRootPath[MAX_PATH]; // PathBuffer 727 728 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); 729 730 if (!FileSystemName || !*FileSystemName) 731 { 732 DPRINT1("No file system specified?\n"); 733 return STATUS_UNRECOGNIZED_VOLUME; 734 } 735 736 /* 737 * Prepare the partition for formatting (for MBR disks, reset the 738 * partition type), and adjust the filesystem name in case of FAT 739 * vs. FAT32, depending on the geometry of the partition. 740 */ 741 742 // FIXME: Do this only if QuickFormat == FALSE? What about FAT handling? 743 744 /* 745 * Retrieve a partition type as a hint only. It will be used to determine 746 * whether to actually use FAT12/16 or FAT32 filesystem, depending on the 747 * geometry of the partition. If the partition resides on an MBR disk, 748 * the partition style will be reset to this value as well, unless the 749 * partition is OEM. 750 */ 751 PartitionType = FileSystemToMBRPartitionType(FileSystemName, 752 PartEntry->StartSector.QuadPart, 753 PartEntry->SectorCount.QuadPart); 754 if (PartitionType == PARTITION_ENTRY_UNUSED) 755 { 756 /* Unknown file system */ 757 DPRINT1("Unknown file system '%S'\n", FileSystemName); 758 return STATUS_UNRECOGNIZED_VOLUME; 759 } 760 761 /* Reset the MBR partition type, unless this is an OEM partition */ 762 if (DiskEntry->DiskStyle == PARTITION_STYLE_MBR) 763 { 764 if (!IsOEMPartition(PartEntry->PartitionType)) 765 SetMBRPartitionType(PartEntry, PartitionType); 766 } 767 768 /* 769 * Adjust the filesystem name in case of FAT vs. FAT32, according to 770 * the type of partition returned by FileSystemToMBRPartitionType(). 771 */ 772 if (wcsicmp(FileSystemName, L"FAT") == 0) 773 { 774 if ((PartitionType == PARTITION_FAT32) || 775 (PartitionType == PARTITION_FAT32_XINT13)) 776 { 777 FileSystemName = L"FAT32"; 778 } 779 } 780 781 /* Commit the partition changes to the disk */ 782 Status = WritePartitions(DiskEntry); 783 if (!NT_SUCCESS(Status)) 784 { 785 DPRINT1("WritePartitions(disk %lu) failed, Status 0x%08lx\n", 786 DiskEntry->DiskNumber, Status); 787 return STATUS_PARTITION_FAILURE; 788 } 789 790 /* Set PartitionRootPath */ 791 RtlStringCchPrintfW(PartitionRootPath, ARRAYSIZE(PartitionRootPath), 792 L"\\Device\\Harddisk%lu\\Partition%lu", 793 DiskEntry->DiskNumber, 794 PartEntry->PartitionNumber); 795 DPRINT("PartitionRootPath: %S\n", PartitionRootPath); 796 797 /* Format the partition */ 798 Status = FormatFileSystem(PartitionRootPath, 799 FileSystemName, 800 MediaFlag, 801 Label, 802 QuickFormat, 803 ClusterSize, 804 Callback); 805 if (!NT_SUCCESS(Status)) 806 return Status; 807 808 // 809 // TODO: Here, call a partlist.c function that update the actual 810 // FS name and the label fields of the volume. 811 // 812 PartEntry->FormatState = Formatted; 813 814 /* Set the new partition's file system proper */ 815 RtlStringCbCopyW(PartEntry->FileSystem, 816 sizeof(PartEntry->FileSystem), 817 FileSystemName); 818 819 PartEntry->New = FALSE; 820 821 return STATUS_SUCCESS; 822 } 823 824 /* EOF */ 825