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