mirror of
https://github.com/reactos/reactos.git
synced 2024-09-11 13:23:51 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
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.
572 lines
16 KiB
C
572 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 */
|