Kernel: Update the ".." inode for directories after a rename

Because the ".." entry in a directory is a separate inode, if a
directory is renamed to a new location, then we should update this entry
the point to the new parent directory as well.

Co-authored-by: Liav A <liavalb@gmail.com>
This commit is contained in:
sin-ack 2022-10-08 11:22:12 +02:00 committed by Andreas Kling
parent 2b246d980a
commit 3b03077abb
Notes: sideshowbarker 2024-07-18 03:20:18 +09:00
18 changed files with 128 additions and 0 deletions
Kernel/FileSystem/Ext2FS

View file

@ -892,6 +892,63 @@ ErrorOr<void> Ext2FSInode::remove_child(StringView name)
return {};
}
ErrorOr<void> Ext2FSInode::replace_child(StringView name, Inode& child)
{
MutexLocker locker(m_inode_lock);
dbgln_if(EXT2_DEBUG, "Ext2FSInode[{}]::replace_child(): Replacing '{}' with inode {}", identifier(), name, child.index());
VERIFY(is_directory());
TRY(populate_lookup_cache());
if (name.length() > EXT2_NAME_LEN)
return ENAMETOOLONG;
Vector<Ext2FSDirectoryEntry> entries;
Optional<InodeIndex> old_child_index;
TRY(traverse_as_directory([&](auto& entry) -> ErrorOr<void> {
auto is_replacing_this_inode = name == entry.name;
auto inode_index = is_replacing_this_inode ? child.index() : entry.inode.index();
auto entry_name = TRY(KString::try_create(entry.name));
TRY(entries.try_empend(move(entry_name), inode_index, to_ext2_file_type(child.mode())));
if (is_replacing_this_inode)
old_child_index = entry.inode.index();
return {};
}));
if (!old_child_index.has_value())
return ENOENT;
auto old_child = TRY(fs().get_inode({ fsid(), *old_child_index }));
auto old_index_it = m_lookup_cache.find(name);
VERIFY(old_index_it != m_lookup_cache.end());
old_index_it->value = child.index();
// NOTE: Between this line and the write_directory line, all operations must
// be atomic. Any changes made should be reverted.
TRY(child.increment_link_count());
auto maybe_decrement_error = old_child->decrement_link_count();
if (maybe_decrement_error.is_error()) {
old_index_it->value = *old_child_index;
MUST(child.decrement_link_count());
return maybe_decrement_error;
}
// FIXME: The filesystem is left in an inconsistent state if this fails.
// Revert the changes made above if we can't write_directory.
// Ideally, decrement should be the last operation, but we currently
// can't "un-write" a directory entry list.
TRY(write_directory(entries));
// TODO: Emit a did_replace_child event.
return {};
}
ErrorOr<void> Ext2FSInode::populate_lookup_cache()
{
VERIFY(m_inode_lock.is_exclusively_locked_by_current_thread());