reactos/base/setup/lib/fsutil.c
Hermès Bélusca-Maïto c1fbc2d651
[SETUPLIB][USETUP] Factor out the usage of FS provider structures.
Similarly to FMIFS this structure should be private. Instead file-system
names are passed to the helper functions, allowing to use the names
returned by the FS drivers. The names are then internally mapped to the
corresponding FS providers.

In particular this allows to handle the "RAW" file-system and to assign
the 'Unformatted' flag to partitions having this FS.

Finally this helps us refining the checks performed to see whether the
current "active" system partition uses a supported file-system.
2019-03-10 15:41:05 +01:00

571 lines
16 KiB
C

/*
* PROJECT: ReactOS Setup Library
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Filesystem support functions
* COPYRIGHT: Copyright 2003-2019 Casper S. Hornstrup (chorns@users.sourceforge.net)
* Copyright 2017-2019 Hermes Belusca-Maito
*/
//
// See also: https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/dll/win32/fmifs/init.c;h=e895f5ef9cae4806123f6bbdd3dfed37ec1c8d33;hb=b9db9a4e377a2055f635b2fb69fef4e1750d219c
// for how to get FS providers in a dynamic way. In the (near) future we may
// consider merging some of this code with us into a fmifs / fsutil / fslib library...
//
/* INCLUDES *****************************************************************/
#include "precomp.h"
#include "fsutil.h"
#include "partlist.h"
#include <fslib/vfatlib.h>
#include <fslib/btrfslib.h>
// #include <fslib/ext2lib.h>
// #include <fslib/ntfslib.h>
#define NDEBUG
#include <debug.h>
/* LOCALS *******************************************************************/
/** IFS_PROVIDER **/
typedef struct _FILE_SYSTEM
{
PCWSTR FileSystemName;
FORMATEX FormatFunc;
CHKDSKEX ChkdskFunc;
} FILE_SYSTEM, *PFILE_SYSTEM;
/* The list of file systems on which we can install ReactOS */
static FILE_SYSTEM RegisteredFileSystems[] =
{
/* NOTE: The FAT formatter automatically determines
* whether it will use FAT-16 or FAT-32. */
{ L"FAT" , VfatFormat, VfatChkdsk },
#if 0
{ L"FAT32", VfatFormat, VfatChkdsk }, // Do we support specific FAT sub-formats specifications?
{ L"FATX" , VfatxFormat, VfatxChkdsk },
{ L"NTFS" , NtfsFormat, NtfsChkdsk },
#endif
{ L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx },
#if 0
{ L"EXT2" , Ext2Format, Ext2Chkdsk },
{ L"EXT3" , Ext2Format, Ext2Chkdsk },
{ L"EXT4" , Ext2Format, Ext2Chkdsk },
{ L"FFS" , FfsFormat , FfsChkdsk },
{ L"REISERFS", ReiserfsFormat, ReiserfsChkdsk },
#endif
};
/* FUNCTIONS ****************************************************************/
/** QueryAvailableFileSystemFormat() **/
BOOLEAN
GetRegisteredFileSystems(
IN ULONG Index,
OUT PCWSTR* FileSystemName)
{
if (Index >= ARRAYSIZE(RegisteredFileSystems))
return FALSE;
*FileSystemName = RegisteredFileSystems[Index].FileSystemName;
return TRUE;
}
/** GetProvider() **/
static PFILE_SYSTEM
GetFileSystemByName(
IN PCWSTR FileSystemName)
{
#if 0 // Reenable when the list of registered FSes will again be dynamic
PLIST_ENTRY ListEntry;
PFILE_SYSTEM_ITEM Item;
ListEntry = List->ListHead.Flink;
while (ListEntry != &List->ListHead)
{
Item = CONTAINING_RECORD(ListEntry, FILE_SYSTEM_ITEM, ListEntry);
if (Item->FileSystemName &&
(wcsicmp(FileSystemName, Item->FileSystemName) == 0 ||
/* Map FAT32 back to FAT */
(wcsicmp(FileSystemName, L"FAT32") == 0 && wcsicmp(Item->FileSystemName, L"FAT") == 0)))
{
return Item;
}
ListEntry = ListEntry->Flink;
}
#else
ULONG Count = ARRAYSIZE(RegisteredFileSystems);
PFILE_SYSTEM FileSystems = RegisteredFileSystems;
ASSERT(FileSystems && Count != 0);
while (Count--)
{
if (FileSystems->FileSystemName &&
(wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0 ||
/* Map FAT32 back to FAT */
(wcsicmp(FileSystemName, L"FAT32") == 0 && wcsicmp(FileSystems->FileSystemName, L"FAT") == 0)))
{
return FileSystems;
}
++FileSystems;
}
#endif
return NULL;
}
//
// FileSystem recognition, using NT OS functionality
//
/* NOTE: Ripped & adapted from base/system/autochk/autochk.c */
NTSTATUS
GetFileSystemNameByHandle(
IN HANDLE PartitionHandle,
IN OUT PWSTR FileSystemName,
IN SIZE_T FileSystemNameSize)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
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,
IN OUT PWSTR FileSystemName,
IN SIZE_T FileSystemNameSize)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE PartitionHandle;
IO_STATUS_BLOCK IoStatusBlock;
/* Open the partition */
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 = GetFileSystemNameByHandle(PartitionHandle, FileSystemName, FileSystemNameSize);
if (!NT_SUCCESS(Status))
{
DPRINT1("GetFileSystemNameByHandle() failed for partition '%wZ', Status 0x%08lx\n",
PartitionPath, Status);
}
/* Close the partition */
NtClose(PartitionHandle);
return Status;
}
NTSTATUS
GetFileSystemName(
IN PCWSTR Partition,
IN OUT PWSTR FileSystemName,
IN SIZE_T FileSystemNameSize)
{
UNICODE_STRING PartitionPath;
RtlInitUnicodeString(&PartitionPath, Partition);
return GetFileSystemName_UStr(&PartitionPath,
FileSystemName,
FileSystemNameSize);
}
NTSTATUS
InferFileSystemByHandle(
IN HANDLE PartitionHandle,
IN UCHAR PartitionType,
IN OUT PWSTR FileSystemName,
IN SIZE_T FileSystemNameSize)
{
NTSTATUS Status;
if (FileSystemNameSize < sizeof(WCHAR))
return STATUS_BUFFER_TOO_SMALL;
*FileSystemName = L'\0';
/* Try to infer a file system using NT file system recognition */
Status = GetFileSystemNameByHandle(PartitionHandle,
FileSystemName,
FileSystemNameSize);
if (NT_SUCCESS(Status) && *FileSystemName)
{
goto Quit;
}
/*
* Try to infer a preferred file system for this partition, given its ID.
*
* WARNING: This is partly a hack, since partitions with the same ID 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 ID 0x83.
*
* The proper fix is to make a function that detects the existing FS
* from a given partition (not based on the partition ID).
* On the contrary, for unformatted partitions with a given ID, 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)
{
// WARNING: We cannot write on this FS yet!
if (PartitionType == PARTITION_IFS)
{
DPRINT1("Recognized file system '%S' that doesn't have write support yet!\n",
FileSystemName);
}
}
DPRINT1("InferFileSystem -- PartitionType: 0x%02X ; FileSystem (guessed): %S\n",
PartitionType, *FileSystemName ? FileSystemName : L"None");
return Status;
}
NTSTATUS
InferFileSystem(
IN PCWSTR Partition,
IN UCHAR PartitionType,
IN OUT PWSTR FileSystemName,
IN SIZE_T FileSystemNameSize)
{
NTSTATUS Status;
UNICODE_STRING PartitionPath;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE PartitionHandle;
IO_STATUS_BLOCK IoStatusBlock;
/* Open the partition */
RtlInitUnicodeString(&PartitionPath, Partition);
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 */
Status = InferFileSystemByHandle(PartitionHandle,
PartitionType,
FileSystemName,
FileSystemNameSize);
/* Close the partition */
NtClose(PartitionHandle);
return Status;
}
/** ChkdskEx() **/
NTSTATUS
ChkdskFileSystem_UStr(
IN PUNICODE_STRING DriveRoot,
IN PCWSTR FileSystemName,
IN BOOLEAN FixErrors,
IN BOOLEAN Verbose,
IN BOOLEAN CheckOnlyIfDirty,
IN BOOLEAN ScanDrive,
IN PFMIFSCALLBACK Callback)
{
PFILE_SYSTEM FileSystem;
FileSystem = GetFileSystemByName(FileSystemName);
if (!FileSystem || !FileSystem->ChkdskFunc)
{
// BOOLEAN Argument = FALSE;
// Callback(DONE, 0, &Argument);
return STATUS_NOT_SUPPORTED;
}
return FileSystem->ChkdskFunc(DriveRoot,
FixErrors,
Verbose,
CheckOnlyIfDirty,
ScanDrive,
Callback);
}
NTSTATUS
ChkdskFileSystem(
IN PCWSTR DriveRoot,
IN PCWSTR FileSystemName,
IN BOOLEAN FixErrors,
IN BOOLEAN Verbose,
IN BOOLEAN CheckOnlyIfDirty,
IN BOOLEAN ScanDrive,
IN PFMIFSCALLBACK Callback)
{
UNICODE_STRING DriveRootU;
RtlInitUnicodeString(&DriveRootU, DriveRoot);
return ChkdskFileSystem_UStr(&DriveRootU,
FileSystemName,
FixErrors,
Verbose,
CheckOnlyIfDirty,
ScanDrive,
Callback);
}
/** FormatEx() **/
NTSTATUS
FormatFileSystem_UStr(
IN PUNICODE_STRING DriveRoot,
IN PCWSTR FileSystemName,
IN FMIFS_MEDIA_FLAG MediaFlag,
IN PUNICODE_STRING Label,
IN BOOLEAN QuickFormat,
IN ULONG ClusterSize,
IN PFMIFSCALLBACK Callback)
{
PFILE_SYSTEM FileSystem;
FileSystem = GetFileSystemByName(FileSystemName);
if (!FileSystem || !FileSystem->FormatFunc)
{
// BOOLEAN Argument = FALSE;
// Callback(DONE, 0, &Argument);
return STATUS_NOT_SUPPORTED;
}
return FileSystem->FormatFunc(DriveRoot,
MediaFlag,
Label,
QuickFormat,
ClusterSize,
Callback);
}
NTSTATUS
FormatFileSystem(
IN PCWSTR DriveRoot,
IN PCWSTR FileSystemName,
IN FMIFS_MEDIA_FLAG MediaFlag,
IN PCWSTR Label,
IN BOOLEAN QuickFormat,
IN ULONG ClusterSize,
IN PFMIFSCALLBACK Callback)
{
UNICODE_STRING DriveRootU;
UNICODE_STRING LabelU;
RtlInitUnicodeString(&DriveRootU, DriveRoot);
RtlInitUnicodeString(&LabelU, Label);
return FormatFileSystem_UStr(&DriveRootU,
FileSystemName,
MediaFlag,
&LabelU,
QuickFormat,
ClusterSize,
Callback);
}
UCHAR
FileSystemToPartitionType(
IN PCWSTR FileSystem,
IN PULARGE_INTEGER StartSector,
IN PULARGE_INTEGER SectorCount)
{
ASSERT(FileSystem && StartSector && SectorCount);
if (wcsicmp(FileSystem, L"FAT") == 0 ||
wcsicmp(FileSystem, L"FAT32") == 0 ||
wcsicmp(FileSystem, L"RAW") == 0)
{
if (SectorCount->QuadPart < 8192)
{
/* FAT12 CHS partition (disk is smaller than 4.1MB) */
return PARTITION_FAT_12;
}
else if (StartSector->QuadPart < 1450560)
{
/* Partition starts below the 8.4GB boundary ==> CHS partition */
if (SectorCount->QuadPart < 65536)
{
/* FAT16 CHS partition (partition size < 32MB) */
return PARTITION_FAT_16;
}
else if (SectorCount->QuadPart < 1048576)
{
/* 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->QuadPart < 1048576)
{
/* 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 ||
wcsicmp(FileSystem, L"FFS") == 0 ||
wcsicmp(FileSystem, L"REISERFS") == 0)
{
return PARTITION_LINUX;
}
else
{
/* Unknown file system */
DPRINT1("Unknown file system '%S'\n", FileSystem);
return PARTITION_ENTRY_UNUSED;
}
}
//
// Formatting routines
//
BOOLEAN
PreparePartitionForFormatting(
IN struct _PARTENTRY* PartEntry,
IN PCWSTR FileSystemName)
{
UCHAR PartitionType;
if (!FileSystemName || !*FileSystemName)
{
DPRINT1("No file system specified?\n");
return FALSE;
}
PartitionType = FileSystemToPartitionType(FileSystemName,
&PartEntry->StartSector,
&PartEntry->SectorCount);
if (PartitionType == PARTITION_ENTRY_UNUSED)
{
/* Unknown file system */
DPRINT1("Unknown file system '%S'\n", FileSystemName);
return FALSE;
}
SetPartitionType(PartEntry, PartitionType);
//
// FIXME: Do this now, or after the partition was actually formatted??
//
/* Set the new partition's file system proper */
RtlStringCbCopyW(PartEntry->FileSystem,
sizeof(PartEntry->FileSystem),
FileSystemName);
return TRUE;
}
/* EOF */