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
9 /* INCLUDES *****************************************************************/
19 /* FUNCTIONS ****************************************************************/
21 /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
24 GetFileSystemNameWorker(
25 IN HANDLE PartitionHandle
,
26 OUT PIO_STATUS_BLOCK IoStatusBlock
,
27 IN OUT PWSTR FileSystemName
,
28 IN SIZE_T FileSystemNameSize
)
31 UCHAR Buffer
[sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
32 PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute
= (PFILE_FS_ATTRIBUTE_INFORMATION
)Buffer
;
34 /* Retrieve the FS attributes */
35 Status
= NtQueryVolumeInformationFile(PartitionHandle
,
39 FileFsAttributeInformation
);
40 if (!NT_SUCCESS(Status
))
42 DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status
);
46 if (FileSystemNameSize
< FileFsAttribute
->FileSystemNameLength
+ sizeof(WCHAR
))
47 return STATUS_BUFFER_TOO_SMALL
;
49 return RtlStringCbCopyNW(FileSystemName
, FileSystemNameSize
,
50 FileFsAttribute
->FileSystemName
,
51 FileFsAttribute
->FileSystemNameLength
);
55 GetFileSystemName_UStr(
56 IN PUNICODE_STRING PartitionPath OPTIONAL
,
57 IN HANDLE PartitionHandle OPTIONAL
,
58 IN OUT PWSTR FileSystemName
,
59 IN SIZE_T FileSystemNameSize
)
62 OBJECT_ATTRIBUTES ObjectAttributes
;
63 IO_STATUS_BLOCK IoStatusBlock
;
65 if (PartitionPath
&& PartitionHandle
)
66 return STATUS_INVALID_PARAMETER
;
68 /* Open the partition if a path has been given;
69 * otherwise just use the provided handle. */
72 InitializeObjectAttributes(&ObjectAttributes
,
77 Status
= NtOpenFile(&PartitionHandle
,
78 FILE_GENERIC_READ
/* | SYNCHRONIZE */,
81 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
82 0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
83 if (!NT_SUCCESS(Status
))
85 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n",
86 PartitionPath
, Status
);
91 /* Retrieve the FS attributes */
92 Status
= GetFileSystemNameWorker(PartitionHandle
,
96 if (!NT_SUCCESS(Status
))
98 DPRINT1("GetFileSystemName() failed for partition '%wZ' (0x%p), Status 0x%08lx\n",
99 PartitionPath
, PartitionHandle
, Status
);
104 /* Close the partition */
105 NtClose(PartitionHandle
);
113 IN PCWSTR PartitionPath OPTIONAL
,
114 IN HANDLE PartitionHandle OPTIONAL
,
115 IN OUT PWSTR FileSystemName
,
116 IN SIZE_T FileSystemNameSize
)
118 UNICODE_STRING PartitionPathU
;
120 if (PartitionPath
&& PartitionHandle
)
121 return STATUS_INVALID_PARAMETER
;
124 RtlInitUnicodeString(&PartitionPathU
, PartitionPath
);
126 return GetFileSystemName_UStr(PartitionPath
? &PartitionPathU
: NULL
,
127 PartitionPath
? NULL
: PartitionHandle
,
134 InferFileSystemWorker(
135 IN HANDLE PartitionHandle
,
136 OUT PIO_STATUS_BLOCK IoStatusBlock
,
137 IN OUT PWSTR FileSystemName
,
138 IN SIZE_T FileSystemNameSize
)
140 NTSTATUS Status
, Status2
;
143 PARTITION_INFORMATION_EX InfoEx
;
144 PARTITION_INFORMATION Info
;
148 if (FileSystemNameSize
< sizeof(WCHAR
))
149 return STATUS_BUFFER_TOO_SMALL
;
151 *FileSystemName
= L
'\0';
153 /* Try to infer a file system using NT file system recognition */
154 Status
= GetFileSystemName_UStr(NULL
, PartitionHandle
,
157 if (NT_SUCCESS(Status
) && *FileSystemName
)
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.
165 // NOTE: Use Status2 in order not to clobber the original Status.
166 Status2
= NtDeviceIoControlFile(PartitionHandle
,
171 IOCTL_DISK_GET_PARTITION_INFO_EX
,
175 sizeof(PartInfo
.InfoEx
));
176 if (!NT_SUCCESS(Status2
))
178 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status2
);
180 if (Status2
!= STATUS_INVALID_DEVICE_REQUEST
)
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.
187 Status2
= NtDeviceIoControlFile(PartitionHandle
,
192 IOCTL_DISK_GET_PARTITION_INFO
,
196 sizeof(PartInfo
.Info
));
197 if (!NT_SUCCESS(Status2
))
199 /* We failed again, bail out */
200 DPRINT1("IOCTL_DISK_GET_PARTITION_INFO failed (Status %lx)\n", Status2
);
204 /* The partition is supposed to be on an MBR disk; retrieve its type */
205 PartitionType
= PartInfo
.Info
.PartitionType
;
209 /* We succeeded; retrieve the partition type only if it is on an MBR disk */
210 if (PartInfo
.InfoEx
.PartitionStyle
!= PARTITION_STYLE_MBR
)
212 /* Disk is not MBR, bail out */
215 PartitionType
= PartInfo
.InfoEx
.Mbr
.PartitionType
;
219 * Given an MBR partition type, try to infer a preferred file system.
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.
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.
231 if ((PartitionType
== PARTITION_FAT_12
) ||
232 (PartitionType
== PARTITION_FAT_16
) ||
233 (PartitionType
== PARTITION_HUGE
) ||
234 (PartitionType
== PARTITION_XINT13
))
237 Status
= RtlStringCbCopyW(FileSystemName
, FileSystemNameSize
, L
"FAT");
239 else if ((PartitionType
== PARTITION_FAT32
) ||
240 (PartitionType
== PARTITION_FAT32_XINT13
))
242 Status
= RtlStringCbCopyW(FileSystemName
, FileSystemNameSize
, L
"FAT32");
244 else if (PartitionType
== PARTITION_LINUX
)
246 // WARNING: See the warning above.
247 /* Could also be EXT2/3/4, ReiserFS, ... */
248 Status
= RtlStringCbCopyW(FileSystemName
, FileSystemNameSize
, L
"BTRFS");
250 else if (PartitionType
== PARTITION_IFS
)
252 // WARNING: See the warning above.
253 /* Could also be HPFS */
254 Status
= RtlStringCbCopyW(FileSystemName
, FileSystemNameSize
, L
"NTFS");
258 if (*FileSystemName
&& wcsicmp(FileSystemName
, L
"NTFS") == 0)
260 // WARNING: We cannot write on this FS yet!
261 DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
270 IN PCWSTR PartitionPath OPTIONAL
,
271 IN HANDLE PartitionHandle OPTIONAL
,
272 IN OUT PWSTR FileSystemName
,
273 IN SIZE_T FileSystemNameSize
)
276 UNICODE_STRING PartitionPathU
;
277 OBJECT_ATTRIBUTES ObjectAttributes
;
278 IO_STATUS_BLOCK IoStatusBlock
;
280 if (PartitionPath
&& PartitionHandle
)
281 return STATUS_INVALID_PARAMETER
;
283 /* Open the partition if a path has been given;
284 * otherwise just use the provided handle. */
287 RtlInitUnicodeString(&PartitionPathU
, PartitionPath
);
288 InitializeObjectAttributes(&ObjectAttributes
,
290 OBJ_CASE_INSENSITIVE
,
293 Status
= NtOpenFile(&PartitionHandle
,
294 FILE_GENERIC_READ
/* | SYNCHRONIZE */,
297 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
298 0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
299 if (!NT_SUCCESS(Status
))
301 DPRINT1("Failed to open partition '%S', Status 0x%08lx\n",
302 PartitionPath
, Status
);
307 /* Retrieve the FS */
308 Status
= InferFileSystemWorker(PartitionHandle
,
312 if (!NT_SUCCESS(Status
))
314 DPRINT1("InferFileSystem() failed for partition '%S' (0x%p), Status 0x%08lx\n",
315 PartitionPath
, PartitionHandle
, Status
);
319 DPRINT1("InferFileSystem(): FileSystem (guessed): %S\n",
320 *FileSystemName
? FileSystemName
: L
"None");
325 /* Close the partition */
326 NtClose(PartitionHandle
);
333 FileSystemToMBRPartitionType(
334 IN PCWSTR FileSystem
,
335 IN ULONGLONG StartSector
,
336 IN ULONGLONG SectorCount
)
340 if (SectorCount
== 0)
341 return PARTITION_ENTRY_UNUSED
;
343 if (wcsicmp(FileSystem
, L
"FAT") == 0 ||
344 wcsicmp(FileSystem
, L
"FAT32") == 0 ||
345 wcsicmp(FileSystem
, L
"RAW") == 0)
347 if (SectorCount
< 8192ULL)
349 /* FAT12 CHS partition (disk is smaller than 4.1MB) */
350 return PARTITION_FAT_12
;
352 else if (StartSector
< 1450560ULL)
354 /* Partition starts below the 8.4GB boundary ==> CHS partition */
356 if (SectorCount
< 65536ULL)
358 /* FAT16 CHS partition (partition size < 32MB) */
359 return PARTITION_FAT_16
;
361 else if (SectorCount
< 1048576ULL)
363 /* FAT16 CHS partition (partition size < 512MB) */
364 return PARTITION_HUGE
;
368 /* FAT32 CHS partition (partition size >= 512MB) */
369 return PARTITION_FAT32
;
374 /* Partition starts above the 8.4GB boundary ==> LBA partition */
376 if (SectorCount
< 1048576ULL)
378 /* FAT16 LBA partition (partition size < 512MB) */
379 return PARTITION_XINT13
;
383 /* FAT32 LBA partition (partition size >= 512MB) */
384 return PARTITION_FAT32_XINT13
;
388 else if (wcsicmp(FileSystem
, L
"NTFS") == 0)
390 return PARTITION_IFS
;
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)
397 return PARTITION_LINUX
;
401 /* Unknown file system */
402 DPRINT1("Unknown file system '%S'\n", FileSystem
);
403 return PARTITION_ENTRY_UNUSED
;