1 /* 2 * PROJECT: ReactOS Setup Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Filesystem support functions 5 * COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net) 6 * Copyright 2017-2019 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 "fsutil.h" 20 #include "partlist.h" 21 22 #include <fslib/vfatlib.h> 23 #include <fslib/btrfslib.h> 24 // #include <fslib/ext2lib.h> 25 // #include <fslib/ntfslib.h> 26 27 #define NDEBUG 28 #include <debug.h> 29 30 31 /* LOCALS *******************************************************************/ 32 33 /** IFS_PROVIDER **/ 34 typedef struct _FILE_SYSTEM 35 { 36 PCWSTR FileSystemName; 37 FORMATEX FormatFunc; 38 CHKDSKEX ChkdskFunc; 39 } FILE_SYSTEM, *PFILE_SYSTEM; 40 41 /* The list of file systems on which we can install ReactOS */ 42 static FILE_SYSTEM RegisteredFileSystems[] = 43 { 44 /* NOTE: The FAT formatter automatically determines 45 * whether it will use FAT-16 or FAT-32. */ 46 { L"FAT" , VfatFormat, VfatChkdsk }, 47 #if 0 48 { L"FAT32", VfatFormat, VfatChkdsk }, // Do we support specific FAT sub-formats specifications? 49 { L"FATX" , VfatxFormat, VfatxChkdsk }, 50 { L"NTFS" , NtfsFormat, NtfsChkdsk }, 51 #endif 52 { L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx }, 53 #if 0 54 { L"EXT2" , Ext2Format, Ext2Chkdsk }, 55 { L"EXT3" , Ext2Format, Ext2Chkdsk }, 56 { L"EXT4" , Ext2Format, Ext2Chkdsk }, 57 { L"FFS" , FfsFormat , FfsChkdsk }, 58 { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk }, 59 #endif 60 }; 61 62 63 /* FUNCTIONS ****************************************************************/ 64 65 /** QueryAvailableFileSystemFormat() **/ 66 BOOLEAN 67 GetRegisteredFileSystems( 68 IN ULONG Index, 69 OUT PCWSTR* FileSystemName) 70 { 71 if (Index >= ARRAYSIZE(RegisteredFileSystems)) 72 return FALSE; 73 74 *FileSystemName = RegisteredFileSystems[Index].FileSystemName; 75 76 return TRUE; 77 } 78 79 80 /** GetProvider() **/ 81 static PFILE_SYSTEM 82 GetFileSystemByName( 83 IN PCWSTR FileSystemName) 84 { 85 #if 0 // Reenable when the list of registered FSes will again be dynamic 86 87 PLIST_ENTRY ListEntry; 88 PFILE_SYSTEM_ITEM Item; 89 90 ListEntry = List->ListHead.Flink; 91 while (ListEntry != &List->ListHead) 92 { 93 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry); 94 if (Item->FileSystemName && 95 (wcsicmp(FileSystemName, Item->FileSystemName) == 0 || 96 /* Map FAT32 back to FAT */ 97 (wcsicmp(FileSystemName, L"FAT32") == 0 && wcsicmp(Item->FileSystemName, L"FAT") == 0))) 98 { 99 return Item; 100 } 101 102 ListEntry = ListEntry->Flink; 103 } 104 105 #else 106 107 ULONG Count = ARRAYSIZE(RegisteredFileSystems); 108 PFILE_SYSTEM FileSystems = RegisteredFileSystems; 109 110 ASSERT(FileSystems && Count != 0); 111 112 while (Count--) 113 { 114 if (FileSystems->FileSystemName && 115 (wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0 || 116 /* Map FAT32 back to FAT */ 117 (wcsicmp(FileSystemName, L"FAT32") == 0 && wcsicmp(FileSystems->FileSystemName, L"FAT") == 0))) 118 { 119 return FileSystems; 120 } 121 122 ++FileSystems; 123 } 124 125 #endif 126 127 return NULL; 128 } 129 130 131 // 132 // FileSystem recognition, using NT OS functionality 133 // 134 135 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */ 136 NTSTATUS 137 GetFileSystemNameByHandle( 138 IN HANDLE PartitionHandle, 139 IN OUT PWSTR FileSystemName, 140 IN SIZE_T FileSystemNameSize) 141 { 142 NTSTATUS Status; 143 IO_STATUS_BLOCK IoStatusBlock; 144 UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; 145 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; 146 147 /* Retrieve the FS attributes */ 148 Status = NtQueryVolumeInformationFile(PartitionHandle, 149 &IoStatusBlock, 150 FileFsAttribute, 151 sizeof(Buffer), 152 FileFsAttributeInformation); 153 if (!NT_SUCCESS(Status)) 154 { 155 DPRINT1("NtQueryVolumeInformationFile failed, Status 0x%08lx\n", Status); 156 return Status; 157 } 158 159 if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) 160 return STATUS_BUFFER_TOO_SMALL; 161 162 return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize, 163 FileFsAttribute->FileSystemName, 164 FileFsAttribute->FileSystemNameLength); 165 } 166 167 NTSTATUS 168 GetFileSystemName_UStr( 169 IN PUNICODE_STRING PartitionPath, 170 IN OUT PWSTR FileSystemName, 171 IN SIZE_T FileSystemNameSize) 172 { 173 NTSTATUS Status; 174 OBJECT_ATTRIBUTES ObjectAttributes; 175 HANDLE PartitionHandle; 176 IO_STATUS_BLOCK IoStatusBlock; 177 178 /* Open the partition */ 179 InitializeObjectAttributes(&ObjectAttributes, 180 PartitionPath, 181 OBJ_CASE_INSENSITIVE, 182 NULL, 183 NULL); 184 Status = NtOpenFile(&PartitionHandle, 185 FILE_GENERIC_READ /* | SYNCHRONIZE */, 186 &ObjectAttributes, 187 &IoStatusBlock, 188 FILE_SHARE_READ | FILE_SHARE_WRITE, 189 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 190 if (!NT_SUCCESS(Status)) 191 { 192 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", PartitionPath, Status); 193 return Status; 194 } 195 196 /* Retrieve the FS attributes */ 197 Status = GetFileSystemNameByHandle(PartitionHandle, FileSystemName, FileSystemNameSize); 198 if (!NT_SUCCESS(Status)) 199 { 200 DPRINT1("GetFileSystemNameByHandle() failed for partition '%wZ', Status 0x%08lx\n", 201 PartitionPath, Status); 202 } 203 204 /* Close the partition */ 205 NtClose(PartitionHandle); 206 207 return Status; 208 } 209 210 NTSTATUS 211 GetFileSystemName( 212 IN PCWSTR Partition, 213 IN OUT PWSTR FileSystemName, 214 IN SIZE_T FileSystemNameSize) 215 { 216 UNICODE_STRING PartitionPath; 217 218 RtlInitUnicodeString(&PartitionPath, Partition); 219 return GetFileSystemName_UStr(&PartitionPath, 220 FileSystemName, 221 FileSystemNameSize); 222 } 223 224 NTSTATUS 225 InferFileSystemByHandle( 226 IN HANDLE PartitionHandle, 227 IN UCHAR PartitionType, 228 IN OUT PWSTR FileSystemName, 229 IN SIZE_T FileSystemNameSize) 230 { 231 NTSTATUS Status; 232 233 if (FileSystemNameSize < sizeof(WCHAR)) 234 return STATUS_BUFFER_TOO_SMALL; 235 236 *FileSystemName = L'\0'; 237 238 /* Try to infer a file system using NT file system recognition */ 239 Status = GetFileSystemNameByHandle(PartitionHandle, 240 FileSystemName, 241 FileSystemNameSize); 242 if (NT_SUCCESS(Status) && *FileSystemName) 243 { 244 goto Quit; 245 } 246 247 /* 248 * Try to infer a preferred file system for this partition, given its ID. 249 * 250 * WARNING: This is partly a hack, since partitions with the same ID can 251 * be formatted with different file systems: for example, usual Linux 252 * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the 253 * same partition ID 0x83. 254 * 255 * The proper fix is to make a function that detects the existing FS 256 * from a given partition (not based on the partition ID). 257 * On the contrary, for unformatted partitions with a given ID, the 258 * following code is OK. 259 */ 260 if ((PartitionType == PARTITION_FAT_12) || 261 (PartitionType == PARTITION_FAT_16) || 262 (PartitionType == PARTITION_HUGE ) || 263 (PartitionType == PARTITION_XINT13)) 264 { 265 /* FAT12 or FAT16 */ 266 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT"); 267 } 268 else if ((PartitionType == PARTITION_FAT32) || 269 (PartitionType == PARTITION_FAT32_XINT13)) 270 { 271 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT32"); 272 } 273 else if (PartitionType == PARTITION_LINUX) 274 { 275 // WARNING: See the warning above. 276 /* Could also be EXT2/3/4, ReiserFS, ... */ 277 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"BTRFS"); 278 } 279 else if (PartitionType == PARTITION_IFS) 280 { 281 // WARNING: See the warning above. 282 /* Could also be HPFS */ 283 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"NTFS"); 284 } 285 286 Quit: 287 if (*FileSystemName) 288 { 289 // WARNING: We cannot write on this FS yet! 290 if (PartitionType == PARTITION_IFS) 291 { 292 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n", 293 FileSystemName); 294 } 295 } 296 297 DPRINT1("InferFileSystem -- PartitionType: 0x%02X ; FileSystem (guessed): %S\n", 298 PartitionType, *FileSystemName ? FileSystemName : L"None"); 299 300 return Status; 301 } 302 303 NTSTATUS 304 InferFileSystem( 305 IN PCWSTR Partition, 306 IN UCHAR PartitionType, 307 IN OUT PWSTR FileSystemName, 308 IN SIZE_T FileSystemNameSize) 309 { 310 NTSTATUS Status; 311 UNICODE_STRING PartitionPath; 312 OBJECT_ATTRIBUTES ObjectAttributes; 313 HANDLE PartitionHandle; 314 IO_STATUS_BLOCK IoStatusBlock; 315 316 /* Open the partition */ 317 RtlInitUnicodeString(&PartitionPath, Partition); 318 InitializeObjectAttributes(&ObjectAttributes, 319 &PartitionPath, 320 OBJ_CASE_INSENSITIVE, 321 NULL, 322 NULL); 323 Status = NtOpenFile(&PartitionHandle, 324 FILE_GENERIC_READ /* | SYNCHRONIZE */, 325 &ObjectAttributes, 326 &IoStatusBlock, 327 FILE_SHARE_READ | FILE_SHARE_WRITE, 328 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 329 if (!NT_SUCCESS(Status)) 330 { 331 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionPath, Status); 332 return Status; 333 } 334 335 /* Retrieve the FS */ 336 Status = InferFileSystemByHandle(PartitionHandle, 337 PartitionType, 338 FileSystemName, 339 FileSystemNameSize); 340 341 /* Close the partition */ 342 NtClose(PartitionHandle); 343 344 return Status; 345 } 346 347 /** ChkdskEx() **/ 348 NTSTATUS 349 ChkdskFileSystem_UStr( 350 IN PUNICODE_STRING DriveRoot, 351 IN PCWSTR FileSystemName, 352 IN BOOLEAN FixErrors, 353 IN BOOLEAN Verbose, 354 IN BOOLEAN CheckOnlyIfDirty, 355 IN BOOLEAN ScanDrive, 356 IN PFMIFSCALLBACK Callback) 357 { 358 PFILE_SYSTEM FileSystem; 359 360 FileSystem = GetFileSystemByName(FileSystemName); 361 362 if (!FileSystem || !FileSystem->ChkdskFunc) 363 { 364 // BOOLEAN Argument = FALSE; 365 // Callback(DONE, 0, &Argument); 366 return STATUS_NOT_SUPPORTED; 367 } 368 369 return FileSystem->ChkdskFunc(DriveRoot, 370 FixErrors, 371 Verbose, 372 CheckOnlyIfDirty, 373 ScanDrive, 374 Callback); 375 } 376 377 NTSTATUS 378 ChkdskFileSystem( 379 IN PCWSTR DriveRoot, 380 IN PCWSTR FileSystemName, 381 IN BOOLEAN FixErrors, 382 IN BOOLEAN Verbose, 383 IN BOOLEAN CheckOnlyIfDirty, 384 IN BOOLEAN ScanDrive, 385 IN PFMIFSCALLBACK Callback) 386 { 387 UNICODE_STRING DriveRootU; 388 389 RtlInitUnicodeString(&DriveRootU, DriveRoot); 390 return ChkdskFileSystem_UStr(&DriveRootU, 391 FileSystemName, 392 FixErrors, 393 Verbose, 394 CheckOnlyIfDirty, 395 ScanDrive, 396 Callback); 397 } 398 399 400 /** FormatEx() **/ 401 NTSTATUS 402 FormatFileSystem_UStr( 403 IN PUNICODE_STRING DriveRoot, 404 IN PCWSTR FileSystemName, 405 IN FMIFS_MEDIA_FLAG MediaFlag, 406 IN PUNICODE_STRING Label, 407 IN BOOLEAN QuickFormat, 408 IN ULONG ClusterSize, 409 IN PFMIFSCALLBACK Callback) 410 { 411 PFILE_SYSTEM FileSystem; 412 413 FileSystem = GetFileSystemByName(FileSystemName); 414 415 if (!FileSystem || !FileSystem->FormatFunc) 416 { 417 // BOOLEAN Argument = FALSE; 418 // Callback(DONE, 0, &Argument); 419 return STATUS_NOT_SUPPORTED; 420 } 421 422 return FileSystem->FormatFunc(DriveRoot, 423 MediaFlag, 424 Label, 425 QuickFormat, 426 ClusterSize, 427 Callback); 428 } 429 430 NTSTATUS 431 FormatFileSystem( 432 IN PCWSTR DriveRoot, 433 IN PCWSTR FileSystemName, 434 IN FMIFS_MEDIA_FLAG MediaFlag, 435 IN PCWSTR Label, 436 IN BOOLEAN QuickFormat, 437 IN ULONG ClusterSize, 438 IN PFMIFSCALLBACK Callback) 439 { 440 UNICODE_STRING DriveRootU; 441 UNICODE_STRING LabelU; 442 443 RtlInitUnicodeString(&DriveRootU, DriveRoot); 444 RtlInitUnicodeString(&LabelU, Label); 445 446 return FormatFileSystem_UStr(&DriveRootU, 447 FileSystemName, 448 MediaFlag, 449 &LabelU, 450 QuickFormat, 451 ClusterSize, 452 Callback); 453 } 454 455 456 UCHAR 457 FileSystemToPartitionType( 458 IN PCWSTR FileSystem, 459 IN PULARGE_INTEGER StartSector, 460 IN PULARGE_INTEGER SectorCount) 461 { 462 ASSERT(FileSystem && StartSector && SectorCount); 463 464 if (wcsicmp(FileSystem, L"FAT") == 0 || 465 wcsicmp(FileSystem, L"FAT32") == 0 || 466 wcsicmp(FileSystem, L"RAW") == 0) 467 { 468 if (SectorCount->QuadPart < 8192) 469 { 470 /* FAT12 CHS partition (disk is smaller than 4.1MB) */ 471 return PARTITION_FAT_12; 472 } 473 else if (StartSector->QuadPart < 1450560) 474 { 475 /* Partition starts below the 8.4GB boundary ==> CHS partition */ 476 477 if (SectorCount->QuadPart < 65536) 478 { 479 /* FAT16 CHS partition (partition size < 32MB) */ 480 return PARTITION_FAT_16; 481 } 482 else if (SectorCount->QuadPart < 1048576) 483 { 484 /* FAT16 CHS partition (partition size < 512MB) */ 485 return PARTITION_HUGE; 486 } 487 else 488 { 489 /* FAT32 CHS partition (partition size >= 512MB) */ 490 return PARTITION_FAT32; 491 } 492 } 493 else 494 { 495 /* Partition starts above the 8.4GB boundary ==> LBA partition */ 496 497 if (SectorCount->QuadPart < 1048576) 498 { 499 /* FAT16 LBA partition (partition size < 512MB) */ 500 return PARTITION_XINT13; 501 } 502 else 503 { 504 /* FAT32 LBA partition (partition size >= 512MB) */ 505 return PARTITION_FAT32_XINT13; 506 } 507 } 508 } 509 else if (wcsicmp(FileSystem, L"NTFS") == 0) 510 { 511 return PARTITION_IFS; 512 } 513 else if (wcsicmp(FileSystem, L"BTRFS") == 0 || 514 wcsicmp(FileSystem, L"EXT2") == 0 || 515 wcsicmp(FileSystem, L"EXT3") == 0 || 516 wcsicmp(FileSystem, L"EXT4") == 0 || 517 wcsicmp(FileSystem, L"FFS") == 0 || 518 wcsicmp(FileSystem, L"REISERFS") == 0) 519 { 520 return PARTITION_LINUX; 521 } 522 else 523 { 524 /* Unknown file system */ 525 DPRINT1("Unknown file system '%S'\n", FileSystem); 526 return PARTITION_ENTRY_UNUSED; 527 } 528 } 529 530 531 // 532 // Formatting routines 533 // 534 535 BOOLEAN 536 PreparePartitionForFormatting( 537 IN struct _PARTENTRY* PartEntry, 538 IN PCWSTR FileSystemName) 539 { 540 UCHAR PartitionType; 541 542 if (!FileSystemName || !*FileSystemName) 543 { 544 DPRINT1("No file system specified?\n"); 545 return FALSE; 546 } 547 548 PartitionType = FileSystemToPartitionType(FileSystemName, 549 &PartEntry->StartSector, 550 &PartEntry->SectorCount); 551 if (PartitionType == PARTITION_ENTRY_UNUSED) 552 { 553 /* Unknown file system */ 554 DPRINT1("Unknown file system '%S'\n", FileSystemName); 555 return FALSE; 556 } 557 558 SetPartitionType(PartEntry, PartitionType); 559 560 // 561 // FIXME: Do this now, or after the partition was actually formatted?? 562 // 563 /* Set the new partition's file system proper */ 564 RtlStringCbCopyW(PartEntry->FileSystem, 565 sizeof(PartEntry->FileSystem), 566 FileSystemName); 567 568 return TRUE; 569 } 570 571 /* EOF */ 572