1 /* 2 * PROJECT: ReactOS Setup Library 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Filesystem Recognition support functions, 5 * using NT OS functionality. 6 * COPYRIGHT: Copyright 2017-2020 Hermes Belusca-Maito 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include "precomp.h" 12 13 #include "fsrec.h" 14 15 #define NDEBUG 16 #include <debug.h> 17 18 19 /* FUNCTIONS ****************************************************************/ 20 21 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */ 22 static inline 23 NTSTATUS 24 GetFileSystemNameWorker( 25 IN HANDLE PartitionHandle, 26 OUT PIO_STATUS_BLOCK IoStatusBlock, 27 IN OUT PWSTR FileSystemName, 28 IN SIZE_T FileSystemNameSize) 29 { 30 NTSTATUS Status; 31 UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; 32 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; 33 34 /* Retrieve the FS attributes */ 35 Status = NtQueryVolumeInformationFile(PartitionHandle, 36 IoStatusBlock, 37 FileFsAttribute, 38 sizeof(Buffer), 39 FileFsAttributeInformation); 40 if (!NT_SUCCESS(Status)) 41 { 42 DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status); 43 return Status; 44 } 45 46 if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) 47 return STATUS_BUFFER_TOO_SMALL; 48 49 return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize, 50 FileFsAttribute->FileSystemName, 51 FileFsAttribute->FileSystemNameLength); 52 } 53 54 NTSTATUS 55 GetFileSystemName_UStr( 56 IN PUNICODE_STRING PartitionPath OPTIONAL, 57 IN HANDLE PartitionHandle OPTIONAL, 58 IN OUT PWSTR FileSystemName, 59 IN SIZE_T FileSystemNameSize) 60 { 61 NTSTATUS Status; 62 OBJECT_ATTRIBUTES ObjectAttributes; 63 IO_STATUS_BLOCK IoStatusBlock; 64 65 if (PartitionPath && PartitionHandle) 66 return STATUS_INVALID_PARAMETER; 67 68 /* Open the partition if a path has been given; 69 * otherwise just use the provided handle. */ 70 if (PartitionPath) 71 { 72 InitializeObjectAttributes(&ObjectAttributes, 73 PartitionPath, 74 OBJ_CASE_INSENSITIVE, 75 NULL, 76 NULL); 77 Status = NtOpenFile(&PartitionHandle, 78 FILE_GENERIC_READ /* | SYNCHRONIZE */, 79 &ObjectAttributes, 80 &IoStatusBlock, 81 FILE_SHARE_READ | FILE_SHARE_WRITE, 82 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 83 if (!NT_SUCCESS(Status)) 84 { 85 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", 86 PartitionPath, Status); 87 return Status; 88 } 89 } 90 91 /* Retrieve the FS attributes */ 92 Status = GetFileSystemNameWorker(PartitionHandle, 93 &IoStatusBlock, 94 FileSystemName, 95 FileSystemNameSize); 96 if (!NT_SUCCESS(Status)) 97 { 98 DPRINT1("GetFileSystemName() failed for partition '%wZ' (0x%p), Status 0x%08lx\n", 99 PartitionPath, PartitionHandle, Status); 100 } 101 102 if (PartitionPath) 103 { 104 /* Close the partition */ 105 NtClose(PartitionHandle); 106 } 107 108 return Status; 109 } 110 111 NTSTATUS 112 GetFileSystemName( 113 IN PCWSTR PartitionPath OPTIONAL, 114 IN HANDLE PartitionHandle OPTIONAL, 115 IN OUT PWSTR FileSystemName, 116 IN SIZE_T FileSystemNameSize) 117 { 118 UNICODE_STRING PartitionPathU; 119 120 if (PartitionPath && PartitionHandle) 121 return STATUS_INVALID_PARAMETER; 122 123 if (PartitionPath) 124 RtlInitUnicodeString(&PartitionPathU, PartitionPath); 125 126 return GetFileSystemName_UStr(PartitionPath ? &PartitionPathU : NULL, 127 PartitionPath ? NULL : PartitionHandle, 128 FileSystemName, 129 FileSystemNameSize); 130 } 131 132 static inline 133 NTSTATUS 134 InferFileSystemWorker( 135 IN HANDLE PartitionHandle, 136 OUT PIO_STATUS_BLOCK IoStatusBlock, 137 IN OUT PWSTR FileSystemName, 138 IN SIZE_T FileSystemNameSize) 139 { 140 NTSTATUS Status, Status2; 141 union 142 { 143 PARTITION_INFORMATION_EX InfoEx; 144 PARTITION_INFORMATION Info; 145 } PartInfo; 146 UCHAR PartitionType; 147 148 if (FileSystemNameSize < sizeof(WCHAR)) 149 return STATUS_BUFFER_TOO_SMALL; 150 151 *FileSystemName = L'\0'; 152 153 /* Try to infer a file system using NT file system recognition */ 154 Status = GetFileSystemName_UStr(NULL, PartitionHandle, 155 FileSystemName, 156 FileSystemNameSize); 157 if (NT_SUCCESS(Status) && *FileSystemName) 158 goto Quit; 159 160 /* 161 * Check whether the partition is MBR, and if so, retrieve its MBR 162 * partition type and try to infer a preferred file system for it. 163 */ 164 165 // NOTE: Use Status2 in order not to clobber the original Status. 166 Status2 = NtDeviceIoControlFile(PartitionHandle, 167 NULL, 168 NULL, 169 NULL, 170 IoStatusBlock, 171 IOCTL_DISK_GET_PARTITION_INFO_EX, 172 NULL, 173 0, 174 &PartInfo.InfoEx, 175 sizeof(PartInfo.InfoEx)); 176 if (!NT_SUCCESS(Status2)) 177 { 178 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status2); 179 180 if (Status2 != STATUS_INVALID_DEVICE_REQUEST) 181 goto Quit; 182 183 /* 184 * We could have failed because the partition is on a dynamic 185 * MBR or GPT data disk, so retry with the non-EX IOCTL. 186 */ 187 Status2 = NtDeviceIoControlFile(PartitionHandle, 188 NULL, 189 NULL, 190 NULL, 191 IoStatusBlock, 192 IOCTL_DISK_GET_PARTITION_INFO, 193 NULL, 194 0, 195 &PartInfo.Info, 196 sizeof(PartInfo.Info)); 197 if (!NT_SUCCESS(Status2)) 198 { 199 /* We failed again, bail out */ 200 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO failed (Status %lx)\n", Status2); 201 goto Quit; 202 } 203 204 /* The partition is supposed to be on an MBR disk; retrieve its type */ 205 PartitionType = PartInfo.Info.PartitionType; 206 } 207 else 208 { 209 /* We succeeded; retrieve the partition type only if it is on an MBR disk */ 210 if (PartInfo.InfoEx.PartitionStyle != PARTITION_STYLE_MBR) 211 { 212 /* Disk is not MBR, bail out */ 213 goto Quit; 214 } 215 PartitionType = PartInfo.InfoEx.Mbr.PartitionType; 216 } 217 218 /* 219 * Given an MBR partition type, try to infer a preferred file system. 220 * 221 * WARNING: This is partly a hack, since partitions with the same type 222 * can be formatted with different file systems: for example, usual Linux 223 * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the 224 * same partition type 0x83. 225 * 226 * The proper fix is to make a function that detects the existing FS 227 * from a given partition (not based on the partition type). 228 * On the contrary, for unformatted partitions with a given type, the 229 * following code is OK. 230 */ 231 if ((PartitionType == PARTITION_FAT_12) || 232 (PartitionType == PARTITION_FAT_16) || 233 (PartitionType == PARTITION_HUGE ) || 234 (PartitionType == PARTITION_XINT13)) 235 { 236 /* FAT12 or FAT16 */ 237 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT"); 238 } 239 else if ((PartitionType == PARTITION_FAT32) || 240 (PartitionType == PARTITION_FAT32_XINT13)) 241 { 242 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT32"); 243 } 244 else if (PartitionType == PARTITION_LINUX) 245 { 246 // WARNING: See the warning above. 247 /* Could also be EXT2/3/4, ReiserFS, ... */ 248 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"BTRFS"); 249 } 250 else if (PartitionType == PARTITION_IFS) 251 { 252 // WARNING: See the warning above. 253 /* Could also be HPFS */ 254 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"NTFS"); 255 } 256 257 Quit: 258 if (*FileSystemName && _wcsicmp(FileSystemName, L"NTFS") == 0) 259 { 260 // WARNING: We cannot write on this FS yet! 261 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n", 262 FileSystemName); 263 } 264 265 return Status; 266 } 267 268 NTSTATUS 269 InferFileSystem( 270 IN PCWSTR PartitionPath OPTIONAL, 271 IN HANDLE PartitionHandle OPTIONAL, 272 IN OUT PWSTR FileSystemName, 273 IN SIZE_T FileSystemNameSize) 274 { 275 NTSTATUS Status; 276 UNICODE_STRING PartitionPathU; 277 OBJECT_ATTRIBUTES ObjectAttributes; 278 IO_STATUS_BLOCK IoStatusBlock; 279 280 if (PartitionPath && PartitionHandle) 281 return STATUS_INVALID_PARAMETER; 282 283 /* Open the partition if a path has been given; 284 * otherwise just use the provided handle. */ 285 if (PartitionPath) 286 { 287 RtlInitUnicodeString(&PartitionPathU, PartitionPath); 288 InitializeObjectAttributes(&ObjectAttributes, 289 &PartitionPathU, 290 OBJ_CASE_INSENSITIVE, 291 NULL, 292 NULL); 293 Status = NtOpenFile(&PartitionHandle, 294 FILE_GENERIC_READ /* | SYNCHRONIZE */, 295 &ObjectAttributes, 296 &IoStatusBlock, 297 FILE_SHARE_READ | FILE_SHARE_WRITE, 298 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 299 if (!NT_SUCCESS(Status)) 300 { 301 DPRINT1("Failed to open partition '%S', Status 0x%08lx\n", 302 PartitionPath, Status); 303 return Status; 304 } 305 } 306 307 /* Retrieve the FS */ 308 Status = InferFileSystemWorker(PartitionHandle, 309 &IoStatusBlock, 310 FileSystemName, 311 FileSystemNameSize); 312 if (!NT_SUCCESS(Status)) 313 { 314 DPRINT1("InferFileSystem() failed for partition '%S' (0x%p), Status 0x%08lx\n", 315 PartitionPath, PartitionHandle, Status); 316 } 317 else 318 { 319 DPRINT1("InferFileSystem(): FileSystem (guessed): %S\n", 320 *FileSystemName ? FileSystemName : L"None"); 321 } 322 323 if (PartitionPath) 324 { 325 /* Close the partition */ 326 NtClose(PartitionHandle); 327 } 328 329 return Status; 330 } 331 332 UCHAR 333 FileSystemToMBRPartitionType( 334 IN PCWSTR FileSystem, 335 IN ULONGLONG StartSector, 336 IN ULONGLONG SectorCount) 337 { 338 ASSERT(FileSystem); 339 340 if (SectorCount == 0) 341 return PARTITION_ENTRY_UNUSED; 342 343 if (_wcsicmp(FileSystem, L"FAT") == 0 || 344 _wcsicmp(FileSystem, L"FAT32") == 0 || 345 _wcsicmp(FileSystem, L"RAW") == 0) 346 { 347 if (SectorCount < 8192ULL) 348 { 349 /* FAT12 CHS partition (disk is smaller than 4.1MB) */ 350 return PARTITION_FAT_12; 351 } 352 else if (StartSector < 1450560ULL) 353 { 354 /* Partition starts below the 8.4GB boundary ==> CHS partition */ 355 356 if (SectorCount < 65536ULL) 357 { 358 /* FAT16 CHS partition (partition size < 32MB) */ 359 return PARTITION_FAT_16; 360 } 361 else if (SectorCount < 1048576ULL) 362 { 363 /* FAT16 CHS partition (partition size < 512MB) */ 364 return PARTITION_HUGE; 365 } 366 else 367 { 368 /* FAT32 CHS partition (partition size >= 512MB) */ 369 return PARTITION_FAT32; 370 } 371 } 372 else 373 { 374 /* Partition starts above the 8.4GB boundary ==> LBA partition */ 375 376 if (SectorCount < 1048576ULL) 377 { 378 /* FAT16 LBA partition (partition size < 512MB) */ 379 return PARTITION_XINT13; 380 } 381 else 382 { 383 /* FAT32 LBA partition (partition size >= 512MB) */ 384 return PARTITION_FAT32_XINT13; 385 } 386 } 387 } 388 else if (_wcsicmp(FileSystem, L"NTFS") == 0) 389 { 390 return PARTITION_IFS; 391 } 392 else if (_wcsicmp(FileSystem, L"BTRFS") == 0 || 393 _wcsicmp(FileSystem, L"EXT2") == 0 || 394 _wcsicmp(FileSystem, L"EXT3") == 0 || 395 _wcsicmp(FileSystem, L"EXT4") == 0) 396 { 397 return PARTITION_LINUX; 398 } 399 else 400 { 401 /* Unknown file system */ 402 DPRINT1("Unknown file system '%S'\n", FileSystem); 403 return PARTITION_ENTRY_UNUSED; 404 } 405 } 406 407 /* EOF */ 408