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