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 NTSTATUS 23 GetFileSystemNameByHandle( 24 IN HANDLE PartitionHandle, 25 IN OUT PWSTR FileSystemName, 26 IN SIZE_T FileSystemNameSize) 27 { 28 NTSTATUS Status; 29 IO_STATUS_BLOCK IoStatusBlock; 30 UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; 31 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; 32 33 /* Retrieve the FS attributes */ 34 Status = NtQueryVolumeInformationFile(PartitionHandle, 35 &IoStatusBlock, 36 FileFsAttribute, 37 sizeof(Buffer), 38 FileFsAttributeInformation); 39 if (!NT_SUCCESS(Status)) 40 { 41 DPRINT1("NtQueryVolumeInformationFile failed, Status 0x%08lx\n", Status); 42 return Status; 43 } 44 45 if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) 46 return STATUS_BUFFER_TOO_SMALL; 47 48 return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize, 49 FileFsAttribute->FileSystemName, 50 FileFsAttribute->FileSystemNameLength); 51 } 52 53 NTSTATUS 54 GetFileSystemName_UStr( 55 IN PUNICODE_STRING PartitionPath, 56 IN OUT PWSTR FileSystemName, 57 IN SIZE_T FileSystemNameSize) 58 { 59 NTSTATUS Status; 60 OBJECT_ATTRIBUTES ObjectAttributes; 61 HANDLE PartitionHandle; 62 IO_STATUS_BLOCK IoStatusBlock; 63 64 /* Open the partition */ 65 InitializeObjectAttributes(&ObjectAttributes, 66 PartitionPath, 67 OBJ_CASE_INSENSITIVE, 68 NULL, 69 NULL); 70 Status = NtOpenFile(&PartitionHandle, 71 FILE_GENERIC_READ /* | SYNCHRONIZE */, 72 &ObjectAttributes, 73 &IoStatusBlock, 74 FILE_SHARE_READ | FILE_SHARE_WRITE, 75 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 76 if (!NT_SUCCESS(Status)) 77 { 78 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", PartitionPath, Status); 79 return Status; 80 } 81 82 /* Retrieve the FS attributes */ 83 Status = GetFileSystemNameByHandle(PartitionHandle, FileSystemName, FileSystemNameSize); 84 if (!NT_SUCCESS(Status)) 85 { 86 DPRINT1("GetFileSystemNameByHandle() failed for partition '%wZ', Status 0x%08lx\n", 87 PartitionPath, Status); 88 } 89 90 /* Close the partition */ 91 NtClose(PartitionHandle); 92 93 return Status; 94 } 95 96 NTSTATUS 97 GetFileSystemName( 98 IN PCWSTR Partition, 99 IN OUT PWSTR FileSystemName, 100 IN SIZE_T FileSystemNameSize) 101 { 102 UNICODE_STRING PartitionPath; 103 104 RtlInitUnicodeString(&PartitionPath, Partition); 105 return GetFileSystemName_UStr(&PartitionPath, 106 FileSystemName, 107 FileSystemNameSize); 108 } 109 110 NTSTATUS 111 InferFileSystemByHandle( 112 IN HANDLE PartitionHandle, 113 IN UCHAR PartitionType, 114 IN OUT PWSTR FileSystemName, 115 IN SIZE_T FileSystemNameSize) 116 { 117 NTSTATUS Status; 118 119 if (FileSystemNameSize < sizeof(WCHAR)) 120 return STATUS_BUFFER_TOO_SMALL; 121 122 *FileSystemName = L'\0'; 123 124 /* Try to infer a file system using NT file system recognition */ 125 Status = GetFileSystemNameByHandle(PartitionHandle, 126 FileSystemName, 127 FileSystemNameSize); 128 if (NT_SUCCESS(Status) && *FileSystemName) 129 { 130 goto Quit; 131 } 132 133 /* 134 * Try to infer a preferred file system for this partition, given its ID. 135 * 136 * WARNING: This is partly a hack, since partitions with the same ID can 137 * be formatted with different file systems: for example, usual Linux 138 * partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the 139 * same partition ID 0x83. 140 * 141 * The proper fix is to make a function that detects the existing FS 142 * from a given partition (not based on the partition ID). 143 * On the contrary, for unformatted partitions with a given ID, the 144 * following code is OK. 145 */ 146 if ((PartitionType == PARTITION_FAT_12) || 147 (PartitionType == PARTITION_FAT_16) || 148 (PartitionType == PARTITION_HUGE ) || 149 (PartitionType == PARTITION_XINT13)) 150 { 151 /* FAT12 or FAT16 */ 152 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT"); 153 } 154 else if ((PartitionType == PARTITION_FAT32) || 155 (PartitionType == PARTITION_FAT32_XINT13)) 156 { 157 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT32"); 158 } 159 else if (PartitionType == PARTITION_LINUX) 160 { 161 // WARNING: See the warning above. 162 /* Could also be EXT2/3/4, ReiserFS, ... */ 163 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"BTRFS"); 164 } 165 else if (PartitionType == PARTITION_IFS) 166 { 167 // WARNING: See the warning above. 168 /* Could also be HPFS */ 169 Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"NTFS"); 170 } 171 172 Quit: 173 if (*FileSystemName) 174 { 175 // WARNING: We cannot write on this FS yet! 176 if (PartitionType == PARTITION_IFS) 177 { 178 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n", 179 FileSystemName); 180 } 181 } 182 183 DPRINT1("InferFileSystem -- PartitionType: 0x%02X ; FileSystem (guessed): %S\n", 184 PartitionType, *FileSystemName ? FileSystemName : L"None"); 185 186 return Status; 187 } 188 189 NTSTATUS 190 InferFileSystem( 191 IN PCWSTR Partition, 192 IN UCHAR PartitionType, 193 IN OUT PWSTR FileSystemName, 194 IN SIZE_T FileSystemNameSize) 195 { 196 NTSTATUS Status; 197 UNICODE_STRING PartitionPath; 198 OBJECT_ATTRIBUTES ObjectAttributes; 199 HANDLE PartitionHandle; 200 IO_STATUS_BLOCK IoStatusBlock; 201 202 /* Open the partition */ 203 RtlInitUnicodeString(&PartitionPath, Partition); 204 InitializeObjectAttributes(&ObjectAttributes, 205 &PartitionPath, 206 OBJ_CASE_INSENSITIVE, 207 NULL, 208 NULL); 209 Status = NtOpenFile(&PartitionHandle, 210 FILE_GENERIC_READ /* | SYNCHRONIZE */, 211 &ObjectAttributes, 212 &IoStatusBlock, 213 FILE_SHARE_READ | FILE_SHARE_WRITE, 214 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); 215 if (!NT_SUCCESS(Status)) 216 { 217 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionPath, Status); 218 return Status; 219 } 220 221 /* Retrieve the FS */ 222 Status = InferFileSystemByHandle(PartitionHandle, 223 PartitionType, 224 FileSystemName, 225 FileSystemNameSize); 226 227 /* Close the partition */ 228 NtClose(PartitionHandle); 229 230 return Status; 231 } 232 233 UCHAR 234 FileSystemToPartitionType( 235 IN PCWSTR FileSystem, 236 IN PULARGE_INTEGER StartSector, 237 IN PULARGE_INTEGER SectorCount) 238 { 239 ASSERT(FileSystem && StartSector && SectorCount); 240 241 if (wcsicmp(FileSystem, L"FAT") == 0 || 242 wcsicmp(FileSystem, L"FAT32") == 0 || 243 wcsicmp(FileSystem, L"RAW") == 0) 244 { 245 if (SectorCount->QuadPart < 8192) 246 { 247 /* FAT12 CHS partition (disk is smaller than 4.1MB) */ 248 return PARTITION_FAT_12; 249 } 250 else if (StartSector->QuadPart < 1450560) 251 { 252 /* Partition starts below the 8.4GB boundary ==> CHS partition */ 253 254 if (SectorCount->QuadPart < 65536) 255 { 256 /* FAT16 CHS partition (partition size < 32MB) */ 257 return PARTITION_FAT_16; 258 } 259 else if (SectorCount->QuadPart < 1048576) 260 { 261 /* FAT16 CHS partition (partition size < 512MB) */ 262 return PARTITION_HUGE; 263 } 264 else 265 { 266 /* FAT32 CHS partition (partition size >= 512MB) */ 267 return PARTITION_FAT32; 268 } 269 } 270 else 271 { 272 /* Partition starts above the 8.4GB boundary ==> LBA partition */ 273 274 if (SectorCount->QuadPart < 1048576) 275 { 276 /* FAT16 LBA partition (partition size < 512MB) */ 277 return PARTITION_XINT13; 278 } 279 else 280 { 281 /* FAT32 LBA partition (partition size >= 512MB) */ 282 return PARTITION_FAT32_XINT13; 283 } 284 } 285 } 286 else if (wcsicmp(FileSystem, L"NTFS") == 0) 287 { 288 return PARTITION_IFS; 289 } 290 else if (wcsicmp(FileSystem, L"BTRFS") == 0 || 291 wcsicmp(FileSystem, L"EXT2") == 0 || 292 wcsicmp(FileSystem, L"EXT3") == 0 || 293 wcsicmp(FileSystem, L"EXT4") == 0 || 294 wcsicmp(FileSystem, L"FFS") == 0 || 295 wcsicmp(FileSystem, L"REISERFS") == 0) 296 { 297 return PARTITION_LINUX; 298 } 299 else 300 { 301 /* Unknown file system */ 302 DPRINT1("Unknown file system '%S'\n", FileSystem); 303 return PARTITION_ENTRY_UNUSED; 304 } 305 } 306 307 /* EOF */ 308