/* * PROJECT: ReactOS Setup Library * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Filesystem support functions * COPYRIGHT: Copyright 2003-2018 Casper S. Hornstrup (chorns@users.sourceforge.net) * Copyright 2017-2018 Hermes Belusca-Maito */ // // This is basically the code for listing available FileSystem providers // (currently hardcoded in a list), and for performing a basic FileSystem // recognition for a given disk partition. // // 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 FILE_SYSTEM RegisteredFileSystems[] = { { L"FAT" , VfatFormat, VfatChkdsk }, // { L"FAT32", VfatFormat, VfatChkdsk }, #if 0 { L"FATX" , VfatxFormat, VfatxChkdsk }, { L"NTFS" , NtfsFormat, NtfsChkdsk }, { L"EXT2" , Ext2Format, Ext2Chkdsk }, { L"EXT3" , Ext2Format, Ext2Chkdsk }, { L"EXT4" , Ext2Format, Ext2Chkdsk }, #endif { L"BTRFS", BtrfsFormatEx, BtrfsChkdskEx }, #if 0 { L"FFS" , FfsFormat , FfsChkdsk }, { L"REISERFS", ReiserfsFormat, ReiserfsChkdsk }, #endif }; /* FUNCTIONS ****************************************************************/ PFILE_SYSTEM GetRegisteredFileSystems(OUT PULONG Count) { *Count = ARRAYSIZE(RegisteredFileSystems); return RegisteredFileSystems; } PFILE_SYSTEM GetFileSystemByName( // IN PFILE_SYSTEM_LIST List, 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) return Item; ListEntry = ListEntry->Flink; } #else ULONG Count; PFILE_SYSTEM FileSystems; FileSystems = GetRegisteredFileSystems(&Count); if (!FileSystems || Count == 0) return NULL; while (Count--) { if (FileSystems->FileSystemName && wcsicmp(FileSystemName, FileSystems->FileSystemName) == 0) return FileSystems; ++FileSystems; } #endif return NULL; } // // FileSystem recognition (using NT OS functionality) // #if 0 // FIXME: To be fully enabled when our storage stack & al. will work better! /* NOTE: Ripped & adapted from base/system/autochk/autochk.c */ static NTSTATUS _MyGetFileSystem( IN struct _PARTENTRY* PartEntry, IN OUT PWSTR FileSystemName, IN SIZE_T FileSystemNameSize) { NTSTATUS Status; HANDLE FileHandle; IO_STATUS_BLOCK IoStatusBlock; PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute; UCHAR Buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING PartitionRootPath; WCHAR PathBuffer[MAX_PATH]; FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; /* Set PartitionRootPath */ RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), // L"\\Device\\Harddisk%lu\\Partition%lu", // Should work! But because ReactOS sucks atm. it actually doesn't work!! L"\\Device\\Harddisk%lu\\Partition%lu\\", // HACK: Use this as a temporary hack! PartEntry->DiskEntry->DiskNumber, PartEntry->PartitionNumber); RtlInitUnicodeString(&PartitionRootPath, PathBuffer); DPRINT("PartitionRootPath: %wZ\n", &PartitionRootPath); /* Open the partition */ InitializeObjectAttributes(&ObjectAttributes, &PartitionRootPath, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&FileHandle, // PartitionHandle, FILE_GENERIC_READ /* | SYNCHRONIZE */, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, 0 /* FILE_SYNCHRONOUS_IO_NONALERT */); if (!NT_SUCCESS(Status)) { DPRINT1("Failed to open partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status); return Status; } /* Retrieve the FS attributes */ Status = NtQueryVolumeInformationFile(FileHandle, &IoStatusBlock, FileFsAttribute, sizeof(Buffer), FileFsAttributeInformation); NtClose(FileHandle); if (!NT_SUCCESS(Status)) { DPRINT1("NtQueryVolumeInformationFile failed for partition %wZ, Status 0x%08lx\n", &PartitionRootPath, Status); return Status; } if (FileSystemNameSize * sizeof(WCHAR) < FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) return STATUS_BUFFER_TOO_SMALL; RtlCopyMemory(FileSystemName, FileFsAttribute->FileSystemName, FileFsAttribute->FileSystemNameLength); FileSystemName[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = UNICODE_NULL; return STATUS_SUCCESS; } #endif PFILE_SYSTEM GetFileSystem( // IN PFILE_SYSTEM_LIST FileSystemList, IN struct _PARTENTRY* PartEntry) { PFILE_SYSTEM CurrentFileSystem; PWSTR FileSystemName = NULL; #if 0 // For code temporarily disabled below NTSTATUS Status; WCHAR FsRecFileSystemName[MAX_PATH]; #endif CurrentFileSystem = PartEntry->FileSystem; /* We have a file system, return it */ if (CurrentFileSystem != NULL && CurrentFileSystem->FileSystemName != NULL) return CurrentFileSystem; DPRINT1("File system not found, try to guess one...\n"); CurrentFileSystem = NULL; #if 0 // This is an example of old code... if ((PartEntry->PartitionType == PARTITION_FAT_12) || (PartEntry->PartitionType == PARTITION_FAT_16) || (PartEntry->PartitionType == PARTITION_HUGE) || (PartEntry->PartitionType == PARTITION_XINT13) || (PartEntry->PartitionType == PARTITION_FAT32) || (PartEntry->PartitionType == PARTITION_FAT32_XINT13)) { if (CheckFatFormat()) FileSystemName = L"FAT"; else FileSystemName = NULL; } else if (PartEntry->PartitionType == PARTITION_EXT2) { if (CheckExt2Format()) FileSystemName = L"EXT2"; else FileSystemName = NULL; } else if (PartEntry->PartitionType == PARTITION_IFS) { if (CheckNtfsFormat()) FileSystemName = L"NTFS"; else if (CheckHpfsFormat()) FileSystemName = L"HPFS"; else FileSystemName = NULL; } else { FileSystemName = NULL; } #endif #if 0 // FIXME: To be fully enabled when our storage stack & al. work better! /* * We don't have one... * * Try to infer one using NT file system recognition. */ Status = _MyGetFileSystem(PartEntry, FsRecFileSystemName, ARRAYSIZE(FsRecFileSystemName)); if (NT_SUCCESS(Status) && *FsRecFileSystemName) { /* Temporary HACK: map FAT32 back to FAT */ if (wcscmp(FsRecFileSystemName, L"FAT32") == 0) wcscpy(FsRecFileSystemName, L"FAT"); FileSystemName = FsRecFileSystemName; goto Quit; } #endif /* * We don't have one... * * 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 ((PartEntry->PartitionType == PARTITION_FAT_12) || (PartEntry->PartitionType == PARTITION_FAT_16) || (PartEntry->PartitionType == PARTITION_HUGE ) || (PartEntry->PartitionType == PARTITION_XINT13) || (PartEntry->PartitionType == PARTITION_FAT32 ) || (PartEntry->PartitionType == PARTITION_FAT32_XINT13)) { FileSystemName = L"FAT"; } else if (PartEntry->PartitionType == PARTITION_LINUX) { // WARNING: See the warning above. FileSystemName = L"BTRFS"; } else if (PartEntry->PartitionType == PARTITION_IFS) { // WARNING: See the warning above. FileSystemName = L"NTFS"; /* FIXME: Not quite correct! */ // FIXME: We may have HPFS too... } #if 0 Quit: // For code temporarily disabled above #endif // HACK: WARNING: We cannot write on this FS yet! if (FileSystemName) { if (PartEntry->PartitionType == PARTITION_IFS) DPRINT1("Recognized file system %S that doesn't support write support yet!\n", FileSystemName); } DPRINT1("GetFileSystem -- PartitionType: 0x%02X ; FileSystemName (guessed): %S\n", PartEntry->PartitionType, FileSystemName ? FileSystemName : L"None"); if (FileSystemName != NULL) CurrentFileSystem = GetFileSystemByName(FileSystemName); return CurrentFileSystem; } // // Formatting routines // BOOLEAN PreparePartitionForFormatting( IN struct _PARTENTRY* PartEntry, IN PFILE_SYSTEM FileSystem) { if (!FileSystem) { DPRINT1("No file system specified?\n"); return FALSE; } if (wcscmp(FileSystem->FileSystemName, L"FAT") == 0) { if (PartEntry->SectorCount.QuadPart < 8192) { /* FAT12 CHS partition (disk is smaller than 4.1MB) */ SetPartitionType(PartEntry, PARTITION_FAT_12); } else if (PartEntry->StartSector.QuadPart < 1450560) { /* Partition starts below the 8.4GB boundary ==> CHS partition */ if (PartEntry->SectorCount.QuadPart < 65536) { /* FAT16 CHS partition (partition size < 32MB) */ SetPartitionType(PartEntry, PARTITION_FAT_16); } else if (PartEntry->SectorCount.QuadPart < 1048576) { /* FAT16 CHS partition (partition size < 512MB) */ SetPartitionType(PartEntry, PARTITION_HUGE); } else { /* FAT32 CHS partition (partition size >= 512MB) */ SetPartitionType(PartEntry, PARTITION_FAT32); } } else { /* Partition starts above the 8.4GB boundary ==> LBA partition */ if (PartEntry->SectorCount.QuadPart < 1048576) { /* FAT16 LBA partition (partition size < 512MB) */ SetPartitionType(PartEntry, PARTITION_XINT13); } else { /* FAT32 LBA partition (partition size >= 512MB) */ SetPartitionType(PartEntry, PARTITION_FAT32_XINT13); } } } else if (wcscmp(FileSystem->FileSystemName, L"BTRFS") == 0) { SetPartitionType(PartEntry, PARTITION_LINUX); } #if 0 else if (wcscmp(FileSystem->FileSystemName, L"EXT2") == 0) { SetPartitionType(PartEntry, PARTITION_EXT2); } else if (wcscmp(FileSystem->FileSystemName, L"NTFS") == 0) { SetPartitionType(PartEntry, PARTITION_IFS); } #endif else { /* Unknown file system? */ DPRINT1("Unknown file system \"%S\"?\n", FileSystem->FileSystemName); return FALSE; } // // FIXME: Do this now, or after the partition was actually formatted?? // /* Set the new partition's file system proper */ PartEntry->FormatState = Formatted; // Well... This may be set after the real formatting takes place (in which case we should change the FormatState to another value) PartEntry->FileSystem = FileSystem; return TRUE; } /* EOF */