/* * Copyright (c) 2022-2024, Undefine * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace Kernel { DOSBIOSParameterBlockVersion DOSBIOSParameterBlock::bpb_version() const { bool dos3_valid = m_dos4_block->signature == 0x28; bool dos4_valid = m_dos4_block->signature == 0x29; bool dos7_valid = m_dos7_block->signature == 0x28 || m_dos7_block->signature == 0x29; // A DOS 7 EBPB should _never_ contain the values 0x28 or 0x29 at // the offset associated with `m_dos4_block->signature` // (aka `m_dos7_block->sectors_per_fat_32bit`) due to the maximum number of // clusters ensuring the number of sectors per fat will not exceed 0x200000. // As a result, it should be safe to determine BPB version through the // signature fields by checking the DOS 4 signature offset prior to the DOS 7 one. // // With a DOS 3 or DOS 4 EBPB, the DOS 7 signature offset references uninitialized // space. While unlikely to be set to a valid signature value, it is not implausible. // We warn the user here, but because it does not represent an invalid FS configuration, // do not error. if ((dos3_valid || dos4_valid) && dos7_valid) dbgln("FATFS: DOS 4 and DOS 7 EBPB signatures detected, EBPB/FAT version detection may be incorrect."); if (dos3_valid) return DOS_BPB_3; else if (dos4_valid) return DOS_BPB_4; else if (dos7_valid) return DOS_BPB_7; else return DOS_BPB_UNKNOWN; } DOS3BIOSParameterBlock const* DOSBIOSParameterBlock::common_bpb() const { return m_common_block; } DOS4BIOSParameterBlock const* DOSBIOSParameterBlock::dos4_bpb() const { // Only return parameter block if signature indicates this portion of the block is filled out. if (m_dos4_block->signature == 0x28 || m_dos4_block->signature == 0x29) return m_dos4_block; else return nullptr; } DOS7BIOSParameterBlock const* DOSBIOSParameterBlock::dos7_bpb() const { // Only return parameter block if signature indicates this portion of the block is filled out. if (m_dos7_block->signature == 0x28 || m_dos7_block->signature == 0x29) return m_dos7_block; else return nullptr; } u16 DOSBIOSParameterBlock::sectors_per_fat() const { return common_bpb()->sectors_per_fat_16bit != 0 ? common_bpb()->sectors_per_fat_16bit : m_dos7_block->sectors_per_fat_32bit; } u32 DOSBIOSParameterBlock::sector_count() const { if (common_bpb()->sector_count_16bit != 0) { // The `16bit` field is only used on partitions smaller than 32 MB, // and never for FAT32. // It is set to `0` when the 32 bit field contains the sector count. return common_bpb()->sector_count_16bit; } else { return common_bpb()->sector_count_32bit; // FIXME: If this is 0 for a FAT32 EBPB with a signature of 0x29, // read 0x052, which is a 64-bit wide sector count. } } u8 DOSBIOSParameterBlock::signature() const { if (bpb_version() == DOS_BPB_3 || bpb_version() == DOS_BPB_4) return m_dos4_block->signature; else return m_dos7_block->signature; } ErrorOr> FATFS::try_create(OpenFileDescription& file_description, ReadonlyBytes) { return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FATFS(file_description))); } FATFS::FATFS(OpenFileDescription& file_description) : BlockBasedFileSystem(file_description) { } bool FATFS::is_initialized_while_locked() { VERIFY(m_lock.is_locked()); return !m_root_inode.is_null(); } ErrorOr FATFS::initialize_while_locked() { VERIFY(m_lock.is_locked()); VERIFY(!is_initialized_while_locked()); m_boot_record = TRY(KBuffer::try_create_with_size("FATFS: Boot Record"sv, m_device_block_size)); auto boot_record_buffer = UserOrKernelBuffer::for_kernel_buffer(m_boot_record->data()); TRY(raw_read(0, boot_record_buffer)); m_parameter_block = TRY(adopt_nonnull_own_or_enomem(new (nothrow) DOSBIOSParameterBlock(m_boot_record))); // Alias for extended BPB. DOSBIOSParameterBlock& ebpb = *m_parameter_block; // Alias for block of common parameters in BPB. DOS3BIOSParameterBlock const* block = ebpb.common_bpb(); if constexpr (FAT_DEBUG) { dbgln("FATFS: oem_identifier: {}", block->oem_identifier); dbgln("FATFS: bytes_per_sector: {}", static_cast(block->bytes_per_sector)); dbgln("FATFS: sectors_per_cluster: {}", block->sectors_per_cluster); dbgln("FATFS: reserved_sector_count: {}", block->reserved_sector_count); dbgln("FATFS: fat_count: {}", block->fat_count); dbgln("FATFS: root_directory_entry_count: {}", static_cast(block->root_directory_entry_count)); dbgln("FATFS: media_descriptor_type: {}", block->media_descriptor_type); dbgln("FATFS: sectors_per_track: {}", block->sectors_per_track); dbgln("FATFS: head_count: {}", block->head_count); dbgln("FATFS: hidden_sector_count: {}", block->hidden_sector_count); dbgln("FATFS: sector_count: {}", ebpb.sector_count()); dbgln("FATFS: sectors_per_fat: {}", ebpb.sectors_per_fat()); auto ebpb_version = ebpb.bpb_version(); if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_7) { DOS7BIOSParameterBlock const* dos7_boot_record = ebpb.dos7_bpb(); dbgln("FATFS: EBPB: DOS 7"); dbgln("FATFS: flags: {}", dos7_boot_record->flags); dbgln("FATFS: fat_version: {}", dos7_boot_record->fat_version); dbgln("FATFS: root_directory_cluster: {}", dos7_boot_record->root_directory_cluster); dbgln("FATFS: fs_info_sector: {}", dos7_boot_record->fs_info_sector); dbgln("FATFS: backup_boot_sector: {}", dos7_boot_record->backup_boot_sector); dbgln("FATFS: drive_number: {}", dos7_boot_record->drive_number); dbgln("FATFS: volume_id: {}", static_cast(dos7_boot_record->volume_id)); } else if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_3 || ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_4) { DOS4BIOSParameterBlock const* dos4_boot_record = ebpb.dos4_bpb(); if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_3) { dbgln("FATFS: EBPB: DOS 3.4"); } else if (ebpb_version == DOSBIOSParameterBlockVersion::DOS_BPB_4) { dbgln("FATFS: EBPB: DOS 4"); } dbgln("FATFS: drive_number: {}", dos4_boot_record->drive_number); dbgln("FATFS: flags: {}", dos4_boot_record->flags); dbgln("FATFS: volume_id: {}", static_cast(dos4_boot_record->volume_id)); // volume_label_string and file_system_type are only valid when // ebpb_version == DOSBIOSParameterBlockVersion::DOS4. } } if (ebpb.signature() != signature_1 && ebpb.signature() != signature_2) { dbgln("FATFS: Invalid signature"); return EINVAL; } DOS3BIOSParameterBlock const* ebpb_block = ebpb.common_bpb(); // The number of data area sectors is what DOS/Windows used to determine // if a partition was a FAT12, FAT16, or FAT32 file system. // From "FAT Type Determination" section of Microsoft FAT Specification // (fatgen103.doc): // The FAT type—one of FAT12, FAT16, or FAT32—is determined by the count // of clusters on the volume and nothing else. // // The following calculations are based on the equations provided in this // section. // "RootDirSectors" from MS FAT Specification. This is calculated as: // Number of bytes occupied by root directory area (0 on FAT32) // + // Bytes to fill final sector (ie, round up) // Converted into sector count (by dividing by bytes per sector). u32 root_directory_sectors = ((ebpb_block->root_directory_entry_count * sizeof(FATEntry)) + (ebpb_block->bytes_per_sector - 1)) / ebpb_block->bytes_per_sector; // "DataSec" from MS FAT Specification. u32 data_area_sectors = ebpb.sector_count() - ((ebpb_block->reserved_sector_count) + (ebpb_block->fat_count * ebpb.sectors_per_fat()) + root_directory_sectors); // CountofClusters from MS FAT Specification. u32 data_area_clusters = data_area_sectors / ebpb_block->sectors_per_cluster; // Cluster thresholds and operators as defined in MS FAT Specification. if (data_area_clusters < 4085) { dbgln("FATFS: Detected FAT12 with {} data area clusters", data_area_clusters); m_fat_version = FATVersion::FAT12; } else if (data_area_clusters < 65525) { dbgln("FATFS: Detected FAT16 with {} data area clusters", data_area_clusters); m_fat_version = FATVersion::FAT16; } else { dbgln("FATFS: Assuming FAT32 with {} data area clusters", data_area_clusters); m_fat_version = FATVersion::FAT32; } m_device_block_size = ebpb_block->bytes_per_sector; set_logical_block_size(m_device_block_size); m_first_data_sector = block->reserved_sector_count + (block->fat_count * ebpb.sectors_per_fat()) + root_directory_sectors; TRY(BlockBasedFileSystem::initialize_while_locked()); FATEntry root_entry {}; if (m_fat_version == FATVersion::FAT32) { // FAT32 stores the root directory within the FAT (at the clusters specified // in the boot record), as opposed to the root directory area // (as done by FAT 12/16). DOS7BIOSParameterBlock const* boot_record = ebpb.dos7_bpb(); // Ensure we have a DOS7 BPB (so that we can find the root directory cluster). if (boot_record == nullptr) { dbgln("FATFS: Non-DOS7 BPB for FAT32 FS."); return EINVAL; } root_entry.first_cluster_low = boot_record->root_directory_cluster & 0xFFFF; root_entry.first_cluster_high = boot_record->root_directory_cluster >> 16; } else { // FAT12/FAT16. // Use cluster = 0 as a signal to `first_block_of_cluster()` to look in the // root directory area for the root entry. // Clusters 0 and 1 hold special values, and will never be used to store file // data. root_entry.first_cluster_low = 0; root_entry.first_cluster_high = 0; } root_entry.attributes = FATAttributes::Directory; m_root_inode = TRY(FATInode::create(*this, root_entry, { 0, 1 })); if (m_fat_version == FATVersion::FAT32) { auto fs_info_buffer = UserOrKernelBuffer::for_kernel_buffer(bit_cast(&m_fs_info)); // We know that there is a DOS7 BPB, because if it wasn't present // we would have returned EINVAL above. TRY(read_block(ebpb.dos7_bpb()->fs_info_sector, &fs_info_buffer, sizeof(m_fs_info))); if (m_fs_info.lead_signature != fs_info_signature_1 || m_fs_info.struct_signature != fs_info_signature_2 || m_fs_info.trailing_signature != fs_info_signature_3) { dbgln("FATFS: Invalid FSInfo struct signature"); dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature1: {:#x}, expected: {:#x}", m_fs_info.lead_signature, fs_info_signature_1); dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature2: {:#x}, expected: {:#x}", m_fs_info.struct_signature, fs_info_signature_2); dbgln_if(FAT_DEBUG, "FATFS: FSInfo signature3: {:#x}, expected: {:#x}", m_fs_info.trailing_signature, fs_info_signature_3); return Error::from_errno(EINVAL); } dbgln_if(FAT_DEBUG, "FATFS: fs_info.last_known_free_cluster_count: {}", m_fs_info.last_known_free_cluster_count); dbgln_if(FAT_DEBUG, "FATFS: fs_info.next_free_cluster_hint: {}", m_fs_info.next_free_cluster_hint); } return {}; } Inode& FATFS::root_inode() { return *m_root_inode; } FatBlockSpan FATFS::first_block_of_cluster(u32 cluster) const { // For FAT12/16, we use a value of cluster 0 to indicate this is a cluster for the root directory. // Cluster 0 and cluster 1 hold special values (cluster 0 holds the FAT ID, and cluster 1 // the "end of chain marker"), neither of which will be present in the table or associated // with any file. // "Entries with the Volume Label flag, subdirectory ".." pointing to the FAT12 and FAT16 root, and empty files with size 0 should have first cluster 0." // --Wikipedia // DOSBIOSParameterBlock ebpb(m_boot_record); DOS3BIOSParameterBlock const* ebpb_block = ebpb.common_bpb(); if (m_fat_version != FATVersion::FAT32 && cluster == 0) { // Root directory area follows the FATs after the reserved sectors. return FatBlockSpan { ebpb_block->reserved_sector_count + (ebpb_block->fat_count * ebpb.sectors_per_fat()), (ebpb_block->root_directory_entry_count * sizeof(FATEntry)) / ebpb_block->bytes_per_sector }; } else { return FatBlockSpan { ((cluster - first_data_cluster) * ebpb_block->sectors_per_cluster) + m_first_data_sector, ebpb_block->sectors_per_cluster }; } } size_t FATFS::fat_offset_for_cluster(u32 cluster) const { switch (m_fat_version) { case FATVersion::FAT12: { // In FAT12, a cluster entry is stored in a byte, plus // the low/high nibble of an adjacent byte. // // CLSTR: 0 1 2 3 4 5 // INDEX: [0 1 2], [3 4 5], [6 7 8] // Every 2 clusters are represented using 3 bytes. return (cluster * 3) / 2; } break; case FATVersion::FAT16: return cluster * 2; // Each cluster is stored in 2 bytes. case FATVersion::FAT32: return cluster * 4; // Each cluster is stored in 4 bytes. default: VERIFY_NOT_REACHED(); } } u32 FATFS::cluster_number(KBuffer const& fat_sector, u32 entry_cluster_number, u32 entry_offset) const { u32 cluster = 0; switch (m_fat_version) { case FATVersion::FAT12: { u16 fat12_bytes_le = 0; // Two FAT12 entries get stored in a total of 3 bytes, as follows: // AB CD EF are grouped as [D AB] and [E FC] (little-endian). // For a given cluster, we interpret the associated 2 bytes as a little-endian // 16-bit value ({CD AB} or {EF CD}), and then shift/mask the extra high or low nibble. ByteReader::load(fat_sector.bytes().offset(entry_offset), fat12_bytes_le); cluster = AK::convert_between_host_and_little_endian(fat12_bytes_le); if (entry_cluster_number % 2 == 0) { // CD AB -> D AB cluster &= 0x0FFF; } else { // EF CD -> E FC. cluster = cluster >> 4; } break; } case FATVersion::FAT16: { u16 cluster_u16_le = 0; ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u16_le); cluster = AK::convert_between_host_and_little_endian(cluster_u16_le); break; } case FATVersion::FAT32: { u32 cluster_u32_le = 0; ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u32_le); cluster = AK::convert_between_host_and_little_endian(cluster_u32_le); // FAT32 entries use 28-bits to represent the cluster number. The top 4 bits // may contain flags or other data and must be masked off. cluster &= 0x0FFFFFFF; break; } default: VERIFY_NOT_REACHED(); } return cluster; } u32 FATFS::end_of_chain_marker() const { // Returns the end of chain entry for the given file system. // Any FAT entry of this value or greater signifies the end // of the chain has been reached for a given entry. switch (m_fat_version) { case FATVersion::FAT12: return 0xFF8; case FATVersion::FAT16: return 0xFFF8; case FATVersion::FAT32: return 0x0FFFFFF8; default: VERIFY_NOT_REACHED(); } } ErrorOr FATFS::set_free_cluster_count(u32 value) { VERIFY(m_fat_version == FATVersion::FAT32); m_fs_info.last_known_free_cluster_count = value; auto fs_info_buffer = UserOrKernelBuffer::for_kernel_buffer(bit_cast(&m_fs_info)); TRY(write_block(m_parameter_block->dos7_bpb()->fs_info_sector, fs_info_buffer, sizeof(m_fs_info))); return {}; } ErrorOr FATFS::allocate_cluster() { u32 start_cluster; if (m_fat_version == FATVersion::FAT32) { // If we have a hint, start there. if (m_fs_info.next_free_cluster_hint != fs_info_data_unknown) { start_cluster = m_fs_info.next_free_cluster_hint; } else { // Otherwise, start at the beginning of the data area. start_cluster = first_data_cluster; } } else { // For FAT12/16, start at the beginning of the data area, as there is no // FSInfo struct to store the hint. start_cluster = first_data_cluster; } MutexLocker locker(m_lock); for (u32 i = start_cluster; i < m_parameter_block->sector_count() / m_parameter_block->common_bpb()->sectors_per_cluster; i++) { if (TRY(fat_read(i)) == 0) { dbgln_if(FAT_DEBUG, "FATFS: Allocating cluster {}", i); if (m_fat_version == FATVersion::FAT32 && m_fs_info.last_known_free_cluster_count != fs_info_data_unknown) TRY(set_free_cluster_count(m_fs_info.last_known_free_cluster_count - 1)); TRY(fat_write(i, end_of_chain_marker())); return i; } } return Error::from_errno(ENOSPC); } ErrorOr FATFS::notify_cluster_freed() { if (m_fat_version == FATVersion::FAT32 && m_fs_info.last_known_free_cluster_count != fs_info_data_unknown) TRY(set_free_cluster_count(m_fs_info.last_known_free_cluster_count + 1)); return {}; } ErrorOr FATFS::fat_read(u32 cluster) { dbgln_if(FAT_DEBUG, "FATFS: Reading FAT entry for cluster {}", cluster); u32 fat_offset = fat_offset_for_cluster(cluster); u32 fat_sector_index = m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / m_device_block_size); u32 entry_offset = fat_offset % m_device_block_size; // NOTE: On FAT12, FATs aren't necessarily block aligned, so in the worst case we have to read // an extra byte from the next block. bool read_extra_block = m_fat_version == FATVersion::FAT12 && entry_offset == m_device_block_size - 1; size_t buffer_size = m_device_block_size; if (read_extra_block) buffer_size += m_device_block_size; auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, buffer_size)); auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); MutexLocker locker(m_lock); if (read_extra_block) TRY(read_blocks(fat_sector_index, 2, fat_sector_buffer)); else TRY(read_block(fat_sector_index, &fat_sector_buffer, m_device_block_size)); // Look up the next cluster to read, or read End of Chain marker from table. return cluster_number(*fat_sector, cluster, entry_offset); } ErrorOr FATFS::fat_write(u32 cluster, u32 value) { dbgln_if(FAT_DEBUG, "FATFS: Writing FAT entry for cluster {} with value {}", cluster, value); u32 fat_offset = fat_offset_for_cluster(cluster); u32 fat_sector_index = m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / m_device_block_size); u32 entry_offset = fat_offset % m_device_block_size; // See the comment in fat_read(). bool need_extra_block = m_fat_version == FATVersion::FAT12 && entry_offset == m_device_block_size - 1; size_t buffer_size = m_device_block_size; if (need_extra_block) buffer_size += m_device_block_size; auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, buffer_size)); auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); MutexLocker locker(m_lock); if (need_extra_block) TRY(read_blocks(fat_sector_index, 2, fat_sector_buffer)); else TRY(read_block(fat_sector_index, &fat_sector_buffer, m_device_block_size)); switch (m_fat_version) { case FATVersion::FAT12: { auto write_misaligned_u16 = [&](u16 word) { *bit_cast(&fat_sector->data()[entry_offset]) = word & 0xFF; *(bit_cast(&fat_sector->data()[entry_offset]) + 1) = word >> 8; }; u16 existing_bytes_le = 0; ByteReader::load(fat_sector->bytes().offset(entry_offset), existing_bytes_le); u16 existing_bytes = AK::convert_between_host_and_little_endian(existing_bytes_le); if (cluster % 2 == 0) { existing_bytes &= 0xF000; existing_bytes |= static_cast(value) & 0xFFF; } else { existing_bytes &= 0x000F; existing_bytes |= static_cast(value) << 4; } write_misaligned_u16(AK::convert_between_host_and_little_endian(existing_bytes)); break; } case FATVersion::FAT16: { *bit_cast(&fat_sector->data()[entry_offset]) = AK::convert_between_host_and_little_endian(static_cast(value)); break; } case FATVersion::FAT32: { *bit_cast(&fat_sector->data()[entry_offset]) = AK::convert_between_host_and_little_endian(value); break; } } if (need_extra_block) TRY(write_blocks(fat_sector_index, 2, fat_sector_buffer)); else TRY(write_block(fat_sector_index, fat_sector_buffer, m_device_block_size)); return {}; } u8 FATFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const { FATAttributes attrib = static_cast(entry.file_type); if (has_flag(attrib, FATAttributes::Directory)) { return DT_DIR; } else if (has_flag(attrib, FATAttributes::VolumeID)) { return DT_UNKNOWN; } else { // ReadOnly, Hidden, System, Archive, LongFileName. return DT_REG; } return DT_UNKNOWN; } }