mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:52:43 +00:00 
			
		
		
		
	Kernel/FileSystem/FATFS: Support FAT16 file system clusters
This commit is contained in:
		
							parent
							
								
									67f567348f
								
							
						
					
					
						commit
						1f70a728f0
					
				
					 2 changed files with 92 additions and 8 deletions
				
			
		|  | @ -4,6 +4,8 @@ | |||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/ByteReader.h> | ||||
| #include <AK/Endian.h> | ||||
| #include <AK/Time.h> | ||||
| #include <Kernel/Debug.h> | ||||
| #include <Kernel/FileSystem/FATFS/Inode.h> | ||||
|  | @ -18,7 +20,7 @@ ErrorOr<NonnullRefPtr<FATInode>> FATInode::create(FATFS& fs, FATEntry entry, Vec | |||
| } | ||||
| 
 | ||||
| FATInode::FATInode(FATFS& fs, FATEntry entry, NonnullOwnPtr<KString> filename) | ||||
|     : Inode(fs, first_cluster()) | ||||
|     : Inode(fs, first_cluster(fs.m_fat_version)) | ||||
|     , m_entry(entry) | ||||
|     , m_filename(move(filename)) | ||||
| { | ||||
|  | @ -55,7 +57,7 @@ ErrorOr<Vector<BlockBasedFileSystem::BlockIndex>> FATInode::compute_block_list() | |||
|     auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, fs().m_device_block_size)); | ||||
|     auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); | ||||
| 
 | ||||
|     while (cluster < no_more_clusters) { | ||||
|     while (cluster < end_of_chain_marker()) { | ||||
|         dbgln_if(FAT_DEBUG, "FATFS: Appending cluster {} to inode {}'s cluster chain", cluster, index()); | ||||
| 
 | ||||
|         auto first_block_and_length = fs().first_block_of_cluster(cluster); | ||||
|  | @ -78,19 +80,78 @@ ErrorOr<Vector<BlockBasedFileSystem::BlockIndex>> FATInode::compute_block_list() | |||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         u32 fat_offset = cluster * sizeof(u32); | ||||
|         u32 fat_offset = cluster * cluster_size(); | ||||
|         u32 fat_sector_index = fs().m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / fs().m_device_block_size); | ||||
|         u32 entry_offset = fat_offset % fs().m_device_block_size; | ||||
| 
 | ||||
|         TRY(fs().raw_read(fat_sector_index, fat_sector_buffer)); | ||||
| 
 | ||||
|         cluster = *reinterpret_cast<u32*>(&fat_sector->data()[entry_offset]); | ||||
|         cluster &= cluster_number_mask; | ||||
|         cluster = cluster_number(*fat_sector, entry_offset); | ||||
|     } | ||||
| 
 | ||||
|     return block_list; | ||||
| } | ||||
| 
 | ||||
| u32 FATInode::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 (fs().m_fat_version) { | ||||
|     case FATVersion::FAT12: | ||||
|         return 0xff8; | ||||
|     case FATVersion::FAT16: | ||||
|         return 0xfff8; | ||||
|     case FATVersion::FAT32: | ||||
|         return 0x0FFFFFF8; | ||||
|     default: | ||||
|         VERIFY_NOT_REACHED(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t FATInode::cluster_size() const | ||||
| { | ||||
|     switch (fs().m_fat_version) { | ||||
|     case FATVersion::FAT12: | ||||
|         VERIFY(false); | ||||
|         break; | ||||
|     case FATVersion::FAT16: | ||||
|         return 2; | ||||
|     case FATVersion::FAT32: | ||||
|         return 4; | ||||
|     default: | ||||
|         VERIFY(false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| u32 FATInode::cluster_number(KBuffer const& fat_sector, u32 entry_offset) const | ||||
| { | ||||
|     u32 cluster = 0; | ||||
|     switch (fs().m_fat_version) { | ||||
|     case FATVersion::FAT12: | ||||
|         VERIFY(false); | ||||
|         break; | ||||
|     case FATVersion::FAT16: { | ||||
|         u16 cluster_u16_le = 0; | ||||
|         ByteReader::load<u16>(fat_sector.bytes().offset(entry_offset), cluster_u16_le); | ||||
|         cluster = LittleEndian { cluster_u16_le }; | ||||
|         break; | ||||
|     } | ||||
|     case FATVersion::FAT32: { | ||||
|         u32 cluster_u32_le = 0; | ||||
|         ByteReader::load<u32>(fat_sector.bytes().offset(entry_offset), cluster_u32_le); | ||||
|         cluster = LittleEndian { 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(false); | ||||
|     } | ||||
|     return cluster; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<NonnullOwnPtr<KBuffer>> FATInode::read_block_list() | ||||
| { | ||||
|     VERIFY(m_inode_lock.is_locked()); | ||||
|  | @ -220,7 +281,18 @@ StringView FATInode::byte_terminated_string(StringView string, u8 fill_byte) | |||
| 
 | ||||
| u32 FATInode::first_cluster() const | ||||
| { | ||||
|     return (((u32)m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; | ||||
|     return first_cluster(fs().m_fat_version); | ||||
| } | ||||
| 
 | ||||
| u32 FATInode::first_cluster(FATVersion const version) const | ||||
| { | ||||
|     if (version == FATVersion::FAT32) { | ||||
|         return (static_cast<u32>(m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; | ||||
|     } | ||||
|     // The space occupied in a directory entry by `first_cluster_high` (0x14)
 | ||||
|     // is reserved in FAT12/16, and may be used to store file meta-data.
 | ||||
|     // As a result, do not include it.
 | ||||
|     return m_entry.first_cluster_low; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<size_t> FATInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const | ||||
|  |  | |||
|  | @ -30,8 +30,16 @@ public: | |||
| private: | ||||
|     FATInode(FATFS&, FATEntry, NonnullOwnPtr<KString> filename); | ||||
| 
 | ||||
|     static constexpr u32 no_more_clusters = 0x0FFFFFF8; | ||||
|     static constexpr u32 cluster_number_mask = 0x0FFFFFFF; | ||||
|     // Number of bytes used to store a cluster within the table.
 | ||||
|     size_t cluster_size() const; | ||||
| 
 | ||||
|     // Returns cluster number value that indicates the end of the chain
 | ||||
|     // has been reached. Any cluster value >= this value indicates this
 | ||||
|     // is the last cluster.
 | ||||
|     u32 end_of_chain_marker() const; | ||||
| 
 | ||||
|     // Reads the cluster number located at the offset within the table.
 | ||||
|     u32 cluster_number(KBuffer const& fat_sector, u32 entry_offset) const; | ||||
| 
 | ||||
|     static constexpr u8 end_entry_byte = 0x00; | ||||
|     static constexpr u8 unused_entry_byte = 0xE5; | ||||
|  | @ -49,6 +57,10 @@ private: | |||
|     ErrorOr<NonnullOwnPtr<KBuffer>> read_block_list(); | ||||
|     ErrorOr<RefPtr<FATInode>> traverse(Function<ErrorOr<bool>(RefPtr<FATInode>)> callback); | ||||
|     u32 first_cluster() const; | ||||
|     // This overload of `first_cluster` does not rely on the base Inode
 | ||||
|     // already being created to determine the FAT version. It is used
 | ||||
|     // during FATInode creation (create()).
 | ||||
|     u32 first_cluster(FATVersion const version) const; | ||||
| 
 | ||||
|     // ^Inode
 | ||||
|     virtual ErrorOr<size_t> write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Taj Morton
						Taj Morton