reactos/base/setup/lib/fsutil.c

572 lines
16 KiB
C
Raw Normal View History

/*
* 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 */