Serenity Operating System
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}