Serenity Operating System
at master 286 lines 10 kB view raw
1/* 2 * Copyright (c) 2022, Undefine <undefine@undefine.pl> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Time.h> 8#include <Kernel/Debug.h> 9#include <Kernel/FileSystem/FATFS/Inode.h> 10#include <Kernel/KBufferBuilder.h> 11 12namespace Kernel { 13 14ErrorOr<NonnullRefPtr<FATInode>> FATInode::create(FATFS& fs, FATEntry entry, Vector<FATLongFileNameEntry> const& lfn_entries) 15{ 16 auto filename = TRY(compute_filename(entry, lfn_entries)); 17 return adopt_nonnull_ref_or_enomem(new (nothrow) FATInode(fs, entry, move(filename))); 18} 19 20FATInode::FATInode(FATFS& fs, FATEntry entry, NonnullOwnPtr<KString> filename) 21 : Inode(fs, first_cluster()) 22 , m_entry(entry) 23 , m_filename(move(filename)) 24{ 25 dbgln_if(FAT_DEBUG, "FATFS: Creating inode {} with filename \"{}\"", index(), m_filename); 26 27 m_metadata = { 28 .inode = identifier(), 29 .size = m_entry.file_size, 30 .mode = static_cast<mode_t>((has_flag(m_entry.attributes, FATAttributes::Directory) ? S_IFDIR : S_IFREG) | 0777), 31 .uid = 0, 32 .gid = 0, 33 .link_count = 0, 34 .atime = time_from_packed_dos(m_entry.last_accessed_date, { 0 }), 35 .ctime = time_from_packed_dos(m_entry.creation_date, m_entry.creation_time), 36 .mtime = time_from_packed_dos(m_entry.modification_date, m_entry.modification_time), 37 .dtime = {}, 38 .block_count = 0, 39 .block_size = 0, 40 .major_device = 0, 41 .minor_device = 0, 42 }; 43} 44 45ErrorOr<Vector<BlockBasedFileSystem::BlockIndex>> FATInode::compute_block_list() 46{ 47 VERIFY(m_inode_lock.is_locked()); 48 49 dbgln_if(FAT_DEBUG, "FATFS: computing block list for inode {}", index()); 50 51 u32 cluster = first_cluster(); 52 53 Vector<BlockBasedFileSystem::BlockIndex> block_list; 54 55 auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, fs().m_logical_block_size)); 56 auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); 57 58 while (cluster < no_more_clusters) { 59 dbgln_if(FAT_DEBUG, "FATFS: Appending cluster {} to inode {}'s cluster chain", cluster, index()); 60 61 BlockBasedFileSystem::BlockIndex first_block = fs().first_block_of_cluster(cluster); 62 for (u8 i = 0; i < fs().boot_record()->sectors_per_cluster; i++) 63 block_list.append(BlockBasedFileSystem::BlockIndex { first_block.value() + i }); 64 65 u32 fat_offset = cluster * sizeof(u32); 66 u32 fat_sector_index = fs().boot_record()->reserved_sector_count + (fat_offset / fs().m_logical_block_size); 67 u32 entry_offset = fat_offset % fs().m_logical_block_size; 68 69 TRY(fs().raw_read(fat_sector_index, fat_sector_buffer)); 70 71 cluster = *reinterpret_cast<u32*>(&fat_sector->data()[entry_offset]); 72 cluster &= cluster_number_mask; 73 } 74 75 return block_list; 76} 77 78ErrorOr<NonnullOwnPtr<KBuffer>> FATInode::read_block_list() 79{ 80 VERIFY(m_inode_lock.is_locked()); 81 82 dbgln_if(FAT_DEBUG, "FATFS: reading block list for inode {} ({} blocks)", index(), m_block_list.size()); 83 84 if (m_block_list.is_empty()) 85 m_block_list = TRY(compute_block_list()); 86 87 auto builder = TRY(KBufferBuilder::try_create()); 88 89 u8 buffer[512]; 90 VERIFY(fs().m_logical_block_size <= sizeof(buffer)); 91 auto buf = UserOrKernelBuffer::for_kernel_buffer(buffer); 92 93 for (BlockBasedFileSystem::BlockIndex block : m_block_list) { 94 dbgln_if(FAT_DEBUG, "FATFS: reading block: {}", block); 95 TRY(fs().raw_read(block, buf)); 96 TRY(builder.append((char const*)buffer, fs().m_logical_block_size)); 97 } 98 99 auto blocks = builder.build(); 100 if (!blocks) 101 return ENOMEM; 102 return blocks.release_nonnull(); 103} 104 105ErrorOr<void> FATInode::replace_child(StringView, Inode&) 106{ 107 // TODO: Implement this once we have write support. 108 return Error::from_errno(EROFS); 109} 110 111ErrorOr<RefPtr<FATInode>> FATInode::traverse(Function<ErrorOr<bool>(RefPtr<FATInode>)> callback) 112{ 113 VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); 114 115 Vector<FATLongFileNameEntry> lfn_entries; 116 auto blocks = TRY(read_block_list()); 117 118 for (u32 i = 0; i < blocks->size() / sizeof(FATEntry); i++) { 119 auto* entry = reinterpret_cast<FATEntry*>(blocks->data() + i * sizeof(FATEntry)); 120 if (entry->filename[0] == end_entry_byte) { 121 dbgln_if(FAT_DEBUG, "FATFS: Found end entry"); 122 return nullptr; 123 } else if (static_cast<u8>(entry->filename[0]) == unused_entry_byte) { 124 dbgln_if(FAT_DEBUG, "FATFS: Found unused entry"); 125 lfn_entries.clear(); 126 } else if (entry->attributes == FATAttributes::LongFileName) { 127 dbgln_if(FAT_DEBUG, "FATFS: Found LFN entry"); 128 TRY(lfn_entries.try_append(*reinterpret_cast<FATLongFileNameEntry*>(entry))); 129 } else { 130 dbgln_if(FAT_DEBUG, "FATFS: Found 8.3 entry"); 131 lfn_entries.reverse(); 132 auto inode = TRY(FATInode::create(fs(), *entry, lfn_entries)); 133 if (TRY(callback(inode))) 134 return inode; 135 lfn_entries.clear(); 136 } 137 } 138 139 return EINVAL; 140} 141 142ErrorOr<NonnullOwnPtr<KString>> FATInode::compute_filename(FATEntry& entry, Vector<FATLongFileNameEntry> const& lfn_entries) 143{ 144 if (lfn_entries.is_empty()) { 145 StringBuilder filename; 146 filename.append(byte_terminated_string(StringView(entry.filename, normal_filename_length), ' ')); 147 if (entry.extension[0] != ' ') { 148 filename.append('.'); 149 filename.append(byte_terminated_string(StringView(entry.extension, normal_extension_length), ' ')); 150 } 151 return TRY(KString::try_create(filename.string_view())); 152 } else { 153 StringBuilder filename; 154 for (auto& lfn_entry : lfn_entries) { 155 filename.append(lfn_entry.characters1[0]); 156 filename.append(lfn_entry.characters1[1]); 157 filename.append(lfn_entry.characters1[2]); 158 filename.append(lfn_entry.characters1[3]); 159 filename.append(lfn_entry.characters1[4]); 160 filename.append(lfn_entry.characters2[0]); 161 filename.append(lfn_entry.characters2[1]); 162 filename.append(lfn_entry.characters2[2]); 163 filename.append(lfn_entry.characters2[3]); 164 filename.append(lfn_entry.characters2[4]); 165 filename.append(lfn_entry.characters2[5]); 166 filename.append(lfn_entry.characters3[0]); 167 filename.append(lfn_entry.characters3[1]); 168 } 169 170 // Long Filenames have two terminators: 171 // 1. Completely unused "entries" (the `characterN` fields of 172 // `lfn_entry`) are filled with 0xFF (`lfn_entry_unused_byte`). 173 // 2. Partially used entries (within `characterN`) are null-padded. 174 // 175 // `filename` is truncated first to eliminate unused entries, and 176 // then further truncated to remove any existing null padding characters. 177 // 178 // Page 8 of the Long Filename Specification 179 // (http://www.osdever.net/documents/LongFileName.pdf) 180 // details this encoding ("If the long name does not fill..."). 181 return TRY(KString::try_create( 182 byte_terminated_string( 183 byte_terminated_string(filename.string_view(), lfn_entry_unused_byte), lfn_entry_character_termination))); 184 } 185 186 VERIFY_NOT_REACHED(); 187} 188 189StringView FATInode::byte_terminated_string(StringView string, u8 fill_byte) 190{ 191 if (auto index = string.find_last_not(fill_byte); index.has_value()) 192 return string.substring_view(0, index.value() + 1); 193 return string; 194} 195 196u32 FATInode::first_cluster() const 197{ 198 return (((u32)m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; 199} 200 201ErrorOr<size_t> FATInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const 202{ 203 dbgln_if(FAT_DEBUG, "FATFS: Reading inode {}: size: {} offset: {}", identifier().index(), size, offset); 204 VERIFY(offset >= 0); 205 if (offset >= m_metadata.size) 206 return 0; 207 208 // FIXME: Read only the needed blocks instead of the whole file 209 auto blocks = TRY(const_cast<FATInode&>(*this).read_block_list()); 210 TRY(buffer.write(blocks->data() + offset, min(size, m_block_list.size() * fs().m_logical_block_size - offset))); 211 212 return min(size, m_block_list.size() * fs().m_logical_block_size - offset); 213} 214 215InodeMetadata FATInode::metadata() const 216{ 217 return m_metadata; 218} 219 220ErrorOr<void> FATInode::traverse_as_directory(Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const 221{ 222 MutexLocker locker(m_inode_lock); 223 224 VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); 225 226 [[maybe_unused]] auto inode = TRY(const_cast<FATInode&>(*this).traverse([&callback](auto inode) -> ErrorOr<bool> { 227 if (inode->m_filename->view() == "" || inode->m_filename->view() == "." || inode->m_filename->view() == "..") 228 return false; 229 TRY(callback({ inode->m_filename->view(), inode->identifier(), static_cast<u8>(inode->m_entry.attributes) })); 230 return false; 231 })); 232 233 return {}; 234} 235 236ErrorOr<NonnullRefPtr<Inode>> FATInode::lookup(StringView name) 237{ 238 MutexLocker locker(m_inode_lock); 239 240 VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); 241 242 auto inode = TRY(traverse([name](auto child) -> ErrorOr<bool> { 243 return child->m_filename->view() == name; 244 })); 245 246 if (inode.is_null()) 247 return ENOENT; 248 return inode.release_nonnull(); 249} 250 251ErrorOr<size_t> FATInode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) 252{ 253 return EROFS; 254} 255 256ErrorOr<NonnullRefPtr<Inode>> FATInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) 257{ 258 return EROFS; 259} 260 261ErrorOr<void> FATInode::add_child(Inode&, StringView, mode_t) 262{ 263 return EROFS; 264} 265 266ErrorOr<void> FATInode::remove_child(StringView) 267{ 268 return EROFS; 269} 270 271ErrorOr<void> FATInode::chmod(mode_t) 272{ 273 return EROFS; 274} 275 276ErrorOr<void> FATInode::chown(UserID, GroupID) 277{ 278 return EROFS; 279} 280 281ErrorOr<void> FATInode::flush_metadata() 282{ 283 return EROFS; 284} 285 286}