Переглянути джерело

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>
sin-ack 2 роки тому
батько
коміт
3b03077abb

+ 5 - 0
Kernel/FileSystem/DevPtsFS/Inode.cpp

@@ -104,6 +104,11 @@ ErrorOr<void> DevPtsFSInode::remove_child(StringView)
     return EROFS;
     return EROFS;
 }
 }
 
 
+ErrorOr<void> DevPtsFSInode::replace_child(StringView, Inode&)
+{
+    return EROFS;
+}
+
 ErrorOr<void> DevPtsFSInode::chmod(mode_t)
 ErrorOr<void> DevPtsFSInode::chmod(mode_t)
 {
 {
     return EROFS;
     return EROFS;

+ 1 - 0
Kernel/FileSystem/DevPtsFS/Inode.h

@@ -35,6 +35,7 @@ private:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
 
 

+ 57 - 0
Kernel/FileSystem/Ext2FS/Inode.cpp

@@ -892,6 +892,63 @@ ErrorOr<void> Ext2FSInode::remove_child(StringView name)
     return {};
     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()
 ErrorOr<void> Ext2FSInode::populate_lookup_cache()
 {
 {
     VERIFY(m_inode_lock.is_exclusively_locked_by_current_thread());
     VERIFY(m_inode_lock.is_exclusively_locked_by_current_thread());

+ 1 - 0
Kernel/FileSystem/Ext2FS/Inode.h

@@ -36,6 +36,7 @@ private:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode& child, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode& child, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> update_timestamps(Optional<Time> atime, Optional<Time> ctime, Optional<Time> mtime) override;
     virtual ErrorOr<void> update_timestamps(Optional<Time> atime, Optional<Time> ctime, Optional<Time> mtime) override;
     virtual ErrorOr<void> increment_link_count() override;
     virtual ErrorOr<void> increment_link_count() override;
     virtual ErrorOr<void> decrement_link_count() override;
     virtual ErrorOr<void> decrement_link_count() override;

+ 6 - 0
Kernel/FileSystem/FATFS/Inode.cpp

@@ -102,6 +102,12 @@ ErrorOr<NonnullOwnPtr<KBuffer>> FATInode::read_block_list()
     return blocks.release_nonnull();
     return blocks.release_nonnull();
 }
 }
 
 
+ErrorOr<void> FATInode::replace_child(StringView, Inode&)
+{
+    // TODO: Implement this once we have write support.
+    return Error::from_errno(EROFS);
+}
+
 ErrorOr<LockRefPtr<FATInode>> FATInode::traverse(Function<ErrorOr<bool>(LockRefPtr<FATInode>)> callback)
 ErrorOr<LockRefPtr<FATInode>> FATInode::traverse(Function<ErrorOr<bool>(LockRefPtr<FATInode>)> callback)
 {
 {
     VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory));
     VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory));

+ 1 - 0
Kernel/FileSystem/FATFS/Inode.h

@@ -62,6 +62,7 @@ private:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> flush_metadata() override;
     virtual ErrorOr<void> flush_metadata() override;

+ 5 - 0
Kernel/FileSystem/ISO9660FS/Inode.cpp

@@ -70,6 +70,11 @@ ErrorOr<void> ISO9660Inode::traverse_as_directory(Function<ErrorOr<void>(FileSys
     });
     });
 }
 }
 
 
+ErrorOr<void> ISO9660Inode::replace_child(StringView, Inode&)
+{
+    return EROFS;
+}
+
 ErrorOr<NonnullLockRefPtr<Inode>> ISO9660Inode::lookup(StringView name)
 ErrorOr<NonnullLockRefPtr<Inode>> ISO9660Inode::lookup(StringView name)
 {
 {
     LockRefPtr<Inode> inode;
     LockRefPtr<Inode> inode;

+ 1 - 0
Kernel/FileSystem/ISO9660FS/Inode.h

@@ -28,6 +28,7 @@ public:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> truncate(u64) override;
     virtual ErrorOr<void> truncate(u64) override;

+ 3 - 0
Kernel/FileSystem/Inode.h

@@ -66,6 +66,9 @@ public:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) = 0;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) = 0;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) = 0;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) = 0;
     virtual ErrorOr<void> remove_child(StringView name) = 0;
     virtual ErrorOr<void> remove_child(StringView name) = 0;
+    /// Replace child atomically, incrementing the link count of the replacement
+    /// inode and decrementing the older inode's.
+    virtual ErrorOr<void> replace_child(StringView name, Inode&) = 0;
     virtual ErrorOr<void> chmod(mode_t) = 0;
     virtual ErrorOr<void> chmod(mode_t) = 0;
     virtual ErrorOr<void> chown(UserID, GroupID) = 0;
     virtual ErrorOr<void> chown(UserID, GroupID) = 0;
     virtual ErrorOr<void> truncate(u64) { return {}; }
     virtual ErrorOr<void> truncate(u64) { return {}; }

+ 6 - 0
Kernel/FileSystem/Plan9FS/Inode.cpp

@@ -96,6 +96,12 @@ ErrorOr<size_t> Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserO
     return nread;
     return nread;
 }
 }
 
 
