/* * 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 #include // #include // #include #define NDEBUG #include /* 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 */