Skip to content

Commit 007a093

Browse files
committed
Kernel/FATFS: Compute cluster lists somewhat lazily
Now we no longer compute the cluster list at the exact moment when an in-memory inode is created, but rather defer doing that until we actually need the cluster list. This distinction is important when traversing directories, as we *are* interested in the inode, but not really in its contents (though now we do have to resort to using the file size to approximate the amount of allocated blocks, but this shouldn't be an issue when working with well-formed filesystems.)
1 parent 1185e56 commit 007a093

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

Kernel/FileSystem/FATFS/Inode.cpp

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@ namespace Kernel {
1515
ErrorOr<NonnullRefPtr<FATInode>> FATInode::create(FATFS& fs, FATEntry entry, FATEntryLocation inode_metadata_location, Vector<FATLongFileNameEntry> const& lfn_entries)
1616
{
1717
auto filename = TRY(compute_filename(entry, lfn_entries));
18-
u32 entry_first_cluster = entry.first_cluster_low;
19-
if (fs.m_fat_version == FATVersion::FAT32)
20-
entry_first_cluster |= (static_cast<u32>(entry.first_cluster_high) << 16);
21-
auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FATInode(fs, entry, inode_metadata_location, move(filename))));
22-
MutexLocker locker(inode->m_inode_lock);
23-
inode->m_cluster_list = TRY(inode->compute_cluster_list(fs, entry_first_cluster));
24-
return inode;
18+
return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) FATInode(fs, entry, inode_metadata_location, move(filename))));
2519
}
2620

2721
FATInode::FATInode(FATFS& fs, FATEntry entry, FATEntryLocation inode_metadata_location, NonnullOwnPtr<KString> filename)
@@ -33,6 +27,18 @@ FATInode::FATInode(FATFS& fs, FATEntry entry, FATEntryLocation inode_metadata_lo
3327
dbgln_if(FAT_DEBUG, "FATInode[{}]::FATInode(): Creating inode with filename \"{}\"", identifier(), m_filename);
3428
}
3529