+ErrorOr<void> Plan9FSInode::replace_child(StringView, Inode&)
+{
+    // TODO
+    return ENOTIMPL;
+}
+
 ErrorOr<size_t> Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*)
 ErrorOr<size_t> Plan9FSInode::write_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer const& data, OpenFileDescription*)
 {
 {
     TRY(ensure_open_for_mode(O_WRONLY));
     TRY(ensure_open_for_mode(O_WRONLY));

+ 1 - 0
Kernel/FileSystem/Plan9FS/Inode.h

@@ -30,6 +30,7 @@ public:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> truncate(u64) override;
     virtual ErrorOr<void> truncate(u64) override;

+ 5 - 0
Kernel/FileSystem/ProcFS/Inode.cpp

@@ -47,4 +47,9 @@ ErrorOr<void> ProcFSInode::chown(UserID, GroupID)
     return EPERM;
     return EPERM;
 }
 }
 
 
+ErrorOr<void> ProcFSInode::replace_child(StringView, Inode&)
+{
+    return EROFS;
+}
+
 }
 }

+ 1 - 0
Kernel/FileSystem/ProcFS/Inode.h

@@ -33,6 +33,7 @@ protected:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override final;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override final;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override final;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override final;
     virtual ErrorOr<void> remove_child(StringView name) override final;
     virtual ErrorOr<void> remove_child(StringView name) override final;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override final;
     virtual ErrorOr<void> chmod(mode_t) override final;
     virtual ErrorOr<void> chmod(mode_t) override final;
     virtual ErrorOr<void> chown(UserID, GroupID) override final;
     virtual ErrorOr<void> chown(UserID, GroupID) override final;
 };
 };

+ 5 - 0
Kernel/FileSystem/SysFS/Inode.cpp

@@ -90,6 +90,11 @@ ErrorOr<void> SysFSInode::remove_child(StringView)
     return EROFS;
     return EROFS;
 }
 }
 
 
+ErrorOr<void> SysFSInode::replace_child(StringView, Inode&)
+{
+    return EROFS;
+}
+
 ErrorOr<void> SysFSInode::chmod(mode_t)
 ErrorOr<void> SysFSInode::chmod(mode_t)
 {
 {
     return EPERM;
     return EPERM;

+ 1 - 0
Kernel/FileSystem/SysFS/Inode.h

@@ -31,6 +31,7 @@ protected:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> truncate(u64) override;
     virtual ErrorOr<void> truncate(u64) override;

+ 20 - 0
Kernel/FileSystem/TmpFS/Inode.cpp

@@ -60,6 +60,26 @@ ErrorOr<void> TmpFSInode::traverse_as_directory(Function<ErrorOr<void>(FileSyste
     return {};
     return {};
 }
 }
 
 
+ErrorOr<void> TmpFSInode::replace_child(StringView name, Inode& new_child)
+{
+    MutexLocker locker(m_inode_lock);
+    VERIFY(is_directory());
+    VERIFY(new_child.fsid() == fsid());
+
+    auto* child = find_child_by_name(name);
+    if (!child)
+        return ENOENT;
+
+    auto old_child = child->inode;
+    child->inode = static_cast<TmpFSInode&>(new_child);
+
+    old_child->did_delete_self();
+
+    // TODO: Emit a did_replace_child event.
+
+    return {};
+}
+
 ErrorOr<NonnullOwnPtr<TmpFSInode::DataBlock>> TmpFSInode::DataBlock::create()
 ErrorOr<NonnullOwnPtr<TmpFSInode::DataBlock>> TmpFSInode::DataBlock::create()
 {
 {
     auto data_block_buffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(DataBlock::block_size, AllocationStrategy::AllocateNow));
     auto data_block_buffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(DataBlock::block_size, AllocationStrategy::AllocateNow));

+ 1 - 0
Kernel/FileSystem/TmpFS/Inode.h

@@ -30,6 +30,7 @@ public:
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<NonnullLockRefPtr<Inode>> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> add_child(Inode&, StringView name, mode_t) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
     virtual ErrorOr<void> remove_child(StringView name) override;
+    virtual ErrorOr<void> replace_child(StringView name, Inode& child) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chmod(mode_t) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> chown(UserID, GroupID) override;
     virtual ErrorOr<void> truncate(u64) override;
     virtual ErrorOr<void> truncate(u64) override;

+ 8 - 0
Kernel/FileSystem/VirtualFileSystem.cpp

@@ -652,6 +652,14 @@ ErrorOr<void> VirtualFileSystem::rename(Credentials const& credentials, StringVi
 
 
     TRY(new_parent_inode.add_child(old_inode, new_basename, old_inode.mode()));
     TRY(new_parent_inode.add_child(old_inode, new_basename, old_inode.mode()));
     TRY(old_parent_inode.remove_child(old_basename));
     TRY(old_parent_inode.remove_child(old_basename));
+
+    // If the inode that we moved is a directory and we changed parent
+    // directories, then we also have to make .. point to the new parent inode,
+    // because .. is its own inode.
+    if (old_inode.is_directory() && old_parent_inode.index() != new_parent_inode.index()) {
+        TRY(old_inode.replace_child(".."sv, new_parent_inode));
+    }
+
     return {};
     return {};
 }
 }