mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 04:12:43 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									2b246d980a
								
							
						
					
					
						commit
						3b03077abb
					
				
					 18 changed files with 128 additions and 0 deletions
				
			
		|  | @ -104,6 +104,11 @@ ErrorOr<void> DevPtsFSInode::remove_child(StringView) | |||
|     return EROFS; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> DevPtsFSInode::replace_child(StringView, Inode&) | ||||
| { | ||||
|     return EROFS; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> DevPtsFSInode::chmod(mode_t) | ||||
| { | ||||
|     return EROFS; | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ private: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ private: | |||
|     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> 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> increment_link_count() override; | ||||
|     virtual ErrorOr<void> decrement_link_count() override; | ||||
|  |  | |||
|  | @ -102,6 +102,12 @@ ErrorOr<NonnullOwnPtr<KBuffer>> FATInode::read_block_list() | |||
|     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) | ||||
| { | ||||
|     VERIFY(has_flag(m_entry.attributes, FATAttributes::Directory)); | ||||
|  |  | |||
|  | @ -62,6 +62,7 @@ private: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
|     virtual ErrorOr<void> flush_metadata() override; | ||||
|  |  | |||
|  | @ -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) | ||||
| { | ||||
|     LockRefPtr<Inode> inode; | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ public: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
|     virtual ErrorOr<void> truncate(u64) override; | ||||
|  |  | |||
|  | @ -66,6 +66,9 @@ public: | |||
|     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> 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> chown(UserID, GroupID) = 0; | ||||
|     virtual ErrorOr<void> truncate(u64) { return {}; } | ||||
|  |  | |||
|  | @ -96,6 +96,12 @@ ErrorOr<size_t> Plan9FSInode::read_bytes_locked(off_t offset, size_t size, UserO | |||
|     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*) | ||||
| { | ||||
|     TRY(ensure_open_for_mode(O_WRONLY)); | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ public: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
|     virtual ErrorOr<void> truncate(u64) override; | ||||
|  |  | |||
|  | @ -47,4 +47,9 @@ ErrorOr<void> ProcFSInode::chown(UserID, GroupID) | |||
|     return EPERM; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> ProcFSInode::replace_child(StringView, Inode&) | ||||
| { | ||||
|     return EROFS; | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ protected: | |||
|     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> 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> chown(UserID, GroupID) override final; | ||||
| }; | ||||
|  |  | |||
|  | @ -90,6 +90,11 @@ ErrorOr<void> SysFSInode::remove_child(StringView) | |||
|     return EROFS; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> SysFSInode::replace_child(StringView, Inode&) | ||||
| { | ||||
|     return EROFS; | ||||
| } | ||||
| 
 | ||||
| ErrorOr<void> SysFSInode::chmod(mode_t) | ||||
| { | ||||
|     return EPERM; | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ protected: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
|     virtual ErrorOr<void> truncate(u64) override; | ||||
|  |  | |||
|  | @ -60,6 +60,26 @@ ErrorOr<void> TmpFSInode::traverse_as_directory(Function<ErrorOr<void>(FileSyste | |||
|     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() | ||||
| { | ||||
|     auto data_block_buffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(DataBlock::block_size, AllocationStrategy::AllocateNow)); | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ public: | |||
|     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> 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> chown(UserID, GroupID) override; | ||||
|     virtual ErrorOr<void> truncate(u64) override; | ||||
|  |  | |||
|  | @ -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(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 {}; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 sin-ack
						sin-ack