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