mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 21:48:10 +00:00
49cfac17c5
The upstream driver is not maintained and the file system itself
is in a semi-abandoned state.
Originally imported at 3a3ef631d1
The driver is written by Lee Jae-Hong, updated by Bo Brantén.
ReactOS porting made by Peter Hater and Pierre Schweitzer.
Follow updates at http://www.acc.umu.se/~bosse/
FS Recognizer code is left to keep the FS support as an
installable driver.
CORE-11040
407 lines
13 KiB
C
407 lines
13 KiB
C
/*
|
|
* PROJECT: ReactOS Setup Library
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Filesystem Recognition support functions,
|
|
* using NT OS functionality.
|
|
* COPYRIGHT: Copyright 2017-2020 Hermes Belusca-Maito
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "fsrec.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
/* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
|
|
static inline
|
|
NTSTATUS
|
|
GetFileSystemNameWorker(
|
|
IN HANDLE PartitionHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN OUT PWSTR FileSystemName,
|
|
IN SIZE_T FileSystemNameSize)
|
|
{
|
|
NTSTATUS Status;
|
|
UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
|
|
PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer;
|
|
|
|
/* Retrieve the FS attributes */
|
|
Status = NtQueryVolumeInformationFile(PartitionHandle,
|
|
IoStatusBlock,
|
|
FileFsAttribute,
|
|
sizeof(Buffer),
|
|
FileFsAttributeInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtQueryVolumeInformationFile() failed, Status 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (FileSystemNameSize < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
return RtlStringCbCopyNW(FileSystemName, FileSystemNameSize,
|
|
FileFsAttribute->FileSystemName,
|
|
FileFsAttribute->FileSystemNameLength);
|
|
}
|
|
|
|
NTSTATUS
|
|
GetFileSystemName_UStr(
|
|
IN PUNICODE_STRING PartitionPath OPTIONAL,
|
|
IN HANDLE PartitionHandle OPTIONAL,
|
|
IN OUT PWSTR FileSystemName,
|
|
IN SIZE_T FileSystemNameSize)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
if (PartitionPath && PartitionHandle)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Open the partition if a path has been given;
|
|
* otherwise just use the provided handle. */
|
|
if (PartitionPath)
|
|
{
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
PartitionPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenFile(&PartitionHandle,
|
|
FILE_GENERIC_READ /* | SYNCHRONIZE */,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n",
|
|
PartitionPath, Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Retrieve the FS attributes */
|
|
Status = GetFileSystemNameWorker(PartitionHandle,
|
|
&IoStatusBlock,
|
|
FileSystemName,
|
|
FileSystemNameSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("GetFileSystemName() failed for partition '%wZ' (0x%p), Status 0x%08lx\n",
|
|
PartitionPath, PartitionHandle, Status);
|
|
}
|
|
|
|
if (PartitionPath)
|
|
{
|
|
/* Close the partition */
|
|
NtClose(PartitionHandle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetFileSystemName(
|
|
IN PCWSTR PartitionPath OPTIONAL,
|
|
IN HANDLE PartitionHandle OPTIONAL,
|
|
IN OUT PWSTR FileSystemName,
|
|
IN SIZE_T FileSystemNameSize)
|
|
{
|
|
UNICODE_STRING PartitionPathU;
|
|
|
|
if (PartitionPath && PartitionHandle)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (PartitionPath)
|
|
RtlInitUnicodeString(&PartitionPathU, PartitionPath);
|
|
|
|
return GetFileSystemName_UStr(PartitionPath ? &PartitionPathU : NULL,
|
|
PartitionPath ? NULL : PartitionHandle,
|
|
FileSystemName,
|
|
FileSystemNameSize);
|
|
}
|
|
|
|
static inline
|
|
NTSTATUS
|
|
InferFileSystemWorker(
|
|
IN HANDLE PartitionHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN OUT PWSTR FileSystemName,
|
|
IN SIZE_T FileSystemNameSize)
|
|
{
|
|
NTSTATUS Status, Status2;
|
|
union
|
|
{
|
|
PARTITION_INFORMATION_EX InfoEx;
|
|
PARTITION_INFORMATION Info;
|
|
} PartInfo;
|
|
UCHAR PartitionType;
|
|
|
|
if (FileSystemNameSize < sizeof(WCHAR))
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
*FileSystemName = L'\0';
|
|
|
|
/* Try to infer a file system using NT file system recognition */
|
|
Status = GetFileSystemName_UStr(NULL, PartitionHandle,
|
|
FileSystemName,
|
|
FileSystemNameSize);
|
|
if (NT_SUCCESS(Status) && *FileSystemName)
|
|
goto Quit;
|
|
|
|
/*
|
|
* Check whether the partition is MBR, and if so, retrieve its MBR
|
|
* partition type and try to infer a preferred file system for it.
|
|
*/
|
|
|
|
// NOTE: Use Status2 in order not to clobber the original Status.
|
|
Status2 = NtDeviceIoControlFile(PartitionHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
IoStatusBlock,
|
|
IOCTL_DISK_GET_PARTITION_INFO_EX,
|
|
NULL,
|
|
0,
|
|
&PartInfo.InfoEx,
|
|
sizeof(PartInfo.InfoEx));
|
|
if (!NT_SUCCESS(Status2))
|
|
{
|
|
DPRINT1("IOCTL_DISK_GET_PARTITION_INFO_EX failed (Status %lx)\n", Status2);
|
|
|
|
if (Status2 != STATUS_INVALID_DEVICE_REQUEST)
|
|
goto Quit;
|
|
|
|
/*
|
|
* We could have failed because the partition is on a dynamic
|
|
* MBR or GPT data disk, so retry with the non-EX IOCTL.
|
|
*/
|
|
Status2 = NtDeviceIoControlFile(PartitionHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
IoStatusBlock,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
&PartInfo.Info,
|
|
sizeof(PartInfo.Info));
|
|
if (!NT_SUCCESS(Status2))
|
|
{
|
|
/* We failed again, bail out */
|
|
DPRINT1("IOCTL_DISK_GET_PARTITION_INFO failed (Status %lx)\n", Status2);
|
|
goto Quit;
|
|
}
|
|
|
|
/* The partition is supposed to be on an MBR disk; retrieve its type */
|
|
PartitionType = PartInfo.Info.PartitionType;
|
|
}
|
|
else
|
|
{
|
|
/* We succeeded; retrieve the partition type only if it is on an MBR disk */
|
|
if (PartInfo.InfoEx.PartitionStyle != PARTITION_STYLE_MBR)
|
|
{
|
|
/* Disk is not MBR, bail out */
|
|
goto Quit;
|
|
}
|
|
PartitionType = PartInfo.InfoEx.Mbr.PartitionType;
|
|
}
|
|
|
|
/*
|
|
* Given an MBR partition type, try to infer a preferred file system.
|
|
*
|
|
* WARNING: This is partly a hack, since partitions with the same type
|
|
* can be formatted with different file systems: for example, usual Linux
|
|
* partitions that are formatted in EXT2/3/4, ReiserFS, etc... have the
|
|
* same partition type 0x83.
|
|
*
|
|
* The proper fix is to make a function that detects the existing FS
|
|
* from a given partition (not based on the partition type).
|
|
* On the contrary, for unformatted partitions with a given type, the
|
|
* following code is OK.
|
|
*/
|
|
if ((PartitionType == PARTITION_FAT_12) ||
|
|
(PartitionType == PARTITION_FAT_16) ||
|
|
(PartitionType == PARTITION_HUGE ) ||
|
|
(PartitionType == PARTITION_XINT13))
|
|
{
|
|
/* FAT12 or FAT16 */
|
|
Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT");
|
|
}
|
|
else if ((PartitionType == PARTITION_FAT32) ||
|
|
(PartitionType == PARTITION_FAT32_XINT13))
|
|
{
|
|
Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"FAT32");
|
|
}
|
|
else if (PartitionType == PARTITION_LINUX)
|
|
{
|
|
// WARNING: See the warning above.
|
|
/* Could also be EXT2/3/4, ReiserFS, ... */
|
|
Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"BTRFS");
|
|
}
|
|
else if (PartitionType == PARTITION_IFS)
|
|
{
|
|
// WARNING: See the warning above.
|
|
/* Could also be HPFS */
|
|
Status = RtlStringCbCopyW(FileSystemName, FileSystemNameSize, L"NTFS");
|
|
}
|
|
|
|
Quit:
|
|
if (*FileSystemName && wcsicmp(FileSystemName, L"NTFS") == 0)
|
|
{
|
|
// WARNING: We cannot write on this FS yet!
|
|
DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
|
|
FileSystemName);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
InferFileSystem(
|
|
IN PCWSTR PartitionPath OPTIONAL,
|
|
IN HANDLE PartitionHandle OPTIONAL,
|
|
IN OUT PWSTR FileSystemName,
|
|
IN SIZE_T FileSystemNameSize)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING PartitionPathU;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
if (PartitionPath && PartitionHandle)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Open the partition if a path has been given;
|
|
* otherwise just use the provided handle. */
|
|
if (PartitionPath)
|
|
{
|
|
RtlInitUnicodeString(&PartitionPathU, PartitionPath);
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&PartitionPathU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenFile(&PartitionHandle,
|
|
FILE_GENERIC_READ /* | SYNCHRONIZE */,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
0 /* FILE_SYNCHRONOUS_IO_NONALERT */);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to open partition '%S', Status 0x%08lx\n",
|
|
PartitionPath, Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Retrieve the FS */
|
|
Status = InferFileSystemWorker(PartitionHandle,
|
|
&IoStatusBlock,
|
|
FileSystemName,
|
|
FileSystemNameSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("InferFileSystem() failed for partition '%S' (0x%p), Status 0x%08lx\n",
|
|
PartitionPath, PartitionHandle, Status);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("InferFileSystem(): FileSystem (guessed): %S\n",
|
|
*FileSystemName ? FileSystemName : L"None");
|
|
}
|
|
|
|
if (PartitionPath)
|
|
{
|
|
/* Close the partition */
|
|
NtClose(PartitionHandle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
UCHAR
|
|
FileSystemToMBRPartitionType(
|
|
IN PCWSTR FileSystem,
|
|
IN ULONGLONG StartSector,
|
|
IN ULONGLONG SectorCount)
|
|
{
|
|
ASSERT(FileSystem);
|
|
|
|
if (SectorCount == 0)
|
|
return PARTITION_ENTRY_UNUSED;
|
|
|
|
if (wcsicmp(FileSystem, L"FAT") == 0 ||
|
|
wcsicmp(FileSystem, L"FAT32") == 0 ||
|
|
wcsicmp(FileSystem, L"RAW") == 0)
|
|
{
|
|
if (SectorCount < 8192ULL)
|
|
{
|
|
/* FAT12 CHS partition (disk is smaller than 4.1MB) */
|
|
return PARTITION_FAT_12;
|
|
}
|
|
else if (StartSector < 1450560ULL)
|
|
{
|
|
/* Partition starts below the 8.4GB boundary ==> CHS partition */
|
|
|
|
if (SectorCount < 65536ULL)
|
|
{
|
|
/* FAT16 CHS partition (partition size < 32MB) */
|
|
return PARTITION_FAT_16;
|
|
}
|
|
else if (SectorCount < 1048576ULL)
|
|
{
|
|
/* FAT16 CHS partition (partition size < 512MB) */
|
|
return PARTITION_HUGE;
|
|
}
|
|
else
|
|
{
|
|
/* FAT32 CHS partition (partition size >= 512MB) */
|
|
return PARTITION_FAT32;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Partition starts above the 8.4GB boundary ==> LBA partition */
|
|
|
|
if (SectorCount < 1048576ULL)
|
|
{
|
|
/* FAT16 LBA partition (partition size < 512MB) */
|
|
return PARTITION_XINT13;
|
|
}
|
|
else
|
|
{
|
|
/* FAT32 LBA partition (partition size >= 512MB) */
|
|
return PARTITION_FAT32_XINT13;
|
|
}
|
|
}
|
|
}
|
|
else if (wcsicmp(FileSystem, L"NTFS") == 0)
|
|
{
|
|
return PARTITION_IFS;
|
|
}
|
|
else if (wcsicmp(FileSystem, L"BTRFS") == 0 ||
|
|
wcsicmp(FileSystem, L"EXT2") == 0 ||
|
|
wcsicmp(FileSystem, L"EXT3") == 0 ||
|
|
wcsicmp(FileSystem, L"EXT4") == 0)
|
|
{
|
|
return PARTITION_LINUX;
|
|
}
|
|
else
|
|
{
|
|
/* Unknown file system */
|
|
DPRINT1("Unknown file system '%S'\n", FileSystem);
|
|
return PARTITION_ENTRY_UNUSED;
|
|
}
|
|
}
|
|
|
|
/* EOF */
|