30+
ErrorOr<RawPtr<Vector<u32>>> FATInode::get_cluster_list()
31+
{
32+
VERIFY(m_inode_lock.is_locked());
33+
34+
if (m_cluster_list)
35+
return m_cluster_list.ptr();
36+
37+
auto cluster_list = TRY(compute_cluster_list(fs(), first_cluster()));
38+
m_cluster_list = TRY(try_make<Vector<u32>>(move(cluster_list)));
39+
return m_cluster_list.ptr();
40+
}
41+
3642
ErrorOr<Vector<u32>> FATInode::compute_cluster_list(FATFS& fs, u32 first_cluster)
3743
{
3844
VERIFY(m_inode_lock.is_locked());
@@ -203,7 +209,8 @@ ErrorOr<Vector<BlockBasedFileSystem::BlockIndex>> FATInode::get_block_list()
203209

204210
Vector<BlockBasedFileSystem::BlockIndex> block_list;
205211

206-
for (auto cluster : m_cluster_list) {
212+
auto cluster_list = TRY(get_cluster_list());
213+
for (auto cluster : *cluster_list) {
207214
auto span = fs().first_block_of_cluster(cluster);
208215
for (size_t i = 0; i < span.number_of_sectors; i++) {
209216
dbgln_if(FAT_DEBUG, "FATInode[{}]::get_block_list(): Appending block {} to block list", identifier(), BlockBasedFileSystem::BlockIndex { span.start_block.value() + i });
@@ -271,7 +278,8 @@ ErrorOr<RefPtr<FATInode>> FATInode::traverse(Function<ErrorOr<bool>(RefPtr<FATIn
271278
return EINVAL;
272279
} else {
273280
auto entry_number_bytes = i * sizeof(FATEntry);
274-
auto cluster = m_cluster_list[entry_number_bytes / bytes_per_cluster];
281+
auto cluster_list = TRY(get_cluster_list());
282+
auto cluster = (*cluster_list)[entry_number_bytes / bytes_per_cluster];
275283
auto block = BlockBasedFileSystem::BlockIndex { fs().first_block_of_cluster(cluster).start_block.value() + (entry_number_bytes % bytes_per_cluster) / fs().m_device_block_size };
276284

277285
auto entries_per_sector = fs().m_device_block_size / sizeof(FATEntry);
@@ -365,8 +373,9 @@ ErrorOr<void> FATInode::allocate_and_add_cluster_to_chain()
365373

366374
u32 allocated_cluster = TRY(fs().allocate_cluster());
367375
dbgln_if(FAT_DEBUG, "FATInode[{}]::allocate_and_add_cluster_to_chain(): allocated cluster {}", identifier(), allocated_cluster);
376+
auto cluster_list = TRY(get_cluster_list());
368377

369-
if (m_cluster_list.is_empty() || (m_cluster_list.size() == 1 && first_cluster() <= 1)) {
378+
if (cluster_list->is_empty() || (cluster_list->size() == 1 && first_cluster() <= 1)) {
370379
// This is the first cluster in the chain, so update the inode metadata.
371380
if (fs().m_fat_version == FATVersion::FAT32) {
372381
// Only FAT32 uses the `first_cluster_high` field.
@@ -380,27 +389,28 @@ ErrorOr<void> FATInode::allocate_and_add_cluster_to_chain()
380389
// This is not the first cluster in the chain, so we need to update the
381390
// FAT entry for the last cluster in the chain to point to the newly
382391
// allocated cluster.
383-
TRY(fs().fat_write(m_cluster_list.last(), allocated_cluster));
392+
TRY(fs().fat_write(cluster_list->last(), allocated_cluster));
384393
}
385394

386-
m_cluster_list.append(allocated_cluster);
395+
cluster_list->append(allocated_cluster);
387396

388397
return {};
389398
}
390399

391400
ErrorOr<void> FATInode::remove_last_cluster_from_chain()
392401
{
393402
VERIFY(m_inode_lock.is_locked());
394-
VERIFY(m_cluster_list.size() > 0);
403+
auto cluster_list = TRY(get_cluster_list());
404+
VERIFY(cluster_list->size() > 0);
395405

396-
u32 last_cluster = m_cluster_list.take_last();
406+
u32 last_cluster = cluster_list->take_last();
397407

398408
dbgln_if(FAT_DEBUG, "FATInode[{}]::remove_last_cluster_from_chain(): freeing cluster {}", identifier(), last_cluster);
399409

400410
TRY(fs().fat_write(last_cluster, 0));
401411
TRY(fs().notify_cluster_freed(last_cluster));
402412

403-
if (m_cluster_list.is_empty() || (m_cluster_list.size() == 1 && first_cluster() <= 1)) {
413+
if (cluster_list->is_empty() || (cluster_list->size() == 1 && first_cluster() <= 1)) {
404414
// We have removed the last cluster in the chain, so update the inode metadata.
405415
if (fs().m_fat_version == FATVersion::FAT32) {
406416
// Only FAT32 uses the `first_cluster_high` field.
@@ -413,7 +423,7 @@ ErrorOr<void> FATInode::remove_last_cluster_from_chain()
413423
} else {
414424
// We have removed a cluster from the chain, so update the FAT entry for
415425
// the last cluster in the chain mark it as the end of the chain.
416-
last_cluster = m_cluster_list.last();
426+
last_cluster = cluster_list->last();
417427
TRY(fs().fat_write(last_cluster, fs().end_of_chain_marker()));
418428
}
419429

@@ -523,6 +533,7 @@ ErrorOr<size_t> FATInode::read_bytes_locked(off_t offset, size_t count, UserOrKe
523533

524534
InodeMetadata FATInode::metadata() const
525535
{
536+
auto cluster_count = ceil_div(static_cast<u64>(m_entry.file_size), fs().m_device_block_size * fs().m_parameter_block->common_bpb()->sectors_per_cluster);
526537
return {
527538
.inode = identifier(),
528539
.size = m_entry.file_size,
@@ -535,7 +546,7 @@ InodeMetadata FATInode::metadata() const
535546
.ctime = time_from_packed_dos(m_entry.creation_date, m_entry.creation_time),
536547
.mtime = time_from_packed_dos(m_entry.modification_date, m_entry.modification_time),
537548
.dtime = {},
538-
.block_count = m_cluster_list.size() * fs().m_parameter_block->common_bpb()->sectors_per_cluster,
549+
.block_count = cluster_count * fs().m_parameter_block->common_bpb()->sectors_per_cluster,
539550
.block_size = fs().m_device_block_size,
540551
.major_device = 0,
541552
.minor_device = 0,
@@ -868,6 +879,8 @@ ErrorOr<void> FATInode::resize(u64 size, Optional<u64> clear_from, Optional<u64>
868879
VERIFY(m_inode_lock.is_locked());
869880
VERIFY(size != m_entry.file_size);
870881

882+
auto cluster_list = TRY(get_cluster_list());
883+
871884
u64 bytes_per_cluster = fs().m_device_block_size * fs().m_parameter_block->common_bpb()->sectors_per_cluster;
872885

873886
u64 size_rounded_up_to_bytes_per_cluster = size;
@@ -877,10 +890,10 @@ ErrorOr<void> FATInode::resize(u64 size, Optional<u64> clear_from, Optional<u64>
877890
size_rounded_up_to_bytes_per_cluster = (size + bytes_per_cluster) - (size % bytes_per_cluster);
878891

879892
if (size > m_entry.file_size) {
880-
while (m_cluster_list.size() * bytes_per_cluster < size_rounded_up_to_bytes_per_cluster)
893+
while (cluster_list->size() * bytes_per_cluster < size_rounded_up_to_bytes_per_cluster)
881894
TRY(allocate_and_add_cluster_to_chain());
882895
} else {
883-
while (m_cluster_list.size() * bytes_per_cluster > size_rounded_up_to_bytes_per_cluster)
896+
while (cluster_list->size() * bytes_per_cluster > size_rounded_up_to_bytes_per_cluster)
884897
TRY(remove_last_cluster_from_chain());
885898
}
886899

Kernel/FileSystem/FATFS/Inode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class FATInode final : public Inode {
6262
static ErrorOr<void> encode_known_good_sfn_for(FATEntry& entry, StringView name);
6363
static ErrorOr<Vector<FATLongFileNameEntry>> create_lfn_entries(StringView name, u8 checksum);
6464

65+
ErrorOr<RawPtr<Vector<u32>>> get_cluster_list();
6566
ErrorOr<Vector<u32>> compute_cluster_list(FATFS&, u32 first_cluster);
6667
ErrorOr<Vector<BlockBasedFileSystem::BlockIndex>> get_block_list();
6768
ErrorOr<NonnullOwnPtr<KBuffer>> read_block_list();
@@ -103,7 +104,7 @@ class FATInode final : public Inode {
103104
virtual ErrorOr<void> flush_metadata() override;
104105
virtual ErrorOr<void> update_timestamps(Optional<UnixDateTime> atime, Optional<UnixDateTime> ctime, Optional<UnixDateTime> mtime) override;
105106

106-
Vector<u32> m_cluster_list;
107+
OwnPtr<Vector<u32>> m_cluster_list;
107108
FATEntry m_entry;
108109
FATEntryLocation m_inode_metadata_location;
109110
NonnullOwnPtr<KString> m_filename;

0 commit comments

Comments
 (0)