From 414bfed6fec287a5efa5aeae2d0726c36d74f43f Mon Sep 17 00:00:00 2001 From: implicitfield <114500360+implicitfield@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:42:49 +0400 Subject: [PATCH] mkfs.fat: Add support for autodetecting FAT type based on file size --- Userland/Utilities/mkfs.fat.cpp | 48 ++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/Userland/Utilities/mkfs.fat.cpp b/Userland/Utilities/mkfs.fat.cpp index 47bacd69f13..21b441f6ac6 100644 --- a/Userland/Utilities/mkfs.fat.cpp +++ b/Userland/Utilities/mkfs.fat.cpp @@ -570,29 +570,69 @@ static ErrorOr format_fat32(Core::File& file, u64 file_size, u32 volume_id return {}; } +static ErrorOr detect_fat_type_from_file_size(u64 file_size) +{ + u32 sector_count = file_size / 512; + + auto check_table_for_support = [sector_count](auto const& table, bool fat12 = false) { + for (auto const& potential_entry : table) { + if (fat12 && sector_count == potential_entry.disk_size) + return true; + else if (!fat12 && sector_count <= potential_entry.disk_size) + return potential_entry.sectors_per_cluster != 0; + } + return false; + }; + + if (check_table_for_support(s_disk_table_fat12, true)) + return 12; + + if (file_size < 512 * MiB && check_table_for_support(s_disk_table_fat16)) + return 16; + + if (check_table_for_support(s_disk_table_fat32)) + return 32; + + return Error::from_string_literal("Unable to autodetect a compatible FAT variant"); +} + +static bool is_valid_fat_type(int fat_type) +{ + return fat_type == 12 || fat_type == 16 || fat_type == 32; +} + ErrorOr serenity_main(Main::Arguments arguments) { StringView file_path; - int fat_type = 0; + Optional fat_type_or_empty; struct timeval time; gettimeofday(&time, NULL); u32 volume_id = static_cast(time.tv_sec) | static_cast(time.tv_usec); Core::ArgsParser args_parser; - args_parser.add_option(fat_type, "FAT type to use, valid types are 12, 16, and 32", "FAT-type", 'F', "FAT type"); + args_parser.add_option(fat_type_or_empty, "FAT type to use, valid types are 12, 16, and 32", "FAT-type", 'F', "FAT type"); args_parser.add_positional_argument(file_path, "File to format", "file", Core::ArgsParser::Required::Yes); args_parser.parse(arguments); auto file = TRY(Core::File::open(file_path, Core::File::OpenMode::ReadWrite | Core::File::OpenMode::DontCreate)); u64 file_size = 0; - if (FileSystem::is_device(file->fd())) file_size = TRY(FileSystem::block_device_size_from_ioctl(file->fd())); else file_size = TRY(FileSystem::size_from_fstat(file->fd())); + int fat_type = 0; + if (fat_type_or_empty.has_value()) { + fat_type = fat_type_or_empty.release_value(); + + if (!is_valid_fat_type(fat_type)) + return Error::from_string_literal("Invalid FAT type specified, valid types are 12, 16, and 32"); + } else { + fat_type = TRY(detect_fat_type_from_file_size(file_size)); + } + switch (fat_type) { case 12: TRY(format_fat_16_bit(*file, FATType::FAT12, file_size, volume_id)); @@ -604,7 +644,7 @@ ErrorOr serenity_main(Main::Arguments arguments) TRY(format_fat32(*file, file_size, volume_id)); break; default: - return Error::from_string_literal("Invalid or no FAT type specified, valid types are 12, 16, and 32"); + VERIFY_NOT_REACHED(); } sync();