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-2018 Casper S. Hornstrup (chorns@users.sourceforge.net) 6 * Copyright 2017-2018 Hermes Belusca-Maito 7 */ 8 9 // 10 // This is basically the code for listing available FileSystem providers 11 // (currently hardcoded in a list), and for performing a basic FileSystem 12 // recognition for a given disk partition. 13 // 14 // See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c 15 // for how to get FS providers in a dynamic way. In the (near) future we may 16 // consider merging some of this code with us into a fmifs / fsutil / fslib library... 17 // 18 19 /* INCLUDES *****************************************************************/ 20 21 #include "precomp.h" 22 23 #include "fsutil.h" 24 #include "partlist.h" 25 26 #include <fslib/vfatlib.h> 27 #include <fslib/btrfslib.h> 28 // #include <fslib/ext2lib.h> 29 // #include <fslib/ntfslib.h> 30 31 #define NDEBUG 32 #include <debug.h> 33 34 35 FILE_SYSTEM RegisteredFileSystems[] = 36 { 37 /* NOTE: The FAT formatter automatically determines 38 * whether it will use FAT-16 or FAT-32. */ 39 { L"FAT" , VfatFormat, VfatChkdsk }, 40 #if 0 41 { L"FAT32", VfatFormat, VfatChkdsk }, // Do we support specific FAT sub-formats specifications? 42 { L"FATX" , VfatxFormat, VfatxChkdsk }, 43 { L"NTFS" , NtfsFormat, NtfsChkdsk }, 44 45 { L"EXT2" , Ext2Format, Ext2Chkdsk }, 46 { L"EXT3" , Ext2Format, Ext2Chkdsk }, 47 { L"EXT4" , Ext2Format, Ext2Chkdsk }, 48 #endif 49 { L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx }, 50 #if 0 51 { L"FFS" , FfsFormat , FfsChkdsk }, 52 { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk }, 53 #endif 54 }; 55 56 57 /* FUNCTIONS ****************************************************************/ 58 59 PFILE_SYSTEM 60 GetRegisteredFileSystems(OUT PULONG Count) 61 { 62 *Count = ARRAYSIZE(RegisteredFileSystems); 63 return RegisteredFileSystems; 64 } 65 66 PFILE_SYSTEM 67 GetFileSystemByName( 68 // IN PFILE_SYSTEM_LIST List, 69 IN PCWSTR FileSystemName) 70 { 71 #if 0 // Reenable when the list of registered FSes will again be dynamic 72 73 PLIST_ENTRY ListEntry; 74 PFILE_SYSTEM_ITEM Item; 75 76 ListEntry = List->ListHead.Flink; 77 while (ListEntry != &List->ListHead) 78 { 79 Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry); 80 if (Item->FileSystemName && wcsicmp(FileSystemName, Item->FileSystemName) == 0) 81 return Item; 82 83 ListEntry = ListEntry->Flink; 84 } 85 86 #else 87 88 ULONG Count; 89 PFILE_SYSTEM FileSystems; 90 91 FileSystems = GetRegisteredFileSystems(&Count); 92 if (!FileSystems || Count == 0) 93 return NULL; 94 95 while (Count--) 96 { 97 if (FileSystems->FileSystemName && wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0) 98 return FileSystems; 99 100 ++FileSystems; 101 } 102 103 #endif 104 105 return NULL; 106 } 107 108 109 // 110 // FileSystem recognition (using NT OS functionality) 111 // 112 113 #if 0 // FIXME: To be fully enabled when our storage stack & al. will work better! 114 115 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */ 116 static NTSTATUS 117 _MyGetFileSystem( 118 IN struct _PARTENTRY* PartEntry, 119 IN OUT PWSTR FileSystemName, 120 IN SIZE_T FileSystemNameSize) 121 { 122 NTSTATUS Status; 123 HANDLE FileHandle; 124 IO_STATUS_BLOCK IoStatusBlock; 125 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute; 126 UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; 127 128 OBJECT_ATTRIBUTES ObjectAttributes; 129 UNICODE_STRING PartitionRootPath; 130 WCHAR PathBuffer[MAX_PATH]; 131 132 FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; 133 134 /* Set PartitionRootPath */ 135 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 136 // L"\\Device\\Harddisk%lu\\Partition%lu", // Should work! But because ReactOS sucks atm. it actually doesn't work!! 137 L"\\Device\\Harddisk%lu\\Partition%lu\\", // HACK: Use this as a temporary hack! 138 PartEntry->DiskEntry->DiskNumber, 139 PartEntry->PartitionNumber); 140 RtlInitUnicodeString(&PartitionRootPath, PathBuffer); 141 DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath); 142 143 /* Open the partition */ 144 InitializeObjectAttributes(&ObjectAttributes, 145 &PartitionRootPath, 146 OBJ_CASE_INSENSITIVE, 147 NULL, 148 NULL); 149 Status = NtOpenFile(&FileHandle, // PartitionHandle, 150 FILE_GENERIC_READ /* | SYNCHRONIZE */, 151 &ObjectAttributes, 152 &IoStatusBlock, 153 FILE_SHARE_READ, 154 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 155 if (!NT_SUCCESS(Status)) 156 { 157 DPRINT1("Failed to open partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status); 158 return Status; 159 } 160 161 /* Retrieve the FS attributes */ 162 Status = NtQueryVolumeInformationFile(FileHandle, 163 &IoStatusBlock, 164 FileFsAttribute, 165 sizeof(Buffer), 166 FileFsAttributeInformation); 167 NtClose(FileHandle); 168 169 if (!NT_SUCCESS(Status)) 170 { 171 DPRINT1("NtQueryVolumeInformationFile failed for partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status); 172 return Status; 173 } 174 175 if (FileSystemNameSize * sizeof(WCHAR) < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) 176 return STATUS_BUFFER_TOO_SMALL; 177 178 RtlCopyMemory(FileSystemName, 179 FileFsAttribute->FileSystemName, 180 FileFsAttribute->FileSystemNameLength); 181 FileSystemName[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL; 182 183 return STATUS_SUCCESS; 184 } 185 186 #endif 187 188 PFILE_SYSTEM 189 GetFileSystem( 190 // IN PFILE_SYSTEM_LIST FileSystemList, 191 IN struct _PARTENTRY* PartEntry) 192 { 193 PFILE_SYSTEM CurrentFileSystem; 194 PWSTR FileSystemName = NULL; 195 #if 0 // For code temporarily disabled below 196 NTSTATUS Status; 197 WCHAR FsRecFileSystemName[MAX_PATH]; 198 #endif 199 200 CurrentFileSystem = PartEntry->FileSystem; 201 202 /* We have a file system, return it */ 203 if (CurrentFileSystem != NULL && CurrentFileSystem->FileSystemName != NULL) 204 return CurrentFileSystem; 205 206 DPRINT1("File system not found, try to guess one...\n"); 207 208 CurrentFileSystem = NULL; 209 210 #if 0 // This is an example of old code... 211 212 if ((PartEntry->PartitionType == PARTITION_FAT_12) || 213 (PartEntry->PartitionType == PARTITION_FAT_16) || 214 (PartEntry->PartitionType == PARTITION_HUGE) || 215 (PartEntry->PartitionType == PARTITION_XINT13) || 216 (PartEntry->PartitionType == PARTITION_FAT32) || 217 (PartEntry->PartitionType == PARTITION_FAT32_XINT13)) 218 { 219 if (CheckFatFormat()) 220 FileSystemName = L"FAT"; 221 else 222 FileSystemName = NULL; 223 } 224 else if (PartEntry->PartitionType == PARTITION_LINUX) 225 { 226 if (CheckExt2Format()) 227 FileSystemName = L"EXT2"; 228 else 229 FileSystemName = NULL; 230 } 231 else if (PartEntry->PartitionType == PARTITION_IFS) 232 { 233 if (CheckNtfsFormat()) 234 FileSystemName = L"NTFS"; 235 else if (CheckHpfsFormat()) 236 FileSystemName = L"HPFS"; 237 else 238 FileSystemName = NULL; 239 } 240 else 241 { 242 FileSystemName = NULL; 243 } 244 245 #endif 246 247 #if 0 // FIXME: To be fully enabled when our storage stack & al. work better! 248 249 /* 250 * We don't have one... 251 * 252 * Try to infer one using NT file system recognition. 253 */ 254 Status = _MyGetFileSystem(PartEntry, FsRecFileSystemName, ARRAYSIZE(FsRecFileSystemName)); 255 if (NT_SUCCESS(Status) && *FsRecFileSystemName) 256 { 257 /* Temporary HACK: map FAT32 back to FAT */ 258 if (wcscmp(FsRecFileSystemName, L"FAT32") == 0) 259 wcscpy(FsRecFileSystemName, L"FAT"); 260 261 FileSystemName = FsRecFileSystemName; 262 goto Quit; 263 } 264 265 #endif 266 267 /* 268 * We don't have one... 269 * 270 * Try to infer a preferred file system for this partition, given its ID. 271 * 272 * WARNING: This is partly a hack, since partitions with the same ID can 273 * be formatted with different file systems: for example, usual Linux 274 * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the 275 * same partition ID 0x83. 276 * 277 * The proper fix is to make a function that detects the existing FS 278 * from a given partition (not based on the partition ID). 279 * On the contrary, for unformatted partitions with a given ID, the 280 * following code is OK. 281 */ 282 if ((PartEntry->PartitionType == PARTITION_FAT_12) || 283 (PartEntry->PartitionType == PARTITION_FAT_16) || 284 (PartEntry->PartitionType == PARTITION_HUGE ) || 285 (PartEntry->PartitionType == PARTITION_XINT13) || 286 (PartEntry->PartitionType == PARTITION_FAT32 ) || 287 (PartEntry->PartitionType == PARTITION_FAT32_XINT13)) 288 { 289 FileSystemName = L"FAT"; 290 } 291 else if (PartEntry->PartitionType == PARTITION_LINUX) 292 { 293 // WARNING: See the warning above. 294 FileSystemName = L"BTRFS"; 295 } 296 else if (PartEntry->PartitionType == PARTITION_IFS) 297 { 298 // WARNING: See the warning above. 299 FileSystemName = L"NTFS"; /* FIXME: Not quite correct! */ 300 // FIXME: We may have HPFS too... 301 } 302 303 #if 0 304 Quit: // For code temporarily disabled above 305 #endif 306 307 // HACK: WARNING: We cannot write on this FS yet! 308 if (FileSystemName) 309 { 310 if (PartEntry->PartitionType == PARTITION_IFS) 311 DPRINT1("Recognized file system %S that doesn't support write support yet!\n", FileSystemName); 312 } 313 314 DPRINT1("GetFileSystem -- PartitionType: 0x%02X ; FileSystemName (guessed): %S\n", 315 PartEntry->PartitionType, FileSystemName ? FileSystemName : L"None"); 316 317 if (FileSystemName != NULL) 318 CurrentFileSystem = GetFileSystemByName(FileSystemName); 319 320 return CurrentFileSystem; 321 } 322 323 324 // 325 // Formatting routines 326 // 327 328 BOOLEAN 329 PreparePartitionForFormatting( 330 IN struct _PARTENTRY* PartEntry, 331 IN PFILE_SYSTEM FileSystem) 332 { 333 if (!FileSystem) 334 { 335 DPRINT1("No file system specified?\n"); 336 return FALSE; 337 } 338 339 if (wcscmp(FileSystem->FileSystemName, L"FAT") == 0) 340 { 341 if (PartEntry->SectorCount.QuadPart < 8192) 342 { 343 /* FAT12 CHS partition (disk is smaller than 4.1MB) */ 344 SetPartitionType(PartEntry, PARTITION_FAT_12); 345 } 346 else if (PartEntry->StartSector.QuadPart < 1450560) 347 { 348 /* Partition starts below the 8.4GB boundary ==> CHS partition */ 349 350 if (PartEntry->SectorCount.QuadPart < 65536) 351 { 352 /* FAT16 CHS partition (partition size < 32MB) */ 353 SetPartitionType(PartEntry, PARTITION_FAT_16); 354 } 355 else if (PartEntry->SectorCount.QuadPart < 1048576) 356 { 357 /* FAT16 CHS partition (partition size < 512MB) */ 358 SetPartitionType(PartEntry, PARTITION_HUGE); 359 } 360 else 361 { 362 /* FAT32 CHS partition (partition size >= 512MB) */ 363 SetPartitionType(PartEntry, PARTITION_FAT32); 364 } 365 } 366 else 367 { 368 /* Partition starts above the 8.4GB boundary ==> LBA partition */ 369 370 if (PartEntry->SectorCount.QuadPart < 1048576) 371 { 372 /* FAT16 LBA partition (partition size < 512MB) */ 373 SetPartitionType(PartEntry, PARTITION_XINT13); 374 } 375 else 376 { 377 /* FAT32 LBA partition (partition size >= 512MB) */ 378 SetPartitionType(PartEntry, PARTITION_FAT32_XINT13); 379 } 380 } 381 } 382 else if (wcscmp(FileSystem->FileSystemName, L"BTRFS") == 0) 383 { 384 SetPartitionType(PartEntry, PARTITION_LINUX); 385 } 386 #if 0 387 else if (wcscmp(FileSystem->FileSystemName, L"EXT2") == 0) 388 { 389 SetPartitionType(PartEntry, PARTITION_LINUX); 390 } 391 else if (wcscmp(FileSystem->FileSystemName, L"NTFS") == 0) 392 { 393 SetPartitionType(PartEntry, PARTITION_IFS); 394 } 395 #endif 396 else 397 { 398 /* Unknown file system? */ 399 DPRINT1("Unknown file system \"%S\"?\n", FileSystem->FileSystemName); 400 return FALSE; 401 } 402 403 // 404 // FIXME: Do this now, or after the partition was actually formatted?? 405 // 406 /* Set the new partition's file system proper */ 407 PartEntry->FormatState = Formatted; // Well... This may be set after the real formatting takes place (in which case we should change the FormatState to another value) 408 PartEntry->FileSystem = FileSystem; 409 410 return TRUE; 411 } 412 413 /* EOF */ 